Compare commits

...
Author SHA1 Message Date
Gijs NoorlanderandGitHub d6a2798408 Merge pull request #2136 from TD-er/bugfix/GPIO_bootmode_checks
[GPIO] Add checks for GPIO boot states
2018-12-10 12:53:36 +01:00
Gijs Noorlander e2ec04825b [GPIO] Move duplicate code to switch helper file. 2018-12-09 16:49:43 +01:00
Gijs Noorlander 593da3146f [GPIO] Use descriptive get/set functions to access settings
For example:
```c++
int16_t hlp_getUseDoubleClick(byte taskIndex) { 
  return Settings.TaskDevicePluginConfig[taskIndex][4]; 
} 
 
void hlp_setUseDoubleClick(byte taskIndex, int16_t value) { 
   Settings.TaskDevicePluginConfig[taskIndex][4] = value; 
} 
```

N.B. A number of temporary variables like counters and whether a long press has fired appear to be stored in the settings too.
This is something still needed to be fixed.
2018-12-09 01:29:59 +01:00
Gijs Noorlander 24ce8a8ef6 [GPIO] Cleanup and documentation of GPIO code. 2018-12-08 01:07:59 +01:00
Gijs Noorlander 5179111408 [GPIO] Use helper class for duplicate code in P001, P009 and P019
Moved duplicate code to functions used in:
- PLUGIN_WEBFORM_LOAD
- PLUGIN_WEBFORM_SAVE
2018-12-07 22:55:13 +01:00
Gijs Noorlander e9fe48c4cb [GPIO] Simple clean-up to understand the portstatus code
See https://github.com/letscontrolit/ESPEasy/pull/2057#issuecomment-445247213
2018-12-07 15:23:05 +01:00
Gijs Noorlander a2a7d68594 [CSS] Fix padding in menu.
`.menu {padding: 4px 16px 8px;}` should be `.menu {padding: 4px 16px 4px;}`
2018-12-07 12:30:11 +01:00
Gijs Noorlander eda236a816 [GPIO] Move portStatusStruct and related functions to separate file
And some clean-up of the code.
2018-12-07 01:15:47 +01:00
Gijs Noorlander d594cf040d [GPIO] Add checks for GPIO boot states
Added some checks for the GPIO boot states.
Changing boot state will now set the new state. (was only active after reboot)
2018-12-06 16:42:43 +01:00
21 changed files with 1384 additions and 1280 deletions
+2 -1
View File
@@ -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
^^^^^^^^^^^^^^^^
+29
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
+185
View File
@@ -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
View File
@@ -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_ */
View File
+169 -139
View File
@@ -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
View File
@@ -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
View File
@@ -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 &rsquo;GPIO-2 (D4)&rsquo; with &rsquo;Inversed&rsquo; checked for onboard LED"));
addFormNote(F("On most boards, use &rsquo;GPIO-2 (D4)&rsquo; with &rsquo;Inversed&rsquo; 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
View File
File diff suppressed because one or more lines are too long
+88 -219
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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)
+402
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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{
+1 -1
View File
File diff suppressed because one or more lines are too long