Impressum Projects Music Research Code About
About Research Music Projects Code Thingiverse Impressum

 

 

 

 

 

Fetching Time and Weather Data w/ ESP32

Running the weather-data-fetching program from last time on a small MCU as an embedded software requires some adjustment. It is most of the time necessary to know which hardware you are using to compile accordingly. Now, the plan is to run the software on an ESP32. This MCU is readily available and easily programmed with an arduino IDE.
In addition, a small display shall be added.

For controlling the display, the TFT_eSPI library works great. Before using it, though, the User_Setup.h file needs to be edited to fit your needs. A closer look at this file is also very helpful to get the pinning right because it is commented very well.

Now on first glance it could be the most simple way to take the C++ code from the first part and adapt it for the Arduino environment. On second glance, this does not work very well. The libcurl does not work on the ESP32. The nlohmann/json library is not made for Arduinos and Arduino type MCUs. But there is an Arduino JSON library which works great. But then, this uses the Arduino String instead of std::string. And then there is the clock, which should be running in the background and only sometimes check against an online clock to be correct.
Given this, it will be simpler to use a clock library for the time and adapt the weatherStation.h in such a way that it uses standard Arduino Strings and ESP32 internet connectivity instead of curl. The class still looks similar, but a bit more bulky.

class weatherStation
{
  public:
    weatherStation(String city, String owmId);
    ~weatherStation();
    void init();
    void pull_weatherdata();
    void pull_forecastdata();

    void updateTime();
    String year;
    String month;
    String day;
    String time;

    String temperature;
    String description;

    int min_int;
    int hour_int;

    std::vector fc_desc;
    std::vector fc_temp;
    std::vector fc_times;

  private:
    String weatherUrl;
    String forecastUrl;
    // struct for the clock
    struct tm timeInfo;
    // setup of the clock
    const char* ntpServer = "pool.ntp.org";
    const long gmtOffset_sec = 3600;
    const int daylightOffset_sec = 3600;
};

The constructor works more or less the same as before, it needs a city and your OWM ID. New is, that you need to call init(). This is needed because init() will setup time which needs an internet connection and if you define the weatherstation object globally, you will probably not have setup a connection on your ESP32.
You still have update routines for time, weather and forecast and variables in which the data is stored. As pivate variables there are also a bunch of new ones needed for the time. Setting up the library in the code is still pretty straightforward:

...
weatherStation ws("<your city>", "< Your ID >");
...
void setup() {
  ...
  // Initialize the WiFi connection
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    ...
    delay(WAIT);
  }
  ...
  // Pull the weather- and forecastdata and print them to the screen.
  ws.init();
  ws.pull_weatherdata();
  ws.pull_forecastdata();
  ws.updateTime();
  ...
}

And after that you can just update your data as long as you don't close your connection or reach your OWM limit. One possibility to make sure this does not happen is to update different data in different intervals. The loop() could for example look like this:

void loop() {
  // timebuf is used to store the minute value
  int timebuf = ws.min_int;
  // get new time
  ws.updateTime();
  // New minute? Pull New weather data
  if (timebuf != ws.min_int) {
    ws.pull_weatherdata();
    // New hour? Pull new forecast data
    if ((ws.min_int == 0)) {
      ws.pull_forecastdata();
    }
    // update display accordingly
    updateDisplay();
  }
  delay(WAIT);
}

The display is updated and drawn in the updateDisplay() function. For a 160 x 128 pixel display the function could look like the following example, where the current time and weather as well as the forecast for the next few hours is displayed. The degree Celcius unit is not quite as straightforward because the font library misses the small circle you need. In the following this is substituted with an out-of-line 'o':

void updateDisplay() {
  // Display Size: 160 x 128
  // Set textsize multiplier to 1, black background and green text.
  tft.setTextSize(1);
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_GREEN, TFT_BLACK);

  // First, convert the hour from int to string
  char hour[12];
  itoa(ws.hour_int, hour, 10);
  // Take care, that there is is no '0' in front of single digit times
  if (ws.hour_int < 10) {
    hour[1] = hour[0];
    hour[0] = '0';
  }

  // Same for the Minutes.
  char minit[12];
  itoa(ws.min_int, minit, 10);//timeinfo.tm_min;
  if (ws.min_int < 10) {
    minit[1] = minit[0];
  minit[0] = '0';
  }

  // Write the time in the middle top of the display with font '7'
  float xstart = 80. - tft.textWidth("00:00", 7) / 2; // Screenwidth/2 - textwidth/2
  tft.drawString(hour, xstart, 2, 7);
  tft.drawString(":", xstart + tft.textWidth("00", 7), 2, 7);
  tft.drawString(minit, xstart + tft.textWidth("00:", 7), 2, 7);

  // some variables are defined to simplify the positioning of text
  float y = tft.fontHeight(7) + 4;
  float xbuf = 7.;
  float startbuf = 15.;
  float degbuf = tft.textWidth("oC", 2) + 2;
  xstart = tft.textWidth("00:00", 2);

  // Print the current weather
  tft.drawString(ws.temperature, xbuf, y, 2);
  tft.drawString("o", xstart + xbuf - 5, y - 3, 2);
  tft.drawString("C", xstart + xbuf + xbuf - 6, y, 2);
  tft.drawString(ws.description, 1 * xstart + 1 * xbuf + degbuf, y, 2);

  // print the weather in 3 hours
  y += tft.fontHeight(2) + 2;
  tft.drawString(ws.fc_times.at(0), xbuf, y, 2);
  tft.drawString(ws.fc_temp.at(0), xstart + 2 * xbuf, y, 2);
  tft.drawString("o", 2 * xstart + xbuf + 3, y - 3, 2);
  tft.drawString("C", 2 * xstart + 2 * xbuf + 2, y, 2);
  tft.drawString(ws.fc_desc.at(0), 2 * xstart + 2 * xbuf + degbuf, y, 2);

  // print the weather in 6 hours
  y += tft.fontHeight(2) + 2;
  tft.drawString(ws.fc_times.at(1), xbuf, y, 2);
  tft.drawString(ws.fc_temp.at(1), xstart + 2 * xbuf, y, 2);
  tft.drawString("o", 2 * xstart + xbuf + 3, y - 3, 2);
  tft.drawString("C", 2 * xstart + 2 * xbuf + 2, y, 2);
  tft.drawString(ws.fc_desc.at(1), 2 * xstart + 2 * xbuf + degbuf, y, 2);

  // print the weather in 9 hours
  y += tft.fontHeight(2) + 2;
  tft.drawString(ws.fc_times.at(2), xbuf, y, 2);
  tft.drawString(ws.fc_temp.at(2), xstart + 2 * xbuf, y, 2);
  tft.drawString("o", 2 * xstart + xbuf + 3, y - 3, 2);
  tft.drawString("C", 2 * xstart + 2 * xbuf + 2, y, 2);
  tft.drawString(ws.fc_desc.at(2), 2 * xstart + 2 * xbuf + degbuf, y, 2);
}

My version of this code is available under gitlab.