#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h> // Display library for SC-01
#include <time.h> // time
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
// Wi-Fi Credentials
//const char* ssid = "Snackis 2";
//const char* password = "33666999";
// API URL (byt till ditt elområde och dagens datum)
// String apiUrl = "https://www.elprisetjustnu.se/api/v1/prices/2024/09-09_SE4.json";
String apiUrl1;
String apiUrl2;
String apiUrltot;
String responseBody1;
String responseBody2;
String responseBody;
int recTim;
int nuTimme;
int gR = 0, gG = 255, gB = 0; // grön
int mgR = 60, mgG = 80, mgB = 0; // mörkgrön
int dgR = 20, dgG = 20, dgB = 0; // darkmörkgrön
int yR = 255, yG = 210, yB = 6; //gul
int myR = 90, myG = 70, myB = 0; //mörkgul
int dyR = 70, dyG = 50, dyB = 0; //darkmörkgul
int rR = 255, rG = 0, rB = 0; // röd
int mrR = 70, mrG = 15, mrB = 0; // mörkröd
int drR = 60, drG = 10, drB = 0; // darkmörkröd
int oneOrtwopl;
//int recordNo;
// TFT display object
TFT_eSPI tft = TFT_eSPI();
// Uppdateringsintervall i millisekunder (10 minuter = 600000 ms)
//const long updateInterval = 600000;
//const long updateInterval = 3600000;
const long updateInterval = 600000;
unsigned long previousMillis = 0;
// def en struktur för 24h elpriser
typedef struct
{
int dag;
int timme;
float pris;
} record_type;
record_type record[50];
void setup() {
Serial.begin(115200);
// Initialize display
tft.init();
tft.setRotation(1); // Adjust for landscape or portrait as needed
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); // White text on black background
tft.setTextSize(2);
// Connect to Wi-Fi
tft.println("Connecting to WiFi...");
/*WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
tft.print(".");
}*/
// WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
// it is a good practice to make sure your code sets wifi mode how you want it.
// put your setup code here, to run once:
Serial.begin(115200);
//WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wm;
// reset settings - wipe stored credentials for testing
// these are stored by the esp library
// wm.resetSettings();
// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result
bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP"); // anonymous ap
// wm.resetSettings();
// res = wm.autoConnect("AutoConnectAP","password"); // password protected ap
res = wm.autoConnect("Elpriser"); // password protected ap
if(!res) {
Serial.println("Failed to connect");
// ESP.restart();
}
else {
//if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
}
Serial.println("Connected!");
tft.fillScreen(TFT_BLACK);
tft.println("WiFi connected!");
// Initial fetch of electricity prices
fetchElectricityPrices();
}
void fetchElectricityPrices() {
// tillfälliga rader för att testa structen
/*
record[0].dag = 3;
record[0].timme = 16;
record[4] = (record_type) {6, 20};
Serial.printf("Dag: %i, Timme: %i \n", record[0].dag, record[0].timme);
Serial.printf("Dag: %i, Timme: %i \n", record[4].dag, record[4].timme);
*/
if (WiFi.status() == WL_CONNECTED) {
fetchTime:
// Time settings for NTP synchronization
configTime(7200, 0, "pool.ntp.org");
// Wi-Fi connection
// Wait for time sync
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println(getLocalTime(&timeinfo)); // extra kollrad
Serial.println("Failed to obtain time try again");
goto fetchTime;
// return;
}
// Skriv ut dagens datum
Serial.print("Dagens datum: ");
Serial.printf("%04d-%02d-%02d\n", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
// Lägg till 24 timmar för att få morgondagens tid
time_t now = time(nullptr);
time_t tomorrow = now + 86400; // Lägg till 86400 sekunder (24 timmar)
// Konvertera till en struct tm för att få datumdelarna
struct tm* timeinfo_tomorrow = localtime(&tomorrow);
// Skriv ut morgondagens datum
Serial.print("Morgondagens datum: ");
Serial.printf("%04d-%02d-%02d\n", timeinfo_tomorrow->tm_year + 1900, timeinfo_tomorrow->tm_mon + 1, timeinfo_tomorrow->tm_mday);
// Build API URL dynamically
char apiBuffer[100];
snprintf(apiBuffer, sizeof(apiBuffer), "https://www.elprisetjustnu.se/api/v1/prices/%d/%02d-%02d_SE4.json", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
apiUrl1 = String(apiBuffer);
// char apiBuffer2[100];
snprintf(apiBuffer, sizeof(apiBuffer), "https://www.elprisetjustnu.se/api/v1/prices/%d/%02d-%02d_SE4.json", timeinfo_tomorrow->tm_year + 1900, timeinfo_tomorrow->tm_mon + 1, timeinfo_tomorrow->tm_mday);
apiUrl2 = String(apiBuffer);
Serial.println(apiUrl1);
Serial.println(apiUrl2);
String responseBody;
String responseBody2;
HTTPClient http;
http.begin(apiUrl1);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
responseBody1 = http.getString();
// Serial.println(httpResponseCode);
// Serial.println(responseBody1);
HTTPClient http;
http.begin(apiUrl2);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
responseBody2 = http.getString();
// Serial.println(httpResponseCode);
// Serial.println(responseBody2);
Serial.println(responseBody2.charAt(0));
if (responseBody2.charAt(0) == '[') {
responseBody = responseBody1 + responseBody2;
oneOrtwopl = 47;
} else {
responseBody = responseBody1;
oneOrtwopl = 23;
}
// Serial.println(responseBody);
int position = responseBody.indexOf("][");
if (position != -1) {
responseBody.remove(position, 1);
char ord = ',';
responseBody.setCharAt(position, ord);
// responseBody.insert(position, ',');
}
} else {
Serial.println("Kunde inte hämta morgondagens data");
responseBody = responseBody1;
}
Serial.println(responseBody1);
Serial.println(responseBody2);
Serial.println(responseBody);
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, responseBody);
if (error) {
Serial.print(F("Failed to parse JSON: "));
Serial.println(error.f_str());
tft.fillScreen(TFT_BLACK);
tft.println("JSON Parse Error");
return; // Stop execution if JSON fails
}
JsonArray prices = doc.as<JsonArray>();
if (prices.isNull()) {
Serial.println("Prices array is null");
tft.fillScreen(TFT_BLACK);
tft.println("No prices available");
return; // Stop if the array is null
}
tft.fillScreen(TFT_BLACK); // Clear display
tft.setCursor(0, 0);
tft.println("Elpriser:");
int recordNo = 0;
for (JsonObject price : prices) {
const char* time = price["time_start"].as<const char*>();
String timex = price["time_start"];
float priceValue = price["SEK_per_kWh"];
// Kontrollera om data är giltig innan bearbetning
if (time != nullptr && !isnan(priceValue)) {
// fyll på värden i structen
record[recordNo].dag = timex.substring(8, 10).toInt();
record[recordNo].timme = timex.substring(11, 13).toInt();
record[recordNo].pris = (priceValue + 0.0841) * 1.25;
Serial.printf("Dag: %i, Timme: %i, Pris: %.2f SEK/kWh, record no: %i\n", record[recordNo].dag, record[recordNo].timme, record[recordNo].pris, recordNo);
recordNo++;
// Serial.printf("Tid: %s, Pris: %.2f SEK/kWh\n", time, priceValue);
Serial.printf("Dag: %s, Tid: %s, Pris: %.2f SEK/kWh\n", timex.substring(8, 10), timex.substring(11, 13), (priceValue + 0.0841) * 1.25);
// tft.printf("Dag: %s, Tid: %s, Pris: %.2f SEK/kWh\n", timex.substring(8,10),timex.substring(11,13), priceValue);
// delay(100); // Delay to make it easier to read
}
// Ladda nuTimme med nuvarande timme
nuTimme = timeinfo.tm_hour;
// tft.printf("Hej: %s, Tid: %s, Pris: %.2f", timex.substring(8,10),timex.substring(11,13), priceValue);
}
Serial.printf("nu timme: %i\n", nuTimme);
// for (int i = 0; i <= 47 and timeinfo.tm_hour <= record[i].timme; i++) {
for (int i = 0; i <= 47; i++) {
// Serial.printf("Match: %i, recno: %i, innehåll %i\n", timeinfo.tm_hour, i, record[i].timme);
if (nuTimme == record[i].timme) {
recTim = i;
i = 50;
Serial.printf("Matchx: %i, rec: %i\n", timeinfo.tm_hour, recTim);
}
}
// tft.fillScreen(TFT_BLACK); // Clear display
tft.setCursor(5, 2);
// tft.println("Elpriser:");
// stora cirkeln
if (record[recTim].pris <= 0.25) {
tft.fillScreen(tft.color565(dgR, dgG, dgB));
tft.fillCircle(240, 100, 90, tft.color565(gR, gG, gB));
tft.fillCircle(240, 100, 70, tft.color565(mgR, mgG, mgB));
tft.setTextColor(TFT_WHITE, tft.color565(mgR, mgG, mgB));
} else {
tft.fillScreen(tft.color565(dyR, dyG, dyB));
tft.fillCircle(240, 100, 90, tft.color565(yR, yG, yB));
tft.fillCircle(240, 100, 70, tft.color565(myR, myG, myB));
tft.setTextColor(TFT_WHITE, tft.color565(myR, myG, myB));
}
if (record[recTim].pris >= 0.75) {
tft.fillScreen(tft.color565(drR, drG, drB));
tft.fillCircle(240, 100, 90, tft.color565(rR, rG, rB));
tft.fillCircle(240, 100, 70, tft.color565(mrR, mrG, mrB));
tft.setTextColor(TFT_WHITE, tft.color565(mrR, mrG, mrB));
}
// tft.fillCircle(240, 100, 55, TFT_BLACK);
tft.setCursor(225, 70);
tft.setTextSize(3);
tft.printf("%02d", record[recTim].timme);
tft.setCursor(205, 102);
tft.printf("%.2f", record[recTim].pris);
// tft.setTextColor(TFT_WHITE, TFT_BLACK);
// lilla nr 1
if (record[recTim + 1].pris <= 0.25) {
tft.fillCircle(50, 250, 45, tft.color565(gR, gG, gB));
tft.fillCircle(50, 250, 35, tft.color565(mgR, mgG, mgB));
tft.setTextColor(TFT_WHITE, tft.color565(mgR, mgG, mgB));
} else {
tft.fillCircle(50, 250, 45, tft.color565(yR, yG, yB));
tft.fillCircle(50, 250, 35, tft.color565(myR, myG, myB));
tft.setTextColor(TFT_WHITE, tft.color565(myR, myG, myB));
}
if (record[recTim + 1].pris >= 0.75) {
tft.fillCircle(50, 250, 45, tft.color565(rR, rG, rB));
tft.fillCircle(50, 250, 35, tft.color565(mrR, mrG, mrB));
tft.setTextColor(TFT_WHITE, tft.color565(mrR, mrG, mrB));
}
// tft.fillCircle(50, 250, 35, TFT_BLACK);
tft.setCursor(40, 230);
tft.setTextSize(2);
tft.printf("%02d", record[recTim + 1].timme);
tft.setCursor(28, 250);
tft.printf("%.2f", record[recTim + 1].pris);
// lilla nr 2
if (record[recTim + 2].pris <= 0.25) {
tft.fillCircle(145, 250, 45, tft.color565(gR, gG, gB));
tft.fillCircle(145, 250, 35, tft.color565(mgR, mgG, mgB));
tft.setTextColor(TFT_WHITE, tft.color565(mgR, mgG, mgB));
} else {
tft.fillCircle(145, 250, 45, tft.color565(yR, yG, yB));
tft.fillCircle(145, 250, 35, tft.color565(myR, myG, myB));
tft.setTextColor(TFT_WHITE, tft.color565(myR, myG, myB));
}
if (record[recTim + 2].pris >= 0.75) {
tft.fillCircle(145, 250, 45, tft.color565(rR, rG, rB));
tft.fillCircle(145, 250, 35, tft.color565(mrR, mrG, mrB));
tft.setTextColor(TFT_WHITE, tft.color565(mrR, mrG, mrB));
}
// tft.fillCircle(145, 250, 35, TFT_BLACK);
tft.setCursor(135, 230);
tft.setTextSize(2);
tft.printf("%02d", record[recTim + 2].timme);
tft.setCursor(123, 250);
tft.printf("%.2f", record[recTim + 2].pris);
// lilla nr 3
if (record[recTim + 3].pris <= 0.25) {
tft.fillCircle(240, 250, 45, tft.color565(gR, gG, gB));
tft.fillCircle(240, 250, 35, tft.color565(mgR, mgG, mgB));
tft.setTextColor(TFT_WHITE, tft.color565(mgR, mgG, mgB));
} else {
tft.fillCircle(240, 250, 45, tft.color565(yR, yG, yB));
tft.fillCircle(240, 250, 35, tft.color565(myR, myG, myB));
tft.setTextColor(TFT_WHITE, tft.color565(myR, myG, myB));
}
if (record[recTim + 3].pris >= 0.75) {
tft.fillCircle(240, 250, 45, tft.color565(rR, rG, rB));
tft.fillCircle(240, 250, 35, tft.color565(mrR, mrG, mrB));
tft.setTextColor(TFT_WHITE, tft.color565(mrR, mrG, mrB));
}
// tft.fillCircle(240, 250, 35, TFT_BLACK);
tft.setCursor(230, 230);
tft.setTextSize(2);
tft.printf("%02d", record[recTim + 3].timme);
tft.setCursor(218, 250);
tft.printf("%.2f", record[recTim + 3].pris);
// lilla nr 4
if (record[recTim + 4].pris <= 0.25) {
tft.fillCircle(335, 250, 45, tft.color565(gR, gG, gB));
tft.fillCircle(335, 250, 35, tft.color565(mgR, mgG, mgB));
tft.setTextColor(TFT_WHITE, tft.color565(mgR, mgG, mgB));
} else {
tft.fillCircle(335, 250, 45, tft.color565(yR, yG, yB));
tft.fillCircle(335, 250, 35, tft.color565(myR, myG, myB));
tft.setTextColor(TFT_WHITE, tft.color565(myR, myG, myB));
}
if (record[recTim + 4].pris >= 0.75) {
tft.fillCircle(335, 250, 45, tft.color565(rR, rG, rB));
tft.fillCircle(335, 250, 35, tft.color565(mrR, mrG, mrB));
tft.setTextColor(TFT_WHITE, tft.color565(mrR, mrG, mrB));
}
// tft.fillCircle(335, 250, 35, TFT_BLACK);
tft.setCursor(325, 230);
tft.setTextSize(2);
tft.printf("%02d", record[recTim + 4].timme);
tft.setCursor(313, 250);
tft.printf("%.2f", record[recTim + 4].pris);
// lilla nr 5
if (record[recTim + 5].pris <= 0.25) {
tft.fillCircle(430, 250, 45, tft.color565(gR, gG, gB));
tft.fillCircle(430, 250, 35, tft.color565(mgR, mgG, mgB));
tft.setTextColor(TFT_WHITE, tft.color565(mgR, mgG, mgB));
} else {
tft.fillCircle(430, 250, 45, tft.color565(yR, yG, yB));
tft.fillCircle(430, 250, 35, tft.color565(myR, myG, myB));
tft.setTextColor(TFT_WHITE, tft.color565(myR, myG, myB));
}
if (record[recTim + 5].pris >= 0.75) {
tft.fillCircle(430, 250, 45, tft.color565(rR, rG, rB));
tft.fillCircle(430, 250, 35, tft.color565(mrR, mrG, mrB));
tft.setTextColor(TFT_WHITE, tft.color565(mrR, mrG, mrB));
}
// tft.fillCircle(430, 250, 35, TFT_BLACK);
tft.setCursor(420, 230);
tft.setTextSize(2);
// tft.setFreeFont(&FreeSans9pt7b); // För FreeSans storlek 9
//tft.setFreeFont(&FreeSansBold9pt7b); // För FreeSans Bold storlek 9
tft.printf("%02d", record[recTim + 5].timme);
tft.setCursor(408, 250);
tft.printf("%.2f", record[recTim + 5].pris);
// skriv ut kommande priser i en färgad "bar"
// int noOfsegments = oneOrtwopl - recTim - 6;
int noOfsegments = oneOrtwopl - recTim - 5;
float segLen = 470.00 / ((float)noOfsegments - 1.00); // minus 1 för att räkna ut mellanrumsstorleken
int pos = 0;
int step = 0;
int stepDif = 0;
// testa att skriva ut alla records
for (int i = 0; i <= 47; i++) {
Serial.printf("nu jäklar blir det koll - Dag: %i, Timme: %i, Pris: %.2f SEK/kWh, record no: %i\n", record[i].dag, record[i].timme, record[i].pris, i);
}
Serial.printf("****** antal segment: %i segments pixellängd: %.2f recTim+6: %i längd på prislista: %i\n", noOfsegments, segLen, recTim+6, oneOrtwopl);
// for (int i = recTim + 6; i <= oneOrtwopl; i++) {
for (int g = recTim + 6; g <= oneOrtwopl; g++) {
// fyll med utskrift av rektanglar
step = pos * segLen;
// Serial.printf("** step: %i pos: %i\n", step, pos);
// Parametrar: drawRect(x, y, bredd, höjd, färg)
// tft.fillRect(5+(segLen * pos), 305 , segLen, 5, TFT_WHITE); // Rita en vit rektangel
// Parametrar: fillRoundRect(x, y, bredd, höjd, radie, färg)
if (record[g].pris <= 0.25) {
// tft.fillRoundRect(5 + (step), 305, segLen-1, 5, 1, tft.color565(gR, gG, gB));
tft.fillCircle(5 + (step), 305, 3, tft.color565(gR, gG, gB));
} else {
// tft.fillRoundRect(5 + (step), 305, segLen-1, 5, 1, tft.color565(yR, yG, yB));
tft.fillCircle(5 + (step), 305, 3, tft.color565(yR, yG, yB));
}
if (record[g].pris >= 0.75) {
// tft.fillRoundRect(5 + (step), 305, segLen-1, 5, 1, tft.color565(rR, rG, rB));
tft.fillCircle(5 + (step), 305, 3, tft.color565(rR, rG, rB));
}
// printa en svart mellan barsen
// tft.fillRect(5 + (segLen * pos), 305, 1, 5, TFT_BLACK);
Serial.printf("g: %i x: %i stepDif: %i y: %i bredd: %.2f höjd: %i timme: %2d pris: %.2f\n", g, 5 + step, stepDif, 305, segLen, 5, record[g].timme, record[g].pris);
stepDif = step;
pos++;
}
// tft.fillRect(5, 305, 100, 5, TFT_WHITE); // Rita en vit rektangel
} else {
Serial.printf("Error on HTTP request: %d\n", httpResponseCode);
tft.fillScreen(TFT_BLACK);
tft.println("Failed to fetch data.");
}
http.end();
} else {
tft.fillScreen(TFT_BLACK);
tft.println("WiFi Disconnected!");
}
}
void loop() {
// Check if 10 minutes have passed
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= updateInterval) {
previousMillis = currentMillis; // Reset the timer
// Fetch and display new electricity prices
fetchElectricityPrices();
}
}