{"id":161067,"date":"2024-08-07T10:27:09","date_gmt":"2024-08-07T10:27:09","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=161067"},"modified":"2024-09-02T15:01:59","modified_gmt":"2024-09-02T15:01:59","slug":"esp32-cheap-yellow-display-cyd-resistive-touchscreen-calibration","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-cheap-yellow-display-cyd-resistive-touchscreen-calibration\/","title":{"rendered":"ESP32 Cheap Yellow Display (CYD) Resistive Touchscreen Calibration"},"content":{"rendered":"\n<p>In this guide, we&#8217;ll show you how to calibrate the ESP32 Cheap Yellow Display (CYD) Touchscreen. This calibration method also applies to other touchscreen displays. Our codes are compatible with resistive touchscreens only.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Cheap Yellow Display CYD Resistive Touchscreen Calibration\" class=\"wp-image-161080\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?w=1920&amp;quality=100&amp;strip=all&amp;ssl=1 1920w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?resize=1536%2C864&amp;quality=100&amp;strip=all&amp;ssl=1 1536w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntclgray\"><strong>Note:<\/strong> this procedure was tested with the <a href=\"https:\/\/makeradvisor.com\/tools\/cyd-cheap-yellow-display-esp32-2432s028r\/\" title=\"\">ESP32 Cheap Yellow Display (ESP32-2432S028R)<\/a> and the <a href=\"https:\/\/makeradvisor.com\/tools\/2-8-inch-ili9341-tft-240x320\/\" title=\"\">2.8 inch ILI9341 TFT LCD Touchscreen<\/a>, but can be applied to different touchscreen displays with a few modifications in the sketches.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quick Overview<\/h2>\n\n\n\n<p>Most of the time, your touchscreen might not be precisely calibrated by default and that\u2019s normal.<\/p>\n\n\n\n<p>Usually, the x and y axis are deviated just a few pixels and that won\u2019t be noticeable when using larger widgets like big buttons for example. However, when more precision is required, like when using a keyboard with smaller keys, the calibration is a critical aspect.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"474\" height=\"330\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/uncalibrated-touchscreen.png?resize=474%2C330&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Calibrating a touchscreen display\" class=\"wp-image-161077\" style=\"width:468px;height:auto\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/uncalibrated-touchscreen.png?w=474&amp;quality=100&amp;strip=all&amp;ssl=1 474w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/uncalibrated-touchscreen.png?resize=300%2C209&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><figcaption class=\"wp-element-caption\"><a href=\"https:\/\/www.ti.com\/lit\/an\/slyt277\/slyt277.pdf\" target=\"_blank\" rel=\"noopener\" title=\"\"><em>Image source<\/em><\/a><\/figcaption><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">The Calibration Method<\/h3>\n\n\n\n<p>In this Unit, we\u2019ll show you how to calibrate your touchscreen using the methodology described in the following paper:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.ti.com\/lit\/an\/slyt277\/slyt277.pdf\" target=\"_blank\" rel=\"noopener\" title=\"\">\u201cCalibration in touch-screen systems\u201d by Wendy Fang and Tony Chang<\/a><\/li>\n<\/ul>\n\n\n\n<p>The Arduino sketch we\u2019ll use was adapted from the code developed by one of our readers, <em>Robert Fleming<\/em>. You can check <a href=\"https:\/\/github.com\/CF20852\/ESP32-2432S028-Touchscreen-Calibration\" target=\"_blank\" rel=\"noopener\" title=\"\">his original work<\/a>.<\/p>\n\n\n\n<p>Calibrating the touchscreen involves determining the values of six different coefficients that will then be used in calibration equations to determine the real value of the (x,y) coordinates.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>Before proceeding, make sure you follow the next prerequisites.<\/p>\n\n\n\n<p class=\"rntbox rntclgray\"><strong>New to the ESP32 CYD Board?<\/strong> <a href=\"https:\/\/randomnerdtutorials.com\/cheap-yellow-display-esp32-2432s028r\/\" target=\"_blank\" rel=\"noopener\" title=\"\">Get Started here with the ESP32 Cheap Yellow Display<\/a>.<\/p>\n\n\n\n<p><strong>1) <\/strong>We&#8217;ll use the<strong> LVGL Library<\/strong> for the calibration procedure. Make sure you set up the <strong>LVGL<\/strong>, <strong>TFT_eSPI <\/strong>and <strong>XPT2046_Touchscreen<\/strong> libraries properly as explained in one of the following tutorials:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/lvgl-cheap-yellow-display-esp32-2432s028r\/\" title=\"\">Getting Started with LVGL (ESP32 Cheap Yellow Display)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/lvgl-esp32-tft-touchscreen-display-ili9341-arduino\/\" title=\"\">Getting Started with LVGL (2.8 inch ILI9341 TFT LCD Touchscreen)<\/a><\/li>\n<\/ul>\n\n\n\n<p><strong>2)<\/strong> To calculate and determine the values of the calibration coefficients, you&#8217;ll need to install the <strong>BasicLinearAlgebra<\/strong> library.<\/p>\n\n\n\n<p>In the Arduino IDE, go to <strong>Sketch<\/strong> &gt; <strong>Library<\/strong> &gt; <strong>Include Libraries<\/strong>. Search for <strong>BasicLinearAlgebra<\/strong> and install the library by Tom Stewart. We tested the sketch using the library version 5.1.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"447\" height=\"351\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/installing-basiclinearalgebra-library-Arduino-IDE.png?resize=447%2C351&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"installing basic linear algebra library arduino ide\" class=\"wp-image-161070\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/installing-basiclinearalgebra-library-Arduino-IDE.png?w=447&amp;quality=100&amp;strip=all&amp;ssl=1 447w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/installing-basiclinearalgebra-library-Arduino-IDE.png?resize=300%2C236&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 447px) 100vw, 447px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">The Calibration Sketch<\/h2>\n\n\n\n<p>Run the following code on your board.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*  \n    Example created by Robert (Chip) Fleming for touchscreen calibration (https:\/\/github.com\/CF20852\/ESP32-2432S028-Touchscreen-Calibration)\n    Rui Santos &amp; Sara Santos - Random Nerd Tutorials - https:\/\/RandomNerdTutorials.com\/touchscreen-calibration\/\n    THIS EXAMPLE WAS TESTED WITH THE FOLLOWING HARDWARE:\n    1) ESP32-2432S028R 2.8 inch 240\u00d7320 also known as the Cheap Yellow Display (CYD): https:\/\/makeradvisor.com\/tools\/cyd-cheap-yellow-display-esp32-2432s028r\/\n      SET UP INSTRUCTIONS: https:\/\/RandomNerdTutorials.com\/cyd-lvgl\/\n    2) REGULAR ESP32 Dev Board + 2.8 inch 240x320 TFT Display: https:\/\/makeradvisor.com\/tools\/2-8-inch-ili9341-tft-240x320\/ and https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\n      SET UP INSTRUCTIONS: https:\/\/RandomNerdTutorials.com\/esp32-tft-lvgl\/\n    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n\n\/*  Install the &quot;lvgl&quot; library version 9.2 by kisvegabor to interface with the TFT Display - https:\/\/lvgl.io\/\n    *** IMPORTANT: lv_conf.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***\n    *** YOU MUST USE THE lv_conf.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***\n    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https:\/\/RandomNerdTutorials.com\/cyd-lvgl\/ or https:\/\/RandomNerdTutorials.com\/esp32-tft-lvgl\/   *\/\n#include &lt;lvgl.h&gt;\n\n\/*  Install the &quot;TFT_eSPI&quot; library by Bodmer to interface with the TFT Display - https:\/\/github.com\/Bodmer\/TFT_eSPI\n    *** IMPORTANT: User_Setup.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***\n    *** YOU MUST USE THE User_Setup.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***\n    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https:\/\/RandomNerdTutorials.com\/cyd-lvgl\/ or https:\/\/RandomNerdTutorials.com\/esp32-tft-lvgl\/   *\/\n#include &lt;TFT_eSPI.h&gt;\n\n\/\/ Install the &quot;XPT2046_Touchscreen&quot; library by Paul Stoffregen to use the Touchscreen - https:\/\/github.com\/PaulStoffregen\/XPT2046_Touchscreen - Note: this library doesn't require further configuration\n#include &lt;XPT2046_Touchscreen.h&gt;\n\n\/\/ Install the Basic Linear Algebra library for TI appnote Equation 7 calculations\n#include &lt;BasicLinearAlgebra.h&gt;\n\n\/\/ Touchscreen pins\n#define XPT2046_IRQ 36   \/\/ T_IRQ\n#define XPT2046_MOSI 32  \/\/ T_DIN\n#define XPT2046_MISO 39  \/\/ T_OUT\n#define XPT2046_CLK 25   \/\/ T_CLK\n#define XPT2046_CS 33    \/\/ T_CS\n\nunsigned long delay_start = 0;\n#define DELAY_1S 1000\n#define DELAY_5S 5000\n\nSPIClass touchscreenSPI = SPIClass(VSPI);\nXPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);\n\n#define SCREEN_WIDTH 240\n#define SCREEN_HEIGHT 320\n\nString s;\n\nint ts_points[6][2];\n\n\/* define the screen points where touch samples will be taken *\/\nconst int scr_points[6][2] = { {13, 11}, {20, 220}, {167, 60}, {155, 180}, {300, 13}, {295, 225} };\n\nstruct point {\n  int x;\n  int y;\n};\n\n\/* pS is a screen point; pT is a resistive touchscreen point *\/\nstruct point aS = {scr_points[0][0], scr_points[0][1] };\nstruct point bS = {scr_points[1][0], scr_points[1][1] };\nstruct point cS = {scr_points[2][0], scr_points[2][1] };\nstruct point dS = {scr_points[3][0], scr_points[3][1] };\nstruct point eS = {scr_points[4][0], scr_points[4][1] };\nstruct point fS = {scr_points[5][0], scr_points[5][1] };\n\nstruct point aT;\nstruct point bT;\nstruct point cT;\nstruct point dT;\nstruct point eT;\nstruct point fT;\n\n\/* coefficients for transforming the X and Y coordinates of the resistive touchscreen\n   to display coordinates... the ones with the &quot;pref_&quot; prefix are retrieved from NVS   *\/\nfloat alphaX, betaX, deltaX, alphaY, betaY, deltaY;\nfloat pref_alphaX, pref_betaX, pref_deltaX, pref_alphaY, pref_betaY, pref_deltaY;\n\n#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT \/ 10 * (LV_COLOR_DEPTH \/ 8))\nuint32_t draw_buf[DRAW_BUF_SIZE \/ 4];\n\n\/\/ If logging is enabled, it will inform the user about what is happening in the library\nvoid log_print(lv_log_level_t level, const char * buf) {\n  LV_UNUSED(level);\n  Serial.println(buf);\n  Serial.flush();\n}\n\n\/\/ Declare function to get the Raw Touchscreen data\nvoid touchscreen_read_pts(bool, bool, int, int);\n\n\/* Declare function to display a user instruction upon startup *\/\nvoid lv_display_instruction(void);\n\n\/* Declare function to display crosshair at indexed point *\/\nvoid display_crosshair(int);\n\n\/* Declare function to display crosshairs at given coordinates *\/\nvoid display_crosshairs(int, int);\n\n\/* Declare function to display 'X's at given coordinates *\/\nvoid display_xs(int, int);\n\n\/* Declare function to compute the resistive touchscreen coordinates to display coordinates conversion coefficients *\/\nvoid ts_calibration (\n  const point, const point,\n\tconst point, const point,\n\tconst point, const point,\n  const point, const point,\n\tconst point, const point,\n\tconst point, const point);\n\nvoid gather_cal_data(void) {\n  \/\/Function to draw the crosshairs and collect data\n  bool reset, finished;\n  int x_avg, y_avg;\n\n  for (int i = 0; i &lt; 6; i++) {\n    lv_obj_clean(lv_scr_act());\n    \/\/lv_draw_cross(i);\n    display_crosshair(i);\n\n    reset = true;\n    x_avg = 0;\n    y_avg = 0;\n    \n    touchscreen_read_pts(reset, &amp;finished, &amp;x_avg, &amp;y_avg);\n\n    reset = false;\n    while (!finished) {\n      touchscreen_read_pts(reset, &amp;finished, &amp;x_avg, &amp;y_avg);\n\n      \/* found out the hard way that if I don't do this, the screen doesn't update *\/\n      lv_task_handler();\n      lv_tick_inc(10);\n      delay(10);\n    }\n\n    lv_obj_clean(lv_scr_act());\n    lv_task_handler();\n    lv_tick_inc(10);\n    delay(10);\n\n    ts_points[i][0] = x_avg;\n    ts_points[i][1] = y_avg;\n\n    String s = String(&quot;x_avg = &quot; + String(x_avg) + &quot; y_avg = &quot; + String(y_avg) );\n    Serial.println(s);\n    delay(1500);\n  }\n}\n\nvoid compute_transformation_coefficients(void) {\n  \/* finished collecting data, now compute correction coefficients *\/\n  \/* first initialize the function call parameters *\/\n  aT = { ts_points[0][0], ts_points[0][1] };\n  bT = { ts_points[1][0], ts_points[1][1] };\n  cT = { ts_points[2][0], ts_points[2][1] };\n  dT = { ts_points[3][0], ts_points[3][1] };\n  eT = { ts_points[4][0], ts_points[4][1] };\n  fT = { ts_points[5][0], ts_points[5][1] };\n\n  \/* compute the resisitve touchscreen to display coordinates conversion coefficients *\/\n  ts_calibration(aS, aT, bS, bT, cS, cT, dS, dT, eS, eT, fS, fT);\n}\n\nvoid check_calibration_results(void) {\n  int x_touch, y_touch, x_scr, y_scr, error;\n\n  \/* if we did our job well, the screen points computed below should match\n     aS, bS, and cS defined near the top *\/\n  for (int i = 0; i &lt; 6; i++) {\n    \/* define some touch points and translate them to screen points *\/\n    x_touch = ts_points[i][0];\n    y_touch = ts_points[i][1];\n\n    \/* here's the magic equation that uses the magic coefficients *\/\n    \/* use it to convert resistive touchscreen points to display points *\/\n    x_scr = alphaX * x_touch + betaX * y_touch + deltaX;\n    y_scr = alphaY * x_touch + betaY * y_touch + deltaY;\n\n    display_crosshairs(scr_points[i][0], scr_points[i][1]);\n    display_xs(x_scr, y_scr);\n\n    s = String(&quot;x_touch = &quot; + String(x_touch) + &quot; y_touch = &quot; + String(y_touch) );\n    Serial.println(s);\n\n    s = String(&quot;x_scr = &quot; + String(x_scr) + &quot; y_scr = &quot; + String(y_scr) );\n    Serial.println(s);\n\n    error = (int) sqrt( sq(x_scr - scr_points[i][0]) + sq(y_scr - scr_points[i][1]) );\n    s = String(&quot;error = &quot; + String(error) );\n    Serial.println(s);\n    Serial.println();\n  }\n\n  Serial.println(&quot;******************************************************************&quot;);\n  Serial.println(&quot;******************************************************************&quot;);\n  Serial.println(&quot;USE THE FOLLOWING COEFFICIENT VALUES TO CALIBRATE YOUR TOUCHSCREEN&quot;);\n  s = String(&quot;Computed X:  alpha_x = &quot; + String(alphaX, 3) + &quot;, beta_x = &quot; + String(betaX, 3) + &quot;, delta_x = &quot; + String(deltaX, 3) );\n  Serial.println(s);\n  s = String(&quot;Computed Y:  alpha_y = &quot; + String(-alphaY, 3) + &quot;, beta_y = &quot; + String(-betaY, 3) + &quot;, delta_y = &quot; + String(SCREEN_WIDTH-deltaY, 3) );\n  Serial.println(s);\n  Serial.println(&quot;******************************************************************&quot;);\n  Serial.println(&quot;******************************************************************&quot;);\n}\n\nvoid setup() {\n  String LVGL_Arduino = String(&quot;LVGL Library Version: &quot;) + lv_version_major() + &quot;.&quot; + lv_version_minor() + &quot;.&quot; + lv_version_patch();\n  Serial.begin(115200);\n  Serial.println(LVGL_Arduino);\n  \n  \/\/ Start LVGL\n  lv_init();\n  \/\/ Register print function for debugging\n  lv_log_register_print_cb(log_print);\n\n  \/\/ Start the SPI for the touchscreen and init the touchscreen\n  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);\n  touchscreen.begin(touchscreenSPI);\n  \/\/ Set the Touchscreen rotation in landscape mode\n  \/\/ Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 0: touchscreen.setRotation(0);\n  touchscreen.setRotation(2);\n\n  \/\/ Create a display object\n  lv_display_t * disp;\n  \/\/ Initialize the TFT display using the TFT_eSPI library\n  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));\n  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);\n\n  lv_display_instruction();\n  delay_start = millis();\n  while ((millis() - delay_start) &lt; DELAY_5S) {\n    lv_task_handler();\n    lv_tick_inc(10);\n    delay(10);   \n  } \n\n  \/* display crosshairs and have the user tap on them until enough samples are gathered *\/;\n  gather_cal_data();\n\n  \/* crunch the numbers to compute the touchscreen to display coordinate transformation equation coefficients *\/\n  compute_transformation_coefficients();\n\n  \/* display stored correction data and display generated vs measured screen points *\/\n  check_calibration_results();\n}\n\nvoid loop() {\n  lv_task_handler();  \/\/ let the GUI do its work\n  lv_tick_inc(5);     \/\/ tell LVGL how much time has passed\n  delay(5);           \/\/ let this time pass\n}\n \n\/* Function to read a number of points from the resistive touchscreen as the user taps\n   a stylus on displayed crosshairs.  Once the samples have been collected, they are\n   filtered to remove outliers and then averaged and the average x &amp; y are returned to the caller. *\/\nvoid touchscreen_read_pts(bool reset, bool *finished, int *x_avg, int *y_avg) {\n  \/* nr_samples = samples taken; good_samples = samples used, samples = array of 100 samples *\/\n  static int i, nr_samples, good_samples;\n  static uint32_t samples[100][2];\n\n  \/* coordinates to shift and rotate touch screen coordinates to display coordinates *\/\n  static float mean_x, mean_y, filt_mean_x, filt_mean_y, stdev_x, stdev_y;\n\n  \/* caller resets the sample run at each new displayed crosshair *\/\n  if (reset) {\n    nr_samples = 0;\n    *x_avg = 0;\n    *y_avg = 0;\n    *finished = false;\n  }\n  \/\/ Checks if Touchscreen was touched, and prints X, Y\n  if(touchscreen.tirqTouched() &amp;&amp; touchscreen.touched()) {\n    \/\/ Get Touchscreen points\n    TS_Point p = touchscreen.getPoint();\n    samples[nr_samples][0] = p.x;\n    samples[nr_samples][1] = p.y;\n   \n    s = String(&quot;x, y = &quot; + String(samples[nr_samples][0]) + &quot;, &quot; + String(samples[nr_samples][1]) );\n    Serial.println(s);\n\n    nr_samples++;\n\n    \/* first compute the x &amp; y averages of all the samples *\/\n    if (nr_samples &gt;= 100) {\n      mean_x = 0;\n      mean_y = 0;\n      for (i = 0; i &lt; 100; i++) {\n        mean_x += (float)samples[i][0];\n        mean_y += (float)samples[i][1];\n      }\n      mean_x = mean_x \/ (float)nr_samples;\n      mean_y = mean_y \/ (float)nr_samples;\n      s = String(&quot;Unfiltered values:  mean_x = &quot; + String(mean_x) + &quot;, mean_y = &quot; + String(mean_y));\n      Serial.println(s);\n\n      \/* now compute the x &amp; y standard deviations of all the samples *\/\n      stdev_x = 0;\n      stdev_y = 0;\n      for (i = 0; i &lt; 100; i++) {\n        stdev_x += sq((float)samples[i][0] - mean_x);\n        stdev_y += sq((float)samples[i][1] - mean_y);\n      }\n      stdev_x = sqrt(stdev_x \/ (float)nr_samples);\n      stdev_y = sqrt(stdev_y \/ (float)nr_samples);\n\n      s = String(&quot;stdev_x = &quot; + String(stdev_x) + &quot;, stdev_y = &quot; + String(stdev_y));\n      Serial.println(s);   \n\n      \/* now average the samples that are less than one standard deviation from the mean *\/\n      \/* this filtering is called &quot;outlier rejection,&quot; and is included because outliers were observed in testing *\/\n      good_samples = 0;\n      filt_mean_x = 0;\n      filt_mean_y = 0;\n      for (i = 0; i &lt; 100; i++) {\n        if ((abs((float)samples[i][0] - mean_x) &lt; stdev_x) &amp;&amp; (abs((float)samples[i][1] - mean_y) &lt; stdev_y)) {\n          filt_mean_x += (float)samples[i][0];\n          filt_mean_y += (float)samples[i][1];\n          good_samples++;\n        }        \n      }\n\n      s = String(&quot;Good samples = &quot; + String(good_samples));\n      Serial.println(s);      \n\n      filt_mean_x = filt_mean_x \/ (float)good_samples;\n      filt_mean_y = filt_mean_y \/ (float)good_samples;\n      s = String(&quot;Filtered values:  filt_mean_x = &quot; + String(filt_mean_x) + &quot;, filt_mean_y = &quot; + String(filt_mean_y));\n      Serial.println(s);\n      Serial.println();\n\n      *x_avg = (int)mean_x;\n      *y_avg = (int)mean_y;\n\n      *finished = true;\n    }\n  }\n  else {\n    \/\/ nada\n  }\n}\n\n\/* Function to display a user instruction on startup *\/\nvoid lv_display_instruction(void) {\n  \/\/ Create a text label aligned center: https:\/\/docs.lvgl.io\/master\/widgets\/label.html\n  lv_obj_t * text_label = lv_label_create(lv_screen_active());\n  lv_label_set_text(text_label, &quot;Tap each crosshair until it disappears.&quot;);\n  lv_obj_align(text_label, LV_ALIGN_CENTER, 0, 0);\n  \/\/ Set font type and font size. More information: https:\/\/docs.lvgl.io\/master\/overview\/font.html\n  static lv_style_t style_text_label;\n  lv_style_init(&amp;style_text_label);\n  lv_style_set_text_font(&amp;style_text_label, &amp;lv_font_montserrat_14);\n  lv_obj_add_style(text_label, &amp;style_text_label, 0);\n}\n\n\/* function to display crosshair at given index of coordinates array *\/\nvoid display_crosshair(int cross_nr) {\n\n  static lv_point_precise_t h_line_points[] = { {0, 0}, {10, 0} };\n  static lv_point_precise_t v_line_points[] = { {0, 0}, {0, 10} };\n\n  static lv_style_t style_line;\n  lv_style_init(&amp;style_line);\n  lv_style_set_line_width(&amp;style_line, 2);\n  lv_style_set_line_color(&amp;style_line, lv_palette_main(LV_PALETTE_RED));\n  lv_style_set_line_rounded(&amp;style_line, true);\n\n  \/\/ Create crosshair lines\n  lv_obj_t* crosshair_h = lv_line_create(lv_screen_active());\n  lv_obj_t* crosshair_v = lv_line_create(lv_screen_active());\n\n  lv_line_set_points(crosshair_h, h_line_points, 2); \/\/ Set the coordinates for the crosshair_h line\n  lv_obj_add_style(crosshair_h, &amp;style_line, 0);\n\n  lv_line_set_points(crosshair_v, v_line_points, 2); \/\/ Set the coordinates for the crosshair_h line\n  lv_obj_add_style(crosshair_v, &amp;style_line, 0);\n\n  lv_obj_set_pos(crosshair_h, scr_points[cross_nr][0] - 5, scr_points[cross_nr][1]);\n  lv_obj_set_pos(crosshair_v, scr_points[cross_nr][0], scr_points[cross_nr][1] - 5);\n}\n\n\/* function to display crosshairs at given coordinates *\/\nvoid display_crosshairs(int x, int y) {\n\n  static lv_point_precise_t h_line_points[] = { {0, 0}, {10, 0} };\n  static lv_point_precise_t v_line_points[] = { {0, 0}, {0, 10} };\n\n  static lv_style_t style_line;\n  lv_style_init(&amp;style_line);\n  lv_style_set_line_width(&amp;style_line, 2);\n  lv_style_set_line_color(&amp;style_line, lv_palette_main(LV_PALETTE_BLUE));\n  lv_style_set_line_rounded(&amp;style_line, true);\n\n  \/\/ Create crosshair lines\n  lv_obj_t* crosshair_h = lv_line_create(lv_screen_active());\n  lv_obj_t* crosshair_v = lv_line_create(lv_screen_active());\n\n  lv_line_set_points(crosshair_h, h_line_points, 2); \/\/ Set the coordinates for the crosshair_h line\n  lv_obj_add_style(crosshair_h, &amp;style_line, 0);\n\n  lv_line_set_points(crosshair_v, v_line_points, 2); \/\/ Set the coordinates for the crosshair_h line\n  lv_obj_add_style(crosshair_v, &amp;style_line, 0);\n\n  lv_obj_set_pos(crosshair_h, x - 5, y);\n  lv_obj_set_pos(crosshair_v, x, y - 5);\n}\n\n\/* function to display 'X's at given coordinates *\/\nvoid display_xs(int x, int y) {\n\n  static lv_point_precise_t u_line_points[] = { {0, 0}, {10, 10} };  \/\/upsloping\n  static lv_point_precise_t d_line_points[] = { {0, 10}, {10, 0} };  \/\/downsloping\n\n  static lv_style_t style_line;\n  lv_style_init(&amp;style_line);\n  lv_style_set_line_width(&amp;style_line, 2);\n  lv_style_set_line_color(&amp;style_line, lv_palette_main(LV_PALETTE_RED));\n  lv_style_set_line_rounded(&amp;style_line, true);\n\n  \/\/ Create crosshair lines\n  lv_obj_t* x_u = lv_line_create(lv_screen_active());\n  lv_obj_t* x_d = lv_line_create(lv_screen_active());\n\n  lv_line_set_points(x_u, u_line_points, 2); \/\/ Set the coordinates for the upsloping line\n  lv_obj_add_style(x_u, &amp;style_line, 0);\n\n  lv_line_set_points(x_d, d_line_points, 2); \/\/ Set the coordinates for the downsloping line\n  lv_obj_add_style(x_d, &amp;style_line, 0);\n\n  lv_obj_set_pos(x_u, x - 5, y - 5);\n  lv_obj_set_pos(x_d, x - 5, y - 5);\n}\n\n\/* function to compute the transformation equation coefficients from resistive touchscreen\n   coordinates to display coordinates...\n  This was based on the Texas Instruments appnote at:\n  https:\/\/www.ti.com\/lit\/an\/slyt277\/slyt277.pdf\n  It implements Equation 7 of that appnote, which computes a least-squares set of coefficients. *\/\nvoid ts_calibration (\n\tconst point aS, const point aT,\n\tconst point bS, const point bT,\n\tconst point cS, const point cT,\n  const point dS, const point dT,\n\tconst point eS, const point eT,\n\tconst point fS, const point fT) {\n\n\tbool defined;\n\tuint16_t screenWidth, screenHeight;\n\n  BLA::Matrix&lt;6, 3&gt; A;\n  BLA::Matrix&lt;3, 6&gt; transA;\n  BLA::Matrix&lt;6&gt; X;\n  BLA::Matrix&lt;6&gt; Y;\n  BLA::Matrix&lt;3, 3&gt; B;\n  BLA::Matrix&lt;3, 6&gt; C;\n  BLA::Matrix&lt;3&gt; X_coeff;\n  BLA::Matrix&lt;3&gt; Y_coeff;\n\n  s = String(&quot;aS = &quot; + String(aS.x) + &quot;, &quot; + String(aS.y));\n  Serial.println(s);\n  s = String(&quot;bS = &quot; + String(bS.x) + &quot;, &quot; + String(bS.y));\n  Serial.println(s);\n  s = String(&quot;cS = &quot; + String(cS.x) + &quot;, &quot; + String(cS.y));\n  Serial.println(s);\n  s = String(&quot;dS = &quot; + String(dS.x) + &quot;, &quot; + String(dS.y));\n  Serial.println(s);\n  s = String(&quot;eS = &quot; + String(eS.x) + &quot;, &quot; + String(eS.y));\n  Serial.println(s);\n  s = String(&quot;fS = &quot; + String(fS.x) + &quot;, &quot; + String(fS.y));\n  Serial.println(s);\n\n  s = String(&quot;aT = &quot; + String(aT.x) + &quot;, &quot; + String(aT.y));\n  Serial.println(s);\n  s = String(&quot;bT = &quot; + String(bT.x) + &quot;, &quot; + String(bT.y));\n  Serial.println(s);\n  s = String(&quot;cT = &quot; + String(cT.x) + &quot;, &quot; + String(cT.y));\n  Serial.println(s);\n  s = String(&quot;eT = &quot; + String(dT.x) + &quot;, &quot; + String(dT.y));\n  Serial.println(s);\n  s = String(&quot;eT = &quot; + String(eT.x) + &quot;, &quot; + String(eT.y));\n  Serial.println(s);\n  s = String(&quot;fT = &quot; + String(fT.x) + &quot;, &quot; + String(fT.y));\n  Serial.println(s);\n  Serial.println();\n\n  struct f_point {\n    float x;\n    float y;\n  };\n\n  struct f_point faS, fbS, fcS, fdS, feS, ffS, faT, fbT, fcT, fdT, feT, ffT;\n\n  faS.x = (float)aS.x; fbS.x = (float)bS.x; fcS.x = (float)cS.x, fdS.x = (float)dS.x; feS.x = (float)eS.x; ffS.x = (float)fS.x;\n  faS.y = (float)aS.y; fbS.y = (float)bS.y; fcS.y = (float)cS.y; fdS.y = (float)dS.y; feS.y = (float)eS.y; ffS.y = (float)fS.y;\n\n  faT.x = (float)aT.x; fbT.x = (float)bT.x; fcT.x = (float)cT.x; fdT.x = (float)dT.x; feT.x = (float)eT.x; ffT.x = (float)fT.x;\n  faT.y = (float)aT.y; fbT.y = (float)bT.y; fcT.y = (float)cT.y; fdT.y = (float)dT.y; feT.y = (float)eT.y; ffT.y = (float)fT.y;\n\n  A = { faT.x, faT.y, 1,\n        fbT.x, fbT.y, 1,\n        fcT.x, fcT.y, 1,\n        fdT.x, fdT.y, 1,\n        feT.x, feT.y, 1,             \n        ffT.x, ffT.y, 1 };\n\n  X = { faS.x,\n        fbS.x,\n        fcS.x,\n        fdS.x,\n        feS.x,\n        ffS.x };\n\n  Y = { faS.y,\n        fbS.y,\n        fcS.y,\n        fdS.y,\n        feS.y,\n        ffS.y };\n\n  \/* Now compute [AtA]^-1 * AtA * X and [AtA]^-1 * AtA * Y *\/\n  Serial.print (&quot;A = &quot;);\n  Serial.println(A);\n\n  transA = ~A;\n  Serial.print (&quot;transA = &quot;);\n  Serial.println(transA);\n\n  B = transA * A;\n  Serial.print (&quot;Before inversion, B = &quot;);\n  Serial.println(B);\n\n  if (!Invert(B) ) {\n    Serial.println(&quot;Singular matrix in computation of inverse of B = transA*A!&quot;);\n  }\n  Serial.print (&quot;After inversion, B = &quot;);\n  Serial.println(B);\n\n  C = B * transA;\n  Serial.print (&quot;C = &quot;);\n  Serial.println(C);\n\n  X_coeff = C * X;\n  Y_coeff = C * Y;\n\n  \/* transfer the X and Y coefficients to the Greek-letter variables\n     Note that BLA requires round brackets while MatrixMath requires square ones *\/\n  alphaX = X_coeff(0); betaX = X_coeff(1); deltaX = X_coeff(2);\n  alphaY = Y_coeff(0); betaY = Y_coeff(1); deltaY = Y_coeff(2);\n\n  Serial.println();\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/ESP32-TFT-Touchscreen\/raw\/main\/examples\/ESP32_Calibration_Sketch\/ESP32_Calibration_Sketch.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Getting Your Calibration Coefficients<\/h3>\n\n\n\n<p>Open the Serial Monitor.<\/p>\n\n\n\n<p>After running that sketch, you should use a stylus (the one that came with the display) to tap repeatedly and as precisely as possible on the center of each of the six &#8216;<strong>+<\/strong>&#8216; signs that are displayed one at a time after the initial text display.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Cheap-Yellow-Display-Touchscreen-Calibration.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Cheap Yellow Display Calibrating the touchscreen\" class=\"wp-image-161071\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Cheap-Yellow-Display-Touchscreen-Calibration.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Cheap-Yellow-Display-Touchscreen-Calibration.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>Stop tapping when the &#8216;<strong>+<\/strong>&#8216; sign disappears, and watch for another one to appear, for a total of six appearances of the &#8216;<strong>+<\/strong>&#8216; sign.<\/p>\n\n\n\n<p>After the sketch processes the resistive touchscreen samples you create with your taps, it will display six &#8216;<strong>+<\/strong>&#8216; symbols where it asked you to tap and six &#8216;<strong>\u00d7<\/strong>&#8216;s where it thinks you tapped, after the calibration procedure.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/touchscreen-calibration-example.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Cheap Yellow Display Touchscreen calibration\" class=\"wp-image-161072\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/touchscreen-calibration-example.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/touchscreen-calibration-example.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>If the crosses are well aligned, you can proceed. Otherwise, reset the ESP32 and repeat the same procedure (try to be as precise as possible so that the calibration goes successfully).<\/p>\n\n\n\n<p>After this procedure, your coefficient values will be displayed in the Serial Monitor.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"666\" height=\"251\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-touchscreen-calibration-values-serial-monitor.png?resize=666%2C251&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Touchscreen Calibration Getting the Coefficient Values\" class=\"wp-image-161073\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-touchscreen-calibration-values-serial-monitor.png?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-touchscreen-calibration-values-serial-monitor.png?resize=300%2C113&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<p>Save those values because you\u2019ll need them later in all your sketches that use the touchscreen and that need to be calibrated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing the Touchscreen (Calibrated)<\/h2>\n\n\n\n<p>Now let&#8217;s test the touchscreen using the calibration equations. Before running the code, make sure you modify the code to use the coefficients you got in the previous test.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n    Complete project details: https:\/\/RandomNerdTutorials.com\/touchscreen-calibration\/\n    THIS EXAMPLE WAS TESTED WITH THE FOLLOWING HARDWARE:\n    1) ESP32-2432S028R 2.8 inch 240\u00d7320 also known as the Cheap Yellow Display (CYD): https:\/\/makeradvisor.com\/tools\/cyd-cheap-yellow-display-esp32-2432s028r\/\n      SET UP INSTRUCTIONS: https:\/\/RandomNerdTutorials.com\/cyd-lvgl\/\n    2) REGULAR ESP32 Dev Board + 2.8 inch 240x320 TFT Display: https:\/\/makeradvisor.com\/tools\/2-8-inch-ili9341-tft-240x320\/ and https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\n      SET UP INSTRUCTIONS: https:\/\/RandomNerdTutorials.com\/esp32-tft-lvgl\/\n    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n\n\/*  Install the &quot;lvgl&quot; library version 9.2 by kisvegabor to interface with the TFT Display - https:\/\/lvgl.io\/\n    *** IMPORTANT: lv_conf.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***\n    *** YOU MUST USE THE lv_conf.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***\n    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https:\/\/RandomNerdTutorials.com\/cyd-lvgl\/ or https:\/\/RandomNerdTutorials.com\/esp32-tft-lvgl\/   *\/\n#include &lt;lvgl.h&gt;\n\n\/*  Install the &quot;TFT_eSPI&quot; library by Bodmer to interface with the TFT Display - https:\/\/github.com\/Bodmer\/TFT_eSPI\n    *** IMPORTANT: User_Setup.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***\n    *** YOU MUST USE THE User_Setup.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***\n    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https:\/\/RandomNerdTutorials.com\/cyd-lvgl\/ or https:\/\/RandomNerdTutorials.com\/esp32-tft-lvgl\/   *\/\n#include &lt;TFT_eSPI.h&gt;\n\n\/\/ Install the &quot;XPT2046_Touchscreen&quot; library by Paul Stoffregen to use the Touchscreen - https:\/\/github.com\/PaulStoffregen\/XPT2046_Touchscreen - Note: this library doesn't require further configuration\n#include &lt;XPT2046_Touchscreen.h&gt;\n\n\/\/ Touchscreen pins\n#define XPT2046_IRQ 36   \/\/ T_IRQ\n#define XPT2046_MOSI 32  \/\/ T_DIN\n#define XPT2046_MISO 39  \/\/ T_OUT\n#define XPT2046_CLK 25   \/\/ T_CLK\n#define XPT2046_CS 33    \/\/ T_CS\n\nSPIClass touchscreenSPI = SPIClass(VSPI);\nXPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);\n\n#define SCREEN_WIDTH 240\n#define SCREEN_HEIGHT 320\n\n\/\/ Touchscreen coordinates: (x, y) and pressure (z)\nint x, y, z;\n\n#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT \/ 10 * (LV_COLOR_DEPTH \/ 8))\nuint32_t draw_buf[DRAW_BUF_SIZE \/ 4];\n\n\/\/ If logging is enabled, it will inform the user about what is happening in the library\nvoid log_print(lv_log_level_t level, const char * buf) {\n  LV_UNUSED(level);\n  Serial.println(buf);\n  Serial.flush();\n}\n\nstatic lv_obj_t * text_label_touch;\n\/\/ Get the Touchscreen data\nvoid touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {\n  \/\/ Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)\n  if(touchscreen.tirqTouched() &amp;&amp; touchscreen.touched()) {\n    \/\/ Get Touchscreen points\n    TS_Point p = touchscreen.getPoint();\n\n    \/\/ Advanced Touchscreen calibration, LEARN MORE \u00bb https:\/\/RandomNerdTutorials.com\/touchscreen-calibration\/\n    float alpha_x, beta_x, alpha_y, beta_y, delta_x, delta_y;\n\n    \/\/ REPLACE WITH YOUR OWN CALIBRATION VALUES \u00bb https:\/\/RandomNerdTutorials.com\/touchscreen-calibration\/\n    alpha_x = -0.000;\n    beta_x = 0.090;\n    delta_x = -33.771;\n    alpha_y = 0.066;\n    beta_y = 0.000;\n    delta_y = -14.632;\n\n    x = alpha_y * p.x + beta_y * p.y + delta_y;\n    \/\/ clamp x between 0 and SCREEN_WIDTH - 1\n    x = max(0, x);\n    x = min(SCREEN_WIDTH - 1, x);\n\n    y = alpha_x * p.x + beta_x * p.y + delta_x;\n    \/\/ clamp y between 0 and SCREEN_HEIGHT - 1\n    y = max(0, y);\n    y = min(SCREEN_HEIGHT - 1, y);\n\n    \/\/ Basic Touchscreen calibration points with map function to the correct width and height\n    \/\/x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);\n    \/\/y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);\n\n    z = p.z;\n\n    data-&gt;state = LV_INDEV_STATE_PRESSED;\n\n    \/\/ Set the coordinates\n    data-&gt;point.x = x;\n    data-&gt;point.y = y;\n\n    \/\/ Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor\n    Serial.print(&quot;X = &quot;);\n    Serial.print(x);\n    Serial.print(&quot; | Y = &quot;);\n    Serial.print(y);\n    Serial.print(&quot; | Pressure = &quot;);\n    Serial.print(z);\n    Serial.println();\n    \/\/ Print the coordinates on the screen\n    String touch_data = &quot;X = &quot; + String(x) + &quot;\\nY = &quot; + String(y) + &quot;\\nZ = &quot; + String(z);\n    lv_label_set_text(text_label_touch, touch_data.c_str());\n  }\n  else {\n    data-&gt;state = LV_INDEV_STATE_RELEASED;\n  }\n}\n\n\nvoid lv_create_main_gui(void) {\n  \/\/ Create a text label aligned center: https:\/\/docs.lvgl.io\/master\/widgets\/label.html\n  text_label_touch = lv_label_create(lv_screen_active());\n  lv_label_set_text(text_label_touch, &quot;Touch screen to test&quot;);\n  lv_obj_align(text_label_touch, LV_ALIGN_CENTER, 0, 0);\n\n  \/\/ Set font type and size. More information: https:\/\/docs.lvgl.io\/master\/overview\/font.html\n  static lv_style_t style_text_label;\n  lv_style_init(&amp;style_text_label);\n  lv_style_set_text_font(&amp;style_text_label, &amp;lv_font_montserrat_18);\n  lv_obj_add_style(text_label_touch, &amp;style_text_label, 0); \n}\n\nvoid setup() {\n  String LVGL_Arduino = String(&quot;LVGL Library Version: &quot;) + lv_version_major() + &quot;.&quot; + lv_version_minor() + &quot;.&quot; + lv_version_patch();\n  Serial.begin(115200);\n  Serial.println(LVGL_Arduino);\n  \n  \/\/ Start LVGL\n  lv_init();\n  \/\/ Register print function for debugging\n  lv_log_register_print_cb(log_print);\n\n  \/\/ Start the SPI for the touchscreen and init the touchscreen\n  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);\n  touchscreen.begin(touchscreenSPI);\n  \/\/ Set the Touchscreen rotation in landscape mode\n  \/\/ Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 0: touchscreen.setRotation(0);\n  touchscreen.setRotation(2);\n\n  \/\/ Create a display object\n  lv_display_t * disp;\n  \/\/ Initialize the TFT display using the TFT_eSPI library\n  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));\n  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);\n    \n  \/\/ Initialize an LVGL input device object (Touchscreen)\n  lv_indev_t * indev = lv_indev_create();\n  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);\n  \/\/ Set the callback function to read Touchscreen input\n  lv_indev_set_read_cb(indev, touchscreen_read);\n\n  \/\/ Function to draw the GUI with switches\n  lv_create_main_gui();\n}\n\nvoid loop() {\n  lv_task_handler();  \/\/ let the GUI do its work\n  lv_tick_inc(5);     \/\/ tell LVGL how much time has passed\n  delay(5);           \/\/ let this time pass\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/ESP32-TFT-Touchscreen\/raw\/main\/examples\/ESP32_Touchscreen_Test_Calibrated\/ESP32_Touchscreen_Test_Calibrated.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Touchscreen Read Function<\/h3>\n\n\n\n<p>Basically, you need to take a look at the <span class=\"rnthl rntliteral\">touchscreen_read()<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get the Touchscreen data\nvoid touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {\n\n  \/\/ Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)\n  if(touchscreen.tirqTouched() &amp;&amp; touchscreen.touched()) {\n\n    \/\/ Get Touchscreen points\n    TS_Point p = touchscreen.getPoint();\n\n    \/\/ Advanced Touchscreen calibration, LEARN MORE\n    float alpha_x, beta_x, alpha_y, beta_y, delta_x, delta_y;\n\n    \/\/ REPLACE WITH YOUR OWN CALIBRATION VALUES\n    alpha_x = -0.000;\n    beta_x = 0.090;\n    delta_x = -33.771;\n    alpha_y = 0.066;\n    beta_y = 0.000;\n    delta_y = -14.632;\n\n    x = alpha_y * p.x + beta_y * p.y + delta_y;\n    \/\/ clamp x between 0 and SCREEN_WIDTH - 1\n    x = max(0, x);\n    x = min(SCREEN_WIDTH - 1, x);\n\n    y = alpha_x * p.x + beta_x * p.y + delta_x;\n    \/\/ clamp y between 0 and SCREEN_HEIGHT - 1\n    y = max(0, y);\n    y = min(SCREEN_HEIGHT - 1, y);\n\n    \/\/ Basic Touchscreen calibration points with map function to the correct width and height\n    \/\/x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);\n    \/\/y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);\n\n    z = p.z;\n\n    data->state = LV_INDEV_STATE_PRESSED;\n\n    \/\/ Set the coordinates\n    data->point.x = x;\n    data->point.y = y;\n\n    \/\/ Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor\n    Serial.print(\"X = \");\n    Serial.print(x);\n    Serial.print(\" | Y = \");\n    Serial.print(y);\n    Serial.print(\" | Pressure = \");\n    Serial.print(z);\n    Serial.println();\n    \n   \/\/Print the coordinates on the screen (delete when using with other examples)\n    String touch_data = \"X = \" + String(x) + \"nY = \" + String(y) + \"nZ = \" + String(z);\n    lv_label_set_text(text_label_touch, touch_data.c_str());\n\n  }\n  else {\n    data->state = LV_INDEV_STATE_RELEASED;\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Insert Your Coefficient Values<\/h4>\n\n\n\n<p>You need to insert your coefficient values in the following variables, before uploading the code to your board.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ REPLACE WITH YOUR OWN CALIBRATION VALUES \u00bb \nalpha_x = -0.000;\nbeta_x = 0.090;\ndelta_x = -33.771;\nalpha_y = 0.066;\nbeta_y = 0.000;\ndelta_y = -14.632;<\/code><\/pre>\n\n\n\n<p>Then, the code uses the equations described in the paper to calibrate the touchscreen.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>x = alpha_y * p.x + beta_y * p.y + delta_y;\n\/\/ clamp x between 0 and SCREEN_WIDTH - 1\nx = max(0, x);\nx = min(SCREEN_WIDTH - 1, x);\n\ny = alpha_x * p.x + beta_x * p.y + delta_x;\n\/\/ clamp y between 0 and SCREEN_HEIGHT - 1\ny = max(0, y);\ny = min(SCREEN_HEIGHT - 1, y);<\/code><\/pre>\n\n\n\n<p>Now, you should get calibrated X and Y values when you touch the screen.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"508\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-Cheap-Yellow-Display-Testing-Calibrated-Touchscreen.jpg?resize=900%2C508&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 CYD Testing Calibrated Touchscreen\" class=\"wp-image-161074\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-Cheap-Yellow-Display-Testing-Calibrated-Touchscreen.jpg?w=900&amp;quality=100&amp;strip=all&amp;ssl=1 900w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-Cheap-Yellow-Display-Testing-Calibrated-Touchscreen.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-Cheap-Yellow-Display-Testing-Calibrated-Touchscreen.jpg?resize=768%2C433&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 900px) 100vw, 900px\" \/><\/figure><\/div>\n\n\n<p><strong>In summary\u2026<\/strong><\/p>\n\n\n\n<p>Use this touchscreen_read() function in all your projects with your own coefficients values so that you always have a calibrated touchscreen using values that are specific for your board.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get the Touchscreen data\nvoid touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {\n  \/\/ Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)\n  if(touchscreen.tirqTouched() &amp;&amp; touchscreen.touched()) {\n    \/\/ Get Touchscreen points\n    TS_Point p = touchscreen.getPoint();\n    \/\/ Advanced Touchscreen calibration \n    float alpha_x, beta_x, alpha_y, beta_y, delta_x, delta_y;\n    \/\/ REPLACE WITH YOUR OWN CALIBRATION VALUES \n    alpha_x = -0.000;\n    beta_x = 0.090;\n    delta_x = -33.771;\n    alpha_y = 0.066;\n    beta_y = 0.000;\n    delta_y = -14.632;\n\n    x = alpha_y * p.x + beta_y * p.y + delta_y;\n    \/\/ clamp x between 0 and SCREEN_WIDTH - 1\n    x = max(0, x);\n    x = min(SCREEN_WIDTH - 1, x);\n    y = alpha_x * p.x + beta_x * p.y + delta_x;\n    \/\/ clamp y between 0 and SCREEN_HEIGHT - 1\n    y = max(0, y);\n    y = min(SCREEN_HEIGHT - 1, y);\n\n    z = p.z;\n\n    data->state = LV_INDEV_STATE_PRESSED;\n    \/\/ Set the coordinates\n    data->point.x = x;\n    data->point.y = y;\n\n    \/\/ Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor\n    Serial.print(\"X = \");\n    Serial.print(x);\n    Serial.print(\" | Y = \");\n    Serial.print(y);\n    Serial.print(\" | Pressure = \");\n    Serial.print(z);\n    Serial.println();\n  }\n  else {\n    data->state = LV_INDEV_STATE_RELEASED;\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Taking it Further &#8211; Using the <em>Preferences<\/em> Library<\/h2>\n\n\n\n<p>If you don\u2019t have to worry about writing your coefficient values in every sketch, you can save the values on the ESP32 permanently using the Preferences library. Then, on each example, load the values from Preferences at the start of the code. The example developed by Robert Fleming uses this approach, you can check it out <a href=\"https:\/\/github.com\/CF20852\/ESP32-2432S028-Touchscreen-Calibration\" target=\"_blank\" rel=\"noopener\" title=\"\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>We hope you found this article useful to calibrate your ESP32 Cheap Yellow Display Touchscreen. The same technique can be applied to any other touchscreen with a few modifications in the code.<\/p>\n\n\n\n<p class=\"rntbox rntclgray\">Special thanks to <u>Robert Fleming<\/u> for providing the original calibration sketch.<\/p>\n\n\n\n<p>More tutorials using the ESP32 Cheap Yellow Display:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-cyd-lvgl-temperature-ds18b20\/\">ESP32 CYD with LVGL: Display Temperature with DS18B20 Sensor (Text and Arc)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-cheap-yellow-display-cyd-pinout-esp32-2432s028r\/\">ESP32 Cheap Yellow Display (CYD) Pinout (ESP32-2432S028R)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/lvgl-cheap-yellow-display-esp32-2432s028r\/\">LVGL with ESP32 Cheap Yellow Display Board (ESP32-2432S028R)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/touchscreen-on-off-button-cheap-yellow-display-esp32-2432s028r\/\">ESP32 Touchscreen On\/Off Button \u2013 Cheap Yellow Display (ESP32-2432S028R)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/cheap-yellow-display-esp32-2432s028r\/\">Getting Started with ESP32 Cheap Yellow Display Board \u2013 CYD (ESP32-2432S028R)<\/a><\/li>\n<\/ul>\n\n\n\n<p>To learn more about creating GUIs using the ESP32, check out our eBook:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-lvgl-esp32-ebook\/\" title=\"\">Learn LVGL: Build GUIs for ESP32 Projects (eBook)<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this guide, we&#8217;ll show you how to calibrate the ESP32 Cheap Yellow Display (CYD) Touchscreen. This calibration method also applies to other touchscreen displays. Our codes are compatible with &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Cheap Yellow Display (CYD) Resistive Touchscreen Calibration\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-cheap-yellow-display-cyd-resistive-touchscreen-calibration\/#more-161067\" aria-label=\"Read more about ESP32 Cheap Yellow Display (CYD) Resistive Touchscreen Calibration\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":161080,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[281,276,299,264],"tags":[],"class_list":["post-161067","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32-project","category-esp32","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/07\/ESP32-CYD-Touchscreen-Calibration.jpg?fit=1920%2C1080&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/161067","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=161067"}],"version-history":[{"count":10,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/161067\/revisions"}],"predecessor-version":[{"id":162120,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/161067\/revisions\/162120"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/161080"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=161067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=161067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=161067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}