// Standard ESP8266 libs from project folder #include #include #include #include #include #include // WiFiClient // Project includes #include "constants.h" #include "config.h" #include "config_user.h" #ifdef ENABLE_PING_HOST_TEST #include #endif #ifndef DISABLE_WIFIMANAGER #include // WiFiManager from bib manager #endif #ifdef HTTP_CALL_ON_WINDSPEED_EXCEED #include #endif #ifdef HTTP_CALL_SEND_JSON_DATA #include #include // from bib manager UrlEncode by Masayuki #endif //*************************************************************************// // check if some settings are correct #ifdef HTTP_CALL_ON_WINDSPEED_EXCEED #if (HTTP_CALL_ON_WINDSPEED_INTERVAL_S < WIND_SENSOR_MEAS_TIME_S) #error "HTTP_CALL_ON_WINDSPEED_INTERVAL_S < WIND_SENSOR_MEAS_TIME_S" #endif #endif //*************************************************************************// // constant variables const uint8_t VALUES = 8; // see constants.h file - count of number of SENSOR_ defines float currentSensorData[VALUES] = { nanf("no value"), nanf("no value"), nanf("no value"), nanf("no value"), nanf("no value"), nanf("no value"), nanf("no value"), nanf("no value") }; uint32_t update_sensor_cnt = 0; uint32_t update_webserver_cnt = 0; uint32_t update_windspeed_exceed_cnt = 0; uint32_t wifi_check_interval_counter = 0; uint32_t http_call_send_json_data_cnt = 0; #ifndef DISABLE_WIFIMANAGER const String wifiName = "oko-weather-" + DEVICE_NAME; WiFiManager wifiManager; #endif uint8_t fsm_state = FSM_STATE_1; uint8_t sensor_cnt = 0; boolean validData = false; boolean do_not_read_windsensor = false; uint32_t wifi_reconnect_cnt = 0; //*************************************************************************// void debug(String x) { #ifdef DEBUG Serial.println(x); #endif #ifdef USE_LOGGER logdata(String(millis()) + ":" + x); #endif } //*************************************************************************// void setup() { #ifdef ENABLE_WATCHDOG //wdt_disable(); #endif #if defined(DEBUG) || defined(SERIAL_FEATURE) || defined(DEBUG_RESET_REASON) Serial.begin(SERIAL_BAUD_RATE); #endif // Pin settings pinMode(BAT_CHARGED_PIN, INPUT); pinMode(BAT_CHARGING_PIN, INPUT); pinMode(STATUS_LED_PIN, OUTPUT); pinMode(ANEMOMETER_PIN, INPUT_PULLUP); pinMode(A0, INPUT); digitalWrite(STATUS_LED_PIN, HIGH); #ifndef BAT_PINS_D34 debug("D5 D6 used as battery pins"); #else debug("D3 D4 used as battery pins"); #endif #ifdef BATTERY_POWERED criticalBatCheck(); #endif wifiConnect(); debug("Connected!"); initWifiBasedSW(); initSensors(); //It's magic! leave in delay(100); #ifdef DEBUG_RESET_REASON debugResetReason(); #endif #ifdef BATTERY_POWERED debug("battery powered"); _battery_mode_main(); digitalWrite(STATUS_LED_PIN, HIGH); criticalBatCheck(); WiFi.mode(WIFI_OFF); WiFi.forceSleepBegin(); debug("deep sleep"); // the ESP.deepSleep requires microseconds as input, after the // sleep the system will run into the setup routine ESP.deepSleep(POWERSAVING_SLEEP_S * 1000000, WAKE_RF_DEFAULT); delay(100); #else // not in battery mode #ifdef ENABLE_WATCHDOG wdt_disable(); wdt_reset(); // Enable the internal watchdog wdt_enable(WATCHDOG_TIMEOUT_MS); debug("Watchdog enabled"); #endif #endif } //*************************************************************************// void initWifiBasedSW() { #ifdef INFLUXDB_FEATURE influxdb_begin(); #endif #ifdef WEBUPDATER_FEATURE #ifndef BATTERY_POWERED setupWebUpdater(DEVICE_NAME, WiFi.localIP().toString()); #endif #endif } //*************************************************************************// void initSensors() { // Initialize and configure the sensors #ifdef SENSOR_APDS9930 if (sensor_apds9930_begin(); #endif #ifdef SENSOR_APDS9960 sensor_apds9960_begin(); #endif #ifdef SENSOR_BME280 //Temperature + pressure + humidity sensor_bme280_begin(BME_ADDRESS); #endif #ifdef SENSOR_BMP280 //Temperature + pressure + humidity sensor_bmp280_begin(BMP_ADDRESS); #endif } //*************************************************************************// float readSensors(uint8_t s) { float ret = nan("no value"); switch (s) { case SENSOR_LIGHT: // Initialize and configure the sensors #ifdef SENSOR_APDS9930 ret = apds9930_light(); #endif #ifdef SENSOR_APDS9960 ret = apds9960_light(); #endif break; case SENSOR_TEMPERATURE: #ifdef SENSOR_BME280 ret = bme280_temperature(); #endif #ifdef SENSOR_BMP280 ret = bmp280_temperature(); #endif break; case SENSOR_HUMIDITY: #ifdef SENSOR_BME280 ret = bme280_humidity(); #endif break; case SENSOR_PRESSURE: #ifdef SENSOR_BME280 ret = bme280_pressure(); #endif #ifdef SENSOR_BMP280 ret = bmp280_pressure(); #endif break; case SENSOR_WINDSPEED: #ifdef SENSOR_WIND if (do_not_read_windsensor == false) { ret = wind_speed(); } #endif break; case SENSOR_VOLTAGE: #ifdef SENSOR_BATTERY ret = battery_voltage(); #endif #ifdef SENSOR_USB_VOLTAGE ret = usb_voltage(); #endif break; case SENSOR_ESAVEMODE: #ifdef SENSOR_BATTERY ret = battery_charging(); #endif break; case SENSOR_BATCHARGESTATE: #ifdef SENSOR_BATTERY ret = isEnergySavingMode(); #endif break; default: break; } return ret; } //*************************************************************************// void wifiConnectionCheck() { bool success = false; if ((wifi_check_interval_counter + WIFI_CHECK_INTERVAL_MS) > millis()) { // if check interval is not exceeded abort check return; } wifi_check_interval_counter = millis(); /*if (WiFi.status() == WL_CONNECTED) { // if we are connected return; }*/ #ifdef ENABLE_PING_HOST_TEST debug("Ping 192.168.0.85"); success = Ping.ping(PING_HOST_IP, 3); if (success) { debug("Ping success"); return; } #endif // ENABLE_PING_HOST_TEST debug("Connection problem, resetting ESP"); #ifdef ENABLE_WATCHDOG // loop endless, watchdog will reset the device while (1 == 1) {} #endif ESP.reset(); } #ifdef DISABLE_WIFIMANAGER void wifi_disconnected(WiFiEvent_t event) { if (wifi_reconnect_cnt >= 5) { debug("\nReboot, to much reconnects to wifi done before"); ESP.restart(); } else { debug("no wifi connection, try to reconnect " + String(wifi_reconnect_cnt)); wifi_reconnect_cnt++; wifiConnect(); #ifdef WEBUPDATER_FEATURE setWifiReconnectCnt(wifi_reconnect_cnt); #endif } } #endif //*************************************************************************// void wifiConnect() { // Establish WiFi connection if not already applied #ifndef DISABLE_WIFIMANAGER wifiManager.setMinimumSignalQuality(WIFI_MINIMUM_SIGNAL_QUALITY); // the time in seconds to wait for the known wifi connection wifiManager.setConnectTimeout(WIFI_AUTOCONNECT_TIMEOUT_S); // the time in seconds to wait for the user to configure the device wifiManager.setTimeout(WIFI_CONFIG_PORTAL_TIMEOUT_S); while (!wifiManager.autoConnect(wifiName.c_str(), "DEADBEEF")) { #ifdef SLEEP_IF_NO_WLAN_CONNECTION // If autoconnect to WLAN failed and no client connected, go to deep sleep ESP.deepSleep(POWERSAVING_SLEEP_S * 1000000, WAKE_RF_DEFAULT); delay(100); #endif #ifndef SLEEP_IF_NO_WLAN_CONNECTION // sleep a few seconds and go on trying to connect debug("WiFi connection failed, try again in 5 seconds..."); delay(5000); #endif } #else // DISABLE_WIFIMANAGER is defined if (!WiFi.config(local_IP, gateway, subnet)) { debug("Failed to set IP configuration"); } else { debug("Successful set IP configuration"); } // bind the check function to the disconnected wifi event WiFi.onEvent(wifi_disconnected, WIFI_EVENT_STAMODE_DISCONNECTED); WiFi.begin(WIFI_SSID, WIFI_PASSWD); WiFi.setAutoReconnect(true); debug("Connecting to WLAN"); while (WiFi.status() != WL_CONNECTED) { delay(100); } #endif // DISABLE_WIFIMANAGER } //*************************************************************************// #ifdef BATTERY_POWERED void criticalBatCheck() { float volt = battery_voltage(); if (volt <= BAT_EMERGENCY_DEEPSLEEP_VOLTAGE) { debug("Bat Voltage: " + String(volt) + " V"); debug("Low battery, going into deep sleep."); // Casting to an unsigned int, so it fits into the integer range ESP.deepSleep(1U * EMERGENCY_SLEEP_S * 1000000u); // battery low, shutting down delay(100); } } #endif //*************************************************************************// void loop() { #ifdef BATTERY_POWERED delay(50); return; #else // not in BATTERY_POWERED mode #ifdef ENABLE_WATCHDOG WDT_FEED(); #endif // call fsm loop function _fsm_loop(); // Needed to give WIFI time to function properly delay(DELAY_LOOP_MS); #endif } //*************************************************************************// #ifndef BATTERY_POWERED void _fsm_loop() { #ifdef WEBUPDATER_FEATURE if ((update_webserver_cnt + (UPDATE_WEBSERVER_INTVERVAL_MS)) <= millis()) { //debug("web updater call"); update_webserver_cnt = millis(); doWebUpdater(); } #ifdef ENABLE_WATCHDOG WDT_FEED(); #endif #endif #ifdef HTTP_CALL_SEND_JSON_DATA if ((http_call_send_json_data_cnt + (HTTP_CALL_SEND_JSON_DATA_INTERVAL_S * 1000)) <= millis() and validData == true) { // send the data to the server debug("Sending weather json data to http webserver"); //debug(String(0) + "=" + String(currentSensorData[0])); //debug(String(SENSOR_TEMPERATURE) + "=" + String(currentSensorData[SENSOR_TEMPERATURE])); http_call_send_json_data_cnt = millis(); http_call_send_json_data(); } #ifdef ENABLE_WATCHDOG WDT_FEED(); #endif #endif switch (fsm_state) { /* -------------------------------------------------------------------------------- */ case FSM_STATE_1: //debug("wind speed exceeded check if required"); #ifdef HTTP_CALL_ON_WINDSPEED_EXCEED if ((update_windspeed_exceed_cnt + (HTTP_CALL_ON_WINDSPEED_INTERVAL_S * 1000)) <= millis()) { debug("reading wind sensor exceed"); // reset the wait timer to get a value every HTTP_CALL_ON_WINDSPEED_INTERVAL_S independently to the runtime of the measurement update_windspeed_exceed_cnt = millis(); // start measurement of wind speed start_measure_wind(); fsm_state = FSM_STATE_11; // wait untile the wind meas time exceeded break; // abort case here to prevent read of next sensor in list } #endif fsm_state = FSM_STATE_2; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_2: //debug("reset time check if required"); #ifdef RESET_ESP_TIME_INTERVAL // if millis() reached interval restart ESP if (RESET_ESP_TIME_INTERVAL_MS <= millis()) { debug("resetting firmware intentionally"); // Push reset button after flashing once or do a manual power cycle to get the functionality working. ESP.restart(); } #endif fsm_state = FSM_STATE_3; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_3: wifiConnectionCheck(); fsm_state = FSM_STATE_4; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_4: //debug("disable measure of wind speed if required"); #ifdef defined(BATTERY_POWERED) && defined(SENSOR_WIND) if (energySavingMode() == 1) { // Disable expensive tasks //sensors[SENSOR_WINDSPEED] = 0; //debug("read of wind sensor because of low battery disabled"); do_not_read_windsensor = true; } else { //sensors[SENSOR_WINDSPEED] = &wind_speed; //debug("read of wind sensor because of high battery enabled"); do_not_read_windsensor = false; } #endif sensor_cnt = 0; fsm_state = FSM_STATE_5; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_5: //debug("read sensor data check"); if ((update_sensor_cnt + (UPDATE_SENSOR_INTERVAL_S * 1000)) <= millis() or validData == false) { debug("read sensor data " + String(sensor_cnt)); if (sensor_cnt != SENSOR_WINDSPEED) { // read data from sensor currentSensorData[sensor_cnt] = readSensors(sensor_cnt); //debug(String(sensor_cnt) + "=" + String(currentSensorData[sensor_cnt])); } else { start_measure_wind(); // start measurement of wind speed fsm_state = FSM_STATE_9; // wait untile the wind meas time exceeded break; // abort case here to prevent read of next sensor in list } if (sensor_cnt < VALUES - 1) { sensor_cnt++; fsm_state = FSM_STATE_5; // jump to same state again, more sensors to read } else { update_sensor_cnt = millis(); // reset the update interval counter sensor_cnt = 0; fsm_state = FSM_STATE_6; // next state } } else { //debug("skip read sensor data"); fsm_state = FSM_STATE_1; // no new data, reset FSM } break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_6: //debug("log to serial if required"); #ifdef SERIAL_FEATURE logToSerial(currentSensorData); #endif fsm_state = FSM_STATE_7; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_7: //debug("send data to influxdb if required"); #ifdef INFLUXDB_FEATURE for (uint8_t i = 0; i < 5 and validData == false; i++) { // only check sensor data 0 to 4 -> SENSOR_TEMPERATURE, SENSOR_HUMIDITY, SENSOR_LIGHT, SENSOR_WINDSPEED, SENSOR_PRESSURE if (currentSensorData[i] != 0 and currentSensorData[i] != nanf("no value") and (not isnan(currentSensorData[i]))) { validData = true; } } if (validData == true) { // send data only if valid data is available pushToInfluxDB(DEVICE_NAME, currentSensorData); } #endif fsm_state = FSM_STATE_8; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_8: //debug("set sensor data in webupdater if required"); #ifdef WEBUPDATER_FEATURE #ifdef SHOW_SENSOR_DATA_ON_WEBUPDATER_MAIN_PAGE setSensorData(currentSensorData); #endif #endif fsm_state = FSM_STATE_1; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_9: #ifdef SENSOR_WIND if (check_measure_wind_done() == false) { //debug("wait for wind sensor finish"); fsm_state = FSM_STATE_9; // stay here until the wind measurement is done } else { //debug("wind sensor read finish"); fsm_state = FSM_STATE_10; } #else // in case that the wind sensor is not used skip this step sensor_cnt++; fsm_state = FSM_STATE_5; #endif break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_10: #ifdef SENSOR_WIND currentSensorData[SENSOR_WINDSPEED] = measure_wind_result(); debug("wind sensor " + String(currentSensorData[SENSOR_WINDSPEED])); #endif // step into read of next sensor read sensor_cnt++; fsm_state = FSM_STATE_5; break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_11: #ifdef SENSOR_WIND if (check_measure_wind_done() == false) { //debug("wait for wind sensor finish"); fsm_state = FSM_STATE_11; // stay here until the wind measurement is done } else { //debug("wind sensor read finish"); fsm_state = FSM_STATE_12; } #else // in case that the wind sensor is not used skip this step sensor_cnt++; fsm_state = FSM_STATE_5; #endif break; /* -------------------------------------------------------------------------------- */ case FSM_STATE_12: #ifdef HTTP_CALL_ON_WINDSPEED_EXCEED currentSensorData[SENSOR_WINDSPEED] = measure_wind_result(); debug("wind sensor value " + String(currentSensorData[SENSOR_WINDSPEED])); if (currentSensorData[SENSOR_WINDSPEED] >= HTTP_CALL_ON_WINDSPEED_EXCEED_MPS) { // windspeed exceeded send http call digitalWrite(STATUS_LED_PIN, LOW); // call the url HTTP_CALL_ON_WINDSPEED_URL WiFiClient client; HTTPClient http; http.begin(client, String(HTTP_CALL_ON_WINDSPEED_URL).c_str()); // Send HTTP GET request int httpResponseCode = http.GET(); #ifdef ENABLE_WATCHDOG WDT_FEED(); #endif if (httpResponseCode > 0) { String response = http.getString(); debug("http response code: " + String(httpResponseCode) + " = " + response); // TODO handle response } http.end(); debug("Called windspeed exceed callout"); digitalWrite(STATUS_LED_PIN, HIGH); } #ifdef WEBUPDATER_FEATURE #ifdef SHOW_SENSOR_DATA_ON_WEBUPDATER_MAIN_PAGE sentWindspeed(currentSensorData[SENSOR_WINDSPEED]); #endif // SHOW_SENSOR_DATA_ON_WEBUPDATER_MAIN_PAGE #endif // WEBUPDATER_FEATURE #endif // HTTP_CALL_ON_WINDSPEED_EXCEED // step into read of next fsm state fsm_state = FSM_STATE_2; break; /* -------------------------------------------------------------------------------- */ default: fsm_state = FSM_STATE_1; break; } // close of switch //debug("FSM state = " + String(fsm_state)); /*if (fsm_state == FSM_STATE_1) { debug("----------"); }*/ } #endif //*************************************************************************// void _battery_mode_main() { #ifdef SENSOR_BATTERY if (energySavingMode() == 1) { // Disable expensive tasks //debug("read of wind sensor because of low battery disabled"); do_not_read_windsensor = true; } else { //debug("read of wind sensor because of high battery enabled"); do_not_read_windsensor = false; } #endif for (uint8_t i = 0; i < VALUES; i++) { currentSensorData[i] = readSensors(i); } #ifdef SERIAL_FEATURE logToSerial(currentSensorData); #endif #ifdef INFLUXDB_FEATURE pushToInfluxDB(DEVICE_NAME, currentSensorData); #endif #ifdef WEBUPDATER_FEATURE #ifdef SHOW_SENSOR_DATA_ON_WEBUPDATER_MAIN_PAGE setSensorData(currentSensorData); #endif #endif } //*************************************************************************// #ifdef SERIAL_FEATURE void logToSerial(float sensorValues[]) { Serial.println(""); Serial.println("Current readings:"); Serial.println("Temperature: " + String(sensorValues[SENSOR_TEMPERATURE]) + " °C"); Serial.println("Humidity: " + String(sensorValues[SENSOR_HUMIDITY]) + " %"); Serial.println("Light: " + String(sensorValues[SENSOR_LIGHT]) + " Lux"); Serial.println("Windspeed: " + String(sensorValues[SENSOR_WINDSPEED]) + " km/h"); Serial.println("Pressure: " + String(sensorValues[SENSOR_PRESSURE]) + " hPa"); Serial.println("Bat Voltage: " + String(sensorValues[SENSOR_VOLTAGE]) + " V"); Serial.println("Bat charge state: " + String(sensorValues[SENSOR_BATCHARGESTATE])); Serial.println("Energy saving: " + String(sensorValues[SENSOR_ESAVEMODE])); } #endif //*************************************************************************// #ifdef HTTP_CALL_SEND_JSON_DATA String getJsonData() { debug(String(SENSOR_TEMPERATURE) + "=" + String(currentSensorData[SENSOR_TEMPERATURE])); String msg = hb_ws_msg_start + hb_ws_msg_temp + String(currentSensorData[SENSOR_TEMPERATURE], 2) + ", " + hb_ws_msg_humi + String((isnan(currentSensorData[SENSOR_HUMIDITY]) ? 0.0 : currentSensorData[SENSOR_HUMIDITY]), 2) + ", " + hb_ws_msg_light + String(currentSensorData[SENSOR_LIGHT], 0) + // The light level for the homebridge-http-lux2 plugin is only able to parse integer values ", " + hb_ws_msg_windspeed + String(currentSensorData[SENSOR_WINDSPEED], 2) + ", " + hb_ws_msg_pressure + String(currentSensorData[SENSOR_PRESSURE], 2) + ", " + hb_ws_msg_voltage + String(currentSensorData[SENSOR_VOLTAGE], 2) + ", " + hb_ws_msg_timestamp + String(millis()) + ", " + hb_ws_msg_valid + String(validData) + hb_ws_msg_end; return msg; } void http_call_send_json_data() { //debug("http call to " + String(HTTP_CALL_SEND_JSON_DATA_URL)); //debug(String(SENSOR_TEMPERATURE) + "=" + String(currentSensorData[SENSOR_TEMPERATURE])); // windspeed exceeded send http call digitalWrite(STATUS_LED_PIN, LOW); // call the url HTTP_CALL_SEND_JSON_DATA_URL WiFiClient client; HTTPClient http; String tmp_str = HTTP_CALL_SEND_JSON_DATA_URL + urlEncode(getJsonData()); debug("send: " + tmp_str); http.begin(client, String(tmp_str).c_str()); // Send HTTP GET request int httpResponseCode = http.GET(); #ifdef ENABLE_WATCHDOG WDT_FEED(); #endif if (httpResponseCode > 0) { String response = http.getString(); debug("http response code: " + String(httpResponseCode) + " = " + response); // TODO handle response } http.end(); digitalWrite(STATUS_LED_PIN, HIGH); } #endif #ifdef DEBUG_RESET_REASON void debugResetReason() { rst_info *resetInfo; // 1. read eeprom reset reason //int eep_reset_reason = eeprom_read(0); //debug("EEPROM reset reason " + String(eep_reset_reason)); // 2. read real reset reason int reset_reason = resetInfo->reason; debug("New reset reason " + String(reset_reason)); //eeprom_write(0, reset_reason); } #endif