Arduino Analogue 'ring' Meter on Colour TFT Display
Intro: Arduino Analogue 'ring' Meter on Colour TFT Display
Yet another Arduino project for the display of sensor readings etc.
The example sketch included in the instructable includes the meter drawing function to use in your own projects, the function is not in a library so could be adapted for use with other graphics libraries. The meter drawing function has been "parameterised" so the meter size, position, range, units and colour scheme can be easily configured to suit your own needs.
Part of the inspiration for the development of the graphic was to have a similar style to the forthcoming Adafruit IO "Internet of Things" website.
The display is a 2.2" 320 x 240 pixel TFT colour display with the ILI9341 driver chip, this is driven by an Arduino UNO.
STEP 1: The Meter Graphics
The value displayed is shown as a bar graph that can be segmented or continuous. Larger values are shown as more segments being coloured in, thus the display gives an instant visual indication of a reading at a glance without having to read the numbers. The numerical value is shown in the center of the ring, a bit like the Google Nest display.
The colour of the ring segments can be set to change as the value increases, so this means a display showing temperatures can be set change from blue to red as the temperature increases. Another display for say a battery level could change from red for a low value, through yellow to green for a high value.
The meter position and size can be set as desired to make a meter arrangement that suits your taste. The sizes are set independently so more important information can be shown with a bigger graphic, or the display could cycle though several meters.
The unlit segments are dark grey (these segments look brighter in the images as I had to adjust the web cam settings to try to capture the display colours.)
The displays are best for values that change slowly with time such as temperature and humidity etc.
STEP 2: Hardware Configuration
The hardware setup is common to some of my other Instructables, it consists of the UNO, the 2.2" TFT display based on the ILI9341 driver chip and a set of libraries that are enhanced versions of the Adafruit GFX and ILI9341 libraries.
The libraries attached have been enhanced to give a much faster performance on an UNO using direct port access, other boards could be used by commenting out a #define and changing the the SPI pins.
The UNO is connected to the ILI9241 2.2" TFT display like this:
- UNO +5V to display pin 1 (VCC)
- UNO +5V through a 56 Ohm resistor to display pin 8 (LED)
- UNO 0V (GND) to display pin 2 (GND)
- UNO digital pin 7 through a 1K2 resistor to display pin 4 (RESET), add a 1K8 resistor from display pin 4 to GND
- UNO digital pin 9 through a 1K2 resistor to display pin 5 (DC/RS), add a 1K8 resistor from display pin 5 to GND
- UNO digital pin 10 through a 1K2 resistor to display pin 3 (CS), add a 1K8 resistor from display pin 3 to GND
- UNO digital pin 11 through a 1K2 resistor to display pin 6 (SDI/MOSI), add a 1K8 resistor from display pin 6 to GND
- UNO digital pin 13 through a 1K2 resistor to display pin 7 (SCK), add a 1K8 resistor from display pin 7 to GND
It is important to include the 1K8 resistors to GND with this 2.2" display as otherwise it will not work! The 1K2 and 1K8 resistors are a "potential divider", acting as a logic level shifter so that the logic level at the display is reduced from 5V to around 3V. Pin 9 of the display does not need to be connected up.
I have included a couple of short (!) videos (rather poor quality...) showing the graphics speed using an UNO and hardware SPI. (These have had the long delay() intervals between each test screen removed from the sketch!)
One video is the UTFT Demo running on a Mega board with a 16 bit parallel interface to the display. It is faster mostly because the 10,000 pixel plot test at the end has been changed so 30,000 16 bit random numbers do not need to be produced at the same time as plotting 10,000 pixels (generating 30,000 16 bit random values takes nearly 3 seconds so the UNO can really drive the display faster than it appears during that part of the test!)
The "Sine wave" animation is limited mainly by the floating point maths involved and could be speeded up dramatically by using a simple Sine lookup table, that test would then be over in the blink of an eye!
STEP 3: The Meter Drawing Function
Here is the function prototype:
int ringMeter(value, vmin, vmax, x, y, r, "Units", scheme)
The ringMeter function returns the x coordinate of the right hand side of the meter to aid placement of the next meter.
value is the value to be displayed and plotted, integer values up to 4 digits are accommodated
vmin is the minimum value to be plotted
vmax is the maximum value to be plotted, thus vmin and vmax set the full meter range
x and y are the coordinates of the top left corner of an imaginary box that contains the meter
r in the outer radius of the ring in pixels, the minimum is about 52 before the text intrudes on the ring
"Units" is the text string such as "Volts", "C" etc.
scheme sets the colour scheme, there are some # define statements in the example that enumerate the settings available, others could be added:
- #define RED2RED 0
- #define GREEN2GREEN 1
- #define BLUE2BLUE 2
- #define BLUE2RED 3
- #define GREEN2RED 4
- #define RED2GREEN 5
These different schemes set the colour change as the value grows from vmin to vmax, so for example in the following the colour scheme is change from Blue to Red as the value increases:
ringMeter(reading,-10,50, xpos,ypos,radius,"degC",BLUE2RED);
In this case a temperature reading is being plotted in the range -10 to +50 degrees C, as the value increases the segments turn from blue (cold!) to red (hot!) as shown in the picture.
STEP 4: Libraries and Sketch
The ring meter sketch and the two libraries needed are in the attached zip file.
Some fonts have been disabled in the "Load_fonts.h" file to save space in the compiled sketch. Fonts 2,4 and 6 are enabled.
The "Adafruit_ILI9341_AS" library as provided here is optimised for the UNO and ATmega 328p microcontroller, so it is best to start with that device.
To use with others such as the Mega the SPI pins in the sketch will need to be changed and also the #define F_AS_T line will need to be commented out in the "Adafruit_ILI9341_FAST.h" file found inside the "Adafruit_ILI9341_AS" library. When the line is commented out the ATmega328 specific code will not be used so the display updates will be 50% slower.
Eventually I will add speed enhancing code for the Mega as that is likely to be the upgrade path for projects that use large font files!
The libraries and sketch were really developed for my own use and hence may have a few rough edges... but it works for me! If you have problems then report them below and I will try to help... given time!
Warning: This version of the "Adafruit_GFX_AS" has NOT been tested with the other display libraries (S6D02A1 and ST7735) in my other instructables. So please keep a copy of your old library in case this one has an incompatibility! I will be adding compatible hardware drivers to this instructable at some point...
Lastly, there is a comment at line 121 in the sketch describing how to turn a continuous ring into a segmented one.
60 Comments
Bodmer 9 years ago
Following on from FabioB3's comment, I have run the Adafruit "graphicstest" sketch under the original Adafruit_GFX library and my enhanced version. The results are quite interesting:
The speed has been increased to an average of 2.3 times faster. This is an equivalent boost to running a 16MHz UNO at 36MHz!
Of particular note is the "line", triangle outline and circle outline drawing speed boost. This is because the Bresenham algorithm has been enhanced to take advantage of the way the display hardware has to be driven. The line drawing speed increase is the equivalent of running an UNO at 88MHz.
See attached table of results.
Mery YC 8 years ago
Hi, Bodmer
Congratulations again for your projects. The truth is that they are something that I always had in my mind. I wanted to ask for your help again, I'm interested in displaying data in the wattmeter display through input "A0", because in your sketch, the indication is simulated by a sinusoidal signal. It's posible?
BODMER thank you very much, I hope your response as you have always done, congratulations !!!!
Bodmer 8 years ago
reading = analogRead(A0);
Then change the calling function to, for example:
ringMeter(reading, 0, 1023, xpos, ypos, radius, "W", GREEN2RED);
Mery YC 7 years ago
Hi, Bodmer, All Good?
Can you help me again? I am trying one of the examples to make a voltmeter with of your ring put and it would need to visualize a precision value in the center of the display (the information her provides a converter ADC 24Bits "LTC2400") I have the code to monitor the readings of the LTC2400 but only it does it across ARDUINO's serial monitor, but it wanted to visualize it in the own display. Can you help me?
Mery YC 8 years ago
THANKS A LOT BODMER!!!
janrichardbecke 4 months ago
I use last version of IDE.
Bodmer 4 months ago
tmanolidis 2 years ago
joecgz 3 years ago
Joe
joecgz 3 years ago
I wonder if you still are answering questions?
Joe
Joachims_Fotografie 5 years ago
Hardware is:
ESP8266-07 programmed with Arduino IDE
Using this for the ILI9341:
#include <TFT_eSPI.h>;
altaafsayed1 3 years ago
Alessandro87 3 years ago
I would like to know if is possible to color the segment green to a value and red above this value
Thank you
Bodmer 3 years ago
int ringMeter(int value, int vmin, int vmax, int x, int y, int r, char *units, byte scheme)
{
// Minimum value of r is about 52 before value text intrudes on ring
// drawing the text first is an option
x += r; y += r; // Calculate coords of centre of ring
int w = r / 4; // Width of outer ring is 1/4 of radius
int angle = 150; // Half the sweep angle of meter (300 degrees)
int text_colour = 0; // To hold the text colour
int v = map(value, vmin, vmax, -angle, angle); // Map the value to an angle v
int vt = map(50, vmin, vmax, -angle, angle); // Map the threshold value (50 in this case)
byte seg = 5; // Segments are 5 degrees wide = 60 segments for 300 degrees
byte inc = 5; // Draw segments every 5 degrees, increase to 10 for segmented ring
// Draw colour blocks every inc degrees
for (int i = -angle; i < angle; i += inc) {
// Choose colour
int colour = ILI9341_GREEN;
if (i > vt) colour = ILI9341_RED;
// Calculate pair of coordinates for segment start
float sx = cos((i - 90) * 0.0174532925);
float sy = sin((i - 90) * 0.0174532925);
uint16_t x0 = sx * (r - w) + x;
uint16_t y0 = sy * (r - w) + y;
uint16_t x1 = sx * r + x;
uint16_t y1 = sy * r + y;
// Calculate pair of coordinates for segment end
float sx2 = cos((i + seg - 90) * 0.0174532925);
float sy2 = sin((i + seg - 90) * 0.0174532925);
int x2 = sx2 * (r - w) + x;
int y2 = sy2 * (r - w) + y;
int x3 = sx2 * r + x;
int y3 = sy2 * r + y;
if (i < v) { // Fill in coloured segments with 2 triangles
tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);
text_colour = colour; // Save the last colour drawn
}
else // Fill in blank segments
{
tft.fillTriangle(x0, y0, x1, y1, x2, y2, ILI9341_GREY);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, ILI9341_GREY);
}
}
// Convert value to a string
char buf[10];
byte len = 4; if (value > 999) len = 5;
dtostrf(value, len, 0, buf);
// Set the text colour to default
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
// Uncomment next line to set the text colour to the last segment value!
// tft.setTextColor(text_colour, ILI9341_BLACK);
// Print value, if the meter is large then use big font 6, othewise use 4
if (r > 84) tft.drawCentreString(buf, x - 5, y - 20, 6); // Value in middle
else tft.drawCentreString(buf, x - 5, y - 20, 4); // Value in middle
// Print units, if the meter is large then use big font 4, othewise use 2
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
if (r > 84) tft.drawCentreString(units, x, y + 30, 4); // Units display
else tft.drawCentreString(units, x, y + 5, 2); // Units display
// Calculate and return right hand side x coordinate
return x + r;
}
Alessandro87 3 years ago
Liesjek 4 years ago
Amazing project at first!
I have a question, i would like to use it for a air quality meter. But I have an issue with the color scheme. I want to go from red to green to red, so in the middel the colour is green and on the ends is red. I don't know how to do this. Can you help me?
takacs.gergely95 4 years ago
I dont know whether this thred is still alive but I'm stuck.
I'm using an Mega2560 board with and 400*240 TFT Shield probably ILI 9341. And MCUFRIEND libraries and images show up alright but your library just ends up in a blank white screen with no error message et all. Could you please help?
JohnP338 5 years ago
My version, thanks for the idea.
psychlopath 5 years ago
I think you should consider an instructable on your individual project; or at least a write up somewhere.
JohnP338 5 years ago
When I finish and optimize the functions, I will share.