// Standard ESP8266 libs #include #include #include #include #include // External libs // We decided to put these libs inside the project folder, because we have a lot // of issues with finding the right libs (some libs need a specific version, some // libs have no unique name, some libs are broken, ...). This is not a fine // software engineering practice, but it was too much of a hassle to get this right, // so we decided to put everything in version control. This way, we also can control // the version of the lib, which makes the build process of this project more // robust to changes. // IMPORTANT: You need to set the sketch location of your Arduino IDE to the firmware // folder in order to use the libs. (File -> Preferences -> Sketchbook Location) #include // WiFiClient #include // WiFiManager #include // https://github.com/hwwong/ESP8266Influxdb auchecken und den ordner in das arduino\library verzeichnis kopieren #include // Adafruit APDS9960 - https://www.makerfabs.com/index.php?route=product/product&product_id=281 #include // BME280 - https://www.roboter-bausatz.de/1704/bmp280-barometer-luftdrucksensor?gclid=EAIaIQobChMIlpumj8Hp2wIVFWYbCh01PgmFEAQYAyABEgIwBvD_BwE // Project includes #include "config.h" // IMPORTANT: If you compile the sourcecode and it can't find this file, this is // intentional :) You need to create a config_user.h with your own settings. Use the // config_user.h.example as a starting point. #include "config_user.h" //*************************************************************************// /** * Whishlist: * - Webserver for /metrics endpoint (Prometheus) * - Show current sensor values over simple webpage * - Push sensor values to various 3rd party services (https://openweathermap.org/) * - MQTT? * * - Buffer sensor values if there is no WIFI connection * - Configure weather station over http webinterface * - OTA firmware update * * TODO: * - https://sminghub.github.io/Sming/about/ * - https://github.com/marvinroger/homie-esp8266 */ //*************************************************************************// float currentSensorData[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; uint16_t update_sensor_cnt = 0; uint16_t update_webserver_cnt = 0; uint16_t energySavingIterations = 0; WiFiManager wifiManager; Influxdb influxdb(INFLUXDB_HOST, INFLUXDB_PORT); Adafruit_APDS9960 apds; Adafruit_BME280 bme; bool apds_connected = false; #ifdef WEBUPDATER_FEATURE String localIP = "127.0.0.1"; #endif //*************************************************************************// void debug(String x) { #ifdef DEBUG Serial.println(x); #endif } void setup() { // Erase WiFi Credentials (maybe this will work ...) //WiFi.disconnect(true); //delay(2000); //ESP.reset(); #ifdef DEBUG Serial.begin(115200); #endif // Pin settings pinMode(BAT_CHARGED_PIN, INPUT_PULLUP); pinMode(BAT_CHARGING_PIN, INPUT_PULLUP); pinMode(STATUS_LED_PIN, OUTPUT); pinMode(ANEMOMETER_PIN, INPUT_PULLUP); pinMode(A0, INPUT); digitalWrite(STATUS_LED_PIN, LOW); #ifdef BATTERY_POWERED criticalBatCheck(); #endif // Establish WiFi connection String wifiName = "oko-weather-" + DEVICE_NAME; wifiManager.setMinimumSignalQuality(16); wifiManager.setConnectTimeout(WIFI_AUTOCONNECT_TIMEOUT_S); // the time in seconds to wait for the known wifi connection wifiManager.setTimeout(WIFI_CONFIG_PORTAL_TIMEOUT_S); // the time in seconds to wait for the user to configure the device if (!wifiManager.autoConnect(wifiName.c_str(), "DEADBEEF")) { debug("WiFi connection failed, going into deep sleep ..."); // If autoconnect to WLAN failed and no client connected, go to deep sleep ESP.deepSleep(POWERSAVING_SLEEP_S * 1000000, WAKE_RF_DEFAULT); delay(100); } debug("Connected!"); // Init variables to influxdb config - doesn't talk to database influxdb.opendb(INFLUXDB_DB, INFLUXDB_USER, INFLUXDB_PASS); // Initialize and configure the sensors //Light Sensor if (apds.begin()) { apds_connected = true; apds.enableColor(true); } debug("APDS Connected!"); //Temperature + pressure bool status = bme.begin(BME_ADDRESS); if (!status) { debug("Could not find a valid BME280 sensor, check wiring!"); //#warning TODO: FIXME while (1); } debug("BME Connected!"); #ifdef WEBUPDATER_FEATURE #ifndef BATTERY_POWERED setupWebUpdater(); localIP = WiFi.localIP().toString(); #endif #endif //It's magic! leave in delay(100); #ifdef BATTERY_POWERED debug("battery powered"); _loop(); 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); #endif } //*************************************************************************// #ifdef BATTERY_POWERED void criticalBatCheck() { float volt = getBatteryVoltage(); 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 #ifdef BATTERY_POWERED int energySavingMode() { // Give the solar panel some time to load the cell to prevent // flapping. if (energySavingIterations > 0) { energySavingIterations--; return 1; } // Is the battery low? if (currentSensorData[SENSOR_BAT_VOLTAGE] <= BAT_LOW_VOLTAGE) { // Entering energy saving energySavingIterations = ENERGY_SAVING_ITERATIONS; return 1; } return 0; } #endif void loop() { #ifdef BATTERY_POWERED delay(50); return; #endif #ifdef WEBUPDATER_FEATURE if (UPDATE_WEBSERVER_INTVERVAL_S * 1000 / DELAY_LOOP_MS <= update_webserver_cnt) { update_webserver_cnt = 0; doWebUpdater(); } #endif _loop(); //Needed to give WIFI time to function properly delay(DELAY_LOOP_MS); update_sensor_cnt++; #ifdef WEBUPDATER_FEATURE update_webserver_cnt++; #endif } void _loop() { #ifndef BATTERY_POWERED if (UPDATE_SENSOR_INTERVAL_S * 1000 / DELAY_LOOP_MS > update_sensor_cnt) { return; } #endif update_sensor_cnt = 0; currentSensorData[SENSOR_TEMPERATURE] = fetchTemperature(); currentSensorData[SENSOR_HUMIDITY] = fetchHumidity(); if (apds_connected) { currentSensorData[SENSOR_LIGHT] = fetchLight(); } currentSensorData[SENSOR_PRESSURE] = fetchPressure(); #ifdef BATTERY_POWERED currentSensorData[SENSOR_BAT_VOLTAGE] = getBatteryVoltage(); currentSensorData[SENSOR_BATCHARGESTATE] = isBatCharging(); #else currentSensorData[SENSOR_BAT_VOLTAGE] = 0xFFFFFFFF; currentSensorData[SENSOR_BATCHARGESTATE] = 0xFFFFFFFF; #endif #ifdef BATTERY_POWERED // Disable expensive tasks if (energySavingMode() == 0) { #endif currentSensorData[SENSOR_WINDSPEED] = fetchWindspeed(); currentSensorData[SENSOR_ESAVEMODE] = ENERGY_SAVE_MODE_DISABLED; #ifdef BATTERY_POWERED } else { currentSensorData[SENSOR_WINDSPEED] = 0xFFFFFFFF; currentSensorData[SENSOR_ESAVEMODE] = ENERGY_SAVE_MODE_ENABLED; } #endif debug(""); debug("Current readings:"); debug("Temperature: " + String(currentSensorData[SENSOR_TEMPERATURE]) + " °C"); debug("Humidity: " + String(currentSensorData[SENSOR_HUMIDITY]) + " %"); debug("Light: " + String(currentSensorData[SENSOR_LIGHT]) + " Lux"); debug("Windspeed: " + String(currentSensorData[SENSOR_WINDSPEED]) + " km/h"); debug("Pressure: " + String(currentSensorData[SENSOR_PRESSURE]) + " hPa"); debug("Bat Voltage: " + String(currentSensorData[SENSOR_BAT_VOLTAGE]) + " V"); debug("Bat charge state: " + String(currentSensorData[SENSOR_BATCHARGESTATE])); debug("Energy saving: " + String(currentSensorData[SENSOR_ESAVEMODE])); delay(100); pushToInfluxDB(DEVICE_NAME, currentSensorData); #ifdef WEBUPDATER_FEATURE setSensorData(DEVICE_NAME, localIP, currentSensorData); #endif } //*************************************************************************//