I have tried to make this as simple as possible for paople to use. There are some glitches still in it I am sure. If you come across an issue please let me know.
I am still in development mode for the unix port. I am writing an SDL driver that conforms To the rest of the driver framework. I have started working on writing the frameworks for the different indev (input) types that LVGL supports. The frameworks are written to make it easier to write display and input drivers for the binding.
ESP32-ALL
--optimize-size
: If you are having an issue with getting the firmware to fit into your esp32
or if space is more of a concern than speed you can set this command line option. This will tell the compiler that the
firmware size is more important than performance and the compiled binary will be smaller as a result.
--flash-size={size}
: Flash sizes that are able to be used are 4, 8, 16, 32, 64 and 128 across all
variants of the ESP32. It is up to the user to know what their board is using.
--ota
: If you want to set the partitions so you can do an over the air update of
the firmware. I do want to note that this does take up twice as much application
storage space. This feature applies to any board.
CONFIG_*={value}
: You can alter the config settings of the esp-idf by using these settings. Refer to the ESP-IDF documentation
for further information
SPI
: The machine.SPI
class has undergone a HUGE change. It is now split into 2 pieces. machine.SPI.Bus
and machine.SPI.Device
They exactly what they seem. It is easier to show a code example then it is to explain it.
import machine
spi_bus = machine.SPI.Bus( host=1, mosi=15, miso=16, sck=10 )
spi_device = machine.SPI.Device( spi_bus=spi_bus, freq=10000000, cs=3, polarity=0, phase=0, bits=8, first_bit=machine.SPI.MSB )
spi_device.deinit() del spi_device
del spi_bus del spi_device
All methods that existed for the original machine.SPI
are available in
the machine.SPI.Device
class. They work exactly how they did before.
Display Bus
Memory
Display IC
Touch IC
I have changed the design of the binding so it is no longer a dependancy of MicroPython. Instead MicroPython is now a dependency of the binding. By doing this I have simplified the process up updating the MicroPython version. Only small changes are now needed to support newer versions of MicroPython.
In order to make this all work I have written a Python script that handles Building the binding. The only prerequesits are that you have a C compiler installed (gcc, clang, msvc) and the necessary support libs.
compiling for ESP32
Ubuntu (Linux): you can install all of these using apt-get install
macOS
xcode-select -–install
brew install cmake
brew install ninja
brew install python
Compiling for RP2
Ubuntu (Linux): you can install all of these using apt-get install
macOS
command xcode-select–install
brew install make
brew install cmake
brew install ninja
brew install python
brew install armmbed/formulae/arm-none-eabi-gcc
Windows
Compiling for STM32:
Ubuntu (Linux): you can install all of these using apt-get install
macOS
command xcode-select–install
brew install make
brew install ninja
brew install python
brew install armmbed/formulae/arm-none-eabi-gcc
Windows
Compiling for Ubuntu (Linux): you can install all of these using apt-get install
Compiling for macOS
command xcode-select–install
brew install libffi
brew install ninja
brew install make
Compiling for Windows
You are also going to need Python >= 3.10 installed for all builds
There is a single entry point for all builds. That is the make.py script in the root of the repository.
The first argument is positional and it must be one of the following.
The next few arguments are optional to some degree.
**must be run only one time when the build is intially started. after that you will not need to add these arguments. There is internal checking that is done to see if the argument needs to be carried out. So you can also optionally leave it there if you want.
The next group of options are going to be port specific, some may have them and some may not.
I will go into specifics for what what boards and variants are available for a specific port a little bit further down.
--flash-size ² ³: This is how much flash storage is available.
Allowed Values are:
¹ Available for the ESP32-S3 when BOARD_VARIANT
is set to SPIRAM_OCT
² Available for the ESP32, ESP32-S2 and ESP32-S3
³ Available only when BOARD_VARIANT
is set to SPIRAM
or SPIRAM_OCT
esp32: BOARD=
windows: VARIANT=
stm32: BOARD=
unix: VARIANT=
rp2: BOARD=
ADAFRUIT_FEATHER_RP2040
ADAFRUIT_ITSYBITSY_RP2040
ADAFRUIT_QTPY_RP2040
ARDUINO_NANO_RP2040_CONNECT
GARATRONIC_PYBSTICK26_RP2040
NULLBITS_BIT_C_PRO
PIMORONI_PICOLIPO_16MB
PIMORONI_PICOLIPO_4MB
PIMORONI_TINY2040
POLOLU_3PI_2040_ROBOT
POLOLU_ZUMO_2040_ROBOT
RPI_PICO
RPI_PICO_W
SIL_RP2040_SHIM
SPARKFUN_PROMICRO
SPARKFUN_THINGPLUS
W5100S_EVB_PICO
W5500_EVB_PICO
WEACTSTUDIO
renesas-ra: BOARD=
nrf: BOARD=
mimxrt: BOARD=
samd: BOARD=
build with submodules and mpy_cross
python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911
build without submodules or mpy_cross
python3 make.py esp32 clean BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911
I always recommend building with the clean command, this will ensure you get a good fresh build.
NOTE: There is a bug in the ESP32 build. The first time around it will fail saying that one of the sumbodules is not available. Run the build again with the submodules argument in there and then it will build fine. For the life of me I cam not able to locate where the issue is stemming from. I will find it eventually.
I will provide directions on how to use the driver framework and also the drivers that are included with the binding in the coming weeks.
SDL fpr Unix is working properly. Make sure you review the requirements needed to compile for unix!!! The build system compiles the latest version of SDL2 so the list is pretty long for the requirements.
To build for Unix use the following build command
python3 make.py unix clean DISPLAY=sdl_display INDEV=sdl_pointer
Couple of notes:
lv.task_handler
once every 5 milliseconds, shorter than that and you
will have a lot of CPU time comsumed. Linger than that and your mouse response is not
going to be great.Here is some example code for the unix port
from micropython import const # NOQA
_WIDTH = const(480)
_HEIGHT = const(320)
_BUFFER_SIZE = _WIDTH * _HEIGHT * 3
import lcd_bus # NOQA
bus = lcd_bus.SDLBus(flags=0)
buf1 = bus.allocate_framebuffer(_BUFFER_SIZE, 0)
buf2 = bus.allocate_framebuffer(_BUFFER_SIZE, 0)
import lvgl as lv # NOQA
import sdl_display # NOQA
lv.init()
display = sdl_display.SDLDisplay(
data_bus=bus,
display_width=_WIDTH,
display_height=_HEIGHT,
frame_buffer1=buf1,
frame_buffer2=buf2,
color_space=lv.COLOR_FORMAT.RGB888
)
display.init()
import sdl_pointer
mouse = sdl_pointer.SDLPointer()
scrn = lv.screen_active()
scrn.set_style_bg_color(lv.color_hex(0x000000), 0)
slider = lv.slider(scrn)
slider.set_size(300, 25)
slider.center()
import task_handler
# the duration needs to be set to 5 to have a good response from the mouse.
# There is a thread that runs that facilitates double buffering.
th = task_handler.TaskHandler(duration=5)
The touch screen drivers will handle the rotation that you set to the display. There is a single caviat to this. You MUST set up and initilize the display then create the touch drivers and after that has been done you can set the rotation. The touch driver must exist prior to the display rotation being set.
For the ESP32 SOC's there is NVRAM that is available to store data in. That data is persistant between restarts of the ESP32. This feature is pur to use to store calibration data for the touch screen. In the exmaple below it shows how to properly create a display driver and touch driver and how to set the rotation and also the calibration storage.
import lcd_bus
from micropython import const
# display settings
_WIDTH = const(320)
_HEIGHT = const(480)
_BL = const(45)
_RST = const(4)
_DC = const(0)
_WR = const(47)
_FREQ = const(20000000)
_DATA0 = const(9)
_DATA1 = const(46)
_DATA2 = const(3)
_DATA3 = const(8)
_DATA4 = const(18)
_DATA5 = const(17)
_DATA6 = const(16)
_DATA7 = const(15)
_BUFFER_SIZE = const(30720)
_SCL = const(5)
_SDA = const(6)
_TP_FREQ = const(100000)
display_bus = lcd_bus.I80Bus(
dc=_DC,
wr=_WR,
freq=_FREQ,
data0=_DATA0,
data1=_DATA1,
data2=_DATA2,
data3=_DATA3,
data4=_DATA4,
data5=_DATA5,
data6=_DATA6,
data7=_DATA7
)
fb1 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_DMA)
fb2 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_DMA)
import st7796 # NOQA
import lvgl as lv # NOQA
lv.init()
display = st7796.ST7796(
data_bus=display_bus,
frame_buffer1=fb1,
frame_buffer2=fb2,
display_width=_WIDTH,
display_height=_HEIGHT,
backlight_pin=_BL,
# reset=_RST,
# reset_state=st7796.STATE_LOW,
color_space=lv.COLOR_FORMAT.RGB565,
color_byte_order=st7796.BYTE_ORDER_BGR,
rgb565_byte_swap=True,
)
import i2c # NOQA
import task_handler # NOQA
import ft6x36 # NOQA
import time # NOQA
display.init()
i2c_bus = i2c.I2CBus(scl=_SCL, sda=_SDA, freq=_TP_FREQ, use_locks=False)
indev = ft6x36.FT6x36(i2c_bus)
display.invert_colors()
if not indev.is_calibrated:
display.set_backlight(100)
indev.calibrate()
# you want to rotate the display after the calibration has been done in order
# to keep the corners oriented properly.
display.set_rotation(lv.DISPLAY_ROTATION._90)
display.set_backlight(100)
th = task_handler.TaskHandler()
scrn = lv.screen_active()
scrn.set_style_bg_color(lv.color_hex(0x000000), 0)
slider = lv.slider(scrn)
slider.set_size(300, 50)
slider.center()
label = lv.label(scrn)
label.set_text('HELLO WORLD!')
label.align(lv.ALIGN.CENTER, 0, -50)
You are able to force the calibration at any time by calling indev.calibrate()
regardless of what indev.is_calibrate
returns. This makes it possible to redo
the calibration by either using a pin that you can check the state of or through
a button in your UI that you provide to the user.
Thank again and enjoy!!
NOTE: On ESP32-S3, SPI host 0 and SPI host 1 share a common SPI bus. The main Flash and PSRAM are connected to the host 0. It is recommended to use SPI host 2 when connecting an SPI device like a display that is going to utilize the PSRAM for the frame buffer.