Merge branch 'master' into CCT-support
This commit is contained in:
commit
d590e01a58
27
.github/ISSUE_TEMPLATE/bug.md
vendored
27
.github/ISSUE_TEMPLATE/bug.md
vendored
@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug
|
|
||||||
about: Noticed an issue with your lights?
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is. Please quickly search existing issues first!
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior, if consistently possible
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**WLED version**
|
|
||||||
- Board: [e.g. Wemos D1, ESP32 dev]
|
|
||||||
- Version [e.g. 0.10.0, dev200603]
|
|
||||||
- Format [e.g. Binary, self-compiled]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Anything else you'd like to say about the problem?
|
|
||||||
|
|
||||||
Thank you for your help!
|
|
83
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
83
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Please quickly search existing issues first before submitting a bug.
|
||||||
|
- type: textarea
|
||||||
|
id: what-happened
|
||||||
|
attributes:
|
||||||
|
label: What happened?
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
placeholder: Tell us what the problem is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: how-to-reproduce
|
||||||
|
attributes:
|
||||||
|
label: To Reproduce Bug
|
||||||
|
description: Steps to reproduce the behavior, if consistently possible.
|
||||||
|
placeholder: Tell us how to make the bug appear.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
placeholder: Tell us what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: install_format
|
||||||
|
attributes:
|
||||||
|
label: Install Method
|
||||||
|
description: How did you install WLED?
|
||||||
|
options:
|
||||||
|
- Binary from WLED.me
|
||||||
|
- Self-Compiled
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: What version of WLED?
|
||||||
|
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
||||||
|
placeholder: "e.g. WLED 0.13.0-b4 (build 2110110)"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: Board
|
||||||
|
attributes:
|
||||||
|
label: Which microcontroller/board are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- ESP8266
|
||||||
|
- ESP32
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant log/trace output
|
||||||
|
description: Please copy and paste any relevant log output if you have it. This will be automatically formatted into code, so no need for backticks.
|
||||||
|
render: shell
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Anything else?
|
||||||
|
description: |
|
||||||
|
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||||
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Aircoookie/WLED/blob/master/CODE_OF_CONDUCT.md)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
@ -24,7 +24,7 @@ void RGBNET_readValues() {
|
|||||||
int channel = UDP.read();
|
int channel = UDP.read();
|
||||||
|
|
||||||
//channel data is not used we only supports one channel
|
//channel data is not used we only supports one channel
|
||||||
int len = UDP.read(RGBNET_packet, ledCount*3);
|
int len = UDP.read(RGBNET_packet, strip.getLengthTotal()*3);
|
||||||
if(len==0){
|
if(len==0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ void handleConfig(AsyncWebServerRequest *request)
|
|||||||
\"channels\": [\
|
\"channels\": [\
|
||||||
{\
|
{\
|
||||||
\"channel\": 1,\
|
\"channel\": 1,\
|
||||||
\"leds\": " + ledCount + "\
|
\"leds\": " + strip.getLengthTotal() + "\
|
||||||
},\
|
},\
|
||||||
{\
|
{\
|
||||||
\"channel\": 2,\
|
\"channel\": 2,\
|
||||||
|
@ -9,11 +9,11 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
|
|||||||
|
|
||||||
### Define Your Options
|
### Define Your Options
|
||||||
|
|
||||||
* `USERMOD_BH1750` - define this to have this user mod included wled00\usermods_list.cpp
|
* `USERMOD_BH1750` - define this to have this user mod included wled00\usermods_list.cpp
|
||||||
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
||||||
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
||||||
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
||||||
|
|
||||||
All parameters can be configured at runtime using Usermods settings page.
|
All parameters can be configured at runtime using Usermods settings page.
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ Examples
|
|||||||
1. 4 relays at all, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
|
1. 4 relays at all, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
|
||||||
2. 3 relays at all, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
|
2. 3 relays at all, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
|
||||||
|
|
||||||
|
## JSON API
|
||||||
|
You can switch relay state using the following JSON object transmitted to: `http://[device-ip]/json`
|
||||||
|
|
||||||
|
Switch relay 0 on: `{"MultiRelay":{"relay":0,"on":true}}`
|
||||||
|
Switch relay4 3 & 4 off: `{"MultiRelay":[{"relay":2,"on":false},{"relay":3,"on":false}]}`
|
||||||
|
|
||||||
## MQTT API
|
## MQTT API
|
||||||
|
|
||||||
wled/deviceMAC/relay/0/command on|off|toggle
|
wled/deviceMAC/relay/0/command on|off|toggle
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#define MULTI_RELAY_MAX_RELAYS 4
|
#define MULTI_RELAY_MAX_RELAYS 4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
||||||
|
|
||||||
#define ON true
|
#define ON true
|
||||||
#define OFF false
|
#define OFF false
|
||||||
|
|
||||||
@ -23,6 +25,7 @@ typedef struct relay_t {
|
|||||||
bool state;
|
bool state;
|
||||||
bool external;
|
bool external;
|
||||||
uint16_t delay;
|
uint16_t delay;
|
||||||
|
int8_t button;
|
||||||
} Relay;
|
} Relay;
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +38,7 @@ class MultiRelay : public Usermod {
|
|||||||
// switch timer start time
|
// switch timer start time
|
||||||
uint32_t _switchTimerStart = 0;
|
uint32_t _switchTimerStart = 0;
|
||||||
// old brightness
|
// old brightness
|
||||||
bool _oldBrightness = 0;
|
bool _oldMode;
|
||||||
|
|
||||||
// usermod enabled
|
// usermod enabled
|
||||||
bool enabled = false; // needs to be configured (no default config)
|
bool enabled = false; // needs to be configured (no default config)
|
||||||
@ -49,6 +52,7 @@ class MultiRelay : public Usermod {
|
|||||||
static const char _delay_str[];
|
static const char _delay_str[];
|
||||||
static const char _activeHigh[];
|
static const char _activeHigh[];
|
||||||
static const char _external[];
|
static const char _external[];
|
||||||
|
static const char _button[];
|
||||||
|
|
||||||
|
|
||||||
void publishMqtt(const char* state, int relay) {
|
void publishMqtt(const char* state, int relay) {
|
||||||
@ -170,6 +174,7 @@ class MultiRelay : public Usermod {
|
|||||||
_relay[i].active = false;
|
_relay[i].active = false;
|
||||||
_relay[i].state = false;
|
_relay[i].state = false;
|
||||||
_relay[i].external = false;
|
_relay[i].external = false;
|
||||||
|
_relay[i].button = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -261,11 +266,12 @@ class MultiRelay : public Usermod {
|
|||||||
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
|
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
|
||||||
_relay[i].pin = -1; // allocation failed
|
_relay[i].pin = -1; // allocation failed
|
||||||
} else {
|
} else {
|
||||||
switchRelay(i, _relay[i].state = (bool)bri);
|
if (!_relay[i].external) _relay[i].state = offMode;
|
||||||
|
switchRelay(i, _relay[i].state);
|
||||||
_relay[i].active = false;
|
_relay[i].active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_oldBrightness = (bool)bri;
|
_oldMode = offMode;
|
||||||
initDone = true;
|
initDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,24 +287,110 @@ class MultiRelay : public Usermod {
|
|||||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||||
*/
|
*/
|
||||||
void loop() {
|
void loop() {
|
||||||
|
yield();
|
||||||
if (!enabled || strip.isUpdating()) return;
|
if (!enabled || strip.isUpdating()) return;
|
||||||
|
|
||||||
static unsigned long lastUpdate = 0;
|
static unsigned long lastUpdate = 0;
|
||||||
if (millis() - lastUpdate < 200) return; // update only 5 times/s
|
if (millis() - lastUpdate < 100) return; // update only 10 times/s
|
||||||
lastUpdate = millis();
|
lastUpdate = millis();
|
||||||
|
|
||||||
//set relay when LEDs turn on
|
//set relay when LEDs turn on
|
||||||
if (_oldBrightness != (bool)bri) {
|
if (_oldMode != offMode) {
|
||||||
_oldBrightness = (bool)bri;
|
_oldMode = offMode;
|
||||||
_switchTimerStart = millis();
|
_switchTimerStart = millis();
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relay[i].pin>=0) _relay[i].active = true;
|
if (_relay[i].pin>=0 && !_relay[i].external) _relay[i].active = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOffTimer();
|
handleOffTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handleButton() can be used to override default button behaviour. Returning true
|
||||||
|
* will prevent button working in a default way.
|
||||||
|
* Replicating button.cpp
|
||||||
|
*/
|
||||||
|
bool handleButton(uint8_t b) {
|
||||||
|
yield();
|
||||||
|
if (buttonType[b] == BTN_TYPE_NONE || buttonType[b] == BTN_TYPE_RESERVED || buttonType[b] == BTN_TYPE_PIR_SENSOR || buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handled = false;
|
||||||
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
if (_relay[i].button == b) {
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!handled) return false;
|
||||||
|
|
||||||
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
//button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
||||||
|
if (buttonType[b] == BTN_TYPE_SWITCH) {
|
||||||
|
//handleSwitch(b);
|
||||||
|
if (buttonPressedBefore[b] != isButtonPressed(b)) {
|
||||||
|
buttonPressedTime[b] = now;
|
||||||
|
buttonPressedBefore[b] = !buttonPressedBefore[b];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonLongPressed[b] == buttonPressedBefore[b]) return handled;
|
||||||
|
|
||||||
|
if (now - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
||||||
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
if (_relay[i].pin>=0 && _relay[i].button == b) {
|
||||||
|
switchRelay(i, buttonPressedBefore[b]);
|
||||||
|
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
//momentary button logic
|
||||||
|
if (isButtonPressed(b)) { //pressed
|
||||||
|
|
||||||
|
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
|
||||||
|
buttonPressedBefore[b] = true;
|
||||||
|
|
||||||
|
if (now - buttonPressedTime[b] > 600) { //long press
|
||||||
|
buttonLongPressed[b] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
|
||||||
|
|
||||||
|
long dur = now - buttonPressedTime[b];
|
||||||
|
if (dur < WLED_DEBOUNCE_THRESHOLD) {
|
||||||
|
buttonPressedBefore[b] = false;
|
||||||
|
return handled;
|
||||||
|
} //too short "press", debounce
|
||||||
|
bool doublePress = buttonWaitTime[b]; //did we have short press before?
|
||||||
|
buttonWaitTime[b] = 0;
|
||||||
|
|
||||||
|
if (!buttonLongPressed[b]) { //short press
|
||||||
|
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
||||||
|
if (doublePress) {
|
||||||
|
//doublePressAction(b);
|
||||||
|
} else {
|
||||||
|
buttonWaitTime[b] = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buttonPressedBefore[b] = false;
|
||||||
|
buttonLongPressed[b] = false;
|
||||||
|
}
|
||||||
|
// if 450ms elapsed since last press/release it is a short press
|
||||||
|
if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
|
||||||
|
buttonWaitTime[b] = 0;
|
||||||
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
if (_relay[i].pin>=0 && _relay[i].button == b) {
|
||||||
|
toggleRelay(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||||
*/
|
*/
|
||||||
@ -310,6 +402,26 @@ class MultiRelay : public Usermod {
|
|||||||
|
|
||||||
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
|
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
|
||||||
infoArr.add(String(getActiveRelayCount()));
|
infoArr.add(String(getActiveRelayCount()));
|
||||||
|
|
||||||
|
String uiDomString;
|
||||||
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
if (_relay[i].pin<0 || !_relay[i].external) continue;
|
||||||
|
uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
|
||||||
|
uiDomString += FPSTR(_name);
|
||||||
|
uiDomString += F(":{");
|
||||||
|
uiDomString += FPSTR(_relay_str);
|
||||||
|
uiDomString += F(":");
|
||||||
|
uiDomString += i;
|
||||||
|
uiDomString += F(",on:");
|
||||||
|
uiDomString += _relay[i].state ? "false" : "true";
|
||||||
|
uiDomString += F("}});\">");
|
||||||
|
uiDomString += F("Relay ");
|
||||||
|
uiDomString += i;
|
||||||
|
uiDomString += F(" <i class=\"icons\"></i></button>");
|
||||||
|
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||||
|
|
||||||
|
infoArr.add(_relay[i].state ? "on" : "off");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,15 +429,46 @@ class MultiRelay : public Usermod {
|
|||||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
* 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
|
* Values in the state object may be modified by connected clients
|
||||||
*/
|
*/
|
||||||
//void addToJsonState(JsonObject &root) {
|
void addToJsonState(JsonObject &root) {
|
||||||
//}
|
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
|
||||||
|
JsonObject multiRelay = root[FPSTR(_name)];
|
||||||
|
if (multiRelay.isNull()) {
|
||||||
|
multiRelay = root.createNestedObject(FPSTR(_name));
|
||||||
|
}
|
||||||
|
#if MULTI_RELAY_MAX_RELAYS > 1
|
||||||
|
JsonArray rel_arr = multiRelay.createNestedArray(F("relays"));
|
||||||
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
if (_relay[i].pin < 0) continue;
|
||||||
|
JsonObject relay = rel_arr.createNestedObject();
|
||||||
|
relay[FPSTR(_relay_str)] = i;
|
||||||
|
relay[F("state")] = _relay[i].state;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
multiRelay[FPSTR(_relay_str)] = 0;
|
||||||
|
multiRelay[F("state")] = _relay[0].state;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
* 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
|
* Values in the state object may be modified by connected clients
|
||||||
*/
|
*/
|
||||||
//void readFromJsonState(JsonObject &root) {
|
void readFromJsonState(JsonObject &root) {
|
||||||
//}
|
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
|
||||||
|
JsonObject usermod = root[FPSTR(_name)];
|
||||||
|
if (!usermod.isNull()) {
|
||||||
|
if (usermod["on"].is<bool>() && usermod[FPSTR(_relay_str)].is<int>() && usermod[FPSTR(_relay_str)].as<int>()>=0) {
|
||||||
|
switchRelay(usermod[FPSTR(_relay_str)].as<int>(), usermod["on"].as<bool>());
|
||||||
|
}
|
||||||
|
} else if (root[FPSTR(_name)].is<JsonArray>()) {
|
||||||
|
JsonArray relays = root[FPSTR(_name)].as<JsonArray>();
|
||||||
|
for (JsonVariant r : relays) {
|
||||||
|
if (r["on"].is<bool>() && r[FPSTR(_relay_str)].is<int>() && r[FPSTR(_relay_str)].as<int>()>=0) {
|
||||||
|
switchRelay(r[FPSTR(_relay_str)].as<int>(), r["on"].as<bool>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* provide the changeable values
|
* provide the changeable values
|
||||||
@ -341,6 +484,7 @@ class MultiRelay : public Usermod {
|
|||||||
relay[FPSTR(_activeHigh)] = _relay[i].mode;
|
relay[FPSTR(_activeHigh)] = _relay[i].mode;
|
||||||
relay[FPSTR(_delay_str)] = _relay[i].delay;
|
relay[FPSTR(_delay_str)] = _relay[i].delay;
|
||||||
relay[FPSTR(_external)] = _relay[i].external;
|
relay[FPSTR(_external)] = _relay[i].external;
|
||||||
|
relay[FPSTR(_button)] = _relay[i].button;
|
||||||
}
|
}
|
||||||
DEBUG_PRINTLN(F("MultiRelay config saved."));
|
DEBUG_PRINTLN(F("MultiRelay config saved."));
|
||||||
}
|
}
|
||||||
@ -370,6 +514,7 @@ class MultiRelay : public Usermod {
|
|||||||
_relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode;
|
_relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode;
|
||||||
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
|
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
|
||||||
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
|
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
|
||||||
|
_relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button;
|
||||||
// begin backwards compatibility (beta) remove when 0.13 is released
|
// begin backwards compatibility (beta) remove when 0.13 is released
|
||||||
parName += '-';
|
parName += '-';
|
||||||
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
|
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
|
||||||
@ -394,7 +539,7 @@ class MultiRelay : public Usermod {
|
|||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
|
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
|
||||||
if (!_relay[i].external) {
|
if (!_relay[i].external) {
|
||||||
switchRelay(i, _relay[i].state = (bool)bri);
|
switchRelay(i, offMode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_relay[i].pin = -1;
|
_relay[i].pin = -1;
|
||||||
@ -404,7 +549,7 @@ class MultiRelay : public Usermod {
|
|||||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||||
}
|
}
|
||||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||||
return !top[F("relay-0")]["pin"].isNull();
|
return !top[F("relay-0")][FPSTR(_button)].isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -424,3 +569,4 @@ const char MultiRelay::_relay_str[] PROGMEM = "relay";
|
|||||||
const char MultiRelay::_delay_str[] PROGMEM = "delay-s";
|
const char MultiRelay::_delay_str[] PROGMEM = "delay-s";
|
||||||
const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
|
const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
|
||||||
const char MultiRelay::_external[] PROGMEM = "external";
|
const char MultiRelay::_external[] PROGMEM = "external";
|
||||||
|
const char MultiRelay::_button[] PROGMEM = "button";
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
* I've had good results with settings around 5 (20 fps).
|
* I've had good results with settings around 5 (20 fps).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include "wled.h"
|
||||||
|
|
||||||
const uint8_t PCARS_dimcolor = 20;
|
const uint8_t PCARS_dimcolor = 20;
|
||||||
WiFiUDP UDP;
|
WiFiUDP UDP;
|
||||||
const unsigned int PCARS_localUdpPort = 5606; // local port to listen on
|
const unsigned int PCARS_localUdpPort = 5606; // local port to listen on
|
||||||
@ -49,11 +51,12 @@ void PCARS_readValues() {
|
|||||||
void PCARS_buildcolorbars() {
|
void PCARS_buildcolorbars() {
|
||||||
boolean activated = false;
|
boolean activated = false;
|
||||||
float ledratio = 0;
|
float ledratio = 0;
|
||||||
|
uint16_t totalLen = strip.getLengthTotal();
|
||||||
|
|
||||||
for (uint16_t i = 0; i < ledCount; i++) {
|
for (uint16_t i = 0; i < totalLen; i++) {
|
||||||
if (PCARS_rpmRatio < .95 || (millis() % 100 > 70 )) {
|
if (PCARS_rpmRatio < .95 || (millis() % 100 > 70 )) {
|
||||||
|
|
||||||
ledratio = (float)i / (float)ledCount;
|
ledratio = (float)i / (float)totalLen;
|
||||||
if (ledratio < PCARS_rpmRatio) {
|
if (ledratio < PCARS_rpmRatio) {
|
||||||
activated = true;
|
activated = true;
|
||||||
} else {
|
} else {
|
||||||
|
755
usermods/quinled-an-penta/quinled-an-penta.h
Normal file
755
usermods/quinled-an-penta/quinled-an-penta.h
Normal file
@ -0,0 +1,755 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "U8g2lib.h"
|
||||||
|
#include "SHT85.h"
|
||||||
|
#include "Wire.h"
|
||||||
|
#include "wled.h"
|
||||||
|
|
||||||
|
class QuinLEDAnPentaUsermod : public Usermod
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool enabled = false;
|
||||||
|
bool firstRunDone = false;
|
||||||
|
bool initDone = false;
|
||||||
|
U8G2 *oledDisplay = nullptr;
|
||||||
|
SHT *sht30TempHumidSensor;
|
||||||
|
|
||||||
|
// Network info vars
|
||||||
|
bool networkHasChanged = false;
|
||||||
|
bool lastKnownNetworkConnected;
|
||||||
|
IPAddress lastKnownIp;
|
||||||
|
bool lastKnownWiFiConnected;
|
||||||
|
String lastKnownSsid;
|
||||||
|
bool lastKnownApActive;
|
||||||
|
char *lastKnownApSsid;
|
||||||
|
char *lastKnownApPass;
|
||||||
|
byte lastKnownApChannel;
|
||||||
|
int lastKnownEthType;
|
||||||
|
bool lastKnownEthLinkUp;
|
||||||
|
|
||||||
|
// Brightness / LEDC vars
|
||||||
|
byte lastKnownBri = 0;
|
||||||
|
int8_t currentBussesNumPins[5] = {0, 0, 0, 0, 0};
|
||||||
|
int8_t currentLedPins[5] = {0, 0, 0, 0, 0};
|
||||||
|
uint8_t currentLedcReads[5] = {0, 0, 0, 0, 0};
|
||||||
|
uint8_t lastKnownLedcReads[5] = {0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
// OLED vars
|
||||||
|
bool oledEnabled = false;
|
||||||
|
bool oledInitDone = false;
|
||||||
|
bool oledUseProgressBars = false;
|
||||||
|
bool oledFlipScreen = false;
|
||||||
|
bool oledFixBuggedScreen = false;
|
||||||
|
byte oledMaxPage = 3;
|
||||||
|
byte oledCurrentPage = 3; // Start with the network page to help identifying the IP
|
||||||
|
byte oledSecondsPerPage = 10;
|
||||||
|
unsigned long oledLogoDrawn = 0;
|
||||||
|
unsigned long oledLastTimeUpdated = 0;
|
||||||
|
unsigned long oledLastTimePageChange = 0;
|
||||||
|
unsigned long oledLastTimeFixBuggedScreen = 0;
|
||||||
|
|
||||||
|
// SHT30 vars
|
||||||
|
bool shtEnabled = false;
|
||||||
|
bool shtInitDone = false;
|
||||||
|
bool shtReadDataSuccess = false;
|
||||||
|
byte shtI2cAddress = 0x44;
|
||||||
|
unsigned long shtLastTimeUpdated = 0;
|
||||||
|
bool shtDataRequested = false;
|
||||||
|
float shtCurrentTemp = 0;
|
||||||
|
float shtLastKnownTemp = 0;
|
||||||
|
float shtCurrentHumidity = 0;
|
||||||
|
float shtLastKnownHumidity = 0;
|
||||||
|
|
||||||
|
// Pin/IO vars
|
||||||
|
const int8_t anPentaPins[5] = {14, 13, 12, 4, 2};
|
||||||
|
int8_t oledSpiClk = 15;
|
||||||
|
int8_t oledSpiData = 16;
|
||||||
|
int8_t oledSpiCs = 0;
|
||||||
|
int8_t oledSpiDc = 32;
|
||||||
|
int8_t oledSpiRst = 33;
|
||||||
|
int8_t shtSda = 1;
|
||||||
|
int8_t shtScl = 3;
|
||||||
|
|
||||||
|
|
||||||
|
bool isAnPentaLedPin(int8_t pin)
|
||||||
|
{
|
||||||
|
for(int8_t i = 0; i <= 4; i++)
|
||||||
|
{
|
||||||
|
if(anPentaPins[i] == pin)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getCurrentUsedLedPins()
|
||||||
|
{
|
||||||
|
for (int8_t lp = 0; lp <= 4; lp++) currentLedPins[lp] = 0;
|
||||||
|
byte numBusses = busses.getNumBusses();
|
||||||
|
byte numUsedPins = 0;
|
||||||
|
|
||||||
|
for (int8_t b = 0; b < numBusses; b++) {
|
||||||
|
Bus* curBus = busses.getBus(b);
|
||||||
|
if (curBus != nullptr) {
|
||||||
|
uint8_t pins[5] = {0, 0, 0, 0, 0};
|
||||||
|
currentBussesNumPins[b] = curBus->getPins(pins);
|
||||||
|
for (int8_t p = 0; p < currentBussesNumPins[b]; p++) {
|
||||||
|
if (isAnPentaLedPin(pins[p])) {
|
||||||
|
currentLedPins[numUsedPins] = pins[p];
|
||||||
|
numUsedPins++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getCurrentLedcValues()
|
||||||
|
{
|
||||||
|
byte numBusses = busses.getNumBusses();
|
||||||
|
byte numLedc = 0;
|
||||||
|
|
||||||
|
for (int8_t b = 0; b < numBusses; b++) {
|
||||||
|
Bus* curBus = busses.getBus(b);
|
||||||
|
if (curBus != nullptr) {
|
||||||
|
uint32_t curPixColor = curBus->getPixelColor(0);
|
||||||
|
uint8_t _data[5] = {255, 255, 255, 255, 255};
|
||||||
|
_data[3] = curPixColor >> 24;
|
||||||
|
_data[0] = curPixColor >> 16;
|
||||||
|
_data[1] = curPixColor >> 8;
|
||||||
|
_data[2] = curPixColor;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < currentBussesNumPins[b]; i++) {
|
||||||
|
currentLedcReads[numLedc] = (_data[i] * bri) / 255;
|
||||||
|
numLedc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void initOledDisplay()
|
||||||
|
{
|
||||||
|
PinManagerPinType pins[5] = { { oledSpiClk, true }, { oledSpiData, true }, { oledSpiCs, true }, { oledSpiDc, true }, { oledSpiRst, true } };
|
||||||
|
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_QuinLEDAnPenta)) {
|
||||||
|
DEBUG_PRINTF("[%s] OLED pin allocation failed!\n", _name);
|
||||||
|
oledEnabled = oledInitDone = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
oledDisplay = (U8G2 *) new U8G2_SSD1306_128X64_NONAME_2_4W_SW_SPI(U8G2_R0, oledSpiClk, oledSpiData, oledSpiCs, oledSpiDc, oledSpiRst);
|
||||||
|
if (oledDisplay == nullptr) {
|
||||||
|
DEBUG_PRINTF("[%s] OLED init failed!\n", _name);
|
||||||
|
oledEnabled = oledInitDone = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
oledDisplay->begin();
|
||||||
|
oledDisplay->setBusClock(40 * 1000 * 1000);
|
||||||
|
oledDisplay->setContrast(10);
|
||||||
|
oledDisplay->setPowerSave(0);
|
||||||
|
oledDisplay->setFont(u8g2_font_6x10_tf);
|
||||||
|
oledDisplay->setFlipMode(oledFlipScreen);
|
||||||
|
|
||||||
|
oledDisplay->firstPage();
|
||||||
|
do {
|
||||||
|
oledDisplay->drawXBMP(0, 16, 128, 36, quinLedLogo);
|
||||||
|
} while (oledDisplay->nextPage());
|
||||||
|
oledLogoDrawn = millis();
|
||||||
|
|
||||||
|
oledInitDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupOledDisplay()
|
||||||
|
{
|
||||||
|
if (oledInitDone) {
|
||||||
|
oledDisplay->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pinManager.deallocatePin(oledSpiClk, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
pinManager.deallocatePin(oledSpiData, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
pinManager.deallocatePin(oledSpiCs, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
pinManager.deallocatePin(oledSpiDc, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
pinManager.deallocatePin(oledSpiRst, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
|
||||||
|
delete oledDisplay;
|
||||||
|
|
||||||
|
oledEnabled = false;
|
||||||
|
oledInitDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOledReady()
|
||||||
|
{
|
||||||
|
return oledEnabled && oledInitDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initSht30TempHumiditySensor()
|
||||||
|
{
|
||||||
|
PinManagerPinType pins[2] = { { shtSda, true }, { shtScl, true } };
|
||||||
|
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_QuinLEDAnPenta)) {
|
||||||
|
DEBUG_PRINTF("[%s] SHT30 pin allocation failed!\n", _name);
|
||||||
|
shtEnabled = shtInitDone = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TwoWire *wire = new TwoWire(1);
|
||||||
|
wire->setClock(400000);
|
||||||
|
|
||||||
|
sht30TempHumidSensor = (SHT *) new SHT30();
|
||||||
|
sht30TempHumidSensor->begin(shtI2cAddress, wire);
|
||||||
|
// The SHT lib calls wire.begin() again without the SDA and SCL pins... So call it again here...
|
||||||
|
wire->begin(shtSda, shtScl);
|
||||||
|
if (sht30TempHumidSensor->readStatus() == 0xFFFF) {
|
||||||
|
DEBUG_PRINTF("[%s] SHT30 init failed!\n", _name);
|
||||||
|
shtEnabled = shtInitDone = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shtInitDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupSht30TempHumiditySensor()
|
||||||
|
{
|
||||||
|
if (shtInitDone) {
|
||||||
|
sht30TempHumidSensor->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
pinManager.deallocatePin(shtSda, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
pinManager.deallocatePin(shtScl, PinOwner::UM_QuinLEDAnPenta);
|
||||||
|
|
||||||
|
delete sht30TempHumidSensor;
|
||||||
|
|
||||||
|
shtEnabled = false;
|
||||||
|
shtInitDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup()
|
||||||
|
{
|
||||||
|
if (isOledReady()) {
|
||||||
|
cleanupOledDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isShtReady()) {
|
||||||
|
cleanupSht30TempHumiditySensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oledCheckForNetworkChanges()
|
||||||
|
{
|
||||||
|
if (lastKnownNetworkConnected != Network.isConnected() || lastKnownIp != Network.localIP()
|
||||||
|
|| lastKnownWiFiConnected != WiFi.isConnected() || lastKnownSsid != WiFi.SSID()
|
||||||
|
|| lastKnownApActive != apActive || lastKnownApSsid != apSSID || lastKnownApPass != apPass || lastKnownApChannel != apChannel) {
|
||||||
|
lastKnownNetworkConnected = Network.isConnected();
|
||||||
|
lastKnownIp = Network.localIP();
|
||||||
|
lastKnownWiFiConnected = WiFi.isConnected();
|
||||||
|
lastKnownSsid = WiFi.SSID();
|
||||||
|
lastKnownApActive = apActive;
|
||||||
|
lastKnownApSsid = apSSID;
|
||||||
|
lastKnownApPass = apPass;
|
||||||
|
lastKnownApChannel = apChannel;
|
||||||
|
|
||||||
|
return networkHasChanged = true;
|
||||||
|
}
|
||||||
|
#ifdef WLED_USE_ETHERNET
|
||||||
|
if (lastKnownEthType != ethernetType || lastKnownEthLinkUp != ETH.linkUp()) {
|
||||||
|
lastKnownEthType = ethernetType;
|
||||||
|
lastKnownEthLinkUp = ETH.linkUp();
|
||||||
|
|
||||||
|
return networkHasChanged = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return networkHasChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte oledGetNextPage()
|
||||||
|
{
|
||||||
|
return oledCurrentPage + 1 <= oledMaxPage ? oledCurrentPage + 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledShowPage(byte page, bool updateLastTimePageChange = false)
|
||||||
|
{
|
||||||
|
oledCurrentPage = page;
|
||||||
|
updateOledDisplay();
|
||||||
|
oledLastTimeUpdated = millis();
|
||||||
|
if (updateLastTimePageChange) oledLastTimePageChange = oledLastTimeUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Page 1: Overall brightness and LED outputs
|
||||||
|
* Page 2: General info like temp, humidity and others
|
||||||
|
* Page 3: Network info
|
||||||
|
*/
|
||||||
|
void updateOledDisplay()
|
||||||
|
{
|
||||||
|
if (!isOledReady()) return;
|
||||||
|
|
||||||
|
oledDisplay->firstPage();
|
||||||
|
do {
|
||||||
|
oledDisplay->setFont(u8g2_font_chroma48medium8_8r);
|
||||||
|
oledDisplay->drawStr(0, 8, serverDescription);
|
||||||
|
oledDisplay->drawHLine(0, 13, 127);
|
||||||
|
oledDisplay->setFont(u8g2_font_6x10_tf);
|
||||||
|
|
||||||
|
byte charPerRow = 21;
|
||||||
|
byte oledRow = 23;
|
||||||
|
switch (oledCurrentPage) {
|
||||||
|
// LED Outputs
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
char charCurrentBrightness[charPerRow+1] = "Brightness:";
|
||||||
|
if (oledUseProgressBars) {
|
||||||
|
oledDisplay->drawStr(0, oledRow, charCurrentBrightness);
|
||||||
|
// There is no method to draw a filled box with rounded corners. So draw the rounded frame first, then fill that frame accordingly to LED percentage
|
||||||
|
oledDisplay->drawRFrame(68, oledRow - 6, 60, 7, 2);
|
||||||
|
oledDisplay->drawBox(69, oledRow - 5, int(round(58*getPercentageForBrightness(bri)) / 100), 5);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(charCurrentBrightness, "%s %d%%", charCurrentBrightness, getPercentageForBrightness(bri));
|
||||||
|
oledDisplay->drawStr(0, oledRow, charCurrentBrightness);
|
||||||
|
}
|
||||||
|
oledRow += 8;
|
||||||
|
|
||||||
|
byte drawnLines = 0;
|
||||||
|
for (int8_t app = 0; app <= 4; app++) {
|
||||||
|
for (int8_t clp = 0; clp <= 4; clp++) {
|
||||||
|
if (anPentaPins[app] == currentLedPins[clp]) {
|
||||||
|
char charCurrentLedcReads[17];
|
||||||
|
sprintf(charCurrentLedcReads, "LED %d:", app+1);
|
||||||
|
if (oledUseProgressBars) {
|
||||||
|
oledDisplay->drawStr(0, oledRow+(drawnLines*8), charCurrentLedcReads);
|
||||||
|
oledDisplay->drawRFrame(38, oledRow - 6 + (drawnLines * 8), 90, 7, 2);
|
||||||
|
oledDisplay->drawBox(39, oledRow - 5 + (drawnLines * 8), int(round(88*getPercentageForBrightness(currentLedcReads[clp])) / 100), 5);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(charCurrentLedcReads, "%s %d%%", charCurrentLedcReads, getPercentageForBrightness(currentLedcReads[clp]));
|
||||||
|
oledDisplay->drawStr(0, oledRow+(drawnLines*8), charCurrentLedcReads);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawnLines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Various info
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
if (isShtReady() && shtReadDataSuccess) {
|
||||||
|
char charShtCurrentTemp[charPerRow+4]; // Reserve 3 more bytes than usual as we gonna have one UTF8 char which can be up to 4 bytes.
|
||||||
|
sprintf(charShtCurrentTemp, "Temperature: %.02f°C", shtCurrentTemp);
|
||||||
|
char charShtCurrentHumidity[charPerRow+1];
|
||||||
|
sprintf(charShtCurrentHumidity, "Humidity: %.02f RH", shtCurrentHumidity);
|
||||||
|
|
||||||
|
oledDisplay->drawUTF8(0, oledRow, charShtCurrentTemp);
|
||||||
|
oledDisplay->drawStr(0, oledRow + 10, charShtCurrentHumidity);
|
||||||
|
oledRow += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mqttEnabled && mqttServer[0] != 0) {
|
||||||
|
char charMqttStatus[charPerRow+1];
|
||||||
|
sprintf(charMqttStatus, "MQTT: %s", (WLED_MQTT_CONNECTED ? "Connected" : "Disconnected"));
|
||||||
|
oledDisplay->drawStr(0, oledRow, charMqttStatus);
|
||||||
|
oledRow += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always draw these two on the bottom
|
||||||
|
char charUptime[charPerRow+1];
|
||||||
|
sprintf(charUptime, "Uptime: %ds", int(millis()/1000 + rolloverMillis*4294967)); // From json.cpp
|
||||||
|
oledDisplay->drawStr(0, 53, charUptime);
|
||||||
|
|
||||||
|
char charWledVersion[charPerRow+1];
|
||||||
|
sprintf(charWledVersion, "WLED v%s", versionString);
|
||||||
|
oledDisplay->drawStr(0, 63, charWledVersion);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network Info
|
||||||
|
case 3:
|
||||||
|
#ifdef WLED_USE_ETHERNET
|
||||||
|
if (lastKnownEthType == WLED_ETH_NONE) {
|
||||||
|
oledDisplay->drawStr(0, oledRow, "Ethernet: No board selected");
|
||||||
|
oledRow += 10;
|
||||||
|
}
|
||||||
|
else if (!lastKnownEthLinkUp) {
|
||||||
|
oledDisplay->drawStr(0, oledRow, "Ethernet: Link Down");
|
||||||
|
oledRow += 10;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (lastKnownNetworkConnected) {
|
||||||
|
#ifdef WLED_USE_ETHERNET
|
||||||
|
if (lastKnownEthLinkUp) {
|
||||||
|
oledDisplay->drawStr(0, oledRow, "Ethernet: Link Up");
|
||||||
|
oledRow += 10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
// Wi-Fi can be active with ETH being connected, but we don't mind...
|
||||||
|
if (lastKnownWiFiConnected) {
|
||||||
|
#ifdef WLED_USE_ETHERNET
|
||||||
|
if (!lastKnownEthLinkUp) {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
oledDisplay->drawStr(0, oledRow, "Wi-Fi: Connected");
|
||||||
|
char currentSsidChar[lastKnownSsid.length() + 1];
|
||||||
|
lastKnownSsid.toCharArray(currentSsidChar, lastKnownSsid.length() + 1);
|
||||||
|
char charCurrentSsid[50];
|
||||||
|
sprintf(charCurrentSsid, "SSID: %s", currentSsidChar);
|
||||||
|
oledDisplay->drawStr(0, oledRow + 10, charCurrentSsid);
|
||||||
|
oledRow += 20;
|
||||||
|
|
||||||
|
#ifdef WLED_USE_ETHERNET
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
String currentIpStr = lastKnownIp.toString();
|
||||||
|
char currentIpChar[currentIpStr.length() + 1];
|
||||||
|
currentIpStr.toCharArray(currentIpChar, currentIpStr.length() + 1);
|
||||||
|
char charCurrentIp[30];
|
||||||
|
sprintf(charCurrentIp, "IP: %s", currentIpChar);
|
||||||
|
oledDisplay->drawStr(0, oledRow, charCurrentIp);
|
||||||
|
}
|
||||||
|
// If WLED AP is active. Theoretically, it can even be active with ETH being connected, but we don't mind...
|
||||||
|
else if (lastKnownApActive) {
|
||||||
|
char charCurrentApStatus[charPerRow+1];
|
||||||
|
sprintf(charCurrentApStatus, "WLED AP: %s (Ch: %d)", (lastKnownApActive ? "On" : "Off"), lastKnownApChannel);
|
||||||
|
oledDisplay->drawStr(0, oledRow, charCurrentApStatus);
|
||||||
|
|
||||||
|
char charCurrentApSsid[charPerRow+1];
|
||||||
|
sprintf(charCurrentApSsid, "SSID: %s", lastKnownApSsid);
|
||||||
|
oledDisplay->drawStr(0, oledRow + 10, charCurrentApSsid);
|
||||||
|
|
||||||
|
char charCurrentApPass[charPerRow+1];
|
||||||
|
sprintf(charCurrentApPass, "PW: %s", lastKnownApPass);
|
||||||
|
oledDisplay->drawStr(0, oledRow + 20, charCurrentApPass);
|
||||||
|
|
||||||
|
// IP is hardcoded / no var exists in WLED at the time this mod was coded, so also hardcode it here
|
||||||
|
oledDisplay->drawStr(0, oledRow + 30, "IP: 4.3.2.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (oledDisplay->nextPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isShtReady()
|
||||||
|
{
|
||||||
|
return shtEnabled && shtInitDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
|
static const char _name[];
|
||||||
|
static const char _enabled[];
|
||||||
|
static const char _oledEnabled[];
|
||||||
|
static const char _oledUseProgressBars[];
|
||||||
|
static const char _oledFlipScreen[];
|
||||||
|
static const char _oledSecondsPerPage[];
|
||||||
|
static const char _oledFixBuggedScreen[];
|
||||||
|
static const char _shtEnabled[];
|
||||||
|
static const unsigned char quinLedLogo[];
|
||||||
|
|
||||||
|
|
||||||
|
static int8_t getPercentageForBrightness(byte brightness)
|
||||||
|
{
|
||||||
|
return int(((float)brightness / (float)255) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
if (enabled) {
|
||||||
|
lastKnownBri = bri;
|
||||||
|
|
||||||
|
if (oledEnabled) {
|
||||||
|
initOledDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shtEnabled) {
|
||||||
|
initSht30TempHumiditySensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentUsedLedPins();
|
||||||
|
|
||||||
|
initDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstRunDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
if (!enabled || !initDone || strip.isUpdating()) return;
|
||||||
|
|
||||||
|
if (isShtReady()) {
|
||||||
|
if (millis() - shtLastTimeUpdated > 30000 && !shtDataRequested) {
|
||||||
|
sht30TempHumidSensor->requestData();
|
||||||
|
shtDataRequested = true;
|
||||||
|
|
||||||
|
shtLastTimeUpdated = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shtDataRequested) {
|
||||||
|
if (sht30TempHumidSensor->dataReady()) {
|
||||||
|
if (sht30TempHumidSensor->readData()) {
|
||||||
|
shtCurrentTemp = sht30TempHumidSensor->getTemperature();
|
||||||
|
shtCurrentHumidity = sht30TempHumidSensor->getHumidity();
|
||||||
|
shtReadDataSuccess = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
shtReadDataSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shtDataRequested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOledReady() && millis() - oledLogoDrawn > 3000) {
|
||||||
|
// Check for changes on the current page and update the OLED if a change is detected
|
||||||
|
if (millis() - oledLastTimeUpdated > 150) {
|
||||||
|
// If there was a network change, force page 3 (network page)
|
||||||
|
if (oledCheckForNetworkChanges()) {
|
||||||
|
oledCurrentPage = 3;
|
||||||
|
}
|
||||||
|
// Only redraw a page if there was a change for that page
|
||||||
|
switch (oledCurrentPage) {
|
||||||
|
case 1:
|
||||||
|
lastKnownBri = bri;
|
||||||
|
// Probably causes lag to always do ledcRead(), so rather re-do the math, 'cause we can't easily get it...
|
||||||
|
getCurrentLedcValues();
|
||||||
|
|
||||||
|
if (bri != lastKnownBri || lastKnownLedcReads[0] != currentLedcReads[0] || lastKnownLedcReads[1] != currentLedcReads[1] || lastKnownLedcReads[2] != currentLedcReads[2]
|
||||||
|
|| lastKnownLedcReads[3] != currentLedcReads[3] || lastKnownLedcReads[4] != currentLedcReads[4]) {
|
||||||
|
lastKnownLedcReads[0] = currentLedcReads[0]; lastKnownLedcReads[1] = currentLedcReads[1]; lastKnownLedcReads[2] = currentLedcReads[2]; lastKnownLedcReads[3] = currentLedcReads[3]; lastKnownLedcReads[4] = currentLedcReads[4];
|
||||||
|
|
||||||
|
oledShowPage(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if (shtLastKnownTemp != shtCurrentTemp || shtLastKnownHumidity != shtCurrentHumidity) {
|
||||||
|
shtLastKnownTemp = shtCurrentTemp;
|
||||||
|
shtLastKnownHumidity = shtCurrentHumidity;
|
||||||
|
|
||||||
|
oledShowPage(2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if (networkHasChanged) {
|
||||||
|
networkHasChanged = false;
|
||||||
|
|
||||||
|
oledShowPage(3, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Cycle through OLED pages
|
||||||
|
if (millis() - oledLastTimePageChange > oledSecondsPerPage * 1000) {
|
||||||
|
// Periodically fixing a "bugged out" OLED. More details in the ReadMe
|
||||||
|
if (oledFixBuggedScreen && millis() - oledLastTimeFixBuggedScreen > 60000) {
|
||||||
|
oledDisplay->begin();
|
||||||
|
oledLastTimeFixBuggedScreen = millis();
|
||||||
|
}
|
||||||
|
oledShowPage(oledGetNextPage(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToConfig(JsonObject &root)
|
||||||
|
{
|
||||||
|
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||||
|
|
||||||
|
top[FPSTR(_enabled)] = enabled;
|
||||||
|
top[FPSTR(_oledEnabled)] = oledEnabled;
|
||||||
|
top[FPSTR(_oledUseProgressBars)] = oledUseProgressBars;
|
||||||
|
top[FPSTR(_oledFlipScreen)] = oledFlipScreen;
|
||||||
|
top[FPSTR(_oledSecondsPerPage)] = oledSecondsPerPage;
|
||||||
|
top[FPSTR(_oledFixBuggedScreen)] = oledFixBuggedScreen;
|
||||||
|
top[FPSTR(_shtEnabled)] = shtEnabled;
|
||||||
|
|
||||||
|
// Update LED pins on config save
|
||||||
|
getCurrentUsedLedPins();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
JsonObject top = root[FPSTR(_name)];
|
||||||
|
if (top.isNull()) {
|
||||||
|
DEBUG_PRINTF("[%s] No config found. (Using defaults.)\n", _name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oldEnabled = enabled;
|
||||||
|
bool oldOledEnabled = oledEnabled;
|
||||||
|
bool oldOledFlipScreen = oledFlipScreen;
|
||||||
|
bool oldShtEnabled = shtEnabled;
|
||||||
|
|
||||||
|
getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||||
|
getJsonValue(top[FPSTR(_oledEnabled)], oledEnabled);
|
||||||
|
getJsonValue(top[FPSTR(_oledUseProgressBars)], oledUseProgressBars);
|
||||||
|
getJsonValue(top[FPSTR(_oledFlipScreen)], oledFlipScreen);
|
||||||
|
getJsonValue(top[FPSTR(_oledSecondsPerPage)], oledSecondsPerPage);
|
||||||
|
getJsonValue(top[FPSTR(_oledFixBuggedScreen)], oledFixBuggedScreen);
|
||||||
|
getJsonValue(top[FPSTR(_shtEnabled)], shtEnabled);
|
||||||
|
|
||||||
|
// First run: reading from cfg.json, nothing to do here, will be all done in setup()
|
||||||
|
if (!firstRunDone) {
|
||||||
|
DEBUG_PRINTF("[%s] First run, nothing to do\n", _name);
|
||||||
|
}
|
||||||
|
// Check if mod has been en-/disabled
|
||||||
|
else if (enabled != oldEnabled) {
|
||||||
|
enabled ? setup() : cleanup();
|
||||||
|
DEBUG_PRINTF("[%s] Usermod has been en-/disabled\n", _name);
|
||||||
|
}
|
||||||
|
// Config has been changed, so adopt to changes
|
||||||
|
else if (enabled) {
|
||||||
|
if (oldOledEnabled != oledEnabled) {
|
||||||
|
oledEnabled ? initOledDisplay() : cleanupOledDisplay();
|
||||||
|
}
|
||||||
|
else if (oledEnabled && oldOledFlipScreen != oledFlipScreen) {
|
||||||
|
oledDisplay->clear();
|
||||||
|
oledDisplay->setFlipMode(oledFlipScreen);
|
||||||
|
oledShowPage(oledCurrentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldShtEnabled != shtEnabled) {
|
||||||
|
shtEnabled ? initSht30TempHumiditySensor() : cleanupSht30TempHumiditySensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("[%s] Config (re)loaded\n", _name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToJsonInfo(JsonObject& root)
|
||||||
|
{
|
||||||
|
if (!enabled && !isShtReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject user = root["u"];
|
||||||
|
if (user.isNull()) user = root.createNestedObject("u");
|
||||||
|
|
||||||
|
JsonArray jsonTemp = user.createNestedArray("Temperature");
|
||||||
|
JsonArray jsonHumidity = user.createNestedArray("Humidity");
|
||||||
|
|
||||||
|
if (shtLastTimeUpdated == 0 || !shtReadDataSuccess) {
|
||||||
|
jsonTemp.add(0);
|
||||||
|
jsonHumidity.add(0);
|
||||||
|
if (shtLastTimeUpdated == 0) {
|
||||||
|
jsonTemp.add(" Not read yet");
|
||||||
|
jsonHumidity.add(" Not read yet");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jsonTemp.add(" Error");
|
||||||
|
jsonHumidity.add(" Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonHumidity.add(shtCurrentHumidity);
|
||||||
|
jsonHumidity.add(" RH");
|
||||||
|
|
||||||
|
jsonTemp.add(shtCurrentTemp);
|
||||||
|
jsonTemp.add(" °C");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_QUINLED_AN_PENTA;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
|
// Config settings
|
||||||
|
const char QuinLEDAnPentaUsermod::_name[] PROGMEM = "QuinLED-An-Penta";
|
||||||
|
const char QuinLEDAnPentaUsermod::_enabled[] PROGMEM = "Enabled";
|
||||||
|
const char QuinLEDAnPentaUsermod::_oledEnabled[] PROGMEM = "Enable-OLED";
|
||||||
|
const char QuinLEDAnPentaUsermod::_oledUseProgressBars[] PROGMEM = "OLED-Use-Progress-Bars";
|
||||||
|
const char QuinLEDAnPentaUsermod::_oledFlipScreen[] PROGMEM = "OLED-Flip-Screen-180";
|
||||||
|
const char QuinLEDAnPentaUsermod::_oledSecondsPerPage[] PROGMEM = "OLED-Seconds-Per-Page";
|
||||||
|
const char QuinLEDAnPentaUsermod::_oledFixBuggedScreen[] PROGMEM = "OLED-Fix-Bugged-Screen";
|
||||||
|
const char QuinLEDAnPentaUsermod::_shtEnabled[] PROGMEM = "Enable-SHT30-Temp-Humidity-Sensor";
|
||||||
|
// Other strings
|
||||||
|
|
||||||
|
const unsigned char QuinLEDAnPentaUsermod::quinLedLogo[] PROGMEM = {
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFD, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x80, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0x07, 0xFE, 0xFF, 0xFF, 0x0F, 0xFC,
|
||||||
|
0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0x0F, 0xFE,
|
||||||
|
0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0xE3, 0xFF, 0xA5, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x0F, 0xFC, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xE1, 0xFF,
|
||||||
|
0x00, 0xF0, 0xE3, 0xFF, 0x0F, 0xFE, 0x1F, 0xFE, 0xFF, 0xFF, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE3, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0x07, 0xFE, 0x1F, 0xFC,
|
||||||
|
0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0x00, 0xF0, 0x00, 0xFE,
|
||||||
|
0x07, 0xFF, 0x1F, 0xFC, 0xF0, 0xC7, 0x3F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF,
|
||||||
|
0xF1, 0xFF, 0x00, 0xFC, 0x07, 0xFF, 0x1F, 0xFE, 0xF0, 0xC3, 0x1F, 0xFE,
|
||||||
|
0x00, 0xFF, 0xE1, 0xFF, 0xF1, 0xFF, 0x30, 0xF8, 0x07, 0xFF, 0x1F, 0xFE,
|
||||||
|
0xF0, 0xC3, 0x1F, 0xFE, 0x00, 0xFC, 0xC3, 0xFF, 0xE1, 0xFF, 0xF0, 0xF0,
|
||||||
|
0x03, 0xFF, 0x0F, 0x7E, 0xF0, 0xC3, 0x1F, 0x7E, 0x00, 0xF8, 0xE3, 0xFF,
|
||||||
|
0xE1, 0xFF, 0xF1, 0xF1, 0x83, 0xFF, 0x0F, 0x7E, 0xF0, 0xC3, 0x1F, 0x7E,
|
||||||
|
0x00, 0xF0, 0xC3, 0xFF, 0xE1, 0xFF, 0xF1, 0xE1, 0x83, 0xFF, 0x0F, 0xFE,
|
||||||
|
0xF0, 0xC3, 0x1F, 0xFE, 0xF8, 0xF0, 0xC3, 0xFF, 0xA1, 0xFF, 0xF1, 0xE3,
|
||||||
|
0x81, 0xFF, 0x0F, 0x7E, 0xF0, 0xC1, 0x1F, 0x7E, 0xF0, 0xF0, 0xC3, 0xFF,
|
||||||
|
0x01, 0xF8, 0xE1, 0xC3, 0x83, 0xFF, 0x0F, 0x7F, 0xF8, 0xC3, 0x1F, 0x7E,
|
||||||
|
0xF8, 0xF0, 0xC3, 0xFF, 0x03, 0xF8, 0xE1, 0xC7, 0x81, 0xE4, 0x0F, 0x7F,
|
||||||
|
0xF0, 0xC3, 0x1F, 0xFE, 0xF8, 0xF0, 0xC3, 0xFF, 0x01, 0xF8, 0xE3, 0xC7,
|
||||||
|
0x01, 0xC0, 0x07, 0x7F, 0xF8, 0xC1, 0x1F, 0x7E, 0xF0, 0xE1, 0xC3, 0xFF,
|
||||||
|
0xC3, 0xFD, 0xE1, 0x87, 0x01, 0x00, 0x07, 0x7F, 0xF8, 0xC3, 0x1F, 0x7E,
|
||||||
|
0xF8, 0xF0, 0xC3, 0xFF, 0xE3, 0xFF, 0xE3, 0x87, 0x01, 0x00, 0x82, 0x3F,
|
||||||
|
0xF8, 0xE1, 0x1F, 0xFE, 0xF8, 0xE1, 0xC3, 0xFF, 0xC3, 0xFF, 0xC3, 0x87,
|
||||||
|
0x01, 0x00, 0x80, 0x3F, 0xF8, 0xC1, 0x1F, 0x7E, 0xF0, 0xF1, 0xC3, 0xFF,
|
||||||
|
0xC3, 0xFF, 0xC3, 0x87, 0x03, 0x0F, 0x80, 0x3F, 0xF8, 0xE1, 0x0F, 0x7E,
|
||||||
|
0xF8, 0xE1, 0x87, 0xFF, 0xC3, 0xFF, 0xC7, 0x87, 0x03, 0x04, 0xC0, 0x7F,
|
||||||
|
0xF0, 0xE1, 0x0F, 0xFF, 0xF8, 0xF1, 0x87, 0xFF, 0xC3, 0xFF, 0xC3, 0x87,
|
||||||
|
0x07, 0x00, 0xE0, 0x7F, 0x00, 0xE0, 0x1F, 0x7E, 0xF0, 0xE0, 0xC3, 0xFF,
|
||||||
|
0xC7, 0xFF, 0x87, 0x87, 0x0F, 0x00, 0xE0, 0x7F, 0x00, 0xE0, 0x0F, 0x7F,
|
||||||
|
0xF8, 0xE1, 0x07, 0x80, 0x07, 0xEA, 0x87, 0xC1, 0x0F, 0x00, 0x80, 0xFF,
|
||||||
|
0x00, 0xE0, 0x1F, 0x7E, 0xF0, 0xE1, 0x07, 0x00, 0x03, 0x80, 0x07, 0xC0,
|
||||||
|
0x7F, 0x00, 0x00, 0xFF, 0x01, 0xE0, 0x1F, 0xFF, 0xF8, 0xE1, 0x07, 0x00,
|
||||||
|
0x07, 0x00, 0x07, 0xE0, 0xFF, 0xF7, 0x01, 0xFF, 0x57, 0xF7, 0x9F, 0xFF,
|
||||||
|
0xFC, 0xF1, 0x0F, 0x00, 0x07, 0x80, 0x0F, 0xE0, 0xFF, 0xFF, 0x03, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xBF, 0xFE,
|
||||||
|
0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
};
|
69
usermods/quinled-an-penta/readme.md
Normal file
69
usermods/quinled-an-penta/readme.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# QuinLED-An-Penta
|
||||||
|
The (un)official usermod to get the best out of the QuinLED-An-Penta, like using the OLED and the SHT30 temperature/humidity sensor.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
* "u8gs" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2
|
||||||
|
* "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85
|
||||||
|
|
||||||
|
## Usermod installation
|
||||||
|
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D QUINLED_AN_PENTA`.
|
||||||
|
|
||||||
|
ESP32 (**without** ethernet):
|
||||||
|
```
|
||||||
|
[env:custom_esp32dev_usermod_quinled_an_penta]
|
||||||
|
extends = env:esp32dev
|
||||||
|
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D QUINLED_AN_PENTA
|
||||||
|
lib_deps = ${esp32.lib_deps}
|
||||||
|
olikraus/U8g2@~2.28.8
|
||||||
|
robtillaart/SHT85@~0.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
ESP32 (**with** ethernet):
|
||||||
|
```
|
||||||
|
[env:custom_esp32dev_usermod_quinled_an_penta]
|
||||||
|
extends = env:esp32dev
|
||||||
|
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D WLED_USE_ETHERNET -D QUINLED_AN_PENTA
|
||||||
|
lib_deps = ${esp32.lib_deps}
|
||||||
|
olikraus/U8g2@~2.28.8
|
||||||
|
robtillaart/SHT85@~0.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Some words about the (optional) OLED
|
||||||
|
This mod has been optimized for an SSD1306 driven 128x64 OLED. Using a smaller OLED or an OLED using a different driver will result in unexpected results.
|
||||||
|
I highly recommend using these "two color monochromatic OLEDs", which have the first 16 pixels in a different color than the other 48, e.g. a yellow/blue OLED.
|
||||||
|
Also note, you need to have an **SPI** driven OLED, **not i2c**!
|
||||||
|
|
||||||
|
### My OLED flickers after some time, what should I do?
|
||||||
|
That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose it's settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display.
|
||||||
|
If you're facing this issue, you can enable a setting I've added which will call the `begin()` roughly every 60 seconds between a page change. This will make the page change take ~500ms, but will fix the display.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D QUINLED_AN_PENTA`, you will see the config for it there:
|
||||||
|
* Enable-OLED:
|
||||||
|
* What it does: Enabled the optional SPI driven OLED that can be mounted to the 7-pin female header
|
||||||
|
* Possible values: Enabled/Disabled
|
||||||
|
* Default: Disabled
|
||||||
|
* OLED-Use-Progress-Bars:
|
||||||
|
* What it does: Toggle between showing percentage numbers or a progress-bar-like visualization for overall brightness and each LED channels brightness level
|
||||||
|
* Possible values: Enabled/Disabled
|
||||||
|
* Default: Disabled
|
||||||
|
* OLED-Flip-Screen-180:
|
||||||
|
* What it does: Flips the screen 180° / upside-down
|
||||||
|
* Possible values: Enabled/Disabled
|
||||||
|
* Default: Disabled
|
||||||
|
* OLED-Seconds-Per-Page:
|
||||||
|
* What it does: Defines how long the OLED should stay on one page in seconds before changing to the next
|
||||||
|
* Possible values: Enabled/Disabled
|
||||||
|
* Default: 10
|
||||||
|
* OLED-Fix-Bugged-Screen:
|
||||||
|
* What it does: Enable this if your OLED flickers after some time. For more info read above under ["My OLED flickers after some time, what should I do?"](#My-OLED-flickers-after-some-time-what-should-I-do)
|
||||||
|
* Possible values: Enabled/Disabled
|
||||||
|
* Default: Disabled
|
||||||
|
* Enable-SHT30-Temp-Humidity-Sensor:
|
||||||
|
* What it does: Enabled the onboard SHT30 temperature and humidity sensor
|
||||||
|
* Possible values: Enabled/Disabled
|
||||||
|
* Default: Disabled
|
||||||
|
|
||||||
|
## Change log
|
||||||
|
2021-10
|
||||||
|
* First implementation.
|
@ -108,7 +108,6 @@ void WS2812FX::finalizeInit(void)
|
|||||||
if (pins[0] == 3) bd->reinit();
|
if (pins[0] == 3) bd->reinit();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
ledCount = _length;
|
|
||||||
|
|
||||||
//segments are created in makeAutoSegments();
|
//segments are created in makeAutoSegments();
|
||||||
|
|
||||||
@ -636,10 +635,9 @@ void WS2812FX::resetSegments() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WS2812FX::makeAutoSegments() {
|
void WS2812FX::makeAutoSegments() {
|
||||||
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
|
|
||||||
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
|
|
||||||
|
|
||||||
if (autoSegments) { //make one segment per bus
|
if (autoSegments) { //make one segment per bus
|
||||||
|
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
|
||||||
|
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
|
||||||
uint8_t s = 0;
|
uint8_t s = 0;
|
||||||
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
|
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
|
||||||
Bus* b = busses.getBus(i);
|
Bus* b = busses.getBus(i);
|
||||||
|
@ -601,9 +601,10 @@ class BusManager {
|
|||||||
return numBusses;
|
return numBusses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
|
||||||
uint16_t getTotalLength() {
|
uint16_t getTotalLength() {
|
||||||
uint16_t len = 0;
|
uint16_t len = 0;
|
||||||
for (uint8_t i=0; i<numBusses; i++ ) len += busses[i]->getLength();
|
for (uint8_t i=0; i<numBusses; i++) len += busses[i]->getLength();
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,15 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
||||||
|
#define WLED_LONG_PRESS 600 //long press if button is released after held for at least 600ms
|
||||||
|
#define WLED_DOUBLE_PRESS 350 //double press if another press within 350ms after a short press
|
||||||
|
#define WLED_LONG_REPEATED_ACTION 300 //how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
|
||||||
|
#define WLED_LONG_AP 6000 //how long the button needs to be held to activate WLED-AP
|
||||||
|
|
||||||
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
|
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
|
||||||
|
|
||||||
void shortPressAction(uint8_t b)
|
void shortPressAction(uint8_t b)
|
||||||
{
|
{
|
||||||
if (!macroButton[b])
|
if (!macroButton[b]) {
|
||||||
{
|
switch (b) {
|
||||||
toggleOnOff();
|
case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||||
colorUpdated(CALL_MODE_BUTTON);
|
default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
applyPreset(macroButton[b], CALL_MODE_BUTTON);
|
applyPreset(macroButton[b], CALL_MODE_BUTTON);
|
||||||
}
|
}
|
||||||
@ -26,6 +31,44 @@ void shortPressAction(uint8_t b)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void longPressAction(uint8_t b)
|
||||||
|
{
|
||||||
|
if (!macroLongPress[b]) {
|
||||||
|
switch (b) {
|
||||||
|
case 0: _setRandomColor(false,true); break;
|
||||||
|
default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish MQTT message
|
||||||
|
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||||
|
char subuf[64];
|
||||||
|
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||||
|
mqtt->publish(subuf, 0, false, "long");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doublePressAction(uint8_t b)
|
||||||
|
{
|
||||||
|
if (!macroDoublePress[b]) {
|
||||||
|
switch (b) {
|
||||||
|
//case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break; //instant short press on button 0 if no macro set
|
||||||
|
default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish MQTT message
|
||||||
|
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||||
|
char subuf[64];
|
||||||
|
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||||
|
mqtt->publish(subuf, 0, false, "double");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isButtonPressed(uint8_t i)
|
bool isButtonPressed(uint8_t i)
|
||||||
{
|
{
|
||||||
if (btnPin[i]<0) return false;
|
if (btnPin[i]<0) return false;
|
||||||
@ -175,6 +218,8 @@ void handleButton()
|
|||||||
if (btnPin[b]<0 || buttonType[b] == BTN_TYPE_NONE) continue;
|
if (btnPin[b]<0 || buttonType[b] == BTN_TYPE_NONE) continue;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (usermods.handleButton(b)) continue; // did usermod handle buttons
|
||||||
|
|
||||||
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && millis() - lastRead > 250) { // button is not a button but a potentiometer
|
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && millis() - lastRead > 250) { // button is not a button but a potentiometer
|
||||||
if (b+1 == WLED_MAX_BUTTONS) lastRead = millis();
|
if (b+1 == WLED_MAX_BUTTONS) lastRead = millis();
|
||||||
handleAnalog(b); continue;
|
handleAnalog(b); continue;
|
||||||
@ -186,61 +231,46 @@ void handleButton()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//momentary button logic
|
//momentary button logic
|
||||||
if (isButtonPressed(b)) //pressed
|
if (isButtonPressed(b)) { //pressed
|
||||||
{
|
|
||||||
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
|
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
|
||||||
buttonPressedBefore[b] = true;
|
buttonPressedBefore[b] = true;
|
||||||
|
|
||||||
if (millis() - buttonPressedTime[b] > 600) //long press
|
if (millis() - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
|
||||||
{
|
if (!buttonLongPressed[b]) longPressAction(b);
|
||||||
if (!buttonLongPressed[b])
|
else if (b) { //repeatable action (~3 times per s) on button > 0
|
||||||
{
|
longPressAction(b);
|
||||||
if (macroLongPress[b]) {applyPreset(macroLongPress[b], CALL_MODE_BUTTON);}
|
buttonPressedTime[b] = millis() - WLED_LONG_REPEATED_ACTION; //300ms
|
||||||
else _setRandomColor(false,true);
|
|
||||||
|
|
||||||
// publish MQTT message
|
|
||||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
|
||||||
char subuf[64];
|
|
||||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
|
||||||
mqtt->publish(subuf, 0, false, "long");
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonLongPressed[b] = true;
|
|
||||||
}
|
}
|
||||||
|
buttonLongPressed[b] = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released
|
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
|
||||||
{
|
|
||||||
long dur = millis() - buttonPressedTime[b];
|
long dur = millis() - buttonPressedTime[b];
|
||||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
|
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
|
||||||
bool doublePress = buttonWaitTime[b];
|
bool doublePress = buttonWaitTime[b]; //did we have a short press before?
|
||||||
buttonWaitTime[b] = 0;
|
buttonWaitTime[b] = 0;
|
||||||
|
|
||||||
if (dur > 6000 && b==0) //long press on button 0
|
if (b == 0 && dur > WLED_LONG_AP) { //long press on button 0 (when released)
|
||||||
{
|
|
||||||
WLED::instance().initAP(true);
|
WLED::instance().initAP(true);
|
||||||
}
|
} else if (!buttonLongPressed[b]) { //short press
|
||||||
else if (!buttonLongPressed[b]) { //short press
|
if (b == 0 && !macroDoublePress[b]) { //don't wait for double press on button 0 if no double press macro set
|
||||||
if (macroDoublePress[b])
|
shortPressAction(b);
|
||||||
{
|
} else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0)
|
||||||
if (doublePress) {
|
if (doublePress) {
|
||||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
|
doublePressAction(b);
|
||||||
|
} else {
|
||||||
// publish MQTT message
|
buttonWaitTime[b] = millis();
|
||||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
}
|
||||||
char subuf[64];
|
}
|
||||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
|
||||||
mqtt->publish(subuf, 0, false, "double");
|
|
||||||
}
|
|
||||||
} else buttonWaitTime[b] = millis();
|
|
||||||
} else shortPressAction(b);
|
|
||||||
}
|
}
|
||||||
buttonPressedBefore[b] = false;
|
buttonPressedBefore[b] = false;
|
||||||
buttonLongPressed[b] = false;
|
buttonLongPressed[b] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 450 && !buttonPressedBefore[b])
|
//if 350ms elapsed since last short press release it is a short press
|
||||||
{
|
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) {
|
||||||
buttonWaitTime[b] = 0;
|
buttonWaitTime[b] = 0;
|
||||||
shortPressAction(b);
|
shortPressAction(b);
|
||||||
}
|
}
|
||||||
|
@ -77,9 +77,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
// initialize LED pins and lengths prior to other HW (except for ethernet)
|
// initialize LED pins and lengths prior to other HW (except for ethernet)
|
||||||
JsonObject hw_led = hw[F("led")];
|
JsonObject hw_led = hw[F("led")];
|
||||||
|
|
||||||
CJSON(ledCount, hw_led[F("total")]);
|
|
||||||
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
|
|
||||||
|
|
||||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||||
uint8_t rgbwMode = hw_led[F("rgbwm")] | RGBW_MODE_DUAL; // use global setting (legacy)
|
uint8_t rgbwMode = hw_led[F("rgbwm")] | RGBW_MODE_DUAL; // use global setting (legacy)
|
||||||
@ -87,8 +84,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
|
|
||||||
JsonArray ins = hw_led["ins"];
|
JsonArray ins = hw_led["ins"];
|
||||||
|
|
||||||
uint16_t lC = 0;
|
|
||||||
|
|
||||||
if (fromFS || !ins.isNull()) {
|
if (fromFS || !ins.isNull()) {
|
||||||
uint8_t s = 0; // bus iterator
|
uint8_t s = 0; // bus iterator
|
||||||
busses.removeAll();
|
busses.removeAll();
|
||||||
@ -116,15 +111,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
bool refresh = elm["ref"] | false;
|
bool refresh = elm["ref"] | false;
|
||||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||||
s++;
|
s++;
|
||||||
uint16_t busEnd = start + length;
|
|
||||||
if (busEnd > lC) lC = busEnd;
|
|
||||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, awMode);
|
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, awMode);
|
||||||
mem += BusManager::memUsage(bc);
|
mem += BusManager::memUsage(bc);
|
||||||
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
|
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
|
||||||
}
|
}
|
||||||
// finalization done in beginStrip()
|
// finalization done in beginStrip()
|
||||||
}
|
}
|
||||||
if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup)
|
|
||||||
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
||||||
|
|
||||||
// read multiple button configuration
|
// read multiple button configuration
|
||||||
@ -527,7 +519,7 @@ void serializeConfig() {
|
|||||||
JsonObject hw = doc.createNestedObject("hw");
|
JsonObject hw = doc.createNestedObject("hw");
|
||||||
|
|
||||||
JsonObject hw_led = hw.createNestedObject("led");
|
JsonObject hw_led = hw.createNestedObject("led");
|
||||||
hw_led[F("total")] = ledCount;
|
hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade
|
||||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||||
hw_led["cct"] = allowCCT;
|
hw_led["cct"] = allowCCT;
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
#define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h"
|
#define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h"
|
||||||
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
|
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
|
||||||
#define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h"
|
#define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h"
|
||||||
|
#define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h"
|
||||||
|
|
||||||
//Access point behavior
|
//Access point behavior
|
||||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||||
|
@ -130,6 +130,7 @@
|
|||||||
<option value="18">HST (Hawaii)</option>
|
<option value="18">HST (Hawaii)</option>
|
||||||
<option value="19">NOVT (Novosibirsk)</option>
|
<option value="19">NOVT (Novosibirsk)</option>
|
||||||
<option value="20">AKST/AKDT (Anchorage)</option>
|
<option value="20">AKST/AKDT (Anchorage)</option>
|
||||||
|
<option value="21">MX-CST/CDT</option>
|
||||||
</select><br>
|
</select><br>
|
||||||
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
|
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
|
||||||
Current local time is <span class="times">unknown</span>.<br>
|
Current local time is <span class="times">unknown</span>.<br>
|
||||||
|
@ -18,7 +18,8 @@ void handleDMX()
|
|||||||
|
|
||||||
uint8_t brightness = strip.getBrightness();
|
uint8_t brightness = strip.getBrightness();
|
||||||
|
|
||||||
for (int i = DMXStartLED; i < ledCount; i++) { // uses the amount of LEDs as fixture count
|
uint16_t len = strip.getLengthTotal();
|
||||||
|
for (int i = DMXStartLED; i < len; i++) { // uses the amount of LEDs as fixture count
|
||||||
|
|
||||||
uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462
|
uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462
|
||||||
byte w = W(in);
|
byte w = W(in);
|
||||||
|
@ -34,9 +34,11 @@ void handleDDPPacket(e131_packet_t* p) {
|
|||||||
|
|
||||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
|
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
|
||||||
|
|
||||||
for (uint16_t i = start; i < stop; i++) {
|
if (!realtimeOverride) {
|
||||||
setRealtimePixel(i, data[c], data[c+1], data[c+2], 0);
|
for (uint16_t i = start; i < stop; i++) {
|
||||||
c+=3;
|
setRealtimePixel(i, data[c], data[c+1], data[c+2], 0);
|
||||||
|
c+=3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool push = p->flags & DDP_PUSH_FLAG;
|
bool push = p->flags & DDP_PUSH_FLAG;
|
||||||
@ -102,6 +104,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
|||||||
// update status info
|
// update status info
|
||||||
realtimeIP = clientIP;
|
realtimeIP = clientIP;
|
||||||
byte wChannel = 0;
|
byte wChannel = 0;
|
||||||
|
uint16_t totalLen = strip.getLengthTotal();
|
||||||
|
|
||||||
switch (DMXMode) {
|
switch (DMXMode) {
|
||||||
case DMX_MODE_DISABLED:
|
case DMX_MODE_DISABLED:
|
||||||
@ -114,7 +117,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
|||||||
realtimeLock(realtimeTimeoutMs, mde);
|
realtimeLock(realtimeTimeoutMs, mde);
|
||||||
if (realtimeOverride) return;
|
if (realtimeOverride) return;
|
||||||
wChannel = (dmxChannels-DMXAddress+1 > 3) ? e131_data[DMXAddress+3] : 0;
|
wChannel = (dmxChannels-DMXAddress+1 > 3) ? e131_data[DMXAddress+3] : 0;
|
||||||
for (uint16_t i = 0; i < ledCount; i++)
|
for (uint16_t i = 0; i < totalLen; i++)
|
||||||
setRealtimePixel(i, e131_data[DMXAddress+0], e131_data[DMXAddress+1], e131_data[DMXAddress+2], wChannel);
|
setRealtimePixel(i, e131_data[DMXAddress+0], e131_data[DMXAddress+1], e131_data[DMXAddress+2], wChannel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -129,7 +132,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
|||||||
bri = e131_data[DMXAddress+0];
|
bri = e131_data[DMXAddress+0];
|
||||||
strip.setBrightness(bri);
|
strip.setBrightness(bri);
|
||||||
}
|
}
|
||||||
for (uint16_t i = 0; i < ledCount; i++)
|
for (uint16_t i = 0; i < totalLen; i++)
|
||||||
setRealtimePixel(i, e131_data[DMXAddress+1], e131_data[DMXAddress+2], e131_data[DMXAddress+3], wChannel);
|
setRealtimePixel(i, e131_data[DMXAddress+1], e131_data[DMXAddress+2], e131_data[DMXAddress+3], wChannel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -193,6 +193,7 @@ bool isAsterisksOnly(const char* str, byte maxLen);
|
|||||||
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
|
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
|
||||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
|
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
|
||||||
int getNumVal(const String* req, uint16_t pos);
|
int getNumVal(const String* req, uint16_t pos);
|
||||||
|
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
|
||||||
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
||||||
|
|
||||||
//udp.cpp
|
//udp.cpp
|
||||||
@ -209,6 +210,7 @@ class Usermod {
|
|||||||
public:
|
public:
|
||||||
virtual void loop() {}
|
virtual void loop() {}
|
||||||
virtual void handleOverlayDraw() {}
|
virtual void handleOverlayDraw() {}
|
||||||
|
virtual bool handleButton(uint8_t b) { return false; }
|
||||||
virtual void setup() {}
|
virtual void setup() {}
|
||||||
virtual void connected() {}
|
virtual void connected() {}
|
||||||
virtual void addToJsonState(JsonObject& obj) {}
|
virtual void addToJsonState(JsonObject& obj) {}
|
||||||
@ -229,7 +231,7 @@ class UsermodManager {
|
|||||||
public:
|
public:
|
||||||
void loop();
|
void loop();
|
||||||
void handleOverlayDraw();
|
void handleOverlayDraw();
|
||||||
|
bool handleButton(uint8_t b);
|
||||||
void setup();
|
void setup();
|
||||||
void connected();
|
void connected();
|
||||||
void addToJsonState(JsonObject& obj);
|
void addToJsonState(JsonObject& obj);
|
||||||
|
@ -353,6 +353,7 @@ North Korea</option><option value="14">IST (India)</option><option value="15">
|
|||||||
CA-Saskatchewan</option><option value="16">ACST</option><option value="17">
|
CA-Saskatchewan</option><option value="16">ACST</option><option value="17">
|
||||||
ACST/ACDT</option><option value="18">HST (Hawaii)</option><option value="19">
|
ACST/ACDT</option><option value="18">HST (Hawaii)</option><option value="19">
|
||||||
NOVT (Novosibirsk)</option><option value="20">AKST/AKDT (Anchorage)</option>
|
NOVT (Novosibirsk)</option><option value="20">AKST/AKDT (Anchorage)</option>
|
||||||
|
<option value="21">MX-CST/CDT</option>
|
||||||
</select><br>UTC offset: <input name="UO" type="number" min="-65500"
|
</select><br>UTC offset: <input name="UO" type="number" min="-65500"
|
||||||
max="65500" required> seconds (max. 18 hours)<br>Current local time is <span
|
max="65500" required> seconds (max. 18 hours)<br>Current local time is <span
|
||||||
class="times">unknown</span>.<br>Latitude (N): <input name="LT" type="number"
|
class="times">unknown</span>.<br>Latitude (N): <input name="LT" type="number"
|
||||||
|
@ -6,6 +6,20 @@
|
|||||||
* JSON API (De)serialization
|
* JSON API (De)serialization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255) {
|
||||||
|
if (elem.is<int>()) {
|
||||||
|
*val = elem;
|
||||||
|
return true;
|
||||||
|
} else if (elem.is<const char*>()) {
|
||||||
|
const char* str = elem;
|
||||||
|
size_t len = strnlen(str, 12);
|
||||||
|
if (len == 0 || len > 10) return false;
|
||||||
|
parseNumber(str, val, vmin, vmax);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; //key does not exist
|
||||||
|
}
|
||||||
|
|
||||||
void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||||
{
|
{
|
||||||
byte id = elem["id"] | it;
|
byte id = elem["id"] | it;
|
||||||
@ -62,12 +76,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
|||||||
}
|
}
|
||||||
if (stop > start && seg.offset > len -1) seg.offset = len -1;
|
if (stop > start && seg.offset > len -1) seg.offset = len -1;
|
||||||
|
|
||||||
int segbri = elem["bri"] | -1;
|
byte segbri = 0;
|
||||||
if (segbri == 0) {
|
if (getVal(elem["bri"], &segbri)) {
|
||||||
seg.setOption(SEG_OPTION_ON, 0, id);
|
if (segbri > 0) seg.setOpacity(segbri, id);
|
||||||
} else if (segbri > 0) {
|
seg.setOption(SEG_OPTION_ON, segbri, id);
|
||||||
seg.setOpacity(segbri, id);
|
|
||||||
seg.setOption(SEG_OPTION_ON, 1, id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
|
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
|
||||||
@ -212,7 +224,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
|||||||
strip.applyToAllSelected = false;
|
strip.applyToAllSelected = false;
|
||||||
bool stateResponse = root[F("v")] | false;
|
bool stateResponse = root[F("v")] | false;
|
||||||
|
|
||||||
bri = root["bri"] | bri;
|
getVal(root["bri"], &bri);
|
||||||
|
|
||||||
bool on = root["on"] | (bri > 0);
|
bool on = root["on"] | (bri > 0);
|
||||||
if (!on != !bri) toggleOnOff();
|
if (!on != !bri) toggleOnOff();
|
||||||
@ -321,18 +333,18 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
|||||||
strip.deserializeMap(ledmap);
|
strip.deserializeMap(ledmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ps = root[F("psave")] | -1;
|
byte ps = root[F("psave")];
|
||||||
if (ps > 0) {
|
if (ps > 0) {
|
||||||
savePreset(ps, true, nullptr, root);
|
savePreset(ps, true, nullptr, root);
|
||||||
} else {
|
} else {
|
||||||
ps = root[F("pdel")] | -1; //deletion
|
ps = root[F("pdel")]; //deletion
|
||||||
if (ps > 0) {
|
if (ps > 0) {
|
||||||
deletePreset(ps);
|
deletePreset(ps);
|
||||||
}
|
}
|
||||||
ps = root["ps"] | -1; //load preset (clears state request!)
|
|
||||||
if (ps >= 0) {
|
if (getVal(root["ps"], &presetCycCurr, 1, 5)) { //load preset (clears state request!)
|
||||||
if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually
|
if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually
|
||||||
applyPreset(ps, callMode);
|
applyPreset(presetCycCurr, callMode);
|
||||||
return stateResponse;
|
return stateResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,7 +498,7 @@ void serializeInfo(JsonObject root)
|
|||||||
//root[F("cn")] = WLED_CODENAME;
|
//root[F("cn")] = WLED_CODENAME;
|
||||||
|
|
||||||
JsonObject leds = root.createNestedObject("leds");
|
JsonObject leds = root.createNestedObject("leds");
|
||||||
leds[F("count")] = ledCount;
|
leds[F("count")] = strip.getLengthTotal();
|
||||||
leds[F("rgbw")] = strip.isRgbw;
|
leds[F("rgbw")] = strip.isRgbw;
|
||||||
leds[F("wv")] = false;
|
leds[F("wv")] = false;
|
||||||
leds["cct"] = allowCCT;
|
leds["cct"] = allowCCT;
|
||||||
@ -866,7 +878,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t used = ledCount;
|
uint16_t used = strip.getLengthTotal();
|
||||||
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
|
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
|
||||||
char buffer[2000];
|
char buffer[2000];
|
||||||
strcpy_P(buffer, PSTR("{\"leds\":["));
|
strcpy_P(buffer, PSTR("{\"leds\":["));
|
||||||
|
@ -31,6 +31,7 @@ Timezone* tz;
|
|||||||
#define TZ_HAWAII 18
|
#define TZ_HAWAII 18
|
||||||
#define TZ_NOVOSIBIRSK 19
|
#define TZ_NOVOSIBIRSK 19
|
||||||
#define TZ_ANCHORAGE 20
|
#define TZ_ANCHORAGE 20
|
||||||
|
#define TZ_MX_CENTRAL 21
|
||||||
#define TZ_INIT 255
|
#define TZ_INIT 255
|
||||||
|
|
||||||
byte tzCurrent = TZ_INIT; //uninitialized
|
byte tzCurrent = TZ_INIT; //uninitialized
|
||||||
@ -141,6 +142,11 @@ void updateTimezone() {
|
|||||||
tcrStandard = {First, Sun, Nov, 2, -540}; //AKST = UTC - 9 hours
|
tcrStandard = {First, Sun, Nov, 2, -540}; //AKST = UTC - 9 hours
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TZ_MX_CENTRAL : {
|
||||||
|
tcrDaylight = {First, Sun, Apr, 2, -300}; //CDT = UTC - 5 hours
|
||||||
|
tcrStandard = {Last, Sun, Oct, 2, -360}; //CST = UTC - 6 hours
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tzCurrent = currentTimezone;
|
tzCurrent = currentTimezone;
|
||||||
|
@ -52,7 +52,8 @@ enum struct PinOwner : uint8_t {
|
|||||||
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
|
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
|
||||||
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
||||||
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
||||||
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER // 0x16 // Usermod "rgb-rotary-encoder.h"
|
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
|
||||||
|
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
|
||||||
};
|
};
|
||||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
||||||
|
|
||||||
|
@ -519,21 +519,21 @@ int getNumVal(const String* req, uint16_t pos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//helper to get int value at a position in string
|
//helper to get int value with in/decrementing support via ~ syntax
|
||||||
bool updateVal(const String* req, const char* key, byte* val, byte minv, byte maxv)
|
void parseNumber(const char* str, byte* val, byte minv, byte maxv)
|
||||||
{
|
{
|
||||||
int pos = req->indexOf(key);
|
if (str == nullptr || str[0] == '\0') return;
|
||||||
if (pos < 1) return false;
|
if (str[0] == 'r') {*val = random8(minv,maxv); return;}
|
||||||
|
if (str[0] == '~') {
|
||||||
if (req->charAt(pos+3) == '~') {
|
int out = atoi(str +1);
|
||||||
int out = getNumVal(req, pos+1);
|
|
||||||
if (out == 0)
|
if (out == 0)
|
||||||
{
|
{
|
||||||
// we only have ~ (and perhaps -)
|
if (str[1] == '0') return;
|
||||||
if (req->charAt(pos+4) == '-') {
|
if (str[1] == '-')
|
||||||
*val = (int)(*val -1) < (int)minv ? maxv : min((int)maxv,(*val -1));
|
{
|
||||||
|
*val = (int)(*val -1) < (int)minv ? maxv : min((int)maxv,(*val -1)); //-1, wrap around
|
||||||
} else {
|
} else {
|
||||||
*val = (int)(*val +1) > (int)maxv ? minv : max((int)minv,(*val +1));
|
*val = (int)(*val +1) > (int)maxv ? minv : max((int)minv,(*val +1)); //+1, wrap around
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out += *val;
|
out += *val;
|
||||||
@ -543,8 +543,25 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv, byte ma
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
*val = getNumVal(req, pos);
|
byte p1 = atoi(str);
|
||||||
|
const char* str2 = strchr(str,'~'); //min/max range (for preset cycle, e.g. "1~5~")
|
||||||
|
if (str2) {
|
||||||
|
byte p2 = atoi(str2+1);
|
||||||
|
while (isdigit((str2+1)[0])) str2++;
|
||||||
|
parseNumber(str2+1, val, p1, p2);
|
||||||
|
} else {
|
||||||
|
*val = p1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool updateVal(const String* req, const char* key, byte* val, byte minv, byte maxv)
|
||||||
|
{
|
||||||
|
int pos = req->indexOf(key);
|
||||||
|
if (pos < 1) return false;
|
||||||
|
if (req->length() < (unsigned int)(pos + 4)) return false;
|
||||||
|
parseNumber(req->c_str() + pos +3, val, minv, maxv);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,8 @@ void notify(byte callMode, bool followUp)
|
|||||||
void realtimeLock(uint32_t timeoutMs, byte md)
|
void realtimeLock(uint32_t timeoutMs, byte md)
|
||||||
{
|
{
|
||||||
if (!realtimeMode && !realtimeOverride){
|
if (!realtimeMode && !realtimeOverride){
|
||||||
for (uint16_t i = 0; i < ledCount; i++)
|
uint16_t totalLen = strip.getLengthTotal();
|
||||||
|
for (uint16_t i = 0; i < totalLen; i++)
|
||||||
{
|
{
|
||||||
strip.setPixelColor(i,0,0,0,0);
|
strip.setPixelColor(i,0,0,0,0);
|
||||||
}
|
}
|
||||||
@ -168,10 +169,11 @@ void handleNotifications()
|
|||||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION);
|
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION);
|
||||||
if (realtimeOverride) return;
|
if (realtimeOverride) return;
|
||||||
uint16_t id = 0;
|
uint16_t id = 0;
|
||||||
|
uint16_t totalLen = strip.getLengthTotal();
|
||||||
for (uint16_t i = 0; i < packetSize -2; i += 3)
|
for (uint16_t i = 0; i < packetSize -2; i += 3)
|
||||||
{
|
{
|
||||||
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
|
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
|
||||||
id++; if (id >= ledCount) break;
|
id++; if (id >= totalLen) break;
|
||||||
}
|
}
|
||||||
strip.show();
|
strip.show();
|
||||||
return;
|
return;
|
||||||
@ -339,9 +341,10 @@ void handleNotifications()
|
|||||||
byte numPackets = udpIn[5];
|
byte numPackets = udpIn[5];
|
||||||
|
|
||||||
uint16_t id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
|
uint16_t id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
|
||||||
|
uint16_t totalLen = strip.getLengthTotal();
|
||||||
for (uint16_t i = 6; i < tpmPayloadFrameSize + 4; i += 3)
|
for (uint16_t i = 6; i < tpmPayloadFrameSize + 4; i += 3)
|
||||||
{
|
{
|
||||||
if (id < ledCount)
|
if (id < totalLen)
|
||||||
{
|
{
|
||||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
||||||
id++;
|
id++;
|
||||||
@ -372,6 +375,7 @@ void handleNotifications()
|
|||||||
}
|
}
|
||||||
if (realtimeOverride) return;
|
if (realtimeOverride) return;
|
||||||
|
|
||||||
|
uint16_t totalLen = strip.getLengthTotal();
|
||||||
if (udpIn[0] == 1) //warls
|
if (udpIn[0] == 1) //warls
|
||||||
{
|
{
|
||||||
for (uint16_t i = 2; i < packetSize -3; i += 4)
|
for (uint16_t i = 2; i < packetSize -3; i += 4)
|
||||||
@ -385,7 +389,7 @@ void handleNotifications()
|
|||||||
{
|
{
|
||||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
||||||
|
|
||||||
id++; if (id >= ledCount) break;
|
id++; if (id >= totalLen) break;
|
||||||
}
|
}
|
||||||
} else if (udpIn[0] == 3) //drgbw
|
} else if (udpIn[0] == 3) //drgbw
|
||||||
{
|
{
|
||||||
@ -394,14 +398,14 @@ void handleNotifications()
|
|||||||
{
|
{
|
||||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
|
||||||
|
|
||||||
id++; if (id >= ledCount) break;
|
id++; if (id >= totalLen) break;
|
||||||
}
|
}
|
||||||
} else if (udpIn[0] == 4) //dnrgb
|
} else if (udpIn[0] == 4) //dnrgb
|
||||||
{
|
{
|
||||||
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
||||||
for (uint16_t i = 4; i < packetSize -2; i += 3)
|
for (uint16_t i = 4; i < packetSize -2; i += 3)
|
||||||
{
|
{
|
||||||
if (id >= ledCount) break;
|
if (id >= totalLen) break;
|
||||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
@ -410,7 +414,7 @@ void handleNotifications()
|
|||||||
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
||||||
for (uint16_t i = 4; i < packetSize -2; i += 4)
|
for (uint16_t i = 4; i < packetSize -2; i += 4)
|
||||||
{
|
{
|
||||||
if (id >= ledCount) break;
|
if (id >= totalLen) break;
|
||||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
@ -438,7 +442,7 @@ void handleNotifications()
|
|||||||
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
|
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
|
||||||
{
|
{
|
||||||
uint16_t pix = i + arlsOffset;
|
uint16_t pix = i + arlsOffset;
|
||||||
if (pix < ledCount)
|
if (pix < strip.getLengthTotal())
|
||||||
{
|
{
|
||||||
if (!arlsDisableGammaCorrection && strip.gammaCorrectCol)
|
if (!arlsDisableGammaCorrection && strip.gammaCorrectCol)
|
||||||
{
|
{
|
||||||
@ -479,6 +483,7 @@ void sendSysInfoUDP()
|
|||||||
if (!udp2Connected) return;
|
if (!udp2Connected) return;
|
||||||
|
|
||||||
IPAddress ip = Network.localIP();
|
IPAddress ip = Network.localIP();
|
||||||
|
if (!ip || ip == IPAddress(255,255,255,255)) ip = IPAddress(4,3,2,1);
|
||||||
|
|
||||||
// TODO: make a nice struct of it and clean up
|
// TODO: make a nice struct of it and clean up
|
||||||
// 0: 1 byte 'binary token 255'
|
// 0: 1 byte 'binary token 255'
|
||||||
|
@ -6,6 +6,13 @@
|
|||||||
//Usermod Manager internals
|
//Usermod Manager internals
|
||||||
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); }
|
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); }
|
||||||
void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); }
|
void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); }
|
||||||
|
bool UsermodManager::handleButton(uint8_t b) {
|
||||||
|
bool overrideIO = false;
|
||||||
|
for (byte i = 0; i < numMods; i++) {
|
||||||
|
if (ums[i]->handleButton(b)) overrideIO = true;
|
||||||
|
}
|
||||||
|
return overrideIO;
|
||||||
|
}
|
||||||
|
|
||||||
void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); }
|
void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); }
|
||||||
void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); }
|
void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); }
|
||||||
|
@ -100,6 +100,10 @@
|
|||||||
#include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h"
|
#include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef QUINLED_AN_PENTA
|
||||||
|
#include "../usermods/quinled-an-penta/quinled-an-penta.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
void registerUsermods()
|
void registerUsermods()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -187,4 +191,8 @@ void registerUsermods()
|
|||||||
#ifdef USERMOD_SEVEN_SEGMENT
|
#ifdef USERMOD_SEVEN_SEGMENT
|
||||||
usermods.add(new SevenSegmentDisplay());
|
usermods.add(new SevenSegmentDisplay());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef QUINLED_AN_PENTA
|
||||||
|
usermods.add(new QuinLEDAnPentaUsermod());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -212,13 +212,10 @@ void WLED::loop()
|
|||||||
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
|
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
|
||||||
busses.removeAll();
|
busses.removeAll();
|
||||||
uint32_t mem = 0;
|
uint32_t mem = 0;
|
||||||
ledCount = 1;
|
|
||||||
for (uint8_t i = 0; i < WLED_MAX_BUSSES; i++) {
|
for (uint8_t i = 0; i < WLED_MAX_BUSSES; i++) {
|
||||||
if (busConfigs[i] == nullptr) break;
|
if (busConfigs[i] == nullptr) break;
|
||||||
mem += BusManager::memUsage(*busConfigs[i]);
|
mem += BusManager::memUsage(*busConfigs[i]);
|
||||||
if (mem <= MAX_LED_MEMORY) {
|
if (mem <= MAX_LED_MEMORY) {
|
||||||
uint16_t totalNew = busConfigs[i]->start + busConfigs[i]->count;
|
|
||||||
if (totalNew > ledCount && totalNew <= MAX_LEDS) ledCount = totalNew; //total is end of last bus (where start + len is max.)
|
|
||||||
busses.add(*busConfigs[i]);
|
busses.add(*busConfigs[i]);
|
||||||
}
|
}
|
||||||
delete busConfigs[i]; busConfigs[i] = nullptr;
|
delete busConfigs[i]; busConfigs[i] = nullptr;
|
||||||
|
@ -262,7 +262,6 @@ WLED_GLOBAL bool noWifiSleep _INIT(false);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// LED CONFIG
|
// LED CONFIG
|
||||||
WLED_GLOBAL uint16_t ledCount _INIT(DEFAULT_LED_COUNT); // overcurrent prevented by ABL
|
|
||||||
WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up
|
WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up
|
||||||
WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up
|
WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up
|
||||||
|
|
||||||
|
@ -89,7 +89,16 @@ void loadSettingsFromEEPROM()
|
|||||||
if (apChannel > 13 || apChannel < 1) apChannel = 1;
|
if (apChannel > 13 || apChannel < 1) apChannel = 1;
|
||||||
apHide = EEPROM.read(228);
|
apHide = EEPROM.read(228);
|
||||||
if (apHide > 1) apHide = 1;
|
if (apHide > 1) apHide = 1;
|
||||||
ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30;
|
uint16_t length = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); //was ledCount
|
||||||
|
if (length > MAX_LEDS || length == 0) length = 30;
|
||||||
|
uint8_t pins[5] = {2, 255, 255, 255, 255};
|
||||||
|
uint8_t colorOrder = COL_ORDER_GRB;
|
||||||
|
if (lastEEPROMversion > 9) colorOrder = EEPROM.read(383);
|
||||||
|
if (colorOrder > COL_ORDER_GBR) colorOrder = COL_ORDER_GRB;
|
||||||
|
bool skipFirst = EEPROM.read(2204);
|
||||||
|
bool reversed = EEPROM.read(252);
|
||||||
|
BusConfig bc = BusConfig(EEPROM.read(372) ? TYPE_SK6812_RGBW : TYPE_WS2812_RGB, pins, 0, length, colorOrder, reversed, skipFirst);
|
||||||
|
busses.add(bc);
|
||||||
|
|
||||||
notifyButton = EEPROM.read(230);
|
notifyButton = EEPROM.read(230);
|
||||||
notifyTwice = EEPROM.read(231);
|
notifyTwice = EEPROM.read(231);
|
||||||
@ -143,7 +152,7 @@ void loadSettingsFromEEPROM()
|
|||||||
arlsOffset = EEPROM.read(368);
|
arlsOffset = EEPROM.read(368);
|
||||||
if (!EEPROM.read(367)) arlsOffset = -arlsOffset;
|
if (!EEPROM.read(367)) arlsOffset = -arlsOffset;
|
||||||
turnOnAtBoot = EEPROM.read(369);
|
turnOnAtBoot = EEPROM.read(369);
|
||||||
strip.isRgbw = EEPROM.read(372);
|
//strip.isRgbw = EEPROM.read(372);
|
||||||
//374 - strip.paletteFade
|
//374 - strip.paletteFade
|
||||||
|
|
||||||
apBehavior = EEPROM.read(376);
|
apBehavior = EEPROM.read(376);
|
||||||
|
@ -387,7 +387,7 @@ String dmxProcessor(const String& var)
|
|||||||
mapJS += "\nCN=" + String(DMXChannels) + ";\n";
|
mapJS += "\nCN=" + String(DMXChannels) + ";\n";
|
||||||
mapJS += "CS=" + String(DMXStart) + ";\n";
|
mapJS += "CS=" + String(DMXStart) + ";\n";
|
||||||
mapJS += "CG=" + String(DMXGap) + ";\n";
|
mapJS += "CG=" + String(DMXGap) + ";\n";
|
||||||
mapJS += "LC=" + String(ledCount) + ";\n";
|
mapJS += "LC=" + String(strip.getLengthTotal()) + ";\n";
|
||||||
mapJS += "var CH=[";
|
mapJS += "var CH=[";
|
||||||
for (int i=0;i<15;i++) {
|
for (int i=0;i<15;i++) {
|
||||||
mapJS += String(DMXFixtureMap[i]) + ",";
|
mapJS += String(DMXFixtureMap[i]) + ",";
|
||||||
|
Loading…
Reference in New Issue
Block a user