Update dev branch (resolve conflicts)
This commit is contained in:
commit
aa0f4c9985
22
usermods/DHT/platformio_override.ini
Normal file
22
usermods/DHT/platformio_override.ini
Normal file
@ -0,0 +1,22 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_DHT - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; USERMOD_DHT_DHTTYPE - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
|
||||
; USERMOD_DHT_PIN - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
|
||||
; USERMOD_DHT_CELSIUS - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
|
||||
; USERMOD_DHT_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
; USERMOD_DHT_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
|
||||
; USERMOD_DHT_STATS - For debug, report delay stats
|
||||
|
||||
[env:d1_mini_usermod_dht_C]
|
||||
extends = env:d1_mini
|
||||
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS
|
||||
lib_deps = ${env.lib_deps}
|
||||
https://github.com/alwynallan/DHT_nonblocking
|
||||
|
||||
[env:custom32_LEDPIN_16_usermod_dht_C]
|
||||
extends = env:custom32_LEDPIN_16
|
||||
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
|
||||
lib_deps = ${env.lib_deps}
|
||||
https://github.com/alwynallan/DHT_nonblocking
|
||||
|
41
usermods/DHT/readme.md
Normal file
41
usermods/DHT/readme.md
Normal file
@ -0,0 +1,41 @@
|
||||
# DHT Temperature/Humidity sensor usermod
|
||||
|
||||
This usermod will read from an attached DHT22 or DHT11 humidity and temperature sensor.
|
||||
The sensor readings are displayed in the Info section of the web UI.
|
||||
|
||||
If sensor is not detected after a while (10 update intervals), this usermod will be disabled.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_DHT` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_DHT_DHTTYPE` - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
|
||||
* `USERMOD_DHT_PIN` - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
|
||||
* `USERMOD_DHT_CELSIUS` - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
|
||||
* `USERMOD_DHT_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
* `USERMOD_DHT_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
|
||||
* `USERMOD_DHT_STATS` - For debug, report delay stats
|
||||
|
||||
## Project link
|
||||
|
||||
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dht_C`. If not, you can add the libraries and dependencies into `platformio.ini` as you see fit.
|
||||
|
||||
|
||||
## Change Log
|
||||
|
||||
2020-02-04
|
||||
* Change default QuinLed pin to Q2
|
||||
* Instead of trying to keep updates at constant cadence, space readings out by measurement interval; hope this helps to avoid occasional bursts of readings with errors
|
||||
* Add some more (optional) stats
|
||||
2020-02-03
|
||||
* Due to poor readouts on ESP32 with previous DHT library, rewrote to use https://github.com/alwynallan/DHT_nonblocking
|
||||
* The new library serializes/delays up to 5ms for the sensor readout
|
||||
2020-02-02
|
||||
* Created
|
216
usermods/DHT/usermod_dht.h
Normal file
216
usermods/DHT/usermod_dht.h
Normal file
@ -0,0 +1,216 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#include <dht_nonblocking.h>
|
||||
|
||||
// USERMOD_DHT_DHTTYPE:
|
||||
// 11 // DHT 11
|
||||
// 21 // DHT 21
|
||||
// 22 // DHT 22 (AM2302), AM2321 *** default
|
||||
#ifndef USERMOD_DHT_DHTTYPE
|
||||
#define USERMOD_DHT_DHTTYPE 22
|
||||
#endif
|
||||
|
||||
#if USERMOD_DHT_DHTTYPE == 11
|
||||
#define DHTTYPE DHT_TYPE_11
|
||||
#elif USERMOD_DHT_DHTTYPE == 21
|
||||
#define DHTTYPE DHT_TYPE_21
|
||||
#elif USERMOD_DHT_DHTTYPE == 22
|
||||
#define DHTTYPE DHT_TYPE_22
|
||||
#endif
|
||||
|
||||
// Connect pin 1 (on the left) of the sensor to +5V
|
||||
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
|
||||
// to 3.3V instead of 5V!
|
||||
// Connect pin 2 of the sensor to whatever your DHTPIN is
|
||||
// NOTE: Pin defaults below are for QuinLed Dig-Uno's Q2 on the board
|
||||
// Connect pin 4 (on the right) of the sensor to GROUND
|
||||
// NOTE: If using a bare sensor (AM*), Connect a 10K resistor from pin 2
|
||||
// (data) to pin 1 (power) of the sensor. DHT* boards have the pullup already
|
||||
|
||||
#ifdef USERMOD_DHT_PIN
|
||||
#define DHTPIN USERMOD_DHT_PIN
|
||||
#else
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define DHTPIN 21
|
||||
#else //ESP8266 boards
|
||||
#define DHTPIN 4
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// the frequency to check sensor, 1 minute
|
||||
#ifndef USERMOD_DHT_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_DHT_MEASUREMENT_INTERVAL 60000
|
||||
#endif
|
||||
|
||||
// how many seconds after boot to take first measurement, 90 seconds
|
||||
// 90 gives enough time to OTA update firmware if this crashses
|
||||
#ifndef USERMOD_DHT_FIRST_MEASUREMENT_AT
|
||||
#define USERMOD_DHT_FIRST_MEASUREMENT_AT 90000
|
||||
#endif
|
||||
|
||||
// from COOLDOWN_TIME in dht_nonblocking.cpp
|
||||
#define DHT_TIMEOUT_TIME 10000
|
||||
|
||||
DHT_nonblocking dht_sensor(DHTPIN, DHTTYPE);
|
||||
|
||||
class UsermodDHT : public Usermod {
|
||||
private:
|
||||
unsigned long nextReadTime = 0;
|
||||
unsigned long lastReadTime = 0;
|
||||
float humidity, temperature = 0;
|
||||
bool initializing = true;
|
||||
bool disabled = false;
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
unsigned long nextResetStatsTime = 0;
|
||||
uint16_t updates = 0;
|
||||
uint16_t clean_updates = 0;
|
||||
uint16_t errors = 0;
|
||||
unsigned long maxDelay = 0;
|
||||
unsigned long currentIteration = 0;
|
||||
unsigned long maxIteration = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
void setup() {
|
||||
nextReadTime = millis() + USERMOD_DHT_FIRST_MEASUREMENT_AT;
|
||||
lastReadTime = millis();
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
nextResetStatsTime = millis() + 60*60*1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
if (millis() < nextReadTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
if (millis() >= nextResetStatsTime) {
|
||||
nextResetStatsTime += 60*60*1000;
|
||||
errors = 0;
|
||||
updates = 0;
|
||||
clean_updates = 0;
|
||||
}
|
||||
unsigned long dcalc = millis();
|
||||
if (currentIteration == 0) {
|
||||
currentIteration = millis();
|
||||
}
|
||||
#endif
|
||||
|
||||
float tempC;
|
||||
if (dht_sensor.measure(&tempC, &humidity)) {
|
||||
#ifdef USERMOD_DHT_CELSIUS
|
||||
temperature = tempC;
|
||||
#else
|
||||
temperature = tempC * 9 / 5 + 32;
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + USERMOD_DHT_MEASUREMENT_INTERVAL;
|
||||
lastReadTime = millis();
|
||||
initializing = false;
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
unsigned long icalc = millis() - currentIteration;
|
||||
if (icalc > maxIteration) {
|
||||
maxIteration = icalc;
|
||||
}
|
||||
if (icalc > DHT_TIMEOUT_TIME) {
|
||||
errors += icalc/DHT_TIMEOUT_TIME;
|
||||
} else {
|
||||
clean_updates += 1;
|
||||
}
|
||||
updates += 1;
|
||||
currentIteration = 0;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
dcalc = millis() - dcalc;
|
||||
if (dcalc > maxDelay) {
|
||||
maxDelay = dcalc;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (((millis() - lastReadTime) > 10*USERMOD_DHT_MEASUREMENT_INTERVAL)) {
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray temp = user.createNestedArray("Temperature");
|
||||
JsonArray hum = user.createNestedArray("Humidity");
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
JsonArray next = user.createNestedArray("next");
|
||||
if (nextReadTime >= millis()) {
|
||||
next.add((nextReadTime - millis()) / 1000);
|
||||
next.add(" sec until read");
|
||||
} else {
|
||||
next.add((millis() - nextReadTime) / 1000);
|
||||
next.add(" sec active reading");
|
||||
}
|
||||
|
||||
JsonArray last = user.createNestedArray("last");
|
||||
last.add((millis() - lastReadTime) / 60000);
|
||||
last.add(" min since read");
|
||||
|
||||
JsonArray err = user.createNestedArray("errors");
|
||||
err.add(errors);
|
||||
err.add(" Errors");
|
||||
|
||||
JsonArray upd = user.createNestedArray("updates");
|
||||
upd.add(updates);
|
||||
upd.add(" Updates");
|
||||
|
||||
JsonArray cupd = user.createNestedArray("cleanUpdates");
|
||||
cupd.add(clean_updates);
|
||||
cupd.add(" Updates");
|
||||
|
||||
JsonArray iter = user.createNestedArray("maxIter");
|
||||
iter.add(maxIteration);
|
||||
iter.add(" ms");
|
||||
|
||||
JsonArray delay = user.createNestedArray("maxDelay");
|
||||
delay.add(maxDelay);
|
||||
delay.add(" ms");
|
||||
#endif
|
||||
|
||||
if (initializing) {
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
temp.add((nextReadTime - millis()) / 1000);
|
||||
temp.add(" sec until read");
|
||||
hum.add((nextReadTime - millis()) / 1000);
|
||||
hum.add(" sec until read");
|
||||
return;
|
||||
}
|
||||
|
||||
hum.add(humidity);
|
||||
hum.add("%");
|
||||
|
||||
temp.add(temperature);
|
||||
#ifdef USERMOD_DHT_CELSIUS
|
||||
temp.add("°C");
|
||||
#else
|
||||
temp.add("°F");
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_DHT;
|
||||
}
|
||||
|
||||
};
|
45
usermods/usermod_v2_auto_save/readme.md
Normal file
45
usermods/usermod_v2_auto_save/readme.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Auto Save
|
||||
|
||||
v2 Usermod to automatically save settings
|
||||
to preset number AUTOSAVE_PRESET_NUM after a change to any of
|
||||
|
||||
* brightness
|
||||
* effect speed
|
||||
* effect intensity
|
||||
* mode (effect)
|
||||
* palette
|
||||
|
||||
but it will wait for AUTOSAVE_SETTLE_MS milliseconds, a "settle"
|
||||
period in case there are other changes (any change will
|
||||
extend the "settle" window).
|
||||
|
||||
It will additionally load preset AUTOSAVE_PRESET_NUM at startup.
|
||||
during the first `loop()`. Reasoning below.
|
||||
|
||||
AutoSaveUsermod is standalone, but if FourLineDisplayUsermod is installed, it will notify the user of the saved changes.
|
||||
|
||||
Note: I don't love that WLED doesn't respect the brightness of the preset being auto loaded, so the AutoSaveUsermod will set the AUTOSAVE_PRESET_NUM preset in the first loop, so brightness IS honored. This means WLED will effectively ignore Default brightness and Apply N preset at boot when the AutoSaveUsermod is installed.
|
||||
|
||||
## 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_AUTO_SAVE` - define this to have this the Auto Save usermod included wled00\usermods_list.cpp
|
||||
* `USERMOD_FOUR_LINE_DISLAY` - 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)
|
||||
* `AUTOSAVE_SETTLE_MS` - Minimum time to wave before auto saving, defaults to 10000 (10s)
|
||||
* `AUTOSAVE_PRESET_NUM` - Preset number to auto-save to, auto-load at startup from, defaults to 99
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
192
usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
Normal file
192
usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
Normal file
@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//
|
||||
// v2 Usermod to automatically save settings
|
||||
// to preset number AUTOSAVE_PRESET_NUM after a change to any of
|
||||
//
|
||||
// * brightness
|
||||
// * effect speed
|
||||
// * effect intensity
|
||||
// * mode (effect)
|
||||
// * palette
|
||||
//
|
||||
// but it will wait for AUTOSAVE_SETTLE_MS milliseconds, a "settle"
|
||||
// period in case there are other changes (any change will
|
||||
// extend the "settle" window).
|
||||
//
|
||||
// It will additionally load preset AUTOSAVE_PRESET_NUM at startup.
|
||||
// during the first `loop()`. Reasoning below.
|
||||
//
|
||||
// AutoSaveUsermod is standalone, but if FourLineDisplayUsermod
|
||||
// is installed, it will notify the user of the saved changes.
|
||||
//
|
||||
// Note: I don't love that WLED doesn't respect the brightness
|
||||
// of the preset being auto loaded, so the AutoSaveUsermod
|
||||
// will set the AUTOSAVE_PRESET_NUM preset in the first loop,
|
||||
// so brightness IS honored. This means WLED will effectively
|
||||
// ignore Default brightness and Apply N preset at boot when
|
||||
// the AutoSaveUsermod is installed.
|
||||
|
||||
//How long to wait after settings change to auto-save
|
||||
#ifndef AUTOSAVE_SETTLE_MS
|
||||
#define AUTOSAVE_SETTLE_MS 10*1000
|
||||
#endif
|
||||
|
||||
//Preset number to save to
|
||||
#ifndef AUTOSAVE_PRESET_NUM
|
||||
#define AUTOSAVE_PRESET_NUM 99
|
||||
#endif
|
||||
|
||||
// "Auto save MM-DD HH:MM:SS"
|
||||
#define PRESET_NAME_BUFFER_SIZE 25
|
||||
|
||||
class AutoSaveUsermod : public Usermod {
|
||||
private:
|
||||
// If we've detected the need to auto save, this will
|
||||
// be non zero.
|
||||
unsigned long autoSaveAfter = 0;
|
||||
|
||||
char presetNameBuffer[PRESET_NAME_BUFFER_SIZE];
|
||||
|
||||
bool firstLoop = true;
|
||||
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownEffectSpeed = 0;
|
||||
uint8_t knownEffectIntensity = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
FourLineDisplayUsermod* display;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
// This Usermod has enhanced funcionality if
|
||||
// FourLineDisplayUsermod is available.
|
||||
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
|
||||
#endif
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/**
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
unsigned long now = millis();
|
||||
uint8_t currentMode = strip.getMode();
|
||||
uint8_t currentPalette = strip.getSegment(0).palette;
|
||||
if (firstLoop) {
|
||||
firstLoop = false;
|
||||
applyPreset(AUTOSAVE_PRESET_NUM);
|
||||
knownBrightness = bri;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
knownMode = currentMode;
|
||||
knownPalette = currentPalette;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long wouldAutoSaveAfter = now + AUTOSAVE_SETTLE_MS;
|
||||
if (knownBrightness != bri) {
|
||||
knownBrightness = bri;
|
||||
autoSaveAfter = wouldAutoSaveAfter;
|
||||
} else if (knownEffectSpeed != effectSpeed) {
|
||||
knownEffectSpeed = effectSpeed;
|
||||
autoSaveAfter = wouldAutoSaveAfter;
|
||||
} else if (knownEffectIntensity != effectIntensity) {
|
||||
knownEffectIntensity = effectIntensity;
|
||||
autoSaveAfter = wouldAutoSaveAfter;
|
||||
} else if (knownMode != currentMode) {
|
||||
knownMode = currentMode;
|
||||
autoSaveAfter = wouldAutoSaveAfter;
|
||||
} else if (knownPalette != currentPalette) {
|
||||
knownPalette = currentPalette;
|
||||
autoSaveAfter = wouldAutoSaveAfter;
|
||||
}
|
||||
|
||||
if (autoSaveAfter && now > autoSaveAfter) {
|
||||
autoSaveAfter = 0;
|
||||
// Time to auto save. You may have some flickry?
|
||||
saveSettings();
|
||||
displayOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
void saveSettings() {
|
||||
updateLocalTime();
|
||||
sprintf(presetNameBuffer,
|
||||
"Auto save %02d-%02d %02d:%02d:%02d",
|
||||
month(localTime), day(localTime),
|
||||
hour(localTime), minute(localTime), second(localTime));
|
||||
savePreset(AUTOSAVE_PRESET_NUM, true, presetNameBuffer);
|
||||
}
|
||||
|
||||
void displayOverlay() {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
if (display != nullptr) {
|
||||
display->wakeDisplay();
|
||||
display->overlay("Settings", "Auto Saved", 1500);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
*
|
||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||
* Use it sparingly and always in the loop, never in network callbacks!
|
||||
*
|
||||
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
||||
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root) {
|
||||
}
|
||||
|
||||
/*
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
||||
*
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*/
|
||||
void readFromConfig(JsonObject& root) {
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_AUTO_SAVE;
|
||||
}
|
||||
|
||||
};
|
39
usermods/usermod_v2_four_line_display/readme.md
Normal file
39
usermods/usermod_v2_four_line_display/readme.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Rotary Encoder UI Usermod
|
||||
|
||||
First, thanks to the authors of the ssd11306_i2c_oled_u8g2 mod.
|
||||
|
||||
This usermod provides a four line display using either
|
||||
128x32 or 128x64 OLED displays.
|
||||
It's 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_DISLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells Rotary Encoder usermod, if installed, that the display is available
|
||||
* `FLD_PIN_SCL` - The display SCL pin, defaults to 5
|
||||
* `FLD_PIN_SDA` - The display SDA pin, defaults to 4
|
||||
* `FLIP_MODE` - Set to 0 or 1
|
||||
* `LINE_HEIGHT` - Set to 1 or 2
|
||||
|
||||
There are other `#define` values in the Usermod that might be of interest.
|
||||
|
||||
### 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`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
@ -0,0 +1,530 @@
|
||||
#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.
|
||||
//
|
||||
// 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 5
|
||||
#endif
|
||||
|
||||
#ifndef FLD_PIN_SDA
|
||||
#define FLD_PIN_SDA 4
|
||||
#endif
|
||||
|
||||
// U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(
|
||||
// U8X8_PIN_NONE, FLD_PIN_SCL, FLD_PIN_SDA);
|
||||
U8X8_SH1106_128X64_WINSTAR_HW_I2C u8x8(
|
||||
U8X8_PIN_NONE, FLD_PIN_SCL, FLD_PIN_SDA);
|
||||
|
||||
// Screen upside down? Change to 0 or 1
|
||||
#ifndef FLIP_MODE
|
||||
#define FLIP_MODE 0
|
||||
#endif
|
||||
|
||||
// LINE_HEIGHT 1 is single height, for 128x32 displays.
|
||||
// LINE_HEIGHT 2 makes the 128x64 screen display at double height.
|
||||
#ifndef LINE_HEIGHT
|
||||
#define LINE_HEIGHT 2
|
||||
#endif
|
||||
|
||||
// If you aren't also including RotaryEncoderUIUsermod
|
||||
// you probably want to set both
|
||||
// SLEEP_MODE_ENABLED false
|
||||
// CLOCK_MODE_ENABLED false
|
||||
// as you will never be able wake the display / disable the clock.
|
||||
#ifdef USERMOD_ROTARY_ENCODER_UI
|
||||
#ifndef SLEEP_MODE_ENABLED
|
||||
#define SLEEP_MODE_ENABLED true
|
||||
#endif
|
||||
#ifndef CLOCK_MODE_ENABLED
|
||||
#define CLOCK_MODE_ENABLED true
|
||||
#endif
|
||||
#else
|
||||
#define SLEEP_MODE_ENABLED false
|
||||
#define CLOCK_MODE_ENABLED false
|
||||
#endif
|
||||
|
||||
// When to time out to the clock or blank the screen
|
||||
// if SLEEP_MODE_ENABLED.
|
||||
#define SCREEN_TIMEOUT_MS 15*1000
|
||||
|
||||
#define TIME_INDENT 0
|
||||
#define DATE_INDENT 2
|
||||
|
||||
// Minimum time between redrawing screen in ms
|
||||
#define USER_LOOP_REFRESH_RATE_MS 1000
|
||||
|
||||
#if LINE_HEIGHT == 2
|
||||
#define DRAW_STRING draw1x2String
|
||||
#define DRAW_GLYPH draw1x2Glyph
|
||||
#define DRAW_BIG_STRING draw2x2String
|
||||
#else
|
||||
#define DRAW_STRING drawString
|
||||
#define DRAW_GLYPH drawGlyph
|
||||
#define DRAW_BIG_STRING draw2x2String
|
||||
#endif
|
||||
|
||||
// Extra char (+1) for null
|
||||
#define LINE_BUFFER_SIZE 16+1
|
||||
#define FLD_LINE_3_BRIGHTNESS 0
|
||||
#define FLD_LINE_3_EFFECT_SPEED 1
|
||||
#define FLD_LINE_3_EFFECT_INTENSITY 2
|
||||
#define FLD_LINE_3_PALETTE 3
|
||||
|
||||
#if LINE_HEIGHT == 2
|
||||
#define TIME_LINE 1
|
||||
#else
|
||||
#define TIME_LINE 0
|
||||
#endif
|
||||
|
||||
class FourLineDisplayUsermod : public Usermod {
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// 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 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;
|
||||
long lastUpdate = 0;
|
||||
long lastRedraw = 0;
|
||||
long overlayUntil = 0;
|
||||
byte lineThreeType = FLD_LINE_3_BRIGHTNESS;
|
||||
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||
byte markLineNum = 0;
|
||||
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
|
||||
// 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() {
|
||||
u8x8.begin();
|
||||
u8x8.setFlipMode(FLIP_MODE);
|
||||
u8x8.setPowerSave(0);
|
||||
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8.DRAW_STRING(0, 0*LINE_HEIGHT, "Loading...");
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/**
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
|
||||
return;
|
||||
}
|
||||
lastUpdate = millis();
|
||||
|
||||
redraw(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraw the screen (but only if things have changed
|
||||
* or if forceRedraw).
|
||||
*/
|
||||
void redraw(bool forceRedraw) {
|
||||
if (overlayUntil > 0) {
|
||||
if (millis() >= 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) {
|
||||
needRedraw = true;
|
||||
} else if (((apActive) ? 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 (knownEffectSpeed != effectSpeed) {
|
||||
needRedraw = true;
|
||||
} else if (knownEffectIntensity != effectIntensity) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
if (!needRedraw) {
|
||||
// Nothing to change.
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(SLEEP_MODE_ENABLED && !displayTurnedOff &&
|
||||
(millis() - lastRedraw > SCREEN_TIMEOUT_MS)) {
|
||||
// We will still check if there is a change in redraw()
|
||||
// and turn it back on if it changed.
|
||||
sleepOrClock(true);
|
||||
}
|
||||
else if (displayTurnedOff && CLOCK_MODE_ENABLED) {
|
||||
showTime();
|
||||
}
|
||||
return;
|
||||
}
|
||||
needRedraw = false;
|
||||
lastRedraw = millis();
|
||||
|
||||
if (displayTurnedOff)
|
||||
{
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
}
|
||||
|
||||
// Update last known values.
|
||||
#if defined(ESP8266)
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
#else
|
||||
knownSsid = WiFi.SSID();
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
|
||||
// Do the actual drawing
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
// First row with Wifi name
|
||||
String ssidString = knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0);
|
||||
u8x8.DRAW_STRING(1, 0*LINE_HEIGHT, ssidString.c_str());
|
||||
// Print `~` char to indicate that SSID is longer, than owr dicplay
|
||||
if (knownSsid.length() > u8x8.getCols()) {
|
||||
u8x8.DRAW_STRING(u8x8.getCols() - 1, 0*LINE_HEIGHT, "~");
|
||||
}
|
||||
|
||||
// Second row with IP or Psssword
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0) {
|
||||
u8x8.DRAW_STRING(1, 1*LINE_HEIGHT, apPass);
|
||||
}
|
||||
else {
|
||||
String ipString = knownIp.toString();
|
||||
u8x8.DRAW_STRING(1, 1*LINE_HEIGHT, ipString.c_str());
|
||||
}
|
||||
|
||||
// Third row with mode name
|
||||
showCurrentEffectOrPalette(JSON_mode_names, 2, knownMode);
|
||||
|
||||
switch(lineThreeType) {
|
||||
case FLD_LINE_3_BRIGHTNESS:
|
||||
sprintf(lineBuffer, "Brightness %d", bri);
|
||||
u8x8.DRAW_STRING(1, 3*LINE_HEIGHT, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_3_EFFECT_SPEED:
|
||||
sprintf(lineBuffer, "FX Speed %d", effectSpeed);
|
||||
u8x8.DRAW_STRING(1, 3*LINE_HEIGHT, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_3_EFFECT_INTENSITY:
|
||||
sprintf(lineBuffer, "FX Intense %d", effectIntensity);
|
||||
u8x8.DRAW_STRING(1, 3*LINE_HEIGHT, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_3_PALETTE:
|
||||
showCurrentEffectOrPalette(JSON_palette_names, 3, knownPalette);
|
||||
break;
|
||||
}
|
||||
|
||||
u8x8.setFont(u8x8_font_open_iconic_arrow_1x1);
|
||||
u8x8.DRAW_GLYPH(0, markLineNum*LINE_HEIGHT, 66); // arrow icon
|
||||
|
||||
u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
|
||||
u8x8.DRAW_GLYPH(0, 0*LINE_HEIGHT, 80); // wifi icon
|
||||
u8x8.DRAW_GLYPH(0, 1*LINE_HEIGHT, 68); // home icon
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the current effect or palette (desiredEntry)
|
||||
* on the appropriate line (row).
|
||||
*
|
||||
* TODO: Should we cache the current effect and
|
||||
* TODO: palette name? This seems expensive.
|
||||
*/
|
||||
void showCurrentEffectOrPalette(const char json[], uint8_t row, uint8_t desiredEntry) {
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
// advance past the mark for markLineNum that may exist.
|
||||
uint8_t printedChars = 1;
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < strlen_P(json); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(json + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != desiredEntry)) {
|
||||
break;
|
||||
}
|
||||
u8x8.DRAW_GLYPH(printedChars, row * LINE_HEIGHT, singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > desiredEntry) || (printedChars > u8x8.getCols() - 2)) {
|
||||
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 (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 (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
}
|
||||
|
||||
// Print the overlay
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
if (line1) {
|
||||
u8x8.DRAW_STRING(0, 1*LINE_HEIGHT, line1);
|
||||
}
|
||||
if (line2) {
|
||||
u8x8.DRAW_STRING(0, 2*LINE_HEIGHT, line2);
|
||||
}
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify what data should be defined on line 3
|
||||
* (the last line).
|
||||
*/
|
||||
void setLineThreeType(byte newLineThreeType) {
|
||||
if (newLineThreeType == FLD_LINE_3_BRIGHTNESS ||
|
||||
newLineThreeType == FLD_LINE_3_EFFECT_SPEED ||
|
||||
newLineThreeType == FLD_LINE_3_EFFECT_INTENSITY ||
|
||||
newLineThreeType == FLD_LINE_3_PALETTE) {
|
||||
lineThreeType = newLineThreeType;
|
||||
}
|
||||
else {
|
||||
// Unknown value.
|
||||
lineThreeType = FLD_LINE_3_BRIGHTNESS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Line 2 or 3 (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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enable sleep (turn the display off) or clock mode.
|
||||
*/
|
||||
void sleepOrClock(bool enabled) {
|
||||
if (enabled) {
|
||||
if (CLOCK_MODE_ENABLED) {
|
||||
showTime();
|
||||
}
|
||||
else {
|
||||
u8x8.setPowerSave(1);
|
||||
}
|
||||
displayTurnedOff = true;
|
||||
}
|
||||
else {
|
||||
if (!CLOCK_MODE_ENABLED) {
|
||||
u8x8.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() {
|
||||
updateLocalTime();
|
||||
byte minuteCurrent = minute(localTime);
|
||||
byte hourCurrent = hour(localTime);
|
||||
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
|
||||
// Time hasn't changed.
|
||||
return;
|
||||
}
|
||||
knownMinute = minuteCurrent;
|
||||
knownHour = hourCurrent;
|
||||
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
int currentMonth = month(localTime);
|
||||
sprintf(lineBuffer, "%s %d", monthShortStr(currentMonth), day(localTime));
|
||||
u8x8.DRAW_BIG_STRING(DATE_INDENT, TIME_LINE*LINE_HEIGHT, 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(lineBuffer, "%02d:%02d %s", showHour, minuteCurrent, useAMPM ? (isAM ? "AM" : "PM") : "");
|
||||
// For time, we always use LINE_HEIGHT of 2 since
|
||||
// we are printing it big.
|
||||
u8x8.DRAW_BIG_STRING(TIME_INDENT + (useAMPM ? 0 : 2), (TIME_LINE + 1) * 2, lineBuffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
*
|
||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||
* Use it sparingly and always in the loop, never in network callbacks!
|
||||
*
|
||||
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
||||
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root) {
|
||||
}
|
||||
|
||||
/*
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
||||
*
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*/
|
||||
void readFromConfig(JsonObject& root) {
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_FOUR_LINE_DISP;
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
[platformio]
|
||||
default_envs = d1_mini
|
||||
; default_envs = esp32dev
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = espressif32@2.0
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp32}
|
||||
-D USERMOD_FOUR_LINE_DISLAY -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_FOUR_LINE_DISLAY -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
|
33
usermods/usermod_v2_rotary_encoder_ui/readme.md
Normal file
33
usermods/usermod_v2_rotary_encoder_ui/readme.md
Normal file
@ -0,0 +1,33 @@
|
||||
# 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_FOUR_LINE_DISLAY` - 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` - The encoders DT pin, defaults to 12
|
||||
* `ENCODER_CLK_PIN` - The encoders CLK pin, defaults to 14
|
||||
* `ENCODER_SW_PIN` - The encoders SW pin, defaults to 13
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
@ -0,0 +1,420 @@
|
||||
#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 works best coupled with FourLineDisplayUsermod.
|
||||
//
|
||||
// This usermod allows you to control:
|
||||
//
|
||||
// * Brightness
|
||||
// * Selected Effect
|
||||
// * Effect Speed
|
||||
// * Effect Intensity
|
||||
// * Palette
|
||||
//
|
||||
// Change between modes by pressing a button.
|
||||
//
|
||||
|
||||
#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_DISLAY
|
||||
// These constants won't be defined if we aren't using FourLineDisplay.
|
||||
#define FLD_LINE_3_BRIGHTNESS 0
|
||||
#define FLD_LINE_3_EFFECT_SPEED 0
|
||||
#define FLD_LINE_3_EFFECT_INTENSITY 0
|
||||
#define FLD_LINE_3_PALETTE 0
|
||||
#endif
|
||||
|
||||
// The last UI state
|
||||
#define LAST_UI_STATE 4
|
||||
|
||||
/**
|
||||
* Array of mode indexes in alphabetical order.
|
||||
* Should be ordered from JSON_mode_names array in FX.h.
|
||||
*
|
||||
* NOTE: If JSON_mode_names changes, this will need to be updated.
|
||||
*/
|
||||
const byte modes_alpha_order[] = {
|
||||
0, 27, 38, 115, 1, 26, 91, 68, 2, 88, 102, 114, 28, 31, 32,
|
||||
30, 29, 111, 52, 34, 8, 74, 67, 112, 18, 19, 96, 7, 117, 12,
|
||||
69, 66, 45, 42, 90, 89, 110, 87, 46, 53, 82, 100, 58, 64, 75,
|
||||
41, 57, 47, 44, 76, 77, 59, 70, 71, 72, 73, 107, 62, 101, 65,
|
||||
98, 105, 109, 97, 48, 49, 95, 63, 78, 43, 9, 33, 5, 79, 99,
|
||||
15, 37, 16, 10, 11, 40, 60, 108, 92, 93, 94, 103, 83, 84, 20,
|
||||
21, 22, 85, 86, 39, 61, 23, 25, 24, 104, 6, 36, 13, 14, 35,
|
||||
54, 56, 55, 116, 17, 81, 80, 106, 51, 50, 113, 3, 4 };
|
||||
|
||||
/**
|
||||
* Array of palette indexes in alphabetical order.
|
||||
* Should be ordered from JSON_palette_names array in FX.h.
|
||||
*
|
||||
* NOTE: If JSON_palette_names changes, this will need to be updated.
|
||||
*/
|
||||
const byte palettes_alpha_order[] = {
|
||||
0, 1, 2, 3, 4, 5, 18, 46, 51, 50, 55, 39, 26, 22, 15,
|
||||
48, 52, 53, 7, 37, 24, 30, 35, 10, 32, 28, 29, 36, 31,
|
||||
25, 8, 38, 40, 41, 9, 44, 47, 6, 20, 11, 12, 16, 33,
|
||||
14, 49, 27, 19, 13, 21, 54, 34, 45, 23, 43, 17, 42 };
|
||||
|
||||
class RotaryEncoderUIUsermod : public Usermod {
|
||||
private:
|
||||
int fadeAmount = 10; // Amount to change every step (brightness)
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
const int pinA = ENCODER_DT_PIN; // DT from encoder
|
||||
const int pinB = ENCODER_CLK_PIN; // CLK from encoder
|
||||
const int 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_DISLAY
|
||||
FourLineDisplayUsermod* display;
|
||||
#else
|
||||
void* display = nullptr;
|
||||
#endif
|
||||
unsigned char Enc_A;
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
bool currentEffectAndPaleeteInitialized = false;
|
||||
uint8_t effectCurrentIndex = 0;
|
||||
uint8_t effectPaletteIndex = 0;
|
||||
|
||||
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()
|
||||
{
|
||||
pinMode(pinA, INPUT_PULLUP);
|
||||
pinMode(pinB, INPUT_PULLUP);
|
||||
pinMode(pinC, INPUT_PULLUP);
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
// 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->setLineThreeType(FLD_LINE_3_BRIGHTNESS);
|
||||
display->setMarkLine(3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
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 (!currentEffectAndPaleeteInitialized) {
|
||||
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_3_BRIGHTNESS, 3);
|
||||
break;
|
||||
case 1:
|
||||
changedState = changeState("Select FX", FLD_LINE_3_EFFECT_SPEED, 2);
|
||||
break;
|
||||
case 2:
|
||||
changedState = changeState("FX Speed", FLD_LINE_3_EFFECT_SPEED, 3);
|
||||
break;
|
||||
case 3:
|
||||
changedState = changeState("FX Intensity", FLD_LINE_3_EFFECT_INTENSITY, 3);
|
||||
break;
|
||||
case 4:
|
||||
changedState = changeState("Palette", FLD_LINE_3_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() {
|
||||
currentEffectAndPaleeteInitialized = true;
|
||||
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||
byte value = modes_alpha_order[i];
|
||||
if (modes_alpha_order[i] == effectCurrent) {
|
||||
effectCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||
byte value = palettes_alpha_order[i];
|
||||
if (palettes_alpha_order[i] == strip.getSegment(0).palette) {
|
||||
effectPaletteIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return false;
|
||||
}
|
||||
display->overlay("Mode change", stateName, 1500);
|
||||
display->setLineThreeType(lineThreeMode);
|
||||
display->setMarkLine(markedLine);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void lampUdated() {
|
||||
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
|
||||
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
|
||||
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||
updateInterfaces(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
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_DISLAY
|
||||
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_order[effectCurrentIndex];
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffectSpeed(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
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_DISLAY
|
||||
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_DISLAY
|
||||
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_order[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!"));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
@ -1001,14 +1001,6 @@ uint16_t WS2812FX::mode_running_color(void) {
|
||||
return running(SEGCOLOR(0), SEGCOLOR(1));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Alternating red/green pixels running.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_merry_christmas(void) {
|
||||
return running(RED, GREEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternating red/white pixels running.
|
||||
*/
|
||||
@ -1991,7 +1983,7 @@ uint16_t WS2812FX::mode_colortwinkle()
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
CRGB fastled_col, prev;
|
||||
fract8 fadeUpAmount = 8 + (SEGMENT.speed/4), fadeDownAmount = 5 + (SEGMENT.speed/7);
|
||||
fract8 fadeUpAmount = _brightness>28 ? 8 + (SEGMENT.speed>>2) : 68-_brightness, fadeDownAmount = _brightness>28 ? 8 + (SEGMENT.speed>>3) : 68-_brightness;
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
fastled_col = col_to_crgb(getPixelColor(i));
|
||||
prev = fastled_col;
|
||||
@ -3144,6 +3136,59 @@ uint16_t WS2812FX::mode_drip(void)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Tetris or Stacking (falling bricks) Effect
|
||||
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
|
||||
*/
|
||||
typedef struct Tetris {
|
||||
float pos;
|
||||
float speed;
|
||||
uint32_t col;
|
||||
} tetris;
|
||||
|
||||
uint16_t WS2812FX::mode_tetrix(void) {
|
||||
|
||||
uint16_t dataSize = sizeof(tetris);
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
Tetris* drop = reinterpret_cast<Tetris*>(SEGENV.data);
|
||||
|
||||
// initialize dropping on first call or segment full
|
||||
if (SEGENV.call == 0 || SEGENV.aux1 >= SEGLEN) {
|
||||
SEGENV.aux1 = 0; // reset brick stack size
|
||||
SEGENV.step = 0;
|
||||
fill(SEGCOLOR(1));
|
||||
return 250; // short wait
|
||||
}
|
||||
|
||||
if (SEGENV.step == 0) { //init
|
||||
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed
|
||||
drop->pos = SEGLEN-1; // start at end of segment
|
||||
drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap
|
||||
SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling)
|
||||
SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
|
||||
}
|
||||
|
||||
if (SEGENV.step == 1) { // forming
|
||||
if (random8()>>6) { // random drop
|
||||
SEGENV.step = 2; // fall
|
||||
}
|
||||
}
|
||||
|
||||
if (SEGENV.step > 1) { // falling
|
||||
if (drop->pos > SEGENV.aux1) { // fall until top of stack
|
||||
drop->pos -= drop->speed; // may add gravity as: speed += gravity
|
||||
if (int(drop->pos) < SEGENV.aux1) drop->pos = SEGENV.aux1;
|
||||
for (uint16_t i=int(drop->pos); i<SEGLEN; i++) setPixelColor(i,i<int(drop->pos)+SEGENV.aux0 ? drop->col : SEGCOLOR(1));
|
||||
} else { // we hit bottom
|
||||
SEGENV.step = 0; // go back to init
|
||||
SEGENV.aux1 += SEGENV.aux0; // increase the stack size
|
||||
if (SEGENV.aux1 >= SEGLEN) return 1000; // wait for a second
|
||||
}
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/ Plasma Effect
|
||||
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
|
||||
|
14
wled00/FX.h
14
wled00/FX.h
@ -160,7 +160,7 @@
|
||||
#define FX_MODE_COMET 41
|
||||
#define FX_MODE_FIREWORKS 42
|
||||
#define FX_MODE_RAIN 43
|
||||
#define FX_MODE_MERRY_CHRISTMAS 44
|
||||
#define FX_MODE_TETRIX 44
|
||||
#define FX_MODE_FIRE_FLICKER 45
|
||||
#define FX_MODE_GRADIENT 46
|
||||
#define FX_MODE_LOADING 47
|
||||
@ -497,7 +497,7 @@ class WS2812FX {
|
||||
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
|
||||
_mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks;
|
||||
_mode[FX_MODE_RAIN] = &WS2812FX::mode_rain;
|
||||
_mode[FX_MODE_MERRY_CHRISTMAS] = &WS2812FX::mode_merry_christmas;
|
||||
_mode[FX_MODE_TETRIX] = &WS2812FX::mode_tetrix;
|
||||
_mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker;
|
||||
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
|
||||
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
|
||||
@ -721,7 +721,7 @@ class WS2812FX {
|
||||
mode_comet(void),
|
||||
mode_fireworks(void),
|
||||
mode_rain(void),
|
||||
mode_merry_christmas(void),
|
||||
mode_tetrix(void),
|
||||
mode_halloween(void),
|
||||
mode_fire_flicker(void),
|
||||
mode_gradient(void),
|
||||
@ -850,7 +850,11 @@ class WS2812FX {
|
||||
|
||||
void
|
||||
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend),
|
||||
startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot);
|
||||
startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot),
|
||||
deserializeMap(void);
|
||||
|
||||
uint16_t* customMappingTable = nullptr;
|
||||
uint16_t customMappingSize = 0;
|
||||
|
||||
uint32_t _lastPaletteChange = 0;
|
||||
uint32_t _lastShow = 0;
|
||||
@ -881,7 +885,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
|
||||
"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
|
||||
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random",
|
||||
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Aurora","Stream",
|
||||
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All",
|
||||
"Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Police All",
|
||||
"Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
|
||||
"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
|
||||
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple",
|
||||
|
@ -27,27 +27,26 @@
|
||||
#include "FX.h"
|
||||
#include "palettes.h"
|
||||
|
||||
//enable custom per-LED mapping. This can allow for better effects on matrices or special displays
|
||||
//#define WLED_CUSTOM_LED_MAPPING
|
||||
|
||||
#ifdef WLED_CUSTOM_LED_MAPPING
|
||||
//this is just an example (30 LEDs). It will first set all even, then all uneven LEDs.
|
||||
const uint16_t customMappingTable[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,
|
||||
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29};
|
||||
|
||||
//another example. Switches direction every 5 LEDs.
|
||||
/*const uint16_t customMappingTable[] = {
|
||||
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14,
|
||||
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25};*/
|
||||
|
||||
const uint16_t customMappingSize = sizeof(customMappingTable)/sizeof(uint16_t); //30 in example
|
||||
#endif
|
||||
|
||||
#ifndef PWM_INDEX
|
||||
#define PWM_INDEX 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
Custom per-LED mapping has moved!
|
||||
|
||||
Create a file "ledmap.json" using the edit page.
|
||||
|
||||
this is just an example (30 LEDs). It will first set all even, then all uneven LEDs.
|
||||
{"map":[
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,
|
||||
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]}
|
||||
|
||||
another example. Switches direction every 5 LEDs.
|
||||
{"map":[
|
||||
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14,
|
||||
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]
|
||||
*/
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void WS2812FX::finalizeInit(bool supportWhite, uint16_t countPixels, bool skipFirst)
|
||||
{
|
||||
@ -68,6 +67,8 @@ void WS2812FX::finalizeInit(bool supportWhite, uint16_t countPixels, bool skipFi
|
||||
BusConfig defCfg = BusConfig(TYPE_WS2812_RGB, defPin, 0, _lengthRaw, COL_ORDER_GRB);
|
||||
busses.add(defCfg);
|
||||
}
|
||||
|
||||
deserializeMap();
|
||||
|
||||
_segments[0].start = 0;
|
||||
_segments[0].stop = _length;
|
||||
@ -202,9 +203,7 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
int16_t indexSet = realIndex + (reversed ? -j : j);
|
||||
int16_t indexSetRev = indexSet;
|
||||
if (reverseMode) indexSetRev = REV(indexSet);
|
||||
#ifdef WLED_CUSTOM_LED_MAPPING
|
||||
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
|
||||
#endif
|
||||
if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) {
|
||||
busses.setPixelColor(indexSet + skip, col);
|
||||
if (IS_MIRROR) { //set the corresponding mirrored pixel
|
||||
@ -218,9 +217,8 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
}
|
||||
} else { //live data, etc.
|
||||
if (reverseMode) i = REV(i);
|
||||
#ifdef WLED_CUSTOM_LED_MAPPING
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
#endif
|
||||
|
||||
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
busses.setPixelColor(i + skip, col);
|
||||
}
|
||||
@ -499,9 +497,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
i = realPixelIndex(i);
|
||||
|
||||
#ifdef WLED_CUSTOM_LED_MAPPING
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
#endif
|
||||
|
||||
if (_skipFirstMode) i += LED_SKIP_AMOUNT;
|
||||
|
||||
@ -986,6 +982,31 @@ bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b)
|
||||
}
|
||||
|
||||
|
||||
//load custom mapping table from JSON file
|
||||
void WS2812FX::deserializeMap(void) {
|
||||
if (!WLED_FS.exists("/ledmap.json")) return;
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps
|
||||
|
||||
DEBUG_PRINTLN(F("Reading LED map from /ledmap.json..."));
|
||||
|
||||
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
|
||||
|
||||
if (customMappingTable != nullptr) {
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
customMappingSize = 0;
|
||||
}
|
||||
|
||||
JsonArray map = doc[F("map")];
|
||||
if (!map.isNull() && map.size()) { // not an empty map
|
||||
customMappingSize = map.size();
|
||||
customMappingTable = new uint16_t[customMappingSize];
|
||||
for (uint16_t i=0; i<customMappingSize; i++) {
|
||||
customMappingTable[i] = (uint16_t) map[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//gamma 2.8 lookup table used for color correction
|
||||
byte gammaT[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
@ -27,6 +27,10 @@
|
||||
#define USERMOD_ID_FIXNETSERVICES 4 //Usermod "usermod_Fix_unreachable_netservices.h"
|
||||
#define USERMOD_ID_PIRSWITCH 5 //Usermod "usermod_PIR_sensor_switch.h"
|
||||
#define USERMOD_ID_IMU 6 //Usermod "usermod_mpu6050_imu.h"
|
||||
#define USERMOD_ID_FOUR_LINE_DISP 7 //Usermod "usermod_v2_four_line_display.h
|
||||
#define USERMOD_ID_ROTARY_ENC_UI 8 //Usermod "usermod_v2_rotary_encoder_ui.h"
|
||||
#define USERMOD_ID_AUTO_SAVE 9 //Usermod "usermod_v2_auto_save.h"
|
||||
#define USERMOD_ID_DHT 10 //Usermod "usermod_dht.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
|
@ -205,6 +205,7 @@ class UsermodManager {
|
||||
void readFromConfig(JsonObject& obj);
|
||||
|
||||
bool add(Usermod* um);
|
||||
Usermod* lookup(uint16_t mod_id);
|
||||
byte getModCount();
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,18 @@ void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < n
|
||||
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
|
||||
void UsermodManager::readFromConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromConfig(obj); }
|
||||
|
||||
/*
|
||||
* Enables usermods to lookup another Usermod.
|
||||
*/
|
||||
Usermod* UsermodManager::lookup(uint16_t mod_id) {
|
||||
for (byte i = 0; i < numMods; i++) {
|
||||
if (ums[i]->getId() == mod_id) {
|
||||
return ums[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UsermodManager::add(Usermod* um)
|
||||
{
|
||||
if (numMods >= WLED_MAX_USERMODS || um == nullptr) return false;
|
||||
|
@ -23,10 +23,24 @@
|
||||
#include "usermod_v2_SensorsToMqtt.h"
|
||||
#endif
|
||||
|
||||
|
||||
// BME280 v2 usermod. Define "USERMOD_BME280" in my_config.h
|
||||
#ifdef USERMOD_BME280
|
||||
#include "../usermods/BME280_v2/usermod_bme280.h"
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h"
|
||||
#endif
|
||||
#ifdef USERMOD_ROTARY_ENCODER_UI
|
||||
#include "../usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h"
|
||||
#endif
|
||||
#ifdef USERMOD_AUTO_SAVE
|
||||
#include "../usermods/usermod_v2_auto_save/usermod_v2_auto_save.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_DHT
|
||||
#include "../usermods/DHT/usermod_dht.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
@ -50,4 +64,20 @@ void registerUsermods()
|
||||
#ifdef USERMOD_BME280
|
||||
usermods.add(new UsermodBME280());
|
||||
#endif
|
||||
#endif
|
||||
#ifdef USERMOD_SENSORSTOMQTT
|
||||
usermods.add(new UserMod_SensorsToMQTT());
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
usermods.add(new FourLineDisplayUsermod());
|
||||
#endif
|
||||
#ifdef USERMOD_ROTARY_ENCODER_UI
|
||||
usermods.add(new RotaryEncoderUIUsermod());
|
||||
#endif
|
||||
#ifdef USERMOD_AUTO_SAVE
|
||||
usermods.add(new AutoSaveUsermod());
|
||||
#endif
|
||||
#ifdef USERMOD_DHT
|
||||
usermods.add(new UsermodDHT());
|
||||
#endif
|
||||
}
|
@ -235,14 +235,32 @@ void serveIndexOrWelcome(AsyncWebServerRequest *request)
|
||||
}
|
||||
}
|
||||
|
||||
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request)
|
||||
{
|
||||
AsyncWebHeader* header = request->getHeader("If-None-Match");
|
||||
if (header && header->value() == String(VERSION)) {
|
||||
request->send(304);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool setStaticContentCacheHeaders(AsyncWebServerResponse *response)
|
||||
{
|
||||
response->addHeader(F("Cache-Control"),"max-age=2592000");
|
||||
response->addHeader(F("ETag"), String(VERSION));
|
||||
}
|
||||
|
||||
void serveIndex(AsyncWebServerRequest* request)
|
||||
{
|
||||
if (handleFileRead(request, "/index.htm")) return;
|
||||
|
||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
|
||||
|
||||
response->addHeader(F("Content-Encoding"),"gzip");
|
||||
setStaticContentCacheHeaders(response);
|
||||
|
||||
request->send(response);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user