Overview
ESP32 is a SoC micro-controller with WiFi module. It's a low-cost and high speed 32-bit micro-controller suitable for most of hobby and commercial projects. It is very popular in IoT project since it's very to learn and program using various examples and libraries from many developers.
Arduino IDE is very common for most of programmers. PlatformIO plugin for Visual Studio Code is also a second option for experienced programmers. They can program this chip using its effective lower API ESP-IDF that able to access most of its hardware level layers.
Display interfacing is an interesting stuffs for electronic project especially for stand-alone device that has a built-in GUI. There are may newer and low-cost graphical displays especially the TFT display. They come with many physical sizes and screen resolutions, for instance a 128x160RGB, a 240x240RGB and a 240x320RGB etc.
![]() |
| Arduino TFT_eSPI Meter Example |
Some popular TFT LCD controllers the ST7735(128x160RGB), the ST7785(240x320RGB) and the ILI9341(240x320RGB) etc. There are many compatible TFT controllers for these controller chips. For example the ILI9341 and the ST7785 are equivalent.
ESP32 Arduino Programming and Interfacing
Arduino is very easy to use due to its readabilities excessive libraries. There are may TFT libraries for this TFT module especially from the Adafruit industry. However I use the TFT_eSPI libraries since its support many types of display controllers with various micro-controller modules.
I use the ESP32-WROOM-32U and a 2.8" ILI9341 with the XPT2046 resistive touch module to demo the example.
I tested its Meter example using 240x320 TFT display module. Then the "meter.ino" is opened.
- /*
- Example animated analogue meters using a ILI9341 TFT LCD screen
- Needs Font 2 (also Font 4 if using large scale label)
- Make sure all the display driver and pin connections are correct by
- editing the User_Setup.h file in the TFT_eSPI library folder.
- #########################################################################
- ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
- #########################################################################
- */
- #include <TFT_eSPI.h> // Hardware-specific library
- #include <SPI.h>
- TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
- #define TFT_GREY 0x5AEB
- #define LOOP_PERIOD 35 // Display updates every 35 ms
- float ltx = 0; // Saved x coord of bottom of needle
- uint16_t osx = 120, osy = 120; // Saved x & y coords
- uint32_t updateTime = 0; // time for next update
- int old_analog = -999; // Value last displayed
- int old_digital = -999; // Value last displayed
- int value[6] = {0, 0, 0, 0, 0, 0};
- int old_value[6] = { -1, -1, -1, -1, -1, -1};
- int d = 0;
- void setup(void) {
- tft.init();
- tft.setRotation(0);
- Serial.begin(57600); // For debug
- tft.fillScreen(TFT_BLACK);
- analogMeter(); // Draw analogue meter
- // Draw 6 linear meters
- byte d = 40;
- plotLinear("A0", 0, 160);
- plotLinear("A1", 1 * d, 160);
- plotLinear("A2", 2 * d, 160);
- plotLinear("A3", 3 * d, 160);
- plotLinear("A4", 4 * d, 160);
- plotLinear("A5", 5 * d, 160);
- updateTime = millis(); // Next update time
- }
- void loop() {
- if (updateTime <= millis()) {
- updateTime = millis() + LOOP_PERIOD;
- d += 4; if (d >= 360) d = 0;
- //value[0] = map(analogRead(A0), 0, 1023, 0, 100); // Test with value form Analogue 0
- // Create a Sine wave for testing
- value[0] = 50 + 50 * sin((d + 0) * 0.0174532925);
- value[1] = 50 + 50 * sin((d + 60) * 0.0174532925);
- value[2] = 50 + 50 * sin((d + 120) * 0.0174532925);
- value[3] = 50 + 50 * sin((d + 180) * 0.0174532925);
- value[4] = 50 + 50 * sin((d + 240) * 0.0174532925);
- value[5] = 50 + 50 * sin((d + 300) * 0.0174532925);
- //unsigned long t = millis();
- plotPointer();
- plotNeedle(value[0], 0);
- //Serial.println(millis()-t); // Print time taken for meter update
- }
- }
- // #########################################################################
- // Draw the analogue meter on the screen
- // #########################################################################
- void analogMeter()
- {
- // Meter outline
- tft.fillRect(0, 0, 239, 126, TFT_GREY);
- tft.fillRect(5, 3, 230, 119, TFT_WHITE);
- tft.setTextColor(TFT_BLACK); // Text colour
- // Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing)
- for (int i = -50; i < 51; i += 5) {
- // Long scale tick length
- int tl = 15;
- // Coordinates of tick to draw
- float sx = cos((i - 90) * 0.0174532925);
- float sy = sin((i - 90) * 0.0174532925);
- uint16_t x0 = sx * (100 + tl) + 120;
- uint16_t y0 = sy * (100 + tl) + 140;
- uint16_t x1 = sx * 100 + 120;
- uint16_t y1 = sy * 100 + 140;
- // Coordinates of next tick for zone fill
- float sx2 = cos((i + 5 - 90) * 0.0174532925);
- float sy2 = sin((i + 5 - 90) * 0.0174532925);
- int x2 = sx2 * (100 + tl) + 120;
- int y2 = sy2 * (100 + tl) + 140;
- int x3 = sx2 * 100 + 120;
- int y3 = sy2 * 100 + 140;
- // Yellow zone limits
- //if (i >= -50 && i < 0) {
- // tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_YELLOW);
- // tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_YELLOW);
- //}
- // Green zone limits
- if (i >= 0 && i < 25) {
- tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN);
- tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);
- }
- // Orange zone limits
- if (i >= 25 && i < 50) {
- tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE);
- tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE);
- }
- // Short scale tick length
- if (i % 25 != 0) tl = 8;
- // Recalculate coords incase tick lenght changed
- x0 = sx * (100 + tl) + 120;
- y0 = sy * (100 + tl) + 140;
- x1 = sx * 100 + 120;
- y1 = sy * 100 + 140;
- // Draw tick
- tft.drawLine(x0, y0, x1, y1, TFT_BLACK);
- // Check if labels should be drawn, with position tweaks
- if (i % 25 == 0) {
- // Calculate label positions
- x0 = sx * (100 + tl + 10) + 120;
- y0 = sy * (100 + tl + 10) + 140;
- switch (i / 25) {
- case -2: tft.drawCentreString("0", x0, y0 - 12, 2); break;
- case -1: tft.drawCentreString("25", x0, y0 - 9, 2); break;
- case 0: tft.drawCentreString("50", x0, y0 - 6, 2); break;
- case 1: tft.drawCentreString("75", x0, y0 - 9, 2); break;
- case 2: tft.drawCentreString("100", x0, y0 - 12, 2); break;
- }
- }
- // Now draw the arc of the scale
- sx = cos((i + 5 - 90) * 0.0174532925);
- sy = sin((i + 5 - 90) * 0.0174532925);
- x0 = sx * 100 + 120;
- y0 = sy * 100 + 140;
- // Draw scale arc, don't draw the last part
- if (i < 50) tft.drawLine(x0, y0, x1, y1, TFT_BLACK);
- }
- tft.drawString("%RH", 5 + 230 - 40, 119 - 20, 2); // Units at bottom right
- tft.drawCentreString("%RH", 120, 70, 4); // Comment out to avoid font 4
- tft.drawRect(5, 3, 230, 119, TFT_BLACK); // Draw bezel line
- plotNeedle(0, 0); // Put meter needle at 0
- }
- // #########################################################################
- // Update needle position
- // This function is blocking while needle moves, time depends on ms_delay
- // 10ms minimises needle flicker if text is drawn within needle sweep area
- // Smaller values OK if text not in sweep area, zero for instant movement but
- // does not look realistic... (note: 100 increments for full scale deflection)
- // #########################################################################
- void plotNeedle(int value, byte ms_delay)
- {
- tft.setTextColor(TFT_BLACK, TFT_WHITE);
- char buf[8]; dtostrf(value, 4, 0, buf);
- tft.drawRightString(buf, 40, 119 - 20, 2);
- if (value < -10) value = -10; // Limit value to emulate needle end stops
- if (value > 110) value = 110;
- // Move the needle util new value reached
- while (!(value == old_analog)) {
- if (old_analog < value) old_analog++;
- else old_analog--;
- if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0
- float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle
- // Calculate tip of needle coords
- float sx = cos(sdeg * 0.0174532925);
- float sy = sin(sdeg * 0.0174532925);
- // Calculate x delta of needle start (does not start at pivot point)
- float tx = tan((sdeg + 90) * 0.0174532925);
- // Erase old needle image
- tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_WHITE);
- tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_WHITE);
- tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_WHITE);
- // Re-plot text under needle
- tft.setTextColor(TFT_BLACK);
- tft.drawCentreString("%RH", 120, 70, 4); // // Comment out to avoid font 4
- // Store new needle end coords for next erase
- ltx = tx;
- osx = sx * 98 + 120;
- osy = sy * 98 + 140;
- // Draw the needle in the new postion, magenta makes needle a bit bolder
- // draws 3 lines to thicken needle
- tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_RED);
- tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_MAGENTA);
- tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_RED);
- // Slow needle down slightly as it approaches new postion
- if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5;
- // Wait before next update
- delay(ms_delay);
- }
- }
- // #########################################################################
- // Draw a linear meter on the screen
- // #########################################################################
- void plotLinear(char *label, int x, int y)
- {
- int w = 36;
- tft.drawRect(x, y, w, 155, TFT_GREY);
- tft.fillRect(x + 2, y + 19, w - 3, 155 - 38, TFT_WHITE);
- tft.setTextColor(TFT_CYAN, TFT_BLACK);
- tft.drawCentreString(label, x + w / 2, y + 2, 2);
- for (int i = 0; i < 110; i += 10)
- {
- tft.drawFastHLine(x + 20, y + 27 + i, 6, TFT_BLACK);
- }
- for (int i = 0; i < 110; i += 50)
- {
- tft.drawFastHLine(x + 20, y + 27 + i, 9, TFT_BLACK);
- }
- tft.fillTriangle(x + 3, y + 127, x + 3 + 16, y + 127, x + 3, y + 127 - 5, TFT_RED);
- tft.fillTriangle(x + 3, y + 127, x + 3 + 16, y + 127, x + 3, y + 127 + 5, TFT_RED);
- tft.drawCentreString("---", x + w / 2, y + 155 - 18, 2);
- }
- // #########################################################################
- // Adjust 6 linear meter pointer positions
- // #########################################################################
- void plotPointer(void)
- {
- int dy = 187;
- byte pw = 16;
- tft.setTextColor(TFT_GREEN, TFT_BLACK);
- // Move the 6 pointers one pixel towards new value
- for (int i = 0; i < 6; i++)
- {
- char buf[8]; dtostrf(value[i], 4, 0, buf);
- tft.drawRightString(buf, i * 40 + 36 - 5, 187 - 27 + 155 - 18, 2);
- int dx = 3 + 40 * i;
- if (value[i] < 0) value[i] = 0; // Limit value to emulate needle end stops
- if (value[i] > 100) value[i] = 100;
- while (!(value[i] == old_value[i])) {
- dy = 187 + 100 - old_value[i];
- if (old_value[i] > value[i])
- {
- tft.drawLine(dx, dy - 5, dx + pw, dy, TFT_WHITE);
- old_value[i]--;
- tft.drawLine(dx, dy + 6, dx + pw, dy + 1, TFT_RED);
- }
- else
- {
- tft.drawLine(dx, dy + 5, dx + pw, dy, TFT_WHITE);
- old_value[i]++;
- tft.drawLine(dx, dy - 6, dx + pw, dy - 1, TFT_RED);
- }
- }
- }
- }
This libraries is compiled for the ESP8266 by default. So I need to modify its its User_Setup.h library header file in the TFT_eSPI library folder as follow.
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
#define TFT_INVERSION_OFF
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
#define TFT_MISO PIN_D6 // Automatically assigned with ESP8266 if not defined
#define TFT_MOSI PIN_D7 // Automatically assigned with ESP8266 if not defined
#define TFT_SCLK PIN_D5 // Automatically assigned with ESP8266 if not defined
#define TFT_CS PIN_D8 // Chip select control pin D8
#define TFT_DC PIN_D3 // Data Command control pin
#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
I need to un-commend other unused definitions.Then I need to save this file.




No comments:
Post a Comment