//#define DISABLE_WIFI #define HW_PROTO_PAPER 1 #define HW_PROTO_V1 2 #include // https://github.com/admarschoonen/WiFiManager #include #include #include #include #include #include #include #include "Adafruit_BME680.h" //#include "bsec.h" #define NUM_LEDS_PROTO_PAPER 93 #define DATA_PIN_PROTO_PAPER 5 #define CONNECT_SW_PIN_PROTO_PAPER BUTTON_BUILTIN #define NUM_LEDS_PROTO_V1 29 #define DATA_PIN_PROTO_V1 14 #define CONNECT_SW_PIN_PROTO_V1 21 static int BUIENRADAR_START_LED = 0; static int BUIENRADAR_SKIP_LED = 1; static int BUIENRADAR_NUM_LEDS = 12; static int PAQI_START_LED = 0; static int PAQI_SKIP_LED = 0; static int PAQI_NUM_LEDS = 0; static int UVI_LED = 0; static int AQI_LED = 0; static int POLLEN_LED = 0; static int IAQI_LED = 0; static int CONNECT_SW_PIN = 0; #define SHOW_AQI_LED #define SHOW_POLLEN_LED #define SHOW_UVI_LED #define MIN(x, y) (((x) <= (y)) ? (x) : (y)) Adafruit_NeoPixel leds_rgb_proto_paper(NUM_LEDS_PROTO_PAPER, DATA_PIN_PROTO_PAPER, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel leds_rgbw_proto_v1(NUM_LEDS_PROTO_V1, DATA_PIN_PROTO_V1, NEO_RGBW + NEO_KHZ800); #define SEALEVELPRESSURE_HPA (1013.25) #define BME_CS 5 Adafruit_BME680 bme(BME_CS); // hardware SPI //SPIClass SPI1(HSPI); //Bsec iaqSensor; static HTTPClient http; static const String baseUrlAQI = "http://target.luon.net:2356//forecast?&metrics=PAQI&metrics=AQI&metrics=pollen&metrics=UVI"; static const String baseUrlPrecipitation = "http://target.luon.net:2356//forecast?metrics=precipitation"; static int hw_variant = 0; WiFiManager wifiManager; DESPatch dESPatch; const char* root_ca = \ "-----BEGIN CERTIFICATE-----\n" \ "MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\n" \ "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \ "DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\n" \ "PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\n" \ "Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" \ "AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\n" \ "rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\n" \ "OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\n" \ "xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n" \ "7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\n" \ "aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n" \ "HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\n" \ "SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\n" \ "ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\n" \ "AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\n" \ "R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\n" \ "JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\n" \ "Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n" \ "-----END CERTIFICATE-----\n"; const float location[] = {51.445466493287434, 5.515445691496135}; // Telefoonstraat, Eindhoven const String address = "Telefoonstraat 18, Eindhoven"; //const float location[] = {51.51831326813842, 4.451744264773111}; // Bandeliersberg, Roosendaal //const float location[] = {51.44083, 5.47778}; // Eindhoven //const float location[] = {52.09083, 5.12222}; // Utrecht //const float location[] = {51.8425, 5.85278}; // Nijmegen //const float location[] = {52.37403, 4.88969}; // Amsterdam //const float location[] = {52.15833, 4.49306}; // Leiden typedef struct RainPickerDataArray24 { const int len = 24; long time[24]; float value[24]; } RainPickerDataArray24; typedef struct RainPickerDataArray5 { const int len = 5; long time[5]; float value[5]; } RainPickerDataArray5; typedef struct RainpickerData { float lat; float lon; long time; RainPickerDataArray24 PAQI; RainPickerDataArray24 AQI; RainPickerDataArray24 pollen; RainPickerDataArray5 UVI; RainPickerDataArray24 precipitation; } RainpickerData; RainpickerData rainpickerData; StaticJsonDocument<6144> doc; void parseJson(String * payload) { doc.clear(); DeserializationError error = deserializeJson(doc, * payload); if (error) { Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); return; } rainpickerData.lat = doc["lat"]; rainpickerData.lon = doc["lon"]; rainpickerData.time = doc["time"]; int n = 0; for (JsonObject elem : doc["PAQI"].as()) { if (n >= rainpickerData.PAQI.len) { break; } rainpickerData.PAQI.time[n] = elem["time"]; rainpickerData.PAQI.value[n] = elem["value"]; n = n + 1; } n = 0; for (JsonObject elem : doc["AQI"].as()) { if (n >= rainpickerData.AQI.len) { break; } rainpickerData.AQI.time[n] = elem["time"]; rainpickerData.AQI.value[n] = elem["value"]; n = n + 1; } n = 0; for (JsonObject elem : doc["pollen"].as()) { if (n >= rainpickerData.pollen.len) { break; } rainpickerData.pollen.time[n] = elem["time"]; rainpickerData.pollen.value[n] = elem["value"]; n = n + 1; } n = 0; for (JsonObject elem : doc["UVI"].as()) { if (n >= rainpickerData.UVI.len) { break; } rainpickerData.UVI.time[n] = elem["time"]; rainpickerData.UVI.value[n] = elem["value"]; n = n + 1; } n = 0; for (JsonObject elem : doc["precipitation"].as()) { if (n >= rainpickerData.precipitation.len) { break; } rainpickerData.precipitation.time[n] = elem["time"]; rainpickerData.precipitation.value[n] = elem["value"]; n = n + 1; } } static void setup_pins_proto_paper(void) { BUIENRADAR_START_LED = 32; BUIENRADAR_SKIP_LED = 1; BUIENRADAR_NUM_LEDS = 12; PAQI_START_LED = 72; PAQI_SKIP_LED = 0; PAQI_NUM_LEDS = 12; // #define UVI_LED 92 // LED in center UVI_LED = 84; // LED above center AQI_LED = 86; // LED to the right POLLEN_LED = 90; // LED to the left IAQI_LED = 88; // LED below center CONNECT_SW_PIN = CONNECT_SW_PIN_PROTO_PAPER; } static void setup_pins_proto_v1(void) { BUIENRADAR_START_LED = 0; BUIENRADAR_SKIP_LED = 0; BUIENRADAR_NUM_LEDS = 12; PAQI_START_LED = 12; PAQI_SKIP_LED = 0; PAQI_NUM_LEDS = 12; UVI_LED = 27; // LED above center AQI_LED = 24; // LED to the right POLLEN_LED = 26; // LED to the left IAQI_LED = 25; // LED below center CONNECT_SW_PIN = CONNECT_SW_PIN_PROTO_V1; } void leds_clear(void) { if (hw_variant == HW_PROTO_PAPER) { leds_rgb_proto_paper.clear(); leds_rgb_proto_paper.show(); } else if (hw_variant == HW_PROTO_V1) { leds_rgbw_proto_v1.clear(); leds_rgbw_proto_v1.show(); } } void setup() { const char url[] = "https://apikey:cqprlgiafadnidsgeqozcpldkaeqimqw@despatch.luon.net/files/4/despatch.json"; unsigned long interval = 60; // By default check for updates every 60 seconds int x; // sanity check delay - allows reprogramming if accidently blowing power w/leds delay(2000); Serial.begin(115200); Serial.println("buienradarklok starting"); if (!bme.begin()) { Serial.println(F("Could not find a valid BME680 sensor; assuming hw variant \"paper prototype\"!")); hw_variant = HW_PROTO_PAPER; setup_pins_proto_paper(); leds_rgb_proto_paper.begin(); // INITIALIZE NeoPixel strip object } else { Serial.println(F("BME680 found; assuming hw variant \"proto v1\"")); // Set up oversampling and filter initialization bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(320, 150); // 320*C for 150 ms hw_variant = HW_PROTO_V1; setup_pins_proto_v1(); leds_rgbw_proto_v1.begin(); // INITIALIZE NeoPixel strip object } //SPI1.begin(); //iaqSensor.begin(BME_CS, SPI1); //String output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix); //Serial.println(output); leds_clear(); #ifndef DISABLE_WIFI //wifiManager.resetSettings(); wifiManager.configure("Claire-", true, LED_BUILTIN, true, CONNECT_SW_PIN, false); //fetches ssid and pass and tries to connect //if it does not connect it starts an access point //and goes into a blocking loop awaiting configuration if (!wifiManager.autoConnect()) { Serial.println("failed to connect and hit timeout"); //reset and try again, or maybe put it to deep sleep ESP.restart(); delay(1000); } //if you get here you have connected to the WiFi Serial.print("connected with address: "); Serial.println(WiFi.localIP()); if (hw_variant == HW_PROTO_PAPER) { //keep LED on digitalWrite(LED_BUILTIN, LED_ON_VALUE_DEFAULT); } x = dESPatch.configure(url, true, false, interval, false, root_ca); Serial.print("dESPatch.configure() returned with code "); Serial.println(x); #endif } // From https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ String urlencode(String str) { String encodedString=""; char c; char code0; char code1; char code2; for (int i =0; i < str.length(); i++){ c=str.charAt(i); if (c == ' '){ encodedString+= '+'; } else if (isalnum(c)){ encodedString+=c; } else{ code1=(c & 0xf)+'0'; if ((c & 0xf) >9){ code1=(c & 0xf) - 10 + 'A'; } c=(c>>4)&0xf; code0=c+'0'; if (c > 9){ code0=c - 10 + 'A'; } code2='\0'; encodedString+='%'; encodedString+=code0; encodedString+=code1; //encodedString+=code2; } yield(); } return encodedString; } void getPrecipitation(const float * location) { int httpCode; //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); String url, latS, lonS; int tmp1, tmp2; String payload = ""; tmp1 = int(location[0]); tmp2 = int((location[0] - tmp1) * 1000000); latS = String(tmp1) + "." + String(tmp2); tmp1 = int(location[1]); tmp2 = int((location[1] - tmp1) * 1000000); lonS = String(tmp1) + "." + String(tmp2); url = baseUrlPrecipitation + "&lat=" + latS + "&lon=" + lonS; Serial.println(url); http.begin(url); httpCode = http.GET(); if (httpCode > 0) { payload = http.getString(); parseJson(&payload); } else { Serial.print(__func__); Serial.print("(): got http code "); Serial.println(httpCode); } http.end(); } void getPrecipitation(const String address) { int httpCode; //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); String url, a; String payload = ""; a = urlencode(address); url = baseUrlPrecipitation + "&address=" + a; Serial.println(url); http.begin(url); httpCode = http.GET(); if (httpCode > 0) { payload = http.getString(); parseJson(&payload); } else { Serial.print(__func__); Serial.print("(): got http code "); Serial.println(httpCode); } http.end(); } void getAQI(const float * location) { int httpCode; //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); String url, latS, lonS; int tmp1, tmp2; String payload = ""; tmp1 = int(location[0]); tmp2 = int((location[0] - tmp1) * 1000000); latS = String(tmp1) + "." + String(tmp2); tmp1 = int(location[1]); tmp2 = int((location[1] - tmp1) * 1000000); lonS = String(tmp1) + "." + String(tmp2); url = baseUrlAQI + "&lat=" + latS + "&lon=" + lonS; Serial.println(url); http.begin(url); httpCode = http.GET(); if (httpCode > 0) { payload = http.getString(); parseJson(&payload); } else { Serial.print(__func__); Serial.print("(): got http code "); Serial.println(httpCode); } http.end(); } void getAQI(const String address) { int httpCode; //String url = baseUrl + "&lat=" + String(location[0]) + "&lon=" + String(location[1]); String url, a; String payload = ""; a = urlencode(address); url = baseUrlAQI + "&address=" + a; Serial.println(url); http.begin(url); httpCode = http.GET(); if (httpCode > 0) { payload = http.getString(); parseJson(&payload); } else { Serial.print(__func__); Serial.print("(): got http code "); Serial.println(httpCode); } http.end(); } static int colormap(float x) { // Input: intensity // Output: RGB encoded pixel color similar to luchtmeetnet legend int y = 0; if (x < 1.0) { y = (0 << 16) | (0 << 8) | 0; // black (good) } else if (x < 2.0) { // 1.0 <= x < 2.0 if (hw_variant == HW_PROTO_PAPER) { y = (0 << 16) | (0 << 8) | 160; // blue (good) } else if (hw_variant == HW_PROTO_V1) { y = (0 << 16) | (0 << 8) | 255; // blue (good) } } else if (x < 3.0) { // 2.0 <= x < 3.0 y = (0 << 16) | (255 << 8) | 255; // cyan (good) } else if (x < 4.0) { // 3.0 <= x < 4.0 y = (255 << 16) | (255 << 8) | 255; // white (mediocre) } else if (x < 5.0) { // 4.0 <= x < 5.0 y = (200 << 16) | (200 << 8) | 32; // light yellow (mediocre) } else if (x < 6.0) { // 5.0 <= x < 6.0 y = (120 << 16) | (120 << 8) | 0; // yellow (mediocre) } else if (x < 7.0) { // 6.0 <= x < 7.0 y = (200 << 16) | (80 << 8) | 0; // orange (inadequate) } else if (x < 8.0) { // 7.0 <= x < 8.0 y = (255 << 16) | (50 << 8) | 0; // red orange (inadequate) } else if (x < 9.0) { // 8.0 <= x < 9.0 y = (180 << 16) | (0 << 8) | 0; // red (bad) } else if (x < 10.0) { // 9.0 <= x < 10.0 y = (200 << 16) | (0 << 8) | 160; // magenta (bad) } else { // 10.0 <= x y = (60 << 16) | (0 << 8) | 210; // purple (terrible) } return y; } static float buienradarMap(float x) { // Input: rain intensity (mm/h) // Output: floating point number which can be mapped on color scale float y = 0; // Use E3 series of preferred numbers to map intensity to color if (x < 0.1) { y = 0.5; } else if (x < 0.22) { y = 1.5; } else if (x < 0.47) { y = 2.5; } else if (x < 1.0) { y = 3.5; } else if (x < 2.2) { y = 4.5; } else if (x < 4.7) { y = 5.5; } else if (x < 10) { y = 6.5; } else if (x < 22) { y = 7.5; } else if (x < 47) { y = 8.5; } else if (x < 100) { y = 9.5; } else { y = 10.5; } return y; } static void ledsSetPixelColor(uint32_t ledIdx, int r, int g, int b, int w) { if (hw_variant == HW_PROTO_PAPER) { leds_rgb_proto_paper.setPixelColor(ledIdx, leds_rgb_proto_paper.Color(r, g, b)); } else if (hw_variant == HW_PROTO_V1) { leds_rgbw_proto_v1.setPixelColor(ledIdx, leds_rgbw_proto_v1.Color(g, r, b, w)); } } static void ledsSetPixelColor(uint32_t ledIdx, int r, int g, int b) { ledsSetPixelColor(ledIdx, r, g, b, 0); } static void updateLedsNoWifi(void) { uint32_t ledIdx; uint32_t ledCount; uint32_t color; int r, g, b, w, n; if (hw_variant == HW_PROTO_PAPER) { // Start by setting all leds to value of the first datapoint ledIdx = 11; ledCount = 0; for (ledCount = 0; ledCount < 11; ledCount++) { color = colormap(ledCount); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); ledsSetPixelColor(ledIdx, r, g, b); ledIdx = ledIdx + PAQI_SKIP_LED + 1; } } else if (hw_variant == HW_PROTO_V1) { // Start by setting all leds to value of the first datapoint ledIdx = 0; ledCount = 0; for (ledCount = 0; ledCount < 11; ledCount++) { color = colormap(ledCount); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); w = (color & 0xFF000000) >> 24; ledsSetPixelColor(ledIdx, r, g, b, w); ledIdx = ledIdx + PAQI_SKIP_LED + 1; } } } static void updateLeds(void) { uint32_t ledIdx; uint32_t ledCount; uint32_t color; int r, g, b, w, n; // Start by setting all leds to value of the first datapoint ledIdx = PAQI_START_LED; ledCount = 0; for (ledCount = 0; ledCount < PAQI_NUM_LEDS; ledCount++) { color = colormap(rainpickerData.PAQI.value[0]); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); w = (color & 0xFF000000) >> 24; ledsSetPixelColor(ledIdx, r, g, b); ledIdx = ledIdx + PAQI_SKIP_LED + 1; } ledIdx = PAQI_START_LED; ledCount = 0; for (n = 0; n < rainpickerData.PAQI.len; n++) { if (rainpickerData.time < rainpickerData.PAQI.time[n]) { ledIdx = ledIdx + PAQI_SKIP_LED + 1; ledCount = ledCount + 1; } if (ledCount >= PAQI_NUM_LEDS) { break; } color = colormap(rainpickerData.PAQI.value[n]); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); w = (color & 0xFF000000) >> 24; ledsSetPixelColor(ledIdx, r, g, b); } #ifdef SHOW_AQI_LED // calculate AQI max value for next 12 hours uint32_t AQI_max_value = 0; //Serial.println("AQI:"); for (n = 0; n < rainpickerData.AQI.len; n++) { /*Serial.print(" "); Serial.print(rainpickerData.AQI.time[n]); Serial.print(": "); Serial.println(rainpickerData.AQI.value[n]);*/ if (rainpickerData.time - rainpickerData.AQI.time[n] >= 60 * 60) { continue; } if (rainpickerData.AQI.time[n] >= rainpickerData.time + PAQI_NUM_LEDS * 60 * 60) { break; } if (rainpickerData.AQI.value[n] > AQI_max_value) { AQI_max_value = rainpickerData.AQI.value[n]; } } //Serial.print("AQI max value: "); //Serial.println(AQI_max_value); color = colormap(AQI_max_value); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); ledsSetPixelColor(AQI_LED, r, g, b); #endif #ifdef SHOW_POLLEN_LED // calculate pollen max value for next 12 hours uint32_t pollen_max_value = rainpickerData.pollen.value[0]; for (n = 0; n < rainpickerData.pollen.len; n++) { if (rainpickerData.time - rainpickerData.pollen.time[n] >= 60 * 60) { continue; } if (rainpickerData.pollen.time[n] >= rainpickerData.time + PAQI_NUM_LEDS * 60 * 60) { break; } if (rainpickerData.pollen.value[n] > pollen_max_value) { pollen_max_value = rainpickerData.pollen.value[n]; } } color = colormap(pollen_max_value); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); ledsSetPixelColor(POLLEN_LED, r, g, b); #endif #ifdef SHOW_UVI_LED // calculate UVI led value // UVI data is daily based --> search for first timestamp in the future and select value of the timestamp just before that uint32_t UVI_value = rainpickerData.UVI.value[0]; for (n = 0; n < rainpickerData.UVI.len; n++) { if (rainpickerData.UVI.time[n] >= rainpickerData.time) { break; } UVI_value = rainpickerData.UVI.value[n]; } color = colormap(UVI_value); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); ledsSetPixelColor(UVI_LED, r, g, b); #endif // Start by setting all leds to value of the first datapoint ledIdx = BUIENRADAR_START_LED; for (ledCount = 0; ledCount < BUIENRADAR_NUM_LEDS; ledCount++) { color = colormap(rainpickerData.precipitation.value[0]); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); ledIdx = ledIdx + BUIENRADAR_SKIP_LED + 1; } ledIdx = BUIENRADAR_START_LED; ledCount = 0; for (n = 0; n < rainpickerData.precipitation.len; n++) { if (rainpickerData.time < rainpickerData.precipitation.time[n]) { ledIdx = ledIdx + BUIENRADAR_SKIP_LED + 1; ledCount = ledCount + 1; } if (ledCount >= BUIENRADAR_NUM_LEDS) { break; } color = colormap(buienradarMap(rainpickerData.precipitation.value[n])); r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = (color & 0x0000FF); ledsSetPixelColor(ledIdx, r, g, b); } ledsSetPixelColor(IAQI_LED, r, g, b); } void readSensors(void) { static bool readingInProgress = false; static unsigned long endTime = 0; if (hw_variant == HW_PROTO_PAPER) { return; } if (readingInProgress == false) { Serial.println("Starting new measurement"); unsigned long endTime = bme.beginReading(); if (endTime == 0) { Serial.println(F("Failed to begin reading :(")); return; } readingInProgress = true; Serial.print(F("Reading started at ")); Serial.print(millis()); Serial.print(F(" and will finish at ")); Serial.println(endTime); } else { if (millis() - endTime > 0) { // Obtain measurement results from BME680. Note that this operation isn't // instantaneous even if milli() >= endTime due to I2C/SPI latency. // // This call takes about 162 ms if (!bme.endReading()) { Serial.println(F("Failed to complete reading :(")); return; } readingInProgress = false; Serial.print(F("Reading completed at ")); Serial.println(millis()); Serial.print(F("Temperature = ")); Serial.print(bme.temperature); Serial.println(F(" *C")); Serial.print(F("Pressure = ")); Serial.print(bme.pressure / 100.0); Serial.println(F(" hPa")); Serial.print(F("Humidity = ")); Serial.print(bme.humidity); Serial.println(F(" %")); Serial.print(F("Gas = ")); Serial.print(bme.gas_resistance / 1000.0); Serial.println(F(" KOhms")); Serial.print(F("Approx. Altitude = ")); Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); Serial.println(F(" m")); Serial.println(); } } } void readConnectButton(void) { static int reset_wifi_timer = millis(); static bool reset_blocked = true; if (digitalRead(CONNECT_SW_PIN) == HIGH) { // button released reset_wifi_timer = millis(); reset_blocked = false; digitalWrite(LED_BUILTIN, LOW); } else { // button pressed if (reset_blocked == false) { int delta = millis() - reset_wifi_timer; if (delta < 10000) { if (delta % 1000 < 500) { digitalWrite(LED_BUILTIN, HIGH); } else { digitalWrite(LED_BUILTIN, LOW); } } else { leds_clear(); wifiManager.resetSettings(); // block reset so user must release button before we can reset again reset_blocked = true; digitalWrite(LED_BUILTIN, LOW); ESP.restart(); } } } } void loop(void) { static unsigned long t_prev = 0; static bool ledOn = false; unsigned long t_now = millis(); const int buttonPin = 0; static int rainColor[12] = {0}; static int AQIColor[12] = {0}; static bool firstTime = true; static int counter = 0; if (firstTime || (t_now - t_prev >= 60000)) { t_prev = t_now; firstTime = false; #ifndef DISABLE_WIFI getPrecipitation(address); if (counter == 0) { getAQI(address); } counter = counter + 1; if (counter == 5) { // AQI should only be retreived every 5 minutes counter = 0; } #endif } #ifdef DISABLE_WIFI updateLedsNoWifi(); #else updateLeds(); #endif if (hw_variant == HW_PROTO_PAPER) { leds_rgb_proto_paper.show(); } else if (hw_variant == HW_PROTO_V1) { leds_rgbw_proto_v1.show(); } // readSensors(); readConnectButton(); delay(100); #ifndef DISABLE_WIFI dESPatch.checkForUpdate(true); #endif }