Skip to content

ESP32 HMI 5.0-inch MicroPython Tutorial

Overview


he 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.

CrowPanel_ESP32 Display-5.0-micropython_led

Hardware Preparation

CrowPanel ESP32 5.0'' HMI Crowtail-AM2302 Crowtail-LED Crowtail Cable
CrowPanel-ESP32-5-0inch AM2302 CROWTAIL-LED crowtail-cable
BUYNOW BUYNOW BUYNOW BUYNOW

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.

GetStartedWithSquareLine

Design UI file with SquareLine Studio

Let's start learning how to create our own UI after getting an initial understanding of SquareLine Studio.

  1. Open the SquareLine Studio and create a project. Select "Desktop"->"Micropython for Unix and other platform".

    SLS-UI-1

  2. 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.

    SLS-UI-2

    • 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.
  3. After creation, enter the following interface with a blank background.

    SLS-UI-3

  4. In the "Assets" area, click "ADD FILE TO ASSETS" to add custom images or icons.

    Please click download to download the custom images used in this tutorial.

    SLS-UI-3-1

  5. Add background.

    Click "Image" to add the image widgets.

    SLS-UI-4

    Find "Inspector"->"STYLE SETTING", click to expand "STYLE(MAIN)", then click the 2nd "Background". Check the "Bg Image" and select a background picture.

    SLS-UI-5

    Adjust the position of the image.

    SLS-UI-6

    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.

  6. Add Label widget to show temperature and humidity value

    Click "Label" in the "Widgets" area, and "Label1" will be added to the current Screen.

    SLS-LABEL

    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).

    SLS-LABEL-1

    You can set the color and other attributes of the font in STYLE SETTING ->STYLE (MAIN).

    SLS-LABEL-2

    Duplicate a Label2 to show humidity value.

    SLS-LABEL-3

    Adjust the position.

    SLS-LABEL-4

    You can change the text of the label to set a default value.

    label-default

  7. Add Button widget to control the LED.

    Click "Button" in the "Widgets" area, and "Button1" will be added to the current Screen.

    SLS-UI-8

    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.

    SLS-UI-9

    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.

    SLS-UI-10

    Duplicate a Button2 for "OFF".

    SLS-UI-11

    SLS-UI-12

    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.

    SLS-UI-13

    SLS-UI-14

  8. 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".

    SLS-UI-15

    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.

    SLS-UI-16

    Complete the event. Here I choose to change the screen, and the screen to be switched is Screen1.

    SLS-UI-17

    Make the same settings for the "OFF" button.

    SLS-UI-18

  9. Export UI files.

    Click "File" -> "Project Settings" and make settings for the exported file.

    SLS-UI-19

    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".

    SLS-UI-20

    Export UI files. The exported files will be in the path we set earlier.

    SLS-UI-21

    SLS-UI-22

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

  1. Go to the website https://thonny.org/ and download the corresponding software version (here we take the Windows version as an example)

    Thonny-1

  2. Double-click the downloaded exe file to install the software.

    Thonny-2

Upload firmware

  1. Connect the CrowPanel ESP32 HMI with your computer.

  2. Open Thonny IDE and click "Tools"->"Options"->"Interpreter".

    Thonny-3.pngThonny-4.png

  3. Select "MicroPython(ESP32)" for interpreter.

    Thonny-5.png

  4. Select the corresponding serial port(or Try to detect port automatically).

    Thonny-6.png

  5. Click "Install or update MicroPython (esptool)"

    Thonny-7.png

  6. Click the icon with 3 lines, and click "select local MicroPython image...".Select the "firmware-5.0-A.bin" and install.

    Please click download to download the bin file.

    Thonny-8.png

    Thonny-9.png

  7. Waiting for downloading...

    Thonny-10.png

    Thonny-11.png

Edit the code

Please click download to download the code file and libraries.

#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
import dht
import ui_images

WIDTH = 800
HEIGHT = 480


# tft drvier
tft = tft_config.config()

pin38 = Pin(38, Pin.OUT)
pin38.value(0)
# touch drvier
i2c = I2C(1, scl=Pin(20), sda=Pin(19), freq=400000)
sensor = dht.DHT22(Pin(44))

tp = gt911.GT911(i2c, width=800, height=480)
tp.set_rotation(tp.ROTATION_INVERTED)

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()

dispp = lv.disp_get_default()
theme = lv.theme_default_init(dispp, lv.palette_main(lv.PALETTE.BLUE), lv.palette_main(lv.PALETTE.RED), False, lv.font_default())
dispp.set_theme(theme)

def SetFlag( obj, flag, value):
    if (value):
        obj.add_flag(flag)
    else:
        obj.clear_flag(flag)
    return

_ui_comp_table = {}
_ui_comp_prev = None
_ui_name_prev = None
_ui_child_prev = None
_ui_comp_table.clear()

def _ui_comp_del_event(e):
    target = e.get_target()
    _ui_comp_table[id(target)].remove()

def ui_comp_get_child(comp, child_name):
    return _ui_comp_table[id(comp)][child_name]

def ui_comp_get_root_from_child(child, compname):
    for component in _ui_comp_table:
        if _ui_comp_table[component]["_CompName"]==compname:
            for part in _ui_comp_table[component]:
                if id(_ui_comp_table[component][part]) == id(child):
                    return _ui_comp_table[component]
    return None
def SetBarProperty(target, id, val):
   if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON)
   if id == 'Value': target.set_value(val, lv.ANIM.OFF)
   return

def SetPanelProperty(target, id, val):
   if id == 'Position_X': target.set_x(val)
   if id == 'Position_Y': target.set_y(val)
   if id == 'Width': target.set_width(val)
   if id == 'Height': target.set_height(val)
   return

def SetDropdownProperty(target, id, val):
   if id == 'Selected':
      target.set_selected(val)
   return

def SetImageProperty(target, id, val, val2):
   if id == 'Image': target.set_src(val)
   if id == 'Angle': target.set_angle(val2)
   if id == 'Zoom': target.set_zoom(val2)
   return

def SetLabelProperty(target, id, val):
   if id == 'Text': target.set_text(val)
   return

def SetRollerProperty(target, id, val):
   if id == 'Selected':
      target.set_selected(val, lv.ANIM.OFF)
   if id == 'Selected_with_anim':
      target.set_selected(val, lv.ANIM.ON)
   return

def SetSliderProperty(target, id, val):
   if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON)
   if id == 'Value': target.set_value(val, lv.ANIM.OFF)
   return

def ChangeScreen( src, fademode, speed, delay):
    lv.scr_load_anim(src, fademode, speed, delay, False)
    return

def DeleteScreen(src):
    return

def IncrementArc( trg, val):
    trg.set_value(trg.get_value()+val)
    lv.event_send(trg,lv.EVENT.VALUE_CHANGED, None)
    return

def IncrementBar( trg, val, anim):
    trg.set_value(trg.get_value()+val,anim)
    return

def IncrementSlider( trg, val, anim):
    trg.set_value(trg.get_value()+val,anim)
    lv.event_send(trg,lv.EVENT.VALUE_CHANGED, None)
    return

def KeyboardSetTarget( keyboard, textarea):
    keyboard.set_textarea(textarea)
    return

def ModifyFlag( obj, flag, value):
    if (value=="TOGGLE"):
        if ( obj.has_flag(flag) ):
            obj.clear_flag(flag)
        else:
            obj.add_flag(flag)
        return

    if (value=="ADD"):
        obj.add_flag(flag)
    else:
        obj.clear_flag(flag)
    return

def ModifyState( obj, state, value):
    if (value=="TOGGLE"):
        if ( obj.has_state(state) ):
            obj.clear_state(state)
        else:
            obj.add_state(state)
        return

    if (value=="ADD"):
        obj.add_state(state)
    else:
        obj.clear_state(state)
    return

def set_opacity(obj, v):
    obj.set_style_opa(v, lv.STATE.DEFAULT|lv.PART.MAIN)
    return

def SetTextValueArc( trg, src, prefix, postfix):
    trg.set_text(prefix+str(src.get_value())+postfix)
    return

def SetTextValueSlider( trg, src, prefix, postfix):
    trg.set_text(prefix+str(src.get_value())+postfix)
    return

def SetTextValueChecked( trg, src, txton, txtoff):
    if src.has_state(lv.STATE.CHECKED):
        trg.set_text(txton)
    else:
        trg.set_text(txtoff)
    return

def StepSpinbox( trg, val):
    if val==1 : trg.increment()
    if val==-1 : trg.decrement()
    lv.event_send(trg,lv.EVENT.VALUE_CHANGED, None)
    return

# COMPONENTS

 # COMPONENT Button2
def ui_Button2_create(comp_parent):
    cui_Button2 = lv.btn(comp_parent)
    cui_Button2.set_width(100)
    cui_Button2.set_height(50)
    cui_Button2.set_x(4)
    cui_Button2.set_y(32)
    cui_Button2.set_align( lv.ALIGN.CENTER)
    SetFlag(cui_Button2, lv.obj.FLAG.SCROLLABLE, False)
    SetFlag(cui_Button2, lv.obj.FLAG.SCROLL_ON_FOCUS, True)
    _ui_comp_table[id(cui_Button2)]= {"Button2" : cui_Button2, "_CompName" : "Button2"}
    return cui_Button2

ui____initial_actions0 = lv.obj()

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

ui_Screen1 = lv.obj()
SetFlag(ui_Screen1, lv.obj.FLAG.SCROLLABLE, False)
ui_Screen1.set_style_bg_color(lv.color_hex(0xEFF6DB), lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Screen1.set_style_bg_opa(255, lv.PART.MAIN| lv.STATE.DEFAULT )
ui_Screen1.set_style_bg_img_src( ui_images.TemporaryImage, lv.PART.MAIN | lv.STATE.DEFAULT )

ui_Button1 = lv.btn(ui_Screen1)
ui_Button1.set_width(85)
ui_Button1.set_height(85)
ui_Button1.set_x(208)
ui_Button1.set_y(-90)
ui_Button1.set_align( lv.ALIGN.CENTER)
SetFlag(ui_Button1, lv.obj.FLAG.SCROLLABLE, False)
SetFlag(ui_Button1, lv.obj.FLAG.SCROLL_ON_FOCUS, True)
ui_Button1.set_style_bg_color(lv.color_hex(0xFFFFFF), lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Button1.set_style_bg_opa(255, lv.PART.MAIN| lv.STATE.DEFAULT )
ui_Button1.set_style_bg_img_src( ui_images.ui_img_on_png, lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Button1.set_style_bg_color(lv.color_hex(0xE61717), lv.PART.MAIN | lv.STATE.PRESSED )
ui_Button1.set_style_bg_opa(255, lv.PART.MAIN| lv.STATE.PRESSED )

ui_Button1.add_event_cb(Button1_eventhandler, lv.EVENT.ALL, None)
ui_Button2 = lv.btn(ui_Screen1)
ui_Button2.set_width(85)
ui_Button2.set_height(85)
ui_Button2.set_x(205)
ui_Button2.set_y(40)
ui_Button2.set_align( lv.ALIGN.CENTER)
SetFlag(ui_Button2, lv.obj.FLAG.SCROLLABLE, False)
SetFlag(ui_Button2, lv.obj.FLAG.SCROLL_ON_FOCUS, True)
ui_Button2.set_style_bg_color(lv.color_hex(0xFBF9F9), lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Button2.set_style_bg_opa(255, lv.PART.MAIN| lv.STATE.DEFAULT )
ui_Button2.set_style_bg_img_src( ui_images.ui_img_off_png, lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Button2.set_style_bg_color(lv.color_hex(0xB9B3B3), lv.PART.MAIN | lv.STATE.PRESSED )
ui_Button2.set_style_bg_opa(255, lv.PART.MAIN| lv.STATE.PRESSED )

ui_Button2.add_event_cb(Button2_eventhandler, lv.EVENT.ALL, None)
ui_Image1 = lv.img(ui_Screen1)
ui_Image1.set_src(ui_images.ui_img_background1_png)
ui_Image1.set_width(lv.SIZE.CONTENT)    # 1
ui_Image1.set_height(lv.SIZE.CONTENT)   # 1
ui_Image1.set_x(-90)
ui_Image1.set_y(-14)
ui_Image1.set_align( lv.ALIGN.CENTER)
SetFlag(ui_Image1, lv.obj.FLAG.ADV_HITTEST, True)
SetFlag(ui_Image1, lv.obj.FLAG.SCROLLABLE, False)

ui_Label1 = lv.label(ui_Screen1)
ui_Label1.set_text("23")
ui_Label1.set_width(25) # 1
ui_Label1.set_height(25)   # 1
ui_Label1.set_x(-49)
ui_Label1.set_y(-89)
ui_Label1.set_align( lv.ALIGN.CENTER)
ui_Label1.set_style_text_color(lv.color_hex(0xFDF9F9), lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Label1.set_style_text_opa(255, lv.PART.MAIN| lv.STATE.DEFAULT )
ui_Label1.set_style_text_font( lv.font_montserrat_16, lv.PART.MAIN | lv.STATE.DEFAULT )

ui_Label2 = lv.label(ui_Screen1)
ui_Label2.set_text("45")
ui_Label2.set_width(25) # 1
ui_Label2.set_height(25)   # 1
ui_Label2.set_x(-48)
ui_Label2.set_y(60)
ui_Label2.set_align( lv.ALIGN.CENTER)
ui_Label2.set_style_text_color(lv.color_hex(0xFDFBFB), lv.PART.MAIN | lv.STATE.DEFAULT )
ui_Label2.set_style_text_opa(255, lv.PART.MAIN| lv.STATE.DEFAULT )
ui_Label2.set_style_text_font( lv.font_montserrat_16, lv.PART.MAIN | lv.STATE.DEFAULT )

class TEM_HUM():
    def __init__(self, ui_Screen1):
        # Read the temperature and humidity values of DHT20 sensor
        global tem, hum

        tem = sensor.temperature()
        hum = sensor.humidity()

        # Update the temperature and humidity display on the UI interface
        ui_Label1.set_text(f"{round(tem)}")  # Update temperature display
        ui_Label2.set_text(f"{round(hum)}")       # Update humidity display


TEM_HUM(ui_Screen1)
lv.scr_load(ui_Screen1)

while True:
  try:
    time.sleep(0.02)
    sensor.measure()
    temp = sensor.temperature()
    hum = sensor.humidity()
    #temp_f = temp * (9/5) + 32.0
    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)}")
    ui_Label2.set_text(f"{round(hum)}")

  except OSError as e:
    print('Failed to read sensor.')

Code Explanation

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 DHT22 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

button-1

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

Please click download to download the code file and libraries.

Upload the libraries

ui_image.py

  1. 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/

    Thonny-15.png

  2. ui_image.py is added to MicroPython device successfully.

    Thonny-16.png

Upload the code

Click the "run" icon.

Thonny-17.png

Successfully uploaded.

Thonny-18.png

CrowPanel_ESP32 Display-5.0-micropython_led

GPIO Examples

Please click download 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) of ESP32 5.0'' HMI, and upload the following code. The LED will turn on and off in a loop.

#Make by Elecrow
#Web:www.elecrow.com

import time
from machine import Pin
pin38 = Pin(38, Pin.OUT)
while True:

    pin38.value(1)
    time.sleep(0.5)
    pin38.value(0)
    time.sleep(0.5)

Example 2 Turn on/off the LED with the button on the screen

Upload the following code to the module, there will be a button show on the screen. Press the button to turn on/off the LED.

#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


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_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 following code 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-------------------------

dial

Example 4 Connect WiFi

Upload the following code 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:

Connect-wifi

Example 5 Initialize SD card

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 following code.

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")

play-music

For more demo of the i2s speaker please refer to: https://github.com/miketeachman/micropython-i2s-examples

Example 7 Initialize UART port

Upload the following code

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:

uart

Resources