diff --git a/code/deploy b/code/deploy index 8bd0ad79..d80f4b65 100755 --- a/code/deploy +++ b/code/deploy @@ -60,4 +60,4 @@ if valid_ip $ip; then exit 3 fi -echo platformio run -vv -e $device --target upload --upload-port $ip +platformio run -vv -e $device --target upload --upload-port $ip diff --git a/code/platformio.ini b/code/platformio.ini index 6aec6b21..a83933ee 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -81,7 +81,7 @@ board = esp01_1m lib_install = 89,64,19 topic = /home/cellar/washer/ip build_flags = -Wl,-Tesp8266.flash.1m256.ld -build_flags = -D SONOFF -D DEBUG -D ENABLE_POWER -D ENABLE_DHT +build_flags = -D SONOFF -D DEBUG -D ENABLE_EMON -D ENABLE_DHT upload_speed = 115200 upload_port = "192.168.1.114" upload_flags = --auth=fibonacci --port 8266 @@ -97,3 +97,15 @@ build_flags = -D SONOFF -D DEBUG upload_speed = 115200 upload_port = "192.168.1.114" upload_flags = --auth=fibonacci --port 8266 + +[env:living-lamp-device] +platform = espressif +framework = arduino +board = esp01_1m +lib_install = 89,64,19 +topic = /home/living/lamp/ip +build_flags = -Wl,-Tesp8266.flash.1m256.ld +build_flags = -D SONOFF -D DEBUG -D RF +upload_speed = 115200 +upload_port = "192.168.1.114" +upload_flags = --auth=fibonacci --port 8266 diff --git a/code/src/button.ino b/code/src/button.ino new file mode 100644 index 00000000..3aa0d981 --- /dev/null +++ b/code/src/button.ino @@ -0,0 +1,28 @@ +/* + +ESPurna +BUTTON MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#include + +DebounceEvent button1 = false; + +// ----------------------------------------------------------------------------- +// BUTTON +// ----------------------------------------------------------------------------- + +void buttonSetup() { + button1 = DebounceEvent(BUTTON_PIN); +} + +void buttonLoop() { + if (button1.loop()) { + if (button1.getEvent() == EVENT_SINGLE_CLICK) toggleRelay(); + if (button1.getEvent() == EVENT_LONG_CLICK) wifiAP(); + if (button1.getEvent() == EVENT_DOUBLE_CLICK) ESP.reset(); + } +} diff --git a/code/src/defaults.h b/code/src/defaults.h new file mode 100644 index 00000000..171e1825 --- /dev/null +++ b/code/src/defaults.h @@ -0,0 +1,84 @@ +// Managed from platformio.ini +//#define DEBUG +//#define ESPURNA +//#define SONOFF +//#define SLAMPHER +//#define S20 +//#define NODEMCUV2 + +//#define ENABLE_NOFUSS 1 +//#define ENABLE_EMON 1 +//#define ENABLE_RF 1 +//#define ENABLE_DHT 1 + +#define BUTTON_PIN 0 +#define RELAY_PIN 12 + +#ifdef ESPURNA + #define MANUFACTURER "TINKERMAN" + #define DEVICE "ESPURNA" + #define LED_PIN 13 +#endif + +#ifdef SONOFF + #define MANUFACTURER "ITEAD" + #define DEVICE "SONOFF" + #define LED_PIN 13 +#endif + +#ifdef SLAMPHER + #define MANUFACTURER "ITEAD" + #define DEVICE "SLAMPHER" + #define LED_PIN 13 +#endif + +#ifdef S20 + #define MANUFACTURER "ITEAD" + #define DEVICE "S20" + #define LED_PIN 13 +#endif + +#ifdef NODEMCUV2 + #define MANUFACTURER "NODEMCU" + #define DEVICE "LOLIN" + #define LED_PIN 16 +#endif + +#define AP_PASS "fibonacci" + +#define OTA_PASS "fibonacci" +#define OTA_PORT 8266 + +#define BUFFER_SIZE 1024 +#define STATUS_UPDATE_INTERVAL 30000 +#define HEARTBEAT_INTERVAL 60000 +#define FS_VERSION_FILE "/fsversion" + +#define WIFI_RECONNECT_INTERVAL 300000 + +#define RF_PIN 14 + +#define DHT_PIN 14 +#define DHT_UPDATE_INTERVAL 300000 +#define DHT_TYPE DHT22 +#define DHT_TIMING 11 + +#define MQTT_RECONNECT_DELAY 10000 +#define MQTT_RETAIN true +#define MQTT_STATUS_TOPIC "" +#define MQTT_IP_TOPIC "/ip" +#define MQTT_VERSION_TOPIC "/version" +#define MQTT_FSVERSION_TOPIC "/fsversion" +#define MQTT_HEARTBEAT_TOPIC "/heartbeat" +#define MQTT_POWER_TOPIC "/power" +#define MQTT_TEMPERATURE_TOPIC "/temperature" +#define MQTT_HUMIDITY_TOPIC "/humidity" + +#define EMON_CURRENT_PIN 0 +#define EMON_SAMPLES 1000 +#define EMON_INTERVAL 10000 +#define EMON_MEASUREMENTS 6 +#define EMON_ADC_BITS 10 +#define EMON_REFERENCE_VOLTAGE 1.0 +#define EMON_CURRENT_PRECISION 1 +#define EMON_CURRENT_OFFSET 0.25 diff --git a/code/src/dht.ino b/code/src/dht.ino new file mode 100644 index 00000000..0de07b73 --- /dev/null +++ b/code/src/dht.ino @@ -0,0 +1,73 @@ +/* + +ESPurna +DHT MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#if ENABLE_DHT + + #include "DHT.h" + + DHT dht(DHT_PIN, DHT_TYPE, DHT_TIMING); + double temperature; + double humidity; + + // ----------------------------------------------------------------------------- + // DHT + // ----------------------------------------------------------------------------- + + double getTemperature() { + return temperature; + } + + double getHumidity() { + return humidity; + } + + void dhtSetup() { + dht.begin(); + } + + void dhtLoop() { + + static unsigned long last_check = 0; + if (!mqttConnected()) return; + if ((last_check > 0) && ((millis() - last_check) < DHT_UPDATE_INTERVAL)) return; + last_check = millis(); + + char buffer[10]; + + temperature = dht.readTemperature(); + if (isnan(temperature)) { + #ifdef DEBUG + Serial.println(F("[DHT] Error reading temperature")); + #endif + } else { + dtostrf(temperature, 4, 1, buffer); + mqttSend((char *) MQTT_TEMPERATURE_TOPIC, buffer); + #ifdef DEBUG + Serial.print(F("[DHT] Temperature: ")); + Serial.println(temperature); + #endif + } + + humidity = dht.readHumidity(); + if (isnan(humidity)) { + #ifdef DEBUG + Serial.println(F("[DHT] Error reading humidity")); + #endif + } else { + dtostrf(humidity, 4, 1, buffer); + mqttSend((char *) MQTT_HUMIDITY_TOPIC, buffer); + #ifdef DEBUG + Serial.print(F("[DHT] Humidity: ")); + Serial.println(humidity); + #endif + } + + } + +#endif diff --git a/code/src/emon.ino b/code/src/emon.ino new file mode 100644 index 00000000..996600a6 --- /dev/null +++ b/code/src/emon.ino @@ -0,0 +1,85 @@ +/* + +ESPurna +EMON MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#if ENABLE_EMON + + #include + + EmonLiteESP power; + double current; + + // ----------------------------------------------------------------------------- + // EMON + // ----------------------------------------------------------------------------- + + double getCurrent() { + return current; + } + + unsigned int currentCallback() { + return analogRead(EMON_CURRENT_PIN); + } + + void powerMonitorSetup() { + power.initCurrent(currentCallback, EMON_ADC_BITS, EMON_REFERENCE_VOLTAGE, config.pwCurrentRatio.toFloat()); + power.setPrecision(EMON_CURRENT_PRECISION); + } + + void powerMonitorLoop() { + + static unsigned long next_measurement = millis(); + static byte measurements = 0; + static double max = 0; + static double min = 0; + static double sum = 0; + + if (!mqttConnected()) return; + + if (millis() > next_measurement) { + + // Safety check: do not read current if relay is OFF + if (!digitalRead(RELAY_PIN)) { + current = 0; + } else { + current = power.getCurrent(EMON_SAMPLES); + current -= EMON_CURRENT_OFFSET; + if (current < 0) current = 0; + } + + if (measurements == 0) { + max = min = current; + } else { + if (current > max) max = current; + if (current < min) min = current; + } + sum += current; + ++measurements; + + #ifdef DEBUG + Serial.print(F("[ENERGY] Power now: ")); + Serial.print(int(current * config.pwMainsVoltage.toFloat())); + Serial.println(F("W")); + #endif + + if (measurements == EMON_MEASUREMENTS) { + char buffer[8]; + double power = (sum - max - min) * config.pwMainsVoltage.toFloat() / (measurements - 2); + sprintf(buffer, "%d", int(power)); + mqttSend((char *) MQTT_POWER_TOPIC, buffer); + sum = 0; + measurements = 0; + } + + next_measurement += EMON_INTERVAL; + + } + + } + +#endif diff --git a/code/src/main.cpp b/code/src/main.cpp deleted file mode 100644 index f43c34d9..00000000 --- a/code/src/main.cpp +++ /dev/null @@ -1,1270 +0,0 @@ -/* - -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 "FS.h" - -#include "Config.h" -#include "NoFUSSClient.h" -#include "JustWifi.h" -#include -#include - -#include -#include -#include -#include "DHT.h" - -extern "C" { - #include "user_interface.h" -} - -#include "version.h" - -// ----------------------------------------------------------------------------- -// Configuration -// ----------------------------------------------------------------------------- - -// Managed from platformio.ini -//#define DEBUG -//#define ESPURNA -//#define SONOFF -//#define SLAMPHER -//#define S20 -//#define NODEMCUV2 - -#define ENABLE_OTA 1 -#define ENABLE_MQTT 1 -#define ENABLE_WEBSERVER 1 - -#define ENABLE_NOFUSS 0 -#define ENABLE_POWER 0 -#define ENABLE_RF 0 -#define ENABLE_DHT 0 - -#define BUTTON_PIN 0 -#define RELAY_PIN 12 -#define LED_PIN 13 - -#ifdef ESPURNA - #define MANUFACTURER "TINKERMAN" - #define DEVICE "ESPURNA" -#endif - -#ifdef SONOFF - #define MANUFACTURER "ITEAD" - #define DEVICE "SONOFF" -#endif - -#ifdef SLAMPHER - #define MANUFACTURER "ITEAD" - #define DEVICE "SLAMPHER" -#endif - -#ifdef S20 - #define MANUFACTURER "ITEAD" - #define DEVICE "S20" -#endif - -#ifdef NODEMCUV2 - #define MANUFACTURER "NODEMCU" - #define DEVICE "LOLIN" - #undef LED_PIN - #define LED_PIN 16 -#endif - -#define ADMIN_PASS "fibonacci" -#define BUFFER_SIZE 1024 -#define STATUS_UPDATE_INTERVAL 10000 -#define HEARTBEAT_INTERVAL 60000 -#define FS_VERSION_FILE "/fsversion" - -#define RF_PIN 14 - -#define DHT_PIN 14 -#define DHT_UPDATE_INTERVAL 300000 -#define DHT_TYPE DHT22 -#define DHT_TIMING 11 - -#define MQTT_RECONNECT_DELAY 10000 -#define MQTT_RETAIN true - -#define MQTT_STATUS_TOPIC "" -#define MQTT_IP_TOPIC "/ip" -#define MQTT_VERSION_TOPIC "/version" -#define MQTT_FSVERSION_TOPIC "/fsversion" -#define MQTT_HEARTBEAT_TOPIC "/heartbeat" -#define MQTT_POWER_TOPIC "/power" -#define MQTT_TEMPERATURE_TOPIC "/temperature" -#define MQTT_HUMIDITY_TOPIC "/humidity" - -#define CURRENT_PIN 0 -#define ADC_BITS 10 -#define REFERENCE_VOLTAGE 1.0 -#define CURRENT_PRECISION 1 -#define CURRENT_OFFSET 0.25 -#define SAMPLES_X_MEASUREMENT 1000 -#define MEASUREMENT_INTERVAL 10000 -#define MEASUREMENTS_X_MESSAGE 6 - -// ----------------------------------------------------------------------------- -// Globals -// ----------------------------------------------------------------------------- - -JustWifi jw; -DebounceEvent button1 = false; - -#if ENABLE_WEBSERVER - ESP8266WebServer server(80); -#endif - -#if ENABLE_MQTT - void mqttSend(char * topic, char * message); - void getFSVersion(char * buffer); - WiFiClient client; - PubSubClient mqtt(client); - String mqttTopic; - bool isMQTTMessage = false; -#endif - -#if ENABLE_RF - unsigned long rfCode = 0; - unsigned long rfCodeON = 0; - unsigned long rfCodeOFF = 0; -#endif - -#if ENABLE_DHT - DHT dht(DHT_PIN, DHT_TYPE, DHT_TIMING); - double temperature; - double humidity; -#endif - -#if ENABLE_POWER - EmonLiteESP power; - double current; -#endif - -// ----------------------------------------------------------------------------- -// Utils -// ----------------------------------------------------------------------------- - -void 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; - -} - -String getIdentifier() { - char identifier[20]; - sprintf(identifier, "%s_%06X", DEVICE, ESP.getChipId()); - return String(identifier); -} - -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 (jw.connected()) { - blink(5000, 500); - } else { - blink(500, 500); - } -} - -// ----------------------------------------------------------------------------- -// Relay -// ----------------------------------------------------------------------------- - -void switchRelayOn() { - - if (!digitalRead(RELAY_PIN)) { - - #ifdef DEBUG - Serial.println(F("[RELAY] ON")); - #endif - digitalWrite(RELAY_PIN, HIGH); - EEPROM.write(0, 1); - EEPROM.commit(); - - #if ENABLE_MQTT - if (!isMQTTMessage) mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "1"); - #endif - - } - -} - -void switchRelayOff() { - - if (digitalRead(RELAY_PIN)) { - - #ifdef DEBUG - Serial.println(F("[RELAY] OFF")); - #endif - digitalWrite(RELAY_PIN, LOW); - EEPROM.write(0, 0); - EEPROM.commit(); - - #if ENABLE_MQTT - if (!isMQTTMessage) mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "0"); - #endif - - } - -} - -void toggleRelay() { - if (digitalRead(RELAY_PIN)) { - switchRelayOff(); - } else { - switchRelayOn(); - } -} - -// ----------------------------------------------------------------------------- -// NoFUSS -// ----------------------------------------------------------------------------- - -#if ENABLE_NOFUSS - - void nofussSetup() { - - NoFUSSClient.setServer(config.nofussServer); - NoFUSSClient.setDevice(DEVICE); - NoFUSSClient.setVersion(APP_VERSION); - - NoFUSSClient.onMessage([](nofuss_t code) { - - if (code == NOFUSS_START) { - Serial.println(F("[NoFUSS] Start")); - } - - if (code == NOFUSS_UPTODATE) { - Serial.println(F("[NoFUSS] Already in the last version")); - } - - if (code == NOFUSS_PARSE_ERROR) { - Serial.println(F("[NoFUSS] Error parsing server response")); - } - - if (code == NOFUSS_UPDATING) { - Serial.println(F("[NoFUSS] Updating")); - Serial.print( F(" New version: ")); - Serial.println(NoFUSSClient.getNewVersion()); - Serial.print( F(" Firmware: ")); - Serial.println(NoFUSSClient.getNewFirmware()); - Serial.print( F(" File System: ")); - Serial.println(NoFUSSClient.getNewFileSystem()); - } - - if (code == NOFUSS_FILESYSTEM_UPDATE_ERROR) { - Serial.print(F("[NoFUSS] File System Update Error: ")); - Serial.println(NoFUSSClient.getErrorString()); - } - - if (code == NOFUSS_FILESYSTEM_UPDATED) { - Serial.println(F("[NoFUSS] File System Updated")); - } - - if (code == NOFUSS_FIRMWARE_UPDATE_ERROR) { - Serial.print(F("[NoFUSS] Firmware Update Error: ")); - Serial.println(NoFUSSClient.getErrorString()); - } - - if (code == NOFUSS_FIRMWARE_UPDATED) { - Serial.println(F("[NoFUSS] Firmware Updated")); - } - - if (code == NOFUSS_RESET) { - Serial.println(F("[NoFUSS] Resetting board")); - } - - if (code == NOFUSS_END) { - Serial.println(F("[NoFUSS] End")); - } - - }); - - } - - void nofussLoop() { - - static unsigned long last_check = 0; - if (WiFi.status() != WL_CONNECTED) return; - if ((last_check > 0) && ((millis() - last_check) < config.nofussInterval.toInt())) return; - last_check = millis(); - NoFUSSClient.handle(); - - } - -#endif - - -// ----------------------------------------------------------------------------- -// OTA -// ----------------------------------------------------------------------------- - -#if ENABLE_OTA - - void OTASetup() { - - ArduinoOTA.setPort(8266); - ArduinoOTA.setHostname(config.hostname.c_str()); - ArduinoOTA.setPassword((const char *) ADMIN_PASS); - - ArduinoOTA.onStart([]() { - #if ENABLE_RF - RemoteReceiver::disable(); - #endif - #ifdef DEBUG - Serial.println(F("[OTA] Start")); - #endif - }); - - ArduinoOTA.onEnd([]() { - #ifdef DEBUG - Serial.println(F("[OTA] End")); - #endif - #if ENABLE_RF - RemoteReceiver::enable(); - #endif - }); - - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { - #ifdef DEBUG - Serial.printf("[OTA] Progress: %u%%\r", (progress / (total / 100))); - #endif - }); - - ArduinoOTA.onError([](ota_error_t error) { - #ifdef DEBUG - Serial.printf("[OTA] Error[%u]: ", error); - if (error == OTA_AUTH_ERROR) Serial.println(F("[OTA] Auth Failed")); - else if (error == OTA_BEGIN_ERROR) Serial.println(F("[OTA] Begin Failed")); - else if (error == OTA_CONNECT_ERROR) Serial.println(F("[OTA] Connect Failed")); - else if (error == OTA_RECEIVE_ERROR) Serial.println(F("[OTA] Receive Failed")); - else if (error == OTA_END_ERROR) Serial.println(F("[OTA] End Failed")); - #endif - }); - - ArduinoOTA.begin(); - - } - - void OTALoop() { - ArduinoOTA.handle(); - } - -#endif - -// ----------------------------------------------------------------------------- -// Wifi -// ----------------------------------------------------------------------------- - -void wifiSetup() { - - // Message callbacks - jw.onMessage([](justwifi_messages_t code, char * parameter) { - - if (code == MESSAGE_CONNECTED) { - #if ENABLE_NOFUSS - NoFUSSClient.handle(); - #endif - } else { - #if ENABLE_MQTT - if (mqtt.connected()) mqtt.disconnect(); - #endif - } - - #if ENABLE_RF - if (code == MESSAGE_CONNECTING) { - RemoteReceiver::disable(); - } else if (code != MESSAGE_CONNECT_WAITING) { - RemoteReceiver::enable(); - } - #endif - - if (code == MESSAGE_CONNECT_WAITING) { - showStatus(); - } - - #ifdef DEBUG - - if (code == MESSAGE_AUTO_NOSSID) { - Serial.println("[WIFI] No information about the last successful network"); - } - - if (code == MESSAGE_AUTO_CONNECTING) { - Serial.print("[WIFI] Connecting to last successful network: "); - Serial.println(parameter); - } - - if (code == MESSAGE_AUTO_FAILED) { - Serial.println("[WIFI] Could not connect to last successful network"); - } - - if (code == MESSAGE_CONNECTING) { - Serial.print("[WIFI] Connecting to "); - Serial.println(parameter); - } - - if (code == MESSAGE_CONNECT_WAITING) { - // - } - - if (code == MESSAGE_CONNECT_FAILED) { - Serial.print("[WIFI] Could not connect to "); - Serial.println(parameter); - } - - if (code == MESSAGE_CONNECTED) { - Serial.print("[WIFI] Connected to "); - Serial.print(jw.getNetwork()); - Serial.print(" with IP "); - Serial.println(jw.getIP()); - } - - if (code == MESSAGE_DISCONNECTED) { - Serial.println("[WIFI] Disconnected"); - } - - if (code == MESSAGE_ACCESSPOINT_CREATING) { - Serial.println("[WIFI] Creating access point"); - } - - if (code == MESSAGE_ACCESSPOINT_CREATED) { - Serial.print("[WIFI] Access point created with SSID "); - Serial.print(jw.getNetwork()); - Serial.print(" and IP "); - Serial.println(jw.getIP()); - } - - if (code == MESSAGE_ACCESSPOINT_FAILED) { - Serial.println("[WIFI] Could not create access point"); - } - - #endif - - }); - - -} - -bool wifiAP() { - //jw.disconnect(); - return jw.startAP((char *) config.hostname.c_str(), (char *) ADMIN_PASS); -} - -void wifiLoop() { - - jw.loop(); - - // Check disconnection - if ((!jw.connected()) && (jw.getMode() != MODE_ACCESS_POINT)) { - - WiFi.printDiag(Serial); - - // Set networks - jw.cleanNetworks(); - jw.addNetwork((char *) config.ssid[0].c_str(), (char *) config.pass[0].c_str()); - jw.addNetwork((char *) config.ssid[1].c_str(), (char *) config.pass[1].c_str()); - jw.addNetwork((char *) config.ssid[2].c_str(), (char *) config.pass[2].c_str()); - - // Connecting - if (!jw.autoConnect()) { - if (!jw.connect()) { - if (!wifiAP()) { - #ifdef DEBUG - Serial.println("[WIFI] Could not start any wifi interface!"); - #endif - } - } - } - - } - -} - -// ----------------------------------------------------------------------------- -// RF -// ----------------------------------------------------------------------------- - -#if ENABLE_RF - - void rfLoop() { - if (rfCode == 0) return; - #ifdef DEBUG - Serial.print(F("[RF] Received 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 = config.rfChannel.toInt(); - for (byte i = 0; i < 5; i++) { - code *= 3; - if (channel & 1) code += 1; - channel >>= 1; - } - - // device - unsigned int device = config.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(F("[RF] Code ON: ")); - Serial.println(rfCodeON); - Serial.print(F("[RF] Code OFF: ")); - Serial.println(rfCodeOFF); - #endif - - } - - void rfCallback(unsigned long code, unsigned int period) { - rfCode = code; - } - - void rfSetup() { - pinMode(RF_PIN, INPUT_PULLUP); - rfBuildCodes(); - RemoteReceiver::init(RF_PIN, 3, rfCallback); - RemoteReceiver::enable(); - } - -#endif - -// ----------------------------------------------------------------------------- -// 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(F("[WEBSERVER] Request: /relay/on")); - #endif - switchRelayOn(); - server.send(200, "text/plain", "ON"); - } - - void handleRelayOff() { - #ifdef DEBUG - Serial.println(F("[WEBSERVER] Request: /relay/off")); - #endif - switchRelayOff(); - server.send(200, "text/plain", "OFF"); - } - - bool handleFileRead(String path) { - - #ifdef DEBUG - Serial.print(F("[WEBSERVER] Request: ")); - Serial.println(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 handleInit() { - - #ifdef DEBUG - Serial.println("[WEBSERVER] Request: /init"); - #endif - - char buffer[64]; - char built[16]; - getCompileTime(built); - sprintf(buffer, "%s %s built %s", APP_NAME, APP_VERSION, built); - - StaticJsonBuffer<1024> jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - root["appname"] = String(buffer); - root["manufacturer"] = String(MANUFACTURER); - root["device"] = String(DEVICE); - root["hostname"] = config.hostname; - root["network"] = (WiFi.status() == WL_CONNECTED) ? WiFi.SSID() : "ACCESS POINT"; - root["ip"] = (WiFi.status() == WL_CONNECTED) ? WiFi.localIP().toString() : WiFi.softAPIP().toString(); - root["updateInterval"] = STATUS_UPDATE_INTERVAL; - - root["ssid0"] = config.ssid[0]; - root["pass0"] = config.pass[0]; - root["ssid1"] = config.ssid[1]; - root["pass1"] = config.pass[1]; - root["ssid2"] = config.ssid[2]; - root["pass2"] = config.pass[2]; - - #if ENABLE_MQTT - root["mqttServer"] = config.mqttServer; - root["mqttPort"] = config.mqttPort; - root["mqttUser"] = config.mqttUser; - root["mqttPassword"] = config.mqttPassword; - root["mqttTopic"] = config.mqttTopic; - #endif - - #if ENABLE_RF - root["rfChannel"] = config.rfChannel; - root["rfDevice"] = config.rfDevice; - #endif - - #if ENABLE_POWER - root["pwMainsVoltage"] = config.pwMainsVoltage; - root["pwCurrentRatio"] = config.pwCurrentRatio; - #endif - - String output; - root.printTo(output); - server.send(200, "text/json", output); - - } - - void handleStatus() { - - #ifdef DEBUG - //Serial.println("[WEBSERVER] Request: /status"); - #endif - - StaticJsonBuffer<256> jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - root["relay"] = digitalRead(RELAY_PIN) ? 1: 0; - #if ENABLE_MQTT - root["mqtt"] = mqtt.connected() ? 1: 0; - #endif - #if ENABLE_POWER - root["power"] = current * config.pwMainsVoltage.toFloat(); - #endif - #if ENABLE_DHT - root["temperature"] = temperature; - root["humidity"] = humidity; - #endif - - String output; - root.printTo(output); - server.send(200, "text/json", output); - - } - - void handleSave() { - - #ifdef DEBUG - Serial.println(F("[WEBSERVER] Request: /save")); - #endif - - if (server.hasArg("status")) { - if (server.arg("status") == "1") { - switchRelayOn(); - } else { - switchRelayOff(); - } - } - - if (server.hasArg("ssid0")) config.ssid[0] = server.arg("ssid0"); - if (server.hasArg("pass0")) config.pass[0] = server.arg("pass0"); - if (server.hasArg("ssid1")) config.ssid[1] = server.arg("ssid1"); - if (server.hasArg("pass1")) config.pass[1] = server.arg("pass1"); - if (server.hasArg("ssid2")) config.ssid[2] = server.arg("ssid2"); - if (server.hasArg("pass2")) config.pass[2] = server.arg("pass2"); - - #if ENABLE_MQTT - if (server.hasArg("mqttServer")) config.mqttServer = server.arg("mqttServer"); - if (server.hasArg("mqttPort")) config.mqttPort = server.arg("mqttPort"); - if (server.hasArg("mqttUser")) config.mqttUser = server.arg("mqttUser"); - if (server.hasArg("mqttPassword")) config.mqttPassword = server.arg("mqttPassword"); - if (server.hasArg("mqttTopic")) config.mqttTopic = server.arg("mqttTopic"); - #endif - #if ENABLE_RF - if (server.hasArg("rfChannel")) config.rfChannel = server.arg("rfChannel"); - if (server.hasArg("rfDevice")) config.rfDevice = server.arg("rfDevice"); - #endif - #if ENABLE_POWER - if (server.hasArg("pwMainsVoltage")) config.pwMainsVoltage = server.arg("pwMainsVoltage"); - if (server.hasArg("pwCurrentRatio")) config.pwCurrentRatio = server.arg("pwCurrentRatio"); - #endif - - server.send(202, "text/json", "{}"); - - config.save(); - #if ENABLE_RF - rfBuildCodes(); - #endif - #if ENABLE_POWER - power.setCurrentRatio(config.pwCurrentRatio.toFloat()); - #endif - - // Disconnect from current WIFI network, wifiLoop will take care of the reconnection - jw.disconnect(); - - } - - void webServerSetup() { - - // Relay control - server.on("/relay/on", HTTP_GET, handleRelayOn); - server.on("/relay/off", HTTP_GET, handleRelayOff); - - // Configuration page - server.on("/init", HTTP_GET, handleInit); - server.on("/status", HTTP_GET, handleStatus); - server.on("/save", HTTP_POST, handleSave); - - // Anything else - server.onNotFound([]() { - - // Hidden files - #ifndef DEBUG - if (server.uri().startsWith("/.")) { - server.send(403, "text/plain", "Forbidden"); - return; - } - #endif - - // 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() { - // Replace identifier - mqttTopic = config.mqttTopic; - mqttTopic.replace("{identifier}", config.hostname); - } - - void mqttSend(char * topic, char * message) { - - if (!mqtt.connected()) return; - - String path = mqttTopic + String(topic); - - #ifdef DEBUG - Serial.print(F("[MQTT] Sending ")); - Serial.print(path); - Serial.print(F(" ")); - Serial.println(message); - #endif - - mqtt.publish(path.c_str(), message, MQTT_RETAIN); - - } - - void mqttCallback(char* topic, byte* payload, unsigned int length) { - - #ifdef DEBUG - Serial.print(F("[MQTT] Received ")); - Serial.print(topic); - Serial.print(F(" ")); - 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() && (config.mqttServer.length()>0)) { - - mqtt.setServer((const char *) config.mqttServer.c_str(), config.mqttPort.toInt()); - - #ifdef DEBUG - Serial.print(F("[MQTT] Connecting to broker at ")); - Serial.print(config.mqttServer); - #endif - - if (config.mqttUser.length() > 0) { - #ifdef DEBUG - Serial.print(F(" as user ")); - Serial.print(config.mqttUser); - Serial.print(F(": ")); - #endif - mqtt.connect( - config.hostname.c_str(), - (const char *) config.mqttUser.c_str(), - (const char *) config.mqttPassword.c_str() - ); - } else { - #ifdef DEBUG - Serial.print(F(" anonymously: ")); - #endif - mqtt.connect(config.hostname.c_str()); - } - - if (mqtt.connected()) { - - #ifdef DEBUG - Serial.println(F("connected!")); - #endif - - buildTopics(); - - // Say hello and report our IP and VERSION - mqttSend((char *) MQTT_IP_TOPIC, (char *) WiFi.localIP().toString().c_str()); - mqttSend((char *) MQTT_VERSION_TOPIC, (char *) APP_VERSION); - char buffer[10]; - getFSVersion(buffer); - mqttSend((char *) MQTT_FSVERSION_TOPIC, buffer); - - // Publish current relay status - mqttSend((char *) MQTT_STATUS_TOPIC, (char *) (digitalRead(RELAY_PIN) ? "1" : "0")); - - // Subscribe to topic - #ifdef DEBUG - Serial.print(F("[MQTT] Subscribing to ")); - Serial.println(mqttTopic); - #endif - mqtt.subscribe(mqttTopic.c_str()); - - - } else { - - #ifdef DEBUG - Serial.print(F("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 - -// ----------------------------------------------------------------------------- -// DHT -// ----------------------------------------------------------------------------- - -#if ENABLE_MQTT -#if ENABLE_DHT - - void dhtSetup() { - dht.begin(); - } - - void dhtLoop() { - - static unsigned long last_check = 0; - if (!mqtt.connected()) return; - if ((last_check > 0) && ((millis() - last_check) < DHT_UPDATE_INTERVAL)) return; - last_check = millis(); - - char buffer[10]; - - temperature = dht.readTemperature(); - if (isnan(temperature)) { - #ifdef DEBUG - Serial.println(F("[DHT] Error reading temperature")); - #endif - } else { - dtostrf(temperature, 4, 1, buffer); - mqttSend((char *) MQTT_TEMPERATURE_TOPIC, buffer); - #ifdef DEBUG - Serial.print(F("[DHT] Temperature: ")); - Serial.println(temperature); - #endif - } - - humidity = dht.readHumidity(); - if (isnan(humidity)) { - #ifdef DEBUG - Serial.println(F("[DHT] Error reading humidity")); - #endif - } else { - dtostrf(humidity, 4, 1, buffer); - mqttSend((char *) MQTT_HUMIDITY_TOPIC, buffer); - #ifdef DEBUG - Serial.print(F("[DHT] Humidity: ")); - Serial.println(humidity); - #endif - } - - } - -#endif -#endif - -// ----------------------------------------------------------------------------- -// Energy Monitor -// ----------------------------------------------------------------------------- - -#if ENABLE_POWER -#if ENABLE_MQTT - - unsigned int currentCallback() { - return analogRead(CURRENT_PIN); - } - - void powerMonitorSetup() { - power.initCurrent(currentCallback, ADC_BITS, REFERENCE_VOLTAGE, config.pwCurrentRatio.toFloat()); - power.setPrecision(CURRENT_PRECISION); - } - - void powerMonitorLoop() { - - static unsigned long next_measurement = millis(); - static byte measurements = 0; - static double max = 0; - static double min = 0; - static double sum = 0; - - if (!mqtt.connected()) return; - - if (millis() > next_measurement) { - - // Safety check: do not read current if relay is OFF - if (!digitalRead(RELAY_PIN)) { - current = 0; - } else { - current = power.getCurrent(SAMPLES_X_MEASUREMENT); - current -= CURRENT_OFFSET; - if (current < 0) current = 0; - } - - if (measurements == 0) { - max = min = current; - } else { - if (current > max) max = current; - if (current < min) min = current; - } - sum += current; - ++measurements; - - #ifdef DEBUG - Serial.print(F("[ENERGY] Power now: ")); - Serial.print(int(current * config.pwMainsVoltage.toFloat())); - Serial.println(F("W")); - #endif - - if (measurements == MEASUREMENTS_X_MESSAGE) { - char buffer[8]; - double power = (sum - max - min) * config.pwMainsVoltage.toFloat() / (measurements - 2); - sprintf(buffer, "%d", int(power)); - mqttSend((char *) MQTT_POWER_TOPIC, buffer); - sum = 0; - measurements = 0; - } - - next_measurement += MEASUREMENT_INTERVAL; - - } - - } - -#endif -#endif - -// ----------------------------------------------------------------------------- -// Hardware (buttons, LEDs,...) -// ----------------------------------------------------------------------------- - -void hardwareSetup() { - Serial.begin(115200); - pinMode(RELAY_PIN, OUTPUT); - pinMode(LED_PIN, OUTPUT); - button1 = DebounceEvent(BUTTON_PIN); - SPIFFS.begin(); - EEPROM.begin(4096); - EEPROM.read(0) == 1 ? switchRelayOn() : switchRelayOff(); -} - -void getFSVersion(char * buffer) { - File h = SPIFFS.open(FS_VERSION_FILE, "r"); - if (!h) { - #ifdef DEBUG - Serial.println(F("[SPIFFS] Could not open file system version file.")); - #endif - strcpy(buffer, APP_VERSION); - return; - } - size_t size = h.size(); - h.readBytes(buffer, size - 1); - h.close(); -} - -void hardwareLoop() { - - if (button1.loop()) { - if (button1.getEvent() == EVENT_SINGLE_CLICK) toggleRelay(); - if (button1.getEvent() == EVENT_LONG_CLICK) wifiAP(); - if (button1.getEvent() == EVENT_DOUBLE_CLICK) ESP.reset(); - } - - showStatus(); - - // Heartbeat - static unsigned long last_heartbeat = 0; - if (millis() - last_heartbeat > HEARTBEAT_INTERVAL) { - last_heartbeat = millis(); - mqttSend((char *) MQTT_HEARTBEAT_TOPIC, (char *) "1"); - #ifdef DEBUG - Serial.print(F("[BEAT] Free heap: ")); - Serial.println(ESP.getFreeHeap()); - #endif - } - -} - -// ----------------------------------------------------------------------------- -// Booting -// ----------------------------------------------------------------------------- - -void welcome() { - char buffer[BUFFER_SIZE]; - getCompileTime(buffer); - Serial.println(); - Serial.println(); - Serial.print(APP_NAME); - Serial.print(F(" ")); - Serial.print(APP_VERSION); - Serial.print(F(" built ")); - Serial.println(buffer); - Serial.println(APP_AUTHOR); - Serial.println(APP_WEBSITE); - Serial.println(); - Serial.print(F("Device: ")); - Serial.println(getIdentifier()); - Serial.print(F("Last reset reason: ")); - Serial.println(ESP.getResetReason()); - Serial.print(F("Memory size: ")); - Serial.print(ESP.getFlashChipSize()); - Serial.println(F(" bytes")); - Serial.print(F("Free heap: ")); - Serial.print(ESP.getFreeHeap()); - Serial.println(F(" bytes")); - FSInfo fs_info; - if (SPIFFS.info(fs_info)) { - Serial.print(F("File system total size: ")); - Serial.print(fs_info.totalBytes); - Serial.println(F(" bytes")); - Serial.print(F("File system used size : ")); - Serial.print(fs_info.usedBytes); - Serial.println(F(" bytes")); - } - - Serial.println(); -} - -void setup() { - - hardwareSetup(); - delay(1000); - welcome(); - config.load(); - - // At the moment I am overriding any possible hostname stored in EEPROM - // with the generated one until I have a way to change them from the - // configuration interface - config.hostname = getIdentifier(); - wifi_station_set_hostname((char *) config.hostname.c_str()); - wifiSetup(); - - #if ENABLE_OTA - OTASetup(); - #endif - #if ENABLE_NOFUSS - nofussSetup(); - #endif - #if ENABLE_MQTT - mqttSetup(); - #endif - #if ENABLE_WEBSERVER - webServerSetup(); - #endif - #if ENABLE_RF - rfSetup(); - #endif - #if ENABLE_DHT - dhtSetup(); - #endif - #if ENABLE_POWER - powerMonitorSetup(); - #endif - -} - -void loop() { - - wifiLoop(); - hardwareLoop(); - - #if ENABLE_OTA - OTALoop(); - #endif - #if ENABLE_NOFUSS - nofussLoop(); - #endif - #if ENABLE_MQTT - mqttLoop(); - #endif - #if ENABLE_WEBSERVER - webServerLoop(); - #endif - #if ENABLE_RF - rfLoop(); - #endif - #if ENABLE_DHT - dhtLoop(); - #endif - #if ENABLE_POWER - powerMonitorLoop(); - #endif - - delay(1); - -} diff --git a/code/src/main.ino b/code/src/main.ino new file mode 100644 index 00000000..f8d62932 --- /dev/null +++ b/code/src/main.ino @@ -0,0 +1,228 @@ +/* + +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 . + +*/ + +extern "C" { + #include "user_interface.h" +} + +#include +#include "version.h" +#include "defaults.h" +#include "FS.h" +#include "Config.h" + +// ----------------------------------------------------------------------------- +// Methods +// ----------------------------------------------------------------------------- + +void 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; + +} + +String getIdentifier() { + char identifier[20]; + sprintf(identifier, "%s_%06X", DEVICE, ESP.getChipId()); + return String(identifier); +} + +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 (wifiConnected()) { + blink(5000, 500); + } else { + blink(500, 500); + } +} + +void hardwareSetup() { + Serial.begin(115200); + pinMode(LED_PIN, OUTPUT); + SPIFFS.begin(); +} + +void getFSVersion(char * buffer) { + File h = SPIFFS.open(FS_VERSION_FILE, "r"); + if (!h) { + #ifdef DEBUG + Serial.println(F("[SPIFFS] Could not open file system version file.")); + #endif + strcpy(buffer, APP_VERSION); + return; + } + size_t size = h.size(); + h.readBytes(buffer, size - 1); + h.close(); +} + +void hardwareLoop() { + + showStatus(); + + // Heartbeat + static unsigned long last_heartbeat = 0; + if (millis() - last_heartbeat > HEARTBEAT_INTERVAL) { + last_heartbeat = millis(); + mqttSend((char *) MQTT_HEARTBEAT_TOPIC, (char *) "1"); + #ifdef DEBUG + Serial.print(F("[BEAT] Free heap: ")); + Serial.println(ESP.getFreeHeap()); + #endif + } + +} + +// ----------------------------------------------------------------------------- +// Booting +// ----------------------------------------------------------------------------- + +void welcome() { + char buffer[BUFFER_SIZE]; + getCompileTime(buffer); + Serial.println(); + Serial.println(); + Serial.print(APP_NAME); + Serial.print(F(" ")); + Serial.print(APP_VERSION); + Serial.print(F(" built ")); + Serial.println(buffer); + Serial.println(APP_AUTHOR); + Serial.println(APP_WEBSITE); + Serial.println(); + Serial.print(F("Device: ")); + Serial.println(getIdentifier()); + Serial.print(F("Last reset reason: ")); + Serial.println(ESP.getResetReason()); + Serial.print(F("Memory size: ")); + Serial.print(ESP.getFlashChipSize()); + Serial.println(F(" bytes")); + Serial.print(F("Free heap: ")); + Serial.print(ESP.getFreeHeap()); + Serial.println(F(" bytes")); + FSInfo fs_info; + if (SPIFFS.info(fs_info)) { + Serial.print(F("File system total size: ")); + Serial.print(fs_info.totalBytes); + Serial.println(F(" bytes")); + Serial.print(F("File system used size : ")); + Serial.print(fs_info.usedBytes); + Serial.println(F(" bytes")); + } + + Serial.println(); +} + +void setup() { + + hardwareSetup(); + relaySetup(); + buttonSetup(); + delay(1000); + welcome(); + config.load(); + + // At the moment I am overriding any possible hostname stored in EEPROM + // with the generated one until I have a way to change them from the + // configuration interface + config.hostname = getIdentifier(); + wifi_station_set_hostname((char *) config.hostname.c_str()); + wifiSetup(); + + otaSetup(); + mqttSetup(); + webServerSetup(); + + #if ENABLE_NOFUSS + nofussSetup(); + #endif + #if ENABLE_RF + rfSetup(); + #endif + #if ENABLE_DHT + dhtSetup(); + #endif + #if ENABLE_EMON + powerMonitorSetup(); + #endif + +} + +void loop() { + + wifiLoop(); + hardwareLoop(); + buttonLoop(); + otaLoop(); + mqttLoop(); + webServerLoop(); + + #if ENABLE_NOFUSS + nofussLoop(); + #endif + #if ENABLE_RF + rfLoop(); + #endif + #if ENABLE_DHT + dhtLoop(); + #endif + #if ENABLE_EMON + powerMonitorLoop(); + #endif + + delay(1); + +} diff --git a/code/src/mqtt.ino b/code/src/mqtt.ino new file mode 100644 index 00000000..8b64143d --- /dev/null +++ b/code/src/mqtt.ino @@ -0,0 +1,161 @@ +/* + +ESPurna +MQTT MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#include +#include + +WiFiClient client; +PubSubClient mqtt(client); +String mqttTopic; +bool isCallbackMessage = false; + +// ----------------------------------------------------------------------------- +// MQTT +// ----------------------------------------------------------------------------- + +bool mqttConnected() { + return mqtt.connected(); +} + +void buildTopics() { + // Replace identifier + mqttTopic = config.mqttTopic; + mqttTopic.replace("{identifier}", config.hostname); +} + +void mqttSend(char * topic, char * message) { + + if (!mqtt.connected()) return; + if (isCallbackMessage) return; + + String path = mqttTopic + String(topic); + + #ifdef DEBUG + Serial.print(F("[MQTT] Sending ")); + Serial.print(path); + Serial.print(F(" ")); + Serial.println(message); + #endif + + mqtt.publish(path.c_str(), message, MQTT_RETAIN); + +} + +void mqttCallback(char* topic, byte* payload, unsigned int length) { + + #ifdef DEBUG + Serial.print(F("[MQTT] Received ")); + Serial.print(topic); + Serial.print(F(" ")); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + #endif + + // Action to perform + if ((char)payload[0] == '0') { + isCallbackMessage = true; + switchRelayOff(); + } + if ((char)payload[0] == '1') { + isCallbackMessage = true; + switchRelayOn(); + } + if ((char)payload[0] == '2') { + toggleRelay(); + } + isCallbackMessage = false; + + +} + +void mqttConnect() { + + if (!mqtt.connected() && (config.mqttServer.length()>0)) { + + mqtt.setServer((const char *) config.mqttServer.c_str(), config.mqttPort.toInt()); + + #ifdef DEBUG + Serial.print(F("[MQTT] Connecting to broker at ")); + Serial.print(config.mqttServer); + #endif + + if (config.mqttUser.length() > 0) { + #ifdef DEBUG + Serial.print(F(" as user ")); + Serial.print(config.mqttUser); + Serial.print(F(": ")); + #endif + mqtt.connect( + config.hostname.c_str(), + (const char *) config.mqttUser.c_str(), + (const char *) config.mqttPassword.c_str() + ); + } else { + #ifdef DEBUG + Serial.print(F(" anonymously: ")); + #endif + mqtt.connect(config.hostname.c_str()); + } + + if (mqtt.connected()) { + + #ifdef DEBUG + Serial.println(F("connected!")); + #endif + + buildTopics(); + + // Say hello and report our IP and VERSION + mqttSend((char *) MQTT_IP_TOPIC, (char *) getIP().c_str()); + mqttSend((char *) MQTT_VERSION_TOPIC, (char *) APP_VERSION); + char buffer[10]; + getFSVersion(buffer); + mqttSend((char *) MQTT_FSVERSION_TOPIC, buffer); + + // Publish current relay status + mqttSend((char *) MQTT_STATUS_TOPIC, (char *) (digitalRead(RELAY_PIN) ? "1" : "0")); + + // Subscribe to topic + #ifdef DEBUG + Serial.print(F("[MQTT] Subscribing to ")); + Serial.println(mqttTopic); + #endif + mqtt.subscribe(mqttTopic.c_str()); + + + } else { + + #ifdef DEBUG + Serial.print(F("failed, rc=")); + Serial.println(mqtt.state()); + #endif + + } + } + +} + +void mqttSetup() { + mqtt.setCallback(mqttCallback); +} + +void mqttLoop() { + static unsigned long timeout = millis(); + if (wifiConnected()) { + if (!mqtt.connected()) { + if (timeout < millis()) { + mqttConnect(); + timeout = millis() + MQTT_RECONNECT_DELAY; + } + } + if (mqtt.connected()) mqtt.loop(); + } +} diff --git a/code/src/nofuss.ino b/code/src/nofuss.ino new file mode 100644 index 00000000..bad87c36 --- /dev/null +++ b/code/src/nofuss.ino @@ -0,0 +1,88 @@ +/* + +ESPurna +NOFUSS MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#if ENABLE_NOFUSS + + #include "NoFUSSClient.h" + + // ----------------------------------------------------------------------------- + // NOFUSS + // ----------------------------------------------------------------------------- + + void nofussSetup() { + + NoFUSSClient.setServer(config.nofussServer); + NoFUSSClient.setDevice(DEVICE); + NoFUSSClient.setVersion(APP_VERSION); + + NoFUSSClient.onMessage([](nofuss_t code) { + + if (code == NOFUSS_START) { + Serial.println(F("[NoFUSS] Start")); + } + + if (code == NOFUSS_UPTODATE) { + Serial.println(F("[NoFUSS] Already in the last version")); + } + + if (code == NOFUSS_PARSE_ERROR) { + Serial.println(F("[NoFUSS] Error parsing server response")); + } + + if (code == NOFUSS_UPDATING) { + Serial.println(F("[NoFUSS] Updating")); + Serial.print( F(" New version: ")); + Serial.println(NoFUSSClient.getNewVersion()); + Serial.print( F(" Firmware: ")); + Serial.println(NoFUSSClient.getNewFirmware()); + Serial.print( F(" File System: ")); + Serial.println(NoFUSSClient.getNewFileSystem()); + } + + if (code == NOFUSS_FILESYSTEM_UPDATE_ERROR) { + Serial.print(F("[NoFUSS] File System Update Error: ")); + Serial.println(NoFUSSClient.getErrorString()); + } + + if (code == NOFUSS_FILESYSTEM_UPDATED) { + Serial.println(F("[NoFUSS] File System Updated")); + } + + if (code == NOFUSS_FIRMWARE_UPDATE_ERROR) { + Serial.print(F("[NoFUSS] Firmware Update Error: ")); + Serial.println(NoFUSSClient.getErrorString()); + } + + if (code == NOFUSS_FIRMWARE_UPDATED) { + Serial.println(F("[NoFUSS] Firmware Updated")); + } + + if (code == NOFUSS_RESET) { + Serial.println(F("[NoFUSS] Resetting board")); + } + + if (code == NOFUSS_END) { + Serial.println(F("[NoFUSS] End")); + } + + }); + + } + + void nofussLoop() { + + static unsigned long last_check = 0; + if (!wifiConnected()) return; + if ((last_check > 0) && ((millis() - last_check) < config.nofussInterval.toInt())) return; + last_check = millis(); + NoFUSSClient.handle(); + + } + +#endif diff --git a/code/src/ota.ino b/code/src/ota.ino new file mode 100644 index 00000000..3b6797d6 --- /dev/null +++ b/code/src/ota.ino @@ -0,0 +1,64 @@ +/* + +ESPurna +OTA MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#include +#include "ArduinoOTA.h" + +// ----------------------------------------------------------------------------- +// OTA +// ----------------------------------------------------------------------------- + +void otaSetup() { + + ArduinoOTA.setPort(OTA_PORT); + ArduinoOTA.setHostname(config.hostname.c_str()); + ArduinoOTA.setPassword((const char *) OTA_PASS); + + ArduinoOTA.onStart([]() { + #if ENABLE_RF + rfEnable(false); + #endif + #if DEBUG + Serial.println(F("[OTA] Start")); + #endif + }); + + ArduinoOTA.onEnd([]() { + #if DEBUG + Serial.println(F("[OTA] End")); + #endif + #if ENABLE_RF + rfEnable(true); + #endif + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + #if DEBUG + Serial.printf("[OTA] Progress: %u%%\r", (progress / (total / 100))); + #endif + }); + + ArduinoOTA.onError([](ota_error_t error) { + #if DEBUG + Serial.printf("[OTA] Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) Serial.println(F("[OTA] Auth Failed")); + else if (error == OTA_BEGIN_ERROR) Serial.println(F("[OTA] Begin Failed")); + else if (error == OTA_CONNECT_ERROR) Serial.println(F("[OTA] Connect Failed")); + else if (error == OTA_RECEIVE_ERROR) Serial.println(F("[OTA] Receive Failed")); + else if (error == OTA_END_ERROR) Serial.println(F("[OTA] End Failed")); + #endif + }); + + ArduinoOTA.begin(); + +} + +void otaLoop() { + ArduinoOTA.handle(); +} diff --git a/code/src/relay.ino b/code/src/relay.ino new file mode 100644 index 00000000..614612d7 --- /dev/null +++ b/code/src/relay.ino @@ -0,0 +1,62 @@ +/* + +ESPurna +RELAY MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#include + +// ----------------------------------------------------------------------------- +// RELAY +// ----------------------------------------------------------------------------- + +void switchRelayOn() { + + if (!digitalRead(RELAY_PIN)) { + + #ifdef DEBUG + Serial.println(F("[RELAY] ON")); + #endif + digitalWrite(RELAY_PIN, HIGH); + EEPROM.write(0, 1); + EEPROM.commit(); + + mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "1"); + + } + +} + +void switchRelayOff() { + + if (digitalRead(RELAY_PIN)) { + + #ifdef DEBUG + Serial.println(F("[RELAY] OFF")); + #endif + digitalWrite(RELAY_PIN, LOW); + EEPROM.write(0, 0); + EEPROM.commit(); + + mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "0"); + + } + +} + +void toggleRelay() { + if (digitalRead(RELAY_PIN)) { + switchRelayOff(); + } else { + switchRelayOn(); + } +} + +void relaySetup() { + pinMode(RELAY_PIN, OUTPUT); + EEPROM.begin(4096); + EEPROM.read(0) == 1 ? switchRelayOn() : switchRelayOff(); +} diff --git a/code/src/rf.ino b/code/src/rf.ino new file mode 100644 index 00000000..689997c9 --- /dev/null +++ b/code/src/rf.ino @@ -0,0 +1,85 @@ +/* + +ESPurna +RF MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#if ENABLE_RF + + #include + + unsigned long rfCode = 0; + unsigned long rfCodeON = 0; + unsigned long rfCodeOFF = 0; + + // ----------------------------------------------------------------------------- + // RF + // ----------------------------------------------------------------------------- + + void rfEnable(bool enable) { + if (enable) { + RemoteReceiver::enable(); + } else { + RemoteReceiver::disable(); + } + } + + void rfLoop() { + if (rfCode == 0) return; + #ifdef DEBUG + Serial.print(F("[RF] Received 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 = config.rfChannel.toInt(); + for (byte i = 0; i < 5; i++) { + code *= 3; + if (channel & 1) code += 1; + channel >>= 1; + } + + // device + unsigned int device = config.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(F("[RF] Code ON: ")); + Serial.println(rfCodeON); + Serial.print(F("[RF] Code OFF: ")); + Serial.println(rfCodeOFF); + #endif + + } + + void rfCallback(unsigned long code, unsigned int period) { + rfCode = code; + } + + void rfSetup() { + pinMode(RF_PIN, INPUT_PULLUP); + rfBuildCodes(); + RemoteReceiver::init(RF_PIN, 3, rfCallback); + RemoteReceiver::enable(); + } + +#endif diff --git a/code/src/version.h b/code/src/version.h index 2d5eb89f..bfce3233 100644 --- a/code/src/version.h +++ b/code/src/version.h @@ -1,4 +1,4 @@ #define APP_NAME "Espurna" -#define APP_VERSION "0.9.6" +#define APP_VERSION "0.9.7" #define APP_AUTHOR "xose.perez@gmail.com" #define APP_WEBSITE "http://tinkerman.cat" diff --git a/code/src/webserver.ino b/code/src/webserver.ino new file mode 100644 index 00000000..3417eb3f --- /dev/null +++ b/code/src/webserver.ino @@ -0,0 +1,243 @@ +/* + +ESPurna +WEBSERVER MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#include +#include +#include "FS.h" + +ESP8266WebServer server(80); + +// ----------------------------------------------------------------------------- +// 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(F("[WEBSERVER] Request: /relay/on")); + #endif + switchRelayOn(); + server.send(200, "text/plain", "ON"); +} + +void handleRelayOff() { + #ifdef DEBUG + Serial.println(F("[WEBSERVER] Request: /relay/off")); + #endif + switchRelayOff(); + server.send(200, "text/plain", "OFF"); +} + +bool handleFileRead(String path) { + + #ifdef DEBUG + Serial.print(F("[WEBSERVER] Request: ")); + Serial.println(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 handleInit() { + + #ifdef DEBUG + Serial.println("[WEBSERVER] Request: /init"); + #endif + + char buffer[64]; + char built[16]; + getCompileTime(built); + sprintf(buffer, "%s %s built %s", APP_NAME, APP_VERSION, built); + + StaticJsonBuffer<1024> jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + + root["appname"] = String(buffer); + root["manufacturer"] = String(MANUFACTURER); + root["device"] = String(DEVICE); + root["hostname"] = config.hostname; + root["network"] = (WiFi.status() == WL_CONNECTED) ? WiFi.SSID() : "ACCESS POINT"; + root["ip"] = (WiFi.status() == WL_CONNECTED) ? WiFi.localIP().toString() : WiFi.softAPIP().toString(); + root["updateInterval"] = STATUS_UPDATE_INTERVAL; + + root["ssid0"] = config.ssid[0]; + root["pass0"] = config.pass[0]; + root["ssid1"] = config.ssid[1]; + root["pass1"] = config.pass[1]; + root["ssid2"] = config.ssid[2]; + root["pass2"] = config.pass[2]; + + root["mqttServer"] = config.mqttServer; + root["mqttPort"] = config.mqttPort; + root["mqttUser"] = config.mqttUser; + root["mqttPassword"] = config.mqttPassword; + root["mqttTopic"] = config.mqttTopic; + + #if ENABLE_RF + root["rfChannel"] = config.rfChannel; + root["rfDevice"] = config.rfDevice; + #endif + + #if ENABLE_EMON + root["pwMainsVoltage"] = config.pwMainsVoltage; + root["pwCurrentRatio"] = config.pwCurrentRatio; + #endif + + String output; + root.printTo(output); + server.send(200, "text/json", output); + +} + +void handleStatus() { + + // Update reconnection timeout to avoid disconnecting the web client + resetConnectionTimeout(); + + #ifdef DEBUG + //Serial.println("[WEBSERVER] Request: /status"); + #endif + + StaticJsonBuffer<256> jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + root["relay"] = digitalRead(RELAY_PIN) ? 1: 0; + root["mqtt"] = mqtt.connected() ? 1: 0; + #if ENABLE_EMON + root["power"] = getCurrent() * config.pwMainsVoltage.toFloat(); + #endif + #if ENABLE_DHT + root["temperature"] = getTemperature(); + root["humidity"] = getHumidity(); + #endif + + String output; + root.printTo(output); + server.send(200, "text/json", output); + +} + +void handleSave() { + + #ifdef DEBUG + Serial.println(F("[WEBSERVER] Request: /save")); + #endif + + if (server.hasArg("status")) { + if (server.arg("status") == "1") { + switchRelayOn(); + } else { + switchRelayOff(); + } + } + + if (server.hasArg("ssid0")) config.ssid[0] = server.arg("ssid0"); + if (server.hasArg("pass0")) config.pass[0] = server.arg("pass0"); + if (server.hasArg("ssid1")) config.ssid[1] = server.arg("ssid1"); + if (server.hasArg("pass1")) config.pass[1] = server.arg("pass1"); + if (server.hasArg("ssid2")) config.ssid[2] = server.arg("ssid2"); + if (server.hasArg("pass2")) config.pass[2] = server.arg("pass2"); + + if (server.hasArg("mqttServer")) config.mqttServer = server.arg("mqttServer"); + if (server.hasArg("mqttPort")) config.mqttPort = server.arg("mqttPort"); + if (server.hasArg("mqttUser")) config.mqttUser = server.arg("mqttUser"); + if (server.hasArg("mqttPassword")) config.mqttPassword = server.arg("mqttPassword"); + if (server.hasArg("mqttTopic")) config.mqttTopic = server.arg("mqttTopic"); + + #if ENABLE_RF + if (server.hasArg("rfChannel")) config.rfChannel = server.arg("rfChannel"); + if (server.hasArg("rfDevice")) config.rfDevice = server.arg("rfDevice"); + #endif + #if ENABLE_EMON + if (server.hasArg("pwMainsVoltage")) config.pwMainsVoltage = server.arg("pwMainsVoltage"); + if (server.hasArg("pwCurrentRatio")) config.pwCurrentRatio = server.arg("pwCurrentRatio"); + #endif + + server.send(202, "text/json", "{}"); + + config.save(); + #if ENABLE_RF + rfBuildCodes(); + #endif + #if ENABLE_EMON + power.setCurrentRatio(config.pwCurrentRatio.toFloat()); + #endif + + // Disconnect from current WIFI network, wifiLoop will take care of the reconnection + wifiDisconnect(); + +} + +void webServerSetup() { + + // Relay control + server.on("/relay/on", HTTP_GET, handleRelayOn); + server.on("/relay/off", HTTP_GET, handleRelayOff); + + // Configuration page + server.on("/init", HTTP_GET, handleInit); + server.on("/status", HTTP_GET, handleStatus); + server.on("/save", HTTP_POST, handleSave); + + // Anything else + server.onNotFound([]() { + + // Hidden files + #ifndef DEBUG + if (server.uri().startsWith("/.")) { + server.send(403, "text/plain", "Forbidden"); + return; + } + #endif + + // Existing files in SPIFFS + if (!handleFileRead(server.uri())) { + server.send(404, "text/plain", "NotFound"); + return; + } + + }); + + // Run server + server.begin(); + +} + +void webServerLoop() { + server.handleClient(); +} diff --git a/code/src/wifi.ino b/code/src/wifi.ino new file mode 100644 index 00000000..2716b152 --- /dev/null +++ b/code/src/wifi.ino @@ -0,0 +1,162 @@ +/* + +ESPurna +WIFI MODULE + +Copyright (C) 2016 by Xose Pérez + +*/ + +#include "JustWifi.h" + +JustWifi jw; +unsigned long wifiLastConnectionTime = 0; + +// ----------------------------------------------------------------------------- +// WIFI +// ----------------------------------------------------------------------------- + +String getIP() { + return jw.getIP(); +} + +String getNetwork() { + return jw.getNetwork(); +} + +void wifiDisconnect() { + jw.disconnect(); +} + +void resetConnectionTimeout() { + wifiLastConnectionTime = millis(); +} + +bool wifiConnected() { + return jw.connected(); +} + +void wifiSetup() { + + // Message callbacks + jw.onMessage([](justwifi_messages_t code, char * parameter) { + + // Disconnect from MQTT server if no WIFI + if (code != MESSAGE_CONNECTED) { + if (mqtt.connected()) mqtt.disconnect(); + } + + #if DEBUG + + if (code == MESSAGE_AUTO_NOSSID) { + Serial.println("[WIFI] No information about the last successful network"); + } + + if (code == MESSAGE_AUTO_CONNECTING) { + Serial.print("[WIFI] Connecting to last successful network: "); + Serial.println(parameter); + } + + if (code == MESSAGE_AUTO_FAILED) { + Serial.println("[WIFI] Could not connect to last successful network"); + } + + if (code == MESSAGE_CONNECTING) { + Serial.print("[WIFI] Connecting to "); + Serial.println(parameter); + } + + if (code == MESSAGE_CONNECT_WAITING) { + // + } + + if (code == MESSAGE_CONNECT_FAILED) { + Serial.print("[WIFI] Could not connect to "); + Serial.println(parameter); + } + + if (code == MESSAGE_CONNECTED) { + Serial.print("[WIFI] Connected to "); + Serial.print(jw.getNetwork()); + Serial.print(" with IP "); + Serial.println(jw.getIP()); + } + + if (code == MESSAGE_DISCONNECTED) { + Serial.println("[WIFI] Disconnected"); + } + + if (code == MESSAGE_ACCESSPOINT_CREATING) { + Serial.println("[WIFI] Creating access point"); + } + + if (code == MESSAGE_ACCESSPOINT_CREATED) { + Serial.print("[WIFI] Access point created with SSID "); + Serial.print(jw.getNetwork()); + Serial.print(" and IP "); + Serial.println(jw.getIP()); + } + + if (code == MESSAGE_ACCESSPOINT_FAILED) { + Serial.println("[WIFI] Could not create access point"); + } + + #endif + + }); + +} + +bool wifiAP() { + //jw.disconnect(); + return jw.startAP((char *) config.hostname.c_str(), (char *) AP_PASS); +} + +void wifiConnect() { + + resetConnectionTimeout(); + + //WiFi.printDiag(Serial); + + // Set networks + jw.cleanNetworks(); + jw.addNetwork((char *) config.ssid[0].c_str(), (char *) config.pass[0].c_str()); + jw.addNetwork((char *) config.ssid[1].c_str(), (char *) config.pass[1].c_str()); + jw.addNetwork((char *) config.ssid[2].c_str(), (char *) config.pass[2].c_str()); + + // Connecting + if (!jw.autoConnect()) { + if (!jw.connect()) { + if (!wifiAP()) { + #if DEBUG + Serial.println("[WIFI] Could not start any wifi interface!"); + #endif + } + } + } + +} + +void wifiLoop() { + + jw.loop(); + + // Check disconnection + if (!jw.connected()) { + + // If we are in AP mode try to reconnect every WIFI_RECONNECT_INTERVAL + // wifiLastConnectionTime gets updated upon every connect try or when + // the webserver is hit by a request to avoid web clients to be + // disconnected while configuring the board + if (jw.getMode() == MODE_ACCESS_POINT) { + if (millis() - wifiLastConnectionTime > WIFI_RECONNECT_INTERVAL) { + wifiConnect(); + } + + // else reconnect right away + } else { + wifiConnect(); + } + } + +}