ESP32 HMI 7.0-inch MicroPython Tutorial¶
Overview¶
The example tutorial is an environmental monitoring project, demonstrate how to create a UI and use a sensor to obtain the environment temperature and humidity and display it on the screen; and how to control the LED on and off by the buttons on the screen.
In this tutorial, we will show you how to design the UI with SquareLine Studio, and show you how to upload the code with Thonny IDE.
Hardware Preparation¶
CrowPanel ESP32 7.0'' HMI | Crowtail-AM2302 | Crowtail-LED | Crowtail Cable |
---|---|---|---|
Design UI file with SquareLine Studio¶
Get Started with SquareLine Studio¶
Please click the card below to learn how to download the SquareLine Studio, and how to export the demo UI file.
Design UI file with SquareLine Studio¶
Let's start learning how to create our own UI after getting an initial understanding of SquareLine Studio.
-
Open the SquareLine Studio and create a project. Select "Desktop"->"Micropython for Unix and other platform".
-
Set the name of the project, set the screen resolution to 800*480, set the color depth to 16bit, and keep other default settings. After setting, click CREATE to create the project.
- 16-bit color depth: can represent 65,536 colors through RGB 5:6:5 sub-pixel representation, that is, each RGB channel occupies 5 bits and 1 bit (a total of 16 bits) to represent colors.
-
After creation, enter the following interface with a blank background.
-
In the "Assets" area, click "ADD FILE TO ASSETS" to add custom images or icons.
Please click to download the custom images used in this tutorial.
-
Add background.
Click "Image" to add the image widgets.
Find "Inspector"->"STYLE SETTING", click to expand "STYLE(MAIN)", then click the 2nd "Background". Check the "Bg Image" and select a background picture.
Adjust the position of the image.
Note: Because it requires a large amount of memory when running in Thonny IDE, the UI background resolution of this tutorial is small and not full screen, so it is necessary to modify the background to the same color as the image.
-
Add Label widget to show temperature and humidity value
Click "Label" in the "Widgets" area, and "Label1" will be added to the current Screen.
The content, position and size of the label can be adjusted by dragging the mouse. You can also directly enter numbers in the Inspector→LABEL→Transform to adjust. You can set the font color and other attributes in STYLE SETTING→STYLE(MAIN).
You can set the color and other attributes of the font in STYLE SETTING ->STYLE (MAIN).
Duplicate a Label2 to show humidity value.
Adjust the position.
You can change the text of the label to set a default value.
-
Add Button widget to control the LED.
Click "Button" in the "Widgets" area, and "Button1" will be added to the current Screen.
The position and size of the label can be adjusted by dragging the mouse. You can also directly enter numbers in the Inspector→BUTTON→Transform to adjust.
Add an identification symbol to the button. The button in this tutorial controls the LED switch, so you only need to mark the button "on" and "off". You can add LABEL widgets or add a background images to the button. This tutorial will demonstrate how to add a background image to a button.
Click the Button1, then find Inspector->STYLE SETTINGS ->STYLE(MAIN) ->Background, and select the image.
Duplicate a Button2 for "OFF".
Set the status of the button to identify different states.
In "Inspector"->"STYLE SETTINGS"->"STATE", set display white background color by DEFAULT and red when on the PRESSED state.
-
Add events to buttons.
Note: Because the button controls the on and off of the LED, we can add any event here to generate the code framework for the button event when exporting the UI file. We will modify the code of the button event to control the LED latter.
Select the button and click "ADD EVENT".
Select "released" as the trigger condition, select a trigger event in "Action". It will be modified in the generated program to achieve the LED control function.
Complete the event. Here I choose to change the screen, and the screen to be switched is Screen1.
Make the same settings for the "OFF" button.
-
Export UI files.
Click "File" -> "Project Settings" and make settings for the exported file.
Set the export path of the file (set the path according to your own file).
Fill in lvgl.h in LVGL Include Path. Check "Flat export(exports all files to one folder )".
Then click "APPLY CHANGES".
Export UI files. The exported files will be in the path we set earlier.
The UI file export is completed. Next we're going to learn about the main program and learn how to upload the code to the board with Thonny IDE.
Build the Project with Thonny IDE¶
Download Thonny IDE¶
-
Go to the website https://thonny.org/ and download the corresponding software version (here we take the Windows version as an example)
-
Double-click the downloaded exe file to install the software.
Upload firmware¶
-
Connect the CrowPanel ESP32 HMI with your computer.
-
Open Thonny IDE and click "Tools"->"Options"->"Interpreter".
-
Select "MicroPython(ESP32)" for interpreter.
-
Select the corresponding serial port(or Try to detect port automatically).
-
Click "Install or update MicroPython (esptool)"
-
Click the icon with 3 lines, and click "select local MicroPython image...".Select the "firmware-7.0-A.bin" and install.
-
Waiting for downloading...
Code Explanation¶
Please click to download the code file and libraries. And open the CrowPanel_ESP32_7.0.py
Libraries imported in this example¶
import lvgl as lv
import lv_utils
import tft_config
import time
import fs_driver
import gt911
from machine import Pin, I2C
import dht
import ui_images
- lvgl: This is an open-source embedded graphics library used to create cross platform GUI. LVGL is written in C, but provides bindings for multiple languages, including Python. It supports multiple display types and input devices, making it ideal for touch screen applications on microcontrollers.
- lv_utils: This library may be an auxiliary library for LVGL, providing additional utilities or functions to help developers use LVGL more easily. Specific functions may include simplified API calls, universal configuration functions, etc.
- tft_config: This library may be used to configure TFT (Thin Film Transistor) displays. In embedded systems, TFT displays are commonly used to display graphical interfaces.The
tft_config
library may contain functions for initializing the display, setting resolution, color depth, and other configurations. - machine : This is a standard library for MicroPython used for interacting with hardware devices. It provides methods for accessing and controlling hardware interfaces such as GPIO (General Purpose Input/Output) pins, SPI (Serial Peripheral Interface) bus, I2C (Integrated Circuit Bus) of microcontrollers.
- time: This is the standard library in Python used for time related operations, such as pausing program execution (sleep function), retrieving the current time, etc.
- fs_deriver: This library may refer to a file system driver used to manage file systems on microcontrollers. It may include functions such as reading and writing files, directory operations, etc.
- ui_images: This library may be used to manage image resources in the user interface. It may include functions such as loading images into memory and displaying images on the GUI.
- GT911: This may be a touch controller library used to interact with the touch chip of the GT911 model and obtain touch events.
- DHT: This library is used to interact with temperature and humidity sensors of DHT models, reading temperature and humidity data.
Basic Definition¶
Set up LED and sensor.
pin38 = Pin(38, Pin.OUT)# Set GPIO pin 38 to output mode
pin38.value(0)
#Initialize temperature and humidity sensor
sensor = dht.DHT22(Pin(44)) # Create a DHT22 object and specify the GPIO pin to connect to
Screen and touch driver initialization¶
# Define the width and height of the LCD display
WIDTH = 800
HEIGHT = 480
# tft drvier
tft = tft_config.config()
# touch drvier
# Initialize I2C bus for touch driver communication
i2c = I2C(1, scl=Pin(20), sda=Pin(19), freq=400000) # Create an I2C object, specify the I2C port number, clock and data line pins, and frequency
# Initialize GT911 touch controller and set screen resolution
tp = gt911.GT911(i2c, width=800, height=480) # Create GT911 object, pass in I2C bus and screen resolution
# Set screen rotation direction of touch controller
tp.set_rotation(tp.ROTATION_INVERTED) # Set rotation direction to inverted
# Initialize LVGL graphics library
lv.init() # Initialize LVGL
# Check if LVGL event loop is running, if not, create one
if not lv_utils.event_loop.is_running():
event_loop = lv_utils.event_loop() # Create event loop object
print(event_loop.is_running()) # Print if event loop is running
# Create display driver buffer
disp_buf0 = lv.disp_draw_buf_t() # Create display driver buffer object
buf1_0 = bytearray(WIDTH * 50) # Allocate buffer memory, where WIDTH is the width of the screen
disp_buf0.init(buf1_0, None, len(buf1_0) // lv.color_t.__SIZE__) # Initialize buffer
# Register display driver
disp_drv = lv.disp_drv_t() # Create display driver object
disp_drv.init() # Initialize display driver
disp_drv.draw_buf = disp_buf0 # Set the display driver buffer
disp_drv.flush_cb = tft.flush # Set refresh callback function to refresh display content to TFT
disp_drv.hor_res = WIDTH # Set the horizontal resolution of the display driver
disp_drv.ver_res = HEIGHT # Set the vertical resolution of the display driver
# disp_drv.user_data = {"swap": 0} # Optional: Set user data, such as swapping color channels
disp0 = disp_drv.register() # Register display driver
lv.disp_t.set_default(disp0) # Set default display driver
# Initialize touch input device driver
indev_drv = lv.indev_drv_t() # Create input device driver object
indev_drv.init() # Initialize input device driver
indev_drv.disp = disp0 # Associate display driver
indev_drv.type = lv.INDEV_TYPE.POINTER # Set input device type to pointer
indev_drv.read_cb = tp.lvgl_read # Set callback function for reading touch data
indev = indev_drv.register() # Register input device driver
UI code¶
This part copies the code of ui.py generated by Squareline Studio. We need to modify the part that control the button.
Modify from
to
def Button1_eventhandler(event_struct):
event = event_struct.code
if event == lv.EVENT.CLICKED and True:
pin38.value(1)
return
def Button2_eventhandler(event_struct):
event = event_struct.code
if event == lv.EVENT.CLICKED and True:
pin38.value(0)
return
TEM_HUM class¶
Define a class called TEM_HUM to initialize and periodically update the temperature and humidity display on the user interface.
class TEM_HUM():
def __init__(self, ui_Screen1):
global tem, hum # Here we use global variables tem and hum to store the temperature and humidity values of the sensor
tem = sensor.temperature()
hum = sensor.humidity()
# Update the temperature and humidity display on the UI interface, and use the round function to round the value to an integer
ui_Label1.set_text(f"{round(tem)}") # Update the temperature display to Celsius
ui_Label2.set_text(f"{round(hum)}") # Update the humidity display to percentage
# Instantiate the TEM_HUM class and pass in the UI interface object ui_Screen1
TEM_HUM(ui_Screen1)
# Load the UI interface to the screen
lv.scr_load(ui_Screen1)
# Main loop, continuously detect sensors and update UI interface
while True:
try:
time.sleep(0.02) # Short delay to reduce CPU usage
sensor.measure() # Trigger sensor to measure
temp = sensor.temperature()
hum = sensor.humidity()
#temp_f = temp * (9/5) + 32.0 # convert temperature to Fahrenheit, currently not used
print('Temperature: %3.1f C' %temp)
#print('Temperature: %3.1f F' %temp_f)
print('Humidity: %3.1f %%' %hum)
ui_Label1.set_text(f"{round(temp)}") # Update temperature display
ui_Label2.set_text(f"{round(hum)}") # Update humidity display
except OSError as e:
print('Failed to read sensor.') # If an error occurs when reading the sensor, print the error message
Upload the code¶
Upload the libraries¶
ui_image.py
-
In the upper left corner of the thonny, enter the path where ui_image.py is located, right-click ui_image.py, and click Upload to/
-
ui_image.py is added to MicroPython device successfully.
Upload the code¶
Click the "run" icon to run and upload the CrowPanel_ESP32_7.0.py.
Successfully uploaded.
GPIO Examples¶
Please click to download the code files.
Example 1 Turn on/off the LED automatically in a loop¶
- Connect an LED module to the D port(pin38)
- Open and upload the led.py.
- The LED will turn on and off in a loop.
#Make by Elecrow
#Web:www.elecrow.com
import time
from machine import Pin
pin40 = Pin(38, Pin.OUT)
while True:
pin40.value(1)
time.sleep(0.5)
pin40.value(0)
time.sleep(0.5)
Example 2 Show a button on the screen¶
- Open and upload the button-1-7.0.py.
- A button will show on the screen.
#Make by Elecrow
#Web:www.elecrow.com
import lvgl as lv
import lv_utils
import tft_config
import gt911
from machine import Pin, I2C
import time
LV_FONT_MONTSERRAT_28=0
WIDTH = 800
HEIGHT = 480
# tft drvier
tft = tft_config.config()
# touch drvier
i2c = I2C(1, scl=Pin(20), sda=Pin(19), freq=400000)
tp = gt911.GT911(i2c, width=800, height=480)
tp.set_rotation(tp.ROTATION_NORMAL)
lv.init()
if not lv_utils.event_loop.is_running():
event_loop=lv_utils.event_loop()
print(event_loop.is_running())
# create a display 0 buffer
disp_buf0 = lv.disp_draw_buf_t()
buf1_0 = bytearray(WIDTH * 10)
disp_buf0.init(buf1_0, None, len(buf1_0) // lv.color_t.__SIZE__)
# register display driver
disp_drv = lv.disp_drv_t()
disp_drv.init()
disp_drv.draw_buf = disp_buf0
disp_drv.flush_cb = tft.flush
disp_drv.hor_res = WIDTH
disp_drv.ver_res = HEIGHT
# disp_drv.user_data = {"swap": 0}
disp0 = disp_drv.register()
lv.disp_t.set_default(disp0)
# touch driver init
indev_drv = lv.indev_drv_t()
indev_drv.init()
indev_drv.disp = disp0
indev_drv.type = lv.INDEV_TYPE.POINTER
indev_drv.read_cb = tp.lvgl_read
indev = indev_drv.register()
scr = lv.obj()
class CounterBtn():
def __init__(self, scr):
self.cnt = 0
btn = lv.btn(scr)
btn.set_size(120, 50)
btn.align(lv.ALIGN.CENTER,0,0)
btn.add_event_cb(self.btn_event_cb, lv.EVENT.ALL, None)
label = lv.label(btn)
label.set_style_text_font(lv.font_montserrat_32, 0)
label.set_text("Button")
label.center()
def btn_event_cb(self, evt):
code = evt.get_code()
btn = evt.get_target()
if code == lv.EVENT.CLICKED:
self.cnt += 1
label = btn.get_child(0)
label.set_text("Button: " + str(self.cnt))
counterBtn = CounterBtn(scr)
lv.scr_load(scr)
try:
from machine import WDT
wdt = WDT(timeout=1000) # enable it with a timeout of 2s
print("Hint: Press Ctrl+C to end the program")
while True:
wdt.feed()
time.sleep(0.9)
except KeyboardInterrupt as ret:
print("The program stopped running, ESP32 has restarted...")
Example 3 Show dial plate on the screen¶
Upload the biaopan-7.0.py to the module, there will be a dial plate show on the screen.
#Make by Elecrow
#Web:www.elecrow.com
import lvgl as lv
import lv_utils
import tft_config
import time
import fs_driver
import gt911
from machine import Pin, I2C
WIDTH = 800
HEIGHT = 480
# tft drvier
tft = tft_config.config()
# touch drvier
i2c = I2C(1, scl=Pin(20), sda=Pin(19), freq=400000)
tp = gt911.GT911(i2c, width=800, height=480)
tp.set_rotation(tp.ROTATION_NORMAL)
lv.init()
if not lv_utils.event_loop.is_running():
event_loop=lv_utils.event_loop()
print(event_loop.is_running())
# create a display 0 buffer
disp_buf0 = lv.disp_draw_buf_t()
buf1_0 = bytearray(WIDTH * 50)
disp_buf0.init(buf1_0, None, len(buf1_0) // lv.color_t.__SIZE__)
# register display driver
disp_drv = lv.disp_drv_t()
disp_drv.init()
disp_drv.draw_buf = disp_buf0
disp_drv.flush_cb = tft.flush
disp_drv.hor_res = WIDTH
disp_drv.ver_res = HEIGHT
# disp_drv.user_data = {"swap": 0}
disp0 = disp_drv.register()
lv.disp_t.set_default(disp0)
# touch driver init
indev_drv = lv.indev_drv_t()
indev_drv.init()
indev_drv.disp = disp0
indev_drv.type = lv.INDEV_TYPE.POINTER
indev_drv.read_cb = tp.lvgl_read
indev = indev_drv.register()
# 1. Create a display screen. Will need to display the component added to the screen to display
scr = lv.obj() # scr====> screen
fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, 'S')
scr = lv.scr_act()
scr.clean()
# 2. Encapsulate the component to display
class MyWidget():
def __init__(self, scr):
# 1. Create the dashboard object
self.meter = lv.meter(scr)
self.meter.center()
self.meter.set_size(200, 200) # width: 200 height: 200
# 2. To create calibration object
scale = self.meter.add_scale()
self.meter.set_scale_ticks(scale, 51, 2, 10, lv.palette_main(lv.PALETTE.GREY))
self.meter.set_scale_major_ticks(scale, 10, 4, 15, lv.color_black(), 20)
# 3. Add warning scale line
blue_arc = self.meter.add_arc(scale, 2, lv.palette_main(lv.PALETTE.BLUE), 0)
self.meter.set_indicator_start_value(blue_arc, 0)
self.meter.set_indicator_end_value(blue_arc, 20)
blue_arc_scale = self.meter.add_scale_lines(scale, lv.palette_main(lv.PALETTE.BLUE), lv.palette_main(lv.PALETTE.BLUE), False, 0)
self.meter.set_indicator_start_value(blue_arc_scale, 0)
self.meter.set_indicator_end_value(blue_arc_scale, 20)
red_arc = self.meter.add_arc(scale, 2, lv.palette_main(lv.PALETTE.RED), 0)
self.meter.set_indicator_start_value(red_arc, 80)
self.meter.set_indicator_end_value(red_arc, 100)
red_arc_scale = self.meter.add_scale_lines(scale, lv.palette_main(lv.PALETTE.RED), lv.palette_main(lv.PALETTE.RED), False, 0)
self.meter.set_indicator_start_value(red_arc_scale, 80)
self.meter.set_indicator_end_value(red_arc_scale, 100)
# 4. meter needle
self.indic = self.meter.add_needle_line(scale, 4, lv.palette_main(lv.PALETTE.GREY), -10)
# 5. Creating animated objects
a = lv.anim_t()
a.init()
a.set_var(self.indic)
a.set_values(0, 100)
a.set_time(2000)
a.set_repeat_delay(100)
a.set_playback_time(500)
a.set_playback_delay(100)
a.set_repeat_count(lv.ANIM_REPEAT.INFINITE)
a.set_custom_exec_cb(self.set_value)
lv.anim_t.start(a)
def set_value(self, anmi_obj, value):
"""Animation callbacks"""
self.meter.set_indicator_value(self.indic, value)
# 3. Create the component to display
MyWidget(scr)
# 4. Displays the contents of the screen object
lv.scr_load(scr)
# ------------------------------ Guard dog to restart ESP32 equipment --start------------------------
try:
from machine import WDT
wdt = WDT(timeout=1000) # enable it with a timeout of 2s
print("Hint: Press Ctrl+C to end the program")
while True:
wdt.feed()
time.sleep(0.9)
except KeyboardInterrupt as ret:
print("The program stopped running, ESP32 has restarted...")
tft.deinit()
time.sleep(10)
# ------------------------------ Guard dog to restart ESP32 equipment --stop-------------------------
Example 4 Connect WiFi¶
Upload the wifi.py to ESP32 HMI(note to modify the WiFi ssid and password to yours)
import network
import time
def connect():
ssid = 'yanfa_software'
password = 'yanfa-123456'
wlan = network.WLAN(network.STA_IF) # Create a WLAN object in station mode
wlan.active(True) # Activate the network interface
wlan.connect(ssid, password) # Connect to the specified WiFi network
while not wlan.isconnected(): # Wait for the connection to be established
print('Waiting for connection...')
time.sleep(1)
print('Connected on {ip}'.format(ip=wlan.ifconfig()[0])) # Print the IP address
connect()
Running result:
Example 5 Initialize SD card¶
Upload the sd.py to ESP32 HMI
import machine
import os
import sdcard
import uos
# SD Card Initialization
def init_sd():
# Creating SPI Objects
spi = machine.SPI(2, baudrate=1000000, polarity=0, phase=0, sck=machine.Pin(12), mosi=machine.Pin(11), miso=machine.Pin(13))
cs = machine.Pin(10, machine.Pin.OUT)
# SD Card Initialization
sd = sdcard.SDCard(spi, cs)
vfs = uos.VfsFat(sd)
uos.mount(vfs, "/sd")
print("SD card initialization complete")
print("List of documents:", os.listdir("/sd"))
# write to a file
def write_file(filename, data):
with open("/sd/" + filename, "w") as file:
file.write(data)
print("Data has been written to file:", filename)
# Read file
def read_file(filename):
with open("/sd/" + filename, "r") as file:
data = file.read()
print("readout:", data)
return data
# Example: Initialize SD card and read/write files
def main():
init_sd()
filename = "example.txt"
data = "Hello, SD Card!"
write_file(filename, data)
read_file(filename)
if __name__ == "__main__":
main()
Example 6 Playing music¶
Connect a speaker to the ESP32 HMI. Upload the play_tone.py.
import os
import math
import struct
from machine import I2S
from machine import Pin
def make_tone(rate, bits, frequency):
# create a buffer containing the pure tone samples
samples_per_cycle = rate // frequency
sample_size_in_bytes = bits // 8
samples = bytearray(samples_per_cycle * sample_size_in_bytes)
volume_reduction_factor = 32
range = pow(2, bits) // 2 // volume_reduction_factor
if bits == 16:
format = "<h"
else: # assume 32 bits
format = "<l"
for i in range(samples_per_cycle):
sample = range + int((range - 1) * math.sin(2 * math.pi * i / samples_per_cycle))
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
return samples
if os.uname().machine.count("PYBv1"):
# ======= I2S CONFIGURATION =======
SCK_PIN = "Y6"
WS_PIN = "Y5"
SD_PIN = "Y8"
I2S_ID = 2
BUFFER_LENGTH_IN_BYTES = 2000
# ======= I2S CONFIGURATION =======
elif os.uname().machine.count("PYBD"):
import pyb
pyb.Pin("EN_3V3").on() # provide 3.3V on 3V3 output pin
# ======= I2S CONFIGURATION =======
SCK_PIN = "Y6"
WS_PIN = "Y5"
SD_PIN = "Y8"
I2S_ID = 2
BUFFER_LENGTH_IN_BYTES = 2000
# ======= I2S CONFIGURATION =======
elif os.uname().machine.count("ESP32"):
# ======= I2S CONFIGURATION =======
SCK_PIN = 42
WS_PIN = 18
SD_PIN = 17
I2S_ID = 0
BUFFER_LENGTH_IN_BYTES = 2000
# ======= I2S CONFIGURATION =======
elif os.uname().machine.count("Raspberry"):
# ======= I2S CONFIGURATION =======
SCK_PIN = 16
WS_PIN = 17
SD_PIN = 18
I2S_ID = 0
BUFFER_LENGTH_IN_BYTES = 1000
# ======= I2S CONFIGURATION =======
elif os.uname().machine.count("MIMXRT"):
# ======= I2S CONFIGURATION =======
SCK_PIN = 4
WS_PIN = 3
SD_PIN = 2
I2S_ID = 2
BUFFER_LENGTH_IN_BYTES = 2000
# ======= I2S CONFIGURATION =======
else:
print("Warning: program not tested with this board")
# ======= AUDIO CONFIGURATION =======
TONE_FREQUENCY_IN_HZ = 440
SAMPLE_SIZE_IN_BITS = 16
FORMAT = I2S.MONO # only MONO supported in this example
SAMPLE_RATE_IN_HZ = 22_050
# ======= AUDIO CONFIGURATION =======
audio_out = I2S(
I2S_ID,
sck=Pin(SCK_PIN),
ws=Pin(WS_PIN),
sd=Pin(SD_PIN),
mode=I2S.TX,
bits=SAMPLE_SIZE_IN_BITS,
format=FORMAT,
rate=SAMPLE_RATE_IN_HZ,
ibuf=BUFFER_LENGTH_IN_BYTES,
)
samples = make_tone(SAMPLE_RATE_IN_HZ, SAMPLE_SIZE_IN_BITS, TONE_FREQUENCY_IN_HZ)
# continuously write tone sample buffer to an I2S DAC
print("========== START PLAYBACK ==========")
try:
while True:
num_written = audio_out.write(samples)
except (KeyboardInterrupt, Exception) as e:
print("caught exception {} {}".format(type(e).__name__, e))
# cleanup
audio_out.deinit()
print("Done")
For more demo of the i2s speaker please refer to: https://github.com/miketeachman/micropython-i2s-examples
Example 7 Initialize UART port¶
Upload the uart.py
import machine
import time
# Initialize UART
uart = machine.UART(1, baudrate=115200, tx=43, rx=44)
def send_data(data):
uart.write(data) # Send data via UART
print("Sent:", data)
def receive_data():
if uart.any(): # Check for readable data
data = uart.read() # retrieve data
print("Received:", data)
return data
return None
# Example: Sending and Receiving Data
send_data('Hello, UART!\n')
while True:
received = receive_data()
if received:
# do something about it
pass
time.sleep(1)
Running result: