diff --git a/src/main.cpp b/src/main.cpp index 39e1e06..177f307 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,11 +9,11 @@ * 5V O O 5V * 3V3 O O 3V3 * GND O < 36 only input, no pullups - * RX * * 17 + * RX * ? * 17 * TX * < 38 ROTARY_A only input, no pullups * RST * BUTTON < 39 ROTARY_B only input, no pullups * 0 * BUTTON < 34 only input, no pullups - * 22 * < 35 only input, no pullups + * ROTARY_BTN 22 * < 35 only input, no pullups * BME280_SDO 19 * LoRa_MISO ? * 32 * BME280_CS 23 * ? * 33 * 18 x LoRa_CS * 25 MAX98_DIN @@ -51,9 +51,8 @@ #include "main.h" #include "mp3.h" #include "BME280.h" -#include "image.h" #include "rotary.h" -#include "selectionlist.h" +#include "screen.h" U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); @@ -67,11 +66,7 @@ char timeStr[20]; BME280 bme280; MP3 mp3; Rotary rotary; -SelectionList menu; - -uint32_t lastButtonPress = 0; - -u8g2_uint_t titleStr_offset = 0; +Screen* screen; //Variables for the sensor and the battery static const int buttonPin = 0; @@ -80,7 +75,7 @@ static const int SensorPin = 0; static const int BatteryPin = 34; static const int rotaryPinA = 38; static const int rotaryPinB = 39; -static const int rotaryPinButton = 36; +static const int rotaryPinButton = 22; int sensorValue = 0; //The batteryLimit defines the point at which the battery is considered empty. int batteryLimit = 3300; @@ -138,6 +133,7 @@ void setup() { u8g2.begin(); delay(50); + screen = new MainScreen(); u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font u8g2.drawStr(2,10,"esp32-node by hendrik"); // write something to the internal memory @@ -272,11 +268,10 @@ void suspendESP(uint16_t packetId) { } -void rotation(int i, int direction) { - Serial.println(i); - mp3.setVolume(i); - if (direction == 1) menu.next(); - else if (direction == -1) menu.previous(); +void rotation(int i, int direction, int buttonPressed) { + if (buttonPressed == 1) screen->select(); + if (direction == 1) screen->next(); + else if (direction == -1) screen->previous(); } @@ -285,44 +280,7 @@ void loop() timeClient.update(); timeClient.getFormattedTime().toCharArray(timeStr, 50); - if (digitalRead(buttonPin) == LOW) { - if(millis() - lastButtonPress >= 1000) { - Serial.println("Button pressed"); - lastButtonPress = millis(); - if (mp3.playing) { - mp3.stop(); - } else { - mp3.start(); - } - } - } -menu.draw(); -/* - u8g2.clearBuffer(); // clear the internal memory - - if (digitalRead(buttonPin) == HIGH) { - u8g2.setFont(u8g2_font_inb19_mf); - u8g2.drawStr(0, 20, timeStr); + screen->draw(); - char weather[32]; - u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font - sprintf(weather, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure()); - u8g2.drawUTF8(0, 30, weather); - u8g2.setFont(u8g2_font_prospero_bold_nbp_tf); // choose a suitable font - - u8g2_uint_t width = u8g2.getUTF8Width(titleStr); // calculate the pixel width of the text - // repeated drawing of the scrolling text... - u8g2.drawUTF8(titleStr_offset, 54, titleStr); // draw the scolling text - - titleStr_offset-=4; // scroll by one pixel - if ( (u8g2_uint_t)titleStr_offset < (u8g2_uint_t)-width ) - titleStr_offset = 0; // start over again // u8g2.getDisplayWidth() - - } else { - u8g2.drawXBMP(0,0, IMG_1872_width, IMG_1872_height, IMG_1872_bits); - } - u8g2.sendBuffer(); -// bme280.printValues(); -*/ delay(200); } diff --git a/src/main.h b/src/main.h index cad7638..3c74e28 100644 --- a/src/main.h +++ b/src/main.h @@ -7,4 +7,4 @@ void transmitStatus(); void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total); void suspendESP(uint16_t packetId); void loop(); -void rotation(int i, int direction); +void rotation(int i, int direction, int buttonPressed); diff --git a/src/mp3.cpp b/src/mp3.cpp index 4354fda..95e449c 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -49,7 +49,7 @@ void MP3::start() { xTaskCreate( &cTaskWrapper, /* Task function. */ "audioTask", /* String with name of task. */ - 2048, /* Stack size in words. */ + 3072, /* Stack size in words. */ this, /* Parameter passed as input of the task */ tskIDLE_PRIORITY+1, /* Priority of the task. */ &audioTaskHandle); /* Task handle. */ diff --git a/src/mp3.h b/src/mp3.h index a1abb42..de20547 100644 --- a/src/mp3.h +++ b/src/mp3.h @@ -32,7 +32,7 @@ class MP3 { void mp3_decoder_task(void*); static void cTaskWrapper(void*); - static constexpr int preallocateBufferSize = 5*1024; + static constexpr int preallocateBufferSize = 8*1024; static constexpr int preallocateCodecSize = 29192; // MP3 codec max mem needed void *preallocateBuffer = NULL; void *preallocateCodec = NULL; diff --git a/src/rotary.cpp b/src/rotary.cpp index 42ea5e2..d3c0409 100644 --- a/src/rotary.cpp +++ b/src/rotary.cpp @@ -21,18 +21,19 @@ bool Rotary::begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton) { this->pinButton = pinButton; pinMode(pinA, INPUT_PULLUP); pinMode(pinB, INPUT_PULLUP); - pinMode(pinButton, INPUT_PULLUP); + pinMode(pinButton, INPUT_PULLDOWN); xTaskCreate( &cTaskWrapper, /* Task function. */ "encoderTask", /* String with name of task. */ - 1024, /* Stack size in words. */ + 2048, /* Stack size in words. */ this, /* Parameter passed as input of the task */ tskIDLE_PRIORITY+2, /* Priority of the task. */ &taskHandle); /* Task handle. */ attachInterrupt(digitalPinToInterrupt(pinA), doEncoder, CHANGE); attachInterrupt(digitalPinToInterrupt(pinB), doEncoder, CHANGE); + attachInterrupt(digitalPinToInterrupt(pinButton), doEncoder, CHANGE); return true; } @@ -48,6 +49,19 @@ void Rotary::task(void *pvParameters) { ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); delay(2); // wait until bounce settled + if (digitalRead(pinButton) == HIGH) { + Serial.println("button"); + buttonPressed = true; + if (callback) callback(value, 0, 1); + continue; + } else { + if (buttonPressed) { + buttonPressed = false; + if (callback) callback(value, 0, -1); + continue; + } + } + static uint8_t old_AB = 0; // grey code // http://hades.mech.northwestern.edu/index.php/Rotary_Encoder @@ -67,10 +81,10 @@ void Rotary::task(void *pvParameters) { debouncePulses = 0; if (encoderPos > encoderPosOld+1) { value++; // if the value has at least changed for 2 - if (callback) callback(value, 1); + if (callback) callback(value, 1, (int)buttonPressed); } else if (encoderPos < encoderPosOld-1) { value--; - if (callback) callback(value, -1); + if (callback) callback(value, -1, (int)buttonPressed); } // otherwise skip encoderPosOld = encoderPos; @@ -80,7 +94,7 @@ void Rotary::task(void *pvParameters) { vTaskDelete(NULL); } -bool Rotary::registerCallback(std::function callback) { +bool Rotary::registerCallback(std::function callback) { this->callback = callback; return true; } diff --git a/src/rotary.h b/src/rotary.h index f14fc25..925e45f 100644 --- a/src/rotary.h +++ b/src/rotary.h @@ -16,11 +16,12 @@ class Rotary { public: Rotary(); bool begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton); - bool registerCallback(std::function callback); + bool registerCallback(std::function callback); static void IRAM_ATTR doEncoder(void); static Rotary* instance; volatile int encoderPos = 0; int encoderPosOld = 0; + bool buttonPressed = false; int value = 0; uint32_t debounceMillis; uint32_t debounceDelay = 1; @@ -31,7 +32,7 @@ class Rotary { uint8_t pinA; uint8_t pinB; uint8_t pinButton; - std::function callback = nullptr; + std::function callback = nullptr; TaskHandle_t taskHandle; void task(void*); static void cTaskWrapper(void*); diff --git a/src/screen.cpp b/src/screen.cpp new file mode 100644 index 0000000..74074c7 --- /dev/null +++ b/src/screen.cpp @@ -0,0 +1,161 @@ +#include "screen.h" + +void Screen::next() { Serial.println("unhandled next"); } +void Screen::previous() { Serial.println("unhandled previous"); } +uint8_t Screen::select() { Serial.println("unhandled select"); } + +SelectionList::SelectionList() {} + +SelectionList::SelectionList(const char* title, uint8_t start_pos, const char* string_list) {} + +void SelectionList::draw() { + uint8_t item_x_offset = 15; + uint8_t header_height = 12; + uint8_t item_height = 12; + uint8_t num_active_item = current_pos - top_item; + + u8g2.clearBuffer(); + + /* draw Title */ + u8g2.setFont(u8g2_font_guildenstern_nbp_tr); + u8g2.drawUTF8(2,header_height-2,title); + u8g2.drawLine(0,header_height,u8g2.getDisplayWidth(),header_height); + + /* draw menu items */ + u8g2.setFont(u8g2_font_9x18B_tr); + for (int i=0; i top_item+visible-1) top_item++; +} + +void SelectionList::previous() { + if (current_pos > 0 ) current_pos--; + if (current_pos < top_item) top_item--; +} + +uint8_t SelectionList::select() { + return current_pos; +} + +namespace { + + void Drawgauge(int x, byte y, byte r, byte p, int v, int minVal, int maxVal ) { + int n=(r/100.00)*p; // calculate needle percent lenght + + float gs_rad=-1.572; //-90 degrees in radiant + float ge_rad=0; + float i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad); + int xp = x+(sin(i) * n); + int yp = y-(cos(i) * n); + u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT ); + u8g2.drawLine(x,y,xp,yp); + + gs_rad=0; + ge_rad=1.572; //90 degrees in radiant + i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad); + xp = x+(cos(i) * n); + yp = y-(sin(i) * n); + u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_RIGHT ); + u8g2.drawLine(x,y,xp,yp); + + gs_rad=-1.572; + ge_rad=1.572; + i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad); + xp = x+(sin(i) * n); + yp = y-(cos(i) * n); + u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT|U8G2_DRAW_UPPER_RIGHT ); + u8g2.drawLine(x,y,xp,yp); + + gs_rad=-3.142; + ge_rad=1.572; + i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad); + xp = x+(sin(i) * n); + yp = y-(cos(i) * n); + u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT|U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_LEFT ); + u8g2.drawLine(x,y,xp,yp); + + gs_rad=-1.572; + ge_rad=3.141; + i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad); + xp = x+(sin(i) * n); + yp = y-(cos(i) * n); + u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT|U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_RIGHT ); + u8g2.drawLine(x,y,xp,yp); + } +} + +void MainScreen::draw() { + u8g2.clearBuffer(); // clear the internal memory + + if(millis() - lastVolumeChange <= 1000) { + //u8g2.drawXBMP(0,0, IMG_1872_width, IMG_1872_height, IMG_1872_bits); + u8g2.setFont(u8g2_font_inb19_mf); + u8g2.drawStr(20, 20, "Volume"); + + u8g2.drawRBox(10, 28, 108, 22, 5); + u8g2.setDrawColor(0); + u8g2.drawBox(14+volume, 31, 100-volume, 14); + char volumeStr[6]; + sprintf(volumeStr, "%d %%", volume); + u8g2.setFont(u8g2_font_crox3cb_mf); + u8g2.setFontMode(1); + u8g2.setDrawColor(2); + u8g2.drawStr(40, 45, volumeStr); + u8g2.setFontMode(0); + u8g2.setDrawColor(1); + } else { + + u8g2.setFont(u8g2_font_inb19_mf); + u8g2.drawStr(0, 20, timeStr); + + char weather[32]; + u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font + sprintf(weather, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure()); + u8g2.drawUTF8(0, 30, weather); + u8g2.setFont(u8g2_font_prospero_bold_nbp_tf); // choose a suitable font + + u8g2_uint_t width = u8g2.getUTF8Width(titleStr); // calculate the pixel width of the text + // repeated drawing of the scrolling text... + u8g2.drawUTF8(titleStr_offset, 54, titleStr); // draw the scolling text + + titleStr_offset-=4; // scroll by one pixel + if ( (u8g2_uint_t)titleStr_offset < (u8g2_uint_t)-width ) + titleStr_offset = 0; // start over again // u8g2.getDisplayWidth() + } + u8g2.sendBuffer(); +// bme280.printValues(); +} + +uint8_t MainScreen::select() { + if(millis() - lastButtonPress >= 1000) { + Serial.println("Button pressed"); + lastButtonPress = millis(); + if (mp3.playing) { + mp3.stop(); + } else { + mp3.start(); + } + } +} + +void MainScreen::next() { + if (volume < 100) volume++; + lastVolumeChange = millis(); + mp3.setVolume(volume); + Serial.printf("volume: %d\n", volume); +} + +void MainScreen::previous() { + if (volume > 0) volume--; + lastVolumeChange = millis(); + mp3.setVolume(volume); + Serial.printf("volume: %d\n", volume); +} diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000..a0b0bdb --- /dev/null +++ b/src/screen.h @@ -0,0 +1,76 @@ +#ifndef _SCREEN_H +#define _SCREEN_H + +#include +#include +#include + +#include +#ifdef U8X8_HAVE_HW_SPI +#include +#endif +#ifdef U8X8_HAVE_HW_I2C +#include +#endif + +#include "mp3.h" +#include "BME280.h" +#include "image.h" + +extern U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2; +extern char timeStr[]; +extern BME280 bme280; +extern MP3 mp3; + +class Screen { + public: +// Screen(); + virtual void draw(void) = 0; + virtual void next(void); + virtual void previous(void); + virtual uint8_t select(void); + const char* title = "Test Screen"; + private: +}; + +class SelectionList : public Screen { + public: + SelectionList(); + SelectionList(const char*, uint8_t, const char*); + void draw(void) override; + void next(void) override; + void previous(void) override; + uint8_t select(void) override; + const char* title = "Cloud Types"; + private: + std::vector string_list = { + "Altocumulus\n", + "Altostratus\n", + "Cirrocumulus\n", + "Cirrostratus\n", + "Cirrus\n", + "Cumulonimbus\n", + "Cumulus\n", + "Nimbostratus\n", + "Stratocumulus\n", + "Stratus" }; + uint8_t current_pos = 0; + uint8_t top_item = 0; + uint8_t visible = 4; +}; + +class MainScreen : public Screen { + public: + void draw(void) override; + void next(void) override; + void previous(void) override; + uint8_t select(void) override; + const char* title = "Main Screen"; + private: + uint32_t lastButtonPress = 0; + uint32_t lastVolumeChange = 0; + u8g2_uint_t titleStr_offset = 0; + uint8_t volume = 20; +}; + +#endif /* _SCREEN_H */ diff --git a/src/selectionlist.cpp b/src/selectionlist.cpp deleted file mode 100644 index 9085fc4..0000000 --- a/src/selectionlist.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "selectionlist.h" - -SelectionList::SelectionList() {} - -SelectionList::SelectionList(const char* title, uint8_t start_pos, const char* string_list) {} - -void SelectionList::draw() { - uint8_t item_x_offset = 15; - uint8_t header_height = 12; - uint8_t item_height = 12; - uint8_t num_active_item = current_pos - top_item; - - u8g2.clearBuffer(); - - /* draw Title */ - u8g2.setFont(u8g2_font_guildenstern_nbp_tr); - u8g2.drawUTF8(2,header_height-2,title); - u8g2.drawLine(0,header_height,u8g2.getDisplayWidth(),header_height); - - /* draw menu items */ - u8g2.setFont(u8g2_font_9x18B_tr); - for (int i=0; i top_item+visible-1) top_item++; -} - -void SelectionList::previous() { - if (current_pos > 0 ) current_pos--; - if (current_pos < top_item) top_item--; -} - -uint8_t SelectionList::select() { - return current_pos; -} diff --git a/src/selectionlist.h b/src/selectionlist.h deleted file mode 100644 index de158df..0000000 --- a/src/selectionlist.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _SELECTIONLIST_H -#define _SELECTIONLIST_H - -#include -#include -#include - -#include -#ifdef U8X8_HAVE_HW_SPI -#include -#endif -#ifdef U8X8_HAVE_HW_I2C -#include -#endif - -extern U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2; - -class SelectionList { - public: - SelectionList(); - SelectionList(const char*, uint8_t, const char*); - void draw(void); - void next(void); - void previous(void); - uint8_t select(void); - private: - const char* title = "Cloud Types"; - std::vector string_list = { - "Altocumulus\n", - "Altostratus\n", - "Cirrocumulus\n", - "Cirrostratus\n", - "Cirrus\n", - "Cumulonimbus\n", - "Cumulus\n", - "Nimbostratus\n", - "Stratocumulus\n", - "Stratus" }; - uint8_t current_pos = 0; - uint8_t top_item = 0; - uint8_t visible = 4; -}; - -#endif /* _SELECTIONLIST_H */