Skip to content

Commit ccd3152

Browse files
author
Gregory Schmidt
committed
Add Basic Overlay support to Usermods.
1 parent 8453cd8 commit ccd3152

File tree

7 files changed

+277
-4
lines changed

7 files changed

+277
-4
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Seven Segment Display
2+
3+
In this usermod file you can find the documentation on how to take advantage of the new version 2 usermods!
4+
5+
## Installation
6+
7+
Copy `usermod_v2_example.h` to the wled00 directory.
8+
Uncomment the corresponding lines in `usermods_list.cpp` and compile!
9+
_(You shouldn't need to actually install this, it does nothing useful)_
10+
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
#pragma once
2+
3+
#include "wled.h"
4+
5+
6+
7+
class SevenSegmentDisplay : public Usermod {
8+
private:
9+
//Private class members. You can declare variables and functions only accessible to your usermod here
10+
unsigned long lastTime = 0;
11+
12+
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
13+
bool testBool = false;
14+
unsigned long testULong = 42424242;
15+
float testFloat = 42.42;
16+
String testString = "Forty-Two";
17+
18+
// These config variables have defaults set inside readFromConfig()
19+
int testInt;
20+
long testLong;
21+
int8_t testPins[2];
22+
23+
public:
24+
//Functions called by WLED
25+
26+
/*
27+
* setup() is called once at boot. WiFi is not yet connected at this point.
28+
* You can use it to initialize variables, sensors or similar.
29+
*/
30+
void setup() {
31+
//Serial.println("Hello from my usermod!");
32+
}
33+
34+
/*
35+
* loop() is called continuously. Here you can check for events, read sensors, etc.
36+
*
37+
* Tips:
38+
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
39+
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
40+
*
41+
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
42+
* Instead, use a timer check as shown here.
43+
*/
44+
void loop() {
45+
if (millis() - lastTime > 1000) {
46+
//Serial.println("I'm alive!");
47+
lastTime = millis();
48+
}
49+
}
50+
51+
void onMqttConnect(bool sessionPresent)
52+
{
53+
if (mqttDeviceTopic[0] == 0)
54+
return;
55+
56+
for (int pinNr = 0; pinNr < NUM_SWITCH_PINS; pinNr++) {
57+
char buf[128];
58+
StaticJsonDocument<1024> json;
59+
sprintf(buf, "%s Switch %d", serverDescription, pinNr + 1);
60+
json[F("name")] = buf;
61+
62+
sprintf(buf, "%s/switch/%d", mqttDeviceTopic, pinNr);
63+
json["~"] = buf;
64+
strcat(buf, "/set");
65+
mqtt->subscribe(buf, 0);
66+
67+
json[F("stat_t")] = "~/state";
68+
json[F("cmd_t")] = "~/set";
69+
json[F("pl_off")] = F("OFF");
70+
json[F("pl_on")] = F("ON");
71+
72+
char uid[16];
73+
sprintf(uid, "%s_sw%d", escapedMac.c_str(), pinNr);
74+
json[F("unique_id")] = uid;
75+
76+
strcpy(buf, mqttDeviceTopic);
77+
strcat(buf, "/status");
78+
json[F("avty_t")] = buf;
79+
json[F("pl_avail")] = F("online");
80+
json[F("pl_not_avail")] = F("offline");
81+
//TODO: dev
82+
sprintf(buf, "homeassistant/switch/%s/config", uid);
83+
char json_str[1024];
84+
size_t payload_size = serializeJson(json, json_str);
85+
mqtt->publish(buf, 0, true, json_str, payload_size);
86+
updateState(pinNr);
87+
}
88+
}
89+
90+
bool onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
91+
{
92+
//Note: Payload is not necessarily null terminated. Check "len" instead.
93+
for (int pinNr = 0; pinNr < NUM_SWITCH_PINS; pinNr++) {
94+
char buf[64];
95+
sprintf(buf, "%s/switch/%d/set", mqttDeviceTopic, pinNr);
96+
if (strcmp(topic, buf) == 0) {
97+
//Any string starting with "ON" is interpreted as ON, everything else as OFF
98+
setState(pinNr, len >= 2 && payload[0] == 'O' && payload[1] == 'N');
99+
return true;
100+
}
101+
}
102+
}
103+
104+
/*
105+
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
106+
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
107+
* Below it is shown how this could be used for e.g. a light sensor
108+
*/
109+
/*
110+
void addToJsonInfo(JsonObject& root)
111+
{
112+
int reading = 20;
113+
//this code adds "u":{"Light":[20," lux"]} to the info object
114+
JsonObject user = root["u"];
115+
if (user.isNull()) user = root.createNestedObject("u");
116+
117+
JsonArray lightArr = user.createNestedArray("Light"); //name
118+
lightArr.add(reading); //value
119+
lightArr.add(" lux"); //unit
120+
}
121+
*/
122+
123+
124+
/*
125+
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
126+
* Values in the state object may be modified by connected clients
127+
*/
128+
void addToJsonState(JsonObject& root)
129+
{
130+
//root["user0"] = userVar0;
131+
}
132+
133+
134+
/*
135+
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
136+
* Values in the state object may be modified by connected clients
137+
*/
138+
void readFromJsonState(JsonObject& root)
139+
{
140+
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
141+
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
142+
}
143+
144+
145+
/*
146+
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
147+
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
148+
* If you want to force saving the current state, use serializeConfig() in your loop().
149+
*
150+
* CAUTION: serializeConfig() will initiate a filesystem write operation.
151+
* It might cause the LEDs to stutter and will cause flash wear if called too often.
152+
* Use it sparingly and always in the loop, never in network callbacks!
153+
*
154+
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
155+
*
156+
* Usermod Settings Overview:
157+
* - Numeric values are treated as floats in the browser.
158+
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
159+
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
160+
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
161+
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
162+
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
163+
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
164+
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
165+
* used in the Usermod when reading the value from ArduinoJson.
166+
* - Pin values can be treated differently from an integer value by using the key name "pin"
167+
* - "pin" can contain a single or array of integer values
168+
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
169+
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
170+
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
171+
*
172+
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
173+
*
174+
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
175+
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
176+
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
177+
*
178+
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
179+
*/
180+
void addToConfig(JsonObject& root)
181+
{
182+
JsonObject top = root.createNestedObject("exampleUsermod");
183+
top["great"] = userVar0; //save these vars persistently whenever settings are saved
184+
top["testBool"] = testBool;
185+
top["testInt"] = testInt;
186+
top["testLong"] = testLong;
187+
top["testULong"] = testULong;
188+
top["testFloat"] = testFloat;
189+
top["testString"] = testString;
190+
JsonArray pinArray = top.createNestedArray("pin");
191+
pinArray.add(testPins[0]);
192+
pinArray.add(testPins[1]);
193+
}
194+
195+
196+
/*
197+
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
198+
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
199+
*
200+
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
201+
* 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.
202+
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
203+
*
204+
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
205+
*
206+
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
207+
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
208+
*
209+
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
210+
*/
211+
bool readFromConfig(JsonObject& root)
212+
{
213+
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
214+
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
215+
216+
JsonObject top = root["exampleUsermod"];
217+
218+
bool configComplete = !top.isNull();
219+
220+
configComplete &= getJsonValue(top["great"], userVar0);
221+
configComplete &= getJsonValue(top["testBool"], testBool);
222+
configComplete &= getJsonValue(top["testULong"], testULong);
223+
configComplete &= getJsonValue(top["testFloat"], testFloat);
224+
configComplete &= getJsonValue(top["testString"], testString);
225+
226+
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
227+
configComplete &= getJsonValue(top["testInt"], testInt, 42);
228+
configComplete &= getJsonValue(top["testLong"], testLong, -42424242);
229+
configComplete &= getJsonValue(top["pin"][0], testPins[0], -1);
230+
configComplete &= getJsonValue(top["pin"][1], testPins[1], -1);
231+
232+
return configComplete;
233+
}
234+
235+
236+
/*
237+
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
238+
* This could be used in the future for the system to determine whether your usermod is installed.
239+
*/
240+
uint16_t getId()
241+
{
242+
return USERMOD_ID_SEVEN_SEGMENT_DISPLAY;
243+
}
244+
245+
//More methods can be added in the future, this example will then be extended.
246+
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
247+
};

wled00/const.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h"
6060
#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h"
6161
#define USERMOD_ID_BATTERY_STATUS_BASIC 18 //Usermod "usermod_v2_battery_status_basic.h"
62+
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 19 //Usermod "usermod_v2_seven_segment_display.h"
6263

6364
//Access point behavior
6465
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

wled00/fcn_declare.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ void sendSysInfoUDP();
205205
class Usermod {
206206
public:
207207
virtual void loop() {}
208+
virtual void handleOverlayDraw() {}
208209
virtual void setup() {}
209210
virtual void connected() {}
210211
virtual void addToJsonState(JsonObject& obj) {}
@@ -224,6 +225,7 @@ class UsermodManager {
224225

225226
public:
226227
void loop();
228+
void handleOverlayDraw();
227229

228230
void setup();
229231
void connected();

wled00/overlay.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ void initCronixie()
1717
}
1818
}
1919

20-
20+
/*
21+
* handleOverlays is essentially the equivalent of usermods.loop
22+
*/
2123
void handleOverlays()
2224
{
2325
initCronixie();
@@ -111,8 +113,11 @@ void _overlayAnalogCountdown()
111113
}
112114
}
113115

114-
116+
/*
117+
* All overlays should be moved to usermods
118+
*/
115119
void handleOverlayDraw() {
120+
usermods.handleOverlayDraw();
116121
if (!overlayCurrent) return;
117122
switch (overlayCurrent)
118123
{
@@ -121,7 +126,6 @@ void handleOverlayDraw() {
121126
}
122127
}
123128

124-
125129
/*
126130
* Support for the Cronixie clock
127131
*/

wled00/um_manager.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
*/
55

66
//Usermod Manager internals
7-
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); }
7+
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); }
8+
void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); }
89

910
void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); }
1011
void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); }

wled00/usermods_list.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@
8686
#include "../usermods/rgb-rotary-encoder/rgb-rotary-encoder.h"
8787
#endif
8888

89+
#ifdef USERMOD_SEVEN_SEGMENT
90+
#include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h"
91+
#endif
92+
8993
void registerUsermods()
9094
{
9195
/*
@@ -167,4 +171,8 @@ void registerUsermods()
167171
#ifdef RGB_ROTARY_ENCODER
168172
usermods.add(new RgbRotaryEncoderUsermod());
169173
#endif
174+
175+
#ifdef USERMOD_SEVEN_SEGMENT
176+
usermods.add(new SevenSegmentDisplay());
177+
#endif
170178
}

0 commit comments

Comments
 (0)