diff --git a/usermods/readme.md b/usermods/readme.md index 932c0078b..8cd2d486f 100644 --- a/usermods/readme.md +++ b/usermods/readme.md @@ -6,11 +6,11 @@ If you have created an usermod that you believe is useful (for example to suppor In order for other people to be able to have fun with your usermod, please keep these points in mind: -- Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`) -- Include your custom `wled06_usermod.ino` file -- If your usermod requieres changes to other WLED files, please write a `readme.md` outlining the steps one has to take to use the usermod -- Create a pull request! -- If your feature is useful for the majority of WLED users, I will consider adding it to the base code! +- Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`) +- Include your custom `wled06_usermod.ino` file +- If your usermod requieres changes to other WLED files, please write a `readme.md` outlining the steps one has to take to use the usermod +- Create a pull request! +- If your feature is useful for the majority of WLED users, I will consider adding it to the base code! While I do my best to not break too much, keep in mind that as WLED is being updated, usermods might break. I am not actively maintaining any usermod in this directory, that is your responsibility as the creator of the usermod. diff --git a/usermods/ssd1306_i2c_oled_u8g2/README.md b/usermods/ssd1306_i2c_oled_u8g2/README.md new file mode 100644 index 000000000..70919cc54 --- /dev/null +++ b/usermods/ssd1306_i2c_oled_u8g2/README.md @@ -0,0 +1,35 @@ +# SSD1306 128x32 OLED via I2C with u8g2 +This usermod allows to connect 128x32 Oled display to WLED controlled and show +the next information: +- Current SSID +- IP address if obtained + * in AP mode and turned off lightning AP password is shown +- Current effect +- Current palette +- On/Off icon (sun/moon) + +## Hardware +![Hardware connection](assets/hw_connection.png) + +## Requirements +Functionality checked with: +- commit 095429a7df4f9e2b34dd464f7bbfd068df6558eb +- Wemos d1 mini +- PlatformIO +- Generic SSD1306 128x32 I2C OLED display from aliexpress + +### Platformio +Add `U8g2@~2.27.2` dependency to `lib_deps_external` under `[common]` section in `platformio.ini`: +```ini +# platformio.ini +... +[common] +... +lib_deps_external = + ... + U8g2@~2.27.2 +... +``` + +### Arduino IDE +Install library `U8g2 by oliver` in `Tools | Include Library | Manage libraries` menu. \ No newline at end of file diff --git a/usermods/ssd1306_i2c_oled_u8g2/assets/hw_connection.png b/usermods/ssd1306_i2c_oled_u8g2/assets/hw_connection.png new file mode 100644 index 000000000..a0e51b4de Binary files /dev/null and b/usermods/ssd1306_i2c_oled_u8g2/assets/hw_connection.png differ diff --git a/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino b/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino new file mode 100644 index 000000000..915289523 --- /dev/null +++ b/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino @@ -0,0 +1,149 @@ +#include // from https://github.com/olikraus/u8g2/ + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, 5, + 4); // Pins are Reset, SCL, SDA + +// gets called once at boot. Do all initialization that doesn't depend on +// network here +void userSetup() { + u8x8.begin(); + u8x8.setPowerSave(0); + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Check if values which are shown on display changed from the last tiem. + if ((apActive == true ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + // Update last known values. + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(ssid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (ssid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(ip); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} diff --git a/wled00/data/settings_sec.htm b/wled00/data/settings_sec.htm index d6273d4bd..d332ad18a 100644 Binary files a/wled00/data/settings_sec.htm and b/wled00/data/settings_sec.htm differ diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index a06f95386..6c2646554 100644 Binary files a/wled00/data/settings_sync.htm and b/wled00/data/settings_sync.htm differ diff --git a/wled00/data/settings_time.htm b/wled00/data/settings_time.htm index a255df049..29de669fa 100644 Binary files a/wled00/data/settings_time.htm and b/wled00/data/settings_time.htm differ diff --git a/wled00/data/settings_ui.htm b/wled00/data/settings_ui.htm index e82dbefba..ee452adf0 100644 Binary files a/wled00/data/settings_ui.htm and b/wled00/data/settings_ui.htm differ diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index 9516bb769..62ef153de 100644 Binary files a/wled00/data/settings_wifi.htm and b/wled00/data/settings_wifi.htm differ diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 07403d5ef..1a8b3ffb5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -16,14 +16,14 @@ //You are required to disable over-the-air updates: //#define WLED_DISABLE_OTA -//You need to choose 1-2 of these features to disable: +//You need to choose some of these features to disable: //#define WLED_DISABLE_ALEXA //#define WLED_DISABLE_BLYNK //#define WLED_DISABLE_CRONIXIE //#define WLED_DISABLE_HUESYNC //#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01 //#define WLED_DISABLE_MOBILE_UI - +#define WLED_ENABLE_ADALIGHT //only saves about 500b #define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet //#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version @@ -437,7 +437,7 @@ AsyncMqttClient* mqtt = NULL; void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte); void handleE131Packet(e131_packet_t*, IPAddress); -#define E131_MAX_UNIVERSE_COUNT 7 +#define E131_MAX_UNIVERSE_COUNT 9 //udp interface objects WiFiUDP notifierUdp, rgbUdp; diff --git a/wled00/wled04_file.ino b/wled00/wled04_file.ino index 384e5c2da..f1cc5e442 100644 --- a/wled00/wled04_file.ino +++ b/wled00/wled04_file.ino @@ -1,40 +1,87 @@ /* * Utility for SPIFFS filesystem & Serial console */ +enum class AdaState { + Header_A, + Header_d, + Header_a, + Header_CountHi, + Header_CountLo, + Header_CountCheck, + Data_Red, + Data_Green, + Data_Blue +}; + void handleSerial() { - if (Serial.available() > 0) //support for Adalight protocol to high-speed control LEDs over serial + #ifdef WLED_ENABLE_ADALIGHT + static auto state = AdaState::Header_A; + static uint16_t count = 0; + static uint16_t pixel = 0; + static byte check = 0x00; + static byte red = 0x00; + static byte green = 0x00; + static bool changed = false; + + while (Serial.available() > 0) + { + byte next = Serial.read(); + switch (state) { + case AdaState::Header_A: + if (next == 'A') state = AdaState::Header_d; + break; + case AdaState::Header_d: + if (next == 'd') state = AdaState::Header_a; + else state = AdaState::Header_A; + break; + case AdaState::Header_a: + if (next == 'a') state = AdaState::Header_CountHi; + else state = AdaState::Header_A; + break; + case AdaState::Header_CountHi: + pixel = 0; + count = next * 0x100; + check = next; + state = AdaState::Header_CountLo; + break; + case AdaState::Header_CountLo: + count += next + 1; + check = check ^ next ^ 0x55; + state = AdaState::Header_CountCheck; + break; + case AdaState::Header_CountCheck: + if (check == next) state = AdaState::Data_Red; + else state = AdaState::Header_A; + break; + case AdaState::Data_Red: + red = next; + state = AdaState::Data_Green; + break; + case AdaState::Data_Green: + green = next; + state = AdaState::Data_Blue; + break; + case AdaState::Data_Blue: + byte blue = next; + changed = true; + setRealtimePixel(pixel++, red, green, blue, 0); + if (--count > 0) state = AdaState::Data_Red; + else state = AdaState::Header_A; + break; + } + } + + if (changed) { - if (!Serial.find("Ada")) return; - if (!realtimeActive && bri == 0) strip.setBrightness(briLast); arlsLock(realtimeTimeoutMs); - + yield(); - byte hi = Serial.read(); - byte ledc = Serial.read(); - byte chk = Serial.read(); - if(chk != (hi ^ ledc ^ 0x55)) return; - if (ledCount < ledc) ledc = ledCount; - - byte sc[3]; int t =-1; int to = 0; - for (int i=0; i < ledc; i++) - { - for (byte j=0; j<3; j++) - { - while (Serial.peek()<0) //no data yet available - { - yield(); - to++; - if (to>15) {strip.show(); return;} //unexpected end of transmission - } - to = 0; - sc[j] = Serial.read(); - } - setRealtimePixel(i,sc[0],sc[1],sc[2],0); - } strip.show(); + changed = false; } + #endif }