/* --------------------- CONFIG ---------------------------------- */ /* Adapt this sketch to your needs by modifying the config_device.h*/ /* --------------------- GPIO ------------------------------------ */ /* Generic GPIO Control by HTTP REST interface */ /* Modify GPIO by accessing DEVICE_IP/gpio?gpio=[GPIO]&value=[0|1] */ /* -------------------- SETUP ------------------------------------ */ /* At first boot it creates a WiFi access point */ /* and provide a web server on it, so you can configure Wifi ssid */ /* Wifi passwd, and a mDNS HOSTNAME and a mqtt server */ /* In this mode, device will be available at 192.168.4.1 */ /* --------------------- OTA ------------------------------------- */ /* Device can also be put in OTA Mode: In this case, if you have */ /* a little flash (512K), it better to disable SPIFFS in */ /* "Flash Size" Menu to save some place for the sketch to upload */ /* Use espota.py to upload OTA */ /* After passing in OTA mode, next boot will be in setup mode */ /* --------------------- BMP180 -----------------------------------*/ /* if BMP180 is available temperature will be published by mqtt */ /* and on web server interface */ /* --------------------- MQTT ------------------------------------ */ /* Send information to mqtt server configured in the setup mode */ /* GPIO value configured in config_device.h can be get by */ /* subscribing to /feeds/MQTTUSER/[HOSTNAME]/gpio/[GPIO] and */ /* modified by publishin to */ /* /feeds/MQTTUSER/[HOSTNAME]/gpio/[GPIO]/set */ /* BMP180 will be published to /feeds/[HOSTNAME]/temperature and */ /* /feeds/[HOSTNAME]/pressure */ #include #include #include #include #include #include #include #include #include #include "config.h" #include "utils.h" #include "debug_sketch.h" #include "BMP180.h" #include "BME680.h" #include "BME680_BSEC.h" #include "sensor_DHT.h" #include "dry_sensor.h" #include "MQTT.h" #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "EEPROM.h" #include "Teleinfo.h" extern "C" { #include } #define BOOTMODE_SETUP 1 #define BOOTMODE_NORMAL 2 #define BOOTMODE_OTA 3 double temp, pressure; float dhtTemp, dhtHumidity; float bme680T, bme680P, bme680H, bme680G, bme680A; float bme680BSECT, bme680BSECP, bme680BSECH, bme680BSECIaq, bme680BSECIaqAcc; float teleIinst, telePapp, teleBase; int dryness; uint8_t mode; int reconfig = 0; productConfig conf = {BOOTMODE_SETUP, NULL, NULL, NULL, NULL, NULL, NULL, 1883, 0, 0, 0, 0, 0, 0, 0, NULL, CONFIG_SAMPLING_PERIOD_MS}; // Should have less that MAXSUBSCRIPTIONS elements // MAXSUBSCRIPTIONS is defined is Adafruit_mqtt.h const int gpioControlled[] = CONFIG_CONTROLLED_GPIO; const int gpioObserved[] = CONFIG_OBSERVED_GPIO; const int pwmControlled[] = CONFIG_CONTROLLED_PWM; uint samplingPeriod = CONFIG_SAMPLING_PERIOD_MS; uint nbCycle = UINT_MAX - 1; /* Set these to your desired credentials. */ const char *ssid = CONFIG_SSID_NAME; ESP8266WebServer server(80); /* WebServer decl*/ void WebHandleRoot(); void WebHandleSetup(); void WebHandleGpio(); void WebHandleSave(); void WebHandleOTA(); void WebHandleNotFound(); void WebSetupServer(int bootmode); #ifdef CONFIG_SETUP_BUTTON void onLongButtonPressed(uint8_t pin) { if (pin == CONFIG_SETUP_BUTTON) { reconfig = 1; SKETCH_DEBUG_PRINTLN("Putting device in setup mode"); mode = BOOTMODE_SETUP; WiFi.disconnect(); WiFi.softAP(ssid); } } #endif void WifiSetup(productConfig conf) { IPAddress myIP; int connectionTry = 0; if (mode == BOOTMODE_SETUP) { SKETCH_DEBUG_PRINTLN("Configuring access point: " CONFIG_SSID_NAME); /* You can set a password to the AP here */ WiFi.softAP(CONFIG_SSID_NAME); myIP = WiFi.softAPIP(); } else { //Disable previous AP mode WiFi.softAPdisconnect(true); SKETCH_DEBUG_PRINTLN("Connecting to Wifi..."); if (conf.ip_mode == 1) { SKETCH_DEBUG_PRINTLN("Use static ip configuration"); WiFi.config(IPAddress(conf.ip), IPAddress(conf.gw), IPAddress(conf.mask), IPAddress(conf.dns), IPAddress(conf.dns2)); } uint8_t *bssidConf = NULL; uint8_t bssid[6]; if (conf.bssid[0] != '\0') { String bssidStr = conf.bssid; bssid[0] = strtoul(bssidStr.substring(0, 2).c_str(), NULL, 16); bssid[1] = strtoul(bssidStr.substring(3, 5).c_str(), NULL, 16); bssid[2] = strtoul(bssidStr.substring(6, 8).c_str(), NULL, 16); bssid[3] = strtoul(bssidStr.substring(9, 11).c_str(), NULL, 16); bssid[4] = strtoul(bssidStr.substring(12, 14).c_str(), NULL, 16); bssid[5] = strtoul(bssidStr.substring(15, 17).c_str(), NULL, 16); SKETCH_DEBUG_PRINTF("Using BSSID: %02x:%02x:%02x:%02x:%02x:%02x\n", bssid[5], bssid[4], bssid[3], bssid[2], bssid[1], bssid[0]); bssidConf = bssid; } SKETCH_DEBUG_PRINTF("Using channel %u (0==auto)\n", conf.channel ); WiFi.begin(conf.ssid, conf.password, conf.channel, bssidConf); while (WiFi.status() != WL_CONNECTED) { delay(50); SKETCH_DEBUG_PRINT("."); if (reconfig == 1) { reconfig = 0; return; } if (connectionTry == 120) { SKETCH_DEBUG_PRINTLN("Cannot connect to wifi. Try without BSSID and channel"); WiFi.begin(conf.ssid, conf.password); } connectionTry++; } SKETCH_DEBUG_PRINTF("\nWiFi connected\n"); #ifdef CONFIG_ENABLE_MDNS if (!MDNS.begin(conf.host)) { SKETCH_DEBUG_PRINTLN("Error setting up MDNS responder!"); } else { SKETCH_DEBUG_PRINTLN("mDNS responder started"); #ifndef CONFIG_DISABLE_WEB //Needed for OTA by HTTP MDNS.addService("http", "tcp", 80); #endif } #endif myIP = WiFi.localIP(); } SKETCH_DEBUG_PRINT("My IP address: "); SKETCH_DEBUG_PRINTLN(myIP); } void OTASetup() { #ifndef CONFIF_DISABLE_OTA // Port defaults to 8266 // ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] // ArduinoOTA.setHostname("myesp8266"); // No authentication by default // ArduinoOTA.setPassword((const char *)"123"); ArduinoOTA.onStart([]() { SKETCH_DEBUG_PRINTLN("Start"); }); ArduinoOTA.onEnd([]() { SKETCH_DEBUG_PRINTLN("\nEnd"); //Force BOOTMODE_SETUP in case eeprom layout have changed EepromSaveBootMode(BOOTMODE_SETUP); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { SKETCH_DEBUG_PRINTF("Progress: %u%%\n", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { SKETCH_DEBUG_PRINTF("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) { SKETCH_DEBUG_PRINTLN("Auth Failed"); } else if (error == OTA_BEGIN_ERROR) { SKETCH_DEBUG_PRINTLN("Begin Failed"); } else if (error == OTA_CONNECT_ERROR) { SKETCH_DEBUG_PRINTLN("Connect Failed"); } else if (error == OTA_RECEIVE_ERROR) { SKETCH_DEBUG_PRINTLN("Receive Failed"); } else if (error == OTA_END_ERROR) { SKETCH_DEBUG_PRINTLN("End Failed"); } }); ArduinoOTA.begin(); SKETCH_DEBUG_PRINTLN("Ready"); SKETCH_DEBUG_PRINTF("IP address: "); SKETCH_DEBUG_PRINTLN(WiFi.localIP()); SKETCH_DEBUG_PRINTF("Free Space: %d\n", ESP.getFreeSketchSpace()); #endif } void setup() { #ifdef CONFIG_SETUP_BUTTON new HIB(CONFIG_SETUP_BUTTON, HIGH, NULL, NULL, onLongButtonPressed); #endif pinMode(3, OUTPUT); delay(1000); SKETCH_DEBUG_INIT(115200); SKETCH_DEBUG_PRINTLN(); // Get GPIO 3 Status #if CONFIG_SERIAL_SHOULD_SWAP SKETCH_DEBUG_PRINTLN("SWAP UART"); Serial.swap(); // Switch Serial on GPIO 13 & 15 #endif pinMode(CONFIG_SETUP_GPIO, INPUT_PULLUP); int txStatus = digitalRead(CONFIG_SETUP_GPIO); #if !defined(CONFIG_ENABLE_EXTRA_GPIO) && CONFIG_SERIAL_SHOULD_SWAP Serial.swap(); // Switch back on GPIO 1 & 3 SKETCH_DEBUG_PRINTLN("SWAP UART"); #endif EEPROM.begin(CONFIG_EEPROM_SIZE); EepromReadConfig(conf); mode = conf.bootMode; if (mode == BOOTMODE_NORMAL || mode == BOOTMODE_OTA) { SKETCH_DEBUG_PRINTLN("Configuration Found !:"); SKETCH_DEBUG_PRINTLN(conf.bootMode); SKETCH_DEBUG_PRINTLN(conf.ssid); SKETCH_DEBUG_PRINTLN(conf.host); SKETCH_DEBUG_PRINTLN(conf.mqttServer); SKETCH_DEBUG_PRINTLN(conf.mqttUser); SKETCH_DEBUG_PRINTLN(conf.mqttPasswd); SKETCH_DEBUG_PRINTLN(conf.mqttPort); SKETCH_DEBUG_PRINTLN(); } else { SKETCH_DEBUG_PRINTLN("No configuration saved"); } SKETCH_DEBUG_PRINTF("Force Setup Mode ? : %s\n", txStatus ? "No" : "Yes"); if (txStatus == 0) { mode = BOOTMODE_SETUP; } WifiSetup(conf); if (mode == BOOTMODE_NORMAL) { MqttSetup(conf.mqttServer, conf.mqttUser, conf.mqttPasswd, conf.mqttPort, conf.host); MqttPublishIP(WiFi.localIP().toString()); } if (mode == BOOTMODE_OTA) { OTASetup(); } else { if (!BMP180Setup(CONFIG_BMP180_SDA, CONFIG_BMP180_SCL)) { SKETCH_DEBUG_PRINTLN("BMP180 init success"); } if (!DHTSetup(CONFIG_DHT_PIN)) { SKETCH_DEBUG_PRINTLN("DHT init success"); } if (!DrySetup(CONFIG_DRY_POWER_PIN)) { SKETCH_DEBUG_PRINTLN("DRY init success"); } if(!BME680Setup()){ SKETCH_DEBUG_PRINTLN("BME680 init success"); } if(!BME680BSECSetup()){ SKETCH_DEBUG_PRINTLN("BME680 with BSEC init success"); } if(!TeleinfoSetup()){ SKETCH_DEBUG_PRINTLN("Teleinfo init success"); } WebSetupServer(mode); } samplingPeriod = conf.samplingPeriod; #ifdef CONFIG_ENABLE_POWER_SAVE wifi_set_sleep_type(LIGHT_SLEEP_T); #endif } void loop() { if (mode == BOOTMODE_OTA) { ArduinoOTA.handle(); } else { server.handleClient(); if (mode == BOOTMODE_NORMAL) { MqttCheckSubscription(); MqttCheckIRQ(); } delay(CONFIG_WEB_DELAY_MS); nbCycle++; if (nbCycle > samplingPeriod / CONFIG_WEB_DELAY_MS) { std::vector batchInfo; if (!BMP180GetTempAndPressure(temp, pressure)) { SKETCH_DEBUG_PRINTF("Current %f°C Pressure %fmB\n", temp, pressure); batchInfo.push_back({(float)temp, TEMPERATURE_FEED_FORMAT, 0, 0}); batchInfo.push_back({(float)pressure, PRESSURE_FEED_FORMAT, 0, 0}); } if (!DHTGetTempAndHumidity(dhtTemp, dhtHumidity)) { SKETCH_DEBUG_PRINTF("Current %f°C %f%% Humidity\n", dhtTemp, dhtHumidity); batchInfo.push_back({dhtTemp, TEMPERATURE_DHT_FEED_FORMAT, 0, 0}); batchInfo.push_back({dhtHumidity, HUMIDITY_DHT_FEED_FORMAT, 0, 0}); } if (!DryGetMeasure(dryness)) { SKETCH_DEBUG_PRINTF("Current dryness %d %%\n", (dryness * 100) / 1024); batchInfo.push_back({(float)dryness, DRY_FEED_FORMAT, 0, 0}); } if (!BME680GetMeasure(bme680T, bme680P, bme680H, bme680G, bme680A)) { SKETCH_DEBUG_PRINT("Current T°C: "); SKETCH_DEBUG_PRINT(bme680T); SKETCH_DEBUG_PRINT(" Pressure hPa: "); SKETCH_DEBUG_PRINT(bme680P); SKETCH_DEBUG_PRINT(" Humidity %: "); SKETCH_DEBUG_PRINTLN(bme680H); SKETCH_DEBUG_PRINT(" Gaz kOhms: "); SKETCH_DEBUG_PRINTLN(bme680G); SKETCH_DEBUG_PRINT(" Altitude M: "); SKETCH_DEBUG_PRINTLN(bme680A); batchInfo.push_back({bme680T, BME680_TEMPERATURE_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680P, BME680_PRESSURE_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680H, BME680_HUMIDITY_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680G, BME680_GAZ_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680A, BME680_ALT_FEED_FORMAT, 0, 0}); } if (!BME680BSECGetMeasure(bme680BSECT, bme680BSECP, bme680BSECH, bme680BSECIaq, bme680BSECIaqAcc)) { SKETCH_DEBUG_PRINT("Current T°C: "); SKETCH_DEBUG_PRINT(bme680BSECT); SKETCH_DEBUG_PRINT(" Pressure hPa: "); SKETCH_DEBUG_PRINT(bme680BSECP); SKETCH_DEBUG_PRINT(" Humidity %: "); SKETCH_DEBUG_PRINT(bme680BSECH); SKETCH_DEBUG_PRINT(" Indoor Air Quality: "); SKETCH_DEBUG_PRINT(bme680BSECIaq); SKETCH_DEBUG_PRINT(" IAQ Accuracy: "); SKETCH_DEBUG_PRINTLN(bme680BSECIaqAcc); batchInfo.push_back({bme680BSECT, BME680_TEMPERATURE_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680BSECP, BME680_PRESSURE_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680BSECH, BME680_HUMIDITY_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680BSECIaq, BME680_IAQ_FEED_FORMAT, 0, 0}); batchInfo.push_back({bme680BSECIaqAcc, BME680_IAQ_ACC_FEED_FORMAT, 0, 0}); } TeleinfoProcess(teleIinst, telePapp, teleBase, batchInfo); if (mode == BOOTMODE_NORMAL) MqttBatchPublish(batchInfo, conf.mqttUser, conf.host); nbCycle = 0; } } }