From 17fffcd61927b49fea697d26fa9384a74b68b9ae Mon Sep 17 00:00:00 2001 From: Romain <> Date: Sat, 6 Nov 2021 10:37:32 +0100 Subject: [PATCH] Snapshot 1444697 --- Licence note | 1 + NTPSynchronisation.cpp | 57 + NTPSynchronisation.h | 17 + Pomodoro.cpp | 104 + Pomodoro.h | 43 + Pomodoro.ino | 186 ++ README.md | 72 + SegmentCharacter.h | 55 + SegmentDisplay.cpp | 83 + SegmentDisplay.h | 19 + Timer.cpp | 34 + Timer.h | 19 + WifiConfig.cpp | 3 + WifiConfig.h | 7 + doc/schematic.png | Bin 0 -> 27541 bytes doc/schematic.svg | 4125 +++++++++++++++++++++++++ doc/tool_SevenSegmentDisplayValue.png | Bin 0 -> 30325 bytes lib/pitches.h | 94 + tool/SevenSegmentDisplayValue.html | 173 ++ 19 files changed, 5092 insertions(+) create mode 100644 Licence note create mode 100644 NTPSynchronisation.cpp create mode 100644 NTPSynchronisation.h create mode 100644 Pomodoro.cpp create mode 100644 Pomodoro.h create mode 100644 Pomodoro.ino create mode 100644 README.md create mode 100644 SegmentCharacter.h create mode 100644 SegmentDisplay.cpp create mode 100644 SegmentDisplay.h create mode 100644 Timer.cpp create mode 100644 Timer.h create mode 100644 WifiConfig.cpp create mode 100644 WifiConfig.h create mode 100644 doc/schematic.png create mode 100644 doc/schematic.svg create mode 100644 doc/tool_SevenSegmentDisplayValue.png create mode 100644 lib/pitches.h create mode 100644 tool/SevenSegmentDisplayValue.html diff --git a/Licence note b/Licence note new file mode 100644 index 0000000..01464de --- /dev/null +++ b/Licence note @@ -0,0 +1 @@ +If you find software that doesn’t have a license, that means you have no permission from the creators of the software to use, modify, or share the software. Although a code host such as GitHub may allow you to view and fork the code, this does not imply that you are permitted to use, modify, or share the software for any purpose. diff --git a/NTPSynchronisation.cpp b/NTPSynchronisation.cpp new file mode 100644 index 0000000..3df8587 --- /dev/null +++ b/NTPSynchronisation.cpp @@ -0,0 +1,57 @@ +#include "NTPSynchronisation.h" + +const char* ntpServer="pool.ntp.org"; +const long gmtOffset_sec=3600; +const int daylightOffset_sec=3600; +struct tm timeinfo; + +/** + * Try to connect to the Wi-Fi, return true if success, false otherwise. + * A check of the connection will be made every timeDelay, for delayNumber tentative. + * If delayNumber is < 0 then the function will wait in indefinitely until the connection is made. + */ +bool connectWifi(int timeDelay, int delayNumber) { + int tentative=0; + WiFi.begin(wifiSsid, wifiPassword); + while(WiFi.status()!=WL_CONNECTED && (delayNumber<0 || tentative +#include "time.h" +#include "WifiConfig.h" + +extern const char* ntpServer; +extern const long gmtOffset_sec; +extern const int daylightOffset_sec; +extern struct tm timeinfo; + +extern bool connectWifi(int, int); +extern void disconnectWifi(); +extern bool synchroniseTimeNTP(bool, bool, int, int); + +#endif \ No newline at end of file diff --git a/Pomodoro.cpp b/Pomodoro.cpp new file mode 100644 index 0000000..67ce8e5 --- /dev/null +++ b/Pomodoro.cpp @@ -0,0 +1,104 @@ +#include "Pomodoro.h" + +bool useLed=true; +bool useBuzzer=true; + +uint32_t timeWork=25*60*1000; +uint32_t timeShortRest=5*60*1000; +uint32_t timeLongRest=15*60*1000; +int workBetweenLongBreak=4; +int workDone=0; +bool work=true; +int currentWait=timeWork; +uint32_t timerLastValue=0; + +const int pinLedWork=25; +const int pinLedShortBreak=33; +const int pinLedLongBreak=32; +const int ledFrequency=5000; +const int ledResolution=8; +const int ledChannelWork=0; +const int ledChannelShortBreak=1; +const int ledChannelLongBreak=2; +int ledWorkMaxValue=1; +int ledShortBreakMaxValue=1; +int ledLongBreakMaxValue=1; + +const int pinBuzzer=27; +const int buzzerFrequency=2000; +const int buzzerResolution=8; +const int buzzerChannel=3; + +void setupPomodoro(){ + //pinMode(pinLedWork,OUTPUT); + //pinMode(pinLedShortBreak,OUTPUT); + //pinMode(pinLedLongBreak,OUTPUT); + + ledcSetup(ledChannelWork, ledFrequency, ledResolution); + ledcSetup(ledChannelShortBreak, ledFrequency, ledResolution); + ledcSetup(ledChannelLongBreak, ledFrequency, ledResolution); + ledcAttachPin(pinLedWork, ledChannelWork); + ledcAttachPin(pinLedShortBreak, ledChannelShortBreak); + ledcAttachPin(pinLedLongBreak, ledChannelLongBreak); + + ledcSetup(buzzerChannel,buzzerFrequency,buzzerResolution); + ledcAttachPin(pinBuzzer,buzzerChannel); +} + +void writeToBuzzer(int note,int duration,bool continuous=false){ + if(useBuzzer){ + ledcWriteTone(buzzerChannel,note); + if(!continuous){ + delay(duration); + ledcWriteTone(buzzerChannel,0); + } + } +} +void writeToLed(int led,int value){ + if(useLed){ + ledcWrite(led,value); + } +} + +void displayWork(bool afterLongBreak){ + Serial.println("Return to work"); + writeToLed(ledChannelWork,ledWorkMaxValue); + writeToLed(ledChannelShortBreak,0); + writeToLed(ledChannelLongBreak,0); + //digitalWrite(LED_WORK,HIGH); + //digitalWrite(LED_SHORT_BREAK,LOW); + //digitalWrite(LED_LONG_BREAK,LOW); + + if(afterLongBreak){ + for(int i=0;i<3;i++){ + writeToBuzzer(NOTE_C4,200); + writeToBuzzer(NOTE_CS4,200); + delay(500); + } + }else{ + writeToBuzzer(NOTE_C1,200); + } +} +void displayShortRest(){ + Serial.println("Short rest"); + writeToLed(ledChannelWork,0); + writeToLed(ledChannelShortBreak,ledShortBreakMaxValue); + writeToLed(ledChannelLongBreak,0); + //digitalWrite(LED_WORK,LOW); + //digitalWrite(LED_SHORT_BREAK,HIGH); + //digitalWrite(LED_LONG_BREAK,LOW); + + writeToBuzzer(NOTE_F1,200); +} +void displayLongRest(){ + Serial.println("Long rest"); + writeToLed(ledChannelWork,0); + writeToLed(ledChannelShortBreak,0); + writeToLed(ledChannelLongBreak,ledLongBreakMaxValue); + //digitalWrite(LED_WORK,LOW); + //digitalWrite(LED_SHORT_BREAK,LOW); + //digitalWrite(LED_LONG_BREAK,HIGH); + + writeToBuzzer(NOTE_DS4,200); + writeToBuzzer(NOTE_D4,200); +} \ No newline at end of file diff --git a/Pomodoro.h b/Pomodoro.h new file mode 100644 index 0000000..278c5c6 --- /dev/null +++ b/Pomodoro.h @@ -0,0 +1,43 @@ +#ifndef POMODORO_H +#define POMODORO_H + +#include +#include "esp32-hal-ledc.h" +#include "HardwareSerial.h" +#include "lib/pitches.h" + +extern bool useLed; +extern bool useBuzzer; + +extern uint32_t timeWork ; +extern uint32_t timeShortRest ; +extern uint32_t timeLongRest ; +extern int workBetweenLongBreak; +extern int workDone; +extern bool work; +extern int currentWait; +extern uint32_t timerLastValue; + +extern const int pinLedWork; +extern const int pinLedShortBreak; +extern const int pinLedLongBreak; +extern const int ledFrequency; +extern const int ledResolution; +extern const int ledChannelWork; +extern const int ledChannelShortBreak; +extern const int ledChannelLongBreak; +extern int ledWorkMaxValue; +extern int ledShortBreakMaxValue; +extern int ledLongBreakMaxValue; + +extern const int pinBuzzer; +extern const int buzzerFrequency; +extern const int buzzerResolution; +extern const int buzzerChannel; + +void setupPomodoro(); +void displayWork(bool); +void displayShortRest(); +void displayLongRest(); + +#endif \ No newline at end of file diff --git a/Pomodoro.ino b/Pomodoro.ino new file mode 100644 index 0000000..74b69a8 --- /dev/null +++ b/Pomodoro.ino @@ -0,0 +1,186 @@ +#include "Pomodoro.h" +#include "Timer.h" +#include "SegmentDisplay.h" +#include "SegmentCharacter.h" +#include "NTPSynchronisation.h" +#include + +#define serialSpeed 115200 + +uint32_t isrCount=0; +uint32_t isrTime=0; + +enum PomodoroStatus{status_work,status_shortBreak,status_longBreak,status_workAfterLongBreak}; +PomodoroStatus pStatus=status_work; +uint32_t timeBeforeTransition=0; +uint32_t timeBeforeTransitionMinutes=0; +uint32_t timeBeforeTransitionSeconds=0; + +enum DisplayMode{dMode_clockHourMinute,dMode_clockDayMonth,dMode_pomodoro}; +DisplayMode dMode=dMode_pomodoro; +const int pinDisplayMode=4; +uint32_t debounceTime=0; +const uint32_t debounceWait=10; + +bool wifiIssue=false; +bool ntpIssue=false; + +TaskHandle_t taskHandleAnnouncer; + +void taskAnnouncer(void * param){ + switch(pStatus){ + case status_work: + displayWork(false); + break; + case status_workAfterLongBreak: + displayWork(true); + break; + case status_shortBreak: + displayShortRest(); + break; + case status_longBreak: + displayLongRest(); + break; + default: + break; + } + if(taskHandleAnnouncer!=NULL){ + vTaskDelete(taskHandleAnnouncer); + } +} + +void startTaskAnnouncer(){ + xTaskCreatePinnedToCore(taskAnnouncer,"taskAnnouncer",1024,NULL,1,&taskHandleAnnouncer,0); +} + +void IRAM_ATTR displayModeInterrupt(){ + if(xTaskGetTickCount()-debounceTime>debounceWait){ + switch(dMode){ + case dMode_clockHourMinute: + if(wifiIssue||ntpIssue){ + dMode=dMode_pomodoro; + }else{ + dMode=dMode_clockDayMonth; + } + break; + case dMode_clockDayMonth: + dMode=dMode_pomodoro; + break; + default: + dMode=dMode_clockHourMinute; + break; + } + debounceTime=xTaskGetTickCount(); + } +} + +void setup(){ + Serial.begin(115200); + setupTimer(); + setupPomodoro(); + setupSegmentDisplay(); + + sDisplayDigitValue[0]=SC_H; + sDisplayDigitValue[1]=SC_E; + sDisplayDigitValue[2]=SC_L; + sDisplayDigitValue[3]=SC_O; + + wifiIssue=!connectWifi(500,10); + if(!wifiIssue){ + ntpIssue=!synchroniseTimeNTP(false,true,500,10); + } + + pinMode(pinDisplayMode,INPUT_PULLUP); + attachInterrupt(pinDisplayMode, displayModeInterrupt, RISING); + + startTaskAnnouncer(); +} + +void loop() { + powerSegmentDisplay(); + delay(20); + if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){ + isrCount=0; + isrTime=0; + + // Read the interrupt count and time + portENTER_CRITICAL(&timerMux); + isrCount = isrCounter; + isrTime = lastIsrAt; + portEXIT_CRITICAL(&timerMux); + + //Avoid garbage display when isrTime is bigger than the + //waiting time resulting in timeBeforeTransition overflow + if(isrTime>(timerLastValue+currentWait)){ + timeBeforeTransition=0; + }else{ + timeBeforeTransition=((timerLastValue+currentWait)-isrTime)/1000; + } + + switch(dMode){ + case dMode_clockHourMinute: + case dMode_clockDayMonth: + if(wifiIssue){ + sDisplayDigitValue[0]=SC_W; + sDisplayDigitValue[1]=SC_I; + sDisplayDigitValue[2]=SC_F; + sDisplayDigitValue[3]=SC_I; + break; + } + if(ntpIssue){ + sDisplayDigitValue[0]=SC_N; + sDisplayDigitValue[1]=SC_T; + sDisplayDigitValue[2]=SC_P; + sDisplayDigitValue[3]=SC_BLANK; + break; + } + getLocalTime(&timeinfo); + if(dMode==dMode_clockHourMinute){ + setNumberToDisplay(timeinfo.tm_hour*100+timeinfo.tm_min); + }else{ + setNumberToDisplay(timeinfo.tm_mday*100+timeinfo.tm_mon+1); + } + sDisplayDigitValue[1]=sDisplayDigitValue[1]+SC_DOT; + sDisplayDigitValue[3]=sDisplayDigitValue[3]+SC_DOT; + break; + default: + timeBeforeTransitionMinutes=timeBeforeTransition/60; + timeBeforeTransitionSeconds=timeBeforeTransition%60; + sDisplayDigitValue[0]=SC_NUMBER[timeBeforeTransitionMinutes/10]; + sDisplayDigitValue[1]=SC_NUMBER[timeBeforeTransitionMinutes%10]+SC_DOT; + sDisplayDigitValue[2]=SC_NUMBER[timeBeforeTransitionSeconds/10]; + sDisplayDigitValue[3]=SC_NUMBER[timeBeforeTransitionSeconds%10]; + break; + } + + // Pomodoro calculation + if(isrTime>=(timerLastValue+currentWait)){ + timerLastValue=isrTime; + if(work){ + work=false; + workDone++; + if(workDone>=workBetweenLongBreak){ + currentWait=timeLongRest; + workDone=0; + //displayLongRest(); + pStatus=status_longBreak; + }else{ + currentWait=timeShortRest; + //displayShortRest(); + pStatus=status_shortBreak; + } + }else{ + currentWait=timeWork; + work=true; + if(workDone==0){ + //displayWork(true); + pStatus=status_workAfterLongBreak; + }else{ + //displayWork(false); + pStatus=status_work; + } + } + startTaskAnnouncer(); + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..399c31c --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Pomodoro on ESP-32 + +Implementation of the Pomodoro technique on the ESP-32 microcontroller. + +Each transition between work and rest cycle is indicated by one or multiple physical stimulus (melody and/or color). + +A display indicates the progression of the timer and double as an NTP clock. + +## Table of content + +* [Pomodoro](#pomodoro) +* [Electronic circuit](#electronic-circuit) + * [Schematic](#schematic) + * [Materials](#materials) + * [Note on the seven-segments display and shift-register](#note-on-the-seven-segments-display-and-shift-register) +* [Software](#software) + * [Design](#design) + * [Dependency](#dependency) +* [Tool](#tool) + * [Seven-segment display value](#seven-segment-display-value) + +## Pomodoro + +Pomodoro is a time management technique that use a timer to break down task and rest period. + +## Electronic circuit + +### Schematic + +![schematic picture](doc/schematic.png) + +### Materials + +|Type|Designation|Quantity| +|-|-|-:| +|System on a chip|ESP-WROOM-32|1| +|4 digit 7 segment display|HS420561K-32|1| +|8-bit shift register|SN74HC595N|1| +|Transistor|PN2222|4| +|Diode||3| +|Passive buzzer||1| +|Momentary Switch||1| +|Resistor|220 Ω|11| +|Resistor|4.7 kΩ|4| + +#### **Note on the seven-display segment and shift-register** + +You might have notice that it's not the prettiest way to use a 4-digits seven-segments display. + +Initially I wanted to use one shift-register per digit, but as I found only one SN74HC595N on my parts bins, I ultimately decided to make do. The result is a bit messier but works. + +## Software + +### Design + +This software make use of the two core of the ESP-32, and it's FreeRTOS implementation to have two tasks: + +1. Display the time remaining on the clock and use interrupt to count each second passed. +2. Alarm the user of a transition between task and rest with the use of melody and visual cue. + +### Dependency + +Ignoring the common library use in almost every project (like `stdint.h`, `Arduino.h`, `esp32-hal-ledc.h` or `HardwareSerial.h`), the code make use of: + +* [pitches.h from arduino](https://github.com/arduino/arduino-examples/blob/main/examples/02.Digital/toneMelody/pitches.h) to create melody using the piezo speaker. Included in [lib/pitches.h](lib/pitches.h) for ease of deployment. + +## Tool + +### **Seven-segment display value** + +To ease the transition between physical segment and binary/hexadecimal code, I created a small HTML tool [tool/SevenSegmentDisplayValue.html](tool/SevenSegmentDisplayValue.html). +![segmentDisplayConverter.html in action](doc/tool_SevenSegmentDisplayValue.png) diff --git a/SegmentCharacter.h b/SegmentCharacter.h new file mode 100644 index 0000000..0743e5d --- /dev/null +++ b/SegmentCharacter.h @@ -0,0 +1,55 @@ +#ifndef SEGMENT_CHARACTER_H +#define SEGMENT_CHARACTER_H + +#include + +const byte SC_A=0xCF; +const byte SC_B=0xDA; +const byte SC_C=0x1B; +const byte SC_D=0xDC; +const byte SC_E=0x9B; +const byte SC_F=0x8B; +const byte SC_G=0x5B; +const byte SC_H=0xCE; +const byte SC_I=0x0A; +const byte SC_J=0x5C; +const byte SC_K=0x00; +const byte SC_L=0x1A; +const byte SC_M=0x4F; +const byte SC_N=0xC8; +const byte SC_O=0x5F; +const byte SC_o=0xD8; +const byte SC_P=0x8F; +const byte SC_Q=0xC7; +const byte SC_R=0x88; +const byte SC_S=0xD3; +const byte SC_T=0x9A; +const byte SC_U=0x5E; +const byte SC_V=0x58; +const byte SC_W=0x16; +const byte SC_X=0x00; +const byte SC_Y=0xD6; +const byte SC_Z=0x00; + +const byte SC_0=0x5F; +const byte SC_1=0x44; +const byte SC_2=0x9D; +const byte SC_3=0xD5; +const byte SC_4=0xC6; +const byte SC_5=0xD3; +const byte SC_6=0xDA; +const byte SC_7=0x45; +const byte SC_8=0xDF; +const byte SC_9=0xD7; + +const byte SC_NUMBER[]={ + SC_0,SC_1,SC_2,SC_3,SC_4,SC_5,SC_6,SC_7,SC_8,SC_9 +}; + +const byte SC_BLANK=0x00; +const byte SC_DOT=0x20; +const byte SC_EQUAL=0x90; +const byte SC_MINUS=0x80; +const byte SC_UNDERSCORE=0x10; + +#endif \ No newline at end of file diff --git a/SegmentDisplay.cpp b/SegmentDisplay.cpp new file mode 100644 index 0000000..0274559 --- /dev/null +++ b/SegmentDisplay.cpp @@ -0,0 +1,83 @@ +/* +Command a four-digits seven-segments display. + +Use sDisplayDigitValue[] to change the digits values. +The digits are listed from left to right. +You can use SegmentCharacter.h to get the binary code for a given character. + +Example: +void setup(){ + sDisplayDigitValue[0]=SC_4; + sDisplayDigitValue[1]=SC_NUMBER[1]; + sDisplayDigitValue[2]=SC_EQUAL; + sDisplayDigitValue[3]=SC_A; +} +void loop(){ + powerSegmentDisplay(); + delay(20); +} +*/ + +#include "SegmentDisplay.h" +const int pinSDisplayShiftRegisterSER=23; +const int pinSDisplayShiftRegisterRCLK=22; +const int pinSDisplayShiftRegisterSRCLK=5; + +const byte pinSDisplayDigit[4]={17,18,19,21}; + +byte sDisplayDigitValue[]={0,0,0,0}; + +void setupSegmentDisplay(){ + pinMode(pinSDisplayShiftRegisterSER,OUTPUT); + pinMode(pinSDisplayShiftRegisterRCLK,OUTPUT); + pinMode(pinSDisplayShiftRegisterSRCLK,OUTPUT); + + for(int i=0;i 9999 or <-999. + */ +void setNumberToDisplay(int number){ + if(number<0){ + sDisplayDigitValue[0]=SC_MINUS; + if(number<-999){ + sDisplayDigitValue[0]=SC_E; + } + number=number*-1; + }else{ + sDisplayDigitValue[0]=SC_NUMBER[number/1000]; + } + + int n=number%1000; + sDisplayDigitValue[1]=SC_NUMBER[n/100]; + n=n%100; + sDisplayDigitValue[2]=SC_NUMBER[n/10]; + n=n%10; + sDisplayDigitValue[3]=SC_NUMBER[n]; + + if(number>9999){ + sDisplayDigitValue[0]=SC_E; + } +} \ No newline at end of file diff --git a/SegmentDisplay.h b/SegmentDisplay.h new file mode 100644 index 0000000..09e0c7b --- /dev/null +++ b/SegmentDisplay.h @@ -0,0 +1,19 @@ +#ifndef SEGMENT_DISPLAY_H +#define SEGMENT_DISPLAY_H + +#include +#include "SegmentCharacter.h" + +extern const int pinSDisplayShiftRegisterSER; +extern const int pinSDisplayShiftRegisterRCLK; +extern const int pinSDisplayShiftRegisterSRCLK; + +extern const byte pinSDisplayDigit[]; +extern byte sDisplayDigitValue[]; + +extern void setupSegmentDisplay(); +extern void powerSegmentDisplay(); + +extern void setNumberToDisplay(int); + +#endif \ No newline at end of file diff --git a/Timer.cpp b/Timer.cpp new file mode 100644 index 0000000..a574a13 --- /dev/null +++ b/Timer.cpp @@ -0,0 +1,34 @@ +#include "Timer.h" + +hw_timer_t * timer = NULL; +volatile SemaphoreHandle_t timerSemaphore; +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; +volatile uint32_t isrCounter = 0; +volatile uint32_t lastIsrAt = 0; + +void IRAM_ATTR onTimer(){ + // Increment the counter and set the time of ISR + portENTER_CRITICAL_ISR(&timerMux); + isrCounter++; + lastIsrAt = millis(); + portEXIT_CRITICAL_ISR(&timerMux); + // Give a semaphore that we can check in the loop + xSemaphoreGiveFromISR(timerSemaphore, NULL); + // It is safe to use digitalRead/Write here if you want to toggle an output +} + +void setupTimer(){ + // Create semaphore to inform us when the timer has fired + timerSemaphore = xSemaphoreCreateBinary(); + // Use 1st timer of 4 (counted from zero). + // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more + // info). + timer = timerBegin(0, 80, true); + // Attach onTimer function to our timer. + timerAttachInterrupt(timer, &onTimer, true); + // Set alarm to call onTimer function every second (value in microseconds). + // Repeat the alarm (third parameter) + timerAlarmWrite(timer, 1000000, true); + // Start an alarm + timerAlarmEnable(timer); +} \ No newline at end of file diff --git a/Timer.h b/Timer.h new file mode 100644 index 0000000..5063290 --- /dev/null +++ b/Timer.h @@ -0,0 +1,19 @@ +//#pragma once +#ifndef TIMER_SETUP_H +#define TIMER_SETUP_H + +#include +#include +#include +#include + +extern hw_timer_t * timer; +extern volatile SemaphoreHandle_t timerSemaphore; +extern portMUX_TYPE timerMux; +extern volatile uint32_t isrCounter; +extern volatile uint32_t lastIsrAt; + +void IRAM_ATTR onTimer(); +void setupTimer(); + +#endif \ No newline at end of file diff --git a/WifiConfig.cpp b/WifiConfig.cpp new file mode 100644 index 0000000..748e325 --- /dev/null +++ b/WifiConfig.cpp @@ -0,0 +1,3 @@ +#include "WifiConfig.h" +const char *wifiSsid="YourSSIDHere"; +const char *wifiPassword="YourPSKHere"; \ No newline at end of file diff --git a/WifiConfig.h b/WifiConfig.h new file mode 100644 index 0000000..1acf36f --- /dev/null +++ b/WifiConfig.h @@ -0,0 +1,7 @@ +#ifndef WIFI_CONFIG_H +#define WIFI_CONFIG_H + +extern const char *wifiSsid; +extern const char *wifiPassword; + +#endif \ No newline at end of file diff --git a/doc/schematic.png b/doc/schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..b09b230f468caf18e0da446d11f72e4e662861ff GIT binary patch literal 27541 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
    fp(yll64AMLlSfC>5i&5bdyKpP*8suA*r9un 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 V1iQHx%D@DWw8Tg8()arQ{{c30MLYli literal 0 HcmV?d00001 diff --git a/doc/schematic.svg b/doc/schematic.svg new file mode 100644 index 0000000..65366a7 --- /dev/null +++ b/doc/schematic.svg @@ -0,0 +1,4125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +D1 + + + + + + +LED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +D2 + + + + + + +LED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +D3 + + + + + + +LED + + + + + + + + + + + + + + + + + + + + + + + + + + +BUZ1 + + + + + + +BUZZER + + + + + + + + + + + + + + + + + + +R1 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R2 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R3 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R10 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R9 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R8 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R7 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R6 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R5 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R4 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R11 + + + + + + +220R + + + + + + + + + + + + + + + + + + +R12 + + + + + + +4k7 + + + + + + + + + + + + + + + + + + +R13 + + + + + + +4k7 + + + + + + + + + + + + + + + + + + +R14 + + + + + + +4k7 + + + + + + + + + + + + + + + + + + +R15 + + + + + + +4k7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q1 + + + + + + +PN2222 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q2 + + + + + + +PN2222 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q3 + + + + + + +PN2222 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q4 + + + + + + +PN2222 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 + + + + + + + + + + +2 + + + + + + + + + + +3 + + + + + + + + + + +4 + + + + + + + + + + +12 + + + + + + + + + + +11 + + + + + + + + + + +10 + + + + + + + + + + +9 + + + + + + + + + + +8 + + + + + + + + + + +7 + + + + + + + + + + +5 + + + + + + + + + + +6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HS420561K-32 + + + + + + +S1 + + + + + + + + + + + + + + +3.3V + + + + + + + + + + + + + + +3.3V + + + + + + + + + + + + + + +QB + + + + + + +1 + + + + + + + + + + +QC + + + + + + +2 + + + + + + + + + + +QD + + + + + + +3 + + + + + + + + + + +QE + + + + + + +4 + + + + + + + + + + +QF + + + + + + +5 + + + + + + + + + + +QG + + + + + + +6 + + + + + + + + + + +QH_1 + + + + + + +7 + + + + + + + + + + +GND + + + + + + +8 + + + + + + + + + + +VCC + + + + + + +16 + + + + + + + + + + +QA + + + + + + +15 + + + + + + + + + + +SER + + + + + + +14 + + + + + + + + + + +OE + + + + + + + + + + +13 + + + + + + + + + + +RCLK + + + + + + +12 + + + + + + + + + + +SRCLK + + + + + + +11 + + + + + + + + + + +SRCLR + + + + + + + + + + +10 + + + + + + + + + + +QH_2 + + + + + + +9 + + + + + + +U2 + + + + + + +SN74HC595N + + + + + + + + + + + + + + +3.3V + + + + + + + + + + + + + + +GND_1 + + + + + + +1 + + + + + + + + + + +3V3 + + + + + + +2 + + + + + + + + + + +EN + + + + + + +3 + + + + + + + + + + +SENSOR_VP + + + + + + +4 + + + + + + + + + + +SENSOR_VN + + + + + + +5 + + + + + + + + + + +IO34 + + + + + + +6 + + + + + + + + + + +IO35 + + + + + + +7 + + + + + + + + + + +IO32 + + + + + + +8 + + + + + + + + + + +IO33 + + + + + + +9 + + + + + + + + + + +IO25 + + + + + + +10 + + + + + + + + + + +IO26 + + + + + + +11 + + + + + + + + + + +IO27 + + + + + + +12 + + + + + + + + + + +IO14 + + + + + + +13 + + + + + + + + + + +IO12 + + + + + + +14 + + + + + + + + + + +GND_2 + + + + + + +15 + + + + + + + + + + +IO13 + + + + + + +16 + + + + + + + + + + +SD2 + + + + + + +17 + + + + + + + + + + +SD3 + + + + + + +18 + + + + + + + + + + +CMD + + + + + + +19 + + + + + + + + + + +CLK + + + + + + +20 + + + + + + + + + + +SD0 + + + + + + +21 + + + + + + + + + + +SD1 + + + + + + +22 + + + + + + + + + + +IO15 + + + + + + +23 + + + + + + + + + + +IO2 + + + + + + +24 + + + + + + + + + + +IO0 + + + + + + +25 + + + + + + + + + + +IO4 + + + + + + +26 + + + + + + + + + + +IO16 + + + + + + +27 + + + + + + + + + + +IO17 + + + + + + +28 + + + + + + + + + + +IO5 + + + + + + +29 + + + + + + + + + + +IO18 + + + + + + +30 + + + + + + + + + + +IO19 + + + + + + +31 + + + + + + + + + + +NC + + + + + + +32 + + + + + + + + + + +IO21 + + + + + + +33 + + + + + + + + + + +RXD0 + + + + + + +34 + + + + + + + + + + +TXD0 + + + + + + +35 + + + + + + + + + + +IO22 + + + + + + +36 + + + + + + + + + + +IO23 + + + + + + +37 + + + + + + + + + + +GND_3 + + + + + + +38 + + + + + + + + + + +GND_4 + + + + + + +39 + + + + + + +U1 + + + + + + +ESP-WROOM-32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SW1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/tool_SevenSegmentDisplayValue.png b/doc/tool_SevenSegmentDisplayValue.png new file mode 100644 index 0000000000000000000000000000000000000000..b04652807cf77568dd49746878bc35fa816522bb GIT binary patch literal 30325 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�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 literal 0 HcmV?d00001 diff --git a/lib/pitches.h b/lib/pitches.h new file mode 100644 index 0000000..f6a1841 --- /dev/null +++ b/lib/pitches.h @@ -0,0 +1,94 @@ +/************************************************* + * Public Constants + *************************************************/ + +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 + \ No newline at end of file diff --git a/tool/SevenSegmentDisplayValue.html b/tool/SevenSegmentDisplayValue.html new file mode 100644 index 0000000..4f925a2 --- /dev/null +++ b/tool/SevenSegmentDisplayValue.html @@ -0,0 +1,173 @@ + + + + + + + Seven-segment display value + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Seven-segment display value

    +
    Click any number of segments on the display on the left to get the corresponding values.
    + + + + + + + + + + + + + + + + +
    TypeValuePadded
    Binary
    Hexadecimal
    +
    + + \ No newline at end of file