Multirelay button support. (#2284)
* Multirelay button support. Added button hook for usermods. * Added MultiRelay relay states to JSON state object * Move button timings to constants No delay waiting for double press on button 0 if no macro set Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
This commit is contained in:
parent
00238247cd
commit
a93f05c047
@ -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,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);
|
||||||
}
|
}
|
||||||
|
@ -208,6 +208,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) {}
|
||||||
@ -228,7 +229,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);
|
||||||
|
@ -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(); }
|
||||||
|
Loading…
Reference in New Issue
Block a user