/* * WetterStation * Version: 0.0 * Author: Hendrik Langer */ #include #include #include extern "C" { #include "user_interface.h" } #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "passwords.h" const char* server = "ingress.opensensemap.org"; #define MQTT_MAX_PACKET_SIZE 512 const char* mqttserver = "home.xd0.de"; const char* mqttusername = "esp-weatherstation"; const char* mqttpassword = PWD_MQTT; constexpr unsigned int postingInterval = 60000; //Uploadintervall in Millisekunden constexpr unsigned int dhcp_interval = 60*60*1000; int loop_count = 0; #define EXTERNAL_POWER 1 //senseBox ID #define SENSEBOX_ID "5a9e9e38f55bff001a494877" //Sensor IDs // Temperature #define SENSOR1_ID "5a9e9e38f55bff001a49487e" // Humidity #define SENSOR2_ID "5a9e9e38f55bff001a49487d" // Pressure #define SENSOR3_ID "5a9e9e38f55bff001a49487c" // PM10 #define SENSOR4_ID "5a9e9e38f55bff001a49487b" // PM2.5 #define SENSOR5_ID "5a9e9e38f55bff001a49487a" // Radioactivity #define SENSOR6_ID "5a9e9e38f55bff001a494879" // Voltage #define SENSOR7_ID "5a9e9e38f55bff001a494878" // RSSI #define SENSOR8_ID "5a9eddb1f55bff001a51de52" static constexpr uint8_t BMP_SCL = D4; static constexpr uint8_t BMP_SDA = D3; static constexpr uint8_t DHT22_PIN = D7; static constexpr uint8_t DHTTYPE = DHT22; // DHT 22 (AM2302) static constexpr uint8_t SDS_TX = D1; static constexpr uint8_t SDS_RX = D2; static constexpr uint8_t GEIGER_PIN = D6; static constexpr float CONV_FACTOR = 0.008120; static constexpr float OWN_BACKGROUND_CPS = 0; // documentation says 0.2 (make sure value doesn't get negative if subtracting!) #ifndef EXTERNAL_POWER ADC_MODE(ADC_VCC); #endif ESP8266WiFiMulti wifiMulti; os_timer_t Timer1; RunningAverage geigeraverage(10); Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085); DHT dht(DHT22_PIN, DHTTYPE); SdsDustSensor sds(SDS_RX, SDS_TX); volatile unsigned long geiger_counts = 0; unsigned long geiger_previousMillis; unsigned long last_dhcp = 0; unsigned long previousMillis = 0; IPAddress ip, dns, gateway, subnet; char ssid[64]; char password[64]; struct __attribute__((packed)) SENSOR_DATA { float temperature; float humidity; float pressure; float temp2; float p10; float p25; float cpm; float radioactivity; float voltage; float rssi; } sd; static uint32_t cal = system_rtc_clock_cali_proc(); // WARNING: UPDATING THIS WILL MAKE THE rtcMillis() RETURN A LOWER VALUE THEN BEFORE, EVEN IF TIME PASSED, BREAKING INTERVAL CHECKS! unsigned long rtcMillis() { uint64_t rtc_t = system_get_rtc_time(); // uint32_t timemicrosec = rtc_t*((uint64) ((cal * 1000) >> 12)); // Serial.printf("cal: %d.%d \r\n", ((cal*1000)>>12)/1000, ((cal*1000)>>12)%1000 ); // Serial.printf("cal: %u\n", cal); return ((uint64_t)(rtc_t*(uint64_t)cal) >> 12)/1000; } void fpm_wakup_cb_func1(void) { //gpio_pin_wakeup_disable(); //ESP.wdtFeed(); //wifi_fpm_do_wakeup(); wifi_fpm_close(); geiger_counts++; } void lightsleep() { ESP.wdtFeed(); wifi_station_disconnect(); wifi_set_opmode(NULL_MODE); wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); wifi_fpm_open(); // Enables force sleep GPIO_DIS_OUTPUT(GPIO_ID_PIN(GEIGER_PIN)); PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U,FUNC_GPIO13); // PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U,FUNC_GPIO13); gpio_pin_wakeup_enable(GPIO_ID_PIN(GEIGER_PIN), GPIO_PIN_INTR_LOLEVEL); wifi_fpm_set_wakeup_cb(fpm_wakup_cb_func1); wifi_fpm_do_sleep(0xFFFFFFF); // Sleep for longest possible time //delay(1); } void ICACHE_FLASH_ATTR sendValues() { sd.temperature = dht.readTemperature(); sd.humidity = dht.readHumidity(); bmp.getPressure(&(sd.pressure)); sd.pressure /= 100; bmp.getTemperature(&(sd.temp2)); #ifndef EXTERNAL_POWER sd.voltage = ESP.getVcc()/1024.0; #else sd.voltage = analogRead(A0)*0.04285078 -0.05942125; // by linear regression for my(!) voltage divider. else: sd.voltage = analogRead(A0)*3.3*(R1+R2)/(R2*1024) #endif // sds.wakeup(); delay(30000); // working 30 seconds PmResult pm = sds.queryPm(); bool sds_updated = false; if(pm.isOk()) { sd.p25 = pm.pm25; sd.p10 = pm.pm10; sds_updated = true; } else { sds_updated = false; } // sds.sleep(); if (rtcMillis() - geiger_previousMillis > 10000) { sd.cpm = geiger_counts * 60000 / (rtcMillis() - geiger_previousMillis); geiger_previousMillis = rtcMillis(); geiger_counts = 0; geigeraverage.addValue(sd.cpm); } //geiger_runningaverage = geigeraverage.getAverage()*10; float constexpr own_cpm = OWN_BACKGROUND_CPS * 60; sd.radioactivity = (sd.cpm - own_cpm) * CONV_FACTOR; DEBUG_MSG("Temperature : %6.2f°C (DHT22)\n", sd.temperature); DEBUG_MSG("Humidity : %6.2f%% (DHT22)\n", sd.humidity); DEBUG_MSG("Temperature : %6.2f°C (BMP180)\n", sd.temp2); DEBUG_MSG("Pressure : %6.2fhPa (BMP180)\n", sd.pressure); if (sds_updated) DEBUG_MSG("Particles 10 : %6.2fµg/m³ (SDS011)\n", sd.p10); if (sds_updated) DEBUG_MSG("Particles 2.5: %6.2fµg/m³ (SDS011)\n", sd.p25); if (sd.cpm > 0) DEBUG_MSG("Radiation : %6.2fµSv/h (J305)\n", sd.radioactivity); DEBUG_MSG("Voltage : %6.2fV (ESP8266)\n", sd.voltage); DynamicJsonBuffer jsonBuffer; JsonArray& array = jsonBuffer.createArray(); DynamicJsonBuffer jsonBuffer2; JsonObject& root = jsonBuffer.createObject(); JsonObject& temperatureObject = array.createNestedObject(); temperatureObject["sensor"] = SENSOR1_ID; temperatureObject["value"] = sd.temperature; root["temperature"] = sd.temperature; JsonObject& humidityObject = array.createNestedObject(); humidityObject["sensor"] = SENSOR2_ID; humidityObject["value"] = sd.humidity; root["humidity"] = sd.humidity; JsonObject& pressureObject = array.createNestedObject(); pressureObject["sensor"] = SENSOR3_ID; pressureObject["value"] = sd.pressure; root["pressure"] = sd.pressure; if (sds_updated) { JsonObject& pm10Object = array.createNestedObject(); pm10Object["sensor"] = SENSOR4_ID; pm10Object["value"] = sd.p10; root["pm10"] = sd.p10; JsonObject& pm25Object = array.createNestedObject(); pm25Object["sensor"] = SENSOR5_ID; pm25Object["value"] = sd.p25; root["pm2.5"] = sd.p25; } if (sd.cpm > 0) { JsonObject& cpmObject = array.createNestedObject(); cpmObject["sensor"] = SENSOR6_ID; cpmObject["value"] = sd.radioactivity; root["cpm"] = sd.cpm; root["radioactivity"] = sd.radioactivity; } JsonObject& voltageObject = array.createNestedObject(); voltageObject["sensor"] = SENSOR7_ID; voltageObject["value"] = sd.voltage; root["voltage"] = sd.voltage; JsonObject& rssiObject = array.createNestedObject(); rssiObject["sensor"] = SENSOR8_ID; rssiObject["value"] = sd.rssi; root["rssi"] = sd.rssi; char buffer[500]; array.printTo(buffer, sizeof(buffer)); WiFi.forceSleepWake(); delay(1); // yield(); WiFi.persistent(false); // don't load and save credentials to flash WiFi.mode(WIFI_STA); wifiMulti.addAP("nether.net", PWD_NETHERNET); wifiMulti.addAP("LNet", PWD_LNET); wifiMulti.addAP("hw1_gast", PWD_HW1); wifiMulti.addAP("Freifunk", ""); if ( ip != INADDR_NONE && dns != INADDR_NONE && gateway != INADDR_NONE && subnet != INADDR_NONE && ((ip[0] == 192 && ip[1] == 168) || (ip[0] == 172 && ip[1] == 16)) && strlen(ssid) > 0 && strlen(password) > 0 && (rtcMillis() - last_dhcp < dhcp_interval) ) { DEBUG_MSG("static ip\n"); WiFi.config(ip, dns, gateway, subnet); WiFi.begin(ssid, password); int tries = 0; constexpr unsigned int retry_delay = 500; constexpr unsigned int max_retry_delay = 10000; while (WiFi.status() != WL_CONNECTED) { tries++; DEBUG_MSG("."); if (tries*retry_delay >= max_retry_delay) { DEBUG_MSG(" [ERROR]\n"); DEBUG_MSG("Rebooting..\n"); ESP.restart(); } delay(retry_delay); } DEBUG_MSG(" [CONNECTED, static]\n"); DEBUG_MSG("IP address: "); DEBUG_MSG("%s\n", String(WiFi.localIP()).c_str()); } else { DEBUG_MSG("dhcp\n"); int tries = 0; constexpr unsigned int retry_delay = 500; constexpr unsigned int max_retry_delay = 12000; while (wifiMulti.run() != WL_CONNECTED) { tries++; DEBUG_MSG("."); if (tries*retry_delay >= max_retry_delay) { DEBUG_MSG(" [ERROR]\n"); DEBUG_MSG("Rebooting..\n"); ESP.restart(); } delay(retry_delay); } DEBUG_MSG(" [CONNECTED, dhcp]\n"); DEBUG_MSG("IP address: "); DEBUG_MSG("%s\n", String(WiFi.localIP()).c_str()); ip = WiFi.localIP(); dns = WiFi.dnsIP(); gateway = WiFi.gatewayIP(); subnet = WiFi.subnetMask(); strncpy(ssid, WiFi.SSID().c_str(), 64); strncpy(password, WiFi.psk().c_str(), 64); last_dhcp = rtcMillis(); } sd.rssi = WiFi.RSSI(); int httpCode = 0; for (int tries=0; tries<3 && httpCode != HTTP_CODE_CREATED; tries++) { HTTPClient httpclient; char url[100]; sprintf(url, "http://%s/boxes/%s/data", server, SENSEBOX_ID); httpclient.begin(url); httpclient.addHeader("Content-Type", "application/json"); httpCode = httpclient.POST(buffer); if (httpCode > 0) { if (httpCode == HTTP_CODE_CREATED) { httpclient.writeToStream(&Serial); DEBUG_MSG("\n"); } else { DEBUG_MSG("[HTTP] POST... failed, error: %s\n", httpclient.errorToString(httpCode).c_str()); } } httpclient.end(); } WiFiClientSecure net; MQTTClient mqttclient(MQTT_MAX_PACKET_SIZE); mqttclient.begin(mqttserver, 8883, net); int tries = 0; constexpr unsigned int retry_delay = 500; constexpr unsigned int max_retry_delay = 5000; while (!mqttclient.connect(mqttusername, mqttusername, mqttpassword)) { tries++; DEBUG_MSG("."); if (tries*retry_delay >= max_retry_delay) { DEBUG_MSG(" [ERROR]\n"); DEBUG_MSG("Rebooting..\n"); ESP.restart(); } delay(retry_delay); } mqttclient.loop(); root.printTo(buffer, sizeof(buffer)); if (mqttclient.publish("sensor/esp-weatherstation/01/json", buffer, strlen(buffer))) { DEBUG_MSG("mqtt done\n"); } else { DEBUG_MSG("mqtt failed\n"); } mqttclient.loop(); mqttclient.disconnect(); if (loop_count == 1) { XD0OTA ota; ota.update(); } loop_count++; WiFi.disconnect(); WiFi.mode(WIFI_OFF); WiFi.forceSleepBegin(); delay(1); // yield(); } // geiger event when gpio pulled low (called vin on board) /*void ICACHE_RAM_ATTR ISR_geiger_impulse() { geiger_counts++; DEBUG_MSG("X"); }*/ void setup() { #ifdef USERDEBUG Serial.begin(115200); #endif pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); // turn OFF board led //wifi_status_led_uninstall(); sds.begin(); sds.setCustomWorkingPeriod(5); // sensor sends data every 5 minutes sds.setQueryReportingMode(); // ensures sensor is in 'query' reporting mode Wire.begin(BMP_SDA, BMP_SCL); if (!bmp.begin(BMP085_MODE_STANDARD)) { DEBUG_MSG("No valid BMP085 sensor!\n"); } dht.begin(); pinMode(GEIGER_PIN, INPUT); //attachInterrupt(digitalPinToInterrupt(GEIGER_PIN), ISR_geiger_impulse, FALLING); geigeraverage.clear(); previousMillis = millis(); DEBUG_MSG("ready.\n"); Serial.flush(); } void loop() { unsigned long currentMillis = rtcMillis(); if ((currentMillis - previousMillis) >= postingInterval) { previousMillis = currentMillis; DEBUG_MSG("sending values... previousMillis:%u, millis():%u, difference:%d\n", previousMillis, currentMillis, currentMillis-previousMillis); sendValues(); DEBUG_MSG("resetting geiger counts. was: %d\n", geiger_counts); geiger_previousMillis = rtcMillis(); geiger_counts = 0; } DEBUG_MSG("sleeping now... (%u)\n", currentMillis); delay(10); lightsleep(); delay(10); // debounce? }