Add new Usermod
This commit is contained in:
parent
17c20276a9
commit
7a129e6de1
45
usermods/usermod_v2_four_line_display_ALT/readme.md
Normal file
45
usermods/usermod_v2_four_line_display_ALT/readme.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# I2C 4 Line Display Usermod ALT
|
||||||
|
|
||||||
|
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
|
||||||
|
"usermod_v2_four_line_display"
|
||||||
|
"usermod_v2_rotary_encoder_ui"
|
||||||
|
|
||||||
|
The core of these usermods are a copy of the originals. The main changes are done to the FourLineDisplay usermod.
|
||||||
|
The display usermod UI has been completely changed.
|
||||||
|
|
||||||
|
|
||||||
|
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
|
||||||
|
Without the display it functions identical to the original.
|
||||||
|
The original "usermod_v2_auto_save" will not work with the display just yet.
|
||||||
|
|
||||||
|
Press the encoder to cycle through the options:
|
||||||
|
*Brightness
|
||||||
|
*Speed
|
||||||
|
*Intensity
|
||||||
|
*Palette
|
||||||
|
*Effect
|
||||||
|
*Main Color (only if display is used)
|
||||||
|
*Saturation (only if display is used)
|
||||||
|
|
||||||
|
Press and hold the encoder to display Network Info
|
||||||
|
if AP is active then it will display AP ssid and Password
|
||||||
|
|
||||||
|
Also shows if the timer is enabled
|
||||||
|
|
||||||
|
[See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions
|
||||||
|
Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
|
||||||
|
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
|
||||||
|
|
||||||
|
|
||||||
|
### PlatformIO requirements
|
||||||
|
|
||||||
|
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
2021-10
|
||||||
|
* First public release
|
@ -0,0 +1,918 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "wled.h"
|
||||||
|
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||||
|
|
||||||
|
//
|
||||||
|
// Insired by the usermod_v2_four_line_display
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#ifndef FLD_PIN_SCL
|
||||||
|
#define FLD_PIN_SCL 22
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_SDA
|
||||||
|
#define FLD_PIN_SDA 21
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_CLOCKSPI
|
||||||
|
#define FLD_PIN_CLOCKSPI 18
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_DATASPI
|
||||||
|
#define FLD_PIN_DATASPI 23
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_DC
|
||||||
|
#define FLD_PIN_DC 19
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_CS
|
||||||
|
#define FLD_PIN_CS 5
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_RESET
|
||||||
|
#define FLD_PIN_RESET 26
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef FLD_PIN_SCL
|
||||||
|
#define FLD_PIN_SCL 5
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_SDA
|
||||||
|
#define FLD_PIN_SDA 4
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_CLOCKSPI
|
||||||
|
#define FLD_PIN_CLOCKSPI 14
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_DATASPI
|
||||||
|
#define FLD_PIN_DATASPI 13
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_DC
|
||||||
|
#define FLD_PIN_DC 12
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_CS
|
||||||
|
#define FLD_PIN_CS 15
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_RESET
|
||||||
|
#define FLD_PIN_RESET 16
|
||||||
|
#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 100
|
||||||
|
|
||||||
|
// Extra char (+1) for null
|
||||||
|
#define LINE_BUFFER_SIZE 16+1
|
||||||
|
#define MAX_JSON_CHARS 19+1
|
||||||
|
#define MAX_MODE_LINE_SPACE 13+1
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fontname: benji_custom_icons_2x
|
||||||
|
Copyright:
|
||||||
|
Glyphs: 1/1
|
||||||
|
BBX Build Mode: 3
|
||||||
|
* 2x2 custom icons that are not available in the U8X8 library
|
||||||
|
* 64 = custom palette
|
||||||
|
*/
|
||||||
|
const uint8_t u8x8_font_benji_custom_icons_2x2[37] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_2x2") =
|
||||||
|
"@@\2\2\360\370\234\236\376\363\363\377\377\363\363\376><\370\360\3\17\77yy\377\377\377\377\317\17\17"
|
||||||
|
"\17\17\7\3";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fontname: benji_custom_icons_6x
|
||||||
|
Copyright:
|
||||||
|
Glyphs: 8/8
|
||||||
|
BBX Build Mode: 3
|
||||||
|
* 6x6 icons take up a lot of memory, theres not enough momory for the required librries
|
||||||
|
* these are just the ruquired icons stripped for the U8x8 libraries in addition to a few new custom icons
|
||||||
|
* 1 = sun
|
||||||
|
* 2 = skip forward
|
||||||
|
* 3 = fire
|
||||||
|
* 4 = custom palette
|
||||||
|
* 5 = puzzle piece
|
||||||
|
* 6 = moon
|
||||||
|
* 7 = brush
|
||||||
|
* 8 = custom saturation
|
||||||
|
*/
|
||||||
|
const uint8_t u8x8_font_benji_custom_icons_6x6[2308] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_6x6") =
|
||||||
|
"\1\10\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7"
|
||||||
|
"\0\0\0\0\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
|
||||||
|
"\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
|
||||||
|
"\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
|
||||||
|
"\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
|
||||||
|
"\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
|
||||||
|
"\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
|
||||||
|
"\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
|
||||||
|
"\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
|
||||||
|
"\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
|
||||||
|
"\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\376\374\374\370\360\340\300\200\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360\374"
|
||||||
|
"\377\377\377\377\377\377\377\377\377\376\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
|
||||||
|
"\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
|
||||||
|
"\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
|
||||||
|
"\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
|
||||||
|
"\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
|
||||||
|
"\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
|
||||||
|
"\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
|
||||||
|
"\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
|
||||||
|
"\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
|
||||||
|
"\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
|
||||||
|
"\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
|
||||||
|
"\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\177\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
|
||||||
|
"\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||||
|
"\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
|
||||||
|
"\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
|
||||||
|
"\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
|
||||||
|
"\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`px\374\376\377\377\377\377\377\377"
|
||||||
|
"\177\177\177\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\7\17\17\7\3"
|
||||||
|
"\0\200\300\340\360\360\370\370\370\374\374\374\374\370\370\370\360\360\340\300\200\0\3\7\17\17\7\3\0\0\0\0"
|
||||||
|
"\0\0\0\0\300\340\360\360\340\300\0\0\0\0\340\374\377\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177"
|
||||||
|
"\177\177\177\177\177\377\374\340\0\0\0\0\300\340\360\360\340\300\0\0\0\1\3\3\1\0\0\0\0\0\1\17"
|
||||||
|
"\77\177\370\340\300\200\200\0\0\0\0\0\0\0\0\200\200\300\340\370\177\77\17\1\0\0\0\0\0\1\3\3"
|
||||||
|
"\1\0\0\0\0\0\0\0\0\0\60x\374\374x\60\0\0\0\1\3\3\7\7\7\16\16\16\16\7\7\7"
|
||||||
|
"\3\3\1\0\0\0\60x\374\374x\60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
"\0\0\0";
|
||||||
|
|
||||||
|
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)
|
||||||
|
DisplayType type = SSD1306_64; // display type
|
||||||
|
#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
|
||||||
|
DisplayType type = SSD1306_SPI; // display type
|
||||||
|
#endif
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
byte brightness100;
|
||||||
|
byte fxspeed100;
|
||||||
|
byte fxintensity100;
|
||||||
|
bool knownnightlight = nightlightActive;
|
||||||
|
bool wificonnected = interfacesInited;
|
||||||
|
bool powerON = true;
|
||||||
|
|
||||||
|
bool displayTurnedOff = false;
|
||||||
|
unsigned long lastUpdate = 0;
|
||||||
|
unsigned long lastRedraw = 0;
|
||||||
|
unsigned long overlayUntil = 0;
|
||||||
|
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||||
|
byte markLineNum = 0;
|
||||||
|
byte markColNum = 0;
|
||||||
|
|
||||||
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
|
static const char _name[];
|
||||||
|
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) return;
|
||||||
|
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
|
||||||
|
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }};
|
||||||
|
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||||
|
} else {
|
||||||
|
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
|
||||||
|
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||||
|
}
|
||||||
|
DEBUG_PRINTLN(F("Allocating display."));
|
||||||
|
switch (type) {
|
||||||
|
case SSD1306:
|
||||||
|
#ifdef ESP8266
|
||||||
|
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||||
|
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
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:
|
||||||
|
#ifdef ESP8266
|
||||||
|
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||||
|
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
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:
|
||||||
|
#ifdef ESP8266
|
||||||
|
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||||
|
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
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:
|
||||||
|
#ifdef ESP8266
|
||||||
|
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||||
|
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
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:
|
||||||
|
#ifdef ESP8266
|
||||||
|
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||||
|
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
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 (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||||
|
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 (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||||
|
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."));
|
||||||
|
for (byte i=0; i<5 && ioPin[i]>=0; i++) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||||
|
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 (displayTurnedOff && millis() - lastUpdate < 1000) {
|
||||||
|
return;
|
||||||
|
}else if (millis() - lastUpdate < refreshRate){
|
||||||
|
return;}
|
||||||
|
redraw(false);
|
||||||
|
lastUpdate = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrappers for screen drawing
|
||||||
|
*/
|
||||||
|
void setFlipMode(uint8_t mode) {
|
||||||
|
if (type==NONE) return;
|
||||||
|
u8x8->setFlipMode(mode);
|
||||||
|
}
|
||||||
|
void setContrast(uint8_t contrast) {
|
||||||
|
if (type==NONE) return;
|
||||||
|
u8x8->setContrast(contrast);
|
||||||
|
}
|
||||||
|
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
|
||||||
|
if (type==NONE) 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) 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) 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) return 0;
|
||||||
|
return u8x8->getCols();
|
||||||
|
}
|
||||||
|
void clear() {
|
||||||
|
if (type==NONE) return;
|
||||||
|
u8x8->clear();
|
||||||
|
}
|
||||||
|
void setPowerSave(uint8_t save) {
|
||||||
|
if (type==NONE) 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 += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
//function to update lastredraw
|
||||||
|
void updateRedrawTime(){
|
||||||
|
lastRedraw = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redraw the screen (but only if things have changed
|
||||||
|
* or if forceRedraw).
|
||||||
|
*/
|
||||||
|
void redraw(bool forceRedraw) {
|
||||||
|
if (type==NONE) return;
|
||||||
|
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 ((bri == 0 && powerON) || (bri > 0 && !powerON)) { //trigger power icon
|
||||||
|
powerON = !powerON;
|
||||||
|
drawStatusIcons();
|
||||||
|
lastRedraw = millis();
|
||||||
|
} else if (knownnightlight != nightlightActive) { //trigger moon icon
|
||||||
|
knownnightlight = nightlightActive;
|
||||||
|
drawStatusIcons();
|
||||||
|
if (knownnightlight) overlay(" Timer On", 1000, 6);
|
||||||
|
lastRedraw = millis();
|
||||||
|
}else if (wificonnected != interfacesInited){ //trigger wifi icon
|
||||||
|
wificonnected = interfacesInited;
|
||||||
|
drawStatusIcons();
|
||||||
|
lastRedraw = millis();
|
||||||
|
} else if (knownMode != effectCurrent) {
|
||||||
|
knownMode = effectCurrent;
|
||||||
|
if(displayTurnedOff)needRedraw = true;
|
||||||
|
else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3);
|
||||||
|
} else if (knownPalette != effectPalette) {
|
||||||
|
knownPalette = effectPalette;
|
||||||
|
if(displayTurnedOff)needRedraw = true;
|
||||||
|
else showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2);
|
||||||
|
} else if (knownBrightness != bri) {
|
||||||
|
if(displayTurnedOff && nightlightActive){needRedraw = false; knownBrightness = bri;}
|
||||||
|
else if(displayTurnedOff)needRedraw = true;
|
||||||
|
else updateBrightness();
|
||||||
|
} else if (knownEffectSpeed != effectSpeed) {
|
||||||
|
if(displayTurnedOff)needRedraw = true;
|
||||||
|
else updateSpeed();
|
||||||
|
} else if (knownEffectIntensity != effectIntensity) {
|
||||||
|
if(displayTurnedOff)needRedraw = true;
|
||||||
|
else updateIntensity();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!needRedraw) {
|
||||||
|
// Nothing to change.
|
||||||
|
// Turn off display after 1 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;
|
||||||
|
} else {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
needRedraw = false;
|
||||||
|
lastRedraw = millis();
|
||||||
|
|
||||||
|
if (displayTurnedOff) {
|
||||||
|
// Turn the display back on
|
||||||
|
sleepOrClock(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last known values.
|
||||||
|
knownSsid = apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
|
||||||
|
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||||
|
knownBrightness = bri;
|
||||||
|
knownMode = effectCurrent;
|
||||||
|
knownPalette = effectPalette;
|
||||||
|
knownEffectSpeed = effectSpeed;
|
||||||
|
knownEffectIntensity = effectIntensity;
|
||||||
|
knownnightlight = nightlightActive;
|
||||||
|
wificonnected = interfacesInited;
|
||||||
|
|
||||||
|
// Do the actual drawing
|
||||||
|
// First row: Icons
|
||||||
|
draw2x2GlyphIcons();
|
||||||
|
drawArrow();
|
||||||
|
drawStatusIcons();
|
||||||
|
|
||||||
|
// Second row
|
||||||
|
updateBrightness();
|
||||||
|
updateSpeed();
|
||||||
|
updateIntensity();
|
||||||
|
|
||||||
|
// Third row
|
||||||
|
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2); //Palette info
|
||||||
|
|
||||||
|
// Fourth row
|
||||||
|
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); //Effect Mode info
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateBrightness(){
|
||||||
|
knownBrightness = bri;
|
||||||
|
if(overlayUntil == 0){
|
||||||
|
brightness100 = (((float)(bri)/255)*100);
|
||||||
|
char lineBuffer[4];
|
||||||
|
sprintf_P(lineBuffer, PSTR("%-3d"), brightness100);
|
||||||
|
drawString(1, lineHeight, lineBuffer);
|
||||||
|
lastRedraw = millis();}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSpeed(){
|
||||||
|
knownEffectSpeed = effectSpeed;
|
||||||
|
if(overlayUntil == 0){
|
||||||
|
fxspeed100 = (((float)(effectSpeed)/255)*100);
|
||||||
|
char lineBuffer[4];
|
||||||
|
sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100);
|
||||||
|
drawString(5, lineHeight, lineBuffer);
|
||||||
|
lastRedraw = millis();}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateIntensity(){
|
||||||
|
knownEffectIntensity = effectIntensity;
|
||||||
|
if(overlayUntil == 0){
|
||||||
|
fxintensity100 = (((float)(effectIntensity)/255)*100);
|
||||||
|
char lineBuffer[4];
|
||||||
|
sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100);
|
||||||
|
drawString(9, lineHeight, lineBuffer);
|
||||||
|
lastRedraw = millis();}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw2x2GlyphIcons(){
|
||||||
|
drawGlyph(1, 0, 69, u8x8_font_open_iconic_weather_2x2, true);//brightness icon
|
||||||
|
drawGlyph(5, 0, 72, u8x8_font_open_iconic_play_2x2, true);//speed icon
|
||||||
|
drawGlyph(9, 0, 78, u8x8_font_open_iconic_thing_2x2, true);//intensity icon
|
||||||
|
drawGlyph(14, 2*lineHeight, 64, u8x8_font_benji_custom_icons_2x2,true);//palette icon
|
||||||
|
drawGlyph(14, 3*lineHeight, 70, u8x8_font_open_iconic_thing_2x2,true);//effect icon
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawStatusIcons(){
|
||||||
|
drawGlyph(14, 0, 80 + (wificonnected?0:1), u8x8_font_open_iconic_embedded_1x1, true); // wifi icon
|
||||||
|
drawGlyph(15, 0, 78 + (bri > 0 ? 0 : 3), u8x8_font_open_iconic_embedded_1x1, true); // power icon
|
||||||
|
drawGlyph(13, 0, 66 + (nightlightActive?0:4), u8x8_font_open_iconic_weather_1x1, true); // moon icon for nighlight mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* marks the position of the arrow showing
|
||||||
|
* the current setting being changed
|
||||||
|
* pass line and colum info
|
||||||
|
*/
|
||||||
|
void setMarkLine(byte newMarkLineNum, byte newMarkColNum) {
|
||||||
|
markLineNum = newMarkLineNum;
|
||||||
|
markColNum = newMarkColNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Draw the arrow for the current setting beiong changed
|
||||||
|
void drawArrow(){
|
||||||
|
if(markColNum != 255 && markLineNum !=255)drawGlyph(markColNum, markLineNum*lineHeight, 69, u8x8_font_open_iconic_play_1x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Display the current effect or palette (desiredEntry)
|
||||||
|
// on the appropriate line (row).
|
||||||
|
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
|
||||||
|
knownMode = effectCurrent;
|
||||||
|
knownPalette = effectPalette;
|
||||||
|
if(overlayUntil == 0){
|
||||||
|
char lineBuffer[MAX_JSON_CHARS];
|
||||||
|
char smallBuffer1[MAX_MODE_LINE_SPACE];
|
||||||
|
char smallBuffer2[MAX_MODE_LINE_SPACE];
|
||||||
|
uint8_t qComma = 0;
|
||||||
|
bool insideQuotes = false;
|
||||||
|
bool spaceHit = false;
|
||||||
|
uint8_t printedChars = 0;
|
||||||
|
uint8_t smallChars1 = 0;
|
||||||
|
uint8_t smallChars2 = 0;
|
||||||
|
uint8_t totalCount = 0;
|
||||||
|
char singleJsonSymbol;
|
||||||
|
|
||||||
|
// Find the mode name in JSON
|
||||||
|
for (size_t i = 0; i < strlen_P(qstring); i++) {
|
||||||
|
singleJsonSymbol = pgm_read_byte_near(qstring + i);
|
||||||
|
if (singleJsonSymbol == '\0') break;
|
||||||
|
switch (singleJsonSymbol) {
|
||||||
|
case '"':
|
||||||
|
insideQuotes = !insideQuotes;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
qComma++;
|
||||||
|
default:
|
||||||
|
if (!insideQuotes || (qComma != inputEffPal)) break;
|
||||||
|
lineBuffer[printedChars++] = singleJsonSymbol;
|
||||||
|
totalCount++;
|
||||||
|
}
|
||||||
|
if ((qComma > inputEffPal)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(printedChars < (MAX_MODE_LINE_SPACE)){
|
||||||
|
for (;printedChars < (MAX_MODE_LINE_SPACE-1); printedChars++) {lineBuffer[printedChars]=' '; }
|
||||||
|
lineBuffer[printedChars] = 0;
|
||||||
|
drawString(1, row*lineHeight, lineBuffer);
|
||||||
|
lastRedraw = millis();
|
||||||
|
}else{
|
||||||
|
for (uint8_t i = 0; i < printedChars; i++){
|
||||||
|
switch (lineBuffer[i]){
|
||||||
|
case ' ':
|
||||||
|
if(i > 4 && !spaceHit) {
|
||||||
|
spaceHit = true;
|
||||||
|
break;}
|
||||||
|
if(!spaceHit) smallBuffer1[smallChars1++] = lineBuffer[i];
|
||||||
|
if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(!spaceHit) smallBuffer1[smallChars1++] = lineBuffer[i];
|
||||||
|
if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; smallChars1 < (MAX_MODE_LINE_SPACE-1); smallChars1++) smallBuffer1[smallChars1]=' ';
|
||||||
|
smallBuffer1[smallChars1] = 0;
|
||||||
|
drawString(1, row*lineHeight, smallBuffer1, true);
|
||||||
|
for (; smallChars2 < (MAX_MODE_LINE_SPACE-1); smallChars2++) smallBuffer2[smallChars2]=' ';
|
||||||
|
smallBuffer2[smallChars2] = 0;
|
||||||
|
drawString(1, row*lineHeight+1, smallBuffer2, true);
|
||||||
|
lastRedraw = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
//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, long showHowLong, byte glyphType) {
|
||||||
|
if (displayTurnedOff) {
|
||||||
|
// Turn the display back on
|
||||||
|
sleepOrClock(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the overlay
|
||||||
|
clear();
|
||||||
|
if (glyphType > 0)drawGlyph(5, 0, glyphType, u8x8_font_benji_custom_icons_6x6, true);
|
||||||
|
if (line1) drawString(0, 3*lineHeight, line1);
|
||||||
|
overlayUntil = millis() + showHowLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
void networkOverlay(const char* line1, long showHowLong) {
|
||||||
|
if (displayTurnedOff) {
|
||||||
|
// Turn the display back on
|
||||||
|
sleepOrClock(false);
|
||||||
|
}
|
||||||
|
// Print the overlay
|
||||||
|
clear();
|
||||||
|
// First row string
|
||||||
|
if (line1) drawString(0, 0, line1);
|
||||||
|
// Second row with Wifi name
|
||||||
|
String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0); //
|
||||||
|
drawString(0, lineHeight, ssidString.c_str());
|
||||||
|
// Print `~` char to indicate that SSID is longer, than our display
|
||||||
|
if (knownSsid.length() > getCols()) {
|
||||||
|
drawString(getCols() - 1, 0, "~");
|
||||||
|
}
|
||||||
|
// Third row with IP and Psssword in AP Mode
|
||||||
|
drawString(0, lineHeight*2, (knownIp.toString()).c_str());
|
||||||
|
if (apActive) {
|
||||||
|
String appassword = apPass;
|
||||||
|
drawString(0, lineHeight*3, appassword.c_str());
|
||||||
|
}
|
||||||
|
overlayUntil = millis() + showHowLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable sleep (turn the display off) or clock mode.
|
||||||
|
*/
|
||||||
|
void sleepOrClock(bool enabled) {
|
||||||
|
if (enabled) {
|
||||||
|
if (clockMode) {
|
||||||
|
clear();
|
||||||
|
knownMinute = 99;
|
||||||
|
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() {
|
||||||
|
if(knownMinute != minute(localTime)){ //only redraw clock if it has changed
|
||||||
|
char lineBuffer[LINE_BUFFER_SIZE];
|
||||||
|
|
||||||
|
//updateLocalTime();
|
||||||
|
byte AmPmHour = hour(localTime);
|
||||||
|
boolean isitAM = true;
|
||||||
|
if (useAMPM) {
|
||||||
|
if (AmPmHour > 11) AmPmHour -= 12;
|
||||||
|
if (AmPmHour == 0) AmPmHour = 12;
|
||||||
|
if (hour(localTime) > 11) isitAM = false;
|
||||||
|
}
|
||||||
|
clear();
|
||||||
|
drawStatusIcons(); //icons power, wifi, timer, etc
|
||||||
|
|
||||||
|
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
|
||||||
|
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
|
||||||
|
|
||||||
|
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hour(localTime)), minute(localTime));
|
||||||
|
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
|
||||||
|
|
||||||
|
if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time
|
||||||
|
knownMinute = minute(localTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
JsonArray io_pin = top.createNestedArray("pin");
|
||||||
|
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||||
|
top["help4PinTypes"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||||
|
top["type"] = type;
|
||||||
|
top[FPSTR(_flip)] = (bool) flip;
|
||||||
|
top[FPSTR(_contrast)] = contrast;
|
||||||
|
top[FPSTR(_refreshRate)] = refreshRate/10;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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/10) * 10;
|
||||||
|
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
|
||||||
|
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
|
||||||
|
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||||
|
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;
|
||||||
|
for (byte i=0; i<5; i++) {
|
||||||
|
if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||||
|
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[_busClkFrequency]).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::_contrast[] PROGMEM = "contrast";
|
||||||
|
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRate0.01Sec";
|
||||||
|
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";
|
45
usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
Normal file
45
usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Rotary Encoder UI Usermod ALT
|
||||||
|
|
||||||
|
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
|
||||||
|
"usermod_v2_four_line_display"
|
||||||
|
"usermod_v2_rotary_encoder_ui"
|
||||||
|
|
||||||
|
The core of these usermods are a copy of the originals. The main changes are done to the FourLineDisplay usermod.
|
||||||
|
The display usermod UI has been completely changed.
|
||||||
|
|
||||||
|
|
||||||
|
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
|
||||||
|
Without the display it functions identical to the original.
|
||||||
|
The original "usermod_v2_auto_save" will not work with the display just yet.
|
||||||
|
|
||||||
|
Press the encoder to cycle through the options:
|
||||||
|
*Brightness
|
||||||
|
*Speed
|
||||||
|
*Intensity
|
||||||
|
*Palette
|
||||||
|
*Effect
|
||||||
|
*Main Color (only if display is used)
|
||||||
|
*Saturation (only if display is used)
|
||||||
|
|
||||||
|
Press and hold the encoder to display Network Info
|
||||||
|
if AP is active then it will display AP ssid and Password
|
||||||
|
|
||||||
|
Also shows if the timer is enabled
|
||||||
|
|
||||||
|
[See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions
|
||||||
|
Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
|
||||||
|
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
|
||||||
|
|
||||||
|
|
||||||
|
### PlatformIO requirements
|
||||||
|
|
||||||
|
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
2021-10
|
||||||
|
* First public release
|
@ -0,0 +1,569 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "wled.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Inspired by the original v2 usermods
|
||||||
|
// * usermod_v2_rotaty_encoder_ui
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// If FourLineDisplayUsermod is used the folowing options are also inabled
|
||||||
|
//
|
||||||
|
// * main color
|
||||||
|
// * saturation of main color
|
||||||
|
// * display network (long press buttion)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ENCODER_DT_PIN
|
||||||
|
#define ENCODER_DT_PIN 18
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENCODER_CLK_PIN
|
||||||
|
#define ENCODER_CLK_PIN 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENCODER_SW_PIN
|
||||||
|
#define ENCODER_SW_PIN 19
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The last UI state, remove color and saturation option if diplay not active(too many options)
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
#define LAST_UI_STATE 6
|
||||||
|
#else
|
||||||
|
#define LAST_UI_STATE 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class RotaryEncoderUIUsermod : public Usermod {
|
||||||
|
private:
|
||||||
|
int fadeAmount = 5; // Amount to change every step (brightness)
|
||||||
|
unsigned long currentTime;
|
||||||
|
unsigned long loopTime;
|
||||||
|
unsigned long buttonHoldTIme;
|
||||||
|
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;
|
||||||
|
bool networkShown = false;
|
||||||
|
uint16_t currentHue1 = 6425; // default reboot color
|
||||||
|
byte currentSat1 = 255;
|
||||||
|
|
||||||
|
#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;
|
||||||
|
uint8_t knownMode = 0;
|
||||||
|
uint8_t knownPalette = 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()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinMode(pinA, INPUT_PULLUP);
|
||||||
|
pinMode(pinB, INPUT_PULLUP);
|
||||||
|
pinMode(pinC, INPUT_PULLUP);
|
||||||
|
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->setMarkLine(1, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
initDone = true;
|
||||||
|
Enc_A = digitalRead(pinA); // Read encoder pins
|
||||||
|
Enc_B = digitalRead(pinB);
|
||||||
|
Enc_A_prev = Enc_A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 (!currentEffectAndPaletteInitialized) {
|
||||||
|
findCurrentEffectAndPalette();}
|
||||||
|
|
||||||
|
if(modes_alpha_indexes[effectCurrentIndex] != effectCurrent
|
||||||
|
|| palettes_alpha_indexes[effectPaletteIndex] != effectPalette){
|
||||||
|
currentEffectAndPaletteInitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||||
|
{
|
||||||
|
button_state = digitalRead(pinC);
|
||||||
|
if (prev_button_state != button_state)
|
||||||
|
{
|
||||||
|
if (button_state == HIGH && (millis()-buttonHoldTIme < 3000))
|
||||||
|
{
|
||||||
|
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", 1, 0, 1);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
changedState = changeState(" Speed", 1, 4, 2);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
changedState = changeState(" Intensity", 1 ,8, 3);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
changedState = changeState(" Color Palette", 2, 0, 4);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
changedState = changeState(" Effect", 3, 0, 5);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
changedState = changeState(" Main Color", 255, 255, 7);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
changedState = changeState(" Saturation", 255, 255, 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changedState) {
|
||||||
|
select_state = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev_button_state = button_state;
|
||||||
|
networkShown = false;
|
||||||
|
if(!prev_button_state)buttonHoldTIme = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prev_button_state && (millis()-buttonHoldTIme > 3000) && !networkShown) displayNetworkInfo(); //long press for network info
|
||||||
|
|
||||||
|
Enc_A = digitalRead(pinA); // Read encoder pins
|
||||||
|
Enc_B = digitalRead(pinB);
|
||||||
|
if ((Enc_A) && (!Enc_A_prev))
|
||||||
|
{ // A has gone from high to low
|
||||||
|
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
|
||||||
|
{ // B is high so clockwise
|
||||||
|
switch(select_state) {
|
||||||
|
case 0:
|
||||||
|
changeBrightness(true);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
changeEffectSpeed(true);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
changeEffectIntensity(true);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
changePalette(true);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
changeEffect(true);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
changeHue(true);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
changeSat(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Enc_B == HIGH)
|
||||||
|
{ // B is low so counter-clockwise
|
||||||
|
switch(select_state) {
|
||||||
|
case 0:
|
||||||
|
changeBrightness(false);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
changeEffectSpeed(false);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
changeEffectIntensity(false);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
changePalette(false);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
changeEffect(false);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
changeHue(false);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
changeSat(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||||
|
loopTime = currentTime; // Updates loopTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayNetworkInfo(){
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->networkOverlay(" NETWORK INFO", 15000);
|
||||||
|
networkShown = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void findCurrentEffectAndPalette() {
|
||||||
|
currentEffectAndPaletteInitialized = true;
|
||||||
|
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||||
|
if (modes_alpha_indexes[i] == effectCurrent) {
|
||||||
|
effectCurrentIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||||
|
if (palettes_alpha_indexes[i] == effectPalette) {
|
||||||
|
effectPaletteIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
if (display != nullptr) {
|
||||||
|
if (display->wakeDisplay()) {
|
||||||
|
// Throw away wake up input
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
display->overlay(stateName, 750, glyph);
|
||||||
|
display->setMarkLine(markedLine, markedCol);
|
||||||
|
}
|
||||||
|
#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(CALL_MODE_DIRECT_CHANGE);
|
||||||
|
updateInterfaces(CALL_MODE_DIRECT_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->updateBrightness();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->updateSpeed();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->updateIntensity();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void changeHue(bool increase){
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
if (display && display->wakeDisplay()) {
|
||||||
|
// Throw away wake up input
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(increase) currentHue1 += 321;
|
||||||
|
else currentHue1 -= 321;
|
||||||
|
colorHStoRGB(currentHue1, currentSat1, col);
|
||||||
|
lampUdated();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->updateRedrawTime();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeSat(bool increase){
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
if (display && display->wakeDisplay()) {
|
||||||
|
// Throw away wake up input
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(increase) currentSat1 = (currentSat1 + 5 <= 255 ? (currentSat1 + 5) : 255);
|
||||||
|
else currentSat1 = (currentSat1 - 5 >= 0 ? (currentSat1 - 5) : 0);
|
||||||
|
colorHStoRGB(currentHue1, currentSat1, col);
|
||||||
|
lampUdated();
|
||||||
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
|
display->updateRedrawTime();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = pinA;
|
||||||
|
int8_t newCLKpin = pinB;
|
||||||
|
int8_t newSWpin = pinC;
|
||||||
|
|
||||||
|
enabled = top[FPSTR(_enabled)] | enabled;
|
||||||
|
newDTpin = top[FPSTR(_DT_pin)] | newDTpin;
|
||||||
|
newCLKpin = top[FPSTR(_CLK_pin)] | newCLKpin;
|
||||||
|
newSWpin = top[FPSTR(_SW_pin)] | newSWpin;
|
||||||
|
|
||||||
|
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,18 @@
|
|||||||
#include "../usermods/BME280_v2/usermod_bme280.h"
|
#include "../usermods/BME280_v2/usermod_bme280.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||||
#include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h"
|
#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
|
||||||
#endif
|
#endif
|
||||||
#ifdef USERMOD_ROTARY_ENCODER_UI
|
#ifdef USERMOD_ROTARY_ENCODER_UI
|
||||||
#include "../usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h"
|
#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
|
||||||
#endif
|
#endif
|
||||||
#ifdef USERMOD_AUTO_SAVE
|
#ifdef USERMOD_AUTO_SAVE
|
||||||
#include "../usermods/usermod_v2_auto_save/usermod_v2_auto_save.h"
|
#include "../usermods/usermod_v2_auto_save/usermod_v2_auto_save.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user