Projektvorstellung: Spritpreis von deiner Heimtankstelle mit ESP32 auf Display anzeigen

@Tommy56
Tommy, ist dir aufgefallen, dass dein Sketch aus Post #51 sich nicht formatieren lässt ?

Formatieren geht nicht, wenn das Ende des Zertifikats so aussieht:

-----END CERTIFICATE-----
)CERT";

So funktioniert es mit dem Formatieren:

-----END CERTIFICATE-----)CERT";

Nö, ich schreibe im Notepad++, da ist mir das nicht aufgefallen. Aber danke für den Hinweis.

Ich bin gerade dabei in PHP eine Auswertung für 7 Tankstellen (über prices.php) in meiner Umgebung zu bauen und damit eine DB zu beschicken. Mal sehen, wie sich die Preise so ändern. Das wird auch eine nette Spielerei, da PHP natürlich wieder ganz anders auf JSON zugreift.
@dandud100: Da hast Du mit Deinem Beispiel ja was angerichtet :wink:

Gruß Tommy

Tommy, es freut mich wirklich sehr, dass ich dir etwas bieten konnte mit dem du dich so lange, intensiv (und hoffentlich mit Begeisterung) beschäftigen kannst. :wink::grin:

Und es kommt noch dicker. :wink:
Nach dem obigen Beispiel kompiliert er zwar, aber das Zertifikat wird nicht akzeptiert.
Nachdem ich die Änderung rückgängig gemacht habe, immer noch nicht. Ich musste das Zertifikat neu reinkopieren und es läuft wieder.
Habe es dann in ein anderes Register nach deinem Muster eingespielt.
Jetzt läuft es wieder. Auch kann ich den Sketch selbst formatieren. Das Register mit dem Zertifikat nicht. Da es nur das Zertifikat enthält, kein Problem.

Seltsam....

Ich habe es mit 4 Tankstellen in C++ vor.
Mal schauen, wie weit ich komme. :face_with_raised_eyebrow:

Durch die Formatieroptionen verändert...

Dafür gibt es Ausnahmen:

const String url = "https://creativecommons.tankerkoenig.de/json/detail.php?id=TankstellenID&apikey=TankerkoenigApiKey";
// *INDENT-OFF*
const char cert_XSRG_ROOT_X1 [] PROGMEM = R"CERT(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)CERT";
// *INDENT-ON*
X509List cert(cert_XSRG_ROOT_X1);

Nix mit anderem Tab oder Verrenkungen.

Wieso Verrenkungen ?

Ich habe eh geplant, das in das LittleFS zu setzen.

Hallo,

ich werde wohl morgen mal eine Anzeige der Akkuspannung reinbauen, vor allem mal die Stromaufnahme messen. Einen Zweck hat es eigentlich ohnehin nicht. :wink:
Habe gerade so überlegt: wenn ich mir ein SIM-Modul hole eine eine SIM-Karte, dann noch ein GPS-Modul ran. Ins Auto damit und eben alle paar Minuten mit den Koordinaten die Tankstellenliste der Umgebung hole und...

Statistik habe ich nicht vor, die Verläufe sind hier im Umfeld immer ziemlich identisch, die richten sich ja aneinander aus. Ich hatte in FHEM mal das Diagramm für die eine Tankstelle aktiv, mittags ging es für rund eine Stunde etwas runter, abends war der Verlauf auch immer ähnlich an den Tagen.

Gruß aus Berlin
Michael

Die Antwort ist doch ganz einfach:

Das braucht es nicht.
Habe ich da oben geschrieben. Wie.

Genau:

Du hast Dich beschwert.
Dafür kann Tommy nichts.
Und gerne habe ich Dir eine Ausnahmeregelung für STRG-T gezeigt.

Wo beschwert ?
Ich habe Tommy eine Frage gestellt. Mehr nicht.

Aber danke für deine Arbeit.

Danke dafür, das kannte ich noch nicht.

Mein PHP holt bereits viertelstündlich (unrund) die Werte auf dem Server (Cronjob) und schreibt sie in die DB.
Aufpassen: Wenn die Tankstelle closed ist, gibt es keine Preise.

Gruß Tommy

Bitte. Gerne. Ich mach das öfter. Insbesondere bei swicth/case oder ganzen Blöcken mit Zeilen, die sich nicht mehr verändern sollen. Sieht man u.a. auch in der Rennbahn :wink:
Oder auch an ganz anderer Stelle.

Naja, ich mach mal Kicad noch die Tage. Dann wird bald Schluß sein. Meine Idee ist ja noch immer, das auf einen Mega zu adaptieren. Aber....

Hallo Dieter,
darf ich Dir als Anregung meine Lösung vorstellen?

Basis ist der Code aus #15.
Vier Tankstellen, die Abfrage (noch) nicht in einer Funktion, Ausgabe nur auf die serielle Schnittstelle, Intervall 10min.

//
// https://forum.arduino.cc/t/projektvorstellung-spritpreis-von-deiner-heimtankstelle-mit-esp32-auf-display-anzeigen/924802/57
//
/*
 * Ergebnis der Abfrage (17.11.2021, 23:11)
 * 
  23:11:19.635 -> C:\Daten\Projects\Sketchbook\sketches\Tankerkoenig\tankListe\tankListe.ino
  23:11:19.776 -> Connecting
  23:11:20.244 -> .....
  23:11:22.241 -> Connected to WiFi network with IP Address: 192.168.178.85
  23:11:22.287 -> https://creativecommons.tankerkoenig.de/json/prices.php?ids=3c034790-3d2a-4093-8417-032a48cb9f25,e1a15081-24c2-9107-e040-0b0a3dfe563c,51d4b5e1-a095-1aa0-e100-80009459e03a,1a1ec4ba-cc2a-4663-8330-81efc48b9256&apikey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  23:11:23.453 -> HTTP Response code: 200
  23:11:23.453 -> {"ok":true,"license":"CC BY 4.0 -  https:\/\/creativecommons.tankerkoenig.de","data":"MTS-K","prices":{"e1a15081-24c2-9107-e040-0b0a3dfe563c":{"status":"closed"},"1a1ec4ba-cc2a-4663-8330-81efc48b9256":{"status":"open","e5":1.799,"e10":1.739,"diesel":1.609},"51d4b5e1-a095-1aa0-e100-80009459e03a":{"status":"closed"},"3c034790-3d2a-4093-8417-032a48cb9f25":{"status":"closed"}}}
  23:11:23.499 -> Jantzon Holle ist geschlossen.
  23:11:23.499 -> HEM Groß Düngen ist geschlossen.
  23:11:23.499 -> Jet Hi Ochtersum ist geschlossen.
  23:11:23.499 -> ARAL Hi Alfelder Straße
  23:11:23.499 ->         Super E5 : 1.799
  23:11:23.499 ->         Super E10: 1.739
  23:11:23.499 ->         Diesel   : 1.609
 */

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <ArduinoOTA.h>

// Netzwerkdaten
constexpr char ssid[]     { "SSID" };
constexpr char password[] { "PASSWORT" };

// Tankerkoenig ApiKey
String TankerkoenigApiKey { "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" };

// Tankerkönig Tankstellenliste
// TODO: Liste aus nvs oder Filesystem lesen - am besten im Tankerkönig-Format
/*
  {
    "id": "3c034790-3d2a-4093-8417-032a48cb9f25",
    "name": "Tankstelle Holle",
    "brand": "Jantzon Tankstelle",
    "street": "Bindersche Str.",
    "house_number": "2",
    "post_code": 31188,
    "place": "Holle",
    "lat": 52.089633,
    "lng": 10.161023,
    "isOpen": false
  },
  ...
*/
struct Tanke
{
  const char* stationId;
  const char* stationName;
};

constexpr Tanke tankstellenListe[]
{
  {
    "3c034790-3d2a-4093-8417-032a48cb9f25",
    "Jantzon Holle"
  },
  {
    "e1a15081-24c2-9107-e040-0b0a3dfe563c",
    "HEM Groß Düngen"
  },
  {
    "51d4b5e1-a095-1aa0-e100-80009459e03a",
    "Jet Hi Ochtersum"
  },
  {
    "1a1ec4ba-cc2a-4663-8330-81efc48b9256",
    "ARAL Hi Alfelder Straße"
  }
};

// Preisschwelle für Alarm
// TODO: Alarmschwellen einstellbar machen (nvs oder Filesystem).
constexpr float preisalarmDiesel { 1.500 };   // icke
constexpr float preisalarmE5     { 1.700 };   // Frau & Sohn

// RootCA von Tankerkoenig
const char* root_ca = \
                      "-----BEGIN CERTIFICATE-----\n" \
                      "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" \
                      "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \
                      "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" \
                      "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" \
                      "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" \
                      "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" \
                      "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" \
                      "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" \
                      "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" \
                      "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" \
                      "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" \
                      "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" \
                      "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" \
                      "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" \
                      "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" \
                      "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" \
                      "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" \
                      "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" \
                      "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" \
                      "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" \
                      "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" \
                      "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" \
                      "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" \
                      "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" \
                      "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" \
                      "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" \
                      "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" \
                      "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" \
                      "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" \
                      "-----END CERTIFICATE-----\n";

// Abfragetimer, laut Nutzungsbedingungen darf die API alle 5 Minuten abgefragt werden
unsigned long lastTime = 0;
// 10 Minuten (600000)
constexpr unsigned long timerDelay { 600000 };

// Um die ersten 10 Minuten zu überspringen
bool skiptimer = true;

String jsonBuffer;

void setup()
{
  Serial.begin(115200);
  Serial.println(__FILE__);

  WiFi.begin(ssid, password);
  Serial.println(F("Connecting"));
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print('.');
  }
  Serial.println("");
  Serial.print(F("Connected to WiFi network with IP Address: "));
  Serial.println(WiFi.localIP());

  ArduinoOTA.begin();
}


void loop()
{
  ArduinoOTA.handle();

  if ((millis() - lastTime) > timerDelay || skiptimer == true)
  {
    // Send an HTTP GET request
    skiptimer = false;
    lastTime = millis();

    // Check WiFi connection status
    if (WiFi.status() == WL_CONNECTED)
    {
      String serverPath = "https://creativecommons.tankerkoenig.de/json/prices.php?ids=";
      for (const auto& tanke : tankstellenListe)
      {
        serverPath += tanke.stationId;
        serverPath += ',';
      }
      // ersetze das letzte Komma durch ein & (was vorher am Anfang des apikey-Strings stand)
      int lastKomma = serverPath.lastIndexOf(',');
      serverPath[lastKomma] = '&';   
      serverPath += "apikey=" + TankerkoenigApiKey;
      Serial.println(serverPath);

      // TEST: don't call server until request string is correct
      // return;

      jsonBuffer = httpGETRequest(serverPath.c_str());
      Serial.println(jsonBuffer);

      StaticJsonDocument<1024> doc;

      DeserializationError error = deserializeJson(doc, jsonBuffer);

      if (error)
      {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.f_str());
        return;
      }

      // von Tommy
      bool isOk = doc["ok"];
      if (!isOk)
      {
        String msg = doc["message"];
        Serial.println(msg);
        return;
      }

      JsonObject prices = doc["prices"];
      for (const auto& tanke : tankstellenListe)
      {
        Serial.print(tanke.stationName);
        JsonObject station = prices[tanke.stationId];
        if (station["status"] == "closed")
        {
          Serial.println(F(" ist geschlossen."));
          continue;
        }
        if (station["status"] == "no prices")
        {
          Serial.println(F(": keine Preise verfügbar."));
          continue;
        }

        Serial.println();
        float stationE5 = station["e5"];
        float stationE10 = station["e10"];
        float stationDiesel = station["diesel"];
        Serial.print(F("\tSuper E5 : "));
        Serial.println(stationE5, 3);
        Serial.print(F("\tSuper E10: "));
        Serial.println(stationE10, 3);
        Serial.print(F("\tDiesel   : "));
        Serial.println(stationDiesel, 3);

        if (stationDiesel <= preisalarmDiesel)
        {
          Serial.println(F("Mein Auto tanken!"));
        }
        if (stationE5 <= preisalarmE5)
        {
          Serial.println(F("Familienautos tanken!"));
        }
      }
    }
    else
    {
      Serial.println("WiFi Disconnected");
    }
  }
}

String httpGETRequest(const char* serverName)
{
  WiFiClient client;
  HTTPClient http;

  // Your Domain name with URL path or IP address with path
  http.begin(serverName, root_ca);

  // Send HTTP POST request
  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);
  }
  // Free resources
  http.end();

  return payload;
}

Gruß Walter

1 Like

Hallo Walter,

danke Dir. Ich werde es als Anregung mit aufnehmen.
Allerdings verwendest du noch die Version für den ESP32.
Ich setze meine Version auf einem ESP8266 ein und die läuft aktuell mit 2 Tankstellen und einem Array, welches ich nur noch erweitern muss.
ESP8266 weil ich es mit einem ESP-01S aufbauen möchte.
Minimalist halt. :wink: :wink:

Wenn ich damit fertig bin, stell ich es auch hier rein.

Gruß Dieter

Die ganze Key-Abfrage ist übrigens ziemlich für die Füße.
Du kannst zu einem host a die Keyprüfung machen und danach problemlos eine https-URL zu einem Host b abrufen. Das gibt keinen Fehler.

Also eher in der Verantwortung des Entwicklers, intern wird nix geprüft (ESP8266) .
Ich habe das dadurch gemerkt, dass ich fälschlich die SSL-Prüfung für Wikinger-tommy.de gemacht habe, dann aber auf Tankerkönig.de per https zugegriffen habe. Das hat problemlos funktioniert.

Da ist also intern noch ein größerer Bug drin.

Gruß Tommy

Ich habe meine PHP-Abfrage und Grafik soweit fertig.

Hier mal ein Bild.

Gruß Tommy

2 Likes

Tommy,
sieht gut aus.
Mit welcher Zeit fragst du ab ?

Mein Sketch ist auch fast fertig. Ich frage aktuell 6 Stationen ab. Werde den noch ein wenig aufhübschen und dann auch posten.
Eine Anzeige auf Webserver erfolgt später.

Ich frage zu unrunden Zeiten alle 15 Minuten ab (Cronjob) also 6/21/36/51.
Mit dem Aufhübschen bin ich auch noch beschäftigt, da will mjpgraph aber manches Mal nicht so, wie man es der Doku entnehmen kann.
Mich stört die weiße Fläche, da ich meist mit DarkThemes unterwegs bin, das schont die Augen (habe ich anfangs auch nicht glauben wollen, als die Kollegen damit anfingen)

Gruß Tommy

So, ich bin jetzt mit meiner Lösung zufrieden. Daraus kann man shon eine Tankstrategie ableiten. Für die Grafik habe ich die Werte * 10 genommen und von 15 bis 18 in y-Richtung skaliert.
Danke nochmal an den TO für seine Anregung.

Gruß Tommy