/* ESPurna Copyright (C) 2016 by Xose PĆ©rez This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "FS.h" #include #include // ----------------------------------------------------------------------------- // ConfiguraciĆ³ // ----------------------------------------------------------------------------- #define DEBUG #define ENABLE_RF 1 #define ENABLE_OTA 1 #define ENABLE_MQTT 1 #define ENABLE_WEBSERVER 1 #define ENABLE_ENERGYMONITOR 1 #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 ADMIN_PASS "fibonacci" #define CONFIG_PATH "/.config" #define BUFFER_SIZE 1024 #define STATUS_UPDATE_INTERVAL 10000 #define RF_PIN 14 #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 #define CURRENT_RATIO 156 #define CURRENT_PRECISION 1 #define SAMPLES_X_MEASUREMENT 1500 #define MEASUREMENT_INTERVAL 10000 #define MEASUREMENTS_X_MESSAGE 6 // ----------------------------------------------------------------------------- // Globals // ----------------------------------------------------------------------------- char identifier[20] = {0}; byte status = WIFI_STATUS_CONNECTING; String configSSID[NETWORK_BUFFER]; String configPASS[NETWORK_BUFFER]; DebounceEvent button1 = false; #if ENABLE_WEBSERVER ESP8266WebServer server(80); #endif #if ENABLE_MQTT WiFiClient client; PubSubClient mqtt(client); String mqttServer = ""; String mqttTopic = MQTT_TOPIC; String mqttPort = String(MQTT_PORT); String mqttUser = ""; String mqttPassword = ""; char mqttStatusTopic[30]; char mqttIPTopic[30]; #if ENABLE_ENERGYMONITOR char mqttPowerTopic[30]; #endif bool isMQTTMessage = false; #endif #if ENABLE_RF unsigned long rfCode = 0; unsigned long rfCodeON = 0; unsigned long rfCodeOFF = 0; String rfChannel = String(RF_CHANNEL); String rfDevice = String(RF_DEVICE); #endif #if ENABLE_ENERGYMONITOR EnergyMonitor monitor; #endif // ----------------------------------------------------------------------------- // Utils // ----------------------------------------------------------------------------- char * getCompileTime(char * buffer) { int day, month, year, hour, minute, second; // parse date String tmp = String(__DATE__); day = tmp.substring(4,6).toInt(); year = tmp.substring(7).toInt(); tmp = tmp.substring(0,3); if (tmp.equals("Jan")) month = 1; if (tmp.equals("Feb")) month = 2; if (tmp.equals("Mar")) month = 3; if (tmp.equals("Apr")) month = 4; if (tmp.equals("May")) month = 5; if (tmp.equals("Jun")) month = 6; if (tmp.equals("Jul")) month = 7; if (tmp.equals("Aug")) month = 8; if (tmp.equals("Sep")) month = 9; if (tmp.equals("Oct")) month = 10; if (tmp.equals("Nov")) month = 11; if (tmp.equals("Dec")) month = 12; // parse time tmp = String(__TIME__); hour = tmp.substring(0,2).toInt(); minute = tmp.substring(3,5).toInt(); second = tmp.substring(6,8).toInt(); sprintf(buffer, "%d%02d%02d%02d%02d%02d", year, month, day, hour, minute, second); buffer[14] = 0; return buffer; } // ----------------------------------------------------------------------------- // Relay // ----------------------------------------------------------------------------- void switchRelayOn() { if (!digitalRead(RELAY_PIN)) { #ifdef DEBUG Serial.println("Turning the relay ON"); #endif #if ENABLE_MQTT if (!isMQTTMessage && mqtt.connected()) { mqtt.publish(mqttStatusTopic, "1", MQTT_RETAIN); } #endif digitalWrite(RELAY_PIN, HIGH); if (EEPROM.read(0) == 0) { EEPROM.write(0, 1); EEPROM.commit(); } } } void switchRelayOff() { if (digitalRead(RELAY_PIN)) { #ifdef DEBUG Serial.println("Turning the relay OFF"); #endif #if ENABLE_MQTT if (!isMQTTMessage && mqtt.connected()) { mqtt.publish(mqttStatusTopic, "0", MQTT_RETAIN); } #endif digitalWrite(RELAY_PIN, LOW); if (EEPROM.read(0) == 1) { EEPROM.write(0, 0); EEPROM.commit(); } } } void toggleRelay() { if (digitalRead(RELAY_PIN)) { switchRelayOff(); } else { switchRelayOn(); } } // ----------------------------------------------------------------------------- // Wifi // ----------------------------------------------------------------------------- char * getIdentifier() { if (identifier[0] == 0) { sprintf(identifier, "%s_%06X", MODEL, ESP.getChipId()); } return identifier; } void wifiSetupAP() { // 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()); Serial.print(", Password: \""); Serial.print(ADMIN_PASS); Serial.print("\", IP address: "); Serial.println(WiFi.softAPIP()); #endif } void wifiSetupSTA(bool force) { byte network = 0; if (force || (WiFi.status() != WL_CONNECTED)) { // Set WIFI module to STA mode WiFi.mode(WIFI_STA); #ifdef DEBUG WiFi.printDiag(Serial); #endif #if ENABLE_MQTT if (mqtt.connected()) mqtt.disconnect(); #endif #if ENABLE_RF RemoteReceiver::disable(); #endif while (network < 3) { 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); } } 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()); Serial.print(", IP address: "); Serial.println(WiFi.localIP()); #endif } else { #ifdef DEBUG Serial.println("[STA Mode] NOT CONNECTED"); #endif wifiSetupAP(); } } void wifiLoop() { // Trying to reconnect in case of disconnection if ((status == WIFI_STATUS_CONNECTED) && (WiFi.status() != WL_CONNECTED)) { status = WIFI_STATUS_CONNECTING; wifiSetupSTA(false); } } // ----------------------------------------------------------------------------- // WebServer // ----------------------------------------------------------------------------- #if ENABLE_WEBSERVER String getContentType(String filename) { if (server.hasArg("download")) return "application/octet-stream"; else if (filename.endsWith(".htm")) return "text/html"; else if (filename.endsWith(".html")) return "text/html"; else if (filename.endsWith(".css")) return "text/css"; else if (filename.endsWith(".js")) return "application/javascript"; else if (filename.endsWith(".png")) return "image/png"; else if (filename.endsWith(".gif")) return "image/gif"; else if (filename.endsWith(".jpg")) return "image/jpeg"; else if (filename.endsWith(".ico")) return "image/x-icon"; else if (filename.endsWith(".xml")) return "text/xml"; else if (filename.endsWith(".pdf")) return "application/x-pdf"; else if (filename.endsWith(".zip")) return "application/x-zip"; else if (filename.endsWith(".gz")) return "application/x-gzip"; return "text/plain"; } void handleRelayOn() { #ifdef DEBUG Serial.println("Request: /relay/on"); #endif switchRelayOn(); server.send(200, "text/plain", "ON"); } void handleRelayOff() { #ifdef DEBUG Serial.println("Request: /relay/off"); #endif switchRelayOff(); server.send(200, "text/plain", "OFF"); } bool handleFileRead(String path) { #ifdef DEBUG Serial.println("Request: " + path); #endif if (path.endsWith("/")) path += "index.html"; String contentType = getContentType(path); String pathWithGz = path + ".gz"; if (SPIFFS.exists(pathWithGz)) path = pathWithGz; if (SPIFFS.exists(path)) { File file = SPIFFS.open(path, "r"); size_t sent = server.streamFile(file, contentType); size_t contentLength = file.size(); file.close(); return true; } return false; } void handleHome() { #ifdef DEBUG Serial.println("Request: /index.html"); #endif String filename = "/index.html"; String content = ""; char buffer[BUFFER_SIZE]; // Read file in chunks File file = SPIFFS.open(filename, "r"); int size = file.size(); while (size > 0) { size_t len = std::min(BUFFER_SIZE-1, size); file.read((uint8_t *) buffer, len); buffer[len] = 0; content += buffer; size -= len; } file.close(); // Replace placeholders getCompileTime(buffer); content.replace("{appname}", String(APP_NAME) + "." + String(buffer)); content.replace("{status}", digitalRead(RELAY_PIN) ? "1" : "0"); content.replace("{updateInterval}", String(STATUS_UPDATE_INTERVAL)); content.replace("{ssid0}", configSSID[0]); content.replace("{pass0}", configPASS[0]); content.replace("{ssid1}", configSSID[1]); content.replace("{pass1}", configPASS[1]); content.replace("{ssid2}", configSSID[2]); content.replace("{pass2}", configPASS[2]); #if ENABLE_MQTT content.replace("{mqttServer}", mqttServer); content.replace("{mqttPort}", mqttPort); content.replace("{mqttUser}", mqttUser); content.replace("{mqttPassword}", mqttPassword); content.replace("{mqttTopic}", mqttTopic); #endif #if ENABLE_RF content.replace("{rfChannel}", rfChannel); content.replace("{rfDevice}", rfDevice); #endif // Serve content String contentType = getContentType(filename); server.send(200, contentType, content); } void handleSave() { #ifdef DEBUG Serial.println("Request: /save"); #endif if (server.hasArg("status")) { if (server.arg("status") == "1") { switchRelayOn(); } else { switchRelayOff(); } } if (server.hasArg("ssid0")) configSSID[0] = server.arg("ssid0"); if (server.hasArg("pass0")) configPASS[0] = server.arg("pass0"); if (server.hasArg("ssid1")) configSSID[1] = server.arg("ssid1"); if (server.hasArg("pass1")) configPASS[1] = server.arg("pass1"); if (server.hasArg("ssid2")) configSSID[2] = server.arg("ssid2"); if (server.hasArg("pass2")) configPASS[2] = server.arg("pass2"); #if ENABLE_MQTT if (server.hasArg("mqttServer")) mqttServer = server.arg("mqttServer"); if (server.hasArg("mqttPort")) mqttPort = server.arg("mqttPort"); if (server.hasArg("mqttUser")) mqttUser = server.arg("mqttUser"); if (server.hasArg("mqttPassword")) mqttPassword = server.arg("mqttPassword"); if (server.hasArg("mqttTopic")) mqttTopic = server.arg("mqttTopic"); #endif #if ENABLE_RF if (server.hasArg("rfChannel")) rfChannel = server.arg("rfChannel"); if (server.hasArg("rfDevice")) rfDevice = server.arg("rfDevice"); #endif server.send(202, "text/json", "{}"); saveConfig(); #if ENABLE_RF rfBuildCodes(); #endif wifiSetupSTA(true); } void handleStatus() { #ifdef DEBUG //Serial.println("Request: /status"); #endif String output = "{"; output += "\"device\": \""; output += getIdentifier(); output += "\", \"ip\": \""; output += (WiFi.status() == WL_CONNECTED) ? WiFi.localIP().toString() : "NOT CONNECTED"; output += "\", \"network\": \""; output += (WiFi.status() == WL_CONNECTED) ? WiFi.SSID() : ""; output += "\", \"relay\": "; output += (digitalRead(RELAY_PIN)) ? "1": "0"; #if ENABLE_MQTT output += ", \"mqtt\": "; output += (mqtt.connected()) ? "1": "0"; #endif output += "}"; server.send(200, "text/json", output); } void webServerSetup() { // Relay control server.on("/relay/on", HTTP_GET, handleRelayOn); server.on("/relay/off", HTTP_GET, handleRelayOff); // Configuration page server.on("/save", HTTP_POST, handleSave); server.on("/status", HTTP_GET, handleStatus); server.on("/", HTTP_GET, handleHome); server.on("/index.html", HTTP_GET, handleHome); // Anything else server.onNotFound([]() { // Hidden files if (server.uri().startsWith("/.")) { server.send(403, "text/plain", "Forbidden"); return; } // Existing files in SPIFFS if (!handleFileRead(server.uri())) { server.send(404, "text/plain", "NotFound"); return; } }); // Run server server.begin(); } void webServerLoop() { server.handleClient(); } #endif // ----------------------------------------------------------------------------- // MQTT // ----------------------------------------------------------------------------- #if ENABLE_MQTT void buildTopics() { String tmp; // Replace identifier String base = mqttTopic; base.replace("{identifier}", getIdentifier()); // Get publish status topic base.toCharArray(mqttStatusTopic, base.length()+1); mqttStatusTopic[base.length()+1] = 0; // Get publish ip topic tmp = base + "/ip"; tmp.toCharArray(mqttIPTopic, tmp.length()+1); mqttIPTopic[tmp.length()+1] = 0; // Get publish current topic #if ENABLE_ENERGYMONITOR tmp = base + "/power"; tmp.toCharArray(mqttPowerTopic, tmp.length()+1); mqttPowerTopic[tmp.length()+1] = 0; #endif } void mqttCallback(char* topic, byte* payload, unsigned int length) { #ifdef DEBUG Serial.print("MQTT message "); Serial.print(topic); Serial.print(" => "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); #endif // Action to perform if ((char)payload[0] == '0') { isMQTTMessage = true; switchRelayOff(); } if ((char)payload[0] == '1') { isMQTTMessage = true; switchRelayOn(); } if ((char)payload[0] == '2') { toggleRelay(); } isMQTTMessage = false; } void mqttConnect() { if (!mqtt.connected() && (mqttServer.length()>0)) { char buffer[mqttServer.length()+1]; mqttServer.toCharArray(buffer, mqttServer.length()+1); mqtt.setServer(buffer, mqttPort.toInt()); #ifdef DEBUG Serial.print("Connecting to MQTT broker at "); Serial.print(mqttServer); #endif if (mqttUser.length() > 0) { #ifdef DEBUG Serial.print(" as user "); Serial.print(mqttUser); Serial.print(": "); #endif char user[mqttUser.length() + 1]; mqttUser.toCharArray(user, mqttUser.length() + 1); char password[mqttPassword.length() + 1]; mqttPassword.toCharArray(password, mqttPassword.length() + 1); mqtt.connect(getIdentifier(), user, password); } else { #ifdef DEBUG Serial.print(" anonymously: "); #endif mqtt.connect(getIdentifier()); } if (mqtt.connected()) { buildTopics(); #ifdef DEBUG Serial.println("connected!"); Serial.print("Subscribing to "); Serial.println(mqttStatusTopic); #endif // Say hello and report our IP String ipString = WiFi.localIP().toString(); char ip[ipString.length()+1]; ipString.toCharArray(ip, ipString.length()+1); mqtt.publish(mqttIPTopic, ip, MQTT_RETAIN); // Publish current relay status mqtt.publish(mqttStatusTopic, digitalRead(RELAY_PIN) ? "1" : "0", MQTT_RETAIN); // Subscribe to topic mqtt.subscribe(mqttStatusTopic); } else { #ifdef DEBUG Serial.print("failed, rc="); Serial.println(mqtt.state()); #endif } } } void mqttSetup() { mqtt.setCallback(mqttCallback); } void mqttLoop() { static unsigned long timeout = millis(); if (WiFi.status() == WL_CONNECTED) { if (!mqtt.connected()) { if (timeout < millis()) { mqttConnect(); timeout = millis() + MQTT_RECONNECT_DELAY; } } if (mqtt.connected()) mqtt.loop(); } } #endif // ----------------------------------------------------------------------------- // RF // ----------------------------------------------------------------------------- #if ENABLE_RF void rfLoop() { if (rfCode == 0) return; #ifdef DEBUG Serial.print("RF code: "); Serial.println(rfCode); #endif if (rfCode == rfCodeON) switchRelayOn(); if (rfCode == rfCodeOFF) switchRelayOff(); rfCode = 0; } void rfBuildCodes() { unsigned long code = 0; // channel unsigned int channel = rfChannel.toInt(); for (byte i = 0; i < 5; i++) { code *= 3; if (channel & 1) code += 1; channel >>= 1; } // device unsigned int device = rfDevice.toInt(); for (byte i = 0; i < 5; i++) { code *= 3; if (device != i) code += 2; } // status code *= 9; rfCodeOFF = code + 2; rfCodeON = code + 6; #ifdef DEBUG Serial.print("RF code ON: "); Serial.println(rfCodeON); Serial.print("RF code OFF: "); Serial.println(rfCodeOFF); #endif } void rfCallback(unsigned long code, unsigned int period) { rfCode = code; } void rfSetup() { rfBuildCodes(); RemoteReceiver::init(RF_PIN, 3, rfCallback); RemoteReceiver::enable(); } #endif // ----------------------------------------------------------------------------- // Configuration // ----------------------------------------------------------------------------- bool saveConfig() { File file = SPIFFS.open(CONFIG_PATH, "w"); if (file) { file.println("ssid0=" + configSSID[0]); file.println("pass0=" + configPASS[0]); file.println("ssid1=" + configSSID[1]); file.println("pass1=" + configPASS[1]); file.println("ssid2=" + configSSID[2]); file.println("pass2=" + configPASS[2]); #if ENABLE_MQTT file.println("mqttServer=" + mqttServer); file.println("mqttPort=" + mqttPort); file.println("mqttTopic=" + mqttTopic); #endif #if ENABLE_RF file.println("rfChannel=" + rfChannel); file.println("rfDevice=" + rfDevice); #endif file.close(); return true; } return false; } bool loadConfig() { if (SPIFFS.exists(CONFIG_PATH)) { #ifdef DEBUG Serial.println("Reading config file"); #endif // Read contents File file = SPIFFS.open(CONFIG_PATH, "r"); String content = file.readString(); file.close(); // Parse contents content.replace("\r\n", "\n"); content.replace("\r", "\n"); int start = 0; int end = content.indexOf("\n", start); while (end > 0) { String line = content.substring(start, end); #ifdef DEBUG Serial.println(line); #endif if (line.startsWith("ssid0=")) configSSID[0] = line.substring(6); else if (line.startsWith("pass0=")) configPASS[0] = line.substring(6); else if (line.startsWith("ssid1=")) configSSID[1] = line.substring(6); else if (line.startsWith("pass1=")) configPASS[1] = line.substring(6); else if (line.startsWith("ssid2=")) configSSID[2] = line.substring(6); else if (line.startsWith("pass2=")) configPASS[2] = line.substring(6); #if ENABLE_MQTT else if (line.startsWith("mqttServer=")) mqttServer = line.substring(11); else if (line.startsWith("mqttPort=")) mqttPort = line.substring(9); else if (line.startsWith("mqttTopic=")) mqttTopic = line.substring(10); #endif #if ENABLE_RF else if (line.startsWith("rfChannel=")) rfChannel = line.substring(10); else if (line.startsWith("rfDevice=")) rfDevice = line.substring(9); #endif if (end < 0) break; start = end + 1; end = content.indexOf("\n", start); } return true; } return false; } // ----------------------------------------------------------------------------- // OTA // ----------------------------------------------------------------------------- #if ENABLE_OTA void OTASetup() { // Port defaults to 8266 ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname(getIdentifier()); // No authentication by default ArduinoOTA.setPassword((const char *) ADMIN_PASS); ArduinoOTA.onStart([]() { #if ENABLE_RF RemoteReceiver::disable(); #endif #ifdef DEBUG Serial.println("OTA - Start"); #endif }); ArduinoOTA.onEnd([]() { #ifdef DEBUG Serial.println("OTA - End"); #endif #if ENABLE_RF RemoteReceiver::enable(); #endif }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { #ifdef DEBUG Serial.printf("Progress: %u%%\r", (progress / (total / 100))); #endif }); ArduinoOTA.onError([](ota_error_t error) { #ifdef DEBUG Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); #endif }); ArduinoOTA.begin(); } void OTALoop() { ArduinoOTA.handle(); } #endif // ----------------------------------------------------------------------------- // Energy Monitor // ----------------------------------------------------------------------------- #if ENABLE_ENERGYMONITOR void energyMonitorSetup() { monitor.initCurrent(CURRENT_PIN, REFERENCE_VOLTAGE, CURRENT_RATIO); monitor.setPrecision(CURRENT_PRECISION); } void energyMonitorLoop() { static unsigned long next_measurement = millis(); static byte measurements = 0; static double sum = 0; if (millis() > next_measurement) { double current = monitor.getCurrent(SAMPLES_X_MEASUREMENT); sum += current; ++measurements; #ifdef DEBUG Serial.print("Power reading: "); Serial.println(current * MAINS_VOLTAGE); #endif if (measurements == MEASUREMENTS_X_MESSAGE) { char buffer[8]; sprintf(buffer, "%d", int(sum * MAINS_VOLTAGE / measurements)); #ifdef DEBUG Serial.print("Power sending: "); Serial.println(buffer); #endif #if ENABLE_MQTT mqtt.publish(mqttPowerTopic, buffer, MQTT_RETAIN); #endif sum = 0; measurements = 0; } next_measurement += MEASUREMENT_INTERVAL; } } #endif // ----------------------------------------------------------------------------- // Hardware (buttons, LEDs,...) // ----------------------------------------------------------------------------- void hardwareSetup() { Serial.begin(115200); pinMode(RELAY_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); pinMode(RF_PIN, INPUT_PULLUP); button1 = DebounceEvent(BUTTON_PIN); SPIFFS.begin(); EEPROM.begin(1); EEPROM.read(0) == 1 ? switchRelayOn() : switchRelayOff(); } void blink(unsigned long delayOff, unsigned long delayOn) { static unsigned long next = millis(); static bool status = HIGH; if (next < millis()) { status = !status; digitalWrite(LED_PIN, status); next += ((status) ? delayOff : delayOn); } } void showStatus() { if (WiFi.status() == WL_CONNECTED) { blink(5000, 500); } else { blink(500, 500); } } void hardwareLoop() { if (button1.loop()) { if (button1.getEvent() == EVENT_SINGLE_CLICK) toggleRelay(); if (button1.getEvent() == EVENT_DOUBLE_CLICK) wifiSetupAP(); if (button1.getEvent() == EVENT_LONG_CLICK) ESP.reset(); } showStatus(); } // ----------------------------------------------------------------------------- // Booting // ----------------------------------------------------------------------------- void welcome() { Serial.println(); Serial.println(APP_NAME); Serial.println(APP_WEBSITE); Serial.println(APP_AUTHOR); Serial.println(); Serial.print("Device: "); Serial.println(getIdentifier()); Serial.print("Last reset reason: "); Serial.println(ESP.getResetReason()); Serial.println(); } void setup() { hardwareSetup(); welcome(); #if ENABLE_OTA OTASetup(); #endif loadConfig(); wifiSetupSTA(false); #if ENABLE_WEBSERVER webServerSetup(); #endif #if ENABLE_MQTT mqttSetup(); #endif #if ENABLE_RF rfSetup(); #endif #if ENABLE_ENERGYMONITOR energyMonitorSetup(); #endif } void loop() { #if ENABLE_OTA OTALoop(); #endif wifiLoop(); #if ENABLE_MQTT mqttLoop(); #endif #if ENABLE_RF rfLoop(); #endif #if ENABLE_WEBSERVER webServerLoop(); #endif #if ENABLE_ENERGYMONITOR energyMonitorLoop(); #endif hardwareLoop(); delay(1); }