// Standard ESP8266 libs from project folder #include #include #include #include #include #include #include // WiFiClient #include // WiFiManager from bib manager // Project includes #include "constants.h" #include "config.h" #include "config_user.h" //*************************************************************************// // 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; const String wifiName = "oko-weather-" + DEVICE_NAME; WiFiManager wifiManager; 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() { #if defined(DEBUG) || defined(SERIAL_FEATURE) Serial.begin(SERIAL_BARD_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, LOW); #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!"); #ifdef INFLUXDB_FEATURE influxdb_begin(); #endif initSensors(); #ifdef WEBUPDATER_FEATURE #ifndef BATTERY_POWERED setupWebUpdater(DEVICE_NAME, WiFi.localIP().toString()); #endif #endif //It's magic! leave in delay(100); #ifdef BATTERY_POWERED debug("battery powered"); _battery_mode_main(); digitalWrite(STATUS_LED_PIN, LOW); 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 // Enable the internal watchdog ESP.wdtEnable(WATCHDOG_TIMEOUT_MS); #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 sensor_bme280_begin(BME_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 break; case SENSOR_HUMIDITY: #ifdef SENSOR_BME280 ret = bme280_humidity(); #endif break; case SENSOR_PRESSURE: #ifdef SENSOR_BME280 ret = bme280_pressure(); #endif break; case SENSOR_WINDSPEED: #ifdef SENSOR_WIND if (do_not_read_windsensor == false) { ret = wind_speed(); } #endif break; case SENSOR_BAT_VOLTAGE: #ifdef SENSOR_BATTERY ret = battery_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() { 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; } wifi_reconnect_cnt++; debug("no wifi connection, try to reconnect " + String(wifi_reconnect_cnt)); #ifdef WEBUPDATER_FEATURE setWifiReconnectCnt(wifi_reconnect_cnt); #endif wifiConnect(); } //*************************************************************************// void wifiConnect() { // Establish WiFi connection if not already applied 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 } } //*************************************************************************// #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 * 1000000); // battery low, shutting down delay(100); } } #endif //*************************************************************************// void loop() { #ifdef ENABLE_WATCHDOG ESP.wdtFeed(); #endif #ifdef BATTERY_POWERED delay(50); return; #else // 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(); } #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++) { 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 setSensorData(currentSensorData); #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_cnt] = measure_wind_result(); debug("wind sensor " + String(currentSensorData[sensor_cnt])); #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_cnt] = measure_wind_result(); debug("wind sensor value " + String(currentSensorData[sensor_cnt])); if (currentSensorData[SENSOR_WINDSPEED] >= HTTP_CALL_ON_WINDSPEED_EXCEED_MPS) { // windspeed exceeded send http call digitalWrite(STATUS_LED_PIN, HIGH); // 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(); 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, LOW); } #ifdef WEBUPDATER_FEATURE sentWindspeed(currentSensorData[SENSOR_WINDSPEED]); #endif #endif // 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() { 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; } 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 setSensorData(currentSensorData); #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_BAT_VOLTAGE]) + " V"); Serial.println("Bat charge state: " + String(sensorValues[SENSOR_BATCHARGESTATE])); Serial.println("Energy saving: " + String(sensorValues[SENSOR_ESAVEMODE])); } #endif //*************************************************************************//