Create the sorted index of modes and palettes at startup (#1741)

* Very incomplete work toward sorting.

* Sort modes and palettes upon startup so I don't have to maintain the static index array.

* Remove cpp test file I used for development

* Added ModeSortUsermod, modified the other two usermods to use it.

* Update platformio_override.ini.sample and readme for ModeSortUsermod

* restore methods accidentally removed.
This commit is contained in:
Kevin Dorff 2021-02-17 17:28:01 -06:00 committed by GitHub
parent aa242d897d
commit 75c46f7a0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 338 additions and 69 deletions

View File

@ -10,7 +10,10 @@
// OLED displays to provide a four line display
// for WLED.
//
// This Usermod works best, by far, when coupled with RotaryEncoderUIUsermod.
// 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.
//
@ -124,6 +127,9 @@ class FourLineDisplayUsermod : public Usermod {
char lineBuffer[LINE_BUFFER_SIZE];
char **modes_qstrings = nullptr;
char **palettes_qstrings = nullptr;
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
@ -140,6 +146,10 @@ class FourLineDisplayUsermod : public Usermod {
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...");
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
modes_qstrings = modeSortUsermod->getModesQStrings();
palettes_qstrings = modeSortUsermod->getPalettesQStrings();
}
// gets called every time WiFi is (re-)connected. Initialize own network
@ -254,7 +264,7 @@ class FourLineDisplayUsermod : public Usermod {
}
// Third row with mode name
showCurrentEffectOrPalette(JSON_mode_names, 2, knownMode);
showCurrentEffectOrPalette(modes_qstrings[knownMode], 2);
switch(lineThreeType) {
case FLD_LINE_3_BRIGHTNESS:
@ -270,7 +280,7 @@ class FourLineDisplayUsermod : public Usermod {
u8x8.DRAW_STRING(1, 3*LINE_HEIGHT, lineBuffer);
break;
case FLD_LINE_3_PALETTE:
showCurrentEffectOrPalette(JSON_palette_names, 3, knownPalette);
showCurrentEffectOrPalette(palettes_qstrings[knownPalette], 3);
break;
}
@ -289,35 +299,21 @@ class FourLineDisplayUsermod : public Usermod {
* 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.
void showCurrentEffectOrPalette(char *qstring, uint8_t row) {
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)) {
int i = 0;
while (true) {
singleJsonSymbol = pgm_read_byte_near(qstring + i);
if (singleJsonSymbol == '"' || singleJsonSymbol == '\0' ) {
break;
}
u8x8.DRAW_GLYPH(printedChars, row * LINE_HEIGHT, singleJsonSymbol);
printedChars++;
if ( (printedChars > u8x8.getCols() - 2)) {
break;
}
i++;
}
}

View File

@ -0,0 +1,33 @@
# Mode Sort
v2 usermod that provides data about modes and
palettes to other usermods. Notably it provides:
* A direct method for a mode or palette name
* Ability to retrieve mode and palette names in
alphabetical order
```char **getModesQStrings()```
Provides an array of char* (pointers) to the names of the
palettes within JSON_mode_names, in the same order as
JSON_mode_names. These strings end in double quote (")
(or \0 if there is a problem).
```byte *getModesAlphaIndexes()```
An array of byte designating the indexes of names of the
modes in alphabetical order. "Solid" will always remain
at the front of the list.
```char **getPalettesQStrings()```
Provides an array of char* (pointers) to the names of the
palettes within JSON_palette_names, in the same order as
JSON_palette_names. These strings end in double quote (")
(or \0 if there is a problem).
```byte *getPalettesAlphaIndexes()```
An array of byte designating the indexes of names of the
palettes in alphabetical order. "Default" and those
starting with "(" will always remain at the front of the list.

View File

@ -0,0 +1,248 @@
#pragma once
#include "wled.h"
//
// v2 usermod that provides data about modes and
// palettes to other usermods. Notably it provides:
// * A direct method for a mode or palette name
// * Ability to retrieve mode and palette names in
// alphabetical order
//
// char **getModesQStrings()
// Provides an array of char* (pointers) to the names of the
// palettes within JSON_mode_names, in the same order as
// JSON_mode_names. These strings end in double quote (")
// (or \0 if there is a problem).
//
// byte *getModesAlphaIndexes()
// An array of byte designating the indexes of names of the
// modes in alphabetical order. "Solid" will always remain
// at the front of the list.
//
// char **getPalettesQStrings()
// Provides an array of char* (pointers) to the names of the
// palettes within JSON_palette_names, in the same order as
// JSON_palette_names. These strings end in double quote (")
// (or \0 if there is a problem).
//
// byte *getPalettesAlphaIndexes()
// An array of byte designating the indexes of names of the
// palettes in alphabetical order. "Default" and those
// starting with "(" will always remain at the front of the list.
//
// Number of modes at the start of the list to not sort
#define MODE_SORT_SKIP_COUNT 1
// Which list is being sorted
char **listBeingSorted = nullptr;
/**
* Modes and palettes are stored as strings that
* end in a quote character. Compare two of them.
* We are comparing directly within either
* JSON_mode_names or JSON_palette_names.
*/
int re_qstringCmp(const void *ap, const void *bp) {
char *a = listBeingSorted[*((byte *)ap)];
char *b = listBeingSorted[*((byte *)bp)];
int i = 0;
do {
char aVal = pgm_read_byte_near(a + i);
if (aVal >= 97 && aVal <= 122) {
// Lowercase
aVal -= 32;
}
char bVal = pgm_read_byte_near(b + i);
if (bVal >= 97 && bVal <= 122) {
// Lowercase
bVal -= 32;
}
// Relly we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
if (aVal == bVal) {
// Same value, probably shouldn't happen
// with this dataset
return 0;
}
else if (aVal == '"' || aVal == '\0') {
return -1;
}
else {
return 1;
}
}
if (aVal == bVal) {
// Same characters. Move to the next.
i++;
continue;
}
// We're done
if (aVal < bVal) {
return -1;
}
else {
return 1;
}
} while (true);
// We shouldn't get here.
return 0;
}
class ModeSortUsermod : public Usermod {
private:
// Pointers the start of the mode names within JSON_mode_names
char **modes_qstrings = nullptr;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
// Pointers the start of the palette names within JSON_palette_names
char **palettes_qstrings = nullptr;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
public:
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup() {
// Sort the modes and palettes on startup
// as they are guarantted to change.
sortModesAndPalettes();
}
char **getModesQStrings() {
return modes_qstrings;
}
byte *getModesAlphaIndexes() {
return modes_alpha_indexes;
}
char **getPalettesQStrings() {
return palettes_qstrings;
}
byte *getPalettesAlphaIndexes() {
return palettes_alpha_indexes;
}
/**
* This Usermod doesn't have anything for loop.
*/
void loop() {}
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
int skipPaletteCount = 1;
while (true) {
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
if (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') {
skipPaletteCount++;
}
else {
break;
}
}
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
}
byte *re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
return indexes;
}
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
char **re_findModeStrings(const char json[], int numModes) {
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
char singleJsonSymbol;
// Find the mode name in JSON
bool complete = false;
for (size_t i = 0; i < strlen_P(json); i++) {
singleJsonSymbol = pgm_read_byte_near(json + i);
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
if (insideQuotes) {
// We have a new mode or palette
modeStrings[modeIndex] = (char *)(json + i + 1);
}
break;
case '[':
break;
case ']':
complete = true;
break;
case ',':
modeIndex++;
default:
if (!insideQuotes) {
break;
}
}
if (complete) {
break;
}
}
return modeStrings;
}
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr;
}
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void addToJsonState(JsonObject &root) {}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root) {}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_MODE_SORT;
}
};

View File

@ -8,6 +8,7 @@ platform = espressif32@2.0
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_MODE_SORT
-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
@ -26,6 +27,7 @@ board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp8266}
-D USERMOD_MODE_SORT
-D USERMOD_FOUR_LINE_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

View File

@ -9,8 +9,6 @@
//
// v2 usermod that provides a rotary encoder-based UI.
//
// This Usermod works best coupled with FourLineDisplayUsermod.
//
// This usermod allows you to control:
//
// * Brightness
@ -21,6 +19,11 @@
//
// Change between modes by pressing a button.
//
// Dependencies
// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best coupled with
// FourLineDisplayUsermod.
//
#ifndef ENCODER_DT_PIN
#define ENCODER_DT_PIN 12
@ -42,36 +45,10 @@
#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:
@ -86,10 +63,14 @@ private:
unsigned char prev_button_state = HIGH;
#ifdef USERMOD_FOUR_LINE_DISLAY
FourLineDisplayUsermod* 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;
@ -111,6 +92,10 @@ public:
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_DISLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
// But it's optional. But you want it.
@ -246,16 +231,16 @@ public:
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) {
byte value = modes_alpha_indexes[i];
if (modes_alpha_indexes[i] == effectCurrent) {
effectCurrentIndex = i;
break;
}
}
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
byte value = palettes_alpha_order[i];
if (palettes_alpha_order[i] == strip.getSegment(0).palette) {
byte value = palettes_alpha_indexes[i];
if (palettes_alpha_indexes[i] == strip.getSegment(0).palette) {
effectPaletteIndex = i;
break;
}
@ -315,7 +300,7 @@ public:
else {
effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
}
effectCurrent = modes_alpha_order[effectCurrentIndex];
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
lampUdated();
}
@ -364,7 +349,7 @@ public:
else {
effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
}
effectPalette = palettes_alpha_order[effectPaletteIndex];
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
lampUdated();
}
@ -380,7 +365,6 @@ public:
//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
@ -414,7 +398,4 @@ public:
{
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!
};

View File

@ -25,6 +25,7 @@
#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"
#define USERMOD_ID_MODE_SORT 11 //Usermod "usermod_v2_mode_sort.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

View File

@ -21,6 +21,9 @@
#include "usermod_v2_SensorsToMqtt.h"
#endif
#ifdef USERMOD_MODE_SORT
#include "../usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h"
#endif
#ifdef USERMOD_FOUR_LINE_DISLAY
#include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h"
#endif
@ -53,6 +56,10 @@ void registerUsermods()
#ifdef USERMOD_SENSORSTOMQTT
usermods.add(new UserMod_SensorsToMQTT());
#endif
#ifdef USERMOD_MODE_SORT
usermods.add(new ModeSortUsermod());
#endif
#ifdef USERMOD_FOUR_LINE_DISLAY
usermods.add(new FourLineDisplayUsermod());
#endif
@ -62,7 +69,8 @@ void registerUsermods()
#ifdef USERMOD_AUTO_SAVE
usermods.add(new AutoSaveUsermod());
#endif
#ifdef USERMOD_DHT
usermods.add(new UsermodDHT());
#endif
}
}