Kleine Wetterstation mit JSON und Mondphase

Schon länger ärgert es mich, dass ich aktuelle Wetterdaten und weitere nützliche Informationen für den Tag mir immer mühsam von verschiedenen Seiten oder Geräten holen muss. Dazu zählen das Wetter, Sonnenaufgang und -untergang, Temperatur und die Mondphase. Natürlich dürfen auch das aktuelle Datum und die Uhrzeit nicht fehlen.

Die oben genannten Informationen generiere ich mir von aus drei verschiedenen Quellen, einem NTP-Server, einem Wetterdienst, der die Informationen im JSON-Format liefert und durch simple Mathematik. Letzteres dient für die Berechnung der Mondphase für den aktuellen Tag. Die Hardware soll dabei schlank aber gleichzeitig leistungsstark für weitere Modifikationen sein.

Damit das komplette Projekt funktioniert, bedarf es eines freien Accounts bei openweathermap.org und den API-Key für diesen Account. Wie Sie an die API-Key kommen, wird in der Hilfe von openweathermap.org ausführlich erklärt. Damit keiner mit dem IoT-Gerät Unsinn treibt, wird gleichzeitig auch das sichere https-Protokoll verwendet.

Was das JSON-Protokoll ist und wie die Mondphase berechnet wird, erkläre ich vor der benötigten Hard- und Software.

Was das JSON-Protokoll ist und wie die Mondphase berechnet wird, erkläre ich vor der benötigten Hard- und Software.

 Die Berechnung der Mondphase

Wie in der Einleitung erwähnt, wird der ESP32 die Mondphase nicht über eine API abfragen, sondern diese selbst berechnen. Dazu braucht es simple Mathematik. Damit die Berechnung funktioniert, brauchen wir das genaue Datum einer vergangenen Vollmondnacht. Diese kann man sich unter www.mondverlauf.de für einen genauen Standort ausgeben lassen.

Danach errechnet man das Ergebnis zwischen der vergangenen Zeit bis heute und teilt dieses durch die Zeit, die zwischen zwei Neumondphasen vergeht. Diese Zeitspanne nennt sich auch synodische Periode.

Von diesem Ergebnis verwenden wir nur die Nachkommastellen, um die aktuelle Mondphase zu ermitteln. Dabei sagen die Nachkommastellen folgende Mondphasen aus:

  • Vollmond                             bei 0,0
  • Abnehmender Halbmond    bei 0,25
  • Neumond                            bei 0,5
  • Zunehmender Halbmond    bei 0,75


Ein kleines Beispiel dazu:

  • Aktuell ist der 04.05.2020, 22:00 Uhr
  • Der letzte Vollmond war am 08.04.2020 um 4:34 Uhr
  • Dazwischen liegen knapp 26,72 Tage
  • Diese Tage werden durch 29,53 geteilt (synodische Periode)
  • Als Ergebnis erhalten wir 0,90 (zweite Stelle hinter dem Komma gerundet)
  • Die Zahl vor dem Komma gibt an, wie oft schon eine Mondperiode durchlaufen wurde, in diesem Fall kein Mal
  • Die Nachkommastelle gibt Aussage darüber, dass wir einen zunehmenden Mond haben und es bald Vollmond sein wird

Damit die Berechnung im Sketch funktioniert, definieren wir eine Variable vom Typ time und beschreiben diese in der Funktion setup() mit einer Funktion tmConvert, siehe Code 1.

time_t tmConvert(int iYear, byte byMonth, byte byDay, byte byHour, byte byMinute, byte bySecond)
{
  tmElements_t tmSet;
  tmSet.Year = iYear - 1970;
  tmSet.Month = byMonth;
  tmSet.Day = byDay;
  tmSet.Hour = byHour;
  tmSet.Minute = byMinute;
  tmSet.Second = bySecond;
  return makeTime(tmSet);
}

Code 1: Funktion, um UTC-Zeit letzte Mondphase zu errechnen

Die Berechnung und die Ausgabe erfolgen über zwei separate Funktionen. Die Funktion MoonUpdate übernimmt dabei die Berechnung der Mondphase wie oben beschrieben, siehe Code 2. Der Rückgabewert der Funktion ist eine Variable vom Typ double.

double MoonUpdate()
{
    double dDaySinceLastFullmoon = (now() / 86400 - tmLastFullMoon / 86400) / 29.53;
    int iToHundret = (dDaySinceLastFullmoon - int(dDaySinceLastFullmoon)) * 100;
    return (double) iToHundret / 100; //Needed to get only 2 decimal places
}
Code 2: Funktion, um aktuelle Mondphase zu errechnen


Die Funktion GetMoonPhase übernimmt die Interpretation des double-Werts von Moon-Update und wandelt diesen in einen für den Menschen verständlichen Wert, in diesem Fall als lesbaren Text. Zu Beginn ruft GetMoonPhase die Funktion MoonUpdate auf, um den aktuellen double-Wert zu erhalten, siehe 
Code 3.

String GetMoonPhase()
{
    static double dMoonphase = MoonUpdate();     //Method to update moonphase
    Serial.println("Calculated moon result: " +String(dMoonphase,3));
    if(dMoonphase == 0.0) 
      dMoonphase = 1.00;
      
    if(dMoonphase < double(0.25))
    {
      return "Abnehmende Mond";
    }
    else if (dMoonphase == 0.25)
    {
      return "Abnehmender Halbmond";
    }
    else if(0.25 < dMoonphase && dMoonphase < 0.50)
    {
      return "Abnehmende Sichel";
    }
    else if(dMoonphase == 0.50)
    {
      return "Neumond";
    }
    else if(0.50 < dMoonphase && dMoonphase < 0.75)
    {
      return "Zunehmende Sichel";
    }
    else if(dMoonphase == 0.75)
    {
      return "Zunehmender Halbmond";
    }
    else if(0.75 < dMoonphase && dMoonphase < 1.00)
    {
      return "Zunehmender Mond";
    }
    else //Moonphase == 1
    {
      return "Vollmond";
    }
}

Code 3:Berechnung der Mondphase 

Was ist das JSON-Format

JSON ist die Abkürzung von JavaScript Object Notation und ist ein offenes Standard Dateiformat in einer einfachen lesbaren Textform, ähnlich wie XML. Es dient dem simplen Datenaustausch zwischen Anwendungen und findet unter anderem in der Industrie, bei systemübergreifender Kommunikation, Anwendung.

Beim JSON-Format handelt es sich zwar um ein gültiges JavaScript, aber es gibt Parser für alle verbreiteten Programmiersprachen, wie auch dem Arduino. In unserem Falle nutzen wir den Parser ArduinoJson, der uns die Antwort auf unserer Wetteranfrage in gültiges und leicht zu parsendes JSON umwandelt.

Eine Antwort kann wie folgt aussehen und ist dabei schnell und einfach zu lesen.

{
  "Herausgeber": "Xema",
  "Nummer": "1234-5678-9012-3456",
  "Deckung": 2e+6,
  "Waehrung": "EURO",
  "Inhaber":
  {
    "Name": "Mustermann",
    "Vorname": "Max",
    "maennlich": true,
    "Hobbys": ["Reiten", "Golfen", "Lesen"],
    "Alter": 42,
    "Kinder": [],
    "Partner": null
  }
}

Im Falle der Wetterdaten ist die Vorgehensweise wie folgt:

  1. Anfrage an den Wetterdienst openweathermap.org senden
  2. Antwort-String abwarten
  3. Nicht benötigte Daten, wie z.B. den Header, der Antwort eliminieren
  4. Den erhaltenen String ins DynamicJsonDocument-Format konvertieren
  5. Daten auslesen und ggf. schon in den richtigen Variablentyp umwandeln

Die oben beschriebene Arbeitsreihenfolge wird über die Funktion WeatherUpdate ausgeführt, siehe Code 4. Um mit ArduinoJson nun einen genauen Wert, wie z.B. das Alter von Herrn Mustermann aus unserem fiktiven Beispiel, zu bekommen, muss im Unterknoten „Inhaber“ die Variable „Alter“ ausgelesen werden. Diese Abfrage ist relativ simpel.

jsonDoc["Inhaber"]["Alter"]

Durch das Anfügen von „.as<int>()“ kann die Variable direkt noch in das gewünschte Variablenformat, hier Integer, konvertiert werden. Genau dieselbe Methode wird im Sketch genutzt, um die benötigten Werte direkt in die globalen Variablen zu schreiben, zu finden ab dem Kommentar „Save data to global var“.

void WeatherUpdate()
{
  static bool bUpdateDone;
  if((firstrun || (minute() % 5) == 0) && !bUpdateDone)
  {
    jsonDoc.clear();  //Normally not needed, but sometimes new data will not stored
    String strRequestData = RequestWeather(); //Get JSON as RAW string
    Serial.println("Received data: " + strRequestData);
    //Only do an update, if we got valid data
    if(strRequestData != "")  //Only do an update, if we got valid data
    {
      DeserializationError error = deserializeJson(jsonDoc, strRequestData); //Deserialize string to AJSON-doc
      if (error)
      {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return;
      }
      //Save data to global var
      strMinTemp = RoundTemp(jsonDoc["main"]["temp_min"].as<double>());
      strMaxTemp = RoundTemp(jsonDoc["main"]["temp_max"].as<double>());
      strCurTemp = RoundTemp(jsonDoc["main"]["temp"].as<double>());
      strFeelTemp = RoundTemp(jsonDoc["main"]["feels_like"].as<double>());
      strSunrise  = jsonDoc["sys"]["sunrise"].as<int>();
      strSunset   = jsonDoc["sys"]["sunset"].as<int>();
      static long timeZone = jsonDoc["timezone"];  //Get latest timezone
      //Print to Serial Monitor
      Serial.println("Min Temp: " + strMinTemp);
      Serial.println("Max Temp: " + strMaxTemp);
      Serial.println("Cur Temp: " + strCurTemp);
      Serial.println("Feel Temp: " + strFeelTemp);
      //Check if timezone changed (sommer- / wintertime
      if(timeZone != utcOffsetInSeconds)
      {
        utcOffsetInSeconds = timeZone;
        timeClient.setTimeOffset(utcOffsetInSeconds);
      }
    }
    bUpdateDone = true;
    bUpdateDisplay = true;
    }
  if((minute() % 5) != 0)
   bUpdateDone = false;
}

Code 4: Parsen der JSON-Wetterdaten

Interessant ist dabei die Ähnlichkeit zu XML und die Tatsache, dass Variablen durch „[]“ zu einem weiteren Unterknoten werden können, welcher wiederum Variablen oder Unterknoten enthalten kann. Somit kann man schnell und einfach Dateninhalte hinzufügen und auch auslesen, sofern man im Quellcode die genaue Knotenposition einprogrammiert.

Benötigte Hardware

Die Hardware für dieses Projekt kann komplett bei AZ-Delivery bezogen werden.

Anzahl Bauteil Anmerkung
1 ESP32 DevelpomentBoard
1 0,96“ OLED Display mit I2C 128
1 Jumper Wire Female to Male


Sollte ein anderes Display oder ESP-Modul von Ihnen genutzt werden, so muss das Pinout der Hardware beachtet werden.

Benötigte Software 

Einrichtung des ESP32 NodeMCU

Zwar habe ich unter dem Punkt „Software“ schon drauf hingewiesen, dass die richtige Boardbibliothek eingebunden werden soll, dennoch möchte ich Ihnen in kurzen Schritten erklären, was zu tun ist. Die detaillierte Anleitung finden Sie bei AZ-Delivery bei den kostenlosen eBooks.

Starten Sie die Arduino IDE und öffnen Sie die Voreinstellungen und tragen Sie die URL https://dl.espressif.com/dl/package_esp32_index.json unter zusätzliche Boardverwalter-URLs ein, siehe Abbildung 1 und Abbildung 2.

Abbildung 1: Voreinstellungen in der Arduino IDE öffnen
Abbildung 2: Boardverwaltungs-URL für ESP32 NodeMCU eintragen

Nach einem Neustart der Arduino IDE öffnen Sie unter Werkzeuge -> Board die Boardverwaltung, siehe Abbildung 3, und geben Sie bei der Suche ESP32 ein, siehe Abbildung 4.

Abbildung 3: Boardverwaltung in der Arduino IDE öffnen

Abbildung 4: Boardbibliothek ESP32 installieren

Wählen Sie die aktuellste Version der Boardbibliothek es32 von Espressif Systems aus, hier 1.0.4, und installieren Sie diese. Nach einem weiteren Neustart sollten die neuen Boards Im Menü Werkzeuge -> Board erscheinen. Hier suchen Sie nach dem Eintrag ESP32 Dev Module und wählen diesen aus, siehe Abbildung 5. Die Standardeinstellungen für dieses Board können Sie so übernehmen.

Abbildung 5:ESP32 Dev Module auswählen

Der Aufbau

Der Aufbau der Schaltung ist mit vier Anschlüssen relativ einfach gehalten. Wir verbinden dabei das Display via I2C und stellen die Stromversorgung mit dem NodeMCU her, siehe Abbildung 6 und Tabelle 2. Gerade für den Testaufbau ist ein Breadboard für die schnelle Verbindung sinnvoll, aber kein Muss.

Abbildung 6: Schematischer Aufbau

 

NodeMCU – Pin OLED-Pin
3.3 V VCC
GND GND
SCL G22
SDA G21

Die Pinbelegung

Bei dem ESP32 DevelopmentBoard ist der GND-Pin neben dem 5V-Pin kein regulärer Ground-Pin, sondern der CMD-Pin. Dieser ist daher nicht zu benutzen!

Die Software 

Ist alles aufgebaut, muss die SSID und das Passwort vom WLAN sowie der API-Key, die Location für openweathermap.org und die letzte Vollmondphase angepasst werden. An den entsprechenden Stellen ist dies mit dem Kommentar TODO markiert. Anschließend kann der Code auf den NodeMCU hochgeladen werden.

// Weatherstation with JSON and moonphase calculation 
// Autor:   Joern Weise
// License: GNU GPl 3.0
// Created: 14. April 2020
// Update:  14. April 2020
//-----------------------------------------------------

#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <ArduinoJson.h>  
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <WiFiClientSecure.h>
#include <WiFi.h>

//Variables for display
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

//Login data for WiFi
const char *ssid     = "WIFI_SSID";   //TODO
const char *password = "WIFI-PASS";   //TODO

//Needed variables for openweathermap.org
const String apiKey = "ADD-YOUR_API";         //TODO
const String location = "ADD-YOUR-LOCATION";  //TODO like "Wiesbaden,de"
const char *clientAdress = "api.openweathermap.org";
int strMinTemp, strMaxTemp, strCurTemp, strFeelTemp, strSunrise, strSunset;
DynamicJsonDocument jsonDoc(2000);

//Variables to get and set time
int utcOffsetInSeconds = 7200;
String daysOfTheWeek[7] = {"So","Mo", "Di", "Mi", "Do", "Fr", "Sa"};
time_t tmLastFullMoon;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", utcOffsetInSeconds);

bool firstrun = true;
bool bUpdateDisplay = true;

//Setup to init the NodeMCU
void setup() {
  Serial.begin(115200);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) // Address 0x3C for OLED-Display
  { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  WiFi.begin(ssid, password);
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }
  tmLastFullMoon = tmConvert(YEAR,MONTH,DAY,HOUR,MINUTE,SECOND); //TODO https://www.mondverlauf.de
  Serial.println("");
  Serial.println("Last moon: " + String(tmLastFullMoon));
  Serial.println("Connected to Wifi with IP: " + WiFi.localIP().toString());
  timeClient.begin();
}

void loop() {

  WeatherUpdate();  //Method to update weather
  TimeUpdate();     //Method to update time
  DisplayUpdate();  //Method to update Display
  if(firstrun)
    firstrun = false;
}

//Method to update time and overwrite 
void TimeUpdate()
{
  static bool bUpdateDone;
  if((firstrun || (minute() % 2) == 0) && !bUpdateDone)
  {
    bool bUpdate = timeClient.update();
    setTime(timeClient.getEpochTime());
    Serial.print("Update time: ");
    if(bUpdate){
      Serial.println("Success");
    }
    else{
     Serial.println("Failed");
    }
    bUpdateDone = true;
  }
  if((minute() % 2) != 0)
    bUpdateDone = false;
}

//Method to update weather forecast
void WeatherUpdate()
{
  static bool bUpdateDone;
  if((firstrun || (minute() % 5) == 0) && !bUpdateDone)
  {
    jsonDoc.clear();  //Normally not needed, but sometimes new data will not stored
    String strRequestData = RequestWeather(); //Get JSON as RAW string
    Serial.println("Received data: " + strRequestData);
    //Only do an update, if we got valid data
    if(strRequestData != "")  //Only do an update, if we got valid data
    {
      DeserializationError error = deserializeJson(jsonDoc, strRequestData); //Deserialize string to AJSON-doc
      if (error)
      {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return;
      }
      //Save data to global var
      strMinTemp = RoundTemp(jsonDoc["main"]["temp_min"].as<double>());
      strMaxTemp = RoundTemp(jsonDoc["main"]["temp_max"].as<double>());
      strCurTemp = RoundTemp(jsonDoc["main"]["temp"].as<double>());
      strFeelTemp = RoundTemp(jsonDoc["main"]["feels_like"].as<double>());
      strSunrise  = jsonDoc["sys"]["sunrise"].as<int>();
      strSunset   = jsonDoc["sys"]["sunset"].as<int>();
      static long timeZone = jsonDoc["timezone"];  //Get latest timezone
      //Print to Serial Monitor
      Serial.println("Min Temp: " + strMinTemp);
      Serial.println("Max Temp: " + strMaxTemp);
      Serial.println("Cur Temp: " + strCurTemp);
      Serial.println("Feel Temp: " + strFeelTemp);
      //Check if timezone changed (sommer- / wintertime
      if(timeZone != utcOffsetInSeconds)
      {
        utcOffsetInSeconds = timeZone;
        timeClient.setTimeOffset(utcOffsetInSeconds);
      }
    }
    bUpdateDone = true;
    bUpdateDisplay = true;
    }
  if((minute() % 5) != 0)
   bUpdateDone = false;
}

//Method for the API-Request to openweathermap.org
String RequestWeather()
{
  WiFiClientSecure client;
  if(!client.connect(clientAdress,443)){  //Changed to https -> Port 443
    Serial.println("Failed to connect");
    return "";
  }
  /*
   * path as followed:
   * /data/2.5/weather? <- static url-path
   * q="location"       <- given location to get weatherforecast
   * &lang=de           <- german description for weather
   * &units=metric      <- metric value in Celcius and hPa
   * appid="apiKey"     <- API-Key from user-account
   */
  String path = "/data/2.5/weather?q=" + location + "&lang=de&units=metric&appid=" + apiKey;

  //Send request to openweathermap.org
  client.print(
    "GET " + path + " HTTP/1.1\r\n" + 
    "Host: " + clientAdress + "\r\n" + 
    "Connection: close\r\n" + 
    "Pragma: no-cache\r\n" + 
    "Cache-Control: no-cache\r\n" + 
    "User-Agent: ESP32\r\n" + 
    "Accept: text/html,application/json\r\n\r\n");

  //Wait for the answer, max 2 sec.
  uint64_t startMillis = millis();
  while (client.available() == 0) {
    if (millis() - startMillis > 2000) {
      Serial.println("Client timeout");
      client.stop();
      return "";
    }
  }

  //If there is an answer, parse answer from openweathermap.org
  String resHeader = "", resBody = "";
  bool receivingHeader = true;
  while(client.available()) {
    String line = client.readStringUntil('\r');
    if (line.length() == 1 && resBody.length() == 0) {
      receivingHeader = false;
      continue;
    }
    if (receivingHeader) {
      resHeader += line;
    }
    else {
      resBody += line;
    }
  }
  
  client.stop(); //Need to stop, otherwise NodeMCU will crash after a while
  return resBody;
}

int RoundTemp(double dTemp)
{
  return int(dTemp + 0.5);
}

//Method to update display content
void DisplayUpdate()
{
  static int iLastMinute;
  if(iLastMinute != minute() || firstrun || bUpdateDisplay){
    display.clearDisplay();
    display.setTextSize(1);               // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);  // Draw white text
    display.setCursor(0,0);               // Start at top-left corner
    display.println(String(daysOfTheWeek[weekday()-1]) + " " + GetDigits(day()) +
                    String(".") + GetDigits(month()) + String(".") + year() + 
                    "  " + GetDigits(hour()) + String(":") + GetDigits(minute()));
    display.println(String("Minimum  ") + strMinTemp + String(" C"));
    display.println(String("Maximum  ") + strMaxTemp + String(" C"));
    display.println(String("Aktuell   ") + strCurTemp + String(" C"));  //"Aktuell" german for current
    display.println(String("Gefuehlt  ") + strFeelTemp + String(" C")); //"Gefuehlt" german for feels like
    display.cp437(true);         // Use full 256 char 'Code Page 437' font
    display.write(int16_t(30));
    display.println(" Sonne  " + GetTimeAsString(strSunrise, utcOffsetInSeconds));
    display.write(int16_t(31));
    display.println(" Sonne  " + GetTimeAsString(strSunset, utcOffsetInSeconds));
    display.println(GetMoonPhase());
    display.display();
    //Print all in serial monitor
    Serial.println("---------------------");
    Serial.println("EpochTime: " + String(timeClient.getEpochTime()));
    Serial.println(String(daysOfTheWeek[weekday()-1]) + String(". ") +
                  GetDigits(day()) + String(".") + GetDigits(month()) + 
                  String(".") + year());
    Serial.println(GetDigits(hour()) + String(":") + GetDigits(minute()));
    Serial.println(String("Min: ") + strMinTemp);
    Serial.println(String("Max: ") + strMaxTemp);
    Serial.println(String("Current: ") + strCurTemp);
    Serial.println(String("Feels like: ") + strFeelTemp);
    Serial.println(String("Sunrise: ") + GetTimeAsString(strSunrise, utcOffsetInSeconds));
    Serial.println(String("Sunset:  ") + GetTimeAsString(strSunset, utcOffsetInSeconds));
    Serial.println(String("Moonphase: ") + GetMoonPhase());
    iLastMinute = minute();
    bUpdateDisplay = false;
  }
}

//Method to write given integer to String
//If the value is less than 10, a "0" is placed in front
String GetDigits(int iValue)
{
  String rValue = "";
  if(iValue < 10)
    rValue += "0";
  rValue += iValue;
  return rValue;
}

time_t tmConvert(int iYear, byte byMonth, byte byDay, byte byHour, byte byMinute, byte bySecond)
{
  tmElements_t tmSet;
  tmSet.Year = iYear - 1970;
  tmSet.Month = byMonth;
  tmSet.Day = byDay;
  tmSet.Hour = byHour;
  tmSet.Minute = byMinute;
  tmSet.Second = bySecond;
  return makeTime(tmSet);
}

String GetTimeAsString(int iValue,  int iOffset)
{
  if(iValue > 0){
    iValue += iOffset;
    return GetDigits(hour(iValue))+":"+GetDigits(minute(iValue));
  }
  return "";
}

double MoonUpdate()
{
    double dDaySinceLastFullmoon = (now() / 86400 - tmLastFullMoon / 86400) / 29.53;
    int iToHundret = (dDaySinceLastFullmoon - int(dDaySinceLastFullmoon)) * 100;
    return (double) iToHundret / 100; //Needed to get only 2 decimal places
}

String GetMoonPhase()
{
    static double dMoonphase = MoonUpdate();     //Method to update moonphase
    Serial.println("Calculated moon result: " +String(dMoonphase,3));
    if(dMoonphase == 0.0) 
      dMoonphase = 1.00;
      
    if(dMoonphase < double(0.25))
    {
      return "Abnehmender Mond";
    }
    else if (dMoonphase == 0.25)
    {
      return "Abnehmender Halbmond";
    }
    else if(0.25 < dMoonphase && dMoonphase < 0.50)
    {
      return "Abnehmende Sichel";
    }
    else if(dMoonphase == 0.50)
    {
      return "Neumond";
    }
    else if(0.50 < dMoonphase && dMoonphase < 0.75)
    {
      return "Zunehmende Sichel";
    }
    else if(dMoonphase == 0.75)
    {
      return "Zunehmender Halbmond";
    }
    else if(0.75 < dMoonphase && dMoonphase < 1.00)
    {
      return "Zunehmender Mond";
    }
    else //Moonphase == 1
    {
      return "Vollmond";
    }
}

Info zum Code 

Die Funktion loop() führt lediglich die Methoden zum Updaten der Wetterdaten, Zeit und Displayanzeige aus. Beim Update der Displayanzeige wird gleichzeitig auch die aktuelle Mondphase berechnet.

Ist alles hochgeladen und sind die individuellen Änderungen im Code gemacht zeigt das Display die gewünschten Informationen an.

Ich wünsche Ihnen viel Spaß beim Nachbau.

Jörn Weise

Dieses und weitere Projekte finden sich auf GitHub unter: https://github.com/M3taKn1ght/Blog-Repo

Esp-32Progetti per principianti

39 Kommentare

Peter Schildt

Peter Schildt

Nachtrag zu Kommentar vom März 03, 2021 at 08:07am
Geändert: – funktioniert…
String Weather::RequestWeather()
{
// WiFiClientSecure client; // Changed to https → Port 443?
WiFiClient client; // Changed to https → Port 80!
if(!client.connect(clientAdress,80))
{
Serial.println(“Failed to connect”);
return "";
}…
Vermute Library wurde geändert.
der Peter

Peter Schildt

Peter Schildt

Gutes Programm, Respekt.
Erste Installation auf ESP 32-WROOM – ohne Probleme,
spätere Versuche mit gleicher und verschiedenen anderen MCU s – kein Kontakt zu api.openweathermap.org client . 2. apiKey bringt auch keine Lösung.
Fehler: mit Method RequestWeather() => Failed to connect //Changed to https > Port 443
Port 443 Firewall Freigabe ist ok. Seltsam ist, das erste Installation immer problemlos läuft.
Arduino 1.8.13 kompilieren und hochladen – kein Fehler. WIFI auch ok.
Hilfe! der Peter ;(

Thomas Oehmke

Thomas Oehmke

Hallo, Herr Weise,
kann man das Programm auch auf ein Heltec-LoRa-Board
anpassen, und wie müsste ich da vorgehen ? Mit einem
ESP32-MCU und abgesetztem 0,96-Display hat es gut geklappt.

Viele Grüße aus Berlin
Th.Oehmke

Jörn Weise

Jörn Weise

Guten Abend Herr Engel,
Ich nutze bei meinen Projekten immer die neusten Bibliotheken und führe in regelmäßigen Abständen Updates als Bibliotheken über die Verwaltung durch.
Es liegt natürlich auch ein Stück weit an Ihnen, Ihre Bibliotheken auf dem aktuellsten Stand zu halten. In den meisten Beiträgen hier, ist keine spezielle Version einer libary genutzt worden, also können Sie auch davon ausgehen, dass sie Beispiele laufen. Meist werden den libraries nur neue Funktionen oder Hardwareanbindungen hinzugefügt, was sie eigentliche Funktion in unseren Beiträgen nicht beeinflusst.
Wie oben schon beschrieben, sind Sie auch als Endnutzer für die Aktualität ihrer libraries verantwortlich, da wir als Blogger nicht wissen, wie ihr Softwarestand aussieht.
Gruß
Weise

Ulrich Engel

Ulrich Engel

Sehr geehrter Herr Weise,
nun funktioniert es.
Ich habe angefangen von der Boardverwaltung ESP32 Installation und Boardauswahl bis zu den librarys alles neu installiert und merkwürdigerweise hat er mir auch zu einigen Libs weitere Libs angeboten, die mit #include t wurden. Vielleicht habe ich damit eine aktuellere passende Version gefunden. Egal. Meistens liegt es an den Libs. Deshalb wäre eigentlich eine Angabe der Quellen und Versionen immer hilfreich. Es ist oft nicht die neueste Lib, sondern eine spezielle auch mal von einem anderen Programmierer.

Vielen Dank für Ihre Geduld und bleiben Sie gesund
Ulrich Engel aus Berlin

Ulrich Engel

Ulrich Engel

Ich habe ja schon einige Projekte umgesetzt. Auch mit SSD1306 OLED. Dort habe ich meistens die SSD1306.h verwendet:

#include “SSD1306.h”
Adafruit_BME280 bme; // I2C
SSD1306 display(0×3c, 0, 2); // Initialize the OLED display using Wire library

Vielleicht werde ich das hier auch so machen.
Ich habe oft schon erlebt, dass es an der Version einer Lib liegt. Ich habe hier die Versionen:
Arduino IDE 1.8.7
Adafruit_SSD1306 2.4.0
Wire PaulStoffregen /Wire Github

Jörn Weise

Jörn Weise

Hallo Ulrich Engel,
das ist ein ziemlich hartnäckiger Fall, den Sie da beschreiben. Ggf. müssen sie tatsächlich in Ihrem Dokumenten-Ordner Arduino\libraries in libraries_org umbenennen und danach noch einmal alles komplett nach Tutorial installieren. Ich habe mal alles auf meinem Rechner neu gemacht und ihren Fehlerfall nicht nachvollziehen können.
Gruß
jörn Weise

Ulrich Engel

Ulrich Engel

Hallo Jörn,
ich habe nun aus meinen librarys noch einmal die Adafruit_SSD1306 und die Wire Lib rausgeworfen und die neuesten Versionen installiert. Bei der Adafruit_SSD1306 habe ich aber keine (Zitat)
“Abfrage ob alle abhängigen Pakete für die Library “Adafruit SSD1306” (Zitatende) erhalten. Die Installation über die Bibliotheksverwaltung lief glatt durch und die Lib war in meinem library-Verzeichnis.

Trotzdem erhalte ich die Fehlermeldung wieder:
“Wetter_mit_Mondphasen:22:56: error: ‘Wire’ was not declared in this scope
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

Was mich wundert, und ich habe schon andere Profekte mit OLED SSD1306 0,96" gemacht ist, wie hier diese Zuweisung erfolgt.
In den anderen Projekten z.B. Standard “Hello” mit ESP8266 sieht es so aus:

/* Hello World OLED Test */
#include
#include “SSD1306.h”
SSD1306 display(0×3c, 5, 4); // Initialise the OLED display using Wire library
……….

Hier gebe ich die Adresse des Oled (I2C-Scanner) und den SDA und SCL Pin an.
Muss ich vielleicht diese Lib einbinden und dann Pin 21 und 22 angeben? Die Adresse ist gleich.

Nun bin ich mit meinem Anfängerwissen am Ende. Will aber dieses Projekt unbedingt fixen.
Viele Grüße Ulli

Jörn Weise

Jörn Weise

Hallo Ulrich Engel,
so wie ich das sehe, haben Sie bei der Abfrage ob alle abhängigen Pakete für die Library “Adafruit SSD1306” nur die angegebene Library installieren lassen. Diese ist aber abhängig von anderen Librarys und müssen somit nachinstalliert werden. Besser ist der Weg, dass Sie in Ihrem Dokumenten-Ordner nach dem Ordner Arduino\libraries\Adafruit_SSD1306 suchen und diesen noch einmal löschen. Starten Sie danach die Arduino IDE neu und installieren Sie noch einmal die Library “Adafruit_SSD1306”. Bei der Frage, ob auch die weiteren benötigten Librarys installiert werden sollen, bitte dies bestätigen und mit downloaden lassen.
Danach sollte das Projekt so klappen.
Gruß
Jörn Weise

Ulrich Engel

Ulrich Engel

Ich habe nun die Librarys aktualisiert. Es bleibt beim Kompilieren die Fehlermeldung zur Zeile:
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
Fehlermeldung:
Arduino\libraries\Adafruit_SSD1306-master/Adafruit_SSD1306.h:129:42: error: ‘TwoWire’ has not been declared

Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
Jörn Weise

Jörn Weise

Hallo Ulrich Engel,
ich habe mir das gerade mal schnell angesehen und es kann nur daran liegen, dass die Bibliothek ArduinoJson nicht vorhanden ist oder falsch inkludiert wurde. Bitte prüfen Sie noch einmal, ob die aktuelle Version über die Bibliotheksverwaltung installiert wurde.
Ggf. müssen Sie die Arduino IDE noch einmal neustarten, wenn Sie zu viele Bibliotheken auf einmal herunterladen. Da kommt es ab und zu zu einem internen Fehler der Arduino IDE.
Gruß
Jörn Weise

Ulrich Engel

Ulrich Engel

Hallo,
ich starte gerade mit diesem Tutorial.
Leider bekomme ich beim Kompilieren folgenden Fehler:
‘DynamicJsonDocument’ does not name a type

Eigentlich habe ich alle Librarys gespeichert.
Woran könnte es liegen?
Viele Grüße Ulli

Dieter Möller

Dieter Möller

Hallo, so ich habe die Sache mit description hingekriegt: despcrition und icon sind Strings, die id ist ein int. Ich hatte einen dummen Anfängerfehler gemacht: in .as() string klein geschrieben. Es muss so heißen: wetter = jsonDoc[“weather”]0[“description”].as();
strid = jsonDoc[“weather”]0[“id”];
icon = jsonDoc[“weather”]0[“icon”].as();
Die Variabeln wetter, strid und icon müssen natürlich voher global defefeniert sein.
Gruss Dieter

Jörn Weise

Jörn Weise

An Dieter Möller:
Ich habe mir mal angesehen, was sie versuchen zu konvertieren und dazu schon ein bisschen was gefunden
strWeather = jsonDoc[“main”][“description”].as(); <- Diesen Pfad gibt es nicht und nur ein as() funktioniert auch nicht
strDescription = jsonDoc[“main”][“description”]; <- Diesen Pfad gibt es, theoretisch sollte der string so übernommen werden, ansonsten as()
strid = jsonDoc[“weather”][“id”].as(); <- Diesen Pfad gibt es, aber bei dem hier fehlt die korrekte Konvertierung as() oder das as() weglassen
strIcon = jsonDoc[“main”][“icon”]; <- siehe oben
Generell kann ich mit den wenigen Informationen nicht genau sagen, wo der Fehler liegt. Bitte fügen Sie doch dafür bei https://pastebin.com/ mal den Quellcode ein (vorher bitte alle Sicherheitsrelevaten Daten löschen), damit ich besser sehen kann, wo der Fehler liegt.

Dieter Möller

Dieter Möller

nochmal Hallo,
leider ist beim Kopieren was falsch gelaufen. Die Zeilen, die nicht funk­ti­o­nie­ren, sehen so aus:
strWeather = jsonDoc[“main”][“description”];
strid = jsonDoc[“weather”][“id”];
strIcon = jsonDoc[“weather”][“icon”];
Gruss Dieter

Dieter Möller

Dieter Möller

Hallo, nach einem anderen Project wende ich mich wieder diesem Wetter- Project zu. Viel weiter bin ich nicht gekommen. Die nachfolgenden Zeilen funktionieren nicht. ich habs mit "weather und “main” und .as() odwer .as versucht, aber der Monitor zeigt immer “0” an. Wär schön, wenn mir wieder geholfen wird.
Gruss
strWeather = jsonDoc[“main”][“description”].as();
strDescription = jsonDoc[“main”][“description”];
strid = jsonDoc[“weather”][“id”].as();
strIcon = jsonDoc[“main”][“icon”];

Joern Weise

Joern Weise

An Dieter Möller:
Vom Prinzip müssen Sie nur die Funktion WeatherUpdate() modifizieren.
Hier sehen Sie ja schon, wie Sie einzelne Knoten vom JSON-Format auslesen können. Laut API-Dokumentation befindet sich die “description” im Elternknoten “main”, daher sähe der Datenaufruf wie folgt aus:
strDescription = jsonDoc[“main”][“description”];
Wichtig hierbei ist noch, dass Sie die Variable strDescription vorher global definieren, suchen Sie dazu z.B. nach strMinTemp und fügen Sie die neue Variable hinzu.
Danach können Sie diese auf dem seriellen Monitor oder mit Anpassung der Displayanzeige entsprechend lesbar machen.
Bedenke Sie aber bitte, dass die Description länger als die maximale Anzahl an Zeichen fürs Display sein kann.

Dieter Möller

Dieter Möller

Ein sehr schönes Projekt. Das habe ich sofort nachgebastelt. Funktioniert auch. Aber ich möchte gern noch die Openweatermap “description” zufügen, es gelingt mir aber nicht!? Kann mir jemand helfen?
Gruss

Jörn Weise

Jörn Weise

An Gerhard: So ganz kann ich die Änderungen nicht nachvollziehen, Sie können mir diese aber gerne an boardriderxp(at) gmx.net schicken. Im Grunde eliminieren Sie mit einer Codezeile die https-Verbindung (Port 443), da Sie wieder den Port 80 nutzen (http).
Ich kann mir aber gerne Ihre Modifikationen noch einmal genauer ansehen und dann mehr dazu sagen.
Gruß
Jörn Weise

Gerhard

Gerhard

Liebe Freunde, mit der Änderung auf ESP8266 vom 26. Juni 2020 und der Anweisung:
Wire.begin(2, 0); // I2C pins (SDA = GPIO2, SCL = GPIO0)
im setup vor dem öffenen der OLED’s läuft das Ganze auch einwandfrei auf eniem ESP-01
Viel Spass Gerhard

Gerhard

Gerhard

Sehr geehrter Herr Joern Weise!
Ich hab es nun selbst geschafft Ihr wirklich gutes Programm
auf ESP8266 zu portieren, es läuft jetzt und ich es auf verschiedenen
ESP8266 12E mit Modulen versehenen Board’s getestet.
Für alle die es auch erprobrn wollen hier die Änderungen:
Zeile 15 entfernen
16 auf #include //ändern
26 const char* host = “api.openweathermap.org”; //und
27 const char *clientAdress = “api.openweathermap.org”; //einfügen
148 – 152 entfernen, dafür vor
164 if (client.connect(host, 80))
{ // einfügen und nach "Accept….

} else { Serial.println(“Failed to connect”); return ""; } /einfügen Ja das sind alle Änderungendie ich gemacht habe. Noch ein Tipp an alle die ein grösseres Display einbauen, man kann durch z.b. mit strPressure = RoundTemp(jsonDoc[“main”][“pressure”].as()); strHumidity = RoundTemp(jsonDoc[“main”][“humidity”].as()); oder auch andere Parameter mittels dem JASON ganz leicht auch mehrere Parameter holen, muß aber vorher die entsprechenden Strings deffinieren. Enstspechende Information was es alles gibt, gibts auf openweathermap.org Viel Spass Gerhard
Fred Krause

Fred Krause

Vielen Dank Herr Weise.
Ihr Projekt funktionierte bei mir auf Anhieb wie erwartet. Gute Arbeit!
Das Wichtigste ist, man kann viel daraus lernen, wenn man versucht es für die eigenen Wünsche oder andere Displays anzupassen. Probleme macht mir gerade noch das 1.3 Zoll I2C OLED Display (auch von AZ-Del.), weil ich keine passende Bibliothek dafür finde (Controller DS1106). Die u8g2 Bibliothek, die im Quick-Start-Guide beschrieben ist, finde ich nicht. Hat jemand eine Lösung?

Gilbert

Gilbert

Hallo,
Funktioniert sehr gut!
Nur manchmal beim Hochladen den Message:
“A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header”
Dann einfach “neu starten”: einige Sekunden auf den Taster “BOOT” zu Beginn des Uploads.
(Die fünf “TODO” nicht vergessen !)
Mit größerem Display könnte man Luftdruck, Wind und andere Daten hinzuzufügen.
Hier unten wass man im Serial Display lesen kann.

EpochTime: 1592408160
Me. le 17/06/2020
Heure : 15:36
Min: 17°C
Max: 18°C
Actuel : 18°C
Ressenti : 18°C
Lever Soleil : 05:42
Coucher Soleil : 21:35
Calcul de la phase de la lune : 0.400
Phase de la Lune : Dernier croissant

Gerhard

Gerhard

Sehr geehrter Herr Joern Weise!
Ein Wirklich schönes Projekt, dass auch auf dem ESP32 einwandfrei läuft.
Ich versuche es nun auf ein ESP-12 (NodeMCU) zu portieren.
Leider gelingt mir das nicht, die Verbindung zum Timeserver geht einwandfrei, zur Wetter.org aber leider nicht. Geändert habe ich nur die WiFi.h auf WiFi8266.h.
Vielleicht haben Sie eine Tipp für mich wie ich das zum laufen bekomme.
Besten Dank für Ihre Antwort im Voraus.
Gerhard

Joern Weise

Joern Weise

@Juergen Babenhauserheide: Damit belegen Sie aber nur unnötig Speicher für eine globale Variable und ist so auch nicht gedacht gewesen. In dem Artikel wird ja beschrieben, dass Sie die letzte Vollmondphase über die angegebene Website ermitteln sollen. Initial werden Sie dadurch immer eine falsche Prognose bekommen.

@Randy P: Die Fehlermeldung sieht danach aus, dass Sie vergessen haben AJSON runterzuladen, wie es am Anfang vom Artikel beschrieben ist. Dadurch versucht die IDE einen Kopierkonstruktor für ein Array von char zu nutzen, obwohl er einen anderen Datentyp dafür nutzen sollte. Bitte die fehlende Bibliothek nachladen.

Randy P

Randy P

bekomme folgende Fehlermeldung
no match for ‘operator[]’ (operand types are ‘ArduinoJson670_0_0::DynamicJsonDocument’ and ‘const char 5’)
bitte um Hilfe

Joern Weise

Joern Weise

@Jochen Pils
Das sind auch erst mal nur Platzhalter. Sie müssen, wie in der Anleitung beschrieben, an dieser Stelle ein Datum für die letzte Vollmondphase angeben. Steht aber genauso auch noch einmal als Kommentar im Quellcode. Andernfalls klappt es natürlich nicht, die aktuelle mondphase zu berechnen.

Juergen Babenhauserheide

Juergen Babenhauserheide

Hallo,
mit dem Original-Code erhalte ich auch die Fehlermeldung: “‘YEAR’ was not declared in this scope”.
Mit dem folgenden Zusatz am Anfang von void setup():
int YEAR;
byte MONTH, DAY, HOUR, MINUTE, SECOND;
funktioniert der Programm-Code einwandfrei.

MfG
Juergen B.

Jochen Pils

Jochen Pils

Schöne Projektidee!
Zeil 56 macht mir Probleme, so dass der Sketch nicht läuft, da bekomme ich Fehlermeldungen:

Wetterstation_MOndphase:56:29: error: ‘YEAR’ was not declared in this scope
Wetterstation_MOndphase:56:34: error: ‘MONTH’ was not declared in this scope
Wetterstation_MOndphase:56:40: error: ‘DAY’ was not declared in this scope
Wetterstation_MOndphase:56:44: error: ‘HOUR’ was not declared in this scope
Wetterstation_MOndphase:56:49: error: ‘MINUTE’ was not declared in this scope
Wetterstation_MOndphase:56:56: error: ‘SECOND’ was not declared in this scope

Kann jemand helfen?
Danke, Jochen

Jürgen Reimann

Jürgen Reimann

Hallo, hab es gleich ausprobiert und läuft problemlos. Danke für die tolle Vorlage

Da ich als Zugang zum Internet auf meiner FritzBox das Gastnetz benutze hatte ich Probleme mit dem NTP Connect, das ist auf dem Gastnetz nämlich gesperrt. Das lässt sich aber leicht lösen, denn viele Internet Access Points haben einen eigenen NTP Server, so auch die FritzBox. Man trägt man einfach statt “europe.pool.ntp.org” die Adresse “fritz.box” ein, vorher unter Benutzereinstellungen noch NTP aktivieren.

Joern Weise

Joern Weise

@Thomas Bierschwale
Das wundert mich dann aber sehr, weil ich in meiner Anleitung genau das gleiche Display verwenden. Gerade wenn nur “Schnee” angezeigt wird, deutet es darauf hin, dass die i2c-Verbindung nicht korrekt ist. Daher wäre nun die Frage, welchen MicroController Sie nutzen und ob das korrekte Pinout für den MicoController genutzt wird. “Schnee” ist immer ein Anzeichen dafür, dass das Display zar Strom bekommt, aber eben keine Daten.

Gilbert Lerch

Gilbert Lerch

Vielen dank für die Antwort. Ich werde den ESP 32 nehmen.

Thomas Bierschwale

Thomas Bierschwale

Ich habe das Projekt nachgebaut. Es läuft soweit, im Monitore werden richtige Werte angezeigt. Leider zeigt das Display nur “Schnee” an. Das Display ist genau dieses hier: https://az-delivery.de/products/0-96zolldisplay. Normalerweise benutze ich immer die Lib , wie auch im Ebook dazu angegeben.
Irgendwo auf diesen Seiten hatte ich mal was zur Abhilfe gesehen, finde es aber nicht mehr. Was kann ich tun?

Joern Weise

Joern Weise

@Anyone: Das freut mich. Die Bauteile sind recht übersichtlich gehalten, sollte daher schnell und unkompliziert aufgebaut sein.

@Ulrich Klaas: Für jedes Board gibt es in der Kategorie “Kostenlose eBooks” eine Anleitung, wie man das ESP zum Leben erweckt. Mittlerweile gibt es einen Haufen an ESP32-Module, mit den verschiedensten Unterschieden. Ich habe mich bewusst für dieses Model entschieden, da es einen 5V-Ausgang hat, der gerade bei den LCD-Displays gebraucht wird, da 3,3V einfach zu wenig ist. Ich bin kein Freund eine Spannungsquelle so zu splitten, dass ich diverse Geräte damit mit Strom versorgen kann, wobei es auch hier eine gute lösung von AZ-Delivery gibt https://az-delivery.de/collections/basis-produkte/products/mb102-breadboard. Meist ist es bei den ESP’s so, dass man mehr Pinouts hat oder mehr Schnittstellen oder sie sehr kompakt sind. Ähnlich wie beim Arduino ist der Grundaufbau “quelloffen” es kann sich theoretisch also jeder selbst etwas basteln. Zudem unterscheidet man noch darin, ob sie eine Programmierschnittstelle haben oder eben nicht. Ich glaube das Thema hier zur Gänze zu klären schaffe ich nicht, dafür gibt es diverse (englischsprachige) Foren, wo es Diskussionen dazu gibt.

@michael: Natürlich können sie auch andere Displays nutzen, wichtig dabei ist aber, dass Sie die richtige Schnittstelle und Libary für das jeweilige Display einsetzen. Da es auf dem Markt eine große Anzahl an Displays gibt, diese unterschiedlich angesteuert werden, kann es durchaus sein, dass Sie den Code stark modifizieren müssen, um ein ähnliches Ergebnis zu bekommen. Ggf. wird es auf meiner GitHub-Seite eine Update mit dem dem 1,8" Display von AZ-Delivery geben (https://az-delivery.de/collections/displays/products/1-8-zoll-spi-tft-display). Dazu muss ich aber dann die Zeit finden.

@Sven Hesse: Danke für dieses schnelle und positive Feedback. Das freut mich als Blogger und auch das Team von AZ-Delivery, wenn solche Projekte positiven anklang finden.

@Gilbert: Geht theoretisch auch mit einem ESP 12, Sie brauchen aber unbedingt die i2c-Schnittstelle, damit das Display auch geht. Zusätzlich ist die verwendete JSON-Libary sehr groß, das heißt auch der Platzbedarf vom Coder sollte nicht unterschätzt werde.

Gilbert

Gilbert

Sehr interessant!
Frage: Geht es auch mit einem ESP8266NodeMCU ESP 12E?
Danke.

Sven Hesse

Sven Hesse

Hi,

vielen Dank für das Tutorial.
Direkt getestet und läuft.

Kleiner Hinweis an @michael: Man kann natürlich auch größere Displays verwenden, dazu findet man auf Github auch reichlich Informationen.

michael

michael

Hallo, tolles Teil. Frage kannn man cu einen anderen “Monitor” anschließen?
zB.: 1,77 Zoll SPI TFT-Display und 128×160 Pixeln für Arduino
Danke und schönes WE

Ulrich Klaas

Ulrich Klaas

Hallo, gibt es eigentlich irgendwo eine vernünftige Übersicht was man für welches Board im Boardverwalter einstellen muss ? Bisher wurde für alle ESP32-Nodemcu Projekte hier das Board ESP32_Firebeetle angegeben. Hier jetzt für das selbe Board ESP32-DEV. Wo sind die Unterschieden in den Einstellungen ? Bei den einfachen Arduinotypen einschließlich Tinies und auch Mighty-Core (also 644 und 1284) blicke ich ja durch, Aber bei den ganzen EPSs ist das ja inflationär.

Anyone

Anyone

Ein sehr schönes Projekt. Das wird umgehend nachgebastelt.

Einen Kommentar hinterlassen

Alle Kommentare werden vor der Veröffentlichung moderiert

Post di blog consigliati

  1. Installa ESP32 ora dal gestore del consiglio di amministrazione
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - Programmazione ESP tramite WLAN