Zonnestroompanelen in Nederland

duurzaamheid achter de meter

(57) Dedicated ESP8266-ILI9341 TFT JSON OpenWeatherMap station

by Floris Wouterlood – The Netherlands – March 2 2022

This project deals with a permanent weather station running on a Wemos D1 mini ESP8266 microcontroller that drives a 3.2 inch diagonal 320*480 display with ILI9341 controller. Local / regional weather data are downloaded from an OpenWeatherMap server as a JSON object, parsed, processed and displayed.

Access to information about the weather is always handy in Northestern Europe. The Low Countries where I reside has an amazing variety of weather patterns, common to the mild sea climate at northern latitudes. Sunshine, wind and rain succeed each other rapidly.
There are several weather services and airports that continuously broadcast weather reports aimed primarily at aviation and shipping. A service like OpenWeatherMap is relatively new: an open source community that makes weather data available in JSON strings that can be downloaded, parsed and then processed to display the information one assesses important enough to show on screen (figure 1). In a previous project I constructed what was basically a bench based on a ESP32-WROOM32 microcontroller board and a 320*240 display*. However this setup was constructed for experimental purposes using the high-performance ESP32. In the present project we construct a permanent JSON weather station. To have a small footprint and to use a microcontroller whose power matches the job better than the ‘roaring horse’ ESP32 I selected the modest Wemos D1 mini microcontroller board. The Wemos D1 mini has WiFi on board which is most practical, it has enough memory and sufficient speed. The current project leans otherwise heavily on experience obtained during the previous weather station project.

Information selected for display
JSON strings received from www.openweathermap.org contain an amazing lot of weather information of almost any place on the globe. Figure 1 presents the local weather inormation. I am most interested in: current temperature, minimum and maximum temperatures of the day, barometric pressure, relative humidity, wind, wind direction. A little bit of graphical presentation embellished the screen. The big challenge is that all information has to fit a limited, 320*240 pixel screen.

figure 1. Some elements of the JSON weather string information on the display.

The screen is subdivided into two big upper panels: the numeric panel and the temperature gauge. Below these big panels is a row of four small panels: wind compass, humidity panel, barometer panel and, finally, a min-max temperature panel.

Construction of a permanent weather station
The first basic decision was that all components should fit an 8×12 cm double sided prototyping soldering board, with the display in landscape orientation: a 3.2 inch diagonal 320*240 pixel TFT with ILI9341 controller and SPI connectivity. This leaves several possible positions for the microcontroller board: above, below or next to the display. Most pleasing for the eye is when the microcontroller is positioned next to the display, with the power connector directing downward. Wiring can then be ultrashort. All wiring is of course located on the back of the soldering board. Another basic decision (rather a dogma) was to mount the major components always on pin header sockets instead of soldering them directly onto the main board.
Figure 2 shows the schematic placement of pin header sockets on the base board, with the necessary wires connecting the microcontroller sockets and the display sockets. A wiring table is embedded in figure 2.

figure 2. Positioning of components and wiring scheme of the current weather station on the base board.

Wiring of the display with an ESP8266 microcontroller board
Wiring is simple. The display is of the 14-pin SPI type that needs only five wires to control plus, of course, wires for power and GND. An ILI9341 SPI TFT display has a single row of 14 pins of which six serve touch functionality and SD card support. The pinout of the display is presented as a table in figure 2. This is the wiring used in the current weather station. The touch and SD card functionality is not used here.
Even with a Wemos D1 mini with its limited number of pins some pins remain free after wiring has been completed: D0, D1, D6 and D8. To have these pins available in future applications, for instance the addition of a home theromometer or external humidity sensor I opted to add a row of pin sockets below the display. The wiring of these additional sockets is schematized in figure 3. The actual board with its sockets and all wiring completed is shown in figure 4.

figure 3. Positioning of aditional pin header sockets serving the ‘free’ microcontroller pins, and wiring.

figure 4. Actual base board with all pin header sockets and wiring. The Wemos D1 mini is mounted on its socket in the ‘bench top’ picture.

OpenWeatherMap.org is a company specialized in the distribution of global weather data (https://openweathermap.org). Data for 200,000 locations on Earth is continuously available. Although the firm offers a repertoire of (mostly) commercial products, one of their services is a free subscription to a limited amount of weather data, say a ‘teaser’. One can obtain a free API, capped to a generous 60 calls per minute or a one million calls per month. Perfect for the hobbyist.
Once one has registered at OpenWeatherMap.org and an API key has been obtained, software programming can begin.

Parsing the JSON Document
The document received after a WiFI connection has been established and connection has been made with the OpenWeatherMap server, is a string in JSON notation. In my case for instance:

JSON object = {“coord”:{“lon”:4.4931,”lat”:52.1583},”weather”:
[{“id”:804,”main”:”Clouds”,”description”:”overcast clouds”,”icon”:”04d”}],”base”:”stations”,”main”:{“temp”:279.47,”feels_like”:277.62,”temp_min”:278.71,”temp_max”:279.82,

The coordinates “lon” and “lat” translate in Google Maps to the city center of my hometown.
Parsing of the downloaded string into JSON object-value pairs is performed by instructions in the library ‘Arduino_JSON.h’, and the only thing the current sketch does is to extract the values of the objects “temp”, “pressure”, “humidity”, “wind” {“speed”}, “wind” {“deg”}, “temp_max” and “temp_min”. Temperature is provided in degrees Kelvin, thus needing conversion into degrees Celsius (or Fahrenheit if you wish). Wind speed is in meters per second and “deg” represents the compass direction where the wind comes from, with ‘0’ being North, 090 being East, 180 being South and 270 being West. For graphical representation the wind direction needs to be converted from degrees into radials. Hence the conversion factor, ‘#define DEG2RAD 0.0174532925.’

Display control library
Whereas the ESP32- ILI9341-SPI combination is perfectly served by the TFT_eSPI.h library created by Bodmer, support for ESP8266 microcontrollers and ILI9341-SPI is limited. The current sketch therefore leans on the Adafruit_GFX.h and Adafruit_ILI9341.h liraries. There are minor differences between the instructions used in TFT_eSPI.h and the Adafruit libraries.

All static stuff anchored to corner coordinates of their panel
In order to make it possible to move an entire panel from one end of the display to another position, all graphical items are positioned relative to the x-y coordinates of the upper left corner of that panel. This is exemplified with the barometer panel instructions. There are two functions involved here: a function that ‘builds’ static content of the barometer panel and a dynamic function that refreshes the barometric panel every time that a new JSON string is received from the OpenWeatherMap server. The comment lines are removed here to save space.

// #############################################
// # dynamic barometer panel #
// #############################################

Because the information window is limited to a humble 80*80 pixel panel the barometer pressure dynamics must be kept within limits. Barometer pressure itself is represented as a red circle moving up and down a diagonal line. The lower end pressure is unimportant because I pressure never sinks below 950 mBar. At the upper end 1050 mBar was set as limit. For the purpose of making calculations a variable named ‘barval’ was introduced:

void build_dynamic_baropanel (){
barval = 1050-press_02;

The movement of a red circle up and down a diagonal line can be visually enhanced by filling the space below the diagonal line panel with a color – here blue as if it were a swimming pool. The ‘fill’ of this space is determined by four points of which point ‘p1’ is most important since that point is the center point for the red circle indicating current barometric pressure:

p1_x = ((baro_panel_x+2)+ ((100-barval)/100*72));
p1_y = ((baro_panel_y+75)- ((100-barval)/100*73));

Graphical libraries such as Adafruit_GFX.h offer functions to produce filled circles, rectanges and triangles while such an instruction as tft.fillPolygon is not available. A way to nevertheless create a filled polygon is do some creative programming with triangles. This trick is used here (and in Bodmer’s Rainbow Gauge as well): four points that define two triangles (figure 5):

p2_x = baro_panel_x+3;
p2_y = baro_panel_y+74;
p3_x = baro_panel_x+75;
p3_y = baro_panel_y+74;
p4_x = baro_panel_x+75;
p4_y = p1_y;

After drawing the two triangles the x-y coordinates for these points need to be ‘remembered’ to be used later when the polygon needs to be refreshed:

p1_x_old = p1_x;
p1_y_old = p1_y;
p2_x_old = p2_x;
p2_y_old = p2_y;
p3_x_old = p3_x;
p3_y_old = p3_y;
p4_x_old = p4_x;
p4_y_old = p4_y;

Now we can let the graphics work: draw the two-triangle polygon and complete this module by drawing the diagonal line and the red ‘baro pressure’ circle. SCALE2 is a color definition used also in the ‘rainbow gauge’.

tft.fillTriangle (p1_x, p1_y, p2_x, p2_y, p3_x, p3_y, SCALE2);
tft.fillTriangle (p1_x, p1_y, p4_x, p4_y, p3_x, p3_y, SCALE2);

In addition to this we need a few corrections. The following two instructions draw lines to fill the space between the ‘base’ of the two-triangle polygon and the panel edge. This space is caused by the rounded corners of the panel’s edge:

tft.drawLine (baro_panel_x+4, baro_panel_y+75, baro_panel_x+74, baro_panel_y+75, SCALE2);
tft.drawLine (baro_panel_x+4, baro_panel_y+76, baro_panel_x+72, baro_panel_y+76, SCALE2);

Enhancing the horizontal ‘water level’ line :

tft.drawLine (p1_x, p1_y, p4_x, p4_y, CYAN);

Drawing the panel’s diagonal line:

tft.drawLine (baro_panel_x+2, baro_panel_y+75, baro_panel_x+75, (baro_panel_y+2), CYAN);

Drawing the red circle that indicates current barometric pressure:

tft.fillCircle (p1_x, p1_y,3,RED);

Do some additional work:

tft.setTextColor (YELLOW,BLACK);
tft.setCursor (baro_panel_x+5, baro_panel_y+4);
tft.print (“baro “);
tft.setTextColor (WHITE,BLACK);
tft.setCursor (baro_panel_x+32, baro_panel_y+4);
if (press_02>999) tft.print (“high”); else tft.print (“low “);

And ready is the barometric pressure panel. Every cycle of the weather station program the essential components of the panel need to be refreshed: red circle and blue ‘fill’. For this purpose I have composed a function ‘refresh_dynamic_baropanel ():

// #############################################
// # refresh the dynamic barometer panel #
// #############################################

void refresh_dynamic_baropanel (){

tft.fillCircle (p1_x_old, p1_y_old, 3, BLACK);
tft.fillTriangle (p1_x_old, p1_y_old, p2_x_old, p2_y_old, p3_x_old, p3_y_old, BLACK);
tft.fillTriangle (p1_x_old, p1_y_old, p4_x_old, p4_y_old, p3_x_old, p3_y_old, BLACK);
tft.drawLine (p1_x_old, p1_y_old, p4_x_old, p4_y_old, BLACK);
tft.drawLine (baro_panel_x+2, baro_panel_y+75, (baro_panel_x+75), (baro_panel_y+2), CYAN);

tft.drawLine (baro_panel_x+4, baro_panel_y+75, baro_panel_x+74, baro_panel_y+75, SCALE2);
tft.drawLine (baro_panel_x+4, baro_panel_y+76, baro_panel_x+72, baro_panel_y+76, SCALE2);

figure 5. Explanation of the graphics involved in the dynamic barometer window.


The default font of the ILI9341 does not spectacularly catch the eye. Fortunately the Adafruit libraris are accompanied with several font libraries that contain more pleasing fonts than the ILI3941 default font. Here used is the font library “FreeMono9pt7b.h1“.

Min-max temp window
One of the variables provided by the OpenWeatherMaps JSON string are the minimum and maximumn temperatures measured locally. These data inspired me to include a min-max thermometer in the weather station. The ‘mercury level’ in the thermometer graphics consists of a fixed red circle and is a rectangle whose upper edge (the ‘level’) is coupled to the reported maximum temperature. Minimum tenperature is fixed to avoid negative values for the ‘fill’ function.

About the rainbow scale gauge
I would like to mentioned here that the current rainbow scale gauge is an adaptation of the ‘ring meter’ published by Bodmer**

About working with JSON strings
There’s a lot to learn from Rui Santos’ posts at RandomNerdTutorials.com, e.g., ESP8266 NodeMCU HTTP GET with Arduino IDE (OpenWeatherMap.org and ThingSpeak)


figure 6. Final assembly: a working weather station.



Sketch named ‘ESP8266_ILI9341_TFT_permanent_openweathermap.ino
This sketch is packed in a ZIP file.

The folder included in the ZIP file also has “Free_Fonts.h” as that library is required to display text in ‘mono’ font.


*My ESP32-WROOM-32 – ILI9341 TFT OpenWeatherMap station – Thesolaruniverse.wordpress.com – March 1, 2021.

**Arduino Analogue ‘ring’ Meter on Colour TFT Display
Bodmer – Instructables com, March 17, 2015