/* __ __ _______ _ _ __ __ | \/ | |__ __| (_|_) \/ | | \ / | __ _ _ __ __ _| |_ _____ _ _| \ / | __ _ _ __ ___ _ _ | |\/| |/ _` | '_ \ / _` | \ \ / / _ \| | | |\/| |/ _` | '_ ` _ \| | | | | | | | (_| | |_) | (_| | |\ V / (_) | | | | | | (_| | | | | | | |_| | |_| |_|\__,_| .__/ \__,_|_| \_/ \___/| |_|_| |_|\__,_|_| |_| |_|\__, | | | _/ | __/ | |_| |__/ |___/ Map with connected display and touch button, downloads data from TMEP.cz for Czech districts and display temperature/air quality. You can cycle between those with touch button. Display shows what data are you looking at and min/max value. More info about map: https://wiki.tmep.cz/doku.php?id=ruzne:led_mapa_okresu_cr Used components: - MapaTvojiMamy - LaskaKit ESP32-LPkit v2.4 - 0.91" OLED - Catalex touch sensor v2.0 (simply some capacative touch sensor) */ //////////////////// // Variables setup //////////////////// // Wi-Fi, JSON with data const char *ssid = "TVOJE_AP"; const char *password = "TVOJE_HESLO"; const char *json_url = "http://cdn.tmep.cz/app/export/okresy-cr-vse.json"; // After how long get new values - default 2 minutes unsigned long timerDelay = 120000; // After how go to "sleep" (LEDs off, display off, "wake up" by touching button) - default 5 minutes unsigned long offDelay = 300000; // LED strip - MapaTvojiMamy #define LEDS_COUNT 77 #define LEDS_PIN 14 #define CHANNEL 0 #define LEDS_BRIGHTNESS 10 // Touch button #define BUTTON_PIN 16 // Display PINs #define DISPLAY_CLOCK_PIN 25 #define DISPLAY_DATA_PIN 26 // What to show as default - temp or air char *defaultView = "temp"; // Display hello text char *displayText1 = "MapaTvojiMamy"; char displayText2[14] = ""; /////////// // Code /////////// // Libraries #include #include #include //https://github.com/bblanchon/ArduinoJson v6+ #include "Freenove_WS2812_Lib_for_ESP32.h" // Display #include #include U8G2_SSD1306_128X32_UNIVISION_1_SW_I2C u8g2(U8G2_R0, DISPLAY_CLOCK_PIN, DISPLAY_DATA_PIN, /* reset=*/U8X8_PIN_NONE); // Strip Freenove_ESP32_WS2812 strip = Freenove_ESP32_WS2812(LEDS_COUNT, LEDS_PIN, CHANNEL, TYPE_GRB); // Variables unsigned long lastTime = 0; unsigned long lastTouchTime = 0; int lastid, value, color, firstTime = 1, isOn = 1; // Button state boolean oldState = LOW; String sensorReadings; double sensorReadingsArr[3]; // in JSON is h1 used for temperature and h4 for air quality, we will stick with these variables double h1[77]; // array for temperature measurements double h4[77]; // array for air quality measurements double h1min, h1max, h4min, h4max; void setup() { u8g2.begin(); pinMode(BUTTON_PIN, INPUT); Serial.begin(115200); delay(5); Serial.println(); strip.begin(); strip.setBrightness(LEDS_BRIGHTNESS); } void lightenUpYourMamaMap() { // Map value to color, set it to corresponding LED and show it // We already have arrays populated if(defaultView == "temp") { for(int i = 0; i < LEDS_COUNT; i = i + 1) { color = map(h1[i], -15, 40, 170, 0); strip.setLedColorData(i, strip.Wheel(color)); } } else { for(int i = 0; i < LEDS_COUNT; i = i + 1) { color = map(h4[i], -15, 40, 170, 0); strip.setLedColorData(i, strip.Wheel(color)); } } strip.show(); } void setValuesForDisplay() { if(defaultView == "temp") { displayText1 = "Teplota"; sprintf(displayText2, "%d.%01d / %d.%01d", (int)h1min, abs((int)(h1min*10)%10), (int)h1max, abs((int)(h1max*10)%10)); } else { displayText1 = "Ovzdusi"; sprintf(displayText2, "%d.%01d / %d.%01d", (int)h4min, abs((int)(h4min*10)%10), (int)h4max, abs((int)(h4max*10)%10)); } } String httpGETRequest(const char *serverName) { WiFiClient client; HTTPClient http; http.begin(client, serverName); int httpResponseCode = http.GET(); String payload = "{}"; if(httpResponseCode > 0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); payload = http.getString(); } else { Serial.print("Error code: "); Serial.println(httpResponseCode); } http.end(); return payload; } void loop() { boolean newState = digitalRead(BUTTON_PIN); // Touching the button? if((newState == LOW) && (oldState == HIGH)) { // Short delay to debounce button. delay(40); // Check if button is still low after debounce. newState = digitalRead(BUTTON_PIN); // Button touched! if(newState == LOW) { Serial.print("Button touched, mode: "); lastTouchTime = millis(); // Are we on? Change what to show if(isOn == 1) { if(defaultView == "temp") { defaultView = "air"; } else { defaultView = "temp"; } setValuesForDisplay(); Serial.println(defaultView); // Let it shine lightenUpYourMamaMap(); } else { // "Powering up" Serial.println("go online"); isOn = 1; strip.setBrightness(LEDS_BRIGHTNESS); lightenUpYourMamaMap(); firstTime = 1; u8g2.setPowerSave(false); } } } // Let's go "offline" if(isOn == 1 && ((millis() - lastTouchTime) > offDelay)) { Serial.println("go offline"); isOn = 0; strip.setBrightness(0); lightenUpYourMamaMap(); u8g2.setPowerSave(true); } // Set the last-read button state to the old state. oldState = newState; // Did we wait long enough or was it just powered on? // Read JSON and populate variables with measurements from districts if(isOn == 1 && ((millis() - lastTime) > timerDelay || firstTime == 1)) { Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); if(WiFi.status() == WL_CONNECTED) { firstTime = 0; sensorReadings = httpGETRequest(json_url); WiFi.disconnect(); Serial.println("WiFi disconnected"); DynamicJsonDocument doc(12288); DeserializationError error = deserializeJson(doc, sensorReadings); if(error) { Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); return; } // reset min/max values h1min = 100; h1max = -100; h4min = 1000; h4max = 0; for (JsonObject item : doc.as()) { // Index, JSON starts with 1, we need to start with 0 so deduct 1 int ledIndex = item["id"]; ledIndex -= 1; double temp = item["h1"]; double air = item["h4"]; // Set minimums and maximums if(temp > h1max) { h1max = temp; } if(temp < h1min) { h1min = temp; } if(air > h4max) { h4max = air; } if(air < h4min) { h4min = air; } // Let's map air value to respective color on ColorWheel: // https://github.com/Freenove/Freenove_WS2812_Lib_for_ESP32/blob/master/extras/ColorWheel.jpg // green if(air < 20) { air = 85; } // orange else if(air < 40) { air = 45; } // red else if(air < 100) { air = 10; } // purple else if(air < 5000) { air = 200; } // Populate our two arrays h1[ledIndex] = temp; h4[ledIndex] = air; } setValuesForDisplay(); lightenUpYourMamaMap(); // Debugging values we got /* for(int i = 0; i < LEDS_COUNT; i = i + 1) { Serial.print("h1["); Serial.print(i); Serial.print("] value: "); Serial.println(h1[i]); Serial.print("h4["); Serial.print(i); Serial.print("] value: "); Serial.println(h4[i]); } */ } else { displayText1 = "NO WIFI"; Serial.println("WiFi not connected :( is reachable?"); } lastTime = millis(); } // Show texts on display if(isOn == 1) { u8g2.firstPage(); do { u8g2.setFont(u8g2_font_ncenB10_tr); u8g2.drawStr(0, 12, displayText1); u8g2.drawStr(0, 26, displayText2); } while (u8g2.nextPage()); } }