Advertisement
jgoy

Dynamic Electricity Prices (Europe) API - ESP32-3248S035 - including WiFi setup webserver

Jun 20th, 2024 (edited)
421
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 15.92 KB | Source Code | 0 0
  1. /*=================================================================================================
  2. In case of compilation errors in Arduino IDE (library TFT_eSPI) in ESP32 core 3.0.x
  3. Open the library folder TFT_eSPI:
  4. (i.e. Windows) - C:\Users\Your_User_Name\OneDrive\Documents\Arduino\libraries\TFT_eSPI\
  5. Folder C:\Users\Your_User_Name\OneDrive\Documents\Arduino\libraries\TFT_eSPI\Processors - file:
  6. - TFT_eSPI_ESP32.h
  7. - TFT_eSPI_ESP32_S3.h
  8. - TFT_eSPI_ESP32_C3.h
  9. ====================================================================================================
  10. just below the line:
  11. #include "hal/gpio_ll.h"
  12. add:
  13. #include "driver/gpio.h"  // add this line to fix problems during compilation (ESP32 core 3.0.x)
  14. ====================================================================================================
  15.  sketch for ESP32-3248S035 - VERSION WITHOUT the library WiFiManager.h
  16.  If a WiFi connection cannot be made, a Web server is started to store the WiFi credentials.
  17.  SHOULD COMPILE ON CORE 3.0.x (Arduino IDE: boards manager: ESP32 - espressif)
  18.  ====================================================================================
  19.  Arduino IDE - board: ESP32 Dev Module of DOIT ESP32 DEVKIT V1
  20.  https://nl.aliexpress.com/item/1005006398547061.html
  21.  https://nl.aliexpress.com/item/1005005900820162.html
  22. ====================================================================================*/
  23.  
  24. #include <WiFi.h>
  25. #include <ArduinoJson.h>  // https://github.com/bblanchon/ArduinoJson
  26. #include <WebServer.h>
  27. #include <Preferences.h>
  28. #include <HTTPClient.h>  // for API connection
  29. #include <TFT_eSPI.h>    // https://github.com/Bodmer/TFT_eSPI
  30.  
  31. TFT_eSPI tft = TFT_eSPI();  // User_Setup.h settings available on: https://espgo.be board ESP32-3248S035
  32. WebServer server(80);
  33. Preferences flash;
  34.  
  35. #define SHOW_ITEMS 34  // max number of records you want to process.
  36. #define VALUE_RED 100  // electricity price in red color if higher than this value
  37. #define UTC_OFFSET_SUMMER 7200
  38. #define UTC_OFFSET_WINTER 3600
  39. struct tm tInfo;  // https://cplusplus.com/reference/ctime/tm/
  40. bool chart = true, freshStart = true;
  41. const char* time_Zone = "CET-1CEST,M3.5.0,M10.5.0/3";  // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  42. double prijzen[SHOW_ITEMS];                            // most recent 34 prices
  43. double prices[SHOW_ITEMS * 2];                         // all prices returned by the API
  44. double priceNow;                                       // price of current hour
  45. time_t epo[SHOW_ITEMS];                                // most recent 34 epoch times
  46. time_t epoc[SHOW_ITEMS * 2];                           // all epoch times returned by the API
  47. uint8_t API_ITEMS;
  48. String my_link, webText, buttons, Ssid, Pasw;
  49. char* connect[] = { "WiFi: no connection", "Connect to hotspot", "S035_ESP", "Start a browser - address",
  50.                     "192.168.4.1", "to save network", "and password" };
  51.  
  52. void setup() {
  53.   Serial.begin(115200);
  54.   pinMode(0, INPUT_PULLUP);  // flash button
  55.   tft.init();
  56.   tft.setRotation(3);
  57.   tft.setTextWrap(false);
  58.   tft.fillScreen(TFT_BLACK);  // delete output from previous sessions
  59.   connectWiFi();
  60.   configTzTime(time_Zone, "be.pool.ntp.org");
  61.   showStartupData();
  62. }
  63.  
  64. void loop() {
  65.   display_Time();
  66.   if (!digitalRead(0)) changeView();  // flash button changes between chart or list of figures
  67.   // if (priceNow < 0) startHeavyPowerConsumer();  // priceNow = current price
  68.   // else stopHeavyPowerConsumer();  
  69. }
  70.  
  71. void showStartupData() {
  72.   tft.fillScreen(TFT_BLACK);
  73.   tft.drawCentreString("Retrieving SNTP time", 240, 160, 4);
  74.   getLocalTime(&tInfo);
  75.   delay(3000);
  76.   tft.fillScreen(TFT_BLACK);
  77.   tft.setTextColor(TFT_ORANGE);
  78.   tft.drawCentreString("Retrieving Json Data", 240, 160, 4);
  79. }
  80.  
  81. void changeView() {
  82.   chart = !chart;
  83.   chart ? show_Chart() : show_Data();
  84.   showCurrentPrice(), delay(800);  // 800 = UI debouncing (button)
  85. }
  86.  
  87. void display_Time() {
  88.   char theDate[11], theTime[9];
  89.   getLocalTime(&tInfo);  // SNTP sync at startup and every 3 hours thereafter (ESP32)
  90.   time_t now;
  91.   time(&now);  // assign current epoch time (UTC) to "now"
  92.   tft.drawRect(-1, 26, 482, 2, WiFi.isConnected() ? TFT_GREEN : TFT_RED);
  93.   tft.setTextColor(TFT_GREEN, TFT_BLACK);
  94.   strftime(theDate, sizeof(theDate), "%d-%m-%G", &tInfo);  // https://cplusplus.com/reference/ctime/strftime/
  95.   tft.drawString(theDate, 2, 0, 4);
  96.   tft.setTextColor(TFT_WHITE, TFT_BLACK);
  97.   strftime(theTime, sizeof(theTime), "%T", &tInfo);
  98.   tft.drawString(theTime, 135, 0, 4);
  99.   if ((freshStart) || ((((epo[SHOW_ITEMS - 1] - now) / 3600) < 9) && (tInfo.tm_min == 2) && (tInfo.tm_sec == 1))) {
  100.     if (WiFi.isConnected()) retrieve_Data();  // ^^from 1400 hrs: try to retrieve new data every hour until it succeeds
  101.     chart ? show_Chart() : show_Data(), showCurrentPrice();
  102.     if (!freshStart) {
  103.       tft.setTextColor(TFT_DARKGREY, TFT_BLACK);
  104.       tft.drawRightString(String((epo[SHOW_ITEMS - 1] - now) / 3600), 480, 0, 2);
  105.       if (((epo[SHOW_ITEMS - 1] - now) / 3600) > 8) tft.fillRect(444, 0, 36, 22, TFT_BLACK);
  106.     }
  107.     Serial.println(my_link);                             // debugging
  108.     Serial.println((epo[SHOW_ITEMS - 1] - now) / 3600);  // number of hours of future available data
  109.     freshStart = false;
  110.   }
  111.   if ((tInfo.tm_min == 0) && (tInfo.tm_sec == 0)) {  // refresh data (local) every hour
  112.     chart ? show_Chart() : show_Data();
  113.     showCurrentPrice(), delay(950);
  114.   }
  115. }
  116.  
  117. void retrieve_Data() {  // prepare string with epoch times and retrieve energy data
  118.   memset(prijzen, 0, sizeof(prijzen));
  119.   memset(epo, 0, sizeof(epo));
  120.   memset(prices, 0, sizeof(prices));
  121.   memset(epoc, 0, sizeof(epoc));
  122.   my_link = "https://api.energy-charts.info/price?bzn=BE&start=";
  123.   time_t now;
  124.   time(&now);                // assign current epoch time (UTC) to "now"
  125.   now = now - (now % 3600);  // round down to the whole hour
  126.   now = now + ((tInfo.tm_isdst) ? UTC_OFFSET_SUMMER : UTC_OFFSET_WINTER);
  127.   my_link = my_link + String(now - (24 * 3600)) + "&end=" + String(now + (SHOW_ITEMS * 3600));
  128.   if (my_link.length() > 56) get_JsonData();  // do not send empty input (e.g., if SNTP times out)
  129.   else {                                      // composing string (to send to API) = failed
  130.     tft.fillRect(0, 200, 320, 40, TFT_BLACK);
  131.     tft.drawCentreString("Link API incomplete", 160, 200, 4);
  132.   }
  133.   tft.setCursor(264, 0, 4);
  134.   tft.setTextColor(TFT_SILVER, TFT_BLACK);
  135.   tft.print("/MWh:");
  136.   tft.fillCircle(255, 10, 9, TFT_SILVER);  // euro symbol
  137.   tft.fillCircle(255, 10, 7, TFT_BLACK), tft.fillRect(258, 5, 8, 12, TFT_BLACK);
  138.   tft.drawRect(247, 8, 9, 2, TFT_SILVER), tft.drawRect(249, 11, 6, 2, TFT_SILVER);
  139. }
  140.  
  141. void show_Chart() {  // Json data in chart on display
  142.   tft.fillRect(0, 28, 480, 292, TFT_BLACK);
  143.   tft.setTextColor(TFT_WHITE);
  144.   for (uint8_t i = 0; i < 6; i++) {  // draw lines with values -50, 0, 50, 100 etc.
  145.     tft.drawFastHLine(0, 40 + i * 50, 480, tft.color565(64, 64, 64));
  146.     for (uint8_t j = 0; j < 3; j++)  // j handles the horizontal coordinate
  147.       tft.drawRightString(String(i * 50 - 50), j * 228 + 23, 286 - i * 50, 1);
  148.   }
  149.   for (uint8_t i = 0; i < SHOW_ITEMS; i++) {
  150.     struct tm* cor;              // to convert epoch times to tm structure in local time
  151.     if (epo[i]) {                // do not show empty data
  152.       cor = localtime(&epo[i]);  // adjust epoch times in the list to local time / dst
  153.       if ((cor->tm_hour == tInfo.tm_hour) && (cor->tm_mday == tInfo.tm_mday)) {
  154.         priceNow = prijzen[i];
  155.         tft.drawFastVLine(6 + i * 14, 26, 292, TFT_MAGENTA);
  156.         tft.fillTriangle((i * 14) - 4, 300, (i * 14) - 4, 310, 6 + i * 14, 305, TFT_MAGENTA);
  157.         tft.fillTriangle((i * 14) - 4, 55, (i * 14) - 4, 65, 6 + i * 14, 60, TFT_MAGENTA);
  158.       }
  159.       tft.setTextColor(prijzen[i] > 0 ? TFT_BLACK : TFT_YELLOW);
  160.       uint16_t mijnKleur = prijzen[i] < VALUE_RED ? TFT_CYAN : TFT_RED;
  161.       if (prijzen[i] < 50) mijnKleur = TFT_GREEN;
  162.       if (prijzen[i] < 0) {
  163.         mijnKleur = TFT_YELLOW;
  164.         tft.fillRect(i * 14, 240, 13, 0 - prijzen[i], mijnKleur);
  165.         tft.drawFastVLine(i * 14 + 13, 240, 0 - prijzen[i], TFT_BLACK);
  166.       } else {
  167.         tft.fillRect(i * 14, 240 - prijzen[i], 13, prijzen[i], mijnKleur);
  168.         tft.drawFastVLine(i * 14 + 13, 240 - prijzen[i], prijzen[i], TFT_BLACK);
  169.       }
  170.       tft.setCursor(1 + i * 14, 230, 1);  // hours at the bottom of chart
  171.       tft.printf("%02d", cor->tm_hour);
  172.       tft.setTextColor(TFT_RED);
  173.       tft.setCursor(1 + i * 14, 243, 1);
  174.       tft.printf("%02d", cor->tm_hour);
  175.     }
  176.   }
  177. }
  178.  
  179. void show_Data() {  // display Json data in 2 colums time / price
  180.   tft.fillRect(0, 28, 480, 292, TFT_BLACK);
  181.   tft.drawRect(240, 26, 2, 292, WiFi.isConnected() ? TFT_GREEN : TFT_RED);
  182.   struct tm* cor;
  183.   for (uint8_t i = 0; i < SHOW_ITEMS; i++) {
  184.     if (epo[i]) {
  185.       cor = localtime(&epo[i]);
  186.       tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
  187.       tft.setCursor(((i < 18) ? 0 : 260), 33 + (i % 18) * 16, 2);
  188.       tft.printf(" %02d. ", i + 1);
  189.       if ((cor->tm_hour == tInfo.tm_hour) && (cor->tm_mday == tInfo.tm_mday)) {
  190.         priceNow = prijzen[i];
  191.         tft.setTextColor(TFT_MAGENTA, TFT_BLACK);
  192.       } else tft.setTextColor(TFT_WHITE, TFT_BLACK);
  193.       tft.printf("%02d-%02d-%02d %02d:%02d", cor->tm_mday, cor->tm_mon + 1, cor->tm_year + 1900, cor->tm_hour, cor->tm_min);
  194.       tft.setTextColor((prijzen[i] < VALUE_RED) ? TFT_CYAN : TFT_RED);
  195.       if (prijzen[i] < 50) tft.setTextColor(TFT_GREEN, TFT_BLACK);
  196.       if (prijzen[i] < 0) tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  197.       tft.drawRightString(String(prijzen[i]), (i < 18 ? 220 : 480), 33 + (i % 18) * 16, 2);
  198.     }
  199.   }
  200. }
  201.  
  202. void showCurrentPrice() {  // price (upper right corner of display)
  203.   tft.setTextColor(priceNow < VALUE_RED ? TFT_CYAN : TFT_RED);
  204.   if (priceNow < 50) tft.setTextColor(TFT_GREEN);
  205.   if (priceNow < 0) tft.setTextColor(TFT_YELLOW);
  206.   tft.fillRect(344, 0, 100, 22, TFT_BLACK);  
  207.   tft.setCursor(344, 0, 4);
  208.   tft.printf("%3.2F", priceNow);
  209. }
  210.  
  211. void get_JsonData() {
  212.   HTTPClient http;
  213.   http.begin(my_link);
  214.   delay(3000);
  215.   if (http.GET() == 200) {
  216.     DynamicJsonDocument doc(8192);  // choose value large enough to contain the data
  217.     auto error = deserializeJson(doc, http.getString());
  218.     if (error) {
  219.       tft.drawCentreString("deserializeJson() failed", 240, 200, 4);
  220.       tft.drawCentreString(error.c_str(), 240, 250, 4);
  221.     }
  222.     for (uint8_t i = 0; i < SHOW_ITEMS * 2; i++) {  // actually more data than we can show on display
  223.       prices[i] = doc["price"][i];
  224.       epoc[i] = doc["unix_seconds"][i];
  225.       API_ITEMS = i;  // count number of returned items
  226.     }
  227.   } else {  // http.get has failed
  228.     tft.fillScreen(TFT_BLACK);
  229.     tft.drawCentreString("Error in API response", 240, 160, 4);
  230.     tft.drawCentreString("Restart in 18 seconds", 240, 190, 4);
  231.     delay(2000);
  232.     sleep16Sec();
  233.   }
  234.   http.end();
  235.   uint8_t j = SHOW_ITEMS + 1;
  236.   for (uint8_t i = API_ITEMS; i >= 0; i--) {  // select the most recent Json data
  237.     if (epoc[i] != 0) {                       // ignore empty data
  238.       j--;                                    // counter for results
  239.       prijzen[j - 1] = prices[i];             // transferring valid prices from API results
  240.       epo[j - 1] = epoc[i];                   // also hours to "local" array
  241.       if (j == 0) break;                      // stop when we have 34 items
  242.     }
  243.   }
  244. }
  245.  
  246. void sleep16Sec() {
  247.   esp_sleep_enable_timer_wakeup(16000000);  // 16 seconds
  248.   esp_deep_sleep_start();                   // deep sleep: this will reset the clock & force a new time sync
  249. }
  250.  
  251. void connectWiFi() {
  252.   WiFi.disconnect();
  253.   flash.begin("login_data", true);  // true = read only
  254.   Ssid = flash.getString("ssid", "");
  255.   Pasw = flash.getString("pasw", "");
  256.   flash.end();
  257.   WiFi.begin(Ssid.c_str(), Pasw.c_str());
  258.   if (testWifi()) return;
  259.   else {  // no WiFi connection
  260.     messageNoConnection();
  261.     setupAP();
  262.     startWebServer();
  263.     server.begin();
  264.   }
  265.   while ((WiFi.status() != WL_CONNECTED)) server.handleClient();
  266. }
  267.  
  268. bool testWifi() {
  269.   tft.fillScreen(TFT_BLACK);
  270.   tft.setCursor(44, 0, 4);
  271.   tft.setTextColor(TFT_GREEN, TFT_BLACK);
  272.   tft.print("Connect to WiFi");
  273.   tft.fillCircle(160, 138, 10, TFT_YELLOW);
  274.   tft.drawRect(10, 190, 300, 20, TFT_CYAN);
  275.   byte c = 0;
  276.   while (c < 180) {
  277.     if (WiFi.isConnected()) {
  278.       WiFi.setAutoReconnect(true);
  279.       WiFi.persistent(true);
  280.       return true;
  281.     }
  282.     delay(20);
  283.     for (byte t = 0; t < 4; t++) {  // animation logo wifi
  284.       tft.drawSmoothArc(160, 140, (c % 20) + 20 + (t * 20), (c % 20) + 14 + (t * 20), 135, 225, TFT_YELLOW, TFT_YELLOW, true);
  285.       tft.drawSmoothArc(160, 140, (c % 20) + 14 + (t * 20), (c % 20) + 13 + (t * 20), 120, 240, TFT_BLACK, TFT_BLACK, true);
  286.     }
  287.     tft.drawFastVLine(c + 11, 192, 16, TFT_CYAN);
  288.     ++c;
  289.   }
  290.   return false;
  291. }
  292.  
  293. void messageNoConnection() {
  294.   tft.fillScreen(TFT_NAVY);
  295.   tft.setTextColor(TFT_YELLOW), tft.setTextFont(4);
  296.   for (uint8_t i = 0; i < 7; i++) tft.drawCentreString(connect[i], 240, 40 + i * 30, 4);
  297. }
  298.  
  299. void setupAP(void) {
  300.   WiFi.mode(WIFI_STA);
  301.   WiFi.disconnect();
  302.   delay(500);
  303.   int n = WiFi.scanNetworks();
  304.   delay(5000);
  305.   for (int i = 0; i < n; ++i) {  // html to put found networks on buttons on web page
  306.     buttons += "\n<button onclick='scrollNaar(this.id)' id='" + WiFi.SSID(i) + "'>" + WiFi.SSID(i) + "</button><br>";
  307.   }
  308.   delay(1000);
  309.   WiFi.softAP("S035_ESP");
  310.   startWebServer();
  311.   server.begin();
  312. }
  313.  
  314. void startWebServer() {
  315.   server.on("/", []() {
  316.     IPAddress ip = WiFi.softAPIP();
  317.     webText = "<!DOCTYPE HTML>\n<html lang='en'>\n<head><title>Setup</title>\n<meta name='viewport' ";
  318.     webText += "content='width=device-width, initial-scale=1.0'>";
  319.     webText += "\n<style>\np {\n  font-family: Arial, Helvetica, sans-serif;\n  font-size: 18px;\n  margin: 0;\n  text-align: ";
  320.     webText += "center;\n}\n\nbutton, input[type=submit] {\n  width: 250px;\n  border-radius: 5px;\n  color: White;\n  padding:";
  321.     webText += " 4px 4px;\n  margin-top: 16px;\n  margin: 0 auto;\n  display:block;\n  font-size: 18px;\n  font-weight: 600;";
  322.     webText += "\n  background: DodgerBlue;\n}\n\ninput {\n  width: 250px;\n  font-size: 18px;\n  font-weight: 600;\n}";
  323.     webText += "\n</style>\n</head>\n<body><p style='font-family:arial; ";
  324.     webText += "font-size:240%;'>WiFi setup\n</p><p style='font-family:arial; font-size:160%;'>\n<br>";
  325.     webText += "Networks found:<br> Click on item to select or<br>Enter your network data<br> in the boxes below:</p><br>";
  326.     webText += buttons;
  327.     webText += "\n<form method='get' action='setting'>\n<p><b>\nSSID: <br>\n<input id='ssid' name='ssid'>";
  328.     webText += "<br>PASW: </b><br>\n<input type='password' name='pass'><br><br>\n<input type='submit' value='Save'>";
  329.     webText += "\n</p>\n</form>\n<script>\nfunction scrollNaar(tekst) {\n  document.getElementById('ssid')";
  330.     webText += ".value = tekst;\n  window.scrollTo(0, document.body.scrollHeight);\n}\n</script>\n</body>\n</html>";
  331.     server.send(200, "\ntext/html", webText);
  332.   });
  333.   server.on("/setting", []() {  // store data in flash
  334.     webText = "<!DOCTYPE HTML>\n<html lang='en'>\n<head><title>Setup</title>\n<meta name='viewport' ";
  335.     webText += "content='width=device-width, initial-scale=1.0'>\n<style>\np {\n  font-family: Arial, Helvetica";
  336.     webText += ", sans-serif;\n  font-size: 45px;\n  font-weight: 600;\n  margin: 0;\n  text-align: center;\n}";
  337.     webText += "\n</style>\n</head>\n<body><p>ESP will reboot\n<br>Close this window</p><br>\n</body>\n</html>";
  338.     server.send(200, "\ntext/html", webText);
  339.     delay(500);
  340.     String myssid = server.arg("ssid");  // we want to store this in flash memory
  341.     String mypasw = server.arg("pass");
  342.     if (myssid.length() > 0 && mypasw.length() > 0) {
  343.       flash.begin("login_data", false);  // false = write to "flash/login_data"
  344.       flash.putString("ssid", myssid);
  345.       flash.putString("pasw", mypasw);
  346.       flash.end();
  347.       ESP.restart();
  348.     }
  349.   });
  350. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement