7b969bb8c2
Changed main segment, must be selected
556 lines
18 KiB
C++
556 lines
18 KiB
C++
#pragma once
|
|
|
|
#include "wled.h"
|
|
|
|
class UsermodSSDR : public Usermod {
|
|
|
|
//#define REFRESHTIME 497
|
|
|
|
private:
|
|
//Runtime variables.
|
|
unsigned long umSSDRLastRefresh = 0;
|
|
unsigned long umSSDRResfreshTime = 3000;
|
|
bool umSSDRDisplayTime = false;
|
|
bool umSSDRInverted = false;
|
|
bool umSSDRColonblink = true;
|
|
bool umSSDREnableLDR = false;
|
|
String umSSDRHours = "";
|
|
String umSSDRMinutes = "";
|
|
String umSSDRSeconds = "";
|
|
String umSSDRColons = "";
|
|
String umSSDRDays = "";
|
|
String umSSDRMonths = "";
|
|
String umSSDRYears = "";
|
|
uint16_t umSSDRLength = 0;
|
|
uint16_t umSSDRBrightnessMin = 0;
|
|
uint16_t umSSDRBrightnessMax = 255;
|
|
|
|
bool* umSSDRMask = 0;
|
|
|
|
/*// H - 00-23 hours
|
|
// h - 01-12 hours
|
|
// k - 01-24 hours
|
|
// m - 00-59 minutes
|
|
// s - 00-59 seconds
|
|
// d - 01-31 day of month
|
|
// M - 01-12 month
|
|
// y - 21 last two positions of year
|
|
// Y - 2021 year
|
|
// : for a colon
|
|
*/
|
|
String umSSDRDisplayMask = "H:m"; //This should reflect physical equipment.
|
|
|
|
/* Segment order, seen from the front:
|
|
|
|
< A >
|
|
/\ /\
|
|
F B
|
|
\/ \/
|
|
< G >
|
|
/\ /\
|
|
E C
|
|
\/ \/
|
|
< D >
|
|
|
|
*/
|
|
|
|
uint8_t umSSDRNumbers[11][7] = {
|
|
// A B C D E F G
|
|
{ 1, 1, 1, 1, 1, 1, 0 }, // 0
|
|
{ 0, 1, 1, 0, 0, 0, 0 }, // 1
|
|
{ 1, 1, 0, 1, 1, 0, 1 }, // 2
|
|
{ 1, 1, 1, 1, 0, 0, 1 }, // 3
|
|
{ 0, 1, 1, 0, 0, 1, 1 }, // 4
|
|
{ 1, 0, 1, 1, 0, 1, 1 }, // 5
|
|
{ 1, 0, 1, 1, 1, 1, 1 }, // 6
|
|
{ 1, 1, 1, 0, 0, 0, 0 }, // 7
|
|
{ 1, 1, 1, 1, 1, 1, 1 }, // 8
|
|
{ 1, 1, 1, 1, 0, 1, 1 }, // 9
|
|
{ 0, 0, 0, 0, 0, 0, 0 } // blank
|
|
};
|
|
|
|
//String to reduce flash memory usage
|
|
static const char _str_name[];
|
|
static const char _str_ldrEnabled[];
|
|
static const char _str_timeEnabled[];
|
|
static const char _str_inverted[];
|
|
static const char _str_colonblink[];
|
|
static const char _str_displayMask[];
|
|
static const char _str_hours[];
|
|
static const char _str_minutes[];
|
|
static const char _str_seconds[];
|
|
static const char _str_colons[];
|
|
static const char _str_days[];
|
|
static const char _str_months[];
|
|
static const char _str_years[];
|
|
static const char _str_minBrightness[];
|
|
static const char _str_maxBrightness[];
|
|
|
|
#ifdef USERMOD_SN_PHOTORESISTOR
|
|
Usermod_SN_Photoresistor *ptr;
|
|
#else
|
|
void* ptr = nullptr;
|
|
#endif
|
|
|
|
void _overlaySevenSegmentDraw() {
|
|
int displayMaskLen = static_cast<int>(umSSDRDisplayMask.length());
|
|
bool colonsDone = false;
|
|
_setAllFalse();
|
|
for (int index = 0; index < displayMaskLen; index++) {
|
|
int timeVar = 0;
|
|
switch (umSSDRDisplayMask[index]) {
|
|
case 'h':
|
|
timeVar = hourFormat12(localTime);
|
|
_showElements(&umSSDRHours, timeVar, 0, 1);
|
|
break;
|
|
case 'H':
|
|
timeVar = hour(localTime);
|
|
_showElements(&umSSDRHours, timeVar, 0, 1);
|
|
break;
|
|
case 'k':
|
|
timeVar = hour(localTime) + 1;
|
|
_showElements(&umSSDRHours, timeVar, 0, 0);
|
|
break;
|
|
case 'm':
|
|
timeVar = minute(localTime);
|
|
_showElements(&umSSDRMinutes, timeVar, 0, 0);
|
|
break;
|
|
case 's':
|
|
timeVar = second(localTime);
|
|
_showElements(&umSSDRSeconds, timeVar, 0, 0);
|
|
break;
|
|
case 'd':
|
|
timeVar = day(localTime);
|
|
_showElements(&umSSDRDays, timeVar, 0, 0);
|
|
break;
|
|
case 'M':
|
|
timeVar = month(localTime);
|
|
_showElements(&umSSDRMonths, timeVar, 0, 0);
|
|
break;
|
|
case 'y':
|
|
timeVar = second(localTime);
|
|
_showElements(&umSSDRYears, timeVar, 0, 0);
|
|
break;
|
|
case 'Y':
|
|
timeVar = year(localTime);
|
|
_showElements(&umSSDRYears, timeVar, 0, 0);
|
|
break;
|
|
case ':':
|
|
if (!colonsDone) { // only call _setColons once as all colons are printed when the first colon is found
|
|
_setColons();
|
|
colonsDone = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
_setMaskToLeds();
|
|
}
|
|
|
|
void _setColons() {
|
|
if ( umSSDRColonblink ) {
|
|
if ( second(localTime) % 2 == 0 ) {
|
|
_showElements(&umSSDRColons, 0, 1, 0);
|
|
}
|
|
} else {
|
|
_showElements(&umSSDRColons, 0, 1, 0);
|
|
}
|
|
}
|
|
|
|
void _showElements(String *map, int timevar, bool isColon, bool removeZero
|
|
|
|
) {
|
|
if (!(*map).equals("") && !(*map) == NULL) {
|
|
int length = String(timevar).length();
|
|
bool addZero = false;
|
|
if (length == 1) {
|
|
length = 2;
|
|
addZero = true;
|
|
}
|
|
int timeArr[length];
|
|
if(addZero) {
|
|
if(removeZero)
|
|
{
|
|
timeArr[1] = 10;
|
|
timeArr[0] = timevar;
|
|
}
|
|
else
|
|
{
|
|
timeArr[1] = 0;
|
|
timeArr[0] = timevar;
|
|
}
|
|
} else {
|
|
int count = 0;
|
|
while (timevar) {
|
|
timeArr[count] = timevar%10;
|
|
timevar /= 10;
|
|
count++;
|
|
};
|
|
}
|
|
|
|
|
|
int colonsLen = static_cast<int>((*map).length());
|
|
int count = 0;
|
|
int countSegments = 0;
|
|
int countDigit = 0;
|
|
bool range = false;
|
|
int lastSeenLedNr = 0;
|
|
|
|
for (int index = 0; index < colonsLen; index++) {
|
|
switch ((*map)[index]) {
|
|
case '-':
|
|
lastSeenLedNr = _checkForNumber(count, index, map);
|
|
count = 0;
|
|
range = true;
|
|
break;
|
|
case ':':
|
|
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
|
count = 0;
|
|
range = false;
|
|
countDigit++;
|
|
countSegments = 0;
|
|
break;
|
|
case ';':
|
|
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
|
count = 0;
|
|
range = false;
|
|
countSegments++;
|
|
break;
|
|
case ',':
|
|
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
|
count = 0;
|
|
range = false;
|
|
break;
|
|
default:
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
_setLeds(_checkForNumber(count, colonsLen, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
|
}
|
|
}
|
|
|
|
void _setLeds(int lednr, int lastSeenLedNr, bool range, int countSegments, int number, bool colon) {
|
|
|
|
if ((colon && umSSDRColonblink) || umSSDRNumbers[number][countSegments]) {
|
|
|
|
if (range) {
|
|
for(int i = lastSeenLedNr; i <= lednr; i++) {
|
|
umSSDRMask[i] = true;
|
|
}
|
|
} else {
|
|
umSSDRMask[lednr] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void _setMaskToLeds() {
|
|
for(int i = 0; i <= umSSDRLength; i++) {
|
|
if ((!umSSDRInverted && !umSSDRMask[i]) || (umSSDRInverted && umSSDRMask[i])) {
|
|
strip.setPixelColor(i, 0x000000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _setAllFalse() {
|
|
for(int i = 0; i <= umSSDRLength; i++) {
|
|
umSSDRMask[i] = false;
|
|
}
|
|
}
|
|
|
|
int _checkForNumber(int count, int index, String *map) {
|
|
String number = (*map).substring(index - count, index);
|
|
return number.toInt();
|
|
}
|
|
|
|
void _publishMQTTint_P(const char *subTopic, int value)
|
|
{
|
|
if(mqtt == NULL) return;
|
|
|
|
char buffer[64];
|
|
char valBuffer[12];
|
|
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic);
|
|
sprintf_P(valBuffer, PSTR("%d"), value);
|
|
mqtt->publish(buffer, 2, true, valBuffer);
|
|
}
|
|
|
|
void _publishMQTTstr_P(const char *subTopic, String Value)
|
|
{
|
|
if(mqtt == NULL) return;
|
|
char buffer[64];
|
|
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic);
|
|
mqtt->publish(buffer, 2, true, Value.c_str(), Value.length());
|
|
}
|
|
|
|
bool _cmpIntSetting_P(char *topic, char *payload, const char *setting, void *value)
|
|
{
|
|
if (strcmp_P(topic, setting) == 0)
|
|
{
|
|
*((int *)value) = strtol(payload, NULL, 10);
|
|
_publishMQTTint_P(setting, *((int *)value));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _handleSetting(char *topic, char *payload) {
|
|
if (_cmpIntSetting_P(topic, payload, _str_timeEnabled, &umSSDRDisplayTime)) {
|
|
return true;
|
|
}
|
|
if (_cmpIntSetting_P(topic, payload, _str_ldrEnabled, &umSSDREnableLDR)) {
|
|
return true;
|
|
}
|
|
if (_cmpIntSetting_P(topic, payload, _str_inverted, &umSSDRInverted)) {
|
|
return true;
|
|
}
|
|
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
|
|
return true;
|
|
}
|
|
if (strcmp_P(topic, _str_displayMask) == 0) {
|
|
umSSDRDisplayMask = String(payload);
|
|
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void _updateMQTT()
|
|
{
|
|
_publishMQTTint_P(_str_timeEnabled, umSSDRDisplayTime);
|
|
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
|
|
_publishMQTTint_P(_str_inverted, umSSDRInverted);
|
|
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
|
|
|
|
_publishMQTTstr_P(_str_hours, umSSDRHours);
|
|
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
|
|
_publishMQTTstr_P(_str_seconds, umSSDRSeconds);
|
|
_publishMQTTstr_P(_str_colons, umSSDRColons);
|
|
_publishMQTTstr_P(_str_days, umSSDRDays);
|
|
_publishMQTTstr_P(_str_months, umSSDRMonths);
|
|
_publishMQTTstr_P(_str_years, umSSDRYears);
|
|
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
|
|
|
|
_publishMQTTint_P(_str_minBrightness, umSSDRBrightnessMin);
|
|
_publishMQTTint_P(_str_maxBrightness, umSSDRBrightnessMax);
|
|
}
|
|
|
|
void _addJSONObject(JsonObject& root) {
|
|
JsonObject ssdrObj = root[FPSTR(_str_name)];
|
|
if (ssdrObj.isNull()) {
|
|
ssdrObj = root.createNestedObject(FPSTR(_str_name));
|
|
}
|
|
|
|
ssdrObj[FPSTR(_str_timeEnabled)] = umSSDRDisplayTime;
|
|
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
|
|
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
|
|
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
|
|
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
|
|
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
|
|
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
|
|
ssdrObj[FPSTR(_str_seconds)] = umSSDRSeconds;
|
|
ssdrObj[FPSTR(_str_colons)] = umSSDRColons;
|
|
ssdrObj[FPSTR(_str_days)] = umSSDRDays;
|
|
ssdrObj[FPSTR(_str_months)] = umSSDRMonths;
|
|
ssdrObj[FPSTR(_str_years)] = umSSDRYears;
|
|
ssdrObj[FPSTR(_str_minBrightness)] = umSSDRBrightnessMin;
|
|
ssdrObj[FPSTR(_str_maxBrightness)] = umSSDRBrightnessMax;
|
|
}
|
|
|
|
public:
|
|
//Functions called by WLED
|
|
|
|
/*
|
|
* 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() {
|
|
umSSDRLength = strip.getLengthTotal();
|
|
if (umSSDRMask != 0) {
|
|
umSSDRMask = (bool*) realloc(umSSDRMask, umSSDRLength * sizeof(bool));
|
|
} else {
|
|
umSSDRMask = (bool*) malloc(umSSDRLength * sizeof(bool));
|
|
}
|
|
_setAllFalse();
|
|
|
|
#ifdef USERMOD_SN_PHOTORESISTOR
|
|
ptr = (Usermod_SN_Photoresistor*) usermods.lookup(USERMOD_ID_SN_PHOTORESISTOR);
|
|
#endif
|
|
DEBUG_PRINTLN(F("Setup done"));
|
|
}
|
|
|
|
/*
|
|
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
|
*/
|
|
void loop() {
|
|
if (!umSSDRDisplayTime || strip.isUpdating()) {
|
|
return;
|
|
}
|
|
#ifdef USERMOD_ID_SN_PHOTORESISTOR
|
|
if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
|
|
if (ptr != nullptr) {
|
|
uint16_t lux = ptr->getLastLDRValue();
|
|
uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax);
|
|
if (bri != brightness) {
|
|
bri = brightness;
|
|
stateUpdated(1);
|
|
}
|
|
}
|
|
umSSDRLastRefresh = millis();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void handleOverlayDraw() {
|
|
if (umSSDRDisplayTime) {
|
|
_overlaySevenSegmentDraw();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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[F("u")];
|
|
if (user.isNull()) {
|
|
user = root.createNestedObject(F("u"));
|
|
}
|
|
JsonArray enabled = user.createNestedArray("Time enabled");
|
|
enabled.add(umSSDRDisplayTime);
|
|
JsonArray invert = user.createNestedArray("Time inverted");
|
|
invert.add(umSSDRInverted);
|
|
JsonArray blink = user.createNestedArray("Blinking colon");
|
|
blink.add(umSSDRColonblink);
|
|
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
|
|
ldrEnable.add(umSSDREnableLDR);
|
|
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
JsonObject user = root[F("u")];
|
|
if (user.isNull()) {
|
|
user = root.createNestedObject(F("u"));
|
|
}
|
|
_addJSONObject(user);
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
JsonObject user = root[F("u")];
|
|
if (!user.isNull()) {
|
|
JsonObject ssdrObj = user[FPSTR(_str_name)];
|
|
umSSDRDisplayTime = ssdrObj[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime;
|
|
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
|
|
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
|
|
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
|
|
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
|
|
}
|
|
}
|
|
|
|
void onMqttConnect(bool sessionPresent) {
|
|
char subBuffer[48];
|
|
if (mqttDeviceTopic[0] != 0)
|
|
{
|
|
_updateMQTT();
|
|
//subscribe for sevenseg messages on the device topic
|
|
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttDeviceTopic, _str_name);
|
|
mqtt->subscribe(subBuffer, 2);
|
|
}
|
|
|
|
if (mqttGroupTopic[0] != 0)
|
|
{
|
|
//subcribe for sevenseg messages on the group topic
|
|
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_name);
|
|
mqtt->subscribe(subBuffer, 2);
|
|
}
|
|
}
|
|
|
|
bool onMqttMessage(char *topic, char *payload) {
|
|
//If topic beings iwth sevenSeg cut it off, otherwise not our message.
|
|
size_t topicPrefixLen = strlen_P(PSTR("/wledSS/"));
|
|
if (strncmp_P(topic, PSTR("/wledSS/"), topicPrefixLen) == 0) {
|
|
topic += topicPrefixLen;
|
|
} else {
|
|
return false;
|
|
}
|
|
//We only care if the topic ends with /set
|
|
size_t topicLen = strlen(topic);
|
|
if (topicLen > 4 &&
|
|
topic[topicLen - 4] == '/' &&
|
|
topic[topicLen - 3] == 's' &&
|
|
topic[topicLen - 2] == 'e' &&
|
|
topic[topicLen - 1] == 't')
|
|
{
|
|
//Trim /set and handle it
|
|
topic[topicLen - 4] = '\0';
|
|
_handleSetting(topic, payload);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void addToConfig(JsonObject &root) {
|
|
_addJSONObject(root);
|
|
}
|
|
|
|
bool readFromConfig(JsonObject &root) {
|
|
JsonObject top = root[FPSTR(_str_name)];
|
|
|
|
if (top.isNull()) {
|
|
DEBUG_PRINT(FPSTR(_str_name));
|
|
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
|
return false;
|
|
}
|
|
|
|
umSSDRDisplayTime = (top[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime);
|
|
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
|
|
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
|
|
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
|
|
|
|
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
|
|
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
|
|
umSSDRMinutes = top[FPSTR(_str_minutes)] | umSSDRMinutes;
|
|
umSSDRSeconds = top[FPSTR(_str_seconds)] | umSSDRSeconds;
|
|
umSSDRColons = top[FPSTR(_str_colons)] | umSSDRColons;
|
|
umSSDRDays = top[FPSTR(_str_days)] | umSSDRDays;
|
|
umSSDRMonths = top[FPSTR(_str_months)] | umSSDRMonths;
|
|
umSSDRYears = top[FPSTR(_str_years)] | umSSDRYears;
|
|
umSSDRBrightnessMin = top[FPSTR(_str_minBrightness)] | umSSDRBrightnessMin;
|
|
umSSDRBrightnessMax = top[FPSTR(_str_maxBrightness)] | umSSDRBrightnessMax;
|
|
|
|
DEBUG_PRINT(FPSTR(_str_name));
|
|
DEBUG_PRINTLN(F(" config (re)loaded."));
|
|
|
|
return true;
|
|
}
|
|
/*
|
|
* 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_SSDR;
|
|
}
|
|
};
|
|
|
|
const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
|
|
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
|
|
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
|
|
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
|
|
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
|
|
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
|
|
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";
|
|
const char UsermodSSDR::_str_seconds[] PROGMEM = "LED-Numbers-Seconds";
|
|
const char UsermodSSDR::_str_colons[] PROGMEM = "LED-Numbers-Colons";
|
|
const char UsermodSSDR::_str_days[] PROGMEM = "LED-Numbers-Day";
|
|
const char UsermodSSDR::_str_months[] PROGMEM = "LED-Numbers-Month";
|
|
const char UsermodSSDR::_str_years[] PROGMEM = "LED-Numbers-Year";
|
|
const char UsermodSSDR::_str_ldrEnabled[] PROGMEM = "enable-auto-brightness";
|
|
const char UsermodSSDR::_str_minBrightness[] PROGMEM = "auto-brightness-min";
|
|
const char UsermodSSDR::_str_maxBrightness[] PROGMEM = "auto-brightness-max";
|