-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Usermod for ESP32C3 Super Mini OLED 72x40 #5335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 0_15_x
Are you sure you want to change the base?
Changes from all commits
2fce12e
87a5bc1
b4c517b
8376587
d805f8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| # WLED Usermod: OLED 72x40 (SSD1306) | ||
|
|
||
| A custom WLED usermod designed for the **ESP32-C3 Super Mini** development board featuring an integrated **0.42-inch OLED display** (72x40 pixels). | ||
|
|
||
| ## 📱 About This Device | ||
|
|
||
| This specific board is a compact IoT platform based on the **ESP32-C3FN4/FH4** with built-in 4MB Flash, Wi-Fi, and Bluetooth 5.0. It is widely used for teeny-tiny projects like telemetry displays, altitude trackers, or localized status monitors. | ||
|
|
||
|  | ||
| *Image source: AlexYeryomin/ESP32C3-OLED-72x40* | ||
|
|
||
| ### Technical Specifications | ||
|
|
||
| * **Controller:** ESP32-C3 | ||
| * **OLED Resolution:** 72x40 pixels (Effective area) | ||
| * **Driver:** SSD1306 (requires 128x64 driver initialization with specific offsets to avoid clipping) | ||
| * **I2C Pins:** SDA (GPIO 5), SCL (GPIO 6) | ||
| * **Onboard LED:** GPIO 8 (used for heartbeat status) | ||
| * **Function Button:** GPIO 9 (normally used for Bootloader mode), assigned to toggling brightness between 0% and current value. | ||
|
|
||
| --- | ||
|
|
||
| ## 🛠 Features & Functions | ||
|
|
||
| ### 1. Adaptive Dashboard | ||
|
|
||
| The usermod displays a real-time WLED status dashboard including: | ||
|
|
||
| * **Effect Name:** Shows the current WLED mode/effect. | ||
| * **Brightness Visualizer:** A horizontal scrolling graph showing brightness (`bri`) levels over time. | ||
| * **System Stats:** Quick view of Speed (S), Intensity (I), and Brightness (B) percentages. | ||
|
|
||
| ### 2. Smart Coordinate Mapping (Flipping Support) | ||
|
|
||
| Because this 72x40 display uses a 128x64 driver, normal library rotations can cause the image to "fall off" the physical glass. This usermod includes **Dynamic Offset Calculation**: | ||
|
|
||
| * **Normal Mode:** Centers the 72x40 UI in the top-left area. | ||
| * **Flipped Mode:** Automatically shifts coordinates to the bottom-right of the 128x64 memory buffer so the UI remains perfectly centered and visible when rotated 180°. | ||
|
|
||
| ### 3. LED Heartbeat & Network Status | ||
|
|
||
| The onboard **GPIO 8** LED provides visual feedback of the device's connectivity: | ||
|
|
||
| * **Blinking:** The device is disconnected from the network. | ||
| * **Pulsing (Sinusoidal):** The device is successfully connected to WiFi. | ||
|
|
||
| ### 4. Splash Screen & Info | ||
|
|
||
| Upon boot, the display shows the **Akemi Logo** and current **mDNS name or IP Address** for 5 seconds to help you locate the device on your network. | ||
|
|
||
| ### 5. Configurable Sleep Timer | ||
|
|
||
| To prevent OLED burn-in, the screen automatically blanks after a period of inactivity (default: 60 seconds). It wakes up instantly when: | ||
|
|
||
| * The physical button is pressed. | ||
| * Settings are changed via the WLED Web UI or API. | ||
|
|
||
| --- | ||
|
|
||
| ## ⚙️ Configuration | ||
|
|
||
| The following settings are available directly in the **WLED Usermod Settings** page (v0.15.3+): | ||
|
|
||
| * **Enabled:** Toggle the OLED on/off. | ||
| * **Flip Display:** Rotates the UI 180° and adjusts internal offsets. | ||
| * **X/Y-Offset:** Fine-tune the UI position on your specific glass. | ||
| * **Sleep Timeout:** Set how many seconds to wait before the screen turns off. | ||
|
|
||
| ## 🔗 References & Inspiration | ||
|
|
||
| * [AlexYeryomin/ESP32C3-OLED-72x40](https://github.com/AlexYeryomin/ESP32C3-OLED-72x40) - Driver implementation and original Micropython demo. | ||
| * [Kevin's Blog: ESP32-C3 0.42 OLED](https://emalliab.wordpress.com/2025/02/12/esp32-c3-0-42-oled/) - Deep dive into coordinate offsets and hardware constraints. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,234 @@ | ||||||
| #pragma once | ||||||
|
|
||||||
| #include "wled.h" | ||||||
| #include <U8g2lib.h> | ||||||
| #include <Wire.h> | ||||||
|
|
||||||
| #define OLED_SDA 5 | ||||||
| #define OLED_SCL 6 | ||||||
| #define LED_PIN 8 | ||||||
|
|
||||||
| // 128x40 bitmap generated by LCD Matrix Studio | ||||||
| const unsigned char akemi_logo [] PROGMEM = { | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x06, 0x7E, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3C, 0x60, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x1C, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x1C, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x18, 0x20, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x1E, 0x3C, 0x60, 0x00, 0x38, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0x0C, 0x00, 0x80, 0x01, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xE0, 0x00, | ||||||
| 0x02, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x1F, 0xC3, 0xE0, 0x00, 0x01, 0x00, 0x43, 0xC3, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE7, 0xE0, 0x00, 0x01, 0x02, 0x46, 0x43, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xE0, 0x00, | ||||||
| 0x01, 0x02, 0x44, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | ||||||
| 0x1F, 0xFF, 0xE0, 0x01, 0x01, 0x02, 0x45, 0xC9, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x38, 0x1F, 0xFF, 0xE0, 0x1C, 0x01, 0x32, 0x47, 0x09, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x10, | ||||||
| 0x01, 0x12, 0x42, 0x0D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, | ||||||
| 0x30, 0x00, 0x00, 0x30, 0x01, 0xD2, 0x4B, 0x07, 0xE0, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x0C, 0x2E, 0xE7, 0x70, 0x30, 0x00, 0xDE, 0x79, 0xE0, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0xE7, 0x60, 0x60, | ||||||
| 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, | ||||||
| 0x06, 0xC6, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x03, 0x08, 0xC6, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0xC6, 0x08, 0xC0, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | ||||||
| 0xE0, 0xC6, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| 0x00, 0x00, 0x00, 0x00 | ||||||
| }; | ||||||
|
|
||||||
|
|
||||||
| class UsermodOLED72x40 : public Usermod { | ||||||
| private: | ||||||
| U8G2_SSD1306_128X64_NONAME_F_HW_I2C* u8g2 = nullptr; | ||||||
| bool enabled = true, initDone = false, displayOff = false; | ||||||
| unsigned long lastUpdate = 0, lastInteraction = 0; | ||||||
| unsigned long screenTimeoutMS = 60000; | ||||||
| int xOff = 28, yOff = 24; | ||||||
| byte vValues[72]; | ||||||
| bool flipDisplay = false; | ||||||
|
|
||||||
| public: | ||||||
| void setup() { | ||||||
| pinMode(LED_PIN, OUTPUT); | ||||||
| Wire.begin(OLED_SDA, OLED_SCL); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should not be necessary, if you use the usermod global I2C pins. |
||||||
| WiFi.setTxPower(WIFI_POWER_8_5dBm); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are known issues with the WiFi in this board that can make it quite unuseable. But it doesn't happen on all environments, maybe this should be a configuration option, I guess.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ok, actually its already an option, so you don't need to repeat that in your UM Lines 379 to 380 in 6b953d9
just add |
||||||
| // Wire.setClock(100000); // Set to 100kHz (Standard Mode) to avoid frequent connection issues | ||||||
| u8g2 = new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(U8G2_R0, U8X8_PIN_NONE); | ||||||
|
|
||||||
| if (u8g2->begin()) { | ||||||
| u8g2->setContrast(255); | ||||||
| u8g2->setFlipMode(flipDisplay ? 1 : 0); | ||||||
| memset(vValues, 0, sizeof(vValues)); | ||||||
| u8g2->clearBuffer(); | ||||||
|
|
||||||
| // FIXED X-OFFSET FOR SPLASH | ||||||
| // Since akemi_logo is 128px wide (16 bytes), we don't shift X. | ||||||
| // We only shift Y to align the 40px height within the 64px buffer. | ||||||
| int splashX = 0; | ||||||
| int splashY = flipDisplay ? (64 - 40 - yOff) : yOff; | ||||||
|
|
||||||
| // Draw the 128x40 bitmap | ||||||
| u8g2->drawBitmap(splashX, splashY, 16, 40, akemi_logo); | ||||||
|
|
||||||
| // The IP address/mDNS text still needs the relative shift to stay | ||||||
| // centered under the logo area | ||||||
| int textX = flipDisplay ? (128 - 72 - xOff) : xOff; | ||||||
|
|
||||||
| u8g2->setFont(u8g2_font_4x6_tf); | ||||||
| u8g2->setCursor(textX + 12, splashY + 38); | ||||||
|
|
||||||
| if (Network.isConnected()) u8g2->print(Network.localIP()); | ||||||
| else u8g2->print(cmDNS); | ||||||
|
|
||||||
| u8g2->sendBuffer(); | ||||||
| delay(5000); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, such a long delay in the usermod setup function is a no-go.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. It was just for showing a silly "bootsplash", I'll think of some other way. |
||||||
| lastInteraction = millis(); | ||||||
| initDone = true; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| void loop() { | ||||||
| // 1. LED HEARTBEAT (Always runs) | ||||||
| static unsigned long ledTimer = 0; | ||||||
| if (!Network.isConnected()) { | ||||||
| if (millis() - ledTimer > 200) { ledTimer = millis(); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); } | ||||||
| } else { | ||||||
| float pulse = (sin(millis() / 2000.0 * PI) + 1) * 127.5; | ||||||
| analogWrite(LED_PIN, (int)pulse); | ||||||
| } | ||||||
|
|
||||||
| if (!enabled || !initDone) return; | ||||||
|
|
||||||
| // 2. TIMEOUT LOGIC (Moved before the strip updating guard) | ||||||
| if (screenTimeoutMS > 0 && (millis() - lastInteraction > screenTimeoutMS)) { | ||||||
| if (!displayOff) { u8g2->setPowerSave(1); displayOff = true; } | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| // 3. DRAWING LOGIC (Bypassed if screen is blanked) | ||||||
| if (strip.isUpdating()) return; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might stall your usermod completely if someone has lots of LEDs. better:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noted! Thanks for taking the time to review this so thoroughly. First time contributing to WLED. |
||||||
|
|
||||||
| if (millis() - lastUpdate > 100) { | ||||||
| lastUpdate = millis(); | ||||||
| if (displayOff) { u8g2->setPowerSave(0); displayOff = false; } | ||||||
|
|
||||||
| u8g2->clearBuffer(); | ||||||
|
|
||||||
| // ADJUST OFFSETS BASED ON FLIP | ||||||
| // Normal: xOff=28, yOff=24 | ||||||
| // Flipped: We need to shift the content to the opposite side of the 128x64 buffer | ||||||
| int currentX = flipDisplay ? (128 - 72 - xOff) : xOff; | ||||||
| int currentY = flipDisplay ? (64 - 40 - yOff) : yOff; | ||||||
|
|
||||||
| u8g2->setFont(u8g2_font_5x7_tf); | ||||||
| char lineBuffer[17]; | ||||||
| extractModeName(effectCurrent, JSON_mode_names, lineBuffer, 16); | ||||||
| u8g2->drawStr(currentX, currentY + 7, lineBuffer); | ||||||
| u8g2->drawHLine(currentX, currentY + 9, 72); | ||||||
|
|
||||||
| for (int i = 0; i < 71; i++) vValues[i] = vValues[i+1]; | ||||||
| vValues[71] = map(bri, 0, 255, 0, 15); | ||||||
| for (int i = 0; i < 71; i++) { | ||||||
| u8g2->drawLine(currentX + i, currentY + 26, currentX + i, currentY + 26 - vValues[i]); | ||||||
| } | ||||||
|
Comment on lines
+159
to
+163
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brightness graph drops the newest sample.
🐛 Proposed fix- for (int i = 0; i < 71; i++) {
+ for (int i = 0; i < 72; i++) {
u8g2->drawLine(currentX + i, currentY + 26, currentX + i, currentY + 26 - vValues[i]);
}🤖 Prompt for AI Agents |
||||||
|
|
||||||
| u8g2->setFont(u8g2_font_4x6_tf); | ||||||
| u8g2->setCursor(currentX, currentY + 38); | ||||||
| u8g2->print("S:"); u8g2->print(map(effectSpeed, 0, 255, 0, 99)); u8g2->print("% "); | ||||||
| u8g2->print("I:"); u8g2->print(map(effectIntensity, 0, 255, 0, 99)); u8g2->print("% "); | ||||||
| u8g2->print("B:"); u8g2->print(map(bri, 0, 255, 0, 100)); u8g2->print("%"); | ||||||
|
|
||||||
| u8g2->sendBuffer(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| void onStateChange(uint8_t mode) { | ||||||
| lastInteraction = millis(); | ||||||
| lastUpdate = 0; | ||||||
| if (displayOff && u8g2) { u8g2->setPowerSave(0); displayOff = false; } | ||||||
| } | ||||||
|
|
||||||
| bool handleButton(uint8_t b) { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will react on all buttons defined in the LEDs setting page, is this what you want? |
||||||
| lastInteraction = millis(); | ||||||
| if (displayOff && u8g2) { u8g2->setPowerSave(0); displayOff = false; } | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| void appendConfigData(JsonArray &config) { | ||||||
| JsonObject top = config.createNestedObject(); | ||||||
| top[F("usermod")] = F("OLED_72x40"); | ||||||
|
|
||||||
| // This creates the UI elements in the Usermod settings page | ||||||
| config.createNestedObject()[F("OLED_72x40:enabled")]; | ||||||
| config.createNestedObject()[F("OLED_72x40:flipDisplay")]; | ||||||
| config.createNestedObject()[F("OLED_72x40:x-offset")]; | ||||||
| config.createNestedObject()[F("OLED_72x40:y-offset")]; | ||||||
| config.createNestedObject()[F("OLED_72x40:sleepTimeout")]; | ||||||
| config.createNestedObject()[F("OLED_72x40:bootButtonPin")]; | ||||||
| } | ||||||
| void addToConfig(JsonObject& root) { | ||||||
| JsonObject top = root.createNestedObject(F("OLED_72x40")); | ||||||
| top[F("enabled")] = enabled; | ||||||
| top[F("flipDisplay")] = flipDisplay; // Registration for UI | ||||||
| top["bootButtonPin"] = 9; | ||||||
| top["x-offset"] = xOff; | ||||||
| top["y-offset"] = yOff; | ||||||
| top["sleepTimeout"] = screenTimeoutMS / 1000; | ||||||
| } | ||||||
|
|
||||||
| bool readFromConfig(JsonObject& root) { | ||||||
| JsonObject top = root[F("OLED_72x40")]; | ||||||
| if (top.isNull()) return false; | ||||||
|
|
||||||
| enabled = top[F("enabled")] | enabled; | ||||||
| flipDisplay = top[F("flipDisplay")] | flipDisplay; | ||||||
| xOff = top[F("x-offset")] | 28; | ||||||
| yOff = top[F("y-offset")] | 24; | ||||||
|
|
||||||
| // Guard against 0 or negative timeout values | ||||||
| unsigned long timeoutSec = top[F("sleepTimeout")] | 60; | ||||||
| screenTimeoutMS = timeoutSec * 1000; | ||||||
|
|
||||||
| if (initDone && u8g2) u8g2->setFlipMode(flipDisplay ? 1 : 0); | ||||||
|
|
||||||
| // Apply the button pin 9 default logic | ||||||
| int pin = top[F("bootButtonPin")] | 9; | ||||||
| if (btnPin[0] == -1 || btnPin[0] != pin) { | ||||||
| btnPin[0] = pin; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. usermods should not interfere with the button handling code in button.cpp. Edit: It might be better to just make the "button index" (from LEDs preferences) configurable in your UM settings, and leave the button handling (and de-bouncing) to the wled core. You'll still receive the |
||||||
| buttonType[0] = BTN_TYPE_PUSH; | ||||||
| } | ||||||
|
Comment on lines
+199
to
+229
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bootButtonPin setting is never persisted.
🛠️ Proposed fix- top["bootButtonPin"] = 9;
+ top["bootButtonPin"] = bootButtonPin;
@@
- int pin = top[F("bootButtonPin")] | 9;
- if (btnPin[0] == -1 || btnPin[0] != pin) {
- btnPin[0] = pin;
+ bootButtonPin = top[F("bootButtonPin")] | bootButtonPin;
+ if (btnPin[0] == -1 || btnPin[0] != bootButtonPin) {
+ btnPin[0] = bootButtonPin;
buttonType[0] = BTN_TYPE_PUSH;
}// Add alongside other member fields
int bootButtonPin = 9;🤖 Prompt for AI Agents |
||||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| uint16_t getId() { return USERMOD_ID_OLED_72x40; } | ||||||
| }; | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use pin manager to check if the pin is free before writing to it.