Prepare for upstream merge: seperate declarations from definitions

In rotary and 4ld
This commit is contained in:
Ewoud
2023-06-27 22:21:20 +02:00
parent d53de3d82b
commit 17147f3b50
2 changed files with 2098 additions and 1788 deletions

View File

@@ -120,23 +120,6 @@ class FourLineDisplayUsermod : public Usermod {
// HW interface & configuration // HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
// semaphores - needed on ESP32 only, as we use a separate task to update the display
SemaphoreHandle_t drawMux = xSemaphoreCreateBinary(); // for drawstring and drawglyph functions (to prevent concurrent access to HW)
SemaphoreHandle_t drawMuxBig = xSemaphoreCreateBinary(); // for draw2x2GlyphIcons() and showCurrentEffectOrPalette() - more complex and not thread-safe
const TickType_t maxWait = 300 * portTICK_PERIOD_MS; // wait max. 300ms (drawstring semaphore)
const TickType_t maxWaitLong = 800 * portTICK_PERIOD_MS; // wait max. 800ms (big drawing semaphore)
#define FLD_SemaphoreTake(x,t) xSemaphoreTake((x),(t))
#define FLD_SemaphoreGive(x) xSemaphoreGive(x)
#else
// 8266 or no tasks - no semaphores
#define FLD_SemaphoreTake(x,t) pdTRUE
#define FLD_SemaphoreGive(x)
#if !defined(ARDUINO_ARCH_ESP32) && !defined(pdTRUE)
#define pdTRUE true
#endif
#endif
#ifndef FLD_SPI_DEFAULT #ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000) uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
@@ -214,7 +197,7 @@ class FourLineDisplayUsermod : public Usermod {
// or check the gallery: // or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery // https://github.com/olikraus/u8g2/wiki/gallery
// is this display using SPI? // WLEDMM is this display using SPI?
bool displayIsSPI(DisplayType disp) { bool displayIsSPI(DisplayType disp) {
switch(disp) { switch(disp) {
case SSD1306_SPI: // falls thru case SSD1306_SPI: // falls thru
@@ -229,7 +212,231 @@ class FourLineDisplayUsermod : public Usermod {
} }
// some displays need this to properly apply contrast // some displays need this to properly apply contrast
void setVcomh(bool highContrast) { void setVcomh(bool highContrast);
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode);
void setContrast(uint8_t contrast);
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false);
void draw2x2String(uint8_t col, uint8_t row, const char *string);
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false);
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font);
uint8_t getCols();
void clear();
void setPowerSave(uint8_t save);
void center(String &line, uint8_t width);
void draw2x2GlyphIcons();
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime();
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup();
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected();
/**
* Da loop.
*/
void loop();
//function to update lastredraw
inline void updateRedrawTime() {lastRedraw = millis(); }
//function to to check if a redraw or overlay draw is active. Needed for UM Rotary, to avoid random/concurrent drawing
bool canDraw(void);
/**
* Redraw the screen (but only if things have changed
* or if forceRedraw).
*/
void redraw(bool forceRedraw);
void redraw_core(bool forceRedraw);
void updateBrightness();
void updateSpeed();
void updateIntensity();
void drawStatusIcons();
/**
* marks the position of the arrow showing
* the current setting being changed
* pass line and colum info
*/
void setMarkLine(byte newMarkLineNum, byte newMarkColNum);
//Draw the arrow for the current setting being changed
void drawArrow();
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row);
/**
* If there screen is off or in clock is displayed,
* this will return true. This allows us to throw away
* the first input from the rotary encoder but
* to wake up the screen.
*/
bool wakeDisplay();
/**
* Allows you to show one line and a glyph as overlay for a period of time.
* Clears the screen and prints.
* Used in Rotary Encoder usermod.
*/
void overlay(const char* line1, long showHowLong, byte glyphType);
/**
* Allows you to show Akemi WLED logo overlay for a period of time.
* Clears the screen and prints.
*/
void overlayLogo(long showHowLong);
/**
* Allows you to show two lines as overlay for a period of time.
* Clears the screen and prints.
* Used in Auto Save usermod
*/
void overlay(const char* line1, const char* line2, long showHowLong);
void networkOverlay(const char* line1, long showHowLong);
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool sleepEnable);
/**
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b);
void onUpdateBegin(bool init);
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
//void addToJsonInfo(JsonObject& root) {
//JsonObject user = root["u"];
//if (user.isNull()) user = root.createNestedObject("u");
//JsonArray data = user.createNestedArray(F("4LineDisplay"));
//data.add(F("Loaded."));
//}
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject& root) {
//}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject& root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
void appendConfigData();
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root);
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
bool readFromConfig(JsonObject& root);
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() {
return USERMOD_ID_FOUR_LINE_DISP;
}
};
// strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRate-ms";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";
#ifdef ARDUINO_ARCH_ESP32
FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr;
#endif
//WLEDMM
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
// semaphores - needed on ESP32 only, as we use a separate task to update the display
SemaphoreHandle_t drawMux = xSemaphoreCreateBinary(); // for drawstring and drawglyph functions (to prevent concurrent access to HW)
SemaphoreHandle_t drawMuxBig = xSemaphoreCreateBinary(); // for draw2x2GlyphIcons() and showCurrentEffectOrPalette() - more complex and not thread-safe
const TickType_t maxWait = 300 * portTICK_PERIOD_MS; // wait max. 300ms (drawstring semaphore)
const TickType_t maxWaitLong = 800 * portTICK_PERIOD_MS; // wait max. 800ms (big drawing semaphore)
#define FLD_SemaphoreTake(x,t) xSemaphoreTake((x),(t))
#define FLD_SemaphoreGive(x) xSemaphoreGive(x)
#else
// 8266 or no tasks - no semaphores
#define FLD_SemaphoreTake(x,t) pdTRUE
#define FLD_SemaphoreGive(x)
#if !defined(ARDUINO_ARCH_ESP32) && !defined(pdTRUE)
#define pdTRUE true
#endif
#endif
// some displays need this to properly apply contrast
void FourLineDisplayUsermod::setVcomh(bool highContrast) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
@@ -238,22 +445,22 @@ class FourLineDisplayUsermod : public Usermod {
u8x8_cad_SendCmd(u8x8_struct, 0x0db); //address of value u8x8_cad_SendCmd(u8x8_struct, 0x0db); //address of value
u8x8_cad_SendArg(u8x8_struct, highContrast ? 0x000 : 0x040); //value 0 for fix, reboot resets default back to 64 u8x8_cad_SendArg(u8x8_struct, highContrast ? 0x000 : 0x040); //value 0 for fix, reboot resets default back to 64
u8x8_cad_EndTransfer(u8x8_struct); u8x8_cad_EndTransfer(u8x8_struct);
} }
/** /**
* Wrappers for screen drawing * Wrappers for screen drawing
*/ */
void setFlipMode(uint8_t mode) { void FourLineDisplayUsermod::setFlipMode(uint8_t mode) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
u8x8->setFlipMode(mode); u8x8->setFlipMode(mode);
} }
void setContrast(uint8_t contrast) { void FourLineDisplayUsermod::setContrast(uint8_t contrast) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
u8x8->setContrast(contrast); u8x8->setContrast(contrast);
} }
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) { void FourLineDisplayUsermod::drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
@@ -276,8 +483,8 @@ class FourLineDisplayUsermod : public Usermod {
else u8x8->drawString(col, row, string); else u8x8->drawString(col, row, string);
#endif #endif
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
void draw2x2String(uint8_t col, uint8_t row, const char *string) { void FourLineDisplayUsermod::draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (!typeOK || !enabled) return; if (!typeOK || !enabled) return;
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
@@ -301,46 +508,46 @@ class FourLineDisplayUsermod : public Usermod {
} }
#endif #endif
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) { void FourLineDisplayUsermod::drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH) {
if (!typeOK || !enabled) return; if (!typeOK || !enabled) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
u8x8->setFont(font); u8x8->setFont(font);
if (!ignoreLH && lineHeight>1) u8x8->draw1x2Glyph(col, row, glyph); if (!ignoreLH && lineHeight>1) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph); else u8x8->drawGlyph(col, row, glyph);
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { void FourLineDisplayUsermod::draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (!typeOK || !enabled) return; if (!typeOK || !enabled) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
u8x8->setFont(font); u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph); u8x8->draw2x2Glyph(col, row, glyph);
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
uint8_t getCols() { uint8_t FourLineDisplayUsermod::getCols() {
if (!typeOK || !enabled) return 0; if (!typeOK || !enabled) return 0;
return u8x8->getCols(); return u8x8->getCols();
} }
void clear() { void FourLineDisplayUsermod::clear() {
if (!typeOK || !enabled) return; if (!typeOK || !enabled) return;
if (nullptr == u8x8) return; // prevents some crashes if (nullptr == u8x8) return; // prevents some crashes
if (FLD_SemaphoreTake(drawMux, maxWaitLong ) != pdTRUE) return; // WLEDMM acquire draw mutex - clear() can take very long in software I2C mode if (FLD_SemaphoreTake(drawMux, maxWaitLong ) != pdTRUE) return; // WLEDMM acquire draw mutex - clear() can take very long in software I2C mode
u8x8->clear(); // crashes randomly on ESP32 u8x8->clear(); // crashes randomly on ESP32
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
void setPowerSave(uint8_t save) { void FourLineDisplayUsermod::setPowerSave(uint8_t save) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
u8x8->setPowerSave(save); u8x8->setPowerSave(save);
} }
void center(String &line, uint8_t width) { void FourLineDisplayUsermod::center(String &line, uint8_t width) {
int len = line.length(); int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line; if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' '; for (byte i=line.length(); i<width; i++) line += ' ';
} }
void draw2x2GlyphIcons() { void FourLineDisplayUsermod::draw2x2GlyphIcons() {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex
if (lineHeight > 1) { if (lineHeight > 1) {
@@ -357,14 +564,14 @@ class FourLineDisplayUsermod : public Usermod {
drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
} }
FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex
} }
/** /**
* Display the current date and time in large characters * Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on * on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration. * the useAMPM configuration.
*/ */
void showTime() { void FourLineDisplayUsermod::showTime() {
if (!typeOK || !enabled || !displayTurnedOff) return; if (!typeOK || !enabled || !displayTurnedOff) return;
unsigned long now = millis(); unsigned long now = millis();
@@ -424,13 +631,13 @@ class FourLineDisplayUsermod : public Usermod {
drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
} }
drawing = false; drawing = false;
} }
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
// network here // network here
void setup() { void FourLineDisplayUsermod::setup() {
if (!enabled) return; // typeOK = true will be set after successful setup if (!enabled) return; // typeOK = true will be set after successful setup
bool isHW = false; bool isHW = false;
@@ -619,21 +826,21 @@ class FourLineDisplayUsermod : public Usermod {
//drawString(0, 0, "Loading..."); //drawString(0, 0, "Loading...");
overlayLogo(3500); overlayLogo(3500);
initDone = true; initDone = true;
} }
// gets called every time WiFi is (re-)connected. Initialize own network // gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here // interfaces here
void connected() { void FourLineDisplayUsermod::connected() {
knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() : knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
knownIp = Network.localIP(); //apActive ? IPAddress(4, 3, 2, 1) : Network.localIP(); knownIp = Network.localIP(); //apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
networkOverlay(PSTR("NETWORK INFO"),7000); networkOverlay(PSTR("NETWORK INFO"),7000);
} }
/** /**
* Da loop. * Da loop.
*/ */
void loop() { void FourLineDisplayUsermod::loop() {
#if !defined(ARDUINO_ARCH_ESP32) || !defined(FLD_ESP32_USE_THREADS) #if !defined(ARDUINO_ARCH_ESP32) || !defined(FLD_ESP32_USE_THREADS)
static unsigned long lastRunTime = 0; static unsigned long lastRunTime = 0;
unsigned long now = millis(); unsigned long now = millis();
if (!enabled || !typeOK || (strip.isUpdating() && (now - lastRunTime < 50))) return; if (!enabled || !typeOK || (strip.isUpdating() && (now - lastRunTime < 50))) return;
@@ -644,30 +851,25 @@ class FourLineDisplayUsermod : public Usermod {
reDrawing = true; reDrawing = true;
redraw(false); redraw(false);
reDrawing = false; reDrawing = false;
#endif #endif
} }
//function to update lastredraw //function to to check if a redraw or overlay draw is active. Needed for UM Rotary, to avoid random/concurrent drawing
void updateRedrawTime() { bool FourLineDisplayUsermod::canDraw(void) {
lastRedraw = millis();
}
//function to to check if a redraw or overlay draw is active. Needed for UM Rotary, to avoid random/concurrent drawing
bool canDraw(void) {
if (!typeOK || !enabled || !initDone) return(false); // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled || !initDone) return(false); // WLEDMM make sure the display is initialized before we try to draw on it
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) // only necessary on ESP32 #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) // only necessary on ESP32
if (drawing) return(false); // overlay draws someting if (drawing) return(false); // overlay draws someting
if (reDrawing) return(false); // redraw task draws something if (reDrawing) return(false); // redraw task draws something
#endif #endif
return(true); return(true);
} }
/** /**
* Redraw the screen (but only if things have changed * Redraw the screen (but only if things have changed
* or if forceRedraw). * or if forceRedraw).
*/ */
void redraw(bool forceRedraw) { void FourLineDisplayUsermod::redraw(bool forceRedraw) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
// use a wrapper onESP32, to ensure the functions is not running several times in parallel ! // use a wrapper onESP32, to ensure the functions is not running several times in parallel !
static bool doForceRedraw = false; // for delaying "force redraw" static bool doForceRedraw = false; // for delaying "force redraw"
@@ -684,9 +886,9 @@ class FourLineDisplayUsermod : public Usermod {
redraw_core(forceRedraw); redraw_core(forceRedraw);
if (overlayUntil == 0) doForceRedraw = false; // redraw was skipped if overlay is still visible if (overlayUntil == 0) doForceRedraw = false; // redraw was skipped if overlay is still visible
reDrawing = false; // reset activity flag, as redraw has too many early returns that don't take care of this reDrawing = false; // reset activity flag, as redraw has too many early returns that don't take care of this
} }
void redraw_core(bool forceRedraw) { void FourLineDisplayUsermod::redraw_core(bool forceRedraw) {
#endif #endif
bool needRedraw = false; bool needRedraw = false;
unsigned long now = millis(); unsigned long now = millis();
@@ -800,9 +1002,9 @@ class FourLineDisplayUsermod : public Usermod {
// Fourth row // Fourth row
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); //Effect Mode info showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); //Effect Mode info
} }
void updateBrightness() { void FourLineDisplayUsermod::updateBrightness() {
knownBrightness = bri; knownBrightness = bri;
if (overlayUntil == 0) { if (overlayUntil == 0) {
brightness100 = ((uint16_t)bri*100)/255; brightness100 = ((uint16_t)bri*100)/255;
@@ -811,9 +1013,9 @@ class FourLineDisplayUsermod : public Usermod {
drawString(1, lineHeight, lineBuffer); drawString(1, lineHeight, lineBuffer);
//lastRedraw = millis(); //lastRedraw = millis();
} }
} }
void updateSpeed() { void FourLineDisplayUsermod::updateSpeed() {
knownEffectSpeed = effectSpeed; knownEffectSpeed = effectSpeed;
if (overlayUntil == 0) { if (overlayUntil == 0) {
fxspeed100 = ((uint16_t)effectSpeed*100)/255; fxspeed100 = ((uint16_t)effectSpeed*100)/255;
@@ -822,9 +1024,9 @@ class FourLineDisplayUsermod : public Usermod {
drawString(5, lineHeight, lineBuffer); drawString(5, lineHeight, lineBuffer);
//lastRedraw = millis(); //lastRedraw = millis();
} }
} }
void updateIntensity() { void FourLineDisplayUsermod::updateIntensity() {
knownEffectIntensity = effectIntensity; knownEffectIntensity = effectIntensity;
if (overlayUntil == 0) { if (overlayUntil == 0) {
fxintensity100 = ((uint16_t)effectIntensity*100)/255; fxintensity100 = ((uint16_t)effectIntensity*100)/255;
@@ -833,9 +1035,9 @@ class FourLineDisplayUsermod : public Usermod {
drawString(9, lineHeight, lineBuffer); drawString(9, lineHeight, lineBuffer);
//lastRedraw = millis(); //lastRedraw = millis();
} }
} }
void drawStatusIcons() { void FourLineDisplayUsermod::drawStatusIcons() {
uint8_t col = 15; uint8_t col = 15;
uint8_t row = 0; uint8_t row = 0;
drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon
@@ -843,26 +1045,26 @@ class FourLineDisplayUsermod : public Usermod {
drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon
if (lineHeight>1) { col--; } else { col = row = 0; } if (lineHeight>1) { col--; } else { col = row = 0; }
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nightlight mode drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nightlight mode
} }
/** /**
* marks the position of the arrow showing * marks the position of the arrow showing
* the current setting being changed * the current setting being changed
* pass line and colum info * pass line and colum info
*/ */
void setMarkLine(byte newMarkLineNum, byte newMarkColNum) { void FourLineDisplayUsermod::setMarkLine(byte newMarkLineNum, byte newMarkColNum) {
markLineNum = newMarkLineNum; markLineNum = newMarkLineNum;
markColNum = newMarkColNum; markColNum = newMarkColNum;
} }
//Draw the arrow for the current setting being changed //Draw the arrow for the current setting being changed
void drawArrow() { void FourLineDisplayUsermod::drawArrow() {
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1); if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
} }
//Display the current effect or palette (desiredEntry) //Display the current effect or palette (desiredEntry)
// on the appropriate line (row). // on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) { void FourLineDisplayUsermod::showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[MAX_JSON_CHARS] = { '\0' }; char lineBuffer[MAX_JSON_CHARS] = { '\0' };
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex
@@ -926,15 +1128,15 @@ class FourLineDisplayUsermod : public Usermod {
} }
FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex
} }
/** /**
* If there screen is off or in clock is displayed, * If there screen is off or in clock is displayed,
* this will return true. This allows us to throw away * this will return true. This allows us to throw away
* the first input from the rotary encoder but * the first input from the rotary encoder but
* to wake up the screen. * to wake up the screen.
*/ */
bool wakeDisplay() { bool FourLineDisplayUsermod::wakeDisplay() {
if (!typeOK || !enabled) return false; if (!typeOK || !enabled) return false;
if (!initDone) return false; if (!initDone) return false;
if (displayTurnedOff) { if (displayTurnedOff) {
@@ -949,14 +1151,14 @@ class FourLineDisplayUsermod : public Usermod {
return true; return true;
} }
return false; return false;
} }
/** /**
* Allows you to show one line and a glyph as overlay for a period of time. * Allows you to show one line and a glyph as overlay for a period of time.
* Clears the screen and prints. * Clears the screen and prints.
* Used in Rotary Encoder usermod. * Used in Rotary Encoder usermod.
*/ */
void overlay(const char* line1, long showHowLong, byte glyphType) { void FourLineDisplayUsermod::overlay(const char* line1, long showHowLong, byte glyphType) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (!initDone) return; // WLEDMM bugfix if (!initDone) return; // WLEDMM bugfix
unsigned long now = millis(); unsigned long now = millis();
@@ -977,13 +1179,13 @@ class FourLineDisplayUsermod : public Usermod {
} }
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; drawing = false;
} }
/** /**
* Allows you to show Akemi WLED logo overlay for a period of time. * Allows you to show Akemi WLED logo overlay for a period of time.
* Clears the screen and prints. * Clears the screen and prints.
*/ */
void overlayLogo(long showHowLong) { void FourLineDisplayUsermod::overlayLogo(long showHowLong) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
@@ -1038,14 +1240,14 @@ class FourLineDisplayUsermod : public Usermod {
} }
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; drawing = false;
} }
/** /**
* Allows you to show two lines as overlay for a period of time. * Allows you to show two lines as overlay for a period of time.
* Clears the screen and prints. * Clears the screen and prints.
* Used in Auto Save usermod * Used in Auto Save usermod
*/ */
void overlay(const char* line1, const char* line2, long showHowLong) { void FourLineDisplayUsermod::overlay(const char* line1, const char* line2, long showHowLong) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (!initDone) return; // WLEDMM bugfix if (!initDone) return; // WLEDMM bugfix
unsigned long now = millis(); unsigned long now = millis();
@@ -1067,9 +1269,9 @@ class FourLineDisplayUsermod : public Usermod {
} }
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; drawing = false;
} }
void networkOverlay(const char* line1, long showHowLong) { void FourLineDisplayUsermod::networkOverlay(const char* line1, long showHowLong) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (!initDone) return; // WLEDMM bugfix if (!initDone) return; // WLEDMM bugfix
unsigned long now = millis(); unsigned long now = millis();
@@ -1107,13 +1309,13 @@ class FourLineDisplayUsermod : public Usermod {
drawString(0, lineHeight*3, line.c_str()); drawString(0, lineHeight*3, line.c_str());
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; drawing = false;
} }
/** /**
* Enable sleep (turn the display off) or clock mode. * Enable sleep (turn the display off) or clock mode.
*/ */
void sleepOrClock(bool sleepEnable) { void FourLineDisplayUsermod::sleepOrClock(bool sleepEnable) {
if (sleepEnable) { if (sleepEnable) {
displayTurnedOff = true; displayTurnedOff = true;
//setContrast(contrastFix? 2+ contrast/4 : 0); // un-comment to dim display in "clock mode" //setContrast(contrastFix? 2+ contrast/4 : 0); // un-comment to dim display in "clock mode"
@@ -1127,14 +1329,14 @@ class FourLineDisplayUsermod : public Usermod {
setPowerSave(0); setPowerSave(0);
//setContrast(contrast); // un-comment to restore display brightness on wakeup //setContrast(contrast); // un-comment to restore display brightness on wakeup
} }
} }
/** /**
* handleButton() can be used to override default button behaviour. Returning true * handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way. * will prevent button working in a default way.
* Replicating button.cpp * Replicating button.cpp
*/ */
bool handleButton(uint8_t b) { bool FourLineDisplayUsermod::handleButton(uint8_t b) {
yield(); yield();
if (!enabled if (!enabled
|| b // butto 0 only || b // butto 0 only
@@ -1201,17 +1403,17 @@ class FourLineDisplayUsermod : public Usermod {
//handled = false; //handled = false;
} }
return handled; return handled;
} }
#if CONFIG_FREERTOS_UNICORE #if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0 #define ARDUINO_RUNNING_CORE 0
#else #else
#ifndef ARDUINO_RUNNING_CORE #ifndef ARDUINO_RUNNING_CORE
#define ARDUINO_RUNNING_CORE 1 #define ARDUINO_RUNNING_CORE 1
#endif #endif
#endif #endif
void onUpdateBegin(bool init) { void FourLineDisplayUsermod::onUpdateBegin(bool init) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS) #if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
if (init && Display_Task) { if (init && Display_Task) {
vTaskSuspend(Display_Task); // update is about to begin, disable task to prevent crash vTaskSuspend(Display_Task); // update is about to begin, disable task to prevent crash
} else { } else {
@@ -1241,37 +1443,37 @@ class FourLineDisplayUsermod : public Usermod {
ARDUINO_RUNNING_CORE ARDUINO_RUNNING_CORE
); );
} }
#endif #endif
} }
/* /*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor * Below it is shown how this could be used for e.g. a light sensor
*/ */
//void addToJsonInfo(JsonObject& root) { //void addToJsonInfo(JsonObject& root) {
//JsonObject user = root["u"]; //JsonObject user = root["u"];
//if (user.isNull()) user = root.createNestedObject("u"); //if (user.isNull()) user = root.createNestedObject("u");
//JsonArray data = user.createNestedArray(F("4LineDisplay")); //JsonArray data = user.createNestedArray(F("4LineDisplay"));
//data.add(F("Loaded.")); //data.add(F("Loaded."));
//} //}
/* /*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
//void addToJsonState(JsonObject& root) { //void addToJsonState(JsonObject& root) {
//} //}
/* /*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* 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() // if (!initDone) return; // prevent crash on boot applyPreset()
//} //}
void appendConfigData() { void FourLineDisplayUsermod::appendConfigData() {
oappend(SET_F("addHB('4LineDisplay');")); oappend(SET_F("addHB('4LineDisplay');"));
oappend(SET_F("dd=addDropdown('4LineDisplay','type');")); oappend(SET_F("dd=addDropdown('4LineDisplay','type');"));
@@ -1330,9 +1532,9 @@ class FourLineDisplayUsermod : public Usermod {
if (strcmp(errorMessage, "") != 0) { if (strcmp(errorMessage, "") != 0) {
oappend(SET_F("addInfo('errorMessage', 0, '<i>error: ")); oappend(errorMessage); oappend("! Correct and reboot</i>');"); oappend(SET_F("addInfo('errorMessage', 0, '<i>error: ")); oappend(errorMessage); oappend("! Correct and reboot</i>');");
} }
} }
/* /*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved) * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop(). * If you want to force saving the current state, use serializeConfig() in your loop().
@@ -1346,7 +1548,7 @@ class FourLineDisplayUsermod : public Usermod {
* *
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/ */
void addToConfig(JsonObject& root) { void FourLineDisplayUsermod::addToConfig(JsonObject& root) {
// determine if we are using global HW pins (data & clock) // determine if we are using global HW pins (data & clock)
int8_t hw_dta, hw_clk; int8_t hw_dta, hw_clk;
if (displayIsSPI(type)) { if (displayIsSPI(type)) {
@@ -1379,9 +1581,9 @@ class FourLineDisplayUsermod : public Usermod {
top[FPSTR(_showSeconds)] = (bool) showSeconds; top[FPSTR(_showSeconds)] = (bool) showSeconds;
top[FPSTR(_busClkFrequency)] = ioFrequency/1000; top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display config saved.")); DEBUG_PRINTLN(F("4 Line Display config saved."));
} }
/* /*
* readFromConfig() can be used to read back the custom settings you added with addToConfig(). * readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot) * This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
* *
@@ -1389,7 +1591,7 @@ class FourLineDisplayUsermod : public Usermod {
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :) * If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/ */
bool readFromConfig(JsonObject& root) { bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
bool needsRedraw = false; bool needsRedraw = false;
DisplayType newType = type; DisplayType newType = type;
int8_t oldPin[5]; for (byte i=0; i<5; i++) oldPin[i] = ioPin[i]; int8_t oldPin[5]; for (byte i=0; i<5; i++) oldPin[i] = ioPin[i];
@@ -1475,35 +1677,8 @@ class FourLineDisplayUsermod : public Usermod {
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_contrastFix)].isNull(); return !top[FPSTR(_contrastFix)].isNull();
} }
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() {
return USERMOD_ID_FOUR_LINE_DISP;
}
};
// strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRate-ms";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";
#ifdef ARDUINO_ARCH_ESP32
FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr;
#endif
// WLEDMM clean up some macros, so they don't cause problems in other usermods // WLEDMM clean up some macros, so they don't cause problems in other usermods
#undef FLD_SemaphoreTake #undef FLD_SemaphoreTake
#undef FLD_SemaphoreGive #undef FLD_SemaphoreGive

View File

@@ -184,7 +184,162 @@ private:
* Sort the modes and palettes to the index arrays * Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes. * modes_alpha_indexes and palettes_alpha_indexes.
*/ */
void sortModesAndPalettes() { void sortModesAndPalettes();
byte *re_initIndexArray(int numModes);
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
const char **re_findModeStrings(const char json[], int numModes);
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip);
public:
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup();
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected();
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*
* Tips:
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
*
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
* Instead, use a timer check as shown here.
*/
void loop();
void displayNetworkInfo();
void findCurrentEffectAndPalette();
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph);
void lampUdated();
void changeBrightness(bool increase);
void changeEffect(bool increase);
void changeEffectSpeed(bool increase);
void changeEffectIntensity(bool increase);
void changeCustom(uint8_t par, bool increase);
void changePalette(bool increase);
void changeHue(bool increase);
void changeSat(bool increase);
void changePreset(bool increase) ;
void changeCCT(bool increase);
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
/*
void addToJsonInfo(JsonObject& root)
{
int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray lightArr = user.createNestedArray("Light"); //name
lightArr.add(reading); //value
lightArr.add(" lux"); //unit
}
*/
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void addToJsonState(JsonObject &root)
{
//root["user0"] = userVar0;
}
*/
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void readFromJsonState(JsonObject &root)
{
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
*/
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root);
//WLEDMM: add appendConfigData
void appendConfigData();
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_ROTARY_ENC_UI;
}
};
// strings to reduce flash memory usage (used more than twice)
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";
const char RotaryEncoderUIUsermod::_applyToAll[] PROGMEM = "apply-2-all-seg";
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void RotaryEncoderUIUsermod::sortModesAndPalettes() {
//modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount()); //modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_qstrings = strip.getModeDataSrc(); modes_qstrings = strip.getModeDataSrc();
modes_alpha_indexes = re_initIndexArray(strip.getModeCount()); modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
@@ -198,21 +353,21 @@ private:
int skipPaletteCount = 1; int skipPaletteCount = 1;
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ; while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ;
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount); re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
} }
byte *re_initIndexArray(int numModes) { byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes); byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) { for (byte i = 0; i < numModes; i++) {
indexes[i] = i; indexes[i] = i;
} }
return indexes; return indexes;
} }
/** /**
* Return an array of mode or palette names from the JSON string. * Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'. * They don't end in '\0', they end in '"'.
*/ */
const char **re_findModeStrings(const char json[], int numModes) { const char **RotaryEncoderUIUsermod::re_findModeStrings(const char json[], int numModes) {
const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes); const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes);
uint8_t modeIndex = 0; uint8_t modeIndex = 0;
bool insideQuotes = false; bool insideQuotes = false;
@@ -245,26 +400,26 @@ private:
if (complete) break; if (complete) break;
} }
return modeStrings; return modeStrings;
} }
/** /**
* Sort either the modes or the palettes using quicksort. * Sort either the modes or the palettes using quicksort.
*/ */
void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) { void RotaryEncoderUIUsermod::re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
if (!modeNames) return; if (!modeNames) return;
listBeingSorted = modeNames; listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp); qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr; listBeingSorted = nullptr;
} }
public: // public:
/* /*
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() void RotaryEncoderUIUsermod::setup()
{ {
DEBUG_PRINTLN(F("Usermod Rotary Encoder init.")); DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } }; PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if ((pinA < 0) || (pinB < 0)) { //WLEDMM catch error: [ 1839][E][esp32-hal-gpio.c:102] __pinMode(): Invalid pin selected if ((pinA < 0) || (pinB < 0)) { //WLEDMM catch error: [ 1839][E][esp32-hal-gpio.c:102] __pinMode(): Invalid pin selected
@@ -312,18 +467,18 @@ public:
Enc_B = digitalRead(pinB); Enc_B = digitalRead(pinB);
Enc_A_prev = Enc_A; Enc_A_prev = Enc_A;
USER_PRINTLN(F("Rotary encoder (ALT) setup completed.")); // WLEDMM inform user USER_PRINTLN(F("Rotary encoder (ALT) setup completed.")); // WLEDMM inform user
} }
/* /*
* connected() is called every time the WiFi is (re)connected * connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
void connected() void RotaryEncoderUIUsermod::connected()
{ {
//Serial.println("Connected to WiFi!"); //Serial.println("Connected to WiFi!");
} }
/* /*
* loop() is called continuously. Here you can check for events, read sensors, etc. * loop() is called continuously. Here you can check for events, read sensors, etc.
* *
* Tips: * Tips:
@@ -333,8 +488,8 @@ public:
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds. * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
* Instead, use a timer check as shown here. * Instead, use a timer check as shown here.
*/ */
void loop() void RotaryEncoderUIUsermod::loop()
{ {
if (!enabled) return; if (!enabled) return;
unsigned long currentTime = millis(); // get the current elapsed time unsigned long currentTime = millis(); // get the current elapsed time
@@ -467,16 +622,16 @@ public:
} }
Enc_A_prev = Enc_A; // Store value of A for next time Enc_A_prev = Enc_A; // Store value of A for next time
} }
} }
void displayNetworkInfo() { void RotaryEncoderUIUsermod::displayNetworkInfo() {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) if (display != nullptr)
display->networkOverlay(PSTR("NETWORK INFO"), 10000); display->networkOverlay(PSTR("NETWORK INFO"), 10000);
#endif #endif
} }
void findCurrentEffectAndPalette() { void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
if (modes_alpha_indexes == nullptr) return; // WLEDMM bugfix if (modes_alpha_indexes == nullptr) return; // WLEDMM bugfix
currentEffectAndPaletteInitialized = true; currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) { for (uint8_t i = 0; i < strip.getModeCount(); i++) {
@@ -492,10 +647,10 @@ public:
break; break;
} }
} }
} }
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) { boolean RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) { if (display != nullptr) {
if (display->wakeDisplay()) { if (display->wakeDisplay()) {
// Throw away wake up input // Throw away wake up input
@@ -505,49 +660,49 @@ public:
display->overlay(stateName, 750, glyph); display->overlay(stateName, 750, glyph);
display->setMarkLine(markedLine, markedCol); display->setMarkLine(markedLine, markedCol);
} }
#endif #endif
return true; return true;
} }
void lampUdated() { void RotaryEncoderUIUsermod::lampUdated() {
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
//setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required) //setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required)
stateUpdated(CALL_MODE_BUTTON); stateUpdated(CALL_MODE_BUTTON);
if ((millis() - lastInterfaceUpdate) > INTERFACE_UPDATE_COOLDOWN) // WLEDMM respect cooldown times, to avoid crash in AsyncWebSocketMessageBuffer if ((millis() - lastInterfaceUpdate) > INTERFACE_UPDATE_COOLDOWN) // WLEDMM respect cooldown times, to avoid crash in AsyncWebSocketMessageBuffer
updateInterfaces(CALL_MODE_BUTTON); updateInterfaces(CALL_MODE_BUTTON);
} }
void changeBrightness(bool increase) { void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
byte lastBri = bri; byte lastBri = bri;
if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // WLEDMM slower steps when brightness < 16% if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // WLEDMM slower steps when brightness < 16%
else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
if (lastBri != bri) stateChanged = true; // WLEDMM bugfix if (lastBri != bri) stateChanged = true; // WLEDMM bugfix
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display->canDraw()) // only draw if nothing else is drawing if (display->canDraw()) // only draw if nothing else is drawing
display->updateBrightness(); display->updateBrightness();
#endif #endif
} }
void changeEffect(bool increase) { void RotaryEncoderUIUsermod::changeEffect(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0); effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0);
if (modes_alpha_indexes != nullptr) effectCurrent = modes_alpha_indexes[effectCurrentIndex]; if (modes_alpha_indexes != nullptr) effectCurrent = modes_alpha_indexes[effectCurrentIndex];
stateChanged = true; stateChanged = true;
@@ -562,22 +717,22 @@ public:
seg.setMode(effectCurrent); seg.setMode(effectCurrent);
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display->canDraw()) // only draw if nothing else is drawing if (display->canDraw()) // only draw if nothing else is drawing
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
#endif #endif
} }
void changeEffectSpeed(bool increase) { void RotaryEncoderUIUsermod::changeEffectSpeed(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0); effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
@@ -591,22 +746,22 @@ public:
seg.speed = effectSpeed; seg.speed = effectSpeed;
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display->canDraw()) // only draw if nothing else is drawing if (display->canDraw()) // only draw if nothing else is drawing
display->updateSpeed(); display->updateSpeed();
#endif #endif
} }
void changeEffectIntensity(bool increase) { void RotaryEncoderUIUsermod::changeEffectIntensity(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0); effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
@@ -620,15 +775,15 @@ public:
seg.intensity = effectIntensity; seg.intensity = effectIntensity;
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display->canDraw()) // only draw if nothing else is drawing if (display->canDraw()) // only draw if nothing else is drawing
display->updateIntensity(); display->updateIntensity();
#endif #endif
} }
void changeCustom(uint8_t par, bool increase) { void RotaryEncoderUIUsermod::changeCustom(uint8_t par, bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
uint8_t val = 0; uint8_t val = 0;
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@@ -636,18 +791,18 @@ public:
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
uint8_t id = strip.getFirstSelectedSegId(); uint8_t id = strip.getFirstSelectedSegId();
Segment& sid = strip.getSegment(id); Segment& sid = strip.getSegment(id);
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
switch (par) { switch (par) {
case 3: val = sid.custom3 = max(min((increase ? sid.custom3+fadeAmount : sid.custom3-fadeAmount), 255), 0); break; case 3: val = sid.custom3 = max(min((increase ? sid.custom3+fadeAmount : sid.custom3-fadeAmount), 255), 0); break;
case 2: val = sid.custom2 = max(min((increase ? sid.custom2+fadeAmount : sid.custom2-fadeAmount), 255), 0); break; case 2: val = sid.custom2 = max(min((increase ? sid.custom2+fadeAmount : sid.custom2-fadeAmount), 255), 0); break;
default: val = sid.custom1 = max(min((increase ? sid.custom1+fadeAmount : sid.custom1-fadeAmount), 255), 0); break; default: val = sid.custom1 = max(min((increase ? sid.custom1+fadeAmount : sid.custom1-fadeAmount), 255), 0); break;
} }
#endif #endif
for (byte i=0; i<strip.getSegmentsNum(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || i == id) continue; if (!seg.isActive() || i == id) continue;
@@ -658,33 +813,33 @@ public:
} }
} }
} else { } else {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
Segment& seg = strip.getMainSegment(); Segment& seg = strip.getMainSegment();
switch (par) { switch (par) {
case 3: val = seg.custom3 = max(min((increase ? seg.custom3+fadeAmount : seg.custom3-fadeAmount), 255), 0); break; case 3: val = seg.custom3 = max(min((increase ? seg.custom3+fadeAmount : seg.custom3-fadeAmount), 255), 0); break;
case 2: val = seg.custom2 = max(min((increase ? seg.custom2+fadeAmount : seg.custom2-fadeAmount), 255), 0); break; case 2: val = seg.custom2 = max(min((increase ? seg.custom2+fadeAmount : seg.custom2-fadeAmount), 255), 0); break;
default: val = seg.custom1 = max(min((increase ? seg.custom1+fadeAmount : seg.custom1-fadeAmount), 255), 0); break; default: val = seg.custom1 = max(min((increase ? seg.custom1+fadeAmount : seg.custom1-fadeAmount), 255), 0); break;
} }
#endif #endif
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64] = { '\0' }; char lineBuffer[64] = { '\0' };
snprintf(lineBuffer, 63, "%d", val); // WLEDMM: avoid string buffer overflow snprintf(lineBuffer, 63, "%d", val); // WLEDMM: avoid string buffer overflow
display->overlay(lineBuffer, 500, 10); // use star display->overlay(lineBuffer, 500, 10); // use star
#endif #endif
} }
void changePalette(bool increase) { void RotaryEncoderUIUsermod::changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0); effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
effectPalette = palettes_alpha_indexes[effectPaletteIndex]; effectPalette = palettes_alpha_indexes[effectPaletteIndex];
stateChanged = true; stateChanged = true;
@@ -699,22 +854,22 @@ public:
seg.setPalette(effectPalette); seg.setPalette(effectPalette);
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display->canDraw()) // only draw if nothing else is drawing if (display->canDraw()) // only draw if nothing else is drawing
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
#endif #endif
} }
void changeHue(bool increase){ void RotaryEncoderUIUsermod::changeHue(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0); currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col); colorHStoRGB(currentHue1*256, currentSat1, col);
stateChanged = true; stateChanged = true;
@@ -729,22 +884,22 @@ public:
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64] = { '\0' }; char lineBuffer[64] = { '\0' };
snprintf(lineBuffer, 63, "%d", currentHue1); // WLEDMM: avoid string buffer overflow snprintf(lineBuffer, 63, "%d", currentHue1); // WLEDMM: avoid string buffer overflow
display->overlay(lineBuffer, 500, 7); // use brush display->overlay(lineBuffer, 500, 7); // use brush
#endif #endif
} }
void changeSat(bool increase){ void RotaryEncoderUIUsermod::changeSat(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0); currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col); colorHStoRGB(currentHue1*256, currentSat1, col);
if (applyToAll) { if (applyToAll) {
@@ -758,22 +913,22 @@ public:
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
} }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64] = { '\0' }; char lineBuffer[64] = { '\0' };
snprintf(lineBuffer, 63, "%d", currentSat1); // WLEDMM: avoid string buffer overflow snprintf(lineBuffer, 63, "%d", currentSat1); // WLEDMM: avoid string buffer overflow
display->overlay(lineBuffer, 500, 8); // use contrast display->overlay(lineBuffer, 500, 8); // use contrast
#endif #endif
} }
void changePreset(bool increase) { void RotaryEncoderUIUsermod::changePreset(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
if (presetHigh && presetLow && presetHigh > presetLow) { if (presetHigh && presetLow && presetHigh > presetLow) {
StaticJsonDocument<64> root; StaticJsonDocument<64> root;
char str[64] = { '\0' }; char str[64] = { '\0' };
@@ -795,17 +950,17 @@ public:
display->overlay(str, 500, 11); // use heart display->overlay(str, 500, 11); // use heart
#endif #endif
} }
} }
void changeCCT(bool increase){ void RotaryEncoderUIUsermod::changeCCT(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
// Throw away wake up input // Throw away wake up input
return; return;
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0); currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
// if (applyToAll) { // if (applyToAll) {
for (byte i=0; i<strip.getSegmentsNum(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
@@ -818,21 +973,21 @@ public:
// seg.setCCT(currentCCT, strip.getMainSegmentId()); // seg.setCCT(currentCCT, strip.getMainSegmentId());
// } // }
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64] = { '\0' }; char lineBuffer[64] = { '\0' };
snprintf(lineBuffer, 63, "%d", currentCCT); // WLEDMM: avoid string buffer overflow snprintf(lineBuffer, 63, "%d", currentCCT); // WLEDMM: avoid string buffer overflow
display->overlay(lineBuffer, 500, 10); // use star display->overlay(lineBuffer, 500, 10); // use star
#endif #endif
} }
/* /*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor * Below it is shown how this could be used for e.g. a light sensor
*/ */
/* /*
void addToJsonInfo(JsonObject& root) void addToJsonInfo(JsonObject& root)
{ {
int reading = 20; int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object //this code adds "u":{"Light":[20," lux"]} to the info object
JsonObject user = root["u"]; JsonObject user = root["u"];
@@ -840,36 +995,36 @@ public:
JsonArray lightArr = user.createNestedArray("Light"); //name JsonArray lightArr = user.createNestedArray("Light"); //name
lightArr.add(reading); //value lightArr.add(reading); //value
lightArr.add(" lux"); //unit lightArr.add(" lux"); //unit
} }
*/ */
/* /*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/* /*
void addToJsonState(JsonObject &root) void addToJsonState(JsonObject &root)
{ {
//root["user0"] = userVar0; //root["user0"] = userVar0;
} }
*/ */
/* /*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* 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)
{ {
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value //userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
} }
*/ */
/** /**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json * addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/ */
void addToConfig(JsonObject &root) { void RotaryEncoderUIUsermod::addToConfig(JsonObject &root) {
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}} // we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
@@ -880,11 +1035,11 @@ public:
top[FPSTR(_presetHigh)] = presetHigh; top[FPSTR(_presetHigh)] = presetHigh;
top[FPSTR(_applyToAll)] = applyToAll; top[FPSTR(_applyToAll)] = applyToAll;
DEBUG_PRINTLN(F("Rotary Encoder config saved.")); DEBUG_PRINTLN(F("Rotary Encoder config saved."));
} }
//WLEDMM: add appendConfigData //WLEDMM: add appendConfigData
void appendConfigData() void RotaryEncoderUIUsermod::appendConfigData()
{ {
oappend(SET_F("addHB('Rotary-Encoder');")); oappend(SET_F("addHB('Rotary-Encoder');"));
#ifdef ENCODER_DT_PIN #ifdef ENCODER_DT_PIN
@@ -896,14 +1051,14 @@ public:
#ifdef ENCODER_SW_PIN #ifdef ENCODER_SW_PIN
oappend(SET_F("xOpt('Rotary-Encoder:SW-pin',1,' ⎌',")); oappendi(ENCODER_SW_PIN); oappend(");"); oappend(SET_F("xOpt('Rotary-Encoder:SW-pin',1,' ⎌',")); oappendi(ENCODER_SW_PIN); oappend(");");
#endif #endif
} }
/** /**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json * readFromConfig() is called before setup() to populate properties from values stored in cfg.json
* *
* The function should return true if configuration was successfully loaded or false if there was no configuration. * The function should return true if configuration was successfully loaded or false if there was no configuration.
*/ */
bool readFromConfig(JsonObject &root) { bool RotaryEncoderUIUsermod::readFromConfig(JsonObject &root) {
// we look for JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}} // we look for JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
@@ -949,24 +1104,4 @@ public:
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_applyToAll)].isNull(); return !top[FPSTR(_applyToAll)].isNull();
} }
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_ROTARY_ENC_UI;
}
};
// strings to reduce flash memory usage (used more than twice)
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";
const char RotaryEncoderUIUsermod::_applyToAll[] PROGMEM = "apply-2-all-seg";