diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index 2ca26dd4..34b9196b 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -24,7 +24,10 @@ #endif class UsermodTemperature : public Usermod { + private: + + bool initDone = false; OneWire *oneWire; DallasTemperature *sensor; // The device's unique 64-bit serial code stored in on-board ROM. @@ -75,9 +78,7 @@ class UsermodTemperature : public Usermod { public: - void setup() { - // pin retrieved from cfg.json (readFromConfig()) prior to running setup() if (!pinManager.allocatePin(temperaturePin,false)) { temperaturePin = -1; // allocation failed @@ -103,6 +104,7 @@ class UsermodTemperature : public Usermod { } else { DEBUG_PRINTLN(F("Dallas Temperature not found")); } + initDone = true; } void loop() { @@ -123,7 +125,7 @@ class UsermodTemperature : public Usermod { } // we were waiting for a conversion to complete, have we waited log enough? - if (now - lastTemperaturesRequest >= 94 /* 93.75ms per the datasheet */) { + if (now - lastTemperaturesRequest >= 95 /* 93.75ms per the datasheet */) { getTemperature(); if (WLED_MQTT_CONNECTED) { @@ -187,6 +189,7 @@ class UsermodTemperature : public Usermod { * Read "_" from json state and and change settings (i.e. GPIO pin) used. */ void readFromJsonState(JsonObject &root) { + if (!initDone) return; // prevent crash on boot applyPreset() if (root[F("Temperature_pin")] != nullptr) { int8_t pin = (int)root[F("Temperature_pin")]; if (pin != temperaturePin) { diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index ac1584f7..ba166355 100644 --- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h +++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h @@ -57,8 +57,8 @@ typedef enum { FLD_LINE_3_BRIGHTNESS = 0, FLD_LINE_3_EFFECT_SPEED, FLD_LINE_3_EFFECT_INTENSITY, - FLD_LINE_3_PALETTE, - FLD_LINE_3_TIME + FLD_LINE_3_MODE, + FLD_LINE_3_PALETTE } Line3Type; typedef enum { @@ -68,20 +68,23 @@ typedef enum { } DisplayType; class FourLineDisplayUsermod : public Usermod { + private: + + bool initDone = false; unsigned long lastTime = 0; // HW interface & configuration - void *u8x8; // pointer to U8X8 display object - int8_t sclPin, sdaPin; // I2C pins for interfacing, get initialised in readFromConfig() - DisplayType type = NONE; // display type - bool flip; // flip display 180° - uint8_t contrast; - uint8_t lineHeight; // 1 row or 2 rows - uint32_t refreshRate; // in ms - uint32_t screenTimeout; // in ms - bool sleepMode; // allow screen sleep? - bool clockMode; // display clock instead of info? + void *u8x8 = nullptr; // pointer to U8X8 display object + int8_t sclPin=FLD_PIN_SCL, sdaPin=FLD_PIN_SDA; // I2C pins for interfacing, get initialised in readFromConfig() + DisplayType type = SSD1306; // display type + bool flip = false; // flip display 180° + uint8_t contrast = 10; // screen contrast + uint8_t lineHeight = 1; // 1 row or 2 rows + uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms + uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms + bool sleepMode = true; // allow screen sleep? + bool clockMode = false; // display clock // needRedraw marks if redraw is required to prevent often redrawing. bool needRedraw = true; @@ -99,21 +102,19 @@ class FourLineDisplayUsermod : public Usermod { uint8_t knownHour = 99; bool displayTurnedOff = false; - long lastUpdate = 0; - long lastRedraw = 0; - long overlayUntil = 0; + unsigned long lastUpdate = 0; + unsigned long lastRedraw = 0; + unsigned long overlayUntil = 0; Line3Type lineThreeType = FLD_LINE_3_BRIGHTNESS; // Set to 2 or 3 to mark lines 2 or 3. Other values ignored. byte markLineNum = 0; - char **modes_qstrings = nullptr; - char **palettes_qstrings = nullptr; - // If display does not work or looks corrupted check the // constructor reference: // https://github.com/olikraus/u8g2/wiki/u8x8setupcpp // or check the gallery: // https://github.com/olikraus/u8g2/wiki/gallery + public: // gets called once at boot. Do all initialization that doesn't depend on @@ -126,27 +127,22 @@ class FourLineDisplayUsermod : public Usermod { case SSD1306: u8x8 = (void *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA (static_cast(u8x8))->begin(); - (static_cast(u8x8))->setFlipMode(flip); - (static_cast(u8x8))->setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 break; case SH1106: u8x8 = (void *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA (static_cast(u8x8))->begin(); - (static_cast(u8x8))->setFlipMode(flip); - (static_cast(u8x8))->setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 break; default: u8x8 = nullptr; type = NONE; return; } + setFlipMode(flip); + setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setPowerSave(0); String loading = String(F("Loading...")); drawString(0, 0, loading.c_str()); - - ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT); - modes_qstrings = modeSortUsermod->getModesQStrings(); - palettes_qstrings = modeSortUsermod->getPalettesQStrings(); + initDone = true; } // gets called every time WiFi is (re-)connected. Initialize own network @@ -168,8 +164,31 @@ class FourLineDisplayUsermod : public Usermod { /** * Wrappers for screen drawing */ + void setFlipMode(uint8_t mode) { + switch (type) { + case SSD1306: + (static_cast(u8x8))->setFlipMode(mode); + break; + case SH1106: + (static_cast(u8x8))->setFlipMode(mode); + break; + default: + return; + } + } + void setContrast(uint8_t contrast) { + switch (type) { + case SSD1306: + (static_cast(u8x8))->setContrast(contrast); + break; + case SH1106: + (static_cast(u8x8))->setContrast(contrast); + break; + default: + return; + } + } void drawString(uint8_t col, uint8_t row, const char *string) { - if (type==NONE) return; switch (type) { case SSD1306: (static_cast(u8x8))->setFont(u8x8_font_chroma48medium8_r); @@ -181,10 +200,11 @@ class FourLineDisplayUsermod : public Usermod { if (lineHeight==2) (static_cast(u8x8))->draw1x2String(col, row, string); else (static_cast(u8x8))->drawString(col, row, string); break; + default: + return; } } void draw2x2String(uint8_t col, uint8_t row, const char *string) { - if (type==NONE) return; switch (type) { case SSD1306: (static_cast(u8x8))->setFont(u8x8_font_chroma48medium8_r); @@ -194,10 +214,11 @@ class FourLineDisplayUsermod : public Usermod { (static_cast(u8x8))->setFont(u8x8_font_chroma48medium8_r); (static_cast(u8x8))->draw2x2String(col, row, string); break; + default: + return; } } void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { - if (type==NONE) return; switch (type) { case SSD1306: (static_cast(u8x8))->setFont(font); @@ -209,19 +230,21 @@ class FourLineDisplayUsermod : public Usermod { if (lineHeight==2) (static_cast(u8x8))->draw1x2Glyph(col, row, glyph); else (static_cast(u8x8))->drawGlyph(col, row, glyph); break; + default: + return; } } uint8_t getCols() { - if (type==NONE) return 255; switch (type) { case SSD1306: return (static_cast(u8x8))->getCols(); case SH1106: return (static_cast(u8x8))->getCols(); + default: + return 0; } } void clear() { - if (type==NONE) return; switch (type) { case SSD1306: (static_cast(u8x8))->clear(); @@ -229,10 +252,11 @@ class FourLineDisplayUsermod : public Usermod { case SH1106: (static_cast(u8x8))->clear(); break; + default: + return; } } void setPowerSave(uint8_t save) { - if (type==NONE) return; switch (type) { case SSD1306: (static_cast(u8x8))->setPowerSave(save); @@ -240,6 +264,8 @@ class FourLineDisplayUsermod : public Usermod { case SH1106: (static_cast(u8x8))->setPowerSave(save); break; + default: + return; } } @@ -287,6 +313,7 @@ class FourLineDisplayUsermod : public Usermod { if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) { // We will still check if there is a change in redraw() // and turn it back on if it changed. + knownHour = 99; // force screen clear sleepOrClock(true); } else if (displayTurnedOff && clockMode) { showTime(); @@ -294,26 +321,30 @@ class FourLineDisplayUsermod : public Usermod { // change 4th line every 3s switch (lineThreeType) { case FLD_LINE_3_BRIGHTNESS: - setLineThreeType(FLD_LINE_3_PALETTE); + setLineThreeType(FLD_LINE_3_EFFECT_SPEED); + break; + case FLD_LINE_3_MODE: + setLineThreeType(FLD_LINE_3_BRIGHTNESS); break; case FLD_LINE_3_PALETTE: - setLineThreeType(FLD_LINE_3_EFFECT_SPEED); + setLineThreeType(clockMode ? FLD_LINE_3_MODE : FLD_LINE_3_BRIGHTNESS); break; case FLD_LINE_3_EFFECT_SPEED: setLineThreeType(FLD_LINE_3_EFFECT_INTENSITY); break; case FLD_LINE_3_EFFECT_INTENSITY: - setLineThreeType(FLD_LINE_3_BRIGHTNESS); + setLineThreeType(FLD_LINE_3_PALETTE); break; default: - setLineThreeType(FLD_LINE_3_BRIGHTNESS); break; } - drawLineThree(); + drawLineFour(); } return; - } else + } else { + knownHour = 99; // force time display clear(); + } needRedraw = false; lastRedraw = millis(); @@ -354,11 +385,12 @@ class FourLineDisplayUsermod : public Usermod { drawString(1, lineHeight, (knownIp.toString()).c_str()); } - // Third row with mode name - showCurrentEffectOrPalette(modes_qstrings[knownMode], 2); + // Third row with mode name or current time + if (clockMode) showTime(false); + else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 2); // Fourth row - drawLineThree(); + drawLineFour(); drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // wifi icon @@ -366,7 +398,7 @@ class FourLineDisplayUsermod : public Usermod { //if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon } - void drawLineThree() { + void drawLineFour() { char lineBuffer[LINE_BUFFER_SIZE]; switch(lineThreeType) { case FLD_LINE_3_BRIGHTNESS: @@ -381,9 +413,12 @@ class FourLineDisplayUsermod : public Usermod { sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity); drawString(2, 3*lineHeight, lineBuffer); break; + case FLD_LINE_3_MODE: + showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); + break; case FLD_LINE_3_PALETTE: default: - showCurrentEffectOrPalette(palettes_qstrings[knownPalette], 3); + showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 3); break; } } @@ -391,23 +426,34 @@ class FourLineDisplayUsermod : public Usermod { /** * Display the current effect or palette (desiredEntry) * on the appropriate line (row). - * - * TODO: Should we cache the current effect and - * TODO: palette name? This seems expensive. */ - void showCurrentEffectOrPalette(char *qstring, uint8_t row) { + void showCurrentEffectOrPalette(int knownMode, const char *qstring, uint8_t row) { char lineBuffer[LINE_BUFFER_SIZE]; - + uint8_t qComma = 0; + bool insideQuotes = false; uint8_t printedChars = 0; char singleJsonSymbol; - while (printedChars < getCols() - 2) { - singleJsonSymbol = pgm_read_byte_near(qstring + printedChars); - if (singleJsonSymbol == '"' || singleJsonSymbol == '\0' ) { - break; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(qstring); i++) { + singleJsonSymbol = pgm_read_byte_near(qstring + i); + if (singleJsonSymbol == '\0') break; + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) break; + lineBuffer[printedChars++] = singleJsonSymbol; } - lineBuffer[printedChars++] = singleJsonSymbol; + if ((qComma > knownMode) || (printedChars > getCols()-2) || printedChars > sizeof(lineBuffer)-2) break; } - for (;printedChars sizeof(lineBuffer)-2; printedChars++) lineBuffer[printedChars]=' '; lineBuffer[printedChars] = 0; drawString(2, row*lineHeight, lineBuffer); } @@ -419,6 +465,7 @@ class FourLineDisplayUsermod : public Usermod { * to wake up the screen. */ bool wakeDisplay() { + knownHour = 99; if (displayTurnedOff) { // Turn the display back on sleepOrClock(false); @@ -454,8 +501,8 @@ class FourLineDisplayUsermod : public Usermod { if (newLineThreeType == FLD_LINE_3_BRIGHTNESS || newLineThreeType == FLD_LINE_3_EFFECT_SPEED || newLineThreeType == FLD_LINE_3_EFFECT_INTENSITY || - newLineThreeType == FLD_LINE_3_PALETTE || - newLineThreeType == FLD_LINE_3_TIME) { + newLineThreeType == FLD_LINE_3_MODE || + newLineThreeType == FLD_LINE_3_PALETTE) { lineThreeType = newLineThreeType; } else { // Unknown value @@ -483,18 +530,12 @@ class FourLineDisplayUsermod : public Usermod { */ void sleepOrClock(bool enabled) { if (enabled) { - if (clockMode) { - showTime(); - } - else { - setPowerSave(1); - } + if (clockMode) showTime(); + else setPowerSave(1); displayTurnedOff = true; } else { - if (!clockMode) { - setPowerSave(0); - } + setPowerSave(0); displayTurnedOff = false; } } @@ -510,21 +551,22 @@ class FourLineDisplayUsermod : public Usermod { updateLocalTime(); byte minuteCurrent = minute(localTime); byte hourCurrent = hour(localTime); + byte secondCurrent = second(localTime); if (knownMinute == minuteCurrent && knownHour == hourCurrent) { // Time hasn't changed. - return; + if (!fullScreen) return; + } else { + if (fullScreen) clear(); } knownMinute = minuteCurrent; knownHour = hourCurrent; - if (fullScreen) clear(); - int currentMonth = month(localTime); sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime)); if (fullScreen) draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); else - drawString(0, lineHeight, lineBuffer); + drawString(2, lineHeight*2, lineBuffer); byte showHour = hourCurrent; boolean isAM = false; @@ -542,13 +584,17 @@ class FourLineDisplayUsermod : public Usermod { } } - sprintf_P(lineBuffer, PSTR("%02d:%02d %s"), showHour, minuteCurrent, useAMPM ? (isAM ? "AM" : "PM") : ""); + sprintf_P(lineBuffer, PSTR("%2d:%02d"), (useAMPM && fullScreen ? showHour : hourCurrent), minuteCurrent); // For time, we always use LINE_HEIGHT of 2 since // we are printing it big. - if (fullScreen) - draw2x2String(TIME_INDENT + (useAMPM ? 0 : 2), lineHeight*2, lineBuffer); - else - drawString(8, lineHeight, lineBuffer); + if (fullScreen) { + draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer); + if (useAMPM) drawString(12, lineHeight*2, (isAM ? "AM" : "PM")); + sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); + drawString(12, lineHeight*2+1, lineBuffer); + } else { + drawString(9, lineHeight*2, lineBuffer); + } } /* @@ -575,34 +621,48 @@ class FourLineDisplayUsermod : public Usermod { * Values in the state object may be modified by connected clients */ void readFromJsonState(JsonObject& root) { + if (!initDone) return; // prevent crash on boot applyPreset() DisplayType newType = type; uint8_t newScl = sclPin; uint8_t newSda = sdaPin; - uint8_t newContrast = contrast; - bool newFlip = flip; - if (root[F("4LineDisplay_type")] != nullptr) newType = (DisplayType)root[F("4LineDisplay_type")]; - if (root[F("4LineDisplay_pin")] != nullptr) { - newScl = (int)root[F("4LineDisplay_pin")][0]; - newSda = (int)root[F("4LineDisplay_pin")][1]; + // just to reduce memory + String str4LineDisplay_type = String(F("4LineDisplay_type")); + String str4LineDisplay_pin = String(F("4LineDisplay_pin")); + String str4LineDisplay_contrast = String(F("4LineDisplay_contrast")); + String str4LineDisplay_refreshRate = String(F("4LineDisplay_refreshRate")); + String str4LineDisplay_screenTimeOut = String(F("4LineDisplay_screenTimeOut")); + String str4LineDisplay_flip = String(F("4LineDisplay_flip")); + String str4LineDisplay_sleepMode = String(F("4LineDisplay_sleepMode")); + String str4LineDisplay_clockMode = String(F("4LineDisplay_clockMode")); + + if (root[str4LineDisplay_type] != nullptr) newType = (DisplayType)root[str4LineDisplay_type]; + if (root[str4LineDisplay_pin] != nullptr) { + newScl = (int)root[str4LineDisplay_pin][0]; + newSda = (int)root[str4LineDisplay_pin][1]; } - if (root[F("4LineDisplay_contrast")] != nullptr) newContrast = (int)root[F("4LineDisplay_contrast")]; - if (root[F("4LineDisplay_refreshRate")] != nullptr) refreshRate = (int)root[F("4LineDisplay_refreshRate")]*1000; - if (root[F("4LineDisplay_screenTimeOut")] != nullptr) screenTimeout = (int)root[F("4LineDisplay_screenTimeOut")]*1000; - if (root[F("4LineDisplay_flip")] != nullptr) { - String str = root[F("4LineDisplay_flip")]; // checkbox -> off or on - newFlip = (bool)(str!="off"); // off is guaranteed to be present + if (root[str4LineDisplay_contrast] != nullptr) { + contrast = (int)root[str4LineDisplay_contrast]; + setContrast(contrast); } - if (root[F("4LineDisplay_sleepMode")] != nullptr) { - String str = root[F("4LineDisplay_sleepMode")]; // checkbox -> off or on + if (root[str4LineDisplay_refreshRate] != nullptr) refreshRate = (int)root[str4LineDisplay_refreshRate]*1000; + if (root[str4LineDisplay_screenTimeOut] != nullptr) screenTimeout = (int)root[str4LineDisplay_screenTimeOut]*1000; + if (root[str4LineDisplay_flip] != nullptr) { + String str = root[str4LineDisplay_flip]; // checkbox -> off or on + flip = (bool)(str!="off"); // off is guaranteed to be present + setFlipMode(flip); + } + if (root[str4LineDisplay_sleepMode] != nullptr) { + String str = root[str4LineDisplay_sleepMode]; // checkbox -> off or on sleepMode = (bool)(str!="off"); // off is guaranteed to be present } - if (root[F("4LineDisplay_clockMode")] != nullptr) { - String str = root[F("4LineDisplay_clockMode")]; // checkbox -> off or on + if (root[str4LineDisplay_clockMode] != nullptr) { + String str = root[str4LineDisplay_clockMode]; // checkbox -> off or on clockMode = (bool)(str!="off"); // off is guaranteed to be present + setLineThreeType(clockMode ? FLD_LINE_3_MODE : FLD_LINE_3_BRIGHTNESS); } - if (flip!=newFlip || contrast!=newContrast || sclPin!=newScl || sdaPin!=newSda || type!=newType) { + if (sclPin!=newScl || sdaPin!=newSda || type!=newType) { if (type==SSD1306) delete (static_cast(u8x8)); if (type==SH1106) delete (static_cast(u8x8)); pinManager.deallocatePin(sclPin); @@ -610,11 +670,10 @@ class FourLineDisplayUsermod : public Usermod { sclPin = newScl; sdaPin = newSda; type = newType; - contrast = newContrast; - flip = newFlip; lineHeight = type==SH1106 ? 2 : 1; setup(); } + if (!wakeDisplay()) redraw(true); } /* @@ -655,16 +714,20 @@ class FourLineDisplayUsermod : public Usermod { */ void readFromConfig(JsonObject& root) { JsonObject top = root[F("4LineDisplay")]; - sclPin = top["pin"][0] | FLD_PIN_SCL; - sdaPin = top["pin"][1] | FLD_PIN_SDA; - type = top["type"] | SSD1306; - lineHeight = type==SH1106 ? 2 : 1; - flip = top[F("flip")] | false ; - contrast = top[F("contrast")] | 10; - refreshRate = int(top[F("refreshRate")])*1000 | USER_LOOP_REFRESH_RATE_MS; - screenTimeout = int(top[F("screenTimeOut")])*1000 | SCREEN_TIMEOUT_MS; - sleepMode = top[F("sleepMode")] | true; - clockMode = top[F("clockMode")] | false; + if (!top.isNull() && top["pin"] != nullptr) { + sclPin = top["pin"][0] | FLD_PIN_SCL; + sdaPin = top["pin"][1] | FLD_PIN_SDA; + type = top["type"] | SSD1306; + lineHeight = type==SH1106 ? 2 : 1; + flip = top[F("flip")] | false ; + contrast = top[F("contrast")] | 10; + refreshRate = int(top[F("refreshRate")])*1000 | USER_LOOP_REFRESH_RATE_MS; + screenTimeout = int(top[F("screenTimeOut")])*1000 | SCREEN_TIMEOUT_MS; + sleepMode = top[F("sleepMode")] | true; + clockMode = top[F("clockMode")] | false; + } else { + DEBUG_PRINTLN(F("No config found. (Using defaults.)")); + } } /* @@ -674,5 +737,4 @@ class FourLineDisplayUsermod : public Usermod { uint16_t getId() { return USERMOD_ID_FOUR_LINE_DISP; } - }; \ No newline at end of file