4LineDisplay enhancements.

Boot loop (preset apply) fix.
This commit is contained in:
Blaz Kristan 2021-04-11 16:47:53 +02:00
parent a6feb77e52
commit 735205492e
2 changed files with 172 additions and 107 deletions

View File

@ -24,7 +24,10 @@
#endif #endif
class UsermodTemperature : public Usermod { class UsermodTemperature : public Usermod {
private: private:
bool initDone = false;
OneWire *oneWire; OneWire *oneWire;
DallasTemperature *sensor; DallasTemperature *sensor;
// The device's unique 64-bit serial code stored in on-board ROM. // The device's unique 64-bit serial code stored in on-board ROM.
@ -75,9 +78,7 @@ class UsermodTemperature : public Usermod {
public: public:
void setup() { void setup() {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup() // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (!pinManager.allocatePin(temperaturePin,false)) { if (!pinManager.allocatePin(temperaturePin,false)) {
temperaturePin = -1; // allocation failed temperaturePin = -1; // allocation failed
@ -103,6 +104,7 @@ class UsermodTemperature : public Usermod {
} else { } else {
DEBUG_PRINTLN(F("Dallas Temperature not found")); DEBUG_PRINTLN(F("Dallas Temperature not found"));
} }
initDone = true;
} }
void loop() { void loop() {
@ -123,7 +125,7 @@ class UsermodTemperature : public Usermod {
} }
// we were waiting for a conversion to complete, have we waited log enough? // 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(); getTemperature();
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
@ -187,6 +189,7 @@ class UsermodTemperature : public Usermod {
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used. * Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
*/ */
void readFromJsonState(JsonObject &root) { void readFromJsonState(JsonObject &root) {
if (!initDone) return; // prevent crash on boot applyPreset()
if (root[F("Temperature_pin")] != nullptr) { if (root[F("Temperature_pin")] != nullptr) {
int8_t pin = (int)root[F("Temperature_pin")]; int8_t pin = (int)root[F("Temperature_pin")];
if (pin != temperaturePin) { if (pin != temperaturePin) {

View File

@ -57,8 +57,8 @@ typedef enum {
FLD_LINE_3_BRIGHTNESS = 0, FLD_LINE_3_BRIGHTNESS = 0,
FLD_LINE_3_EFFECT_SPEED, FLD_LINE_3_EFFECT_SPEED,
FLD_LINE_3_EFFECT_INTENSITY, FLD_LINE_3_EFFECT_INTENSITY,
FLD_LINE_3_PALETTE, FLD_LINE_3_MODE,
FLD_LINE_3_TIME FLD_LINE_3_PALETTE
} Line3Type; } Line3Type;
typedef enum { typedef enum {
@ -68,20 +68,23 @@ typedef enum {
} DisplayType; } DisplayType;
class FourLineDisplayUsermod : public Usermod { class FourLineDisplayUsermod : public Usermod {
private: private:
bool initDone = false;
unsigned long lastTime = 0; unsigned long lastTime = 0;
// HW interface & configuration // HW interface & configuration
void *u8x8; // pointer to U8X8 display object void *u8x8 = nullptr; // pointer to U8X8 display object
int8_t sclPin, sdaPin; // I2C pins for interfacing, get initialised in readFromConfig() int8_t sclPin=FLD_PIN_SCL, sdaPin=FLD_PIN_SDA; // I2C pins for interfacing, get initialised in readFromConfig()
DisplayType type = NONE; // display type DisplayType type = SSD1306; // display type
bool flip; // flip display 180° bool flip = false; // flip display 180°
uint8_t contrast; uint8_t contrast = 10; // screen contrast
uint8_t lineHeight; // 1 row or 2 rows uint8_t lineHeight = 1; // 1 row or 2 rows
uint32_t refreshRate; // in ms uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint32_t screenTimeout; // in ms uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode; // allow screen sleep? bool sleepMode = true; // allow screen sleep?
bool clockMode; // display clock instead of info? bool clockMode = false; // display clock
// needRedraw marks if redraw is required to prevent often redrawing. // needRedraw marks if redraw is required to prevent often redrawing.
bool needRedraw = true; bool needRedraw = true;
@ -99,21 +102,19 @@ class FourLineDisplayUsermod : public Usermod {
uint8_t knownHour = 99; uint8_t knownHour = 99;
bool displayTurnedOff = false; bool displayTurnedOff = false;
long lastUpdate = 0; unsigned long lastUpdate = 0;
long lastRedraw = 0; unsigned long lastRedraw = 0;
long overlayUntil = 0; unsigned long overlayUntil = 0;
Line3Type lineThreeType = FLD_LINE_3_BRIGHTNESS; Line3Type lineThreeType = FLD_LINE_3_BRIGHTNESS;
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored. // Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
byte markLineNum = 0; byte markLineNum = 0;
char **modes_qstrings = nullptr;
char **palettes_qstrings = nullptr;
// If display does not work or looks corrupted check the // If display does not work or looks corrupted check the
// constructor reference: // constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp // https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery: // or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery // https://github.com/olikraus/u8g2/wiki/gallery
public: public:
// gets called once at boot. Do all initialization that doesn't depend on // gets called once at boot. Do all initialization that doesn't depend on
@ -126,27 +127,22 @@ class FourLineDisplayUsermod : public Usermod {
case SSD1306: case SSD1306:
u8x8 = (void *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (void *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->begin(); (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->begin();
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFlipMode(flip);
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
break; break;
case SH1106: case SH1106:
u8x8 = (void *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA u8x8 = (void *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->begin(); (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->begin();
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setFlipMode(flip);
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
break; break;
default: default:
u8x8 = nullptr; u8x8 = nullptr;
type = NONE; type = NONE;
return; 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); setPowerSave(0);
String loading = String(F("Loading...")); String loading = String(F("Loading..."));
drawString(0, 0, loading.c_str()); drawString(0, 0, loading.c_str());
initDone = true;
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
modes_qstrings = modeSortUsermod->getModesQStrings();
palettes_qstrings = modeSortUsermod->getPalettesQStrings();
} }
// gets called every time WiFi is (re-)connected. Initialize own network // gets called every time WiFi is (re-)connected. Initialize own network
@ -168,8 +164,31 @@ class FourLineDisplayUsermod : public Usermod {
/** /**
* Wrappers for screen drawing * Wrappers for screen drawing
*/ */
void setFlipMode(uint8_t mode) {
switch (type) {
case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFlipMode(mode);
break;
case SH1106:
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setFlipMode(mode);
break;
default:
return;
}
}
void setContrast(uint8_t contrast) {
switch (type) {
case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setContrast(contrast);
break;
case SH1106:
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setContrast(contrast);
break;
default:
return;
}
}
void drawString(uint8_t col, uint8_t row, const char *string) { void drawString(uint8_t col, uint8_t row, const char *string) {
if (type==NONE) return;
switch (type) { switch (type) {
case SSD1306: case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFont(u8x8_font_chroma48medium8_r); (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFont(u8x8_font_chroma48medium8_r);
@ -181,10 +200,11 @@ class FourLineDisplayUsermod : public Usermod {
if (lineHeight==2) (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->draw1x2String(col, row, string); if (lineHeight==2) (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->draw1x2String(col, row, string);
else (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->drawString(col, row, string); else (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->drawString(col, row, string);
break; break;
default:
return;
} }
} }
void draw2x2String(uint8_t col, uint8_t row, const char *string) { void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type==NONE) return;
switch (type) { switch (type) {
case SSD1306: case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFont(u8x8_font_chroma48medium8_r); (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFont(u8x8_font_chroma48medium8_r);
@ -194,10 +214,11 @@ class FourLineDisplayUsermod : public Usermod {
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setFont(u8x8_font_chroma48medium8_r); (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setFont(u8x8_font_chroma48medium8_r);
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->draw2x2String(col, row, string); (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->draw2x2String(col, row, string);
break; break;
default:
return;
} }
} }
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (type==NONE) return;
switch (type) { switch (type) {
case SSD1306: case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFont(font); (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setFont(font);
@ -209,19 +230,21 @@ class FourLineDisplayUsermod : public Usermod {
if (lineHeight==2) (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->draw1x2Glyph(col, row, glyph); if (lineHeight==2) (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->draw1x2Glyph(col, row, glyph);
else (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->drawGlyph(col, row, glyph); else (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->drawGlyph(col, row, glyph);
break; break;
default:
return;
} }
} }
uint8_t getCols() { uint8_t getCols() {
if (type==NONE) return 255;
switch (type) { switch (type) {
case SSD1306: case SSD1306:
return (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->getCols(); return (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->getCols();
case SH1106: case SH1106:
return (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->getCols(); return (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->getCols();
default:
return 0;
} }
} }
void clear() { void clear() {
if (type==NONE) return;
switch (type) { switch (type) {
case SSD1306: case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->clear(); (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->clear();
@ -229,10 +252,11 @@ class FourLineDisplayUsermod : public Usermod {
case SH1106: case SH1106:
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->clear(); (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->clear();
break; break;
default:
return;
} }
} }
void setPowerSave(uint8_t save) { void setPowerSave(uint8_t save) {
if (type==NONE) return;
switch (type) { switch (type) {
case SSD1306: case SSD1306:
(static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setPowerSave(save); (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8))->setPowerSave(save);
@ -240,6 +264,8 @@ class FourLineDisplayUsermod : public Usermod {
case SH1106: case SH1106:
(static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setPowerSave(save); (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8))->setPowerSave(save);
break; break;
default:
return;
} }
} }
@ -287,6 +313,7 @@ class FourLineDisplayUsermod : public Usermod {
if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) { if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
// We will still check if there is a change in redraw() // We will still check if there is a change in redraw()
// and turn it back on if it changed. // and turn it back on if it changed.
knownHour = 99; // force screen clear
sleepOrClock(true); sleepOrClock(true);
} else if (displayTurnedOff && clockMode) { } else if (displayTurnedOff && clockMode) {
showTime(); showTime();
@ -294,26 +321,30 @@ class FourLineDisplayUsermod : public Usermod {
// change 4th line every 3s // change 4th line every 3s
switch (lineThreeType) { switch (lineThreeType) {
case FLD_LINE_3_BRIGHTNESS: 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; break;
case FLD_LINE_3_PALETTE: case FLD_LINE_3_PALETTE:
setLineThreeType(FLD_LINE_3_EFFECT_SPEED); setLineThreeType(clockMode ? FLD_LINE_3_MODE : FLD_LINE_3_BRIGHTNESS);
break; break;
case FLD_LINE_3_EFFECT_SPEED: case FLD_LINE_3_EFFECT_SPEED:
setLineThreeType(FLD_LINE_3_EFFECT_INTENSITY); setLineThreeType(FLD_LINE_3_EFFECT_INTENSITY);
break; break;
case FLD_LINE_3_EFFECT_INTENSITY: case FLD_LINE_3_EFFECT_INTENSITY:
setLineThreeType(FLD_LINE_3_BRIGHTNESS); setLineThreeType(FLD_LINE_3_PALETTE);
break; break;
default: default:
setLineThreeType(FLD_LINE_3_BRIGHTNESS);
break; break;
} }
drawLineThree(); drawLineFour();
} }
return; return;
} else } else {
knownHour = 99; // force time display
clear(); clear();
}
needRedraw = false; needRedraw = false;
lastRedraw = millis(); lastRedraw = millis();
@ -354,11 +385,12 @@ class FourLineDisplayUsermod : public Usermod {
drawString(1, lineHeight, (knownIp.toString()).c_str()); drawString(1, lineHeight, (knownIp.toString()).c_str());
} }
// Third row with mode name // Third row with mode name or current time
showCurrentEffectOrPalette(modes_qstrings[knownMode], 2); if (clockMode) showTime(false);
else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 2);
// Fourth row // Fourth row
drawLineThree(); drawLineFour();
drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon 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 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 //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]; char lineBuffer[LINE_BUFFER_SIZE];
switch(lineThreeType) { switch(lineThreeType) {
case FLD_LINE_3_BRIGHTNESS: case FLD_LINE_3_BRIGHTNESS:
@ -381,9 +413,12 @@ class FourLineDisplayUsermod : public Usermod {
sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity); sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity);
drawString(2, 3*lineHeight, lineBuffer); drawString(2, 3*lineHeight, lineBuffer);
break; break;
case FLD_LINE_3_MODE:
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3);
break;
case FLD_LINE_3_PALETTE: case FLD_LINE_3_PALETTE:
default: default:
showCurrentEffectOrPalette(palettes_qstrings[knownPalette], 3); showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 3);
break; break;
} }
} }
@ -391,23 +426,34 @@ class FourLineDisplayUsermod : public Usermod {
/** /**
* Display the current effect or palette (desiredEntry) * Display the current effect or palette (desiredEntry)
* on the appropriate line (row). * 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]; char lineBuffer[LINE_BUFFER_SIZE];
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0; uint8_t printedChars = 0;
char singleJsonSymbol; char singleJsonSymbol;
while (printedChars < getCols() - 2) {
singleJsonSymbol = pgm_read_byte_near(qstring + printedChars); // Find the mode name in JSON
if (singleJsonSymbol == '"' || singleJsonSymbol == '\0' ) { for (size_t i = 0; i < strlen_P(qstring); i++) {
break; 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<getCols()-2; printedChars++) lineBuffer[printedChars]=' '; for (;printedChars < getCols()-2 || printedChars > sizeof(lineBuffer)-2; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0; lineBuffer[printedChars] = 0;
drawString(2, row*lineHeight, lineBuffer); drawString(2, row*lineHeight, lineBuffer);
} }
@ -419,6 +465,7 @@ class FourLineDisplayUsermod : public Usermod {
* to wake up the screen. * to wake up the screen.
*/ */
bool wakeDisplay() { bool wakeDisplay() {
knownHour = 99;
if (displayTurnedOff) { if (displayTurnedOff) {
// Turn the display back on // Turn the display back on
sleepOrClock(false); sleepOrClock(false);
@ -454,8 +501,8 @@ class FourLineDisplayUsermod : public Usermod {
if (newLineThreeType == FLD_LINE_3_BRIGHTNESS || if (newLineThreeType == FLD_LINE_3_BRIGHTNESS ||
newLineThreeType == FLD_LINE_3_EFFECT_SPEED || newLineThreeType == FLD_LINE_3_EFFECT_SPEED ||
newLineThreeType == FLD_LINE_3_EFFECT_INTENSITY || newLineThreeType == FLD_LINE_3_EFFECT_INTENSITY ||
newLineThreeType == FLD_LINE_3_PALETTE || newLineThreeType == FLD_LINE_3_MODE ||
newLineThreeType == FLD_LINE_3_TIME) { newLineThreeType == FLD_LINE_3_PALETTE) {
lineThreeType = newLineThreeType; lineThreeType = newLineThreeType;
} else { } else {
// Unknown value // Unknown value
@ -483,18 +530,12 @@ class FourLineDisplayUsermod : public Usermod {
*/ */
void sleepOrClock(bool enabled) { void sleepOrClock(bool enabled) {
if (enabled) { if (enabled) {
if (clockMode) { if (clockMode) showTime();
showTime(); else setPowerSave(1);
}
else {
setPowerSave(1);
}
displayTurnedOff = true; displayTurnedOff = true;
} }
else { else {
if (!clockMode) { setPowerSave(0);
setPowerSave(0);
}
displayTurnedOff = false; displayTurnedOff = false;
} }
} }
@ -510,21 +551,22 @@ class FourLineDisplayUsermod : public Usermod {
updateLocalTime(); updateLocalTime();
byte minuteCurrent = minute(localTime); byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime); byte hourCurrent = hour(localTime);
byte secondCurrent = second(localTime);
if (knownMinute == minuteCurrent && knownHour == hourCurrent) { if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
// Time hasn't changed. // Time hasn't changed.
return; if (!fullScreen) return;
} else {
if (fullScreen) clear();
} }
knownMinute = minuteCurrent; knownMinute = minuteCurrent;
knownHour = hourCurrent; knownHour = hourCurrent;
if (fullScreen) clear();
int currentMonth = month(localTime); int currentMonth = month(localTime);
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime)); sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
if (fullScreen) if (fullScreen)
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer);
else else
drawString(0, lineHeight, lineBuffer); drawString(2, lineHeight*2, lineBuffer);
byte showHour = hourCurrent; byte showHour = hourCurrent;
boolean isAM = false; 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 // For time, we always use LINE_HEIGHT of 2 since
// we are printing it big. // we are printing it big.
if (fullScreen) if (fullScreen) {
draw2x2String(TIME_INDENT + (useAMPM ? 0 : 2), lineHeight*2, lineBuffer); draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer);
else if (useAMPM) drawString(12, lineHeight*2, (isAM ? "AM" : "PM"));
drawString(8, lineHeight, lineBuffer); 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 * Values in the state object may be modified by connected clients
*/ */
void readFromJsonState(JsonObject& root) { void readFromJsonState(JsonObject& root) {
if (!initDone) return; // prevent crash on boot applyPreset()
DisplayType newType = type; DisplayType newType = type;
uint8_t newScl = sclPin; uint8_t newScl = sclPin;
uint8_t newSda = sdaPin; uint8_t newSda = sdaPin;
uint8_t newContrast = contrast;
bool newFlip = flip;
if (root[F("4LineDisplay_type")] != nullptr) newType = (DisplayType)root[F("4LineDisplay_type")]; // just to reduce memory
if (root[F("4LineDisplay_pin")] != nullptr) { String str4LineDisplay_type = String(F("4LineDisplay_type"));
newScl = (int)root[F("4LineDisplay_pin")][0]; String str4LineDisplay_pin = String(F("4LineDisplay_pin"));
newSda = (int)root[F("4LineDisplay_pin")][1]; 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[str4LineDisplay_contrast] != nullptr) {
if (root[F("4LineDisplay_refreshRate")] != nullptr) refreshRate = (int)root[F("4LineDisplay_refreshRate")]*1000; contrast = (int)root[str4LineDisplay_contrast];
if (root[F("4LineDisplay_screenTimeOut")] != nullptr) screenTimeout = (int)root[F("4LineDisplay_screenTimeOut")]*1000; setContrast(contrast);
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[F("4LineDisplay_sleepMode")] != nullptr) { if (root[str4LineDisplay_refreshRate] != nullptr) refreshRate = (int)root[str4LineDisplay_refreshRate]*1000;
String str = root[F("4LineDisplay_sleepMode")]; // checkbox -> off or on 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 sleepMode = (bool)(str!="off"); // off is guaranteed to be present
} }
if (root[F("4LineDisplay_clockMode")] != nullptr) { if (root[str4LineDisplay_clockMode] != nullptr) {
String str = root[F("4LineDisplay_clockMode")]; // checkbox -> off or on String str = root[str4LineDisplay_clockMode]; // checkbox -> off or on
clockMode = (bool)(str!="off"); // off is guaranteed to be present 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_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8)); if (type==SSD1306) delete (static_cast<U8X8_SSD1306_128X32_UNIVISION_HW_I2C*>(u8x8));
if (type==SH1106) delete (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8)); if (type==SH1106) delete (static_cast<U8X8_SH1106_128X64_WINSTAR_HW_I2C*>(u8x8));
pinManager.deallocatePin(sclPin); pinManager.deallocatePin(sclPin);
@ -610,11 +670,10 @@ class FourLineDisplayUsermod : public Usermod {
sclPin = newScl; sclPin = newScl;
sdaPin = newSda; sdaPin = newSda;
type = newType; type = newType;
contrast = newContrast;
flip = newFlip;
lineHeight = type==SH1106 ? 2 : 1; lineHeight = type==SH1106 ? 2 : 1;
setup(); setup();
} }
if (!wakeDisplay()) redraw(true);
} }
/* /*
@ -655,16 +714,20 @@ class FourLineDisplayUsermod : public Usermod {
*/ */
void readFromConfig(JsonObject& root) { void readFromConfig(JsonObject& root) {
JsonObject top = root[F("4LineDisplay")]; JsonObject top = root[F("4LineDisplay")];
sclPin = top["pin"][0] | FLD_PIN_SCL; if (!top.isNull() && top["pin"] != nullptr) {
sdaPin = top["pin"][1] | FLD_PIN_SDA; sclPin = top["pin"][0] | FLD_PIN_SCL;
type = top["type"] | SSD1306; sdaPin = top["pin"][1] | FLD_PIN_SDA;
lineHeight = type==SH1106 ? 2 : 1; type = top["type"] | SSD1306;
flip = top[F("flip")] | false ; lineHeight = type==SH1106 ? 2 : 1;
contrast = top[F("contrast")] | 10; flip = top[F("flip")] | false ;
refreshRate = int(top[F("refreshRate")])*1000 | USER_LOOP_REFRESH_RATE_MS; contrast = top[F("contrast")] | 10;
screenTimeout = int(top[F("screenTimeOut")])*1000 | SCREEN_TIMEOUT_MS; refreshRate = int(top[F("refreshRate")])*1000 | USER_LOOP_REFRESH_RATE_MS;
sleepMode = top[F("sleepMode")] | true; screenTimeout = int(top[F("screenTimeOut")])*1000 | SCREEN_TIMEOUT_MS;
clockMode = top[F("clockMode")] | false; 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() { uint16_t getId() {
return USERMOD_ID_FOUR_LINE_DISP; return USERMOD_ID_FOUR_LINE_DISP;
} }
}; };