ESP32-S3 Touchscreen UI with DS18B20 & Relay Using SquareLine Studio
ESP32 GUI: Monitoring and Control Using Arc and Switch Widgets in SquareLine Studio
Last Updated on August 6, 2025 by Engr. Shahzada Fahad
Table of Contents
Introduction:
ESP32-S3 Touchscreen UI with DS18B20 & Relay Using SquareLine Studio- This is my third article on the MaTouch ESP32-S3 AMOLED with a 1.8” FT3168 touch display.
Personally, I like this because it comes with a GPIO Connector Ribbon Cable; this flat ribbon cable breaks out the GPIO pins, making it incredibly easy to connect sensors, modules, or any custom circuits. It’s a great feature that opens up a lot of options for prototyping.
We can use this in bikes and cars for monitoring various sensors, and not just that; it can also be used to start and stop the engine, and control doors, AC, lights, and more.
You can also use it for home automation. The possibilities are endless; it’s hard to list them all!
To demonstrate what I mean, I am going to build a temperature monitoring system using the DS18B20 waterproof one-wire digital temperature sensor. Of course, I won’t overcomplicate it, so that you can easily build it yourself. We will display the temperature both on a label and on an arc.
If you want to create a more complex GUI, I highly recommend reading my 2nd article “Video also available on my YouTube Channel “Electronic Clinic”, because in that article and video I showed how to create an analog watch using custom images.
You can design your complete UI in Adobe Photoshop or Figma, export it as images, and then use those images in SquareLine Studio as part of your UI.
Anyway, on Screen 2, I have also added a switch that lets me turn an LED ON or OFF. Instead of an LED, you could use a relay to control higher AC or DC loads.
Note: I highly recommend to download the complete Template folder from my Patreon page. Because there are so many other files, libraries, example codes, etc.
So, without any further delay let’s get started!!!
Check out our latest article on the BLE Call System for Motorcycles using ESP32-S3 and touch display — a smart, hands-free solution designed for safer rides!
Amazon Links:
Other Tools and Components:
ESP32 WiFi + Bluetooth Module (Recommended)
Arduino Nano USB C type (Recommended)
*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!
Before we start the interfacing, make sure to download this Pinout diagram; it will save you a lot of time and confusion later.
Circuit Diagram:
Connect the voltage and ground wires of the DS18b20 to the VBUS and Ground pins on the GPIO Connector Ribbon Cable. Connect the Data wire; which is usually the yellow wire to the GPIO16.
Connect the Cathode leg of the 2.5V LED to the GPIO21. Connect the Anode Leg to the VBUS pin through a 330-ohm resistor.
Template Code:
So guys, here is my template folder. I start every project using this template folder. Inside it, I have also created a folder specifically for SquareLine Studio project files, and I have made a separate “ui files” folder to save the generated UI files. These are not empty folders.
If I open the “SquareLine Studio project files” folder, you can see I have already saved a project in it.
In a moment, when we import this project into SquareLine Studio, you will understand it better. Basically, this is a template project, which I use as my starting point.
In Part 2, I used the same template project to build digital and analog watches. You all should definitely watch Part 2, because after watching it, you will also learn how to create custom fonts, and how to animate images.
Likewise, in the “ui files” folder, you can see there are many files. These are the files that we previously generated in SquareLine Studio. Whenever we generate new UI files, we copy and paste them alongside the Arduino .ino file. as you can see in the right-side image.
You can see there are many files along with the Arduino .ino file, these files are pasted here from the ui files folder. If you open the Arduino .ino file “lvgl_template.ino” all the .c and .h files will be automatically loaded.
Complete Template Code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
#include <lvgl.h> #include "Arduino_GFX_Library.h" #include "Arduino_DriveBus_Library.h" #include "pin_config.h" #include "ui.h" static const uint16_t screenWidth = 368; static const uint16_t screenHeight = 448; static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[ screenWidth * screenHeight / 10 ]; Arduino_DataBus *bus = new Arduino_ESP32QSPI( LCD_CS /* CS */, LCD_SCLK /* SCK */, LCD_SDIO0 /* SDIO0 */, LCD_SDIO1 /* SDIO1 */, LCD_SDIO2 /* SDIO2 */, LCD_SDIO3 /* SDIO3 */); Arduino_GFX *gfx = new Arduino_SH8601(bus, LCD_RST /* RST */, 0 /* rotation */, false /* IPS */, LCD_WIDTH, LCD_HEIGHT); std::shared_ptr<Arduino_IIC_DriveBus> IIC_Bus = std::make_shared<Arduino_HWIIC>(IIC_SDA, IIC_SCL, &Wire); void Arduino_IIC_Touch_Interrupt(void); std::unique_ptr<Arduino_IIC> FT3168(new Arduino_FT3x68(IIC_Bus, FT3168_DEVICE_ADDRESS, DRIVEBUS_DEFAULT_VALUE, TP_INT, Arduino_IIC_Touch_Interrupt)); void Arduino_IIC_Touch_Interrupt(void) { FT3168->IIC_Interrupt_Flag = true; } #if LV_USE_LOG != 0 /* Serial debugging */ void my_print(const char * buf) { Serial.printf(buf); Serial.flush(); } #endif /* Display flushing */ void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ) { uint32_t w = ( area->x2 - area->x1 + 1 ); uint32_t h = ( area->y2 - area->y1 + 1 ); #if (LV_COLOR_16_SWAP != 0) gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h); #else gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h); #endif lv_disp_flush_ready( disp ); } /*Read the touchpad*/ void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data ) { int32_t touchX = FT3168->IIC_Read_Device_Value(FT3168->Arduino_IIC_Touch::Value_Information::TOUCH_COORDINATE_X); int32_t touchY = FT3168->IIC_Read_Device_Value(FT3168->Arduino_IIC_Touch::Value_Information::TOUCH_COORDINATE_Y); if( FT3168->IIC_Interrupt_Flag == true) { FT3168->IIC_Interrupt_Flag = false; data->state = LV_INDEV_STATE_PR; /*Set the coordinates*/ data->point.x = touchX; data->point.y = touchY; USBSerial.print( "Data x " ); USBSerial.print( touchX ); USBSerial.print( "Data y " ); USBSerial.println( touchY ); char s[40]; sprintf(s, "%d", touchX); } else { data->state = LV_INDEV_STATE_REL; } } void setup() { USBSerial.begin( 115200 ); /* prepare for possible serial debug */ pinMode(LCD_EN, OUTPUT); digitalWrite(LCD_EN, HIGH); while (FT3168->begin() == false) { USBSerial.println("FT3168 initialization fail"); delay(2000); } USBSerial.println("FT3168 initialization successfully"); gfx->begin(); gfx->Display_Brightness(200); String LVGL_Arduino = "Hello Arduino! "; LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch(); USBSerial.println( LVGL_Arduino ); USBSerial.println( "I am LVGL_Arduino" ); lv_init(); #if LV_USE_LOG != 0 lv_log_register_print_cb( my_print ); /* register print function for debugging */ #endif FT3168->IIC_Write_Device_State(FT3168->Arduino_IIC_Touch::Device::TOUCH_POWER_MODE, FT3168->Arduino_IIC_Touch::Device_Mode::TOUCH_POWER_MONITOR); lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * screenHeight / 10 ); /*Initialize the display*/ static lv_disp_drv_t disp_drv; lv_disp_drv_init( &disp_drv ); /*Change the following line to your display resolution*/ disp_drv.hor_res = screenWidth; disp_drv.ver_res = screenHeight; disp_drv.flush_cb = my_disp_flush; disp_drv.draw_buf = &draw_buf; lv_disp_drv_register( &disp_drv ); /*Initialize the (dummy) input device driver*/ static lv_indev_drv_t indev_drv; lv_indev_drv_init( &indev_drv ); indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = my_touchpad_read; lv_indev_drv_register( &indev_drv ); ui_init(); USBSerial.println( "Setup done" ); } void loop() { lv_timer_handler(); /* let the GUI do its work */ delay(5); } |
Anyway, this is our main template code, and we will be modifying this code to display the temperature and control the LED. So let’s quickly go ahead and import the template project into SquareLine Studio.
SquareLine Studio Project:
In part1, I have explained three different methods on how to start a new SquareLine studio project including how to import a project as well. You can go ahead and read that article or watch that video. Let me quickly share the steps with you.
- While SquareLine Studio is open, click on the IMPORT PROJECT.
- Navigate to my lvgl_template folder, and inside that folder, go to the SquareLine Studio project files folder.
- Select the SquareLine_Project file and click the Open button.
So this is our basic template project, and we are going to modify it to design our custom UI.
Before starting the design, we need to set the folder paths; one for the SquareLine Studio project files, and another for the UI files. So let’s go ahead and do that. I have already explained all of this in detail in Part 1 and Part 2.
Simply go to the File Menu and then to Project settings.
Under the FILE EXPORT you can see “Project Export Root” and UI Files Export Path.
We have already created folders for the SquareLine studio project files and for the ui files folder, so simply go ahead and select those folders. Write lvgl.h in the LVGL Include Path and don’t forget to check the “Flat export (export all files to one folder). Finally, you can click on the APPLY CHANGES button.
Once the project settings are done, we can begin the design process. First, let’s delete that label electronic clinic on the screen1.
Next, we need to add another screen where we will control a load using a switch. On Screen 1, we will display the temperature. So let’s quickly add Screen 2.
First of all, we will add screen change events, so we can switch between the two screens. For that;
- Select Screen1 and then on the inspector tab scroll down and click on “Add Event.”
- Set the Trigger Type to “Gesture Left.”
- Set the Action Type to “Change Screen.”
- Click the “Add” button.
- Select Screen2.
Now select Screen2 and follow the same steps. This time, choose “Gesture Right” as the trigger and set Screen1 as the target.
Now, select screen1 and from the widgets tab on the left side, select Arc to add it to the Screen1.
Then on the Inspector Tab go the the Flags and uncheck the “Clikable” box.
Next, we need to set the minimum and maximum values for the temperature sensor. I am entering these values based on the sensor’s datasheet.
Then, let’s go to the arc’s styling section and change the color and width. You can spend more time here to make it look exactly how you want.
But I will keep it simple for now, just to save time.
I think it looks good enough.
Now, let’s also add a label to display the temperature in numbers. If you have seen my previous videos, you already know exactly how to add labels.
Our label is now ready. That’s all the work we needed to do on Screen1.
Now let’s move on to Screen 2.
Select Screen 2, and from the widgets tab, select a Switch.
We can tweak the styling of this switch as well, but it already looks nice; so let’s not waste any time on that.
What I want is: when I turn this switch ON or OFF, it should call a function to control the LED; either turning it ON or OFF.
To do that, while the switch is selected, scroll down and click the ADD EVENT button.
- Set the Trigger type to Released.
- Set the Action type to CALL FUNCTION.
- Click the ADD button.
- Check the Box.
- And write the Function name. I named it Load1Fun.
Both the screens are ready.
Next, you need to save the project; after this, go to the Export menu and click on Export UI Files.
Now, navigate to the UI Files folder, copy all the exported files, and paste them into the same folder where your main Arduino .ino file is located. Then go ahead and open the Arduino main .ino file. It will automatically load all the files.
On Screen 2, we have only added a switch; that’s why you are only seeing the switch here.
Now, if you go to the ui_events.h file, you can check the functions. you should see the Load1Fun function assigned to the switch in SquareLine Studio.
In the same way, you can explore the other files too; doing so will really help deepen your understanding of how everything works behind the scenes.
Complete Code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
#include <lvgl.h> #include "Arduino_GFX_Library.h" #include "Arduino_DriveBus_Library.h" #include "pin_config.h" #include "ui.h" #include <DallasTemperature.h> namespace pin { const byte one_wire_bus = 16; // Dallas Temperature Sensor } namespace sensor { float MyTemp = 0; } OneWire oneWire(pin::one_wire_bus); DallasTemperature dallasTemperature(&oneWire); static const uint16_t screenWidth = 368; static const uint16_t screenHeight = 448; static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[ screenWidth * screenHeight / 10 ]; Arduino_DataBus *bus = new Arduino_ESP32QSPI( LCD_CS /* CS */, LCD_SCLK /* SCK */, LCD_SDIO0 /* SDIO0 */, LCD_SDIO1 /* SDIO1 */, LCD_SDIO2 /* SDIO2 */, LCD_SDIO3 /* SDIO3 */); Arduino_GFX *gfx = new Arduino_SH8601(bus, LCD_RST /* RST */, 0 /* rotation */, false /* IPS */, LCD_WIDTH, LCD_HEIGHT); std::shared_ptr<Arduino_IIC_DriveBus> IIC_Bus = std::make_shared<Arduino_HWIIC>(IIC_SDA, IIC_SCL, &Wire); void Arduino_IIC_Touch_Interrupt(void); std::unique_ptr<Arduino_IIC> FT3168(new Arduino_FT3x68(IIC_Bus, FT3168_DEVICE_ADDRESS, DRIVEBUS_DEFAULT_VALUE, TP_INT, Arduino_IIC_Touch_Interrupt)); void Arduino_IIC_Touch_Interrupt(void) { FT3168->IIC_Interrupt_Flag = true; } #if LV_USE_LOG != 0 /* Serial debugging */ void my_print(const char * buf) { Serial.printf(buf); Serial.flush(); } #endif /* Display flushing */ void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ) { uint32_t w = ( area->x2 - area->x1 + 1 ); uint32_t h = ( area->y2 - area->y1 + 1 ); #if (LV_COLOR_16_SWAP != 0) gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h); #else gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h); #endif lv_disp_flush_ready( disp ); } /*Read the touchpad*/ void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data ) { int32_t touchX = FT3168->IIC_Read_Device_Value(FT3168->Arduino_IIC_Touch::Value_Information::TOUCH_COORDINATE_X); int32_t touchY = FT3168->IIC_Read_Device_Value(FT3168->Arduino_IIC_Touch::Value_Information::TOUCH_COORDINATE_Y); if( FT3168->IIC_Interrupt_Flag == true) { FT3168->IIC_Interrupt_Flag = false; data->state = LV_INDEV_STATE_PR; /*Set the coordinates*/ data->point.x = touchX; data->point.y = touchY; Serial.print( "Data x " ); Serial.print( touchX ); Serial.print( "Data y " ); Serial.println( touchY ); char s[40]; sprintf(s, "%d", touchX); } else { data->state = LV_INDEV_STATE_REL; } } void setup() { Serial.begin( 115200 ); /* prepare for possible serial debug */ dallasTemperature.begin(); pinMode(LCD_EN, OUTPUT); digitalWrite(LCD_EN, HIGH); pinMode(21, OUTPUT); // Set GPIO 21 as output for controlling the load digitalWrite(21, LOW); // Ensure it starts OFF while (FT3168->begin() == false) { Serial.println("FT3168 initialization fail"); delay(2000); } Serial.println("FT3168 initialization successfully"); gfx->begin(); gfx->Display_Brightness(200); String LVGL_Arduino = "Hello Arduino! "; LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch(); Serial.println( LVGL_Arduino ); Serial.println( "I am LVGL_Arduino" ); lv_init(); #if LV_USE_LOG != 0 lv_log_register_print_cb( my_print ); /* register print function for debugging */ #endif FT3168->IIC_Write_Device_State(FT3168->Arduino_IIC_Touch::Device::TOUCH_POWER_MODE, FT3168->Arduino_IIC_Touch::Device_Mode::TOUCH_POWER_MONITOR); lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * screenHeight / 10 ); /*Initialize the display*/ static lv_disp_drv_t disp_drv; lv_disp_drv_init( &disp_drv ); /*Change the following line to your display resolution*/ disp_drv.hor_res = screenWidth; disp_drv.ver_res = screenHeight; disp_drv.flush_cb = my_disp_flush; disp_drv.draw_buf = &draw_buf; lv_disp_drv_register( &disp_drv ); /*Initialize the (dummy) input device driver*/ static lv_indev_drv_t indev_drv; lv_indev_drv_init( &indev_drv ); indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = my_touchpad_read; lv_indev_drv_register( &indev_drv ); ui_init(); Serial.println( "Setup done" ); lv_timer_create(TemperatureTimerCallback, 1000, NULL); // Call every 1 second lv_obj_add_event_cb(ui_SwitchLoad, Load1Fun, LV_EVENT_VALUE_CHANGED, NULL); if (digitalRead(21)) { lv_obj_add_state(ui_SwitchLoad, LV_STATE_CHECKED); } else { lv_obj_clear_state(ui_SwitchLoad, LV_STATE_CHECKED); } } void loop() { lv_timer_handler(); /* let the GUI do its work */ delay(5); } void TemperatureTimerCallback(lv_timer_t * timer) { dallasTemperature.requestTemperatures(); //delay(50); sensor::MyTemp = dallasTemperature.getTempCByIndex(0); char tempStr[16]; if (sensor::MyTemp == DEVICE_DISCONNECTED_C) { ; } else { sprintf(tempStr, "%.2f°C", sensor::MyTemp); } lv_label_set_text(ui_lblTemperature, tempStr); lv_arc_set_value(ui_Arc1, (int)sensor::MyTemp); Serial.print("Temperature: "); Serial.println(tempStr); } void Load1Fun(lv_event_t * e) { bool state = lv_obj_has_state(ui_SwitchLoad, LV_STATE_CHECKED); // true if ON digitalWrite(21, state ? LOW : HIGH); Serial.println(state ? "Load ON (GPIO21 HIGH)" : "Load OFF (GPIO21 LOW)"); } |
Code Explanation:
I have also added this header file for the DS18B20 one-wire waterproof digital temperature sensor.
namespace pin:
Contains pin assignments.
one_wire_bus = 16 sets GPIO16 for the Dallas 1-Wire temperature sensor.
namespace sensor:
Stores sensor-related data.
MyTemp holds the latest temperature reading as a float.
|
1 |
OneWire oneWire(pin::one_wire_bus); |
Initializes the OneWire communication protocol on GPIO16. This is required to communicate with 1-Wire devices like the DS18B20 temperature sensor.
|
1 |
DallasTemperature dallasTemperature(&oneWire); |
Creates a DallasTemperature object and links it to the oneWire bus. This allows higher-level functions like reading temperature from the sensor easily.
|
1 2 3 |
pinMode(21, OUTPUT); // Set GPIO 21 as output for controlling the load digitalWrite(21, LOW); // Ensure it starts OFF |
Set GPIO 21 as output for controlling the load, in my case LED
Ensure it starts OFF.
These lines handle the automatic temperature reading and syncing the state of a UI switch with a physical GPIO pin.
|
1 |
lv_timer_create(TemperatureTimerCallback, 1000, NULL); |
This line creates an LVGL timer that calls the TemperatureTimerCallback function every 1000 milliseconds (1 second) to read the temperature from the sensor and update the display.
|
1 |
lv_obj_add_event_cb(ui_SwitchLoad, Load1Fun, LV_EVENT_VALUE_CHANGED, NULL); |
This line registers an event callback Load1Fun to the UI switch ui_SwitchLoad so that whenever the switch state is changed by the user, the function is triggered; typically to control a connected load like a relay or LED.
|
1 2 3 4 5 6 7 |
if (digitalRead(21)) { lv_obj_add_state(ui_SwitchLoad, LV_STATE_CHECKED); } else { lv_obj_clear_state(ui_SwitchLoad, LV_STATE_CHECKED); |
These lines check the current state of GPIO21 (e.g., connected to a physical load). If GPIO21 is HIGH, the switch on the screen is updated to the checked (on) state. If it’s LOW, the switch is visually cleared (unchecked). This ensures the GUI reflects the actual hardware state during setup or sync.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
void TemperatureTimerCallback(lv_timer_t * timer) { dallasTemperature.requestTemperatures(); //delay(50); sensor::MyTemp = dallasTemperature.getTempCByIndex(0); char tempStr[16]; if (sensor::MyTemp == DEVICE_DISCONNECTED_C) { ; } else { sprintf(tempStr, "%.2f°C", sensor::MyTemp); } lv_label_set_text(ui_lblTemperature, tempStr); lv_arc_set_value(ui_Arc1, (int)sensor::MyTemp); Serial.print("Temperature: "); Serial.println(tempStr); } |
The TemperatureTimerCallback function is triggered every second by an LVGL timer. It initiates a temperature reading from the Dallas temperature sensor by calling requestTemperatures() and then retrieves the temperature using getTempCByIndex(0). The value is stored in sensor::MyTemp. If the sensor is disconnected, the function skips updating the display. Otherwise, it formats the temperature into a string and updates a label (ui_lblTemperature) with the new temperature reading. Additionally, the temperature is set as the value of a UI arc (ui_Arc1), providing a graphical representation of the current temperature. This function ensures the user interface is refreshed with accurate, real-time temperature data.
|
1 2 3 4 5 6 7 8 9 |
void Load1Fun(lv_event_t * e) { bool state = lv_obj_has_state(ui_SwitchLoad, LV_STATE_CHECKED); // true if ON digitalWrite(21, state ? LOW : HIGH); Serial.println(state ? "Load ON (GPIO21 HIGH)" : "Load OFF (GPIO21 LOW)"); } |
The Load1Fun function acts as an event callback for a UI switch (ui_SwitchLoad). It checks whether the switch is in the checked (ON) state using lv_obj_has_state. If it is ON, it sets GPIO21 to LOW, and if it is OFF, it sets GPIO21 to HIGH; assuming an active-low configuration where LOW turns the load ON. This provides control over an external device like a relay or LED directly from the touchscreen interface. A message indicating the load status is also printed to the Serial Monitor for debugging or logging purposes.
Now, let’s go ahead and upload this program.
Practical Demonstration:
When I run the project, I see the temperature being displayed clearly both as a label and visually on the arc gauge.
During the UI design in SquareLine Studio, I made sure to uncheck the “clickable” flag for the arc, so now when I touch or swipe on the arc, it doesn’t rotate or respond; this is exactly what I wanted since the arc is just for displaying the temperature, not for interaction.
When I swipe over to screen2, there’s a switch that I can toggle to control a load. Right now, it’s turning an LED connected to GPIO21 on and off.
The switch works perfectly; when I turn it on, the LED lights up, and when I turn it off, the LED goes out.
Of course, instead of the LED, I could also connect a relay module to GPIO21 and use it to control an AC or DC load like a fan, lamp, or any other load. The whole interface feels responsive and functional.
So, that’s all for now.
Support me on Patreon for more videos and articles.
Watch Video Tutorial:
Discover more from Electronic Clinic
Subscribe to get the latest posts sent to your email.
























