728x90

Showing posts with label Arduino. Show all posts
Showing posts with label Arduino. Show all posts

Tuesday, January 20, 2026

ESP32 ILI9341 2.8 inches TFT Example

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.

ESP32 ILI9341 2.8 inches TFT Example
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.

 

ESP32 ILI9341 2.8 inches TFT Example

I tested its Meter example using 240x320 TFT display module. Then the "meter.ino" is opened.

  1. /*
  2. Example animated analogue meters using a ILI9341 TFT LCD screen

  3. Needs Font 2 (also Font 4 if using large scale label)

  4. Make sure all the display driver and pin connections are correct by
  5. editing the User_Setup.h file in the TFT_eSPI library folder.

  6. #########################################################################
  7. ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
  8. #########################################################################
  9. */

  10. #include <TFT_eSPI.h> // Hardware-specific library
  11. #include <SPI.h>

  12. TFT_eSPI tft = TFT_eSPI(); // Invoke custom library

  13. #define TFT_GREY 0x5AEB

  14. #define LOOP_PERIOD 35 // Display updates every 35 ms

  15. float ltx = 0; // Saved x coord of bottom of needle
  16. uint16_t osx = 120, osy = 120; // Saved x & y coords
  17. uint32_t updateTime = 0; // time for next update

  18. int old_analog = -999; // Value last displayed
  19. int old_digital = -999; // Value last displayed

  20. int value[6] = {0, 0, 0, 0, 0, 0};
  21. int old_value[6] = { -1, -1, -1, -1, -1, -1};
  22. int d = 0;

  23. void setup(void) {
  24. tft.init();
  25. tft.setRotation(0);
  26. Serial.begin(57600); // For debug
  27. tft.fillScreen(TFT_BLACK);

  28. analogMeter(); // Draw analogue meter

  29. // Draw 6 linear meters
  30. byte d = 40;
  31. plotLinear("A0", 0, 160);
  32. plotLinear("A1", 1 * d, 160);
  33. plotLinear("A2", 2 * d, 160);
  34. plotLinear("A3", 3 * d, 160);
  35. plotLinear("A4", 4 * d, 160);
  36. plotLinear("A5", 5 * d, 160);

  37. updateTime = millis(); // Next update time
  38. }


  39. void loop() {
  40. if (updateTime <= millis()) {
  41. updateTime = millis() + LOOP_PERIOD;

  42. d += 4; if (d >= 360) d = 0;

  43. //value[0] = map(analogRead(A0), 0, 1023, 0, 100); // Test with value form Analogue 0

  44. // Create a Sine wave for testing
  45. value[0] = 50 + 50 * sin((d + 0) * 0.0174532925);
  46. value[1] = 50 + 50 * sin((d + 60) * 0.0174532925);
  47. value[2] = 50 + 50 * sin((d + 120) * 0.0174532925);
  48. value[3] = 50 + 50 * sin((d + 180) * 0.0174532925);
  49. value[4] = 50 + 50 * sin((d + 240) * 0.0174532925);
  50. value[5] = 50 + 50 * sin((d + 300) * 0.0174532925);

  51. //unsigned long t = millis();

  52. plotPointer();

  53. plotNeedle(value[0], 0);

  54. //Serial.println(millis()-t); // Print time taken for meter update
  55. }
  56. }


  57. // #########################################################################
  58. // Draw the analogue meter on the screen
  59. // #########################################################################
  60. void analogMeter()
  61. {
  62. // Meter outline
  63. tft.fillRect(0, 0, 239, 126, TFT_GREY);
  64. tft.fillRect(5, 3, 230, 119, TFT_WHITE);

  65. tft.setTextColor(TFT_BLACK); // Text colour

  66. // Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing)
  67. for (int i = -50; i < 51; i += 5) {
  68. // Long scale tick length
  69. int tl = 15;

  70. // Coordinates of tick to draw
  71. float sx = cos((i - 90) * 0.0174532925);
  72. float sy = sin((i - 90) * 0.0174532925);
  73. uint16_t x0 = sx * (100 + tl) + 120;
  74. uint16_t y0 = sy * (100 + tl) + 140;
  75. uint16_t x1 = sx * 100 + 120;
  76. uint16_t y1 = sy * 100 + 140;

  77. // Coordinates of next tick for zone fill
  78. float sx2 = cos((i + 5 - 90) * 0.0174532925);
  79. float sy2 = sin((i + 5 - 90) * 0.0174532925);
  80. int x2 = sx2 * (100 + tl) + 120;
  81. int y2 = sy2 * (100 + tl) + 140;
  82. int x3 = sx2 * 100 + 120;
  83. int y3 = sy2 * 100 + 140;

  84. // Yellow zone limits
  85. //if (i >= -50 && i < 0) {
  86. // tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_YELLOW);
  87. // tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_YELLOW);
  88. //}

  89. // Green zone limits
  90. if (i >= 0 && i < 25) {
  91. tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN);
  92. tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);
  93. }

  94. // Orange zone limits
  95. if (i >= 25 && i < 50) {
  96. tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE);
  97. tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE);
  98. }

  99. // Short scale tick length
  100. if (i % 25 != 0) tl = 8;

  101. // Recalculate coords incase tick lenght changed
  102. x0 = sx * (100 + tl) + 120;
  103. y0 = sy * (100 + tl) + 140;
  104. x1 = sx * 100 + 120;
  105. y1 = sy * 100 + 140;

  106. // Draw tick
  107. tft.drawLine(x0, y0, x1, y1, TFT_BLACK);

  108. // Check if labels should be drawn, with position tweaks
  109. if (i % 25 == 0) {
  110. // Calculate label positions
  111. x0 = sx * (100 + tl + 10) + 120;
  112. y0 = sy * (100 + tl + 10) + 140;
  113. switch (i / 25) {
  114. case -2: tft.drawCentreString("0", x0, y0 - 12, 2); break;
  115. case -1: tft.drawCentreString("25", x0, y0 - 9, 2); break;
  116. case 0: tft.drawCentreString("50", x0, y0 - 6, 2); break;
  117. case 1: tft.drawCentreString("75", x0, y0 - 9, 2); break;
  118. case 2: tft.drawCentreString("100", x0, y0 - 12, 2); break;
  119. }
  120. }

  121. // Now draw the arc of the scale
  122. sx = cos((i + 5 - 90) * 0.0174532925);
  123. sy = sin((i + 5 - 90) * 0.0174532925);
  124. x0 = sx * 100 + 120;
  125. y0 = sy * 100 + 140;
  126. // Draw scale arc, don't draw the last part
  127. if (i < 50) tft.drawLine(x0, y0, x1, y1, TFT_BLACK);
  128. }

  129. tft.drawString("%RH", 5 + 230 - 40, 119 - 20, 2); // Units at bottom right
  130. tft.drawCentreString("%RH", 120, 70, 4); // Comment out to avoid font 4
  131. tft.drawRect(5, 3, 230, 119, TFT_BLACK); // Draw bezel line

  132. plotNeedle(0, 0); // Put meter needle at 0
  133. }

  134. // #########################################################################
  135. // Update needle position
  136. // This function is blocking while needle moves, time depends on ms_delay
  137. // 10ms minimises needle flicker if text is drawn within needle sweep area
  138. // Smaller values OK if text not in sweep area, zero for instant movement but
  139. // does not look realistic... (note: 100 increments for full scale deflection)
  140. // #########################################################################
  141. void plotNeedle(int value, byte ms_delay)
  142. {
  143. tft.setTextColor(TFT_BLACK, TFT_WHITE);
  144. char buf[8]; dtostrf(value, 4, 0, buf);
  145. tft.drawRightString(buf, 40, 119 - 20, 2);

  146. if (value < -10) value = -10; // Limit value to emulate needle end stops
  147. if (value > 110) value = 110;

  148. // Move the needle util new value reached
  149. while (!(value == old_analog)) {
  150. if (old_analog < value) old_analog++;
  151. else old_analog--;

  152. if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0

  153. float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle
  154. // Calculate tip of needle coords
  155. float sx = cos(sdeg * 0.0174532925);
  156. float sy = sin(sdeg * 0.0174532925);

  157. // Calculate x delta of needle start (does not start at pivot point)
  158. float tx = tan((sdeg + 90) * 0.0174532925);

  159. // Erase old needle image
  160. tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_WHITE);
  161. tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_WHITE);
  162. tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_WHITE);

  163. // Re-plot text under needle
  164. tft.setTextColor(TFT_BLACK);
  165. tft.drawCentreString("%RH", 120, 70, 4); // // Comment out to avoid font 4

  166. // Store new needle end coords for next erase
  167. ltx = tx;
  168. osx = sx * 98 + 120;
  169. osy = sy * 98 + 140;

  170. // Draw the needle in the new postion, magenta makes needle a bit bolder
  171. // draws 3 lines to thicken needle
  172. tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_RED);
  173. tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_MAGENTA);
  174. tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_RED);

  175. // Slow needle down slightly as it approaches new postion
  176. if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5;

  177. // Wait before next update
  178. delay(ms_delay);
  179. }
  180. }

  181. // #########################################################################
  182. // Draw a linear meter on the screen
  183. // #########################################################################
  184. void plotLinear(char *label, int x, int y)
  185. {
  186. int w = 36;
  187. tft.drawRect(x, y, w, 155, TFT_GREY);
  188. tft.fillRect(x + 2, y + 19, w - 3, 155 - 38, TFT_WHITE);
  189. tft.setTextColor(TFT_CYAN, TFT_BLACK);
  190. tft.drawCentreString(label, x + w / 2, y + 2, 2);

  191. for (int i = 0; i < 110; i += 10)
  192. {
  193. tft.drawFastHLine(x + 20, y + 27 + i, 6, TFT_BLACK);
  194. }

  195. for (int i = 0; i < 110; i += 50)
  196. {
  197. tft.drawFastHLine(x + 20, y + 27 + i, 9, TFT_BLACK);
  198. }

  199. tft.fillTriangle(x + 3, y + 127, x + 3 + 16, y + 127, x + 3, y + 127 - 5, TFT_RED);
  200. tft.fillTriangle(x + 3, y + 127, x + 3 + 16, y + 127, x + 3, y + 127 + 5, TFT_RED);

  201. tft.drawCentreString("---", x + w / 2, y + 155 - 18, 2);
  202. }

  203. // #########################################################################
  204. // Adjust 6 linear meter pointer positions
  205. // #########################################################################
  206. void plotPointer(void)
  207. {
  208. int dy = 187;
  209. byte pw = 16;

  210. tft.setTextColor(TFT_GREEN, TFT_BLACK);

  211. // Move the 6 pointers one pixel towards new value
  212. for (int i = 0; i < 6; i++)
  213. {
  214. char buf[8]; dtostrf(value[i], 4, 0, buf);
  215. tft.drawRightString(buf, i * 40 + 36 - 5, 187 - 27 + 155 - 18, 2);

  216. int dx = 3 + 40 * i;
  217. if (value[i] < 0) value[i] = 0; // Limit value to emulate needle end stops
  218. if (value[i] > 100) value[i] = 100;

  219. while (!(value[i] == old_value[i])) {
  220. dy = 187 + 100 - old_value[i];
  221. if (old_value[i] > value[i])
  222. {
  223. tft.drawLine(dx, dy - 5, dx + pw, dy, TFT_WHITE);
  224. old_value[i]--;
  225. tft.drawLine(dx, dy + 6, dx + pw, dy + 1, TFT_RED);
  226. }
  227. else
  228. {
  229. tft.drawLine(dx, dy + 5, dx + pw, dy, TFT_WHITE);
  230. old_value[i]++;
  231. tft.drawLine(dx, dy - 6, dx + pw, dy - 1, TFT_RED);
  232. }
  233. }
  234. }
  235. }



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. 

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

 

There are many examples in the TFT_eSPI library. Here I tested some demo programs.

 

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

ESP32 ILI9341 2.8 inches TFT Example

Tuesday, July 5, 2022

A Simple Two-Wire LCD Programming With Arduino Uno

In this example, I use PCF8574T module that designed for character LCD interfacing. We can solder this module directly with the LCD. However I put either PCF8574T module and the LCD on a single breadboard.

A Simple Two-Wire LCD Programming With Arduino Uno

Running program

 The connection between the Arduino Uno and the two-wire display module uses only two communication wires, and two power supply pins.

  1. GND
  2. +5V (VDD)
  3. SCL connects to Arduino pin A5
  4. SDA connects to Arduino pin A4

It need the LiquidCrystal_I2C.h file that we need to install it. The program will show a simple text display with a little delay between each characters.

  1. //Two-Wire Library
  2. #include <Wire.h>
  3. #include <LiquidCrystal_I2C.h>
  4.  
  5. //LCD Setting
  6. LiquidCrystal_I2C lcd(0x27,16,2);
  7.  
  8. void setup(){
  9. //Initialize the LCD
  10. lcd.init();
  11. //Turn on the back light
  12. lcd.backlight();
  13. //Blink the cursor
  14. lcd.blink();
  15.  
  16. lcdShow("AKI Technical");
  17. lcd.setCursor(0,1);
  18. lcdShow("YouTube Channel");
  19. }
  20.  
  21. void lcdShow(char *txt){
  22. while(*txt){
  23. lcd.print(*txt++);
  24. delay(500);
  25. }
  26. }
  27.  
  28. void loop(){
  29.  
  30. }
  31.  

 Click here to download its source file.

We can use the ATMega32 to control a 3-Wire SPI LCD module (SN74HC595N chip).

Wednesday, March 3, 2021

Reading the temperature from DS18B20 one-wire digital thermometer

Overview Of DS18B20

DS18B20 is a digital interface temperature sensor from Maxim Integrated. It has only one wire digital data input output, with two additional supply voltage pins. The temperature conversion from this device ranges from -55 degree Celsius to +125 degree Celsius. 
 
Digital temperature resolution is select-able between 9 to 12-bit. Within these 12-bit digital data, it consists of signed value, decimal temperature value and a 4-bit fraction numbers.

Reading the temperature from DS18B20 one-wire digital thermometer
A temperature reading from DS18B20 strobe-type.

 
Using only one wire digital data line, we can use multiple devices on this single wire. Each device is identified by its serial number and family code.

The conventional package and pin diagram of DS18B20

Within three pins of this device, the data DQ pin is open-drain. Hence it needed to pull up high via a resistor about 4.7 kOhm of resistance.

Typical connection to MCU

In some case, it doesn't require VDD.

Arduino Interfaces To DS18B20 With OneWire Library

Using the OneWire library developed by an author on Github, reading the device ID and temperature from this device family save a lot of time.
 
Installed library for OneWire
 
This library allow us to search for ROM. ROM identifies the devices connect on one wire bus. So we can use as much as one wire devices on a single line.

There are many function that I don't list them here. The example programs come this device could explain a lot about the overall processes.

In this example, I modified the device search and temperature reading to display their information on a SPI character LCD I made.

Reading the temperature from DS18B20 one-wire digital thermometer
Schematic Diagram For This Example

Arduino code lists below.

#include <OneWire.h>
#include <ShiftedLCD.h>
#include <SPI.h>

//OneWire Object Connect To Pin 2
OneWire  ds(2);

//Select pin 8 for Enable pin
LiquidCrystal lcd(8);

String dsString="";

void setup(void) {
  Serial.begin(9600);

  lcd.begin(16,4);
  lcd.clear();
 
}

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
 
  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }
 
  if (OneWire::crc8(addr, 7) != addr[7]) {
      return;
  }
 
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
     
      type_s = 1;
      dsString="DS18S20";
      break;
    case 0x28:
     
      dsString="DS18B20";
      type_s = 0;
      break;
    case 0x22:
     
      dsString="DS1820";
      type_s = 0;
      break;
    default:
     
      dsString="N/A";
      return;
  }

  lcd.setCursor(0,0);
  lcd.print("Sensor: "+dsString);
 
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
 
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
 
  present = ds.reset();
  ds.select(addr);   
  ds.write(0xBE);         // Read Scratchpad

 
 
 

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
 
  lcd.setCursor(0,1);
  lcd.print("Temperature:");
  lcd.setCursor(-4,2);
  //lcd.print(String(celsius)+char(223)+"C" +" " + String(fahrenheit)+char(223)+"C");
  lcd.print(String(celsius)+char(223)+"C");
  lcd.setCursor(-4,3);
  lcd.print(String(fahrenheit)+char(223)+"F");
  delay(1000);
}


Arduino sketch could download here.

Search This Blog

Labels

23K256 (1) 24C16B (2) 25AA010A (2) 8051 (7) 93AA46B (1) ADC (34) Analog Comparator (1) Arduino (16) ARM (13) AT89C52 (7) ATMega32 (58) ATMega644P (27) AVR (87) Bootloader (1) CCS PICC (31) Change Notify Interrupt (CNI) (2) DAC (2) DHT11 (4) Display (133) Distance Sensor (3) ds1307 (10) DS18B20 (5) ds3231 (1) ds3232 (2) dsPIC (5) dsPIC30F1010 (3) dsPIC30F2010 (5) EEPROM (5) Environment Sensor (5) ESP32 (1) esp8266 (1) Graphical LCD (2) I2C (38) ILI9341 (1) Input/Output (75) Interrupt (22) Keil (5) Keypad (16) KS0108 (2) LCD (75) LM35 (3) Master/Slave (2) MAX7221 (1) MCP23017 (8) MCP23S17 (7) MCP4921 (1) MCP4922 (2) Meter (3) MikroC (2) Motor (15) MPLABX (73) Nokia 5110 LCD (4) OLED (2) One-Wire (7) Oscillator (8) PCB (10) PCD8544 (3) PCF8574 (10) PIC (108) PIC12F (3) PIC16F628A (3) PIC16F630 (2) PIC16F716 (4) PIC16F818 (11) PIC16F818/819 (3) PIC16F84A (16) PIC16F876A (2) PIC16F877A (9) PIC16F88 (2) PIC16F887 (60) PIC18 (19) PIC18F1220 (5) PIC18F2550 (5) PIC18F4550 (12) PICKit2 (1) Pin Change Interrupt (1) PWM (12) RTC (12) SBN0064G (1) Sensor (13) SH1106 (3) Shift Register (13) Shift Registers (10) Software TWI (2) SPI (39) ST7735 (1) STM32 (11) STM32 Black Pill (1) STM32 Blue Pill (12) STM32 HAL (5) STM32CubeIDE (13) STM32F103C8T6 (9) STM32F401CCU6 (1) SysTick (4) temperature sensor (13) TFT (1) TG12864A (1) Thermometer (22) Timer/Counter (32) TM1637 (2) twi (10) UART (8) Ultrasonic (4) Voltmeter (7) WDT (1) XC16 (2) XC8 (96)