From 7850a2ae2250c7849b70a6417f4f79817114d4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Sun, 10 Jul 2016 10:48:07 +0200 Subject: [PATCH] Adding double and long click support, double to SoftAP mode, long to reset board, single click toggles relay --- README.md | 8 +- code/lib/DebounceEvent/DebounceEvent.cpp | 53 ++++++++-- code/lib/DebounceEvent/DebounceEvent.h | 19 +++- code/src/code.ino | 127 +++++++++++++---------- 4 files changed, 137 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 60d5dff9..aa57834a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ You can read about this board and firmware in [my blog][2]. * Flashing firmware Over-The-Air (OTA) * Up to 3 configurable WIFI networks * MQTT support with configurable host and topic -* Manual switch ON/OFF with button +* Manual switch ON/OFF with button (single click the button) +* AP mode backup (double click the button) * Support for custom RF module (check blog post) * Visual status of the connection via the LED @@ -60,14 +61,13 @@ It configures the hardware (button, LED, relay), the SPIFFS memory access, the WIFI, the WebServer and MQTT connection. Obviously the default values for WIFI network and MQTT will probably not match -your requirements. Either it connects to a WiFi or not, it will set up a Soft AP -named "SONOFF_XXXX", where XXXX are the las 2 bytes of the radio MAC. Connect with +your requirements. The device will start in Soft AP creating a WIFI SSID named "SONOFF_XXXXXX", where XXXXXX are the last 3 bytes of the radio MAC. Connect with phone, PC, laptop, whatever to that network, password is "fibonacci". Once connected browse to 192.168.4.1 and you will be presented a configuration page where you will be able to define up to 3 possible WIFI networks and the MQTT configuration parameters. It will then try to connect to the first WIFI network. If fail it will try the second -in 30 seconds, and so on. Once connected it will try to connect the MQTT server. +in 30 seconds, and so on. Once connected it will try to connect the MQTT server. If there are no configured networks or none of the configured ones is reachable it defaults to SoftAP mode. You can also switch to SoftAP mode by double clicking the on board button. The device will publish the relay state to the given topic and it will subscribe to the same topic plus "/set" for remote switching. So if your topic is "/home/living/switch" diff --git a/code/lib/DebounceEvent/DebounceEvent.cpp b/code/lib/DebounceEvent/DebounceEvent.cpp index 4423a021..cc458f8a 100644 --- a/code/lib/DebounceEvent/DebounceEvent.cpp +++ b/code/lib/DebounceEvent/DebounceEvent.cpp @@ -41,34 +41,63 @@ DebounceEvent::DebounceEvent(uint8_t pin, callback_t callback, uint8_t defaultSt bool DebounceEvent::loop() { // holds whether status has changed or not + static bool pending = false; bool changed = false; + _event = EVENT_NONE; if (digitalRead(_pin) != _status) { + + // Debounce delay(_delay); uint8_t newStatus = digitalRead(_pin); if (newStatus != _status) { changed = true; + pending = false; _status = newStatus; - // raise events if callback defined - if (_callback) { - - // raise change event - _callback(_pin, EVENT_CHANGED); + // released + if (_status == _defaultStatus) { - if (_status == _defaultStatus) { - // raise released event - _callback(_pin, EVENT_RELEASED); + // get event + if (millis() - _this_start > LONG_CLICK_DELAY) { + _event = EVENT_LONG_CLICK; + } else if (millis() - _last_start < DOUBLE_CLICK_DELAY ) { + _event = EVENT_DOUBLE_CLICK; } else { - // raise pressed event - _callback(_pin, EVENT_PRESSED); + Serial.println("deferring"); + changed = false; + pending = true; + //_event = EVENT_SINGLE_CLICK; } + // pressed + } else { + + _last_start = _this_start; + _this_start = millis(); + _event = EVENT_PRESSED; + } + } } + if (pending && (millis() - _this_start > DOUBLE_CLICK_DELAY) && (!changed) && (_status == _defaultStatus)) { + Serial.println("catched"); + pending = false; + changed = true; + _event = EVENT_SINGLE_CLICK; + } + + if (changed) { + if (_callback) { + _callback(_pin, EVENT_CHANGED); + _callback(_pin, _event); + } + } + + return changed; } @@ -76,3 +105,7 @@ bool DebounceEvent::loop() { bool DebounceEvent::pressed() { return (_status != _defaultStatus); } + +uint8_t DebounceEvent::getEvent() { + return _event; +} diff --git a/code/lib/DebounceEvent/DebounceEvent.h b/code/lib/DebounceEvent/DebounceEvent.h index 1f6064f0..68197929 100644 --- a/code/lib/DebounceEvent/DebounceEvent.h +++ b/code/lib/DebounceEvent/DebounceEvent.h @@ -21,10 +21,17 @@ #ifndef _DEBOUNCE_EVENT_h #define _DEBOUNCE_EVENT_h -#define DEBOUNCE_DELAY 100 -#define EVENT_CHANGED 0 -#define EVENT_PRESSED 1 -#define EVENT_RELEASED 2 +#define DEBOUNCE_DELAY 50 +#define LONG_CLICK_DELAY 1000 +#define DOUBLE_CLICK_DELAY 500 + +#define EVENT_NONE 0 +#define EVENT_CHANGED 1 +#define EVENT_PRESSED 2 +#define EVENT_RELEASED 3 +#define EVENT_SINGLE_CLICK 3 +#define EVENT_DOUBLE_CLICK 4 +#define EVENT_LONG_CLICK 5 typedef void(*callback_t)(uint8_t pin, uint8_t event); @@ -34,6 +41,9 @@ class DebounceEvent { uint8_t _pin; uint8_t _status; + uint8_t _event; + unsigned long _this_start; + unsigned long _last_start; uint8_t _defaultStatus; unsigned long _delay; callback_t _callback; @@ -43,6 +53,7 @@ class DebounceEvent { DebounceEvent(uint8_t pin, callback_t callback = false, uint8_t defaultStatus = HIGH, unsigned long delay = DEBOUNCE_DELAY); bool pressed(); bool loop(); + uint8_t getEvent(); }; diff --git a/code/src/code.ino b/code/src/code.ino index 5b0e7666..6f98cf9c 100644 --- a/code/src/code.ino +++ b/code/src/code.ino @@ -43,21 +43,18 @@ along with this program. If not, see . #define ENABLE_WEBSERVER 1 #define ENABLE_ENERGYMONITOR 1 -#define APP_NAME "Espurna 0.9" +#define APP_NAME "Espurna 0.9.1" #define APP_AUTHOR "xose.perez@gmail.com" #define APP_WEBSITE "http://tinkerman.cat" #define MODEL "SONOFF" #define BUTTON_PIN 0 #define RELAY_PIN 12 -#define LED_PIN 13 +#define LED_PIN 13ter #define ADMIN_PASS "fibonacci" #define CONFIG_PATH "/.config" -#define WIFI_CONNECT_TIMEOUT 5000 -#define WIFI_RECONNECT_DELAY 5000 -#define MQTT_RECONNECT_DELAY 10000 -#define NETWORK_BUFFER 3 + #define BUFFER_SIZE 1024 #define STATUS_UPDATE_INTERVAL 10000 @@ -65,10 +62,18 @@ along with this program. If not, see . #define RF_CHANNEL 31 #define RF_DEVICE 1 +#define MQTT_RECONNECT_DELAY 10000 #define MQTT_RETAIN true #define MQTT_TOPIC "/test/switch/{identifier}" #define MQTT_PORT 1883 +#define NETWORK_BUFFER 3 +#define WIFI_CONNECT_TIMEOUT 5000 +#define WIFI_RECONNECT_DELAY 5000 +#define WIFI_STATUS_CONNECTING 0 +#define WIFI_STATUS_CONNECTED 1 +#define WIFI_STATUS_AP 2 + #define CURRENT_PIN A0 #define REFERENCE_VOLTAGE 1.0 #define MAINS_VOLTAGE 230.0 @@ -84,7 +89,7 @@ along with this program. If not, see . char identifier[20] = {0}; -byte network = 0; +byte status = WIFI_STATUS_CONNECTING; String configSSID[NETWORK_BUFFER]; String configPASS[NETWORK_BUFFER]; @@ -214,26 +219,22 @@ void toggleRelay() { char * getIdentifier() { if (identifier[0] == 0) { - uint8_t mac[WL_MAC_ADDR_LENGTH]; - WiFi.softAPmacAddress(mac); - String name = MODEL + String("_") + String(mac[WL_MAC_ADDR_LENGTH - 2], HEX) + String(mac[WL_MAC_ADDR_LENGTH - 1], HEX); - name.toUpperCase(); - byte length = std::min(20, (int) name.length() + 1); - name.toCharArray(identifier, length); + sprintf(identifier, "%s_%06X", MODEL, ESP.getChipId()); } return identifier; } -void wifiSetup(bool force) { +void wifiSetupAP() { - // Set WIFI module to Mixed Mode - WiFi.mode(WIFI_AP_STA); + // Set WIFI module AP mode + WiFi.mode(WIFI_AP); #ifdef DEBUG WiFi.printDiag(Serial); #endif // SoftAP mode WiFi.softAP(getIdentifier(), ADMIN_PASS); + status = WIFI_STATUS_AP; #ifdef DEBUG Serial.print("[AP Mode] SSID: "); Serial.print(getIdentifier()); @@ -243,45 +244,66 @@ void wifiSetup(bool force) { Serial.println(WiFi.softAPIP()); #endif - // STA mode +} + +void wifiSetupSTA(bool force) { + + byte network = 0; + if (force || (WiFi.status() != WL_CONNECTED)) { - if (configSSID[network].length() > 0) { - #if ENABLE_MQTT - if (mqtt.connected()) mqtt.disconnect(); - #endif + // Set WIFI module to STA mode + WiFi.mode(WIFI_STA); + #ifdef DEBUG + WiFi.printDiag(Serial); + #endif - #if ENABLE_RF - RemoteReceiver::disable(); - #endif + #if ENABLE_MQTT + if (mqtt.connected()) mqtt.disconnect(); + #endif + + #if ENABLE_RF + RemoteReceiver::disable(); + #endif - char ssid[configSSID[network].length()+1]; - char pass[configPASS[network].length()+1]; - configSSID[network].toCharArray(ssid, configSSID[network].length()+1); - configPASS[network].toCharArray(pass, configPASS[network].length()+1); - WiFi.begin(ssid, pass); + while (network < 3) { - #ifdef DEBUG - Serial.println("Connecting to WIFI " + configSSID[network]); - #endif + if (configSSID[network].length() > 0) { + + char ssid[configSSID[network].length()+1]; + char pass[configPASS[network].length()+1]; + configSSID[network].toCharArray(ssid, configSSID[network].length()+1); + configPASS[network].toCharArray(pass, configPASS[network].length()+1); + WiFi.begin(ssid, pass); + + #ifdef DEBUG + Serial.println("Connecting to WIFI " + configSSID[network]); + #endif + + // Wait + unsigned long timeout = millis() + WIFI_CONNECT_TIMEOUT; + while (timeout > millis()) { + showStatus(); + if (WiFi.status() == WL_CONNECTED) break; + delay(100); + } - // Wait - unsigned long timeout = millis() + WIFI_CONNECT_TIMEOUT; - while (timeout > millis()) { - showStatus(); - if (WiFi.status() == WL_CONNECTED) break; - delay(100); } - #if ENABLE_RF - RemoteReceiver::enable(); - #endif + if (WiFi.status() == WL_CONNECTED) break; + network++; } + + #if ENABLE_RF + RemoteReceiver::enable(); + #endif + } if (WiFi.status() == WL_CONNECTED) { WiFi.setAutoConnect(true); + status = WIFI_STATUS_CONNECTED; #ifdef DEBUG Serial.print("[STA Mode] SSID: "); Serial.print(WiFi.SSID()); @@ -289,22 +311,22 @@ void wifiSetup(bool force) { Serial.println(WiFi.localIP()); #endif } else { - network = (network + 1) % NETWORK_BUFFER; #ifdef DEBUG Serial.println("[STA Mode] NOT CONNECTED"); #endif + wifiSetupAP(); } } void wifiLoop() { - static unsigned long timeout = millis(); - if (WiFi.status() != WL_CONNECTED) { - if (timeout < millis()) { - wifiSetup(false); - timeout = millis() + WIFI_RECONNECT_DELAY; - } + + // Trying to reconnect in case of disconnection + if ((status == WIFI_STATUS_CONNECTED) && (WiFi.status() != WL_CONNECTED)) { + status = WIFI_STATUS_CONNECTING; + wifiSetupSTA(false); } + } // ----------------------------------------------------------------------------- @@ -459,8 +481,7 @@ void wifiLoop() { #if ENABLE_RF rfBuildCodes(); #endif - network = 0; - wifiSetup(true); + wifiSetupSTA(true); } @@ -963,7 +984,9 @@ void showStatus() { void hardwareLoop() { if (button1.loop()) { - if (!button1.pressed()) toggleRelay(); + if (button1.getEvent() == EVENT_SINGLE_CLICK) toggleRelay(); + if (button1.getEvent() == EVENT_DOUBLE_CLICK) wifiSetupAP(); + if (button1.getEvent() == EVENT_LONG_CLICK) ESP.restart(); } showStatus(); } @@ -994,7 +1017,7 @@ void setup() { OTASetup(); #endif loadConfig(); - wifiSetup(false); + wifiSetupSTA(false); #if ENABLE_WEBSERVER webServerSetup(); #endif