Zonnestroompanelen in Nederland

duurzaamheid achter de meter

(33) Multi temperature-humidity sensing with an Arduino Nano – TFT display: sketch updated

Some time ago I constructed a desk display centered around an Arduino Nano and equipped with a 3.5″ 320×480 pixel color TFT display. The screen of this station shows temperatures measured by seven Dallas DS18B20 sensors and relative humidity averaged from values reported by two DHT11 sensors. This multi temperature-humidity reporting station is in daily use. The current paper discusses a sketch update designed to maximize the visual experience of the display.

In a previous paper* I described the electric wiring of an Arduino Nano that displays readings of several Dallas DS18B20 temperature sensors and DHT11 relative humidity sensors on a 3.5″ 320×480 color TFT display (figure 1).

figure 1. The multi-temperature-humidity station running the updated software: currently seven temperature measurements and one relative humidity.

The original software sketch is quite straightforward: it requests temperature and relative humidity values and it displays text and numbers on the TFT screen, with as only graphical support a few colored lines (see fig. 2B). As TFT displays running instructions under David Prentices’ mcufriend_kbv library are capable of much more powerful visual representation than just text I experimented with a simple, ‘retro analog’ type of digital representation of the sensor reading, that is, with gauges, scales and needles. The result is an updated sketch.

figure 2. A, Display with updated sketch featuring scales, needles, text and numerical display. B, Original sketch.

Four conditions
The original sketch (figure 2B) dealt with temperatures measured at 8 different spots and with the relative humidity inside and outside my home. Because of a home improvement project the measurements for outside temperature and relative humidity had to move to a new location. For that purpose a nodeMCU ESP8266 based weather station was constructed reporting to the internet**. Remaining were temperature measurements at seven spots instead of eight. In addition one relative humidity sensor remained connected to the microcontroller (the sensor inside the home). This amounts to eight environmental parameters to be displayed continuously and simultaneously. The design of a visually attractive yet practical way of displaying the data required fulfilling four conditions.

condition 1: eight environmental parameters: display name & value

This condition was already met in the original sketch. Of course a 3.5 inch TFT amounts to luxury when a simple 16×2 LCD display can easily show as many environmental parameters as you want with some alternating screen printing of the various environmental parameter values. However, a ‘luxury’ TFT display mounted on the multi-temperature/humidity station (figure 1) strongly invites to experiment with graphical presentation.

condition 2: simultaneous and continuous display

A 3.5 inch TFT display provides enough space to accommodate multiple windows each of which deals with one of the environmental parameters. The temperatures currently monitored concern air temperatures inside in my office and in the attic, central heating pipe temperatures where they are attached to the boiler, the temperature at the outlet of the solar water heater storage tank, and casing temperatures of two solar grid tie (micro) inverters. Relative humidity is that of the air inside my office.

condition 3: visually attractive

In recent years graphical representations for sensor readings have been designed that catch the eye. Most impressive is Bodmer’s rainbow scale representation***.

condition 4: fitting the Arduino Nano’s memory

Graphical representations and text strings require memory hungry programming of the Arduino. While a ‘Bodmer type’ rainbow scale exploits the graphical and color capabilities of a TFT display to the max and at the same time is very pleasing to the eye the combination of eight simultaneously displayed rainbow scales on a 320×480 TFt screen might be a little bit overwhelming for both the human eye and, more important, the microprocessor’s working memory. An 8-rainbow scale sketch might be too big for the limited (32kB at the most) Arduino Nano’s memory.

figure 3. Window elements; x-y pixel coordinate designations in the scale design.

Design of the updated sketch
The display at hand is a 3.5 inch TFT with dimensions 320×480 pixels and 16 bit color depth (ILI 9481 controller). In the design for the updated sketch I divided the screen into eight rectangular ‘windows’, each sized 155 pixels width and 110 pixels high.

Static windows, dynamic needles
The concept is a static screen consisting of eight windows each with a scale and the display of labels that identify the sensor whose data are presented. This part is managed in the Setup section of the sketch. The dynamic part of the sketch (in the Loop section) concerns the updating of needle positions and sensor values in each individual window, one after another.

Each window is outlined with a one-pixel thick rectangle with rounded corners (see figure 3). For instance, for the upper left window the instruction that creates this rectangle is:

tft.drawRoundRect (5, 10, 155, 110, 5, WHITE);   // draw scale window

which produces a white rectangle on screen whose upper left corner starts at absolute screen position x=5, y=10, width is 155 pixels, height is 110 pixels, corner radius 5 pixels and edge color white. Note that WHITE has been predefined at the beginning of the sketch.

Pivot point and pivot circle
In order to save space I applied in each window a semicircular scale (180 degrees) with its pivot point defined by two parameters: center_x and center_y (fig. 3). These variables are also used to position a filled yellow circle that enhances the pivot point. Radius of this ‘pivot circle’ is 4 pixels. The rotation point of the needle coincides with that of the pivot point

The pivot circle is drawn in a subroutine called needle_center ():

tft.fillCircle (center_x, center_y, 4, YELLOW);       // pivot circle

The x and y pixel coordinates for the pivot point serve as a real centerpoint. They are used for building the scale and they define the pivot point for the gauge needle.

Scale and scale markers
In each window the scale is made up of 31 markers arranged archwise. Each scale marker is a line segment pointing towards the pivot point.
Functions that draw line segments need x-y coordinates of two points: beginning and end. Thus, each scale marker is defined by four variables: edge_x_out and edge_y_out for the outer coordinates, and edge_x and edge_y for the inner coordinates. These coordinates are calculated in a subroutine called ‘draw_markers’.

The latter subroutine positions 31 scale markers 6 degrees apart on a 180 degrees arc. the markers are defined by a radius and an angle. This angle is expressed in radians because the x-coordinates are calculated in a ‘do…while’ loop with a cosine (angle) function and the y-coordinate with a sine (angle) function that both require radians as their input variable. Here follows the complete subroutine:

void draw_markers (){
j = 180;
do {
angle_circle = (j*0.01745331);     // 1 degree = 0,01745331 radians
edge_x = (center_x + (radius*cos(angle_circle)));
edge_y = (center_y + (radius*sin(angle_circle)));
edge_x_out = (center_x + ((radius+12)*cos(angle_circle)));
edge_y_out = (center_y + ((radius+12)*sin(angle_circle)));
tft.drawLine (edge_x, edge_y, edge_x_out, edge_y_out,WHITE);

j = j+6; // markers 6 degrees apart on the arc
}while (j<362);                              // if 360 then last marker is not drawn!

Loop section: update windows one by one
The Loop section of the sketch contains a counter called ‘iteration’ and eight subsections. Each subsection is responsible for updating one window. Subsections contain the following series of instructions:

  • Recall the previous needle coordinates. Only the two coordinates defining the needle tip need to be recalled since the base of the needle is always the center point of the window that is being updated,
  • Issue a call to read the sensor that belongs to that window, and store the sensor’s returned value into a variable called ‘temp_hum’,
    ‘temp-hum’ is a float variable with a value between 0 and 100. Because the scale is linear between 0 and 180 a conversion constant for the needle’s angle is sufficient to allow the needle to swing between the minimum (0) and maximum (180) marker of the scale,
  • Transfer ‘temp-hum’ to the subroutine ‘needle’ where its value is multiplied with 1.8 (the conversion constant). The result is used to calculate the coordinates needle_x and needle_y pixel that define the outward point of the needle.

void needle (){              // overwrite old needle and draw new needle

if (iteration >1)
tft.drawLine (center_x, center_y, needle_x_old, needle_y_old, BLACK);
angle_needle = (((temp_hum)*0.01745331*1.8)-3.14);
needle_x = (center_x + ((radius-10)*cos(angle_needle)));
needle_y = (center_y + ((radius-10)*sin(angle_needle)));
tft.drawLine (center_x, center_y, needle_x, needle_y,YELLOW)

Note that this subroutine redraws the previous position of the needle with the color BLACK (visually erasing the previous needle) and follows up immediately by drawing the updated needle in the color YELLOW.
Interesting here is the ‘if (iteration>1)….. instruction. I needed to insert in the Loop section a counter (‘iteration’) and in the subroutine the ‘if….’ instruction in order to prevent the subroutine in the very first iteration of the Loop to start drawing black needle lines from the left upper corner of the TFT display all the way to the pivot points of each of the eight windows. Its cause was that at the start of the very first iteration of the Loop section the variables needle_x_old and needle_y_old in all windows are equal to zero!

  • After updating the needle, redraw the pivot circl,
  • Finally, update in the window the digital value display,
  • save the ‘needle_x’ and ‘needle_y’ values in ‘needle_tip_x_old’ and ‘needle_tip_y_old’ variables.

After this series of instructions has been executed, the value of ‘temp_hum’ is printed in the upper right corner of the window, and Serial Monitor is updated.

The Arduino IDE reports at compilation time that the sketch uses about 20k memory out of 30k available memory. Global variables gobble up 40% of dynamic memory. The sketch works fine, and the windows provide all the necessary information at an eye’s blink.

The current 3.5 inch 320*480 pixel screen is my favorite output device to display multi-sensor readings. The biggest challenge with these screens is to squeeze in as many gauges as possible in the best readable way. Readability conflicts sometimes with graphical highlighting. Rule of thumb with displays should be ‘readability first’. At the same time the screen resolution and the available memory of an Arduino Nano limit the possibilities to tinker with graphical embellishments. Dividing the 3.5 inch screen into eight windows each 155*110 pixels limits scale design to the absolute minimum.

Updated Sketch
attic_graphic_v_03.zip (zip-ped version of attic_graphic_v_03.ino)


* Floris Wouterlood (2016) – Multi temperature-humidity sensing with an Arduino Nano displayed on a 3.5″ color TFT screen.

**Floris Wouterlood (2018) – Constant monitoring of five environmental parameters with a NodeMCU ESP8266 based weather station connected to the Internet.

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