Arduino Analogue 'ring' Meter on Colour TFT Display




Introduction: 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.

    7 People Made This Project!


    • Stick It Challenge

      Stick It Challenge
    • Science Fair Challenge

      Science Fair Challenge
    • Electronics Contest

      Electronics Contest



    7 years ago on Introduction

    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.

    Test results.png
    Mery YC
    Mery YC

    Reply 6 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 !!!!


    Reply 6 years ago

    Try code like this:

    reading = analogRead(A0);

    Then change the calling function to, for example:

    ringMeter(reading, 0, 1023, xpos, ypos, radius, "W", GREEN2RED);

    Mery YC
    Mery YC

    Reply 5 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
    Mery YC

    Reply 6 years ago



    1 year ago

    I wonder if you are still taking questions about the Ring Meter?


    1 year ago

    I have downloaded your Ring meter and the test setup runs and displays OK
    I wonder if you still are answering questions?



    Question 3 years ago on Introduction

    Thank you for this great work, but i have a problem to write correct values. See the Picture, Both Values in the center of Ring Meters. The Value is Temperature from two MAX6675 K-Type Sensors. If the value was greater than 100 and goes back under 100, it shows et the end a half Number. Have you a idea or a solution ? Best regards, Daniel Joachims

    Hardware is:
    ESP8266-07 programmed with Arduino IDE
    Using this for the ILI9341:

    #include <TFT_eSPI.h>;


    Answer 1 year ago

    Hi. Did you ever find the solution to this? I am having the same problem.


    1 year ago

    Hi, amazing work
    I would like to know if is possible to color the segment green to a value and red above this value
    Thank you


    Reply 1 year ago

    Try this modified function. I have added a new variable vt and set the threshold for colour change to 50.

    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;


    Question 2 years ago

    Hi Bodmer,

    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?


    2 years ago

    Dear Bodmer
    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?


    3 years ago

    My version, thanks for the idea.


    Reply 3 years ago

    I think you should consider an instructable on your individual project; or at least a write up somewhere.


    Reply 3 years ago

    When I finish and optimize the functions, I will share.


    Reply 3 years ago

    Hi, My name is Vladimir.
    Can you share your ring meter´s sketch in Arduino?
    Thank so much.


    4 years ago

    Hi Bodmer,

    Thank you very much for this work, its awesome,

    i managed to make this code and library to work on 1.8" TFT ST7735,(160X128) and i want to use it to read from sensor A0, i have a problem with the meter (gauge) position, the value is like you write from right to left, not upside down no, its like the left is right and the right is left, is there any way to fix this from library or the code itself.

    please check the attached picture

    Thank you very much and looking forward to hear form you.