diff --git a/CHANGELOG.md b/CHANGELOG.md index dc447d3c..688b3968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added last-modified header to static contents - Added support for multi-button boards (SONOFF_4CH) - Added support for WorkChoice ecoPlug (ECOPLUG). Thanks to David Myers +- Added support for Sonoff SV +- Added DNS captive portal for AP mode - Force password changing if it's the default one - Comment out hardware selection in hardware.h if using Arduino IDE @@ -15,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Using unreleased AsyncMqttClient with stability improvements - Better decoupling between MQTT and relays/websockets +### Fixed +- Issue #14 MQTT Connection with Username an Password not working +- Issue #11 Compile error when building sonoff-dual-debug + ## [1.1.0] 2016-12-06 ### Added - Added support for DS18B20 temperature sensor. Thanks to Francesco Boscarino diff --git a/code/lib/DebounceEvent/DebounceEvent.cpp b/code/lib/DebounceEvent/DebounceEvent.cpp index 2ccecb31..fccb41e4 100644 --- a/code/lib/DebounceEvent/DebounceEvent.cpp +++ b/code/lib/DebounceEvent/DebounceEvent.cpp @@ -45,7 +45,6 @@ DebounceEvent::DebounceEvent(uint8_t pin, uint8_t defaultStatus, unsigned long d bool DebounceEvent::loop() { // holds whether status has changed or not - static bool pending = false; bool changed = false; _event = EVENT_NONE; @@ -57,7 +56,7 @@ bool DebounceEvent::loop() { if (newStatus != _status) { changed = true; - pending = false; + _clicked = false; _status = newStatus; // released @@ -69,9 +68,13 @@ bool DebounceEvent::loop() { } else if (millis() - _last_start < DOUBLE_CLICK_DELAY ) { _event = EVENT_DOUBLE_CLICK; } else { + + // We are not setting the event type here because we still don't + // know what kind of event it will be (it might be a double click). + // Instead we are setting the _clicked variable to check later + _clicked = true; changed = false; - pending = true; - //_event = EVENT_SINGLE_CLICK; + } // pressed @@ -86,8 +89,8 @@ bool DebounceEvent::loop() { } } - if (pending && (millis() - _this_start > DOUBLE_CLICK_DELAY) && (!changed) && (_status == _defaultStatus)) { - pending = false; + if (_clicked && (millis() - _this_start > DOUBLE_CLICK_DELAY) && (!changed) && (_status == _defaultStatus)) { + _clicked = false; changed = true; _event = EVENT_SINGLE_CLICK; } @@ -99,7 +102,6 @@ bool DebounceEvent::loop() { } } - return changed; } diff --git a/code/lib/DebounceEvent/DebounceEvent.h b/code/lib/DebounceEvent/DebounceEvent.h index d3014e40..b7b30b51 100644 --- a/code/lib/DebounceEvent/DebounceEvent.h +++ b/code/lib/DebounceEvent/DebounceEvent.h @@ -42,6 +42,7 @@ class DebounceEvent { uint8_t _pin; uint8_t _status; uint8_t _event; + bool _clicked = false; unsigned long _this_start; unsigned long _last_start; uint8_t _defaultStatus; diff --git a/code/platformio.ini b/code/platformio.ini index d1aa66de..11563e47 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -219,3 +219,24 @@ build_flags = -g -DDEBUG_PORT=Serial -DDEBUG_PORT=Serial -DESP_RELAY_BOARD -DENA upload_speed = 115200 upload_port = "192.168.4.1" upload_flags = --auth=fibonacci --port 8266 + +[env:ecoplug-debug] +platform = espressif8266 +framework = arduino +board = esp01_1m +lib_deps = ${common.lib_deps} +lib_ignore = ${common.lib_ignore} +extra_script = pio_hooks.py +build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DDEBUG_PORT=Serial -DECOPLUG + +[env:ecoplug-debug-ota] +platform = espressif8266 +framework = arduino +board = esp01_1m +lib_deps = ${common.lib_deps} +lib_ignore = ${common.lib_ignore} +extra_script = pio_hooks.py +build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DDEBUG_PORT=Serial -DECOPLUG +upload_speed = 115200 +upload_port = "192.168.4.1" +upload_flags = --auth=fibonacci --port 8266 diff --git a/code/src/config/general.h b/code/src/config/general.h index bf261172..cfb4338b 100644 --- a/code/src/config/general.h +++ b/code/src/config/general.h @@ -37,6 +37,15 @@ // 0 means no inching, 1 means normally off, 2 normally on #define RELAY_INCHING RELAY_INCHING_NONE +//-------------------------------------------------------------------------------- +// LED +//-------------------------------------------------------------------------------- + +// All defined LEDs in the board can be managed through MQTT +// except the first one when LED_AUTO is set to 1. +// If LED_AUTO is set to 1 the board will use first defined LED to show wifi status. +#define LED_AUTO 1 + // ----------------------------------------------------------------------------- // WIFI & WEB // ----------------------------------------------------------------------------- @@ -47,6 +56,7 @@ #define HTTP_USERNAME "admin" #define WS_BUFFER_SIZE 5 #define WS_TIMEOUT 1800000 +#define DNS_PORT 53 // ----------------------------------------------------------------------------- // OTA & NOFUSS @@ -67,7 +77,8 @@ #define MQTT_QOS 0 #define MQTT_KEEPALIVE 30 #define MQTT_RECONNECT_DELAY 10000 -#define MQTT_RELAY_TOPIC "/relay/%d" +#define MQTT_RELAY_TOPIC "/relay" +#define MQTT_LED_TOPIC "/led" #define MQTT_IP_TOPIC "/ip" #define MQTT_VERSION_TOPIC "/version" #define MQTT_FSVERSION_TOPIC "/fsversion" @@ -77,6 +88,12 @@ #define MQTT_DISCONNECT_EVENT 1 #define MQTT_MESSAGE_EVENT 2 +// Custom get and set postfixes +// Use something like "/status" or "/set", with trailing slash +#define MQTT_USE_GETTER "" +#define MQTT_USE_SETTER "" + + // ----------------------------------------------------------------------------- // NTP // ----------------------------------------------------------------------------- diff --git a/code/src/config/hardware.h b/code/src/config/hardware.h index c44555e9..ace41432 100644 --- a/code/src/config/hardware.h +++ b/code/src/config/hardware.h @@ -10,12 +10,21 @@ //#define SONOFF_POW //#define SONOFF_DUAL //#define SONOFF_4CH +//#define SONOFF_SV //#define SLAMPHER //#define S20 //#define ESP_RELAY_BOARD //#define ECOPLUG //#define ESPURNA +//#define ENABLE_DHT 1 +//#define ENABLE_DS18B20 1 +//#define ENABLE_EMON 1 +//#define ENABLE_HLW8018 1 +//#define ENABLE_RF 1 +//#define ENABLE_FAUXMO 1 +//#define ENABLE_NOFUSS 1 + // ----------------------------------------------------------------------------- // NODEMCUv2 development board // ----------------------------------------------------------------------------- @@ -26,8 +35,8 @@ #define DEVICE "LOLIN" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 2 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 2 + #define LED1_PIN_INVERSE 1 // ----------------------------------------------------------------------------- // Itead Studio boards @@ -39,8 +48,8 @@ #define DEVICE "SONOFF" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 13 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #elif defined(SONOFF_TH) @@ -48,8 +57,8 @@ #define DEVICE "SONOFF_TH" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 13 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #elif defined(SONOFF_TOUCH) @@ -57,8 +66,8 @@ #define DEVICE "SONOFF_TOUCH" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 13 - #define LED_PIN_INVERSE 1 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #elif defined(SONOFF_POW) @@ -66,8 +75,8 @@ #define DEVICE "SONOFF_POW" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 15 - #define LED_PIN_INVERSE 1 + #define LED1_PIN 15 + #define LED1_PIN_INVERSE 0 #define ENABLE_POW 1 #elif defined(SONOFF_DUAL) @@ -75,8 +84,8 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF_DUAL" #define BUTTON1_PIN 0 - #define LED_PIN 13 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #undef SERIAL_BAUDRATE #define SERIAL_BAUDRATE 19230 @@ -92,8 +101,17 @@ #define RELAY2_PIN 5 #define RELAY3_PIN 4 #define RELAY4_PIN 15 - #define LED_PIN 13 - #define LED_PIN_INVERSE 1 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 + +#elif defined(SONOFF_SV) + + #define MANUFACTURER "ITEAD" + #define DEVICE "SONOFF_SV" + #define BUTTON1_PIN 0 + #define RELAY1_PIN 12 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #elif defined(SLAMPHER) @@ -101,8 +119,8 @@ #define DEVICE "SLAMPHER" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 13 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #elif defined(S20) @@ -110,8 +128,8 @@ #define DEVICE "S20" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 13 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 #elif defined(ITEAD_1CH_INCHING) @@ -136,8 +154,8 @@ #define BUTTON2_PIN 2 #define RELAY1_PIN 12 #define RELAY2_PIN 13 - #define LED_PIN 16 - #define LED_PIN_INVERSE 1 + #define LED1_PIN 16 + #define LED1_PIN_INVERSE 0 // ----------------------------------------------------------------------------- // WorkChoice ecoPlug @@ -149,8 +167,8 @@ #define DEVICE "ECOPLUG" #define BUTTON1_PIN 13 #define RELAY_PIN 15 - #define LED_PIN 2 - #define LED_PIN_INVERSE 1 + #define LED1_PIN 2 + #define LED1_PIN_INVERSE 0 // ----------------------------------------------------------------------------- // ESPurna board (still beta) @@ -162,8 +180,8 @@ #define DEVICE "ESPURNA" #define BUTTON1_PIN 0 #define RELAY1_PIN 12 - #define LED_PIN 13 - #define LED_PIN_INVERSE 0 + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 0 // ----------------------------------------------------------------------------- // Unknown hardware diff --git a/code/src/emon.ino b/code/src/emon.ino index c01ab157..7a85ba0a 100644 --- a/code/src/emon.ino +++ b/code/src/emon.ino @@ -39,10 +39,10 @@ void powerMonitorSetup() { // backwards compatibility String tmp; - tmp = getSetting("pwMainsVoltage", String() + EMON_MAINS_VOLTAGE); + tmp = getSetting("pwMainsVoltage", EMON_MAINS_VOLTAGE); setSetting("emonMains", tmp); delSetting("pwMainsVoltage"); - tmp = getSetting("pwCurrentRatio", String() + EMON_CURRENT_RATIO); + tmp = getSetting("pwCurrentRatio", EMON_CURRENT_RATIO); setSetting("emonRatio", tmp); delSetting("pwCurrentRatio"); @@ -50,7 +50,7 @@ void powerMonitorSetup() { currentCallback, EMON_ADC_BITS, EMON_REFERENCE_VOLTAGE, - getSetting("emonRatio", String(EMON_CURRENT_RATIO)).toFloat() + getSetting("emonRatio", EMON_CURRENT_RATIO).toFloat() ); emon.setPrecision(EMON_CURRENT_PRECISION); } @@ -91,7 +91,7 @@ void powerMonitorLoop() { sum += current; ++measurements; - float mainsVoltage = getSetting("emonMains", String(EMON_MAINS_VOLTAGE)).toFloat(); + float mainsVoltage = getSetting("emonMains", EMON_MAINS_VOLTAGE).toFloat(); //DEBUG_MSG("[ENERGY] Power now: %dW\n", int(current * mainsVoltage)); diff --git a/code/src/fauxmo.ino b/code/src/fauxmo.ino index 3d89669a..1d131892 100644 --- a/code/src/fauxmo.ino +++ b/code/src/fauxmo.ino @@ -18,7 +18,7 @@ fauxmoESP fauxmo; // ----------------------------------------------------------------------------- void fauxmoConfigure() { - fauxmo.enable(getSetting("fauxmoEnabled", String(FAUXMO_ENABLED)).toInt() == 1); + fauxmo.enable(getSetting("fauxmoEnabled", FAUXMO_ENABLED).toInt() == 1); } void fauxmoSetup() { diff --git a/code/src/led.ino b/code/src/led.ino index cd8c1346..6a4a06a2 100644 --- a/code/src/led.ino +++ b/code/src/led.ino @@ -11,36 +11,134 @@ Copyright (C) 2016 by Xose Pérez // LED // ----------------------------------------------------------------------------- -#ifdef LED_PIN +#ifdef LED1_PIN -void blink(unsigned long delayOff, unsigned long delayOn) { +typedef struct { + unsigned char pin; + bool reverse; +} led_t; + +std::vector _leds; +bool ledAuto; + +bool ledStatus(unsigned char id) { + if (id <= _leds.size()) return false; + bool status = digitalRead(_leds[id].pin); + return _leds[id].reverse ? !status : status; +} + +bool ledStatus(unsigned char id, bool status) { + if (id <= _leds.size()) return false; + bool s = _leds[id].reverse ? !status : status; + digitalWrite(_leds[id].pin, _leds[id].reverse ? !status : status); + return status; +} + +bool ledToggle(unsigned char id) { + if (id <= _leds.size()) return false; + return ledStatus(id, !ledStatus(id)); +} + +void ledBlink(unsigned char id, unsigned long delayOff, unsigned long delayOn) { + if (id <= _leds.size()) return; static unsigned long next = millis(); - static bool status = HIGH; if (next < millis()) { - status = !status; - digitalWrite(LED_PIN, LED_PIN_INVERSE ? !status : status); - next += ((status) ? delayOff : delayOn); + next += (ledToggle(id) ? delayOn : delayOff); } } void showStatus() { if (wifiConnected()) { if (WiFi.getMode() == WIFI_AP) { - blink(2000, 2000); + ledBlink(0, 2000, 2000); } else { - blink(5000, 500); + ledBlink(0, 5000, 500); } } else { - blink(500, 500); + ledBlink(0, 500, 500); } } +void ledMQTTCallback(unsigned int type, const char * topic, const char * payload) { + + static bool isFirstMessage = true; + + String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER); + + if (type == MQTT_CONNECT_EVENT) { + char buffer[strlen(MQTT_LED_TOPIC) + mqttSetter.length() + 3]; + sprintf(buffer, "%s/+%s", MQTT_LED_TOPIC, mqttSetter.c_str()); + mqttSubscribe(buffer); + } + + if (type == MQTT_MESSAGE_EVENT) { + + // Match topic + String t = String(topic); + if (!t.startsWith(MQTT_LED_TOPIC)) return; + if (!t.endsWith(mqttSetter)) return; + + // Get led ID + unsigned int ledID = topic[strlen(MQTT_LED_TOPIC)+1] - '0'; + if (ledID >= ledCount()) ledID = 0; + + // get value + unsigned int value = (char)payload[0] - '0'; + bool bitAuto = (value & 0x02) > 0; + bool bitState = (value & 0x01) > 0; + + // Check ledAuto + if (ledID == 0) { + ledAuto = bitAuto ? bitState : false; + setSetting("ledAuto", String() + (ledAuto ? "1" : "0")); + if (bitAuto) return; + } + + // Action to perform + ledStatus(ledID, bitState); + + } + +} + +unsigned char ledCount() { + return _leds.size(); +} + +void ledConfigure() { + ledAuto = getSetting("ledAuto", String() + LED_AUTO).toInt() == 1; +} + void ledSetup() { - pinMode(LED_PIN, OUTPUT); + + #ifdef LED1_PIN + _leds.push_back((led_t) { LED1_PIN, LED1_PIN_INVERSE }); + #endif + #ifdef LED2_PIN + _leds.push_back((led_t) { LED2_PIN, LED2_PIN_INVERSE }); + #endif + #ifdef LED3_PIN + _leds.push_back((led_t) { LED3_PIN, LED3_PIN_INVERSE }); + #endif + #ifdef LED4_PIN + _leds.push_back((led_t) { LED4_PIN, LED4_PIN_INVERSE }); + #endif + + for (unsigned int i=0; i < _leds.size(); i++) { + pinMode(_leds[i].pin, OUTPUT); + ledStatus(i, false); + } + + ledConfigure(); + + mqttRegister(ledMQTTCallback); + + DEBUG_MSG("[LED] Number of leds: %d\n", _leds.size()); + } void ledLoop() { - showStatus(); + if (ledAuto) showStatus(); } #else diff --git a/code/src/main.ino b/code/src/main.ino index c87b5c2b..8019104d 100644 --- a/code/src/main.ino +++ b/code/src/main.ino @@ -29,8 +29,9 @@ along with this program. If not, see . #include #include #include "FS.h" -String getSetting(const String& key, String defaultValue = ""); -bool relayStatus(unsigned char id, bool status, bool report = true); +void mqttRegister(void (*callback)(unsigned int, const char *, const char *)); +template bool setSetting(const String& key, T value); +template String getSetting(const String& key, T defaultValue); // ----------------------------------------------------------------------------- // METHODS @@ -85,9 +86,12 @@ void welcome() { DEBUG_MSG("%s\n%s\n\n", (char *) APP_AUTHOR, (char *) APP_WEBSITE); //DEBUG_MSG("Device: %s\n", (char *) getIdentifier().c_str()); DEBUG_MSG("ChipID: %06X\n", ESP.getChipId()); + DEBUG_MSG("CPU frequency: %d MHz\n", ESP.getCpuFreqMHz()); DEBUG_MSG("Last reset reason: %s\n", (char *) ESP.getResetReason().c_str()); DEBUG_MSG("Memory size: %d bytes\n", ESP.getFlashChipSize()); DEBUG_MSG("Free heap: %d bytes\n", ESP.getFreeHeap()); + DEBUG_MSG("Firmware size: %d bytes\n", ESP.getSketchSize()); + DEBUG_MSG("Free firmware space: %d bytes\n", ESP.getFreeSketchSpace()); FSInfo fs_info; if (SPIFFS.info(fs_info)) { DEBUG_MSG("File system total size: %d bytes\n", fs_info.totalBytes); @@ -108,7 +112,7 @@ void setup() { settingsSetup(); if (getSetting("hostname").length() == 0) { - setSetting("hostname", String() + getIdentifier()); + setSetting("hostname", getIdentifier()); saveSettings(); } diff --git a/code/src/mqtt.ino b/code/src/mqtt.ino index f1507512..d69b5d82 100644 --- a/code/src/mqtt.ino +++ b/code/src/mqtt.ino @@ -57,6 +57,7 @@ void _mqttOnConnect(bool sessionPresent) { // Build MQTT topics buildTopics(); + mqtt.setWill((mqttTopic + MQTT_HEARTBEAT_TOPIC).c_str(), MQTT_QOS, MQTT_RETAIN, (char *) "0"); // Say hello and report our IP and VERSION mqttSend((char *) MQTT_IP_TOPIC, (char *) getIP().c_str()); @@ -100,7 +101,7 @@ void mqttConnect() { if (!mqtt.connected()) { String host = getSetting("mqttServer", MQTT_SERVER); - String port = getSetting("mqttPort", String(MQTT_PORT)); + String port = getSetting("mqttPort", MQTT_PORT); String user = getSetting("mqttUser"); String pass = getSetting("mqttPassword"); @@ -112,12 +113,15 @@ void mqttConnect() { mqtt .setKeepAlive(MQTT_KEEPALIVE) .setCleanSession(false) - //.setWill("topic/online", 2, true, "no") .setClientId(getSetting("hostname", HOSTNAME).c_str()); - if ((user != "") & (pass != "")) { - DEBUG_MSG(" as user %s.\n", (char *) user.c_str()); - mqtt.setCredentials(user.c_str(), pass.c_str()); + if ((user != "") && (pass != "")) { + DEBUG_MSG(" as user '%s'.\n", (char *) user.c_str()); + char username[user.length()+1]; + user.toCharArray(username, user.length()+1); + char password[pass.length()+1]; + pass.toCharArray(password, pass.length()+1); + mqtt.setCredentials(username, password); } else { DEBUG_MSG(" anonymously\n"); } diff --git a/code/src/nofuss.ino b/code/src/nofuss.ino index bcb94415..35b1e20e 100644 --- a/code/src/nofuss.ino +++ b/code/src/nofuss.ino @@ -74,7 +74,7 @@ void nofussLoop() { static unsigned long last_check = 0; if (!wifiConnected()) return; -unsigned long interval = getSetting("nofussInterval", String(NOFUSS_INTERVAL)).toInt(); +unsigned long interval = getSetting("nofussInterval", NOFUSS_INTERVAL).toInt(); if ((last_check > 0) && ((millis() - last_check) < interval)) return; last_check = millis(); NoFUSSClient.handle(); diff --git a/code/src/pow.ino b/code/src/pow.ino index 0676ead5..428838e5 100644 --- a/code/src/pow.ino +++ b/code/src/pow.ino @@ -41,18 +41,18 @@ void powDettachInterrupts() { } void powSaveCalibration() { - setSetting("powPowerMult", String() + hlw8012.getPowerMultiplier()); - setSetting("powCurrentMult", String() + hlw8012.getCurrentMultiplier()); - setSetting("powVoltageMult", String() + hlw8012.getVoltageMultiplier()); + setSetting("powPowerMult", hlw8012.getPowerMultiplier()); + setSetting("powCurrentMult", hlw8012.getCurrentMultiplier()); + setSetting("powVoltageMult", hlw8012.getVoltageMultiplier()); } void powRetrieveCalibration() { double value; - value = getSetting("powPowerMult", "0").toFloat(); + value = getSetting("powPowerMult", 0).toFloat(); if (value > 0) hlw8012.setPowerMultiplier((int) value); - value = getSetting("powCurrentMult", "0").toFloat(); + value = getSetting("powCurrentMult", 0).toFloat(); if (value > 0) hlw8012.setCurrentMultiplier((int) value); - value = getSetting("powVoltageMult", "0").toFloat(); + value = getSetting("powVoltageMult", 0).toFloat(); if (value > 0) hlw8012.setVoltageMultiplier((int) value); } diff --git a/code/src/relay.ino b/code/src/relay.ino index 99e6348f..5042eab7 100644 --- a/code/src/relay.ino +++ b/code/src/relay.ino @@ -24,8 +24,10 @@ Ticker inching; // ----------------------------------------------------------------------------- void relayMQTT(unsigned char id) { - char buffer[10]; - sprintf(buffer, MQTT_RELAY_TOPIC, id); + if (id >= _relays.size()) return; + String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); + char buffer[strlen(MQTT_RELAY_TOPIC) + mqttGetter.length() + 3]; + sprintf(buffer, "%s/%d%s", MQTT_RELAY_TOPIC, id, mqttGetter.c_str()); mqttSend(buffer, (char *) (relayStatus(id) ? "1" : "0")); } @@ -54,8 +56,10 @@ void relayWS() { bool relayStatus(unsigned char id) { #ifdef SONOFF_DUAL + if (id >= 2) return false; return ((dualRelayStatus & (1 << id)) > 0); #else + if (id >= _relays.size()) return false; return (digitalRead(_relays[id]) == HIGH); #endif } @@ -87,6 +91,8 @@ void relayInching(unsigned char id) { bool relayStatus(unsigned char id, bool status, bool report) { + if (id >= _relays.size()) return false; + bool changed = false; if (relayStatus(id) != status) { @@ -122,13 +128,17 @@ bool relayStatus(unsigned char id, bool status, bool report) { } +bool relayStatus(unsigned char id, bool status) { + return relayStatus(id, status, true); +} + void relaySync(unsigned char id) { if (_relays.size() > 1) { recursive = true; - byte relaySync = getSetting("relaySync", String(RELAY_SYNC)).toInt(); + byte relaySync = getSetting("relaySync", RELAY_SYNC).toInt(); bool status = relayStatus(id); // If RELAY_SYNC_SAME all relays should have the same state @@ -182,6 +192,7 @@ void relayRetrieve() { } void relayToggle(unsigned char id) { + if (id >= _relays.size()) return; relayStatus(id, !relayStatus(id)); } @@ -193,36 +204,41 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo static bool isFirstMessage = true; + String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER); + String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); + bool sameSetGet = mqttGetter.compareTo(mqttSetter) == 0; + if (type == MQTT_CONNECT_EVENT) { relayMQTT(); - mqttSubscribe("/relay/#"); + char buffer[strlen(MQTT_RELAY_TOPIC) + mqttSetter.length() + 3]; + sprintf(buffer, "%s/+%s", MQTT_RELAY_TOPIC, mqttSetter.c_str()); + mqttSubscribe(buffer); } if (type == MQTT_MESSAGE_EVENT) { // Match topic - if (memcmp("/relay/", topic, 7) != 0) return; + String t = String(topic); + if (!t.startsWith(MQTT_RELAY_TOPIC)) return; + if (!t.endsWith(mqttSetter)) return; // If relayMode is not SAME avoid responding to a retained message - if (isFirstMessage) { + if (sameSetGet && isFirstMessage) { isFirstMessage = false; - byte relayMode = getSetting("relayMode", String(RELAY_MODE)).toInt(); + byte relayMode = getSetting("relayMode", RELAY_MODE).toInt(); if (relayMode != RELAY_MODE_SAME) return; } // Get relay ID - unsigned int relayID = topic[strlen(topic)-1] - '0'; + unsigned int relayID = topic[strlen(MQTT_RELAY_TOPIC)+1] - '0'; if (relayID >= relayCount()) relayID = 0; // Action to perform - if ((char)payload[0] == '0') { - relayStatus(relayID, false, false); - } - if ((char)payload[0] == '1') { - relayStatus(relayID, true, false); - } - if ((char)payload[0] == '2') { + unsigned int value = (char)payload[0] - '0'; + if (value == 2) { relayToggle(relayID); + } else { + relayStatus(relayID, value > 0, !sameSetGet); } } @@ -255,7 +271,7 @@ void relaySetup() { #endif EEPROM.begin(4096); - byte relayMode = getSetting("relayMode", String(RELAY_MODE)).toInt(); + byte relayMode = getSetting("relayMode", RELAY_MODE).toInt(); for (unsigned int i=0; i < _relays.size(); i++) { pinMode(_relays[i], OUTPUT); diff --git a/code/src/rf.ino b/code/src/rf.ino index 8a07f9d7..817c0a8e 100644 --- a/code/src/rf.ino +++ b/code/src/rf.ino @@ -33,7 +33,7 @@ void rfBuildCodes() { unsigned long code = 0; // channel - unsigned int channel = getSetting("rfChannel", String(RF_CHANNEL)).toInt(); + unsigned int channel = getSetting("rfChannel", RF_CHANNEL).toInt(); for (byte i = 0; i < 5; i++) { code *= 3; if (channel & 1) code += 1; @@ -41,7 +41,7 @@ void rfBuildCodes() { } // device - unsigned int device = getSetting("rfDevice", String(RF_DEVICE)).toInt(); + unsigned int device = getSetting("rfDevice", RF_DEVICE).toInt(); for (byte i = 0; i < 5; i++) { code *= 3; if (device != i) code += 2; diff --git a/code/src/settings.ino b/code/src/settings.ino index 29e0826d..b5093d72 100644 --- a/code/src/settings.ino +++ b/code/src/settings.ino @@ -20,6 +20,21 @@ Embedis embedis(Serial); // Settings // ----------------------------------------------------------------------------- +unsigned long settingsSize() { + bool zero = false; + unsigned long size = 0; + for (unsigned int i = SPI_FLASH_SEC_SIZE; i>0; i--) { + size++; + if (EEPROM.read(i) == 0) { + if (zero) break; + zero = true; + } else { + zero = false; + } + } + return size-2; +} + void settingsSetup() { EEPROM.begin(SPI_FLASH_SEC_SIZE); @@ -35,24 +50,46 @@ void settingsSetup() { #endif ); - Embedis::hardware( F("wifi"), [](Embedis* e) { + Embedis::hardware( F("WIFI"), [](Embedis* e) { StreamString s; WiFi.printDiag(s); e->response(s); }, 0); - Embedis::command( F("reconnect"), [](Embedis* e) { + Embedis::command( F("RECONNECT"), [](Embedis* e) { wifiConfigure(); wifiDisconnect(); e->response(Embedis::OK); }); - Embedis::command( F("reset"), [](Embedis* e) { + Embedis::command( F("RESET"), [](Embedis* e) { e->response(Embedis::OK); ESP.reset(); }); - DEBUG_MSG("[SETTINGS] Initialized\n"); + Embedis::command( F("EEPROM.DUMP"), [](Embedis* e) { + for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) { + if (i % 16 == 0) Serial.printf("\n[%04X] ", i); + Serial.printf("%02X ", EEPROM.read(i)); + } + Serial.printf("\n"); + e->response(Embedis::OK); + }); + + Embedis::command( F("EEPROM.ERASE"), [](Embedis* e) { + for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) { + EEPROM.write(i, 0xFF); + } + EEPROM.commit(); + e->response(Embedis::OK); + }); + + Embedis::command( F("SETTINGS.SIZE"), [](Embedis* e) { + e->response(String(settingsSize())); + }); + + DEBUG_MSG("[SETTINGS] EEPROM size: %d bytes\n", SPI_FLASH_SEC_SIZE); + DEBUG_MSG("[SETTINGS] Settings size: %d bytes\n", settingsSize()); } @@ -60,14 +97,18 @@ void settingsLoop() { embedis.process(); } -String getSetting(const String& key, String defaultValue) { +template String getSetting(const String& key, T defaultValue) { String value; - if (!Embedis::get(key, value)) value = defaultValue; + if (!Embedis::get(key, value)) value = String(defaultValue); return value; } -bool setSetting(const String& key, String& value) { - return Embedis::set(key, value); +String getSetting(const String& key) { + return getSetting(key, ""); +} + +template bool setSetting(const String& key, T value) { + return Embedis::set(key, String(value)); } bool delSetting(const String& key) { diff --git a/code/src/web.ino b/code/src/web.ino index cee4cc56..4bbaa97b 100644 --- a/code/src/web.ino +++ b/code/src/web.ino @@ -155,12 +155,12 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { // Checkboxes if (apiEnabled != (getSetting("apiEnabled").toInt() == 1)) { - setSetting("apiEnabled", String() + (apiEnabled ? 1 : 0)); + setSetting("apiEnabled", apiEnabled); dirty = true; } #if ENABLE_FAUXMO if (fauxmoEnabled != (getSetting("fauxmoEnabled").toInt() == 1)) { - setSetting("fauxmoEnabled", String() + (fauxmoEnabled ? 1 : 0)); + setSetting("fauxmoEnabled", fauxmoEnabled); dirty = true; } #endif @@ -229,7 +229,7 @@ void _wsStart(uint32_t client_id) { root["mqttStatus"] = mqttConnected(); root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER); - root["mqttPort"] = getSetting("mqttPort", String(MQTT_PORT)); + root["mqttPort"] = getSetting("mqttPort", MQTT_PORT); root["mqttUser"] = getSetting("mqttUser"); root["mqttPassword"] = getSetting("mqttPassword"); root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC); @@ -238,12 +238,12 @@ void _wsStart(uint32_t client_id) { for (unsigned char relayID=0; relayID 1) { root["multirelayVisible"] = 1; - root["relaySync"] = getSetting("relaySync", String(RELAY_SYNC)); + root["relaySync"] = getSetting("relaySync", RELAY_SYNC); } root["apiEnabled"] = getSetting("apiEnabled").toInt() == 1; @@ -251,7 +251,7 @@ void _wsStart(uint32_t client_id) { #if ENABLE_FAUXMO root["fauxmoVisible"] = 1; - root["fauxmoEnabled"] = getSetting("fauxmoEnabled", String(FAUXMO_ENABLED)).toInt() == 1; + root["fauxmoEnabled"] = getSetting("fauxmoEnabled", FAUXMO_ENABLED).toInt() == 1; #endif #if ENABLE_DS18B20 @@ -267,15 +267,15 @@ void _wsStart(uint32_t client_id) { #if ENABLE_RF root["rfVisible"] = 1; - root["rfChannel"] = getSetting("rfChannel", String(RF_CHANNEL)); - root["rfDevice"] = getSetting("rfDevice", String(RF_DEVICE)); + root["rfChannel"] = getSetting("rfChannel", RF_CHANNEL); + root["rfDevice"] = getSetting("rfDevice", RF_DEVICE); #endif #if ENABLE_EMON root["emonVisible"] = 1; root["emonPower"] = getPower(); - root["emonMains"] = getSetting("emonMains", String(EMON_MAINS_VOLTAGE)); - root["emonRatio"] = getSetting("emonRatio", String(EMON_CURRENT_RATIO)); + root["emonMains"] = getSetting("emonMains", EMON_MAINS_VOLTAGE); + root["emonRatio"] = getSetting("emonRatio", EMON_CURRENT_RATIO); #endif #if ENABLE_POW diff --git a/code/src/wifi.ino b/code/src/wifi.ino index 5a055020..e8c1bb39 100644 --- a/code/src/wifi.ino +++ b/code/src/wifi.ino @@ -8,6 +8,9 @@ Copyright (C) 2016 by Xose Pérez */ #include "JustWifi.h" +#include + +DNSServer dnsServer; // ----------------------------------------------------------------------------- // WIFI @@ -147,10 +150,21 @@ void wifiSetup() { } + // Configure captive portal + if (code == MESSAGE_ACCESSPOINT_CREATED) { + dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); + } + if (code == MESSAGE_DISCONNECTED) { + dnsServer.stop(); + } + }); } void wifiLoop() { jw.loop(); + if (WiFi.getMode() == WIFI_AP) { + dnsServer.processNextRequest(); + } } diff --git a/docs/Hardware.md b/docs/Hardware.md index f13d4121..a62ce51f 100644 --- a/docs/Hardware.md +++ b/docs/Hardware.md @@ -9,16 +9,20 @@ This is the official list of supported hardware for the ESPurna firmware. The ha * [IteadStudio Sonoff TH](#iteadstudio-sonoff-th) * [IteadStudio Sonoff POW](#iteadstudio-sonoff-pow) * [IteadStudio Sonoff DUAL](#iteadstudio-sonoff-dual) +* [IteadStudio Sonoff TOUCH](#iteadstudio-sonoff-touch) +* [IteadStudio Sonoff 4CH](#iteadstudio-sonoff-4ch) +* [IteadStudio Sonoff SV](#iteadstudio-sonoff-sv) * [IteadStudio Slampher](#iteadstudio-slampher) * [IteadStudio S20](#iteadstudio-s20) * [Electrodragon ESP Relay Board](#electrodragon-esp-relay-board) +* [WorkChoice EcoPlug](#workchoice-ecoplug) ## IteadStudio Sonoff |Property|Value| |---|---| |Manufacturer|Itead Studio| -|Web page|https://www.itead.cc/sonoff-wifi-wireless-switch.html| +|Web page|[https://www.itead.cc/sonoff-wifi-wireless-switch.html](https://www.itead.cc/sonoff-wifi-wireless-switch.html)| |Build flag|SONOFF| The [IteadStudio Sonoff][1] has an ESP8266 on board with a 8Mbit flash memory chip, a mains to 3V3 transformer and a relay (GPIO12). It also features a button (GPIO0), an LED (GPIO13) and an unpopulated header you can use to reprogram it. @@ -57,7 +61,7 @@ My recommendation is to **temporary shortcut the bottom pad of the unpopulated R |Property|Value| |---|---| |Manufacturer|Itead Studio| -|Web page|https://www.itead.cc/sonoff-th.html| +|Web page|[https://www.itead.cc/sonoff-th.html](https://www.itead.cc/sonoff-th.html)| |Build flag|SONOFF_TH| ### Flashing @@ -73,7 +77,7 @@ As in the Sonoff the button is connected to GPIO0, so to enter flash mode press |Property|Value| |---|---| |Manufacturer|Itead Studio| -|Web page|https://www.itead.cc/sonoff-pow.html| +|Web page|[https://www.itead.cc/sonoff-pow.html](https://www.itead.cc/sonoff-pow.html)| |Build flag|SONOFF_POW| ### Flashing @@ -87,7 +91,7 @@ Same as for the [Sonoff TH](#iteadstudio-sonoff-th) above. |Property|Value| |---|---| |Manufacturer|Itead Studio| -|Web page|https://www.itead.cc/sonoff-dual.html| +|Web page|[https://www.itead.cc/sonoff-dual.html](https://www.itead.cc/sonoff-dual.html)| |Build flag|SONOFF_DUAL| ### Flashing @@ -100,12 +104,48 @@ In the picture above you have a location of an available and easily accessible G Once flashed use OTA to update the firmware or the filesystem. +## IteadStudio Sonoff TOUCH + +|Property|Value| +|---|---| +|Manufacturer|Itead Studio| +|Web page|[https://www.itead.cc/sonoff-touch.html](https://www.itead.cc/sonoff-touch.html)| +|Build flag|SONOFF_TOUCH| + +### Flashing + +*TODO* + +## IteadStudio Sonoff 4CH + +|Property|Value| +|---|---| +|Manufacturer|Itead Studio| +|Web page|[https://www.itead.cc/sonoff-4ch.html](https://www.itead.cc/sonoff-4ch.html)| +|Build flag|SONOFF_4CH| + +### Flashing + +*TODO* + +## IteadStudio Sonoff SV + +|Property|Value| +|---|---| +|Manufacturer|Itead Studio| +|Web page|[https://www.itead.cc/sonoff-sv.html](https://www.itead.cc/sonoff-sv.html)| +|Build flag|SONOFF_SV| + +### Flashing + +*TODO* + ## IteadStudio Slampher |Property|Value| |---|---| |Manufacturer|Itead Studio| -|Web page|https://www.itead.cc/slampher.html| +|Web page|[https://www.itead.cc/slampher.html](https://www.itead.cc/slampher.html)| |Build flag|SLAMPHER| ### Flashing @@ -124,7 +164,7 @@ My recommendation is to **temporary shortcut the right pad of the unpopulated R2 |Property|Value| |---|---| |Manufacturer|Itead Studio| -|Web page|https://www.itead.cc/smart-socket.html| +|Web page|[https://www.itead.cc/smart-socket.html](https://www.itead.cc/smart-socket.html)| |Build flag|S20| ### Flashing @@ -140,7 +180,7 @@ Solder a 4 pin male or female header and connect it to your USB-to-UART bridge. |Property|Value| |---|---| |Manufacturer|Electrodragon| -|Web page|http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/| +|Web page|[http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/](http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/)| |Build flag|ESP_RELAY_BOARD| ### Flashing @@ -154,6 +194,19 @@ The Electrodragon ESP Relay Board is pretty easy to flash IF you do not follow t * The TX pin in the header should go to your programmer RX pin * The button labeled BTN2 is connected to GPIO0, so hold it down while powering the board, I've had better results keeping it down until the flashing starts +## WorkChoice EcoPlug + +|Property|Value| +|---|---| +|Manufacturer|WorkChoice| +|Web page (non-official)|[http://thegreatgeekery.blogspot.com.es/2016/02/ecoplug-wifi-switch-hacking.html](http://thegreatgeekery.blogspot.com.es/2016/02/ecoplug-wifi-switch-hacking.html)| +|Build flag|ECOPLUG| + +### Flashing + +*TODO* + + [1]: https://www.itead.cc/sonoff-wifi-wireless-switch.html [2]: https://www.itead.cc/sonoff-rf.html