mirror of
https://github.com/wled/WLED.git
synced 2025-04-24 06:47:18 +00:00
Remove obsolete usermods
- mode sort - 4LD (non ALT) - Rotary encoder (non ALT)
This commit is contained in:
parent
0a7e9f9f8f
commit
b8bf2a707c
@ -1,63 +0,0 @@
|
||||
# I2C 4 Line Display Usermod
|
||||
|
||||
First, thanks to the authors of the ssd11306_i2c_oled_u8g2 mod.
|
||||
|
||||
Provides a four line display using either
|
||||
128x32 or 128x64 OLED displays.
|
||||
It can operate independently, but starts to provide
|
||||
a relatively complete on-device UI when paired with the
|
||||
Rotary Encoder UI usermod. I strongly encourage you to use
|
||||
them together.
|
||||
|
||||
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and update the example `platformio_override.ini.sample`
|
||||
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
|
||||
This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this mod included wled00\usermods_list.cpp - also tells Rotary Encoder usermod, if installed, the display is available
|
||||
* `FLD_PIN_SCL` - The display SCL pin, defaults to 5
|
||||
* `FLD_PIN_SDA` - The display SDA pin, defaults to 4
|
||||
|
||||
All of the parameters can be configured via the Usermods settings page, inluding GPIO pins.
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
This usermod requires the `U8g2` and `Wire` libraries. See the
|
||||
`platformio_override.ini.sample` found in the Rotary Encoder
|
||||
UI usermod folder for how to include these using `platformio_override.ini`.
|
||||
|
||||
## Configuration
|
||||
|
||||
* `enabled` - enable/disable usermod
|
||||
* `pin` - GPIO pins used for display; I2C displays use Clk & Data; SPI displays can use SCK, MOSI, CS, DC & RST
|
||||
* `type` - display type in numeric format
|
||||
* 1 = I2C SSD1306 128x32
|
||||
* 2 = I2C SH1106 128x32
|
||||
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
||||
* 4 = I2C SSD1305 128x32
|
||||
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
||||
* 6 = SPI SSD1306 128x32
|
||||
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
||||
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
|
||||
* `refreshRateSec` - display refresh time in seconds
|
||||
* `screenTimeOutSec` - screen saver time-out in seconds
|
||||
* `flip` - flip/rotate display 180°
|
||||
* `sleepMode` - enable/disable screen saver
|
||||
* `clockMode` - enable/disable clock display in screen saver mode
|
||||
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
||||
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
||||
|
||||
2021-11
|
||||
* Added configuration option description.
|
@ -1,742 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||
|
||||
//
|
||||
// Insired by the v1 usermod: ssd1306_i2c_oled_u8g2
|
||||
//
|
||||
// v2 usermod for using 128x32 or 128x64 i2c
|
||||
// OLED displays to provide a four line display
|
||||
// for WLED.
|
||||
//
|
||||
// Dependencies
|
||||
// * This usermod REQURES the ModeSortUsermod
|
||||
// * This Usermod works best, by far, when coupled
|
||||
// with RotaryEncoderUIUsermod.
|
||||
//
|
||||
// Make sure to enable NTP and set your time zone in WLED Config | Time.
|
||||
//
|
||||
// REQUIREMENT: You must add the following requirements to
|
||||
// REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini
|
||||
// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine)
|
||||
// REQUIREMENT: * Wire
|
||||
//
|
||||
|
||||
//The SCL and SDA pins are defined here.
|
||||
#ifndef FLD_PIN_SCL
|
||||
#define FLD_PIN_SCL i2c_scl
|
||||
#endif
|
||||
#ifndef FLD_PIN_SDA
|
||||
#define FLD_PIN_SDA i2c_sda
|
||||
#endif
|
||||
#ifndef FLD_PIN_CLOCKSPI
|
||||
#define FLD_PIN_CLOCKSPI spi_sclk
|
||||
#endif
|
||||
#ifndef FLD_PIN_DATASPI
|
||||
#define FLD_PIN_DATASPI spi_mosi
|
||||
#endif
|
||||
#ifndef FLD_PIN_CS
|
||||
#define FLD_PIN_CS spi_cs
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 19
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 26
|
||||
#endif
|
||||
#else
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 12
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 16
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FLD_TYPE
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
#define FLD_TYPE SSD1306
|
||||
#else
|
||||
#define FLD_TYPE SSD1306_SPI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// When to time out to the clock or blank the screen
|
||||
// if SLEEP_MODE_ENABLED.
|
||||
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
|
||||
|
||||
#define TIME_INDENT 0
|
||||
#define DATE_INDENT 2
|
||||
|
||||
// Minimum time between redrawing screen in ms
|
||||
#define USER_LOOP_REFRESH_RATE_MS 1000
|
||||
|
||||
// Extra char (+1) for null
|
||||
#define LINE_BUFFER_SIZE 16+1
|
||||
|
||||
typedef enum {
|
||||
FLD_LINE_BRIGHTNESS = 0,
|
||||
FLD_LINE_EFFECT_SPEED,
|
||||
FLD_LINE_EFFECT_INTENSITY,
|
||||
FLD_LINE_MODE,
|
||||
FLD_LINE_PALETTE,
|
||||
FLD_LINE_TIME
|
||||
} Line4Type;
|
||||
|
||||
typedef enum {
|
||||
NONE = 0,
|
||||
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
|
||||
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
|
||||
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
|
||||
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
|
||||
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
||||
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
||||
} DisplayType;
|
||||
|
||||
class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
|
||||
bool initDone = false;
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// HW interface & configuration
|
||||
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
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)
|
||||
#else
|
||||
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
|
||||
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
|
||||
#endif
|
||||
DisplayType type = FLD_TYPE; // display type
|
||||
bool flip = false; // flip display 180°
|
||||
uint8_t contrast = 10; // screen contrast
|
||||
uint8_t lineHeight = 1; // 1 row or 2 rows
|
||||
uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
|
||||
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
|
||||
bool sleepMode = true; // allow screen sleep?
|
||||
bool clockMode = false; // display clock
|
||||
bool enabled = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
String knownSsid = "";
|
||||
IPAddress knownIp;
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownEffectSpeed = 0;
|
||||
uint8_t knownEffectIntensity = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
uint8_t knownMinute = 99;
|
||||
uint8_t knownHour = 99;
|
||||
|
||||
bool displayTurnedOff = false;
|
||||
unsigned long lastUpdate = 0;
|
||||
unsigned long lastRedraw = 0;
|
||||
unsigned long overlayUntil = 0;
|
||||
Line4Type lineType = FLD_LINE_BRIGHTNESS;
|
||||
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||
byte markLineNum = 0;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _contrast[];
|
||||
static const char _refreshRate[];
|
||||
static const char _screenTimeOut[];
|
||||
static const char _flip[];
|
||||
static const char _sleepMode[];
|
||||
static const char _clockMode[];
|
||||
static const char _busClkFrequency[];
|
||||
|
||||
// If display does not work or looks corrupted check the
|
||||
// constructor reference:
|
||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||
// or check the gallery:
|
||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||
|
||||
public:
|
||||
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
if (type == NONE || !enabled) return;
|
||||
|
||||
bool isHW;
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
|
||||
isHW = (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi);
|
||||
if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins
|
||||
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
|
||||
} else {
|
||||
isHW = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda);
|
||||
if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
|
||||
}
|
||||
|
||||
DEBUG_PRINTLN(F("Allocating display."));
|
||||
switch (type) {
|
||||
case SSD1306:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SH1106:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1305:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1305_64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_SPI:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1306_SPI64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 2;
|
||||
break;
|
||||
default:
|
||||
u8x8 = nullptr;
|
||||
}
|
||||
|
||||
if (nullptr == u8x8) {
|
||||
DEBUG_PRINTLN(F("Display init failed."));
|
||||
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
|
||||
type = NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
initDone = true;
|
||||
DEBUG_PRINTLN(F("Starting display."));
|
||||
/*if (!(type == SSD1306_SPI || type == SSD1306_SPI64))*/ u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
u8x8->begin();
|
||||
setFlipMode(flip);
|
||||
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
setPowerSave(0);
|
||||
drawString(0, 0, "Loading...");
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/**
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (!enabled || millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
|
||||
lastUpdate = millis();
|
||||
|
||||
redraw(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrappers for screen drawing
|
||||
*/
|
||||
void setFlipMode(uint8_t mode) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFlipMode(mode);
|
||||
}
|
||||
void setContrast(uint8_t contrast) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setContrast(contrast);
|
||||
}
|
||||
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
|
||||
else u8x8->drawString(col, row, string);
|
||||
}
|
||||
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8->draw2x2String(col, row, string);
|
||||
}
|
||||
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(font);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
|
||||
else u8x8->drawGlyph(col, row, glyph);
|
||||
}
|
||||
uint8_t getCols() {
|
||||
if (type==NONE || !enabled) return 0;
|
||||
return u8x8->getCols();
|
||||
}
|
||||
void clear() {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->clear();
|
||||
}
|
||||
void setPowerSave(uint8_t save) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setPowerSave(save);
|
||||
}
|
||||
|
||||
void center(String &line, uint8_t width) {
|
||||
int len = line.length();
|
||||
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
|
||||
for (byte i=line.length(); i<width; i++) line += ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraw the screen (but only if things have changed
|
||||
* or if forceRedraw).
|
||||
*/
|
||||
void redraw(bool forceRedraw) {
|
||||
static bool showName = false;
|
||||
unsigned long now = millis();
|
||||
|
||||
if (type == NONE || !enabled) return;
|
||||
if (overlayUntil > 0) {
|
||||
if (now >= overlayUntil) {
|
||||
// Time to display the overlay has elapsed.
|
||||
overlayUntil = 0;
|
||||
forceRedraw = true;
|
||||
} else {
|
||||
// We are still displaying the overlay
|
||||
// Don't redraw.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (forceRedraw ||
|
||||
(((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
|
||||
(knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
|
||||
(knownBrightness != bri) ||
|
||||
(knownEffectSpeed != effectSpeed) ||
|
||||
(knownEffectIntensity != effectIntensity) ||
|
||||
(knownMode != strip.getMainSegment().mode) ||
|
||||
(knownPalette != strip.getMainSegment().palette)) {
|
||||
knownHour = 99; // force time update
|
||||
lastRedraw = now; // update lastRedraw marker
|
||||
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
|
||||
// change line every 5s
|
||||
showName = !showName;
|
||||
switch (lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
lineType = FLD_LINE_EFFECT_SPEED;
|
||||
break;
|
||||
case FLD_LINE_MODE:
|
||||
lineType = FLD_LINE_BRIGHTNESS;
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
lineType = clockMode ? FLD_LINE_MODE : FLD_LINE_BRIGHTNESS;
|
||||
break;
|
||||
case FLD_LINE_EFFECT_SPEED:
|
||||
lineType = FLD_LINE_EFFECT_INTENSITY;
|
||||
break;
|
||||
case FLD_LINE_EFFECT_INTENSITY:
|
||||
lineType = FLD_LINE_PALETTE;
|
||||
break;
|
||||
default:
|
||||
lineType = FLD_LINE_MODE;
|
||||
break;
|
||||
}
|
||||
knownHour = 99; // force time update
|
||||
// do not update lastRedraw marker if just switching row contenet
|
||||
} else {
|
||||
// Nothing to change.
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
|
||||
// We will still check if there is a change in redraw()
|
||||
// and turn it back on if it changed.
|
||||
sleepOrClock(true);
|
||||
} else if (displayTurnedOff && clockMode) {
|
||||
showTime();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Turn the display back on
|
||||
if (displayTurnedOff) sleepOrClock(false);
|
||||
|
||||
// Update last known values.
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
|
||||
// Do the actual drawing
|
||||
String line;
|
||||
// First row with Wifi name
|
||||
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon
|
||||
line = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
|
||||
center(line, getCols()-2);
|
||||
drawString(1, 0, line.c_str());
|
||||
// Print `~` char to indicate that SSID is longer, than our display
|
||||
if (knownSsid.length() > (int)getCols()-1) {
|
||||
drawString(getCols() - 1, 0, "~");
|
||||
}
|
||||
|
||||
// Second row with IP or Psssword
|
||||
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0) {
|
||||
drawString(1, lineHeight, apPass);
|
||||
} else {
|
||||
// alternate IP address and server name
|
||||
line = knownIp.toString();
|
||||
if (showName && strcmp(serverDescription, "WLED") != 0) {
|
||||
line = serverDescription;
|
||||
}
|
||||
center(line, getCols()-1);
|
||||
drawString(1, lineHeight, line.c_str());
|
||||
}
|
||||
|
||||
// draw third and fourth row
|
||||
drawLine(2, clockMode ? lineType : FLD_LINE_MODE);
|
||||
drawLine(3, clockMode ? FLD_LINE_TIME : lineType);
|
||||
|
||||
drawGlyph(0, 2*lineHeight, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon
|
||||
//if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon
|
||||
}
|
||||
|
||||
void drawLine(uint8_t line, Line4Type lineType) {
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
uint8_t printedChars;
|
||||
switch(lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_EFFECT_SPEED:
|
||||
sprintf_P(lineBuffer, PSTR("FX Speed %3d"), effectSpeed);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_EFFECT_INTENSITY:
|
||||
sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_MODE:
|
||||
printedChars = extractModeName(knownMode, JSON_mode_names, lineBuffer, LINE_BUFFER_SIZE-1);
|
||||
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
printedChars = extractModeName(knownPalette, JSON_palette_names, lineBuffer, LINE_BUFFER_SIZE-1);
|
||||
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_TIME:
|
||||
default:
|
||||
showTime(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if (type == NONE || !enabled) return false;
|
||||
knownHour = 99;
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
redraw(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to show up to two lines as overlay for a
|
||||
* period of time.
|
||||
* Clears the screen and prints on the middle two lines.
|
||||
*/
|
||||
void overlay(const char* line1, const char *line2, long showHowLong) {
|
||||
if (type == NONE || !enabled) return;
|
||||
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on (includes clear())
|
||||
sleepOrClock(false);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
// Print the overlay
|
||||
if (line1) {
|
||||
String buf = line1;
|
||||
center(buf, getCols());
|
||||
drawString(0, 1*lineHeight, buf.c_str());
|
||||
}
|
||||
if (line2) {
|
||||
String buf = line2;
|
||||
center(buf, getCols());
|
||||
drawString(0, 2*lineHeight, buf.c_str());
|
||||
}
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
void setLineType(byte lT) {
|
||||
lineType = (Line4Type) lT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Line 3 or 4 (last two lines) can be marked with an
|
||||
* arrow in the first column. Pass 2 or 3 to this to
|
||||
* specify which line to mark with an arrow.
|
||||
* Any other values are ignored.
|
||||
*/
|
||||
void setMarkLine(byte newMarkLineNum) {
|
||||
if (newMarkLineNum == 2 || newMarkLineNum == 3) {
|
||||
markLineNum = newMarkLineNum;
|
||||
}
|
||||
else {
|
||||
markLineNum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable sleep (turn the display off) or clock mode.
|
||||
*/
|
||||
void sleepOrClock(bool enabled) {
|
||||
clear();
|
||||
if (enabled) {
|
||||
if (clockMode) showTime();
|
||||
else setPowerSave(1);
|
||||
displayTurnedOff = true;
|
||||
} else {
|
||||
setPowerSave(0);
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(bool fullScreen = true) {
|
||||
if (type == NONE || !enabled) return;
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
|
||||
updateLocalTime();
|
||||
byte minuteCurrent = minute(localTime);
|
||||
byte hourCurrent = hour(localTime);
|
||||
byte secondCurrent = second(localTime);
|
||||
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
|
||||
// Time hasn't changed.
|
||||
if (!fullScreen) return;
|
||||
}
|
||||
knownMinute = minuteCurrent;
|
||||
knownHour = hourCurrent;
|
||||
|
||||
byte currentMonth = month(localTime);
|
||||
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
|
||||
if (fullScreen)
|
||||
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays
|
||||
else
|
||||
drawString(2, lineHeight*3, lineBuffer);
|
||||
|
||||
byte showHour = hourCurrent;
|
||||
boolean isAM = false;
|
||||
if (useAMPM) {
|
||||
if (showHour == 0) {
|
||||
showHour = 12;
|
||||
isAM = true;
|
||||
}
|
||||
else if (showHour > 12) {
|
||||
showHour -= 12;
|
||||
isAM = false;
|
||||
}
|
||||
else {
|
||||
isAM = true;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf_P(lineBuffer, (secondCurrent%2 || !fullScreen) ? PSTR("%2d:%02d") : PSTR("%2d %02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent);
|
||||
// For time, we always use LINE_HEIGHT of 2 since
|
||||
// we are printing it big.
|
||||
if (fullScreen) {
|
||||
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer);
|
||||
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true);
|
||||
else drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
|
||||
} else {
|
||||
drawString(9+(useAMPM?0:2), lineHeight*3, lineBuffer);
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*3, (isAM ? "AM" : "PM"), true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
//}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
JsonArray io_pin = top.createNestedArray("pin");
|
||||
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||
top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["type"] = type;
|
||||
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
|
||||
top[FPSTR(_flip)] = (bool) flip;
|
||||
top[FPSTR(_contrast)] = contrast;
|
||||
top[FPSTR(_refreshRate)] = refreshRate/1000;
|
||||
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
|
||||
top[FPSTR(_sleepMode)] = (bool) sleepMode;
|
||||
top[FPSTR(_clockMode)] = (bool) clockMode;
|
||||
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
|
||||
DEBUG_PRINTLN(F("4 Line Display config saved."));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
bool needsRedraw = false;
|
||||
DisplayType newType = type;
|
||||
int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newType = top["type"] | newType;
|
||||
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
|
||||
flip = top[FPSTR(_flip)] | flip;
|
||||
contrast = top[FPSTR(_contrast)] | contrast;
|
||||
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
|
||||
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
|
||||
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
|
||||
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||
if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
|
||||
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
else
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
type = newType;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
bool pinsChanged = false;
|
||||
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
|
||||
if (pinsChanged || type!=newType) {
|
||||
if (type != NONE) delete u8x8;
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
|
||||
if (isSPI) {
|
||||
if (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi) po = PinOwner::HW_SPI; // allow multiple allocations of HW SPI bus pins
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 5, po);
|
||||
} else {
|
||||
if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po);
|
||||
}
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
|
||||
type = NONE;
|
||||
return true;
|
||||
} else type = newType;
|
||||
setup();
|
||||
needsRedraw |= true;
|
||||
}
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
setContrast(contrast);
|
||||
setFlipMode(flip);
|
||||
if (needsRedraw && !wakeDisplay()) redraw(true);
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].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 = "refreshRateSec";
|
||||
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::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
|
@ -1,33 +0,0 @@
|
||||
# Mode Sort
|
||||
|
||||
v2 usermod that provides data about modes and
|
||||
palettes to other usermods. Notably it provides:
|
||||
* A direct method for a mode or palette name
|
||||
* Ability to retrieve mode and palette names in
|
||||
alphabetical order
|
||||
|
||||
```char **getModesQStrings()```
|
||||
|
||||
Provides a char* array (pointers) to the names of the
|
||||
palettes contained in JSON_mode_names, in the same order as
|
||||
JSON_mode_names. These strings end in double quote (")
|
||||
(or \0 if there is a problem).
|
||||
|
||||
```byte *getModesAlphaIndexes()```
|
||||
|
||||
A byte array designating the indexes of names of the
|
||||
modes in alphabetical order. "Solid" will always remain
|
||||
at the top of the list.
|
||||
|
||||
```char **getPalettesQStrings()```
|
||||
|
||||
Provides a char* array (pointers) to the names of the
|
||||
palettes contained in JSON_palette_names, in the same order as
|
||||
JSON_palette_names. These strings end in double quote (")
|
||||
(or \0 if there is a problem).
|
||||
|
||||
```byte *getPalettesAlphaIndexes()```
|
||||
|
||||
A byte array designating the indexes of names of the
|
||||
palettes in alphabetical order. "Default" and those
|
||||
starting with "(" will always remain at the top of the list.
|
@ -1,244 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//
|
||||
// v2 usermod that provides data about modes and
|
||||
// palettes to other usermods. Notably it provides:
|
||||
// * A direct method for a mode or palette name
|
||||
// * Ability to retrieve mode and palette names in
|
||||
// alphabetical order
|
||||
//
|
||||
// char **getModesQStrings()
|
||||
// Provides an array of char* (pointers) to the names of the
|
||||
// palettes within JSON_mode_names, in the same order as
|
||||
// JSON_mode_names. These strings end in double quote (")
|
||||
// (or \0 if there is a problem).
|
||||
//
|
||||
// byte *getModesAlphaIndexes()
|
||||
// An array of byte designating the indexes of names of the
|
||||
// modes in alphabetical order. "Solid" will always remain
|
||||
// at the front of the list.
|
||||
//
|
||||
// char **getPalettesQStrings()
|
||||
// Provides an array of char* (pointers) to the names of the
|
||||
// palettes within JSON_palette_names, in the same order as
|
||||
// JSON_palette_names. These strings end in double quote (")
|
||||
// (or \0 if there is a problem).
|
||||
//
|
||||
// byte *getPalettesAlphaIndexes()
|
||||
// An array of byte designating the indexes of names of the
|
||||
// palettes in alphabetical order. "Default" and those
|
||||
// starting with "(" will always remain at the front of the list.
|
||||
//
|
||||
|
||||
// Number of modes at the start of the list to not sort
|
||||
#define MODE_SORT_SKIP_COUNT 1
|
||||
|
||||
// Which list is being sorted
|
||||
char **listBeingSorted = nullptr;
|
||||
|
||||
/**
|
||||
* Modes and palettes are stored as strings that
|
||||
* end in a quote character. Compare two of them.
|
||||
* We are comparing directly within either
|
||||
* JSON_mode_names or JSON_palette_names.
|
||||
*/
|
||||
int re_qstringCmp(const void *ap, const void *bp) {
|
||||
char *a = listBeingSorted[*((byte *)ap)];
|
||||
char *b = listBeingSorted[*((byte *)bp)];
|
||||
int i = 0;
|
||||
do {
|
||||
char aVal = pgm_read_byte_near(a + i);
|
||||
if (aVal >= 97 && aVal <= 122) {
|
||||
// Lowercase
|
||||
aVal -= 32;
|
||||
}
|
||||
char bVal = pgm_read_byte_near(b + i);
|
||||
if (bVal >= 97 && bVal <= 122) {
|
||||
// Lowercase
|
||||
bVal -= 32;
|
||||
}
|
||||
// Relly we shouldn't ever get to '\0'
|
||||
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
|
||||
// We're done. one is a substring of the other
|
||||
// or something happenend and the quote didn't stop us.
|
||||
if (aVal == bVal) {
|
||||
// Same value, probably shouldn't happen
|
||||
// with this dataset
|
||||
return 0;
|
||||
}
|
||||
else if (aVal == '"' || aVal == '\0') {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (aVal == bVal) {
|
||||
// Same characters. Move to the next.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// We're done
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
} while (true);
|
||||
// We shouldn't get here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
class ModeSortUsermod : public Usermod {
|
||||
private:
|
||||
|
||||
// Pointers the start of the mode names within JSON_mode_names
|
||||
char **modes_qstrings = nullptr;
|
||||
|
||||
// Array of mode indexes in alphabetical order.
|
||||
byte *modes_alpha_indexes = nullptr;
|
||||
|
||||
// Pointers the start of the palette names within JSON_palette_names
|
||||
char **palettes_qstrings = nullptr;
|
||||
|
||||
// Array of palette indexes in alphabetical order.
|
||||
byte *palettes_alpha_indexes = nullptr;
|
||||
|
||||
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() {
|
||||
// Sort the modes and palettes on startup
|
||||
// as they are guarantted to change.
|
||||
sortModesAndPalettes();
|
||||
}
|
||||
|
||||
char **getModesQStrings() {
|
||||
return modes_qstrings;
|
||||
}
|
||||
|
||||
byte *getModesAlphaIndexes() {
|
||||
return modes_alpha_indexes;
|
||||
}
|
||||
|
||||
char **getPalettesQStrings() {
|
||||
return palettes_qstrings;
|
||||
}
|
||||
|
||||
byte *getPalettesAlphaIndexes() {
|
||||
return palettes_alpha_indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Usermod doesn't have anything for loop.
|
||||
*/
|
||||
void loop() {}
|
||||
|
||||
/**
|
||||
* Sort the modes and palettes to the index arrays
|
||||
* modes_alpha_indexes and palettes_alpha_indexes.
|
||||
*/
|
||||
void sortModesAndPalettes() {
|
||||
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
|
||||
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
|
||||
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
|
||||
|
||||
int skipPaletteCount = 1;
|
||||
while (true) {
|
||||
// How many palette names start with '*' and should not be sorted?
|
||||
// (Also skipping the first one, 'Default').
|
||||
if (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') {
|
||||
skipPaletteCount++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
|
||||
}
|
||||
|
||||
byte *re_initIndexArray(int numModes) {
|
||||
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
|
||||
for (byte i = 0; i < numModes; i++) {
|
||||
indexes[i] = i;
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of mode or palette names from the JSON string.
|
||||
* They don't end in '\0', they end in '"'.
|
||||
*/
|
||||
char **re_findModeStrings(const char json[], int numModes) {
|
||||
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
|
||||
uint8_t modeIndex = 0;
|
||||
bool insideQuotes = false;
|
||||
// advance past the mark for markLineNum that may exist.
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
bool complete = false;
|
||||
for (size_t i = 0; i < strlen_P(json); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(json + i);
|
||||
if (singleJsonSymbol == '\0') break;
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
if (insideQuotes) {
|
||||
// We have a new mode or palette
|
||||
modeStrings[modeIndex] = (char *)(json + i + 1);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
break;
|
||||
case ']':
|
||||
if (!insideQuotes) complete = true;
|
||||
break;
|
||||
case ',':
|
||||
if (!insideQuotes) modeIndex++;
|
||||
default:
|
||||
if (!insideQuotes) break;
|
||||
}
|
||||
if (complete) break;
|
||||
}
|
||||
return modeStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort either the modes or the palettes using quicksort.
|
||||
*/
|
||||
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
|
||||
listBeingSorted = modeNames;
|
||||
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
|
||||
listBeingSorted = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {}
|
||||
|
||||
/*
|
||||
* 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_MODE_SORT;
|
||||
}
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
[platformio]
|
||||
default_envs = d1_mini
|
||||
; default_envs = esp32dev
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp32}
|
||||
-D USERMOD_MODE_SORT
|
||||
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21
|
||||
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
|
||||
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
|
||||
-D LEDPIN=16 -D BTNPIN=13
|
||||
upload_speed = 460800
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
upload_speed = 460800
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_MODE_SORT
|
||||
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4
|
||||
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=12 -D ENCODER_CLK_PIN=14 -D ENCODER_SW_PIN=13
|
||||
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
|
||||
-D LEDPIN=3 -D BTNPIN=0
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env]
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.3.2
|
||||
NeoPixelBus @ 2.6.0
|
||||
ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncUDP
|
||||
AsyncTCP @ 1.0.3
|
||||
IRremoteESP8266 @ 2.7.3
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.0
|
||||
U8g2@~2.27.2
|
||||
Wire
|
@ -1,39 +0,0 @@
|
||||
# Rotary Encoder UI Usermod
|
||||
|
||||
First, thanks to the authors of other Rotary Encoder usermods.
|
||||
|
||||
This usermod starts to provide a relatively complete on-device
|
||||
UI when paired with the Four Line Display usermod. I strongly
|
||||
encourage you to try them together.
|
||||
|
||||
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and update the example `platformio_override.ini.sample` to the root directory of your particular build.
|
||||
This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_ROTARY_ENCODER_GPIO` - define the GPIO function (INPUT, INPUT_PULLUP, etc...)
|
||||
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
|
||||
also tells this usermod that the display is available
|
||||
(see the Four Line Display usermod `readme.md` for more details)
|
||||
* `ENCODER_DT_PIN` - defaults to 12
|
||||
* `ENCODER_CLK_PIN` - defaults to 14
|
||||
* `ENCODER_SW_PIN` - defaults to 13
|
||||
* `USERMOD_ROTARY_ENCODER_GPIO` - GPIO functionality:
|
||||
`INPUT_PULLUP` to use internal pull-up
|
||||
`INPUT` to use pull-up on the PCB
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
@ -1,496 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//
|
||||
// Inspired by the v1 usermods
|
||||
// * rotary_encoder_change_brightness
|
||||
// * rotary_encoder_change_effect
|
||||
//
|
||||
// v2 usermod that provides a rotary encoder-based UI.
|
||||
//
|
||||
// This usermod allows you to control:
|
||||
//
|
||||
// * Brightness
|
||||
// * Selected Effect
|
||||
// * Effect Speed
|
||||
// * Effect Intensity
|
||||
// * Palette
|
||||
//
|
||||
// Change between modes by pressing a button.
|
||||
//
|
||||
// Dependencies
|
||||
// * This usermod REQURES the ModeSortUsermod
|
||||
// * This Usermod works best coupled with
|
||||
// FourLineDisplayUsermod.
|
||||
//
|
||||
|
||||
#ifndef ENCODER_DT_PIN
|
||||
#define ENCODER_DT_PIN 12
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_CLK_PIN
|
||||
#define ENCODER_CLK_PIN 14
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_SW_PIN
|
||||
#define ENCODER_SW_PIN 13
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_FOUR_LINE_DISPLAY
|
||||
// These constants won't be defined if we aren't using FourLineDisplay.
|
||||
#define FLD_LINE_BRIGHTNESS 0
|
||||
#define FLD_LINE_MODE 0
|
||||
#define FLD_LINE_EFFECT_SPEED 0
|
||||
#define FLD_LINE_EFFECT_INTENSITY 0
|
||||
#define FLD_LINE_PALETTE 0
|
||||
#endif
|
||||
|
||||
|
||||
// The last UI state
|
||||
#define LAST_UI_STATE 4
|
||||
|
||||
|
||||
class RotaryEncoderUIUsermod : public Usermod {
|
||||
private:
|
||||
int fadeAmount = 10; // Amount to change every step (brightness)
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
|
||||
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
|
||||
int8_t pinC = ENCODER_SW_PIN; // SW from encoder
|
||||
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
|
||||
unsigned char button_state = HIGH;
|
||||
unsigned char prev_button_state = HIGH;
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
FourLineDisplayUsermod *display;
|
||||
#else
|
||||
void* display = nullptr;
|
||||
#endif
|
||||
|
||||
byte *modes_alpha_indexes = nullptr;
|
||||
byte *palettes_alpha_indexes = nullptr;
|
||||
|
||||
unsigned char Enc_A;
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
bool currentEffectAndPaletteInitialized = false;
|
||||
uint8_t effectCurrentIndex = 0;
|
||||
uint8_t effectPaletteIndex = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _DT_pin[];
|
||||
static const char _CLK_pin[];
|
||||
static const char _SW_pin[];
|
||||
|
||||
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()
|
||||
{
|
||||
DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
|
||||
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
// BUG: configuring this usermod with conflicting pins
|
||||
// will cause it to de-allocate pins it does not own
|
||||
// (at second config)
|
||||
// This is the exact type of bug solved by pinManager
|
||||
// tracking the owner tags....
|
||||
pinA = pinB = pinC = -1;
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef USERMOD_ROTARY_ENCODER_GPIO
|
||||
#define USERMOD_ROTARY_ENCODER_GPIO INPUT_PULLUP
|
||||
#endif
|
||||
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
|
||||
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
|
||||
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
|
||||
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
// This Usermod uses FourLineDisplayUsermod for the best experience.
|
||||
// But it's optional. But you want it.
|
||||
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
|
||||
if (display != nullptr) {
|
||||
display->setLineType(FLD_LINE_BRIGHTNESS);
|
||||
display->setMarkLine(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected()
|
||||
{
|
||||
//Serial.println("Connected to WiFi!");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
if (!enabled) return;
|
||||
|
||||
currentTime = millis(); // get the current elapsed time
|
||||
|
||||
// Initialize effectCurrentIndex and effectPaletteIndex to
|
||||
// current state. We do it here as (at least) effectCurrent
|
||||
// is not yet initialized when setup is called.
|
||||
if (!currentEffectAndPaletteInitialized) {
|
||||
findCurrentEffectAndPalette();
|
||||
}
|
||||
|
||||
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||
{
|
||||
button_state = digitalRead(pinC);
|
||||
if (prev_button_state != button_state)
|
||||
{
|
||||
if (button_state == LOW)
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
|
||||
char newState = select_state + 1;
|
||||
if (newState > LAST_UI_STATE) newState = 0;
|
||||
|
||||
bool changedState = true;
|
||||
if (display != nullptr) {
|
||||
switch(newState) {
|
||||
case 0:
|
||||
changedState = changeState("Brightness", FLD_LINE_BRIGHTNESS, 3);
|
||||
break;
|
||||
case 1:
|
||||
changedState = changeState("Select FX", FLD_LINE_MODE, 2);
|
||||
break;
|
||||
case 2:
|
||||
changedState = changeState("FX Speed", FLD_LINE_EFFECT_SPEED, 3);
|
||||
break;
|
||||
case 3:
|
||||
changedState = changeState("FX Intensity", FLD_LINE_EFFECT_INTENSITY, 3);
|
||||
break;
|
||||
case 4:
|
||||
changedState = changeState("Palette", FLD_LINE_PALETTE, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changedState) {
|
||||
select_state = newState;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
}
|
||||
}
|
||||
int Enc_A = digitalRead(pinA); // Read encoder pins
|
||||
int Enc_B = digitalRead(pinB);
|
||||
if ((!Enc_A) && (Enc_A_prev))
|
||||
{ // A has gone from high to low
|
||||
if (Enc_B == HIGH)
|
||||
{ // B is high so clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(true);
|
||||
break;
|
||||
case 1:
|
||||
changeEffect(true);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectSpeed(true);
|
||||
break;
|
||||
case 3:
|
||||
changeEffectIntensity(true);
|
||||
break;
|
||||
case 4:
|
||||
changePalette(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == LOW)
|
||||
{ // B is low so counter-clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(false);
|
||||
break;
|
||||
case 1:
|
||||
changeEffect(false);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectSpeed(false);
|
||||
break;
|
||||
case 3:
|
||||
changeEffectIntensity(false);
|
||||
break;
|
||||
case 4:
|
||||
changePalette(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
}
|
||||
}
|
||||
|
||||
void findCurrentEffectAndPalette() {
|
||||
currentEffectAndPaletteInitialized = true;
|
||||
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||
//byte value = modes_alpha_indexes[i];
|
||||
if (modes_alpha_indexes[i] == effectCurrent) {
|
||||
effectCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||
//byte value = palettes_alpha_indexes[i];
|
||||
if (palettes_alpha_indexes[i] == strip.getSegment(0).palette) {
|
||||
effectPaletteIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return false;
|
||||
}
|
||||
display->overlay("Mode change", stateName, 1500);
|
||||
display->setLineType(lineThreeMode);
|
||||
display->setMarkLine(markedLine);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void lampUdated() {
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
updateInterfaces(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
|
||||
}
|
||||
else {
|
||||
bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffect(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
|
||||
}
|
||||
else {
|
||||
effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
|
||||
}
|
||||
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffectSpeed(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
|
||||
}
|
||||
else {
|
||||
effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffectIntensity(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
|
||||
}
|
||||
else {
|
||||
effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changePalette(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
|
||||
}
|
||||
else {
|
||||
effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
|
||||
}
|
||||
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_DT_pin)] = pinA;
|
||||
top[FPSTR(_CLK_pin)] = pinB;
|
||||
top[FPSTR(_SW_pin)] = pinC;
|
||||
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// we look for JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
|
||||
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
|
||||
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
|
||||
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
if (pinA<0 || pinB<0 || pinC<0) {
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
setup();
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].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";
|
@ -44,10 +44,6 @@
|
||||
#include "../usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_MODE_SORT
|
||||
#include "../usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_BH1750
|
||||
#include "../usermods/BH1750_v2/usermod_BH1750.h"
|
||||
#endif
|
||||
@ -58,19 +54,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
#ifdef USE_ALT_DISPlAY
|
||||
#include "../usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h"
|
||||
#else
|
||||
#include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h"
|
||||
#endif
|
||||
#include "../usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_ROTARY_ENCODER_UI
|
||||
#ifdef USE_ALT_DISPlAY
|
||||
#include "../usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h"
|
||||
#else
|
||||
#include "../usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h"
|
||||
#endif
|
||||
#include "../usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_AUTO_SAVE
|
||||
@ -254,10 +242,6 @@ void registerUsermods()
|
||||
usermods.add(new PIRsensorSwitch());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_MODE_SORT
|
||||
usermods.add(new ModeSortUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
usermods.add(new FourLineDisplayUsermod());
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user