mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6a2798408 | ||
|
|
e2ec04825b | ||
|
|
593da3146f | ||
|
|
24ce8a8ef6 | ||
|
|
5179111408 | ||
|
|
e9fe48c4cb | ||
|
|
a2a7d68594 | ||
|
|
eda236a816 | ||
|
|
d594cf040d |
@@ -108,7 +108,8 @@ Advanced event management
|
||||
|
||||
* **Long press min interval (ms)**: This is the interval that you need to press the button before the long press event is triggered.
|
||||
|
||||
* **Use safe button (slower)**:
|
||||
* **Use safe button (slower)**: If checked, the EVENT of a switch change is created only if the switch remains in the new position for at least 2 cycles of the 10x per second function.
|
||||
This should resolve the cases where there are false positive switch events due to interferences.
|
||||
|
||||
Data acquisition
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -2,6 +2,31 @@ GPIO
|
||||
****
|
||||
|
||||
|
||||
GPIO boot states
|
||||
----------------
|
||||
|
||||
Via the web interface (tab "Hardware"), GPIO pins can be set to some initial state at boot.
|
||||
|
||||
* Default (INPUT)
|
||||
* Output Low
|
||||
* Output High
|
||||
* Input Pull-up
|
||||
|
||||
A GPIO pin used as input may need a pull-up or pull-down resistor to make the observed signal less susceptible to noise.
|
||||
For example cables may act as an antenna and thus pick up noise.
|
||||
If such a cable is not 'terminated' or pulled to a certain level, it may give incorrect readings.
|
||||
|
||||
It is good practice to enable internal (or external) pull-up or pull-down resistors
|
||||
to unused pins to make sure they do not cause undefined or unexpected behavior.
|
||||
|
||||
Some boards already have pull-up or pull-down resistors mounted on the board itself, or present in connected sensors.
|
||||
When already present, there is no need to enable pull-up or pull-down resistors on those input pins.
|
||||
It may even harm stability of the observed signal.
|
||||
|
||||
The value of an internal pull-up resistor is between 30 kOhm and 100 kOhm.
|
||||
|
||||
For ESP8266 see also `ESP8266 Arduino Core - Digital IO <https://arduino-esp8266.readthedocs.io/en/latest/reference.html#digital-io>`_
|
||||
and the notes below.
|
||||
|
||||
Best pins to use on ESP8266
|
||||
---------------------------
|
||||
@@ -108,6 +133,10 @@ Other limitations are:
|
||||
* GPIO16 has a built-in pull-down resistor (all others have built-in pull-up)
|
||||
* To enable the pull-down resistor for GPIO16, you have to use ``INPUT_PULLDOWN_16``
|
||||
|
||||
This pin is disabled to be set as boot state pin, since it can be connected to the RST pin to allow deep sleep.
|
||||
If connected to RST, any toggle to "high" will cause a reset, which makes it
|
||||
impossible to recover from an incorrect configuration.
|
||||
|
||||
|
||||
Best pins to use on ESP32
|
||||
-------------------------
|
||||
|
||||
+34
-39
@@ -114,45 +114,40 @@ String Command_JSONPortStatus(struct EventStruct *event, const char* Line)
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
void createLogPortStatus(std::map<uint32_t,portStatusStruct>::iterator it)
|
||||
{
|
||||
String log = F("PortStatus detail: ");
|
||||
log += F("Port=");
|
||||
log += getPortFromKey(it->first);
|
||||
log += F(" State=");
|
||||
log += it->second.state;
|
||||
log += F(" Output=");
|
||||
log += it->second.output;
|
||||
log += F(" Mode=");
|
||||
log += it->second.mode;
|
||||
log += F(" Task=");
|
||||
log += it->second.task;
|
||||
log += F(" Monitor=");
|
||||
log += it->second.monitor;
|
||||
log += F(" Command=");
|
||||
log += it->second.command;
|
||||
log += F(" Init=");
|
||||
log += it->second.init;
|
||||
log += F(" PreviousTask=");
|
||||
log += it->second.previousTask;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
}
|
||||
|
||||
void debugPortStatus(std::map<uint32_t,portStatusStruct>::iterator it)
|
||||
{
|
||||
createLogPortStatus(it);
|
||||
}
|
||||
|
||||
void logPortStatus(String from) {
|
||||
String log;
|
||||
log=F("PortStatus structure: Called from=");
|
||||
log+=from;
|
||||
log+=F(" Count=");
|
||||
log+=globalMapPortStatus.size();
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
debugPortStatus(it);
|
||||
void logPortStatus(const String& from) {
|
||||
if (!loglevelActiveFor(LOG_LEVEL_INFO)) return;
|
||||
{
|
||||
String log;
|
||||
log.reserve(44 + from.length());
|
||||
log=F("PortStatus structure: Called from=");
|
||||
log+=from;
|
||||
log+=F(" Count=");
|
||||
log+=globalMapPortStatus.size();
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
}
|
||||
for (auto it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
String log;
|
||||
log.reserve(80);
|
||||
log = F("PortStatus detail: ");
|
||||
log += F("Port=");
|
||||
log += getPortFromKey(it->first);
|
||||
log += F(" State=");
|
||||
log += it->second.state;
|
||||
log += F(" Output=");
|
||||
log += it->second.output;
|
||||
log += F(" Mode=");
|
||||
log += it->second.mode;
|
||||
log += F(" Task=");
|
||||
log += it->second.task;
|
||||
log += F(" Monitor=");
|
||||
log += it->second.monitor;
|
||||
log += F(" Command=");
|
||||
log += it->second.command;
|
||||
log += F(" Init=");
|
||||
log += it->second.portstatus_init;
|
||||
log += F(" PreviousTask=");
|
||||
log += it->second.previousTask;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+251
-329
@@ -1,25 +1,23 @@
|
||||
#ifndef COMMAND_GPIO_H
|
||||
#define COMMAND_GPIO_H
|
||||
|
||||
|
||||
#include "ESPEasy-GPIO.h"
|
||||
|
||||
#if defined(ESP8266)
|
||||
Servo servo1;
|
||||
Servo servo2;
|
||||
#endif /* if defined(ESP8266) */
|
||||
|
||||
#define PLUGIN_ID_000 0
|
||||
|
||||
// Forward declarations
|
||||
uint32_t createInternalGpioKey(uint16_t portNumber);
|
||||
bool read_GPIO_state(struct EventStruct *event);
|
||||
bool read_GPIO_state(byte pinNumber,
|
||||
byte pinMode);
|
||||
bool checkValidGpioPin(byte gpio_pin);
|
||||
void analogWriteESP(int pin,
|
||||
int value,
|
||||
unsigned int frequency);
|
||||
String Command_longPulse(struct EventStruct *event,
|
||||
const char *Line,
|
||||
bool time_in_msec);
|
||||
String return_command_failed_invalid_GPIO(byte pinNumber);
|
||||
|
||||
// **************************************************************************/
|
||||
// Command "gpio"
|
||||
@@ -28,41 +26,38 @@ String Command_longPulse(struct EventStruct *event,
|
||||
// **************************************************************************/
|
||||
String Command_GPIO(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
if (event->Par2 == 2) // input PIN
|
||||
{
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
if (event->Par2 == 2) // input PIN
|
||||
{
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_INPUT, 0);
|
||||
pinMode(event->Par1, INPUT_PULLUP);
|
||||
tempStatus.mode = PIN_MODE_INPUT_PULLUP;
|
||||
tempStatus.state = read_GPIO_state(event->Par1, tempStatus.mode);
|
||||
tempStatus.output = tempStatus.state;
|
||||
} else {
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
}
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
|
||||
String log = String(F("GPIO : ")) + String(event->Par1) + String(F(" Set to ")) + String(event->Par2);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_INPUT, 0);
|
||||
pinMode(event->Par1, INPUT_PULLUP);
|
||||
tempStatus.mode = PIN_MODE_INPUT_PULLUP;
|
||||
tempStatus.state = read_GPIO_state(event->Par1, tempStatus.mode);
|
||||
tempStatus.output = tempStatus.state;
|
||||
} else {
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
}
|
||||
return return_command_failed();
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
|
||||
String log = String(F("GPIO : ")) + String(event->Par1) + String(F(" Set to ")) + String(event->Par2);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -72,36 +67,33 @@ String Command_GPIO(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_GPIOtoggle(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
{
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
if ((tempStatus.mode == PIN_MODE_OUTPUT) || (tempStatus.mode == PIN_MODE_UNDEFINED)) { // toggle only output pins
|
||||
tempStatus.state = !(read_GPIO_state(event->Par1, tempStatus.mode)); // toggle current state value
|
||||
tempStatus.output = tempStatus.state;
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the
|
||||
// PinStatus page
|
||||
if ((tempStatus.mode == PIN_MODE_OUTPUT) || (tempStatus.mode == PIN_MODE_UNDEFINED)) { // toggle only output pins
|
||||
tempStatus.state = !(read_GPIO_state(event->Par1, tempStatus.mode)); // toggle current state value
|
||||
tempStatus.output = tempStatus.state;
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the
|
||||
// PinStatus page
|
||||
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, tempStatus.state);
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, tempStatus.state);
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, !currentState);
|
||||
savePortStatus(key, tempStatus);
|
||||
String log = String(F("SW : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(tempStatus.state);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, !currentState);
|
||||
savePortStatus(key, tempStatus);
|
||||
String log = String(F("SW : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(tempStatus.state);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
}
|
||||
return return_command_success();
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
}
|
||||
return return_command_failed();
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -113,71 +105,68 @@ String Command_GPIOtoggle(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_PWM(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
unsigned int frequency = 1000;
|
||||
if (event->Par4 != 0)
|
||||
frequency = event->Par4;
|
||||
|
||||
#if defined(ESP8266)
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
#endif /* if defined(ESP8266) */
|
||||
|
||||
if (event->Par3 != 0)
|
||||
{
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
const byte prev_mode = tempStatus.mode;
|
||||
uint16_t prev_value = tempStatus.state;
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
unsigned int frequency = 1000;
|
||||
if (event->Par4 != 0)
|
||||
frequency = event->Par4;
|
||||
|
||||
#if defined(ESP8266)
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
#endif /* if defined(ESP8266) */
|
||||
|
||||
if (event->Par3 != 0)
|
||||
{
|
||||
const byte prev_mode = tempStatus.mode;
|
||||
uint16_t prev_value = tempStatus.state;
|
||||
|
||||
// getPinState(PLUGIN_ID_000, event->Par1, &prev_mode, &prev_value);
|
||||
if (prev_mode != PIN_MODE_PWM) {
|
||||
prev_value = 0;
|
||||
}
|
||||
|
||||
int32_t step_value = ((event->Par2 - prev_value) << 12) / event->Par3;
|
||||
int32_t curr_value = prev_value << 12;
|
||||
|
||||
int i = event->Par3;
|
||||
|
||||
while (i--) {
|
||||
curr_value += step_value;
|
||||
int16_t new_value;
|
||||
new_value = (uint16_t)(curr_value >> 12);
|
||||
analogWriteESP(event->Par1, new_value, frequency);
|
||||
delay(1);
|
||||
}
|
||||
// getPinState(PLUGIN_ID_000, event->Par1, &prev_mode, &prev_value);
|
||||
if (prev_mode != PIN_MODE_PWM) {
|
||||
prev_value = 0;
|
||||
}
|
||||
analogWriteESP(event->Par1, event->Par2, frequency);
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_PWM, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_PWM;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
int32_t step_value = ((event->Par2 - prev_value) << 12) / event->Par3;
|
||||
int32_t curr_value = prev_value << 12;
|
||||
|
||||
savePortStatus(key, tempStatus);
|
||||
String log = F("GPIO : ");
|
||||
log += event->Par1;
|
||||
log += F(" Set PWM to ");
|
||||
log += event->Par2;
|
||||
int i = event->Par3;
|
||||
|
||||
if (event->Par3 != 0) {
|
||||
log += F(" duration ");
|
||||
log += event->Par3;
|
||||
while (i--) {
|
||||
curr_value += step_value;
|
||||
int16_t new_value;
|
||||
new_value = (uint16_t)(curr_value >> 12);
|
||||
analogWriteESP(event->Par1, new_value, frequency);
|
||||
delay(1);
|
||||
}
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
return return_command_failed();
|
||||
analogWriteESP(event->Par1, event->Par2, frequency);
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_PWM, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_PWM;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
|
||||
savePortStatus(key, tempStatus);
|
||||
String log = F("GPIO : ");
|
||||
log += event->Par1;
|
||||
log += F(" Set PWM to ");
|
||||
log += event->Par2;
|
||||
|
||||
if (event->Par3 != 0) {
|
||||
log += F(" duration ");
|
||||
log += event->Par3;
|
||||
}
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -188,35 +177,32 @@ String Command_PWM(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_Pulse(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
{
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, event->Par2);
|
||||
delay(event->Par3);
|
||||
digitalWrite(event->Par1, !event->Par2);
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, event->Par2);
|
||||
delay(event->Par3);
|
||||
digitalWrite(event->Par1, !event->Par2);
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
|
||||
String log = String(F("GPIO : ")) + String(event->Par1) + String(F(" Pulsed for ")) + String(event->Par3) + String(F(" mS"));
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
String log = String(F("GPIO : ")) + String(event->Par1) + String(F(" Pulsed for ")) + String(event->Par3) + String(F(" mS"));
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
return return_command_failed();
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
|
||||
@@ -244,42 +230,39 @@ String Command_longPulse_msec(struct EventStruct *event, const char *Line)
|
||||
|
||||
String Command_longPulse(struct EventStruct *event, const char *Line, bool time_in_msec)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
{
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
const bool pinStateHigh = event->Par2 != 0;
|
||||
const uint16_t pinStateValue = pinStateHigh ? 1 : 0;
|
||||
const uint16_t inversePinStateValue = pinStateHigh ? 0 : 1;
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, pinStateValue);
|
||||
const bool pinStateHigh = event->Par2 != 0;
|
||||
const uint16_t pinStateValue = pinStateHigh ? 1 : 0;
|
||||
const uint16_t inversePinStateValue = pinStateHigh ? 0 : 1;
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, pinStateValue);
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, pinStateValue);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, pinStateValue);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
|
||||
// FIXME TD-er. No longer part of Plugin, so must use other scheduler mechanism to set GPIO back
|
||||
unsigned long timer = time_in_msec ? event->Par3 : event->Par3 * 1000;
|
||||
// FIXME TD-er. No longer part of Plugin, so must use other scheduler mechanism to set GPIO back
|
||||
unsigned long timer = time_in_msec ? event->Par3 : event->Par3 * 1000;
|
||||
|
||||
// Create a future system timer call to set the GPIO pin back to its normal value.
|
||||
setGPIOTimer(timer, event->Par1, inversePinStateValue);
|
||||
String log = String(F("GPIO : ")) + String(event->Par1) +
|
||||
String(F(" Pulse set for ")) + String(event->Par3) + String(time_in_msec ? F(" msec") : F(" sec"));
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
// Create a future system timer call to set the GPIO pin back to its normal value.
|
||||
setGPIOTimer(timer, event->Par1, inversePinStateValue);
|
||||
String log = String(F("GPIO : ")) + String(event->Par1) +
|
||||
String(F(" Pulse set for ")) + String(event->Par3) + String(time_in_msec ? F(" msec") : F(" sec"));
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
return return_command_failed();
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -290,7 +273,8 @@ String Command_servo(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
// GPIO number is stored inside event->Par2 instead of event->Par1 as in all the other commands
|
||||
// So needs to reload the tempPortStruct.
|
||||
if (checkValidGpioPin(event->Par2) && (event->Par1 >= 0) && (event->Par1 <= 2)) {
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
if ((event->Par1 >= 0) && (event->Par1 <= 2)) {
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par2); // WARNING: 'servo' uses Par2 instead of Par1
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
@@ -349,15 +333,12 @@ String Command_servo(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_status_gpio(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1)) {
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par2, dummyString, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
return return_command_failed();
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par2, dummyString, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -367,23 +348,21 @@ String Command_status_gpio(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_monitor_gpio(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1)) {
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
addMonitorToPort(key);
|
||||
addMonitorToPort(key);
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log;
|
||||
log.reserve(32);
|
||||
log = String(F("GPIO "));
|
||||
log += event->Par1;
|
||||
log += F(" added to monitor list.");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
return return_command_success();
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log;
|
||||
log.reserve(32);
|
||||
log = String(F("GPIO "));
|
||||
log += event->Par1;
|
||||
log += F(" added to monitor list.");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
return return_command_failed();
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -393,23 +372,21 @@ String Command_monitor_gpio(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_unmonitor_gpio(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1)) {
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
removeMonitorFromPort(key);
|
||||
removeMonitorFromPort(key);
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log;
|
||||
log.reserve(36);
|
||||
log = String(F("GPIO "));
|
||||
log += event->Par1;
|
||||
log += F(" removed from monitor list.");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
return return_command_success();
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log;
|
||||
log.reserve(36);
|
||||
log = String(F("GPIO "));
|
||||
log += event->Par1;
|
||||
log += F(" removed from monitor list.");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
return return_command_failed();
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
|
||||
@@ -422,41 +399,38 @@ String Command_rtttl(struct EventStruct *event, const char *Line)
|
||||
// FIXME: Absolutely no error checking in play_rtttl, until then keep it only in testing
|
||||
// play a tune via a RTTTL string, look at https://www.letscontrolit.com/forum/viewtopic.php?f=4&t=343&hilit=speaker&start=10 for more
|
||||
// info.
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
{
|
||||
String tmpString = Line;
|
||||
int colonPos = tmpString.indexOf(':');
|
||||
if (colonPos < 0) {
|
||||
addLog(LOG_LEVEL_ERROR, F("RTTTL : Invalid formatted command"));
|
||||
return return_command_failed();
|
||||
}
|
||||
/*
|
||||
String command = tmpString.substring(0, colonPos);
|
||||
tmpString = tmpString.substring(colonPos);
|
||||
*/
|
||||
tmpString.replace('-', '#');
|
||||
|
||||
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
play_rtttl(event->Par1, tmpString.c_str());
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, tmpString, 0);
|
||||
return return_command_success();
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
String tmpString = Line;
|
||||
int colonPos = tmpString.indexOf(':');
|
||||
if (colonPos < 0) {
|
||||
addLog(LOG_LEVEL_ERROR, F("RTTTL : Invalid formatted command"));
|
||||
return return_command_failed();
|
||||
}
|
||||
return return_command_failed();
|
||||
/*
|
||||
String command = tmpString.substring(0, colonPos);
|
||||
tmpString = tmpString.substring(colonPos);
|
||||
*/
|
||||
tmpString.replace('-', '#');
|
||||
|
||||
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
play_rtttl(event->Par1, tmpString.c_str());
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, tmpString, 0);
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
@@ -465,106 +439,54 @@ String Command_rtttl(struct EventStruct *event, const char *Line)
|
||||
// **************************************************************************/
|
||||
String Command_tone(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (checkValidGpioPin(event->Par1))
|
||||
{
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
if (!checkValidGpioPin(event->Par1)) return return_command_failed_invalid_GPIO(event->Par1);
|
||||
portStatusStruct tempStatus;
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
// WARNING: operator [] creates an entry in the map if key does not exist
|
||||
// So the next command should be part of each command:
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
unsigned int frequency = event->Par2;
|
||||
unsigned long duration = event->Par3;
|
||||
if (duration < 50) {
|
||||
tone_espEasy(event->Par1, frequency, duration);
|
||||
} else {
|
||||
// Do not wait for a very long time using delays.
|
||||
analogWriteESP(event->Par1, 100, frequency);
|
||||
// Create a future system timer call to set the GPIO pin back to its normal value.
|
||||
setGPIOTimer(duration, event->Par1, 0);
|
||||
}
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
String log = String(F("Tone : ")); // + string;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
unsigned int frequency = event->Par2;
|
||||
unsigned long duration = event->Par3;
|
||||
if (duration < 50) {
|
||||
tone_espEasy(event->Par1, frequency, duration);
|
||||
} else {
|
||||
// Do not wait for a very long time using delays.
|
||||
analogWriteESP(event->Par1, 100, frequency);
|
||||
// Create a future system timer call to set the GPIO pin back to its normal value.
|
||||
setGPIOTimer(duration, event->Par1, 0);
|
||||
}
|
||||
return return_command_failed();
|
||||
|
||||
// setPinState(PLUGIN_ID_000, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.output = event->Par2;
|
||||
tempStatus.command = 1; // set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key, tempStatus);
|
||||
String log = String(F("Tone : ")); // + string;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, log, 0);
|
||||
|
||||
// SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_000, event->Par1, log, 0));
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
// **************************************************************************/
|
||||
// Helper functions
|
||||
// **************************************************************************/
|
||||
uint32_t createInternalGpioKey(uint16_t portNumber) {
|
||||
return createKey(PLUGIN_ID_000, portNumber);
|
||||
}
|
||||
|
||||
bool read_GPIO_state(struct EventStruct *event) {
|
||||
byte pinNumber = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
const uint32_t key = createInternalGpioKey(pinNumber);
|
||||
|
||||
if (existPortStatus(key)) {
|
||||
return read_GPIO_state(pinNumber, globalMapPortStatus[key].mode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read_GPIO_state(byte pinNumber, byte pinMode) {
|
||||
bool canRead = false;
|
||||
|
||||
switch (pinMode)
|
||||
{
|
||||
case PIN_MODE_UNDEFINED:
|
||||
case PIN_MODE_INPUT:
|
||||
case PIN_MODE_INPUT_PULLUP:
|
||||
case PIN_MODE_OUTPUT:
|
||||
canRead = true;
|
||||
break;
|
||||
case PIN_MODE_PWM:
|
||||
break;
|
||||
case PIN_MODE_SERVO:
|
||||
break;
|
||||
case PIN_MODE_OFFLINE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!canRead) { return false; }
|
||||
|
||||
// Do not read from the pin while mode is set to PWM or servo.
|
||||
// See https://github.com/letscontrolit/ESPEasy/issues/2117#issuecomment-443516794
|
||||
return digitalRead(pinNumber) == HIGH;
|
||||
}
|
||||
|
||||
|
||||
bool checkValidGpioPin(byte gpio_pin) {
|
||||
int pinnr = -1;
|
||||
bool input, output, warning;
|
||||
if (getGpioInfo(gpio_pin, pinnr, input, output, warning)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String return_command_failed_invalid_GPIO(byte pinNumber) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
String log;
|
||||
log = F("GPIO : Invalid GPIO pin given: ");
|
||||
log += gpio_pin;
|
||||
log += pinNumber;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
return false;
|
||||
return return_command_failed();
|
||||
}
|
||||
|
||||
|
||||
#if defined(ESP32)
|
||||
void analogWriteESP(int pin, int value, unsigned int frequency) {
|
||||
// find existing channel if this pin has been used before
|
||||
|
||||
+2
-2
@@ -257,13 +257,13 @@ bool MQTTCheck(int controller_idx)
|
||||
/*********************************************************************************************\
|
||||
* Send status info to request source
|
||||
\*********************************************************************************************/
|
||||
void SendStatusOnlyIfNeeded(int eventSource, bool param1, uint32_t key, const String& param2, uint16_t param3) {
|
||||
void SendStatusOnlyIfNeeded(int eventSource, bool search, uint32_t key, const String& param2, uint16_t param3) {
|
||||
switch (eventSource) {
|
||||
case VALUE_SOURCE_HTTP:
|
||||
case VALUE_SOURCE_SERIAL:
|
||||
case VALUE_SOURCE_MQTT:
|
||||
case VALUE_SOURCE_WEB_FRONTEND:
|
||||
SendStatus(eventSource, getPinStateJSON(param1, key, param2, param3));
|
||||
SendStatus(eventSource, getPinStateJSON(search, key, param2, param3));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
#ifndef ESPEASY_GPIO_H
|
||||
#define ESPEASY_GPIO_H
|
||||
|
||||
#define PLUGIN_ID_000 0
|
||||
|
||||
// portStatusStruct.mode (max. 8)
|
||||
#define PIN_MODE_UNDEFINED 0
|
||||
#define PIN_MODE_INPUT 1
|
||||
#define PIN_MODE_OUTPUT 2
|
||||
#define PIN_MODE_PWM 3
|
||||
#define PIN_MODE_SERVO 4
|
||||
#define PIN_MODE_INPUT_PULLUP 5
|
||||
#define PIN_MODE_OFFLINE 6
|
||||
|
||||
|
||||
#define SEARCH_PIN_STATE true
|
||||
#define NO_SEARCH_PIN_STATE false
|
||||
|
||||
|
||||
// See description/discussion:
|
||||
// https://github.com/letscontrolit/ESPEasy/pull/2057#issuecomment-445310454
|
||||
struct portStatusStruct {
|
||||
portStatusStruct() : state(-1), output(-1), command(0), portstatus_init(0), mode(0), task(0), monitor(0), previousTask(-1)
|
||||
{}
|
||||
|
||||
int8_t state : 2; // -1,0,1
|
||||
int8_t output : 2; // -1,0,1
|
||||
int8_t command : 2; // 0,1 true when a command (GPIO,PCFGPIO,MCPGPIO,TOGGLEGPIO, etc.) has referenced that gpio/port.
|
||||
int8_t portstatus_init : 2; // true when the gpio/port has been set during boot in the hardware page
|
||||
|
||||
uint8_t mode : 3; // 7 current values (max. 8)
|
||||
uint8_t task : 4; // 0-15 (max. 16) If task = 0 it means that no task are referencing that pin.
|
||||
uint8_t monitor : 1; // 0,1 true means the gpio/port should send an event when it changes
|
||||
|
||||
int8_t previousTask;
|
||||
|
||||
bool readyToDelete() const {
|
||||
return ((task <= 0) && (monitor <= 0) && (command <= 0));
|
||||
}
|
||||
|
||||
bool mustPollGpioState() const {
|
||||
return (monitor != 0) || (command != 0) || (portstatus_init != 0);
|
||||
}
|
||||
|
||||
bool portStateError() const {
|
||||
return state != 0 && state != 1;
|
||||
}
|
||||
|
||||
bool getPinState() const {
|
||||
return state == 1;
|
||||
}
|
||||
|
||||
void updatePinState(int8_t newState) {
|
||||
state = newState;
|
||||
}
|
||||
};
|
||||
|
||||
std::map < uint32_t, portStatusStruct > globalMapPortStatus;
|
||||
|
||||
// Forward declarations
|
||||
uint32_t createInternalGpioKey(uint16_t portNumber);
|
||||
bool read_GPIO_state(struct EventStruct *event);
|
||||
bool read_GPIO_state(byte gpio_pin,
|
||||
byte pinMode);
|
||||
bool checkValidGpioPin(byte gpio_pin);
|
||||
bool getGpioInfo(int gpio_pin,
|
||||
int & nodemcu_pinnr,
|
||||
bool& input,
|
||||
bool& output,
|
||||
bool& warning);
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* *
|
||||
* Helper Functions for managing the status data structure *
|
||||
* *
|
||||
**********************************************************/
|
||||
void savePortStatus(uint32_t key, struct portStatusStruct& tempStatus) {
|
||||
if (tempStatus.readyToDelete()) {
|
||||
globalMapPortStatus.erase(key);
|
||||
}
|
||||
else {
|
||||
globalMapPortStatus[key] = tempStatus;
|
||||
}
|
||||
}
|
||||
|
||||
bool existPortStatus(uint32_t key) {
|
||||
return globalMapPortStatus.find(key) != globalMapPortStatus.end();
|
||||
}
|
||||
|
||||
void removeTaskFromPort(uint32_t key) {
|
||||
if (existPortStatus(key)) {
|
||||
portStatusStruct& portstatus = globalMapPortStatus[key];
|
||||
|
||||
if (portstatus.task > 0) {
|
||||
--portstatus.task;
|
||||
}
|
||||
|
||||
if (portstatus.readyToDelete() && (portstatus.portstatus_init <= 0)) {
|
||||
globalMapPortStatus.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeMonitorFromPort(uint32_t key) {
|
||||
if (existPortStatus(key)) {
|
||||
portStatusStruct& portstatus = globalMapPortStatus[key];
|
||||
portstatus.monitor = 0;
|
||||
|
||||
if (portstatus.readyToDelete() && (portstatus.portstatus_init <= 0)) {
|
||||
globalMapPortStatus.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addMonitorToPort(uint32_t key) {
|
||||
globalMapPortStatus[key].monitor = 1;
|
||||
}
|
||||
|
||||
uint32_t createKey(uint16_t pluginNumber, uint16_t portNumber) {
|
||||
return (uint32_t)pluginNumber << 16 | portNumber;
|
||||
}
|
||||
|
||||
uint16_t getPluginFromKey(uint32_t key) {
|
||||
return (uint16_t)(key >> 16);
|
||||
}
|
||||
|
||||
uint16_t getPortFromKey(uint32_t key) {
|
||||
return (uint16_t)(key);
|
||||
}
|
||||
|
||||
uint32_t createInternalGpioKey(uint16_t portNumber) {
|
||||
return createKey(PLUGIN_ID_000, portNumber);
|
||||
}
|
||||
|
||||
// return true when pin can be used.
|
||||
bool checkValidGpioPin(byte gpio_pin) {
|
||||
int nodemcu_pinnr = -1;
|
||||
bool input, output, warning;
|
||||
|
||||
return getGpioInfo(gpio_pin, nodemcu_pinnr, input, output, warning);
|
||||
}
|
||||
|
||||
bool read_GPIO_state(struct EventStruct *event) {
|
||||
byte gpio_pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
const uint32_t key = createInternalGpioKey(gpio_pin);
|
||||
|
||||
if (existPortStatus(key)) {
|
||||
return read_GPIO_state(gpio_pin, globalMapPortStatus[key].mode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read_GPIO_state(byte gpio_pin, byte pinMode) {
|
||||
bool canRead = false;
|
||||
|
||||
switch (pinMode)
|
||||
{
|
||||
case PIN_MODE_UNDEFINED:
|
||||
case PIN_MODE_INPUT:
|
||||
case PIN_MODE_INPUT_PULLUP:
|
||||
case PIN_MODE_OUTPUT:
|
||||
canRead = true;
|
||||
break;
|
||||
case PIN_MODE_PWM:
|
||||
break;
|
||||
case PIN_MODE_SERVO:
|
||||
break;
|
||||
case PIN_MODE_OFFLINE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!canRead) { return false; }
|
||||
|
||||
// Do not read from the pin while mode is set to PWM or servo.
|
||||
// See https://github.com/letscontrolit/ESPEasy/issues/2117#issuecomment-443516794
|
||||
const auto pinstate = digitalRead(gpio_pin);
|
||||
// const uint32_t key = createInternalGpioKey(gpio_pin);
|
||||
// globalMapPortStatus[key].state = pinstate;
|
||||
return pinstate == HIGH;
|
||||
}
|
||||
|
||||
#endif // ESPEASY_GPIO_H
|
||||
+1
-27
@@ -349,16 +349,7 @@
|
||||
|
||||
#define UDP_PACKETSIZE_MAX 2048
|
||||
|
||||
#define PIN_MODE_UNDEFINED 0
|
||||
#define PIN_MODE_INPUT 1
|
||||
#define PIN_MODE_OUTPUT 2
|
||||
#define PIN_MODE_PWM 3
|
||||
#define PIN_MODE_SERVO 4
|
||||
#define PIN_MODE_INPUT_PULLUP 5
|
||||
#define PIN_MODE_OFFLINE 6
|
||||
|
||||
#define SEARCH_PIN_STATE true
|
||||
#define NO_SEARCH_PIN_STATE false
|
||||
|
||||
#define DEVICE_TYPE_SINGLE 1 // connected through 1 datapin
|
||||
#define DEVICE_TYPE_DUAL 2 // connected through 2 datapins
|
||||
@@ -1994,24 +1985,6 @@ String getMiscStatsName(int stat) {
|
||||
}
|
||||
|
||||
|
||||
struct portStatusStruct {
|
||||
portStatusStruct() : state(-1), output(-1), command(0), init(0), mode(0), task(0), monitor(0), previousTask(-1) {}
|
||||
|
||||
int8_t state : 2; //-1,0,1
|
||||
int8_t output : 2; //-1,0,1
|
||||
int8_t command : 2; //0,1
|
||||
int8_t init : 2; //0,1
|
||||
|
||||
uint8_t mode : 3; //6 current values (max. 8)
|
||||
uint8_t task : 4; //0-15 (max. 16)
|
||||
uint8_t monitor : 1; //0,1
|
||||
|
||||
int8_t previousTask : 8;
|
||||
};
|
||||
|
||||
std::map<uint32_t, portStatusStruct> globalMapPortStatus;
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Pre defined settings for off-the-shelf hardware
|
||||
\*********************************************************************************************/
|
||||
@@ -2163,5 +2136,6 @@ void addPredefinedRules(const GpioFactorySettingsStruct& gpio_settings);
|
||||
#include "ESPEasyWiFiEvent.h"
|
||||
#define SPIFFS_CHECK(result, fname) if (!(result)) { return(FileError(__LINE__, fname)); }
|
||||
#include "WebServer_Rules.h"
|
||||
#include "ESPEasy-GPIO.h"
|
||||
|
||||
#endif /* ESPEASY_GLOBALS_H_ */
|
||||
|
||||
+169
-139
@@ -1,78 +1,102 @@
|
||||
/********************************************************************************************\
|
||||
* Initialize specific hardware settings (only global ones, others are set through devices)
|
||||
* Initialize specific hardware settings (only global ones, others are set
|
||||
through devices)
|
||||
\*********************************************************************************************/
|
||||
bool applyGpioPinBootState(byte gpio_pin, byte PinBootState) {
|
||||
if (!checkValidGpioBootStatePin(gpio_pin))
|
||||
return false;
|
||||
|
||||
void hardwareInit()
|
||||
{
|
||||
bool serialPinConflict =
|
||||
(Settings.UseSerial && (gpio_pin == 1 || gpio_pin == 3));
|
||||
if (serialPinConflict)
|
||||
return false;
|
||||
|
||||
int nodemcu_pinnr = -1;
|
||||
bool input, output, warning;
|
||||
getGpioInfo(gpio_pin, nodemcu_pinnr, input, output, warning);
|
||||
const uint32_t key = createInternalGpioKey(gpio_pin);
|
||||
switch (PinBootState) {
|
||||
case 0:
|
||||
if (!input)
|
||||
return false;
|
||||
pinMode(gpio_pin, INPUT);
|
||||
globalMapPortStatus[key].mode = PIN_MODE_INPUT;
|
||||
globalMapPortStatus[key].portstatus_init = 1;
|
||||
globalMapPortStatus[key].state = read_GPIO_state(gpio_pin, PIN_MODE_INPUT);
|
||||
// setPinState(1, gpio_pin, PIN_MODE_OUTPUT, LOW);
|
||||
break;
|
||||
case 1:
|
||||
if (!output)
|
||||
return false;
|
||||
pinMode(gpio_pin, OUTPUT);
|
||||
digitalWrite(gpio_pin, LOW);
|
||||
globalMapPortStatus[key].state = LOW;
|
||||
globalMapPortStatus[key].mode = PIN_MODE_OUTPUT;
|
||||
globalMapPortStatus[key].portstatus_init = 1;
|
||||
// setPinState(1, gpio_pin, PIN_MODE_OUTPUT, LOW);
|
||||
break;
|
||||
case 2:
|
||||
if (!output)
|
||||
return false;
|
||||
pinMode(gpio_pin, OUTPUT);
|
||||
digitalWrite(gpio_pin, HIGH);
|
||||
globalMapPortStatus[key].state = HIGH;
|
||||
globalMapPortStatus[key].mode = PIN_MODE_OUTPUT;
|
||||
globalMapPortStatus[key].portstatus_init = 1;
|
||||
// setPinState(1, gpio_pin, PIN_MODE_OUTPUT, HIGH);
|
||||
break;
|
||||
case 3:
|
||||
if (!input)
|
||||
return false;
|
||||
pinMode(gpio_pin, INPUT_PULLUP);
|
||||
globalMapPortStatus[key].mode = PIN_MODE_INPUT_PULLUP;
|
||||
globalMapPortStatus[key].portstatus_init = 1;
|
||||
globalMapPortStatus[key].state = read_GPIO_state(gpio_pin, PIN_MODE_INPUT_PULLUP);
|
||||
// setPinState(1, gpio_pin, PIN_MODE_INPUT, 0);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void hardwareInit() {
|
||||
// set GPIO pins state if not set to default
|
||||
for (byte gpio = 0; gpio < PIN_D_MAX; ++gpio) {
|
||||
bool serialPinConflict = (Settings.UseSerial && (gpio == 1 || gpio == 3));
|
||||
if (!serialPinConflict && Settings.PinBootStates[gpio] != 0) {
|
||||
const uint32_t key = createKey(1,gpio);
|
||||
switch(Settings.PinBootStates[gpio])
|
||||
{
|
||||
case 1:
|
||||
pinMode(gpio,OUTPUT);
|
||||
digitalWrite(gpio,LOW);
|
||||
globalMapPortStatus[key].state = LOW;
|
||||
globalMapPortStatus[key].mode = PIN_MODE_OUTPUT;
|
||||
globalMapPortStatus[key].init = 1;
|
||||
//setPinState(1, gpio, PIN_MODE_OUTPUT, LOW);
|
||||
break;
|
||||
case 2:
|
||||
pinMode(gpio,OUTPUT);
|
||||
digitalWrite(gpio,HIGH);
|
||||
globalMapPortStatus[key].state = HIGH;
|
||||
globalMapPortStatus[key].mode = PIN_MODE_OUTPUT;
|
||||
globalMapPortStatus[key].init = 1;
|
||||
//setPinState(1, gpio, PIN_MODE_OUTPUT, HIGH);
|
||||
break;
|
||||
case 3:
|
||||
pinMode(gpio,INPUT_PULLUP);
|
||||
globalMapPortStatus[key].state = 0;
|
||||
globalMapPortStatus[key].mode = PIN_MODE_INPUT_PULLUP;
|
||||
globalMapPortStatus[key].init = 1;
|
||||
//setPinState(1, gpio, PIN_MODE_INPUT, 0);
|
||||
break;
|
||||
}
|
||||
for (byte gpio_pin = 0; gpio_pin < PIN_D_MAX; ++gpio_pin) {
|
||||
if (Settings.PinBootStates[gpio_pin] != 0) {
|
||||
// Do not change at boot when set to default.
|
||||
applyGpioPinBootState(gpio_pin, Settings.PinBootStates[gpio_pin]);
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.Pin_Reset != -1)
|
||||
pinMode(Settings.Pin_Reset,INPUT_PULLUP);
|
||||
pinMode(Settings.Pin_Reset, INPUT_PULLUP);
|
||||
|
||||
// configure hardware pins according to eeprom settings.
|
||||
if (Settings.Pin_i2c_sda != -1)
|
||||
{
|
||||
if (Settings.Pin_i2c_sda != -1) {
|
||||
String log = F("INIT : I2C");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
Wire.begin(Settings.Pin_i2c_sda, Settings.Pin_i2c_scl);
|
||||
if(Settings.WireClockStretchLimit)
|
||||
{
|
||||
String log = F("INIT : I2C custom clockstretchlimit:");
|
||||
log += Settings.WireClockStretchLimit;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#if defined(ESP8266)
|
||||
Wire.setClockStretchLimit(Settings.WireClockStretchLimit);
|
||||
#endif
|
||||
}
|
||||
if (Settings.WireClockStretchLimit) {
|
||||
String log = F("INIT : I2C custom clockstretchlimit:");
|
||||
log += Settings.WireClockStretchLimit;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#if defined(ESP8266)
|
||||
Wire.setClockStretchLimit(Settings.WireClockStretchLimit);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// I2C Watchdog boot status check
|
||||
if (Settings.WDI2CAddress != 0)
|
||||
{
|
||||
if (Settings.WDI2CAddress != 0) {
|
||||
delay(500);
|
||||
Wire.beginTransmission(Settings.WDI2CAddress);
|
||||
Wire.write(0x83); // command to set pointer
|
||||
Wire.write(17); // pointer value to status byte
|
||||
Wire.write(0x83); // command to set pointer
|
||||
Wire.write(17); // pointer value to status byte
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.requestFrom(Settings.WDI2CAddress, (uint8_t)1);
|
||||
if (Wire.available())
|
||||
{
|
||||
if (Wire.available()) {
|
||||
byte status = Wire.read();
|
||||
if (status & 0x1)
|
||||
{
|
||||
if (status & 0x1) {
|
||||
String log = F("INIT : Reset by WD!");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
lastBootCause = BOOT_CAUSE_EXT_WD;
|
||||
@@ -81,47 +105,37 @@ void hardwareInit()
|
||||
}
|
||||
|
||||
// SPI Init
|
||||
if (Settings.InitSPI)
|
||||
{
|
||||
if (Settings.InitSPI) {
|
||||
SPI.setHwCs(false);
|
||||
SPI.begin();
|
||||
String log = F("INIT : SPI Init (without CS)");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
String log = F("INIT : SPI not enabled");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
|
||||
#ifdef FEATURE_SD
|
||||
if (Settings.Pin_sd_cs >= 0)
|
||||
{
|
||||
if (SD.begin(Settings.Pin_sd_cs))
|
||||
{
|
||||
if (Settings.Pin_sd_cs >= 0) {
|
||||
if (SD.begin(Settings.Pin_sd_cs)) {
|
||||
String log = F("SD : Init OK");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
String log = F("SD : Init failed");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void checkResetFactoryPin(){
|
||||
static byte factoryResetCounter=0;
|
||||
void checkResetFactoryPin() {
|
||||
static byte factoryResetCounter = 0;
|
||||
if (Settings.Pin_Reset == -1)
|
||||
return;
|
||||
|
||||
if (digitalRead(Settings.Pin_Reset) == 0){ // active low reset pin
|
||||
factoryResetCounter++; // just count every second
|
||||
}
|
||||
else
|
||||
{ // reset pin released
|
||||
if (digitalRead(Settings.Pin_Reset) == 0) { // active low reset pin
|
||||
factoryResetCounter++; // just count every second
|
||||
} else { // reset pin released
|
||||
if (factoryResetCounter > 9) {
|
||||
// factory reset and reboot
|
||||
ResetFactory();
|
||||
@@ -159,19 +173,23 @@ bool modelMatchingFlashSize(DeviceModel model) {
|
||||
uint32_t size_MB = getFlashRealSizeInBytes() >> 20;
|
||||
// TODO TD-er: Add checks for ESP8266/ESP8285/ESP32
|
||||
switch (model) {
|
||||
case DeviceModel_Sonoff_Basic:
|
||||
case DeviceModel_Sonoff_TH1x:
|
||||
case DeviceModel_Sonoff_S2x:
|
||||
case DeviceModel_Sonoff_TouchT1:
|
||||
case DeviceModel_Sonoff_TouchT2:
|
||||
case DeviceModel_Sonoff_TouchT3:
|
||||
case DeviceModel_Sonoff_4ch: return size_MB == 1;
|
||||
case DeviceModel_Sonoff_POW:
|
||||
case DeviceModel_Sonoff_POWr2: return size_MB == 4;
|
||||
case DeviceModel_Shelly1: return size_MB == 2;
|
||||
case DeviceModel_Sonoff_Basic:
|
||||
case DeviceModel_Sonoff_TH1x:
|
||||
case DeviceModel_Sonoff_S2x:
|
||||
case DeviceModel_Sonoff_TouchT1:
|
||||
case DeviceModel_Sonoff_TouchT2:
|
||||
case DeviceModel_Sonoff_TouchT3:
|
||||
case DeviceModel_Sonoff_4ch:
|
||||
return size_MB == 1;
|
||||
case DeviceModel_Sonoff_POW:
|
||||
case DeviceModel_Sonoff_POWr2:
|
||||
return size_MB == 4;
|
||||
case DeviceModel_Shelly1:
|
||||
return size_MB == 2;
|
||||
|
||||
// case DeviceModel_default:
|
||||
default: return true;
|
||||
// case DeviceModel_default:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -183,48 +201,50 @@ void setFactoryDefault(DeviceModel model) {
|
||||
/********************************************************************************************\
|
||||
Add pre defined plugins and rules.
|
||||
\*********************************************************************************************/
|
||||
void addSwitchPlugin(byte taskIndex, byte gpio, const String& name, bool activeLow) {
|
||||
void addSwitchPlugin(byte taskIndex, byte gpio_pin, const String &name,
|
||||
bool activeLow) {
|
||||
setTaskDevice_to_TaskIndex(1, taskIndex);
|
||||
setBasicTaskValues(
|
||||
taskIndex,
|
||||
0, // taskdevicetimer
|
||||
true, // enabled
|
||||
name, // name
|
||||
gpio, // pin1
|
||||
-1, // pin2
|
||||
-1); // pin3
|
||||
setBasicTaskValues(taskIndex,
|
||||
0, // taskdevicetimer
|
||||
true, // enabled
|
||||
name, // name
|
||||
gpio_pin, // pin1
|
||||
-1, // pin2
|
||||
-1); // pin3
|
||||
Settings.TaskDevicePin1PullUp[taskIndex] = true;
|
||||
if (activeLow)
|
||||
Settings.TaskDevicePluginConfig[taskIndex][2] = 1; // PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW;
|
||||
Settings.TaskDevicePluginConfig[taskIndex][2] =
|
||||
1; // PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
void addPredefinedPlugins(const GpioFactorySettingsStruct& gpio_settings) {
|
||||
void addPredefinedPlugins(const GpioFactorySettingsStruct &gpio_settings) {
|
||||
byte taskIndex = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (gpio_settings.button[i] >= 0) {
|
||||
String label = F("Button");
|
||||
label += (i+1);
|
||||
label += (i + 1);
|
||||
addSwitchPlugin(taskIndex, gpio_settings.button[i], label, true);
|
||||
++taskIndex;
|
||||
}
|
||||
if (gpio_settings.relais[i] >= 0) {
|
||||
String label = F("Relay");
|
||||
label += (i+1);
|
||||
label += (i + 1);
|
||||
addSwitchPlugin(taskIndex, gpio_settings.relais[i], label, false);
|
||||
++taskIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void addButtonRelayRule(byte buttonNumber, byte relay_gpio) {
|
||||
Settings.UseRules = true;
|
||||
String fileName;
|
||||
#if defined(ESP32)
|
||||
fileName += '/';
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
fileName += '/';
|
||||
#endif
|
||||
fileName += F("rules1.txt");
|
||||
String rule = F("on ButtonBNR#switch do\n if [ButtonBNR#switch]=1\n gpio,GNR,1\n else\n gpio,GNR,0\n endif\nendon\n");
|
||||
String rule =
|
||||
F("on ButtonBNR#switch do\n if [ButtonBNR#switch]=1\n "
|
||||
"gpio_pin,GNR,1\n else\n gpio_pin,GNR,0\n endif\nendon\n");
|
||||
rule.replace(F("BNR"), String(buttonNumber));
|
||||
rule.replace(F("GNR"), String(relay_gpio));
|
||||
String result = appendLineToFile(fileName, rule);
|
||||
@@ -233,28 +253,38 @@ void addButtonRelayRule(byte buttonNumber, byte relay_gpio) {
|
||||
}
|
||||
}
|
||||
|
||||
void addPredefinedRules(const GpioFactorySettingsStruct& gpio_settings) {
|
||||
void addPredefinedRules(const GpioFactorySettingsStruct &gpio_settings) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (gpio_settings.button[i] >= 0 && gpio_settings.relais[i] >= 0) {
|
||||
addButtonRelayRule((i+1), gpio_settings.relais[i]);
|
||||
addButtonRelayRule((i + 1), gpio_settings.relais[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
bool checkValidGpioBootStatePin(byte gpio_pin) {
|
||||
#ifdef ESP8266
|
||||
// GPIO 16 is a strange pin, pulled to GND and when used to wake from deep
|
||||
// sleep,
|
||||
// it will trigger a reset when changed.
|
||||
if (gpio_pin == 16)
|
||||
return false;
|
||||
#endif
|
||||
return checkValidGpioPin(gpio_pin);
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
// Get info of a specific GPIO pin.
|
||||
//********************************************************************************
|
||||
bool getGpioInfo(int gpio, int& pinnr, bool& input, bool& output, bool& warning) {
|
||||
pinnr = -1; // ESP32 does not label the pins, they just use the GPIO number.
|
||||
#ifdef ESP32
|
||||
// return true when pin can be used.
|
||||
bool getGpioInfo(int gpio_pin, int& nodemcu_pinnr, bool& input, bool& output, bool& warning) {
|
||||
nodemcu_pinnr = -1; // ESP32 does not label the pins, they just use the GPIO number.
|
||||
|
||||
// Input GPIOs: 0-19, 21-23, 25-27, 32-39
|
||||
// Output GPIOs: 0-19, 21-23, 25-27, 32-33
|
||||
input = gpio <= 39;
|
||||
output = gpio <= 33;
|
||||
if (gpio < 0 || gpio == 20 || gpio == 24 || (gpio > 27 && gpio < 32)) {
|
||||
input = gpio_pin <= 39;
|
||||
output = gpio_pin <= 33;
|
||||
if (gpio_pin < 0 || gpio_pin == 20 || gpio_pin == 24 || (gpio_pin > 27 && gpio_pin < 32)) {
|
||||
input = false;
|
||||
output = false;
|
||||
}
|
||||
@@ -263,8 +293,8 @@ bool getGpioInfo(int gpio, int& pinnr, bool& input, bool& output, bool& warning)
|
||||
}
|
||||
|
||||
// GPIO 0 & 2 can't be used as an input. State during boot is dependent on boot mode.
|
||||
warning = (gpio == 0 || gpio == 2);
|
||||
if (gpio == 12) {
|
||||
warning = (gpio_pin == 0 || gpio_pin == 2);
|
||||
if (gpio_pin == 12) {
|
||||
// If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V.
|
||||
// Has internal pull-down, so unconnected = Low = 3.3V.
|
||||
// May prevent flashing and/or booting if 3.3V flash is used and this pin is
|
||||
@@ -272,50 +302,50 @@ bool getGpioInfo(int gpio, int& pinnr, bool& input, bool& output, bool& warning)
|
||||
// See the ESP32 datasheet for more details.
|
||||
warning = true;
|
||||
}
|
||||
if (gpio == 15) {
|
||||
if (gpio_pin == 15) {
|
||||
// If driven Low, silences boot messages printed by the ROM bootloader.
|
||||
// Has an internal pull-up, so unconnected = High = normal output.
|
||||
warning = true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// return true when pin can be used.
|
||||
bool getGpioInfo(int gpio, int& pinnr, bool& input, bool& output, bool& warning) {
|
||||
pinnr = -1;
|
||||
bool getGpioInfo(int gpio_pin, int& nodemcu_pinnr, bool& input, bool& output, bool& warning) {
|
||||
nodemcu_pinnr = -1;
|
||||
input = true;
|
||||
output = true;
|
||||
// GPIO 0, 2 & 15 can't be used as an input. State during boot is dependent on boot mode.
|
||||
warning = (gpio == 0 || gpio == 2 || gpio == 15);
|
||||
switch (gpio) {
|
||||
case 0: pinnr = 3; break;
|
||||
case 1: pinnr = 10; break;
|
||||
case 2: pinnr = 4; break;
|
||||
case 3: pinnr = 9; break;
|
||||
case 4: pinnr = 2; break;
|
||||
case 5: pinnr = 1; break;
|
||||
// GPIO 16 is a strange pin, pulled to GND and when used to wake from deep sleep,
|
||||
// it will trigger a reset when changed.
|
||||
warning = (gpio_pin == 0 || gpio_pin == 2 || gpio_pin == 15 || gpio_pin == 16);
|
||||
switch (gpio_pin) {
|
||||
case 0: nodemcu_pinnr = 3; break;
|
||||
case 1: nodemcu_pinnr = 10; break;
|
||||
case 2: nodemcu_pinnr = 4; break;
|
||||
case 3: nodemcu_pinnr = 9; break;
|
||||
case 4: nodemcu_pinnr = 2; break;
|
||||
case 5: nodemcu_pinnr = 1; break;
|
||||
case 6: // GPIO 6 .. 8 is used for flash
|
||||
case 7:
|
||||
case 8: pinnr = -1; break;
|
||||
case 9: pinnr = 11; break; // On ESP8266 used for flash
|
||||
case 10: pinnr = 12; break; // On ESP8266 used for flash
|
||||
case 11: pinnr = -1; break;
|
||||
case 12: pinnr = 6; break;
|
||||
case 13: pinnr = 7; break;
|
||||
case 14: pinnr = 5; break;
|
||||
case 8: nodemcu_pinnr = -1; break;
|
||||
case 9: nodemcu_pinnr = 11; break; // On ESP8266 used for flash
|
||||
case 10: nodemcu_pinnr = 12; break; // On ESP8266 used for flash
|
||||
case 11: nodemcu_pinnr = -1; break;
|
||||
case 12: nodemcu_pinnr = 6; break;
|
||||
case 13: nodemcu_pinnr = 7; break;
|
||||
case 14: nodemcu_pinnr = 5; break;
|
||||
// GPIO-15 Can't be used as an input. There is an external pull-down on this pin.
|
||||
case 15: pinnr = 8; input = false; break;
|
||||
case 16: pinnr = 0; break; // This is used by the deep-sleep mechanism
|
||||
case 15: nodemcu_pinnr = 8; input = false; break;
|
||||
case 16: nodemcu_pinnr = 0; break; // This is used by the deep-sleep mechanism
|
||||
}
|
||||
#ifndef ESP8285
|
||||
if (gpio == 9 || gpio == 10) {
|
||||
if (gpio_pin == 9 || gpio_pin == 10) {
|
||||
// On ESP8266 used for flash
|
||||
warning = true;
|
||||
}
|
||||
#endif
|
||||
if (pinnr < 0) {
|
||||
#endif
|
||||
if (nodemcu_pinnr < 0) {
|
||||
input = false;
|
||||
output = false;
|
||||
return false;
|
||||
|
||||
+1
-57
@@ -111,7 +111,7 @@ bool isDeepSleepEnabled()
|
||||
//recommended wiring: 3-pin-header with 1=RST, 2=D0, 3=GND
|
||||
// short 1-2 for normal deep sleep / wakeup loop
|
||||
// short 2-3 to cancel sleep loop for modifying settings
|
||||
pinMode(16,INPUT_PULLUP);
|
||||
pinMode(16,INPUT_PULLUP); // FIXME TD-er: Pin 16 only knows INPUT_PULLDOWN_16
|
||||
if (!digitalRead(16))
|
||||
{
|
||||
return false;
|
||||
@@ -2726,59 +2726,3 @@ float compute_humidity_from_dewpoint(float temperature, float dew_temperature) {
|
||||
return 100.0 * pow((112.0 - 0.1 * temperature + dew_temperature) /
|
||||
(112.0 + 0.9 * temperature), 8);
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* *
|
||||
* Helper Functions for managing the status data structure *
|
||||
* *
|
||||
**********************************************************/
|
||||
|
||||
void savePortStatus(uint32_t key, struct portStatusStruct &tempStatus) {
|
||||
if (tempStatus.task<=0 && tempStatus.monitor<=0 && tempStatus.command<=0)
|
||||
globalMapPortStatus.erase(key);
|
||||
else
|
||||
globalMapPortStatus[key] = tempStatus;
|
||||
}
|
||||
|
||||
bool existPortStatus(uint32_t key) {
|
||||
bool retValue = false;
|
||||
//check if KEY exists:
|
||||
std::map<uint32_t,portStatusStruct>::iterator it;
|
||||
it = globalMapPortStatus.find(key);
|
||||
if (it != globalMapPortStatus.end()) { //if KEY exists...
|
||||
retValue = true;
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
void removeTaskFromPort(uint32_t key) {
|
||||
if (existPortStatus(key)) {
|
||||
(globalMapPortStatus[key].task > 0) ? globalMapPortStatus[key].task-- : globalMapPortStatus[key].task = 0;
|
||||
if (globalMapPortStatus[key].task<=0 && globalMapPortStatus[key].monitor<=0 && globalMapPortStatus[key].command<=0&& globalMapPortStatus[key].init<=0)
|
||||
globalMapPortStatus.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void removeMonitorFromPort(uint32_t key) {
|
||||
if (existPortStatus(key)) {
|
||||
globalMapPortStatus[key].monitor=0;
|
||||
if (globalMapPortStatus[key].task<=0 && globalMapPortStatus[key].monitor<=0 && globalMapPortStatus[key].command<=0&& globalMapPortStatus[key].init<=0)
|
||||
globalMapPortStatus.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void addMonitorToPort(uint32_t key) {
|
||||
globalMapPortStatus[key].monitor=1;
|
||||
}
|
||||
|
||||
uint32_t createKey(uint16_t pluginNumber, uint16_t portNumber) {
|
||||
return (uint32_t) pluginNumber << 16 | portNumber;
|
||||
}
|
||||
|
||||
uint16_t getPluginFromKey(uint32_t key) {
|
||||
return (uint16_t)(key >> 16);
|
||||
}
|
||||
|
||||
uint16_t getPortFromKey(uint32_t key) {
|
||||
return (uint16_t)(key);
|
||||
}
|
||||
|
||||
+47
-50
@@ -1742,21 +1742,28 @@ void handle_hardware() {
|
||||
Settings.Pin_i2c_scl = getFormItemInt(F("pscl"));
|
||||
Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init
|
||||
Settings.Pin_sd_cs = getFormItemInt(F("sd"));
|
||||
int gpio = 0;
|
||||
int gpio_pin = 0;
|
||||
// FIXME TD-er: Max of 17 is a limit in the Settings.PinBootStates array
|
||||
while (gpio < MAX_GPIO && gpio < 17) {
|
||||
if (Settings.UseSerial && (gpio == 1 || gpio == 3)) {
|
||||
while (gpio_pin <= MAX_GPIO && gpio_pin < 17) {
|
||||
if (Settings.UseSerial && (gpio_pin == 1 || gpio_pin == 3)) {
|
||||
// do not add the pin state select for these pins.
|
||||
} else {
|
||||
int pinnr = -1;
|
||||
int nodemcu_pinnr = -1;
|
||||
bool input, output, warning;
|
||||
if (getGpioInfo(gpio, pinnr, input, output, warning)) {
|
||||
if (getGpioInfo(gpio_pin, nodemcu_pinnr, input, output, warning)) {
|
||||
String int_pinlabel = "p";
|
||||
int_pinlabel += gpio;
|
||||
Settings.PinBootStates[gpio] = getFormItemInt(int_pinlabel);
|
||||
int_pinlabel += gpio_pin;
|
||||
byte NewPinBootState = getFormItemInt(int_pinlabel);
|
||||
if (Settings.PinBootStates[gpio_pin] != NewPinBootState) {
|
||||
// Setting has changed, apply setting now.
|
||||
if (applyGpioPinBootState(gpio_pin, NewPinBootState)) {
|
||||
// Only store the settings when they are applicable.
|
||||
Settings.PinBootStates[gpio_pin] = NewPinBootState;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++gpio;
|
||||
++gpio_pin;
|
||||
}
|
||||
addHtmlError(SaveSettings());
|
||||
}
|
||||
@@ -1768,7 +1775,7 @@ void handle_hardware() {
|
||||
addFormSubHeader(F("Wifi Status LED"));
|
||||
addFormPinSelect(formatGpioName_output("LED"), "pled", Settings.Pin_status_led);
|
||||
addFormCheckBox(F("Inversed LED"), F("pledi"), Settings.Pin_status_led_Inversed);
|
||||
addFormNote(F("Use ’GPIO-2 (D4)’ with ’Inversed’ checked for onboard LED"));
|
||||
addFormNote(F("On most boards, use ’GPIO-2 (D4)’ with ’Inversed’ checked for onboard LED"));
|
||||
|
||||
addFormSubHeader(F("Reset Pin"));
|
||||
addFormPinSelect(formatGpioName_input(F("Switch")), "pres", Settings.Pin_Reset);
|
||||
@@ -1788,26 +1795,30 @@ void handle_hardware() {
|
||||
#endif
|
||||
|
||||
addFormSubHeader(F("GPIO boot states"));
|
||||
int gpio = 0;
|
||||
int gpio_pin = 0;
|
||||
// FIXME TD-er: Max of 17 is a limit in the Settings.PinBootStates array
|
||||
while (gpio < MAX_GPIO && gpio < 17) {
|
||||
while (gpio_pin <= MAX_GPIO && gpio_pin < 17) {
|
||||
bool enabled = true;
|
||||
if (Settings.UseSerial && (gpio == 1 || gpio == 3)) {
|
||||
if (Settings.UseSerial && (gpio_pin == 1 || gpio_pin == 3)) {
|
||||
// do not add the pin state select for these pins.
|
||||
enabled = false;
|
||||
}
|
||||
int pinnr = -1;
|
||||
int nodemcu_pinnr = -1;
|
||||
bool input, output, warning;
|
||||
if (getGpioInfo(gpio, pinnr, input, output, warning)) {
|
||||
if (getGpioInfo(gpio_pin, nodemcu_pinnr, input, output, warning)) {
|
||||
enabled = checkValidGpioBootStatePin(gpio_pin);
|
||||
String label;
|
||||
label.reserve(32);
|
||||
label = F("Pin mode ");
|
||||
label += createGPIO_label(gpio, pinnr, input, output, warning);
|
||||
label += createGPIO_label(gpio_pin, nodemcu_pinnr, input, output, warning);
|
||||
String int_pinlabel = "p";
|
||||
int_pinlabel += gpio;
|
||||
addFormPinStateSelect(label, int_pinlabel, Settings.PinBootStates[gpio], enabled);
|
||||
int_pinlabel += gpio_pin;
|
||||
// Add a GPIO pin select dropdown list
|
||||
addRowLabel(label);
|
||||
String options[4] = { F("Default (Input)"), F("Output Low"), F("Output High"), F("Input Pull-up") };
|
||||
addSelector(int_pinlabel, 4, options, NULL, NULL, Settings.PinBootStates[gpio_pin], false, enabled);
|
||||
}
|
||||
++gpio;
|
||||
++gpio_pin;
|
||||
}
|
||||
addFormSeparator(2);
|
||||
|
||||
@@ -1823,20 +1834,6 @@ void handle_hardware() {
|
||||
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
// Add a GPIO pin select dropdown list
|
||||
//********************************************************************************
|
||||
void addFormPinStateSelect(const String& label, const String& id, int choice, bool enabled)
|
||||
{
|
||||
addRowLabel(label);
|
||||
addPinStateSelect(id, choice, enabled);
|
||||
}
|
||||
|
||||
void addPinStateSelect(const String& name, int choice, bool enabled)
|
||||
{
|
||||
String options[4] = { F("Default"), F("Output Low"), F("Output High"), F("Input") };
|
||||
addSelector(name, 4, options, NULL, NULL, choice, false, enabled);
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
// Add a IP Access Control select dropdown list
|
||||
@@ -2584,15 +2581,15 @@ void addFormPinSelectI2C(const String& label, const String& id, int choice)
|
||||
//********************************************************************************
|
||||
// Add a GPIO pin select dropdown list for 8266, 8285 or ESP32
|
||||
//********************************************************************************
|
||||
String createGPIO_label(int gpio, int pinnr, bool input, bool output, bool warning) {
|
||||
if (gpio < 0) return F("- None -");
|
||||
String createGPIO_label(int gpio_pin, int nodemcu_pinnr, bool input, bool output, bool warning) {
|
||||
if (gpio_pin < 0) return F("- None -");
|
||||
String result;
|
||||
result.reserve(24);
|
||||
result = F("GPIO-");
|
||||
result += gpio;
|
||||
if (pinnr >= 0) {
|
||||
result += gpio_pin;
|
||||
if (nodemcu_pinnr >= 0) {
|
||||
result += F(" (D");
|
||||
result += pinnr;
|
||||
result += nodemcu_pinnr;
|
||||
result += ')';
|
||||
}
|
||||
if (input != output) {
|
||||
@@ -2603,10 +2600,10 @@ String createGPIO_label(int gpio, int pinnr, bool input, bool output, bool warni
|
||||
result += ' ';
|
||||
result += F(HTML_SYMBOL_WARNING);
|
||||
}
|
||||
bool serialPinConflict = (Settings.UseSerial && (gpio == 1 || gpio == 3));
|
||||
bool serialPinConflict = (Settings.UseSerial && (gpio_pin == 1 || gpio_pin == 3));
|
||||
if (serialPinConflict) {
|
||||
if (gpio == 1) { result += F(" TX0"); }
|
||||
if (gpio == 3) { result += F(" RX0"); }
|
||||
if (gpio_pin == 1) { result += F(" TX0"); }
|
||||
if (gpio_pin == 3) { result += F(" RX0"); }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2622,18 +2619,18 @@ void addPinSelect(boolean forI2C, String name, int choice)
|
||||
String * gpio_labels = new String[NR_ITEMS_PIN_DROPDOWN];
|
||||
int * gpio_numbers = new int[NR_ITEMS_PIN_DROPDOWN];
|
||||
|
||||
// At i == 0 && gpio == -1, add the "- None -" option first
|
||||
// At i == 0 && gpio_pin == -1, add the "- None -" option first
|
||||
int i = 0;
|
||||
int gpio = -1;
|
||||
while (i < NR_ITEMS_PIN_DROPDOWN && gpio <= MAX_GPIO) {
|
||||
int pinnr = -1;
|
||||
int gpio_pin = -1;
|
||||
while (i < NR_ITEMS_PIN_DROPDOWN && gpio_pin <= MAX_GPIO) {
|
||||
int nodemcu_pinnr = -1;
|
||||
bool input, output, warning;
|
||||
if (getGpioInfo(gpio, pinnr, input, output, warning) || i == 0) {
|
||||
gpio_labels[i] = createGPIO_label(gpio, pinnr, input, output, warning);
|
||||
gpio_numbers[i] = gpio;
|
||||
if (getGpioInfo(gpio_pin, nodemcu_pinnr, input, output, warning) || i == 0) {
|
||||
gpio_labels[i] = createGPIO_label(gpio_pin, nodemcu_pinnr, input, output, warning);
|
||||
gpio_numbers[i] = gpio_pin;
|
||||
++i;
|
||||
}
|
||||
++gpio;
|
||||
++gpio_pin;
|
||||
}
|
||||
renderHTMLForPinSelect(gpio_labels, gpio_numbers, forI2C, name, choice, NR_ITEMS_PIN_DROPDOWN);
|
||||
delete[] gpio_numbers;
|
||||
@@ -3735,7 +3732,7 @@ void handle_pinstates() {
|
||||
html_table_header(F("Monitor"));
|
||||
html_table_header(F("Command"));
|
||||
html_table_header("Init");
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it)
|
||||
for (auto it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it)
|
||||
{
|
||||
html_TR_TD(); TXBuffer += "P";
|
||||
const uint16_t plugin = getPluginFromKey(it->first);
|
||||
@@ -3764,7 +3761,7 @@ void handle_pinstates() {
|
||||
html_TD();
|
||||
TXBuffer += it->second.command;
|
||||
html_TD();
|
||||
TXBuffer += it->second.init;
|
||||
TXBuffer += it->second.portstatus_init;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-2
File diff suppressed because one or more lines are too long
+88
-219
@@ -38,18 +38,6 @@ TaskDevicePluginConfigLong settings:
|
||||
#define PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH 0
|
||||
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW 1
|
||||
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH 2
|
||||
#define PLUGIN_001_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_001_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_001_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_001_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_001_DC_DISABLED 0
|
||||
#define PLUGIN_001_DC_LOW 1
|
||||
#define PLUGIN_001_DC_HIGH 2
|
||||
#define PLUGIN_001_DC_BOTH 3
|
||||
#define PLUGIN_001_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_001_LONGPRESS_LOW 1
|
||||
#define PLUGIN_001_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_001_LONGPRESS_BOTH 3
|
||||
|
||||
|
||||
// Needed for the GPIO handling.
|
||||
@@ -137,49 +125,13 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
int buttonOptionValues[3] = {PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH, PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW, PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH};
|
||||
addFormSelector(F("Switch Button Type"), F("p001_button"), 3, buttonOptions, buttonOptionValues, choice);
|
||||
|
||||
addFormCheckBox(F("Send Boot state"),F("p001_boot"),
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3]);
|
||||
addSendBootStateForm(event->TaskIndex, 3);
|
||||
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
addAdvancedEventManagementSubHeader();
|
||||
|
||||
addFormNumericBox(F("De-bounce (ms)"), F("p001_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
|
||||
|
||||
//set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_001_DC_DISABLED, PLUGIN_001_DC_LOW, PLUGIN_001_DC_HIGH,PLUGIN_001_DC_BOTH};
|
||||
|
||||
addFormSelector(F("Doubleclick event"), F("p001_dc"), 4, buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("p001_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_001_DOUBLECLICK_MIN_INTERVAL, PLUGIN_001_DOUBLECLICK_MAX_INTERVAL);
|
||||
|
||||
//set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
int buttonLPValues[4] = {PLUGIN_001_LONGPRESS_DISABLED, PLUGIN_001_LONGPRESS_LOW, PLUGIN_001_LONGPRESS_HIGH,PLUGIN_001_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F("p001_lp"), 4, buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"), F("p001_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_001_LONGPRESS_MIN_INTERVAL, PLUGIN_001_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
addFormCheckBox(F("Use Safe Button (slower)"), F("p001_sb"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]));
|
||||
|
||||
//TO-DO: add Extra-Long Press event
|
||||
//addFormCheckBox(F("Extra-Longpress event (20 & 21)"), F("p001_elp"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
//addFormNumericBox(F("Extra-Longpress min. interval (ms)"), F("p001_elpmininterval"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][2], 500, 2000);
|
||||
addDebounceForm(event->TaskIndex);
|
||||
addDoubleClickEventForm(event->TaskIndex);
|
||||
addLongPressEventForm(event->TaskIndex);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -195,30 +147,12 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("p001_button"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = isFormItemChecked(F("p001_boot"));
|
||||
saveSendBootStateForm(event->TaskIndex, 3);
|
||||
saveDebounceForm(event->TaskIndex);
|
||||
saveDoubleClickEventForm(event->TaskIndex);
|
||||
saveLongPressEventForm(event->TaskIndex);
|
||||
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("p001_debounce"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("p001_dc"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("p001_dcmaxinterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("p001_lp"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("p001_lpmininterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = isFormItemChecked(F("p001_sb"));
|
||||
|
||||
//TO-DO: add Extra-Long Press event
|
||||
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] = isFormItemChecked(F("p001_elp"));
|
||||
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][2] = getFormItemInt(F("p001_elpmininterval"));
|
||||
|
||||
//check if a task has been edited and remove 'task' bit from the previous pin
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (it->second.previousTask == event->TaskIndex && getPluginFromKey(it->first)==PLUGIN_ID_001) {
|
||||
globalMapPortStatus[it->first].previousTask = -1;
|
||||
removeTaskFromPort(it->first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_globalMapPortStatus_onSave(event->TaskIndex, PLUGIN_ID_000);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -226,15 +160,14 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
//apply INIT only if PORT is in range. Do not start INIT if port not set in the device page.
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex] >= 0 && Settings.TaskDevicePin1[event->TaskIndex] <= PIN_D_MAX)
|
||||
if (checkValidGpioPin(Settings.TaskDevicePin1[event->TaskIndex]))
|
||||
{
|
||||
portStatusStruct newStatus;
|
||||
const uint32_t key = createInternalGpioKey(Settings.TaskDevicePin1[event->TaskIndex]);
|
||||
//Read current status or create empty if it does not exist
|
||||
newStatus = globalMapPortStatus[key];
|
||||
portStatusStruct newStatus = globalMapPortStatus[key];
|
||||
|
||||
// read and store current state to prevent switching at boot time
|
||||
newStatus.state = read_GPIO_state(event);
|
||||
newStatus.updatePinState(read_GPIO_state(event));
|
||||
newStatus.output = newStatus.state;
|
||||
newStatus.task++; // add this GPIO/port as a task
|
||||
|
||||
@@ -249,9 +182,9 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
// if boot state must be send, inverse default state
|
||||
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][3])
|
||||
if (hlp_getMustSendBootState(event->TaskIndex, 3))
|
||||
{
|
||||
newStatus.state = !newStatus.state;
|
||||
newStatus.updatePinState(!newStatus.state);
|
||||
newStatus.output = !newStatus.output;
|
||||
}
|
||||
|
||||
@@ -263,24 +196,20 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
|
||||
// counters = 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0; //doubleclick counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3]=0; //safebutton counter
|
||||
hlp_setDoubleClickCounter(event->TaskIndex, 0); //doubleclick counter
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0); //safebutton counter
|
||||
|
||||
//used to track if LP has fired
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
|
||||
hlp_setLongPressFired(event->TaskIndex, false);
|
||||
|
||||
//store millis for debounce, doubleclick and long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
|
||||
unsigned long currentTime = millis();
|
||||
hlp_setClicktimeDebounce(event->TaskIndex, currentTime); //debounce timer
|
||||
hlp_setClicktimeDoubleClick(event->TaskIndex, currentTime); //doubleclick timer
|
||||
hlp_setClicktimeLongpress(event->TaskIndex, currentTime); //longpress timer
|
||||
|
||||
//set minimum value for doubleclick MIN interval speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
//set minimum value for longpress MIN interval speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
|
||||
setDoubleClickMinInterval(event->TaskIndex);
|
||||
setLongPressMinInterval(event->TaskIndex);
|
||||
|
||||
savePortStatus(key,newStatus);
|
||||
}
|
||||
@@ -308,18 +237,18 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_UNCONDITIONAL_POLL:
|
||||
{
|
||||
// port monitoring, generates an event by rule command 'monitor,gpio,port#'
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if ((it->second.monitor || it->second.command || it->second.init) && getPluginFromKey(it->first)==PLUGIN_ID_001) {
|
||||
// port monitoring, generates an event by rule command 'monitor_gpio,port#'
|
||||
for (auto it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (it->second.mustPollGpioState() && getPluginFromKey(it->first)==PLUGIN_ID_000 ) {
|
||||
const uint16_t port = getPortFromKey(it->first);
|
||||
byte state = read_GPIO_state(port, it->second.mode);
|
||||
if (it->second.state != state) {
|
||||
if (!it->second.task) it->second.state = state; //do not update state if task flag=1 otherwise it will not be picked up by 10xSEC function
|
||||
bool gpio_state = read_GPIO_state(port, it->second.mode);
|
||||
if (it->second.getPinState() != gpio_state) {
|
||||
if (!it->second.task) it->second.updatePinState(gpio_state); //do not update state if task flag=1 otherwise it will not be picked up by 10xSEC function
|
||||
if (it->second.monitor) {
|
||||
String eventString = F("GPIO#");
|
||||
eventString += port;
|
||||
eventString += '=';
|
||||
eventString += state;
|
||||
eventString += static_cast<int>(gpio_state);
|
||||
rulesProcessing(eventString);
|
||||
}
|
||||
}
|
||||
@@ -330,11 +259,15 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
const boolean state = read_GPIO_state(event);
|
||||
const bool gpio_state = read_GPIO_state(event);
|
||||
bool needToSendEvent = false;
|
||||
bool sendState = gpio_state;
|
||||
byte output_value = gpio_state;
|
||||
|
||||
|
||||
/**************************************************************************\
|
||||
20181009 - @giig1967g: new doubleclick logic is:
|
||||
if there is a 'state' change, check debounce period.
|
||||
if there is a 'gpio_state' change, check debounce period.
|
||||
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
|
||||
0: start counting
|
||||
@@ -352,66 +285,44 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
//long timerstats = millis();
|
||||
|
||||
//Bug fixed: avoid 10xSEC in case of a non-fully configured device (no GPIO defined yet)
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex]>=0 && Settings.TaskDevicePin1[event->TaskIndex]<=PIN_D_MAX) {
|
||||
|
||||
portStatusStruct currentStatus;
|
||||
if (checkValidGpioPin(Settings.TaskDevicePin1[event->TaskIndex])) {
|
||||
const uint32_t key = createInternalGpioKey(Settings.TaskDevicePin1[event->TaskIndex]);
|
||||
//WARNING operator [],creates an entry in map if key doesn't exist:
|
||||
currentStatus = globalMapPortStatus[key];
|
||||
portStatusStruct currentStatus = globalMapPortStatus[key];
|
||||
|
||||
//CASE 1: using SafeButton, so wait 1 more 100ms cycle to acknowledge the status change
|
||||
//QUESTION: MAYBE IT'S BETTER TO WAIT 2 CYCLES??
|
||||
if (round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]) && state != currentStatus.state && Settings.TaskDevicePluginConfigLong[event->TaskIndex][3]==0)
|
||||
const bool gpio_state_changed = gpio_state != currentStatus.getPinState();
|
||||
if (round(hlp_getUseSafeButton(event->TaskIndex)) && gpio_state_changed && hlp_getSafebuttonCounter(event->TaskIndex) == 0)
|
||||
{
|
||||
addLog(LOG_LEVEL_DEBUG,"SW :SafeButton activated")
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 1;
|
||||
addLog(LOG_LEVEL_DEBUG, F("SW :SafeButton activated"));
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 1);
|
||||
}
|
||||
//CASE 2: not using SafeButton, or already waited 1 more 100ms cycle, so proceed.
|
||||
else if (state != currentStatus.state)
|
||||
else if (gpio_state_changed)
|
||||
{
|
||||
// FIXME TD-er: Temporary counters and states should not be in settings.
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
|
||||
//reset timer for long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
|
||||
|
||||
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
|
||||
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
hlp_resetLongPressTimer(event->TaskIndex);
|
||||
if (hlp_debounceTimoutPassed(event->TaskIndex)) //de-bounce check
|
||||
{
|
||||
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
|
||||
}
|
||||
hlp_processDoubleClick(event->TaskIndex, gpio_state);
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
|
||||
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
|
||||
#undef DC
|
||||
#undef COUNTER
|
||||
|
||||
currentStatus.state = state;
|
||||
const boolean currentOutputState = currentStatus.output;
|
||||
boolean new_outputState = currentOutputState;
|
||||
currentStatus.updatePinState(gpio_state);
|
||||
const bool currentOutputState = currentStatus.output == 1;
|
||||
bool new_outputState = currentOutputState;
|
||||
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
|
||||
{
|
||||
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
|
||||
new_outputState = state;
|
||||
new_outputState = gpio_state;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
|
||||
if (!state)
|
||||
if (!gpio_state)
|
||||
new_outputState = !currentOutputState;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
|
||||
if (state)
|
||||
if (gpio_state)
|
||||
new_outputState = !currentOutputState;
|
||||
break;
|
||||
}
|
||||
@@ -419,19 +330,10 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
// send if output needs to be changed
|
||||
if (currentOutputState != new_outputState)
|
||||
{
|
||||
byte output_value;
|
||||
needToSendEvent = true;
|
||||
currentStatus.output = new_outputState;
|
||||
boolean sendState = new_outputState;
|
||||
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
sendState = new_outputState;
|
||||
output_value = hlp_getOutputValue_updateSendState(event->TaskIndex, sendState);
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
|
||||
if (sendState) {
|
||||
@@ -440,105 +342,70 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
event->sensorType = SENSOR_TYPE_DIMMER;
|
||||
}
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("SW : GPIO=");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
|
||||
hlp_setClicktimeDebounce(event->TaskIndex, millis());
|
||||
}
|
||||
savePortStatus(key,currentStatus);
|
||||
savePortStatus(key, currentStatus);
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
|
||||
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
|
||||
|
||||
//CASE 3: status unchanged. Checking longpress:
|
||||
//Check if LP is enabled and if LP has not fired yet
|
||||
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
|
||||
|
||||
#undef LP
|
||||
#undef FIRED
|
||||
else if (hlp_LongPressEnabled_and_notFired(event->TaskIndex, gpio_state)) {
|
||||
|
||||
/**************************************************************************\
|
||||
20181009 - @giig1967g: new longpress logic is:
|
||||
if there is no 'state' change, check if longpress interval reached
|
||||
if there is no 'gpio_state' change, check if longpress interval reached
|
||||
When reached send longpress event.
|
||||
Returned Event value = state + 10
|
||||
So if state = 0 => EVENT longpress = 10
|
||||
if state = 1 => EVENT longpress = 11
|
||||
Returned Event value = gpio_state + 10
|
||||
So if gpio_state = 0 => EVENT longpress = 10
|
||||
if gpio_state = 1 => EVENT longpress = 11
|
||||
So we can trigger longpress for high or low contact
|
||||
|
||||
In rules this can be checked:
|
||||
on Button#Switch=10 do //will fire if longpress when state = 0
|
||||
on Button#Switch=11 do //will fire if longpress when state = 1
|
||||
on Button#Switch=10 do //will fire if longpress when gpio_state = 0
|
||||
on Button#Switch=11 do //will fire if longpress when gpio_state = 1
|
||||
\**************************************************************************/
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
|
||||
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
|
||||
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
|
||||
if (hlp_LongPressIntervalReached(event->TaskIndex))
|
||||
{
|
||||
byte output_value;
|
||||
byte needToSendEvent = false;
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true;
|
||||
hlp_setLongPressFired(event->TaskIndex, true);
|
||||
|
||||
needToSendEvent = false;
|
||||
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
|
||||
{
|
||||
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
|
||||
needToSendEvent = true;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
|
||||
if (!state)
|
||||
if (!gpio_state)
|
||||
needToSendEvent = true;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
|
||||
if (state)
|
||||
if (gpio_state)
|
||||
needToSendEvent = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (needToSendEvent) {
|
||||
boolean sendState = state;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
output_value = sendState ? 11 : 10;
|
||||
//output_value = output_value + 10;
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("SW : LongPress: GPIO= ");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
sendState = gpio_state;
|
||||
output_value = hlp_getOutputValue_updateSendState(event->TaskIndex, sendState);
|
||||
}
|
||||
savePortStatus(key,currentStatus);
|
||||
savePortStatus(key, currentStatus);
|
||||
}
|
||||
} else {
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
}
|
||||
}
|
||||
if (needToSendEvent) {
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
hlp_logState_and_Output(event->TaskIndex, gpio_state, sendState, output_value, PLUGIN_ID_000);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct gpio_state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -593,7 +460,7 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
const uint32_t key = createInternalGpioKey(event->Par1);
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
savePortStatus(key,tempStatus);
|
||||
break;
|
||||
@@ -620,4 +487,6 @@ byte P001_getSwitchType(struct EventStruct *event) {
|
||||
return choice;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // USES_P001
|
||||
|
||||
+78
-201
@@ -32,18 +32,6 @@ TaskDevicePluginConfigLong settings:
|
||||
#define PLUGIN_ID_009 9
|
||||
#define PLUGIN_NAME_009 "Switch input - MCP23017"
|
||||
#define PLUGIN_VALUENAME1_009 "Switch"
|
||||
#define PLUGIN_009_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_009_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_009_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_009_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_009_DC_DISABLED 0
|
||||
#define PLUGIN_009_DC_LOW 1
|
||||
#define PLUGIN_009_DC_HIGH 2
|
||||
#define PLUGIN_009_DC_BOTH 3
|
||||
#define PLUGIN_009_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_009_LONGPRESS_LOW 1
|
||||
#define PLUGIN_009_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_009_LONGPRESS_BOTH 3
|
||||
|
||||
boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
@@ -90,45 +78,13 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
globalMapPortStatus[key].previousTask = event->TaskIndex;
|
||||
}
|
||||
|
||||
addFormCheckBox(F("Send Boot state") ,F("p009_boot"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
addSendBootStateForm(event->TaskIndex, 0);
|
||||
|
||||
//@giig1967-20181022
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
addAdvancedEventManagementSubHeader();
|
||||
|
||||
addFormNumericBox(F("De-bounce (ms)"), F("p009_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
|
||||
|
||||
//set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_009_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_009_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_009_DC_DISABLED, PLUGIN_009_DC_LOW, PLUGIN_009_DC_HIGH,PLUGIN_009_DC_BOTH};
|
||||
addFormSelector(F("Doubleclick event"), F("p009_dc"), 4, buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("p009_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_009_DOUBLECLICK_MIN_INTERVAL, PLUGIN_009_DOUBLECLICK_MAX_INTERVAL);
|
||||
|
||||
//set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_009_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_009_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
|
||||
int buttonLPValues[4] = {PLUGIN_009_LONGPRESS_DISABLED, PLUGIN_009_LONGPRESS_LOW, PLUGIN_009_LONGPRESS_HIGH,PLUGIN_009_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F("p009_lp"), 4, buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"), F("p009_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_009_LONGPRESS_MIN_INTERVAL, PLUGIN_009_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
addFormCheckBox(F("Use Safe Button (slower)"), F("p009_sb"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]));
|
||||
addDebounceForm(event->TaskIndex);
|
||||
addDoubleClickEventForm(event->TaskIndex);
|
||||
addLongPressEventForm(event->TaskIndex);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -136,27 +92,12 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = isFormItemChecked(F("p009_boot"));
|
||||
saveSendBootStateForm(event->TaskIndex, 0);
|
||||
saveDebounceForm(event->TaskIndex);
|
||||
saveDoubleClickEventForm(event->TaskIndex);
|
||||
saveLongPressEventForm(event->TaskIndex);
|
||||
|
||||
//@giig1967-20181022
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("p009_debounce"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("p009_dc"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("p009_dcmaxinterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("p009_lp"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("p009_lpmininterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = isFormItemChecked(F("p009_sb"));
|
||||
|
||||
//check if a task has been edited and remove task flag from the previous pin
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (it->second.previousTask == event->TaskIndex && getPluginFromKey(it->first)==PLUGIN_ID_009) {
|
||||
globalMapPortStatus[it->first].previousTask = -1;
|
||||
removeTaskFromPort(it->first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_globalMapPortStatus_onSave(event->TaskIndex, PLUGIN_ID_009);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -176,9 +117,9 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
// read and store current state to prevent switching at boot time
|
||||
// "state" could be -1, 0 or 1
|
||||
newStatus.state = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
newStatus.updatePinState(Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]));
|
||||
newStatus.output = newStatus.state;
|
||||
(newStatus.state == -1) ? newStatus.mode = PIN_MODE_OFFLINE : newStatus.mode = PIN_MODE_INPUT_PULLUP; // @giig1967g: if it is in the device list we assume it's an input pin
|
||||
(newStatus.portStateError()) ? newStatus.mode = PIN_MODE_OFFLINE : newStatus.mode = PIN_MODE_INPUT_PULLUP; // @giig1967g: if it is in the device list we assume it's an input pin
|
||||
newStatus.task++; // add this GPIO/port as a task
|
||||
|
||||
// @giig1967g-20181022: set initial UserVar of the switch
|
||||
@@ -190,28 +131,24 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
// if boot state must be send, inverse default state
|
||||
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
|
||||
newStatus.state = !newStatus.state;
|
||||
if (hlp_getMustSendBootState(event->TaskIndex, 0))
|
||||
newStatus.updatePinState(!newStatus.state);
|
||||
// FIXME TD-er: Why not set the output state, like is done in P001?
|
||||
|
||||
// @giig1967g-20181022: doubleclick counter = 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0; //doubleclick counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3]=0; //safebutton counter
|
||||
hlp_setDoubleClickCounter(event->TaskIndex, 0); //doubleclick counter
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0); //safebutton counter
|
||||
|
||||
// @giig1967g-20181022: used to track if LP has fired
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
|
||||
hlp_setLongPressFired(event->TaskIndex, false);
|
||||
|
||||
// @giig1967g-20181022: store millis for debounce, doubleclick and long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
|
||||
hlp_setClicktimeDebounce(event->TaskIndex, millis()); //debounce timer
|
||||
hlp_setClicktimeDoubleClick(event->TaskIndex, millis()); //doubleclick timer
|
||||
hlp_setClicktimeLongpress(event->TaskIndex, millis()); //longpress timer
|
||||
|
||||
// @giig1967g-20181022: set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_009_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_009_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
// @giig1967g-20181022: set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_009_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_009_LONGPRESS_MIN_INTERVAL;
|
||||
setDoubleClickMinInterval(event->TaskIndex);
|
||||
setLongPressMinInterval(event->TaskIndex);
|
||||
|
||||
//setPinState(PLUGIN_ID_009, Settings.TaskDevicePort[event->TaskIndex], PIN_MODE_INPUT, switchstate[event->TaskIndex]);
|
||||
savePortStatus(key,newStatus);
|
||||
@@ -223,14 +160,14 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_UNCONDITIONAL_POLL:
|
||||
{
|
||||
// port monitoring, generates an event by rule command 'monitor,pcf,port#'
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if ((it->second.monitor || it->second.command || it->second.init) && getPluginFromKey(it->first)==PLUGIN_ID_009) {
|
||||
for (auto it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (it->second.mustPollGpioState() && getPluginFromKey(it->first)==PLUGIN_ID_009) {
|
||||
const uint16_t port = getPortFromKey(it->first);
|
||||
int8_t state = Plugin_009_Read(port);
|
||||
if (it->second.state != state) {
|
||||
if (it->second.mode == PIN_MODE_OFFLINE) it->second.mode=PIN_MODE_UNDEFINED; //changed from offline to online
|
||||
if (state == -1) it->second.mode=PIN_MODE_OFFLINE; //changed from online to offline
|
||||
if (!it->second.task) it->second.state = state; //do not update state if task flag=1 otherwise it will not be picked up by 10xSEC function
|
||||
if (!it->second.task) it->second.updatePinState(state); //do not update state if task flag=1 otherwise it will not be picked up by 10xSEC function
|
||||
if (it->second.monitor) {
|
||||
String eventString = F("MCP#");
|
||||
eventString += port;
|
||||
@@ -247,6 +184,12 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
const int8_t state = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
const bool gpio_state = state == 1;
|
||||
bool needToSendEvent = false;
|
||||
bool sendState = gpio_state;
|
||||
byte output_value = gpio_state;
|
||||
|
||||
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new doubleclick logic is:
|
||||
if there is a 'state' change, check debounce period.
|
||||
@@ -261,101 +204,48 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
In rules this can be checked:
|
||||
on Button#Switch=3 do //will fire if doubleclick
|
||||
\**************************************************************************/
|
||||
portStatusStruct currentStatus;
|
||||
const uint32_t key = createKey(PLUGIN_ID_009,Settings.TaskDevicePort[event->TaskIndex]);
|
||||
//WARNING operator [],creates an entry in map if key doesn't exist:
|
||||
currentStatus = globalMapPortStatus[key];
|
||||
portStatusStruct currentStatus = globalMapPortStatus[key];
|
||||
|
||||
const bool gpio_state_changed = state != currentStatus.state;
|
||||
|
||||
//Bug fixed: avoid 10xSEC in case of a non-fully configured device (no port defined yet)
|
||||
if (state != -1 && Settings.TaskDevicePort[event->TaskIndex]>=0) {
|
||||
|
||||
//CASE 1: using SafeButton, so wait 1 more 100ms cycle to acknowledge the status change
|
||||
if (round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]) && state != currentStatus.state && Settings.TaskDevicePluginConfigLong[event->TaskIndex][3]==0)
|
||||
if (round(hlp_getUseSafeButton(event->TaskIndex)) && gpio_state_changed && hlp_getSafebuttonCounter(event->TaskIndex) == 0)
|
||||
{
|
||||
addLog(LOG_LEVEL_DEBUG,"MCP :SafeButton activated")
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 1;
|
||||
addLog(LOG_LEVEL_DEBUG, F("MCP :SafeButton activated"));
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 1);
|
||||
}
|
||||
//CASE 2: not using SafeButton, or already waited 1 more 100ms cycle, so proceed.
|
||||
else if (state != currentStatus.state)
|
||||
else if (gpio_state_changed)
|
||||
{
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
|
||||
//@giig1967g20181022: reset timer for long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
|
||||
|
||||
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
|
||||
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
hlp_resetLongPressTimer(event->TaskIndex);
|
||||
if (hlp_debounceTimoutPassed(event->TaskIndex)) //de-bounce check
|
||||
{
|
||||
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
|
||||
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
|
||||
#undef DC
|
||||
#undef COUNTER
|
||||
hlp_processDoubleClick(event->TaskIndex, gpio_state);
|
||||
needToSendEvent = true;
|
||||
|
||||
//switchstate[event->TaskIndex] = state;
|
||||
if (currentStatus.mode == PIN_MODE_OFFLINE || currentStatus.mode == PIN_MODE_UNDEFINED) currentStatus.mode = PIN_MODE_INPUT_PULLUP; //changed from offline to online
|
||||
currentStatus.state = state;
|
||||
|
||||
byte output_value;
|
||||
//boolean sendState = switchstate[event->TaskIndex];
|
||||
boolean sendState = currentStatus.state;
|
||||
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("MCP : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state;
|
||||
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
if (currentStatus.mode == PIN_MODE_OFFLINE || currentStatus.mode == PIN_MODE_UNDEFINED) {
|
||||
currentStatus.mode = PIN_MODE_INPUT_PULLUP; //changed from offline to online
|
||||
}
|
||||
currentStatus.updatePinState(state);
|
||||
sendState = currentStatus.getPinState();
|
||||
output_value = hlp_getOutputValue_updateSendState(event->TaskIndex, sendState);
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
|
||||
hlp_setClicktimeDebounce(event->TaskIndex, millis());
|
||||
}
|
||||
savePortStatus(key,currentStatus);
|
||||
savePortStatus(key, currentStatus);
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
|
||||
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
|
||||
|
||||
//check if LP is enabled and if LP has not fired yet
|
||||
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
|
||||
|
||||
#undef LP
|
||||
#undef FIRED
|
||||
//CASE 3: status unchanged. Checking longpress:
|
||||
else if (hlp_LongPressEnabled_and_notFired(event->TaskIndex, gpio_state)) {
|
||||
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new longpress logic is:
|
||||
@@ -371,41 +261,20 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
on Button#Switch=11 do //will fire if longpress when state = 1
|
||||
\**************************************************************************/
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
|
||||
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
|
||||
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
|
||||
if (hlp_LongPressIntervalReached(event->TaskIndex))
|
||||
{
|
||||
byte output_value;
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true; //fired = true
|
||||
|
||||
boolean sendState = state;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
output_value = sendState ? 1 : 0;
|
||||
output_value = output_value + 10;
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("MCP : LongPress: Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
hlp_setLongPressFired(event->TaskIndex, true); //fired = true
|
||||
needToSendEvent = true;
|
||||
sendState = gpio_state;
|
||||
output_value = hlp_getOutputValue_updateSendState(event->TaskIndex, sendState);
|
||||
}
|
||||
} else {
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
}
|
||||
} else if (state != currentStatus.state && state == -1) {
|
||||
} else if (gpio_state_changed && state == -1) {
|
||||
//set UserVar and switchState = -1 and send EVENT to notify user
|
||||
UserVar[event->BaseVarIndex] = state;
|
||||
currentStatus.mode = PIN_MODE_OFFLINE;
|
||||
@@ -417,7 +286,15 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
sendData(event);
|
||||
savePortStatus(key,currentStatus);
|
||||
savePortStatus(key, currentStatus);
|
||||
}
|
||||
if (needToSendEvent) {
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
hlp_logState_and_Output(event->TaskIndex, gpio_state, sendState, output_value, PLUGIN_ID_009);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct gpio_state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
@@ -483,20 +360,20 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (currentState == -1) {
|
||||
tempStatus.mode=PIN_MODE_OFFLINE;
|
||||
tempStatus.state=-1;
|
||||
tempStatus.updatePinState(-1);
|
||||
log = String(F("MCP : GPIO ")) + String(event->Par1) + String(F(" is offline (-1). Cannot set value."));
|
||||
} else if (event->Par2 == 2) { //INPUT
|
||||
// PCF8574 specific: only can read 0/low state, so we must send 1
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_INPUT, 1);
|
||||
tempStatus.mode=PIN_MODE_INPUT_PULLUP;
|
||||
tempStatus.state = currentState;
|
||||
tempStatus.updatePinState(currentState);
|
||||
Plugin_009_Write(event->Par1,1);
|
||||
log = String(F("MCP : GPIO INPUT ")) + String(event->Par1) + String(F(" Set to 1"));
|
||||
} else { // OUTPUT
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
Plugin_009_Write(event->Par1, event->Par2);
|
||||
tempStatus.mode=PIN_MODE_OUTPUT;
|
||||
tempStatus.state=event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
log = String(F("MCP : GPIO OUTPUT ")) + String(event->Par1) + String(F(" Set to ")) + String(event->Par2);
|
||||
}
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
@@ -519,11 +396,11 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (currentState == -1) {
|
||||
tempStatus.mode=PIN_MODE_OFFLINE;
|
||||
tempStatus.state=-1;
|
||||
tempStatus.updatePinState(-1);
|
||||
log = String(F("MCP : GPIO ")) + String(event->Par1) + String(F(" is offline (-1). Cannot set value."));
|
||||
needToSave = true;
|
||||
} else if (tempStatus.mode == PIN_MODE_OUTPUT || tempStatus.mode == PIN_MODE_UNDEFINED) { //toggle only output pins
|
||||
tempStatus.state = !currentState; //toggle current state value
|
||||
tempStatus.updatePinState(!currentState); //toggle current state value
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
Plugin_009_Write(event->Par1, tempStatus.state);
|
||||
log = String(F("MCP : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(tempStatus.state);
|
||||
@@ -555,7 +432,7 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, !event->Par2);
|
||||
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
|
||||
@@ -578,7 +455,7 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
Plugin_009_Write(event->Par1, event->Par2);
|
||||
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
|
||||
@@ -638,7 +515,7 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
const uint32_t key = createKey(PLUGIN_ID_009,event->Par1);
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
savePortStatus(key,tempStatus);
|
||||
break;
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ boolean Plugin_011(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
success = true;
|
||||
const uint32_t key = createKey(PLUGIN_ID_011,event->Par2); //WARNING: 'status' uses Par2 instead of Par1
|
||||
|
||||
|
||||
if (!existPortStatus(key)) //tempStatus.mode == PIN_MODE_OUTPUT) // has been set as output
|
||||
SendStatusOnlyIfNeeded(event->Source, SEARCH_PIN_STATE, key, dummyString, 0);
|
||||
else
|
||||
|
||||
+82
-202
@@ -32,18 +32,6 @@ TaskDevicePluginConfigLong settings:
|
||||
#define PLUGIN_ID_019 19
|
||||
#define PLUGIN_NAME_019 "Switch input - PCF8574"
|
||||
#define PLUGIN_VALUENAME1_019 "Switch"
|
||||
#define PLUGIN_019_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_019_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_019_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_019_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_019_DC_DISABLED 0
|
||||
#define PLUGIN_019_DC_LOW 1
|
||||
#define PLUGIN_019_DC_HIGH 2
|
||||
#define PLUGIN_019_DC_BOTH 3
|
||||
#define PLUGIN_019_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_019_LONGPRESS_LOW 1
|
||||
#define PLUGIN_019_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_019_LONGPRESS_BOTH 3
|
||||
|
||||
boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
@@ -90,44 +78,13 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
globalMapPortStatus[key].previousTask = event->TaskIndex;
|
||||
}
|
||||
|
||||
addFormCheckBox(F("Send Boot state"), F("p019_boot"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
addSendBootStateForm(event->TaskIndex, 0);
|
||||
|
||||
//@giig1967-20181022
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
addAdvancedEventManagementSubHeader();
|
||||
|
||||
addFormNumericBox(F("De-bounce (ms)"), F("p019_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
|
||||
|
||||
//set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_019_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_019_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_019_DC_DISABLED, PLUGIN_019_DC_LOW, PLUGIN_019_DC_HIGH,PLUGIN_019_DC_BOTH};
|
||||
addFormSelector(F("Doubleclick event"), F("p019_dc"), 4, buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("p019_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_019_DOUBLECLICK_MIN_INTERVAL, PLUGIN_019_DOUBLECLICK_MAX_INTERVAL);
|
||||
|
||||
//set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_019_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_019_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
int buttonLPValues[4] = {PLUGIN_019_LONGPRESS_DISABLED, PLUGIN_019_LONGPRESS_LOW, PLUGIN_019_LONGPRESS_HIGH,PLUGIN_019_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F("p019_lp"), 4, buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"), F("p019_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_019_LONGPRESS_MIN_INTERVAL, PLUGIN_019_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
addFormCheckBox(F("Use Safe Button (slower)"), F("p019_sb"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]));
|
||||
addDebounceForm(event->TaskIndex);
|
||||
addDoubleClickEventForm(event->TaskIndex);
|
||||
addLongPressEventForm(event->TaskIndex);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -135,27 +92,12 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = isFormItemChecked(F("p019_boot"));
|
||||
saveSendBootStateForm(event->TaskIndex, 0);
|
||||
saveDebounceForm(event->TaskIndex);
|
||||
saveDoubleClickEventForm(event->TaskIndex);
|
||||
saveLongPressEventForm(event->TaskIndex);
|
||||
|
||||
//@giig1967-20181022
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("p019_debounce"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("p019_dc"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("p019_dcmaxinterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("p019_lp"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("p019_lpmininterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = isFormItemChecked(F("p019_sb"));
|
||||
|
||||
//check if a task has been edited and remove task flag from the previous pin
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (it->second.previousTask == event->TaskIndex && getPluginFromKey(it->first)==PLUGIN_ID_019) {
|
||||
globalMapPortStatus[it->first].previousTask = -1;
|
||||
removeTaskFromPort(it->first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_globalMapPortStatus_onSave(event->TaskIndex, PLUGIN_ID_019);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -172,9 +114,9 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
// read and store current state to prevent switching at boot time
|
||||
// "state" could be -1, 0 or 1
|
||||
newStatus.state = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
newStatus.updatePinState(Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]));
|
||||
newStatus.output = newStatus.state;
|
||||
(newStatus.state == -1) ? newStatus.mode = PIN_MODE_OFFLINE : newStatus.mode = PIN_MODE_INPUT; // @giig1967g: if it is in the device list we assume it's an input pin
|
||||
(newStatus.portStateError()) ? newStatus.mode = PIN_MODE_OFFLINE : newStatus.mode = PIN_MODE_INPUT; // @giig1967g: if it is in the device list we assume it's an input pin
|
||||
newStatus.task++; // add this GPIO/port as a task
|
||||
|
||||
// @giig1967g-20181022: set initial UserVar of the switch
|
||||
@@ -186,28 +128,25 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
// if boot state must be send, inverse default state
|
||||
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
|
||||
newStatus.state = !newStatus.state;
|
||||
if (hlp_getMustSendBootState(event->TaskIndex, 0))
|
||||
newStatus.updatePinState(!newStatus.state);
|
||||
// FIXME TD-er: Why not set the output state, like is done in P001?
|
||||
|
||||
|
||||
// @giig1967g-20181022: counter = 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0; //doubleclick counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3]=0; //safebutton counter
|
||||
hlp_setDoubleClickCounter(event->TaskIndex, 0); //doubleclick counter
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0); //safebutton counter
|
||||
|
||||
// @giig1967g-20181022: used to track if LP has fired
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
|
||||
hlp_setLongPressFired(event->TaskIndex, false);
|
||||
|
||||
// @giig1967g-20181022: store millis for debounce, doubleclick and long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
|
||||
hlp_setClicktimeDebounce(event->TaskIndex, millis()); //debounce timer
|
||||
hlp_setClicktimeDoubleClick(event->TaskIndex, millis()); //doubleclick timer
|
||||
hlp_setClicktimeLongpress(event->TaskIndex, millis()); //longpress timer
|
||||
|
||||
// @giig1967g-20181022: set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_019_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_019_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
// @giig1967g-20181022: set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_019_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_019_LONGPRESS_MIN_INTERVAL;
|
||||
setDoubleClickMinInterval(event->TaskIndex);
|
||||
setLongPressMinInterval(event->TaskIndex);
|
||||
|
||||
//setPinState(PLUGIN_ID_019, Settings.TaskDevicePort[event->TaskIndex], PIN_MODE_INPUT, switchstate[event->TaskIndex]);
|
||||
savePortStatus(key,newStatus);
|
||||
@@ -220,14 +159,14 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_UNCONDITIONAL_POLL:
|
||||
{
|
||||
// port monitoring, generates an event by rule command 'monitor,pcf,port#'
|
||||
for (std::map<uint32_t,portStatusStruct>::iterator it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (getPluginFromKey(it->first)==PLUGIN_ID_019 && (it->second.monitor || it->second.command || it->second.init)) {
|
||||
for (auto it=globalMapPortStatus.begin(); it!=globalMapPortStatus.end(); ++it) {
|
||||
if (getPluginFromKey(it->first)==PLUGIN_ID_019 && it->second.mustPollGpioState()) {
|
||||
const uint16_t port = getPortFromKey(it->first);
|
||||
int8_t state = Plugin_019_Read(port);
|
||||
if (it->second.state != state) {
|
||||
if (it->second.mode == PIN_MODE_OFFLINE) it->second.mode=PIN_MODE_UNDEFINED; //changed from offline to online
|
||||
if (state == -1) it->second.mode=PIN_MODE_OFFLINE; //changed from online to offline
|
||||
if (!it->second.task) it->second.state = state; //do not update state if task flag=1 otherwise it will not be picked up by 10xSEC function
|
||||
if (!it->second.task) it->second.updatePinState(state); //do not update state if task flag=1 otherwise it will not be picked up by 10xSEC function
|
||||
if (it->second.monitor) {
|
||||
String eventString = F("PCF#");
|
||||
eventString += port;
|
||||
@@ -244,6 +183,12 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
const int8_t state = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
const bool gpio_state = state == 1;
|
||||
bool needToSendEvent = false;
|
||||
bool sendState = gpio_state;
|
||||
byte output_value = gpio_state;
|
||||
|
||||
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new doubleclick logic is:
|
||||
if there is a 'state' change, check debounce period.
|
||||
@@ -258,102 +203,50 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
In rules this can be checked:
|
||||
on Button#Switch=3 do //will fire if doubleclick
|
||||
\**************************************************************************/
|
||||
portStatusStruct currentStatus;
|
||||
const uint32_t key = createKey(PLUGIN_ID_019,Settings.TaskDevicePort[event->TaskIndex]);
|
||||
//WARNING operator [],creates an entry in map if key doesn't exist:
|
||||
currentStatus = globalMapPortStatus[key];
|
||||
portStatusStruct currentStatus = globalMapPortStatus[key];
|
||||
|
||||
const bool gpio_state_changed = state != currentStatus.state;
|
||||
|
||||
//Bug fixed: avoid 10xSEC in case of a non-fully configured device (no port defined yet)
|
||||
if (state != -1 && Settings.TaskDevicePort[event->TaskIndex]>=0) {
|
||||
|
||||
//CASE 1: using SafeButton, so wait 1 more 100ms cycle to acknowledge the status change
|
||||
//QUESTION: MAYBE IT'S BETTER TO WAIT 2 CYCLES??
|
||||
if (round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]) && state != currentStatus.state && Settings.TaskDevicePluginConfigLong[event->TaskIndex][3]==0)
|
||||
if (round(hlp_getUseSafeButton(event->TaskIndex)) && gpio_state_changed && hlp_getSafebuttonCounter(event->TaskIndex) == 0)
|
||||
{
|
||||
addLog(LOG_LEVEL_DEBUG,"PCF :SafeButton activated")
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 1;
|
||||
addLog(LOG_LEVEL_DEBUG, F("PCF :SafeButton activated"));
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 1);
|
||||
}
|
||||
//CASE 2: not using SafeButton, or already waited 1 more 100ms cycle, so proceed.
|
||||
else if (state != currentStatus.state)
|
||||
else if (gpio_state_changed)
|
||||
{
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
|
||||
//@giig1967g20181022: reset timer for long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
|
||||
|
||||
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
|
||||
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
hlp_resetLongPressTimer(event->TaskIndex);
|
||||
if (hlp_debounceTimoutPassed(event->TaskIndex)) //de-bounce check
|
||||
{
|
||||
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
|
||||
hlp_processDoubleClick(event->TaskIndex, gpio_state);
|
||||
needToSendEvent = true;
|
||||
|
||||
if (currentStatus.mode == PIN_MODE_OFFLINE || currentStatus.mode == PIN_MODE_UNDEFINED)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
|
||||
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
|
||||
#undef DC
|
||||
#undef COUNTER
|
||||
|
||||
//switchstate[event->TaskIndex] = state;
|
||||
if (currentStatus.mode == PIN_MODE_OFFLINE || currentStatus.mode == PIN_MODE_UNDEFINED) currentStatus.mode = PIN_MODE_INPUT; //changed from offline to online
|
||||
currentStatus.state = state;
|
||||
|
||||
byte output_value;
|
||||
//boolean sendState = switchstate[event->TaskIndex];
|
||||
boolean sendState = currentStatus.state;
|
||||
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("PCF : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state;
|
||||
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
currentStatus.mode = PIN_MODE_INPUT; //changed from offline to online
|
||||
}
|
||||
currentStatus.updatePinState(state);
|
||||
sendState = currentStatus.getPinState();
|
||||
output_value = hlp_getOutputValue_updateSendState(event->TaskIndex, sendState);
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
|
||||
hlp_setClicktimeDebounce(event->TaskIndex, millis());
|
||||
}
|
||||
savePortStatus(key,currentStatus);
|
||||
savePortStatus(key, currentStatus);
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
|
||||
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
|
||||
//CASE 3: status unchanged. Checking longpress:
|
||||
else if (hlp_LongPressEnabled_and_notFired(event->TaskIndex, gpio_state)) {
|
||||
|
||||
//check if LP is enabled and if LP has not fired yet
|
||||
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
|
||||
|
||||
#undef LP
|
||||
#undef FIRED
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new longpress logic is:
|
||||
if there is no 'state' change, check if longpress interval reached
|
||||
@@ -368,45 +261,24 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
on Button#Switch=11 do //will fire if longpress when state = 1
|
||||
\**************************************************************************/
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
|
||||
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
|
||||
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
|
||||
if (hlp_LongPressIntervalReached(event->TaskIndex))
|
||||
{
|
||||
byte output_value;
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true; //fired = true
|
||||
|
||||
boolean sendState = state;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
output_value = sendState ? 1 : 0;
|
||||
output_value = output_value + 10;
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("PCF : LongPress: Port= ");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
hlp_setLongPressFired(event->TaskIndex, true); //fired = true
|
||||
needToSendEvent = true;
|
||||
sendState = gpio_state;
|
||||
output_value = hlp_getOutputValue_updateSendState(event->TaskIndex, sendState);
|
||||
}
|
||||
} else {
|
||||
// Reset SafeButton counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][3] = 0;
|
||||
hlp_setSafebuttonCounter(event->TaskIndex, 0);
|
||||
}
|
||||
} else if (state != currentStatus.state && state == -1) {
|
||||
} else if (gpio_state_changed && state == -1) {
|
||||
//set UserVar and switchState = -1 and send EVENT to notify user
|
||||
UserVar[event->BaseVarIndex] = state;
|
||||
//switchstate[event->TaskIndex] = state;
|
||||
currentStatus.state = state;
|
||||
currentStatus.updatePinState(state);
|
||||
currentStatus.mode = PIN_MODE_OFFLINE;
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("PCF : Port=");
|
||||
@@ -415,7 +287,15 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
sendData(event);
|
||||
savePortStatus(key,currentStatus);
|
||||
savePortStatus(key, currentStatus);
|
||||
}
|
||||
if (needToSendEvent) {
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
hlp_logState_and_Output(event->TaskIndex, gpio_state, sendState, output_value, PLUGIN_ID_019);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct gpio_state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
@@ -481,7 +361,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (currentState == -1) {
|
||||
tempStatus.mode=PIN_MODE_OFFLINE;
|
||||
tempStatus.state=-1;
|
||||
tempStatus.updatePinState(-1);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
log = String(F("PCF : GPIO ")) + String(event->Par1) + String(F(" is offline (-1). Cannot set value."));
|
||||
@@ -489,7 +369,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
// PCF8574 specific: only can read 0/low state, so we must send 1
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_INPUT, 1);
|
||||
tempStatus.mode=PIN_MODE_INPUT;
|
||||
tempStatus.state = currentState;
|
||||
tempStatus.updatePinState(currentState);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
Plugin_019_Write(event->Par1,1);
|
||||
@@ -497,7 +377,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
} else { // OUTPUT
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode=PIN_MODE_OUTPUT;
|
||||
tempStatus.state=event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
Plugin_019_Write(event->Par1, event->Par2);
|
||||
@@ -521,13 +401,13 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (currentState == -1) {
|
||||
tempStatus.mode=PIN_MODE_OFFLINE;
|
||||
tempStatus.state=-1;
|
||||
tempStatus.updatePinState(-1);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
log = String(F("PCF : GPIO ")) + String(event->Par1) + String(F(" is offline (-1). Cannot set value."));
|
||||
needToSave = true;
|
||||
} else if (tempStatus.mode == PIN_MODE_OUTPUT || tempStatus.mode == PIN_MODE_UNDEFINED) { //toggle only output pins
|
||||
tempStatus.state = !currentState; //toggle current state value
|
||||
tempStatus.updatePinState(!currentState); //toggle current state value
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
@@ -554,13 +434,13 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
savePortStatus(key,tempStatus);
|
||||
Plugin_019_Write(event->Par1, event->Par2);
|
||||
delay(event->Par3);
|
||||
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = !event->Par2;
|
||||
tempStatus.updatePinState(!event->Par2);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
Plugin_019_Write(event->Par1, !event->Par2);
|
||||
@@ -583,7 +463,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
//setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.command=1; //set to 1 in order to display the status in the PinStatus page
|
||||
savePortStatus(key,tempStatus);
|
||||
Plugin_019_Write(event->Par1, event->Par2);
|
||||
@@ -642,7 +522,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
const uint32_t key = createKey(PLUGIN_ID_019,event->Par1);
|
||||
tempStatus = globalMapPortStatus[key];
|
||||
|
||||
tempStatus.state = event->Par2;
|
||||
tempStatus.updatePinState(event->Par2);
|
||||
tempStatus.mode = PIN_MODE_OUTPUT;
|
||||
savePortStatus(key,tempStatus);
|
||||
Plugin_019_Write(event->Par1, event->Par2);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define PCA9685_ALLLED_REG (byte)0xFA
|
||||
|
||||
/*
|
||||
is bit flag any bit rapresent the initialization state of PCA9685
|
||||
is bit flag any bit represent the initialization state of PCA9685
|
||||
es: bit 3 is set 1 PCA9685 with address 0X40 + 0x03 is intin
|
||||
*/
|
||||
#define IS_INIT(state, bit) ((state & 1 << bit) == 1 << bit)
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
//##############################################################################
|
||||
//######## Shared functions for all switch input related plugins. ##############
|
||||
//##############################################################################
|
||||
|
||||
#define PLUGIN_HELPER_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_HELPER_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_HELPER_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_HELPER_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_HELPER_DC_DISABLED 0
|
||||
#define PLUGIN_HELPER_DC_LOW 1
|
||||
#define PLUGIN_HELPER_DC_HIGH 2
|
||||
#define PLUGIN_HELPER_DC_BOTH 3
|
||||
#define PLUGIN_HELPER_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_HELPER_LONGPRESS_LOW 1
|
||||
#define PLUGIN_HELPER_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_HELPER_LONGPRESS_BOTH 3
|
||||
|
||||
#define PLUGIN_HELPER_BOOT_STATE_ID "p_hlp_boot"
|
||||
|
||||
#define PLUGIN_HELPER_DEBOUNCE_ID "p_hlp_debounce"
|
||||
|
||||
#define PLUGIN_HELPER_DC_EVENT_ID "p_hlp_dc"
|
||||
#define PLUGIN_HELPER_DC_MAX_INT_ID "p_hlp_dcmaxint"
|
||||
|
||||
#define PLUGIN_HELPER_LP_EVENT_ID "p_hlp_lp"
|
||||
#define PLUGIN_HELPER_LP_MIN_INT_ID "p_hlp_lpminint"
|
||||
#define PLUGIN_HELPER_LP_SAFE_ID "p_hlp_sb"
|
||||
|
||||
|
||||
//#######################################################################################################
|
||||
// PLUGIN_WEBFORM_LOAD & PLUGIN_WEBFORM_SAVE
|
||||
//#######################################################################################################
|
||||
|
||||
void addAdvancedEventManagementSubHeader() {
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
}
|
||||
|
||||
void addSendBootStateForm(byte taskIndex, byte settingsOffset) {
|
||||
addFormCheckBox(F("Send Boot state"), F(PLUGIN_HELPER_BOOT_STATE_ID),
|
||||
Settings.TaskDevicePluginConfig[taskIndex][settingsOffset]);
|
||||
}
|
||||
|
||||
void saveSendBootStateForm(byte taskIndex, byte settingsOffset) {
|
||||
Settings.TaskDevicePluginConfig[taskIndex][settingsOffset] =
|
||||
isFormItemChecked(F(PLUGIN_HELPER_BOOT_STATE_ID));
|
||||
}
|
||||
|
||||
void addDebounceForm(byte taskIndex) {
|
||||
addFormNumericBox(F("De-bounce (ms)"), F(PLUGIN_HELPER_DEBOUNCE_ID),
|
||||
round(hlp_getDebounceInterval(taskIndex)), 0, 250);
|
||||
}
|
||||
|
||||
void saveDebounceForm(byte taskIndex) {
|
||||
hlp_setDebounceInterval(taskIndex,
|
||||
getFormItemInt(F(PLUGIN_HELPER_DEBOUNCE_ID)));
|
||||
}
|
||||
|
||||
void setDoubleClickMinInterval(byte taskIndex) {
|
||||
if (hlp_getDoubleClickInterval(taskIndex) <
|
||||
PLUGIN_HELPER_DOUBLECLICK_MIN_INTERVAL)
|
||||
hlp_setDoubleClickInterval(taskIndex,
|
||||
PLUGIN_HELPER_DOUBLECLICK_MIN_INTERVAL);
|
||||
}
|
||||
|
||||
void addDoubleClickEventForm(byte taskIndex) {
|
||||
setDoubleClickMinInterval(taskIndex);
|
||||
|
||||
byte choiceDC = hlp_getUseDoubleClick(taskIndex);
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_HELPER_DC_DISABLED, PLUGIN_HELPER_DC_LOW,
|
||||
PLUGIN_HELPER_DC_HIGH, PLUGIN_HELPER_DC_BOTH};
|
||||
|
||||
addFormSelector(F("Doubleclick event"), F(PLUGIN_HELPER_DC_EVENT_ID), 4,
|
||||
buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"),
|
||||
F(PLUGIN_HELPER_DC_MAX_INT_ID),
|
||||
round(hlp_getDoubleClickInterval(taskIndex)),
|
||||
PLUGIN_HELPER_DOUBLECLICK_MIN_INTERVAL,
|
||||
PLUGIN_HELPER_DOUBLECLICK_MAX_INTERVAL);
|
||||
}
|
||||
|
||||
void saveDoubleClickEventForm(byte taskIndex) {
|
||||
hlp_setUseDoubleClick(taskIndex, getFormItemInt(F(PLUGIN_HELPER_DC_EVENT_ID)));
|
||||
hlp_setDoubleClickInterval(taskIndex, getFormItemInt(F(PLUGIN_HELPER_DC_MAX_INT_ID)));
|
||||
}
|
||||
|
||||
|
||||
void setLongPressMinInterval(byte taskIndex) {
|
||||
if (hlp_getLongPressInterval(taskIndex) < PLUGIN_HELPER_LONGPRESS_MIN_INTERVAL)
|
||||
hlp_setLongPressInterval(taskIndex, PLUGIN_HELPER_LONGPRESS_MIN_INTERVAL);
|
||||
}
|
||||
|
||||
void addLongPressEventForm(byte taskIndex) {
|
||||
setLongPressMinInterval(taskIndex);
|
||||
|
||||
byte choiceLP = hlp_getUseLongPress(taskIndex);
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
int buttonLPValues[4] = {
|
||||
PLUGIN_HELPER_LONGPRESS_DISABLED, PLUGIN_HELPER_LONGPRESS_LOW,
|
||||
PLUGIN_HELPER_LONGPRESS_HIGH, PLUGIN_HELPER_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F(PLUGIN_HELPER_LP_EVENT_ID), 4,
|
||||
buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"),
|
||||
F(PLUGIN_HELPER_LP_MIN_INT_ID),
|
||||
round(hlp_getLongPressInterval(taskIndex)),
|
||||
PLUGIN_HELPER_LONGPRESS_MIN_INTERVAL,
|
||||
PLUGIN_HELPER_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
addFormCheckBox(F("Use Safe Button (slower)"), F(PLUGIN_HELPER_LP_SAFE_ID),
|
||||
round(hlp_getUseSafeButton(taskIndex)));
|
||||
|
||||
// TO-DO: add Extra-Long Press event
|
||||
// addFormCheckBox(F("Extra-Longpress event (20 & 21)"), F("p001_elp"),
|
||||
// hlp_getClicktimeDoubleClick(taskIndex));
|
||||
// addFormNumericBox(F("Extra-Longpress min. interval (ms)"),
|
||||
// F("p001_elpmininterval"),
|
||||
// hlp_getClicktimeLongpress(taskIndex), 500, 2000);
|
||||
}
|
||||
|
||||
void saveLongPressEventForm(byte taskIndex) {
|
||||
hlp_setUseLongPress(taskIndex, getFormItemInt(F(PLUGIN_HELPER_LP_EVENT_ID)));
|
||||
hlp_setLongPressInterval(taskIndex,
|
||||
getFormItemInt(F(PLUGIN_HELPER_LP_MIN_INT_ID)));
|
||||
|
||||
hlp_setUseSafeButton(taskIndex,
|
||||
isFormItemChecked(F(PLUGIN_HELPER_LP_SAFE_ID)));
|
||||
|
||||
// TO-DO: add Extra-Long Press event
|
||||
// hlp_setClicktimeDoubleClick(taskIndex, isFormItemChecked(F("p001_elp")));
|
||||
// hlp_setClicktimeLongpress(taskIndex,
|
||||
// getFormItemInt(F("p001_elpmininterval")));
|
||||
}
|
||||
|
||||
// check if a task has been edited and remove 'task' bit from the previous pin
|
||||
void update_globalMapPortStatus_onSave(byte taskIndex, byte pluginID) {
|
||||
for (auto it = globalMapPortStatus.begin(); it != globalMapPortStatus.end();
|
||||
++it) {
|
||||
if (it->second.previousTask == taskIndex &&
|
||||
getPluginFromKey(it->first) == pluginID) {
|
||||
globalMapPortStatus[it->first].previousTask = -1;
|
||||
removeTaskFromPort(it->first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################################################################################
|
||||
// PLUGIN_TEN_PER_SECOND actions
|
||||
//#######################################################################################################
|
||||
|
||||
// Reset timer for long press
|
||||
void hlp_resetLongPressTimer(byte taskIndex) {
|
||||
hlp_setClicktimeLongpress(taskIndex, millis());
|
||||
hlp_setLongPressFired(taskIndex, false);
|
||||
}
|
||||
|
||||
|
||||
bool hlp_debounceTimoutPassed(byte taskIndex) {
|
||||
const unsigned long debounceTime = timePassedSince(hlp_getClicktimeDebounce(taskIndex));
|
||||
return (debounceTime >= (unsigned long)lround(hlp_getDebounceInterval(taskIndex)));
|
||||
}
|
||||
|
||||
void hlp_processDoubleClick(byte taskIndex, bool gpio_state) {
|
||||
const unsigned long deltaDC = timePassedSince(hlp_getClicktimeDoubleClick(taskIndex));
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[taskIndex][1])) ||
|
||||
hlp_getDoubleClickCounter(taskIndex) == 3)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
// FIXME TD-er: Temporary counters and states should not be in settings.
|
||||
hlp_setDoubleClickCounter(taskIndex, 0);
|
||||
hlp_setClicktimeDoubleClick(taskIndex, millis());
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
const int16_t COUNTER = Settings.TaskDevicePluginConfig[taskIndex][7];
|
||||
const int16_t DC = hlp_getUseDoubleClick(taskIndex);
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER != 0 ||
|
||||
( COUNTER == 0 &&
|
||||
(DC == 3 ||
|
||||
(DC == 1 && !gpio_state) ||
|
||||
(DC == 2 && gpio_state))) )
|
||||
{
|
||||
Settings.TaskDevicePluginConfig[taskIndex][7]++;
|
||||
}
|
||||
}
|
||||
|
||||
bool hlp_LongPressIntervalReached(byte taskIndex) {
|
||||
const unsigned long deltaLP = timePassedSince(hlp_getClicktimeLongpress(taskIndex));
|
||||
return deltaLP >= (unsigned long)lround(hlp_getLongPressInterval(taskIndex));
|
||||
}
|
||||
|
||||
byte hlp_getOutputValue_updateSendState(byte taskIndex, bool& sendState) {
|
||||
byte output_value;
|
||||
if (Settings.TaskDevicePin1Inversed[taskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (hlp_getLongPressFired(taskIndex)) {
|
||||
output_value = sendState ? 11 : 10;
|
||||
}
|
||||
else if (hlp_getDoubleClickCounter(taskIndex) == 3 && hlp_getUseDoubleClick(taskIndex) > 0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
return output_value;
|
||||
}
|
||||
|
||||
void hlp_logState_and_Output(byte taskIndex, bool gpio_state, bool sendState, byte output_value, byte pluginId) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log;
|
||||
log.reserve(64);
|
||||
switch (pluginId) {
|
||||
case 0: log += F("SW "); break;
|
||||
case 9: log += F("MCP"); break;
|
||||
case 19: log += F("PCF"); break;
|
||||
default: log += F("???"); break;
|
||||
}
|
||||
log += F(" : Port=");
|
||||
log += Settings.TaskDevicePin1[taskIndex];
|
||||
log += F(" pinState=");
|
||||
log += gpio_state ? '1' : '0';
|
||||
log += F(" sendState=");
|
||||
log += sendState ? '1' : '0';
|
||||
switch (output_value) {
|
||||
case 3: log += F(" Doubleclick="); break;
|
||||
case 10:
|
||||
case 11: log += F(" LongPress="); break;
|
||||
|
||||
default:
|
||||
log += F(" Output value=");
|
||||
break;
|
||||
}
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
}
|
||||
|
||||
bool hlp_LongPressEnabled_and_notFired(byte taskIndex, bool gpio_state) {
|
||||
//just to simplify the reading of the code
|
||||
const int16_t LP = hlp_getUseLongPress(taskIndex);
|
||||
const int16_t FIRED = hlp_getLongPressFired(taskIndex);
|
||||
//Check if LP is enabled and if LP has not fired yet
|
||||
if (FIRED) return false;
|
||||
return (
|
||||
LP == 3 ||
|
||||
(LP == 1 && !gpio_state) ||
|
||||
(LP == 2 && gpio_state) );
|
||||
}
|
||||
|
||||
//#######################################################################################################
|
||||
// Settings for switch plugins
|
||||
//#######################################################################################################
|
||||
|
||||
/**************************************************\
|
||||
TaskDevicePluginConfig settings:
|
||||
0 or 3: send boot state (true,false)
|
||||
1:
|
||||
2:
|
||||
3:
|
||||
4: use doubleclick (0,1,2,3)
|
||||
5: use longpress (0,1,2,3)
|
||||
6: LP fired (true,false)
|
||||
7: doubleclick counter (=0,1,2,3)
|
||||
\**************************************************/
|
||||
int16_t hlp_getMustSendBootState(byte taskIndex, byte settingsOffset) {
|
||||
return Settings.TaskDevicePluginConfig[taskIndex][settingsOffset];
|
||||
}
|
||||
|
||||
void hlp_setMustSendBootState(byte taskIndex, byte settingsOffset, int16_t value) {
|
||||
Settings.TaskDevicePluginConfig[taskIndex][settingsOffset] = value;
|
||||
}
|
||||
|
||||
|
||||
int16_t hlp_getUseDoubleClick(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfig[taskIndex][4];
|
||||
}
|
||||
|
||||
void hlp_setUseDoubleClick(byte taskIndex, int16_t value) {
|
||||
Settings.TaskDevicePluginConfig[taskIndex][4] = value;
|
||||
}
|
||||
|
||||
int16_t hlp_getUseLongPress(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfig[taskIndex][5];
|
||||
}
|
||||
|
||||
void hlp_setUseLongPress(byte taskIndex, int16_t value) {
|
||||
Settings.TaskDevicePluginConfig[taskIndex][5] = value;
|
||||
}
|
||||
|
||||
int16_t hlp_getLongPressFired(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfig[taskIndex][6];
|
||||
}
|
||||
|
||||
// FIXME TD-er: Mainly used to store temporary values.
|
||||
void hlp_setLongPressFired(byte taskIndex, int16_t value) {
|
||||
Settings.TaskDevicePluginConfig[taskIndex][6] = value;
|
||||
}
|
||||
|
||||
int16_t hlp_getDoubleClickCounter(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfig[taskIndex][7];
|
||||
}
|
||||
|
||||
// FIXME TD-er: Mainly used to store temporary values.
|
||||
void hlp_setDoubleClickCounter(byte taskIndex, int16_t value) {
|
||||
Settings.TaskDevicePluginConfig[taskIndex][7] = value;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************\
|
||||
TaskDevicePluginConfigFloat settings:
|
||||
0: debounce interval ms
|
||||
1: doubleclick interval ms
|
||||
2: longpress interval ms
|
||||
3: use safebutton (=0,1)
|
||||
\**************************************************/
|
||||
float hlp_getDebounceInterval(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigFloat[taskIndex][0];
|
||||
}
|
||||
|
||||
void hlp_setDebounceInterval(byte taskIndex, float value) {
|
||||
Settings.TaskDevicePluginConfigFloat[taskIndex][0] = value;
|
||||
}
|
||||
|
||||
float hlp_getDoubleClickInterval(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigFloat[taskIndex][1];
|
||||
}
|
||||
|
||||
void hlp_setDoubleClickInterval(byte taskIndex, float value) {
|
||||
Settings.TaskDevicePluginConfigFloat[taskIndex][1] = value;
|
||||
}
|
||||
|
||||
float hlp_getLongPressInterval(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigFloat[taskIndex][2];
|
||||
}
|
||||
|
||||
void hlp_setLongPressInterval(byte taskIndex, float value) {
|
||||
Settings.TaskDevicePluginConfigFloat[taskIndex][2] = value;
|
||||
}
|
||||
|
||||
float hlp_getUseSafeButton(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigFloat[taskIndex][3];
|
||||
}
|
||||
|
||||
void hlp_setUseSafeButton(byte taskIndex, float value) {
|
||||
Settings.TaskDevicePluginConfigFloat[taskIndex][3] = value;
|
||||
}
|
||||
|
||||
/**************************************************\
|
||||
TaskDevicePluginConfigLong settings:
|
||||
0: clickTime debounce ms
|
||||
1: clickTime doubleclick ms
|
||||
2: clickTime longpress ms
|
||||
3: safebutton counter (=0,1)
|
||||
\**************************************************/
|
||||
long hlp_getClicktimeDebounce(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigLong[taskIndex][0];
|
||||
}
|
||||
|
||||
// FIXME TD-er: Mainly used to store temporary values.
|
||||
void hlp_setClicktimeDebounce(byte taskIndex, long value) {
|
||||
Settings.TaskDevicePluginConfigLong[taskIndex][0] = value;
|
||||
}
|
||||
|
||||
long hlp_getClicktimeDoubleClick(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigLong[taskIndex][1];
|
||||
}
|
||||
|
||||
// FIXME TD-er: Mainly used to store temporary values.
|
||||
void hlp_setClicktimeDoubleClick(byte taskIndex, long value) {
|
||||
Settings.TaskDevicePluginConfigLong[taskIndex][1] = value;
|
||||
}
|
||||
|
||||
long hlp_getClicktimeLongpress(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigLong[taskIndex][2];
|
||||
}
|
||||
|
||||
// FIXME TD-er: Mainly used to store temporary values.
|
||||
void hlp_setClicktimeLongpress(byte taskIndex, long value) {
|
||||
Settings.TaskDevicePluginConfigLong[taskIndex][2] = value;
|
||||
}
|
||||
|
||||
long hlp_getSafebuttonCounter(byte taskIndex) {
|
||||
return Settings.TaskDevicePluginConfigLong[taskIndex][3];
|
||||
}
|
||||
|
||||
// FIXME TD-er: Mainly used to store temporary values.
|
||||
void hlp_setSafebuttonCounter(byte taskIndex, long value) {
|
||||
Settings.TaskDevicePluginConfigLong[taskIndex][3] = value;
|
||||
}
|
||||
+8
-8
File diff suppressed because one or more lines are too long
@@ -353,7 +353,7 @@ table.multirow tr:nth-child(even){
|
||||
border-radius:4px 4px 0 0;
|
||||
border-width:4px 1px 1px;
|
||||
color:#444;
|
||||
padding:4px 16px 8px;
|
||||
padding:4px 16px 4px;
|
||||
white-space:nowrap
|
||||
}
|
||||
.menu.active{
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user