aboutsummaryrefslogtreecommitdiff
path: root/docs/integration/bindings/micropython.rst
blob: a0584bb033c2ef05cfdfe85a9557e975cafd22b8 (plain)
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
.. _micropython:

===========
MicroPython
===========

What is MicroPython?
--------------------

`MicroPython <http://micropython.org/>`__ is Python for
microcontrollers. Using MicroPython, you can write Python3 code and run
it even on a bare metal architecture with limited resources.


Highlights of MicroPython
~~~~~~~~~~~~~~~~~~~~~~~~~

- **Compact**: Fits and runs within just 256k of code space and 16k of RAM. No OS is needed, although you
  can also run it with an OS, if you want.
- **Compatible**: Strives to be as compatible as possible with normal Python (known as CPython).
- **Versatile**: Supports many architectures (x86, x86-64, ARM, ARM Thumb, Xtensa).
- **Interactive**: No need for the compile-flash-boot cycle. With the REPL (interactive prompt) you can type
  commands and execute them immediately, run scripts, etc.
- **Popular**: Many platforms are supported. The user base is growing bigger. Notable forks:

  - `MicroPython <https://github.com/micropython/micropython>`__
  - `CircuitPython <https://github.com/adafruit/circuitpython>`__
  - `MicroPython_ESP32_psRAM_LoBo <https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo>`__

- **Embedded Oriented**: Comes with modules specifically for embedded systems, such as the
  `machine module <https://docs.micropython.org/en/latest/library/machine.html#classes>`__
  for accessing low-level hardware (I/O pins, ADC, UART, SPI, I2C, RTC, Timers etc.)

--------------


Why MicroPython + LVGL?
-----------------------

MicroPython `does not have a good native high-level GUI library <https://forum.micropython.org/viewtopic.php?f=18&t=5543>`__.
LVGL is an `Object-Oriented Component Based <https://blog.lvgl.io/2018-12-13/extend-lvgl-objects>`__
high-level GUI library, which seems to be a natural candidate to map into a higher level language, such as Python.
LVGL is implemented in C and its APIs are in C.


Here are some advantages of using LVGL in MicroPython:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- Develop GUI in Python, a very popular high level language. Use paradigms such as Object-Oriented Programming.
- Usually, GUI development requires multiple iterations to get things right. With C, each iteration consists of
  **``Change code`` > ``Build`` > ``Flash`` > ``Run``**. In MicroPython it's just
  **``Change code`` > ``Run``** ! You can even run commands interactively using the
  `REPL <https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop>`__ (the interactive prompt)

MicroPython + LVGL could be used for:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- Fast prototyping GUI.
- Shortening the cycle of changing and fine-tuning the GUI.
- Modelling the GUI in a more abstract way by defining reusable composite objects, taking advantage of Python's language features
  such as Inheritance, Closures, List Comprehension, Generators, Exception Handling, Arbitrary Precision Integers and others.
- Make LVGL accessible to a larger audience. No need to know C to create a nice GUI on an embedded system. This goes well with
  `CircuitPython vision <https://learn.adafruit.com/welcome-to-circuitpython/what-is-circuitpython>`__.
  CircuitPython was designed with education in mind, to make it easier for new or inexperienced users to get started with
  embedded development.
- Creating tools to work with LVGL at a higher level (e.g. drag-and-drop designer).

--------------


So what does it look like?
--------------------------

It's very much like the C API, but Object-Oriented for LVGL components.

Let's dive right into an example!


A simple example
~~~~~~~~~~~~~~~~

.. code:: python

   # Initialize
   import display_driver
   import lvgl as lv

   # Create a button with a label
   scr = lv.obj()
   btn = lv.button(scr)
   btn.align(lv.ALIGN.CENTER, 0, 0)
   label = lv.label(btn)
   label.set_text('Hello World!')
   lv.screen_load(scr)


How can I use it?
-----------------

Online Simulator
~~~~~~~~~~~~~~~~

If you want to experiment with LVGL + MicroPython without downloading
anything - you can use our online simulator! It's a fully functional
LVGL + MicroPython that runs entirely in the browser and allows you to
edit a python script and run it.

`Click here to experiment on the online simulator <https://sim.lvgl.io/>`__

Many `LVGL examples <https://docs.lvgl.io/master/examples.html>`__ are available also for MicroPython. Just click the link!


PC Simulator
~~~~~~~~~~~~

MicroPython is ported to many platforms. One notable port is "unix", which allows you to build and run MicroPython
(+LVGL) on a Linux machine. (On a Windows machine you might need Virtual Box or WSL or MinGW or Cygwin etc.)

`Click here to know more information about building and running the unix port <https://github.com/lvgl/lv_micropython>`__


Embedded Platforms
~~~~~~~~~~~~~~~~~~

In the end, the goal is to run it all on an embedded platform. Both MicroPython and LVGL can be used on many embedded
architectures. `lv_micropython <https://github.com/lvgl/lv_micropython>`__ is a fork of MicroPython+LVGL and currently
supports Linux, ESP32, STM32 and RP2. It can be ported to any other platform supported by MicroPython.

- You would also need display and input drivers. You can either use one of the existing drivers provided with lv_micropython,
  or you can create your own input/display drivers for your specific hardware.
- Drivers can be implemented either in C as a MicroPython module, or in pure Python!

lv_micropython already contains these drivers:

- Display drivers:

  - SDL on Linux
  - X11 on Linux
  - ESP32 specific:

    - ILI9341
    - ILI9488
    - GC9A01
    - ST7789
    - ST7735

  - Generic (pure Python):

    - ILI9341
    - ST7789
    - ST7735

- Input drivers:

  - SDL
  - X11
  - XPT2046
  - FT6X36
  - ESP32 ADC with resistive touch


Where can I find more information?
----------------------------------

- ``lv_micropython`` `README <https://github.com/lvgl/lv_micropython>`__
- ``lv_binding_micropython`` `README <https://github.com/lvgl/lv_binding_micropython>`__
- The `LVGL micropython forum <https://forum.lvgl.io/c/micropython>`__ (Feel free to ask anything!)
- At MicroPython: `docs <http://docs.micropython.org/en/latest/>`__ and `forum <https://forum.micropython.org/>`__
- `Blog Post <https://blog.lvgl.io/2019-02-20/micropython-bindings>`__, a little outdated.


The MicroPython Binding is auto generated!
------------------------------------------

- LVGL is a git submodule inside `lv_micropython <https://github.com/lvgl/lv_micropython>`__
  (LVGL is a git submodule of `lv_binding_micropython <https://github.com/lvgl/lv_binding_micropython>`__
  which is itself a submodule of `lv_micropython <https://github.com/lvgl/lv_micropython>`__).
- When building lv_micropython, the public LVGL C API is scanned and MicroPython API is auto-generated. That means that
  lv_micropython provides LVGL API for **any** LVGL version, and generally does not require code changes as LVGL evolves.


LVGL C API Coding Conventions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For a summary of coding conventions to follow see the :ref:`coding-style`.

.. _memory_management:

Memory Management
~~~~~~~~~~~~~~~~~

| When LVGL runs in MicroPython, all dynamic memory allocations (:cpp:func:`lv_malloc`) are handled by MicroPython's memory
  manager which is `garbage-collected <https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)>`__ (GC).
| To prevent GC from collecting memory prematurely, all dynamic allocated RAM must be reachable by GC.
| GC is aware of most allocations, except from pointers on the `Data Segment <https://en.wikipedia.org/wiki/Data_segment>`__:

    - Pointers which are global variables
    - Pointers which are static global variables
    - Pointers which are static local variables

Such pointers need to be defined in a special way to make them reachable by GC


Identify The Problem
^^^^^^^^^^^^^^^^^^^^

Problem happens when an allocated memory's pointer (return value of :cpp:func:`lv_malloc`) is stored only in either **global**,
**static global** or **static local** pointer variable and not as part of a previously allocated ``struct`` or other variable.


Solve The Problem
^^^^^^^^^^^^^^^^^

- Replace the global/static local var with :cpp:expr:`(LV_GLOBAL_DEFAULT()->_var)`
- Include ``lv_global.h`` on files that use ``LV_GLOBAL_DEFAULT``
- Add ``_var`` to ``lv_global_t`` on ``lv_global.h``

Example
^^^^^^^


More Information
^^^^^^^^^^^^^^^^

- `In the README <https://github.com/lvgl/lv_binding_micropython#memory-management>`__
- `In the Blog <https://blog.lvgl.io/2019-02-20/micropython-bindings#i-need-to-allocate-a-littlevgl-struct-such-as-style-color-etc-how-can-i-do-that-how-do-i-allocatedeallocate-memory-for-it>`__

.. _callbacks:

Callbacks
~~~~~~~~~

In C a callback is just a function pointer. But in MicroPython we need to register a *MicroPython callable object* for each
callback. Therefore in the MicroPython binding we need to register both a function pointer and a MicroPython object for every callback.

Therefore we defined a **callback convention** for the LVGL C API that expects lvgl headers to be defined in a certain
way. Callbacks that are declared according to the convention would allow the binding to register a MicroPython object
next to the function pointer when registering a callback, and access that object when the callback is called.

- The basic idea is that we have ``void * user_data`` field that is used automatically by the MicroPython Binding
  to save the *MicroPython callable object* for a callback. This field must be provided when registering the function
  pointer, and provided to the callback function itself.
- Although called "user_data", the user is not expected to read/write that field. Instead, the MicroPython glue code uses
  ``user_data`` to automatically keep track of the MicroPython callable object. The glue code updates it when the callback
  is registered, and uses it when the callback is called in order to invoke a call to the original callable object.

There are a few options for defining a callback in LVGL C API:

- Option 1: ``user_data`` in a struct

  - There's a struct that contains a field called ``void * user_data``

    - A pointer to that struct is provided as the **first** argument of a callback registration function
    - A pointer to that struct is provided as the **first** argument of the callback itself

- Option 2: ``user_data`` as a function argument

  - A parameter called ``void * user_data`` is provided to the registration function as the **last** argument

    - The callback itself receives ``void *`` as the **last** argument

- Option 3: both callback and ``user_data`` are struct fields

  - The API exposes a struct with both function pointer member and ``user_data`` member

    - The function pointer member receives the same struct as its **first** argument

In practice it's also possible to mix these options, for example provide a struct pointer when registering a callback
(option 1) and provide ``user_data`` argument when calling the callback (options 2),
**as long as the same ``user_data`` that was registered is passed to the callback when it's called**.

Examples
^^^^^^^^

- :cpp:type:`lv_anim_t` contains ``user_data`` field. :cpp:func:`lv_anim_set_path_cb`
  registers `path_cb` callback. Both ``lv_anim_set_path_cb`` and :cpp:type:`lv_anim_path_cb_t`
  receive :cpp:type:`lv_anim_t` as their first argument
- ``path_cb`` field can also be assigned directly in the Python code because it's a member
  of :cpp:type:`lv_anim_t` which contains ``user_data`` field, and :cpp:type:`lv_anim_path_cb_t`
  receive :cpp:type:`lv_anim_t` as its first argument.
- :cpp:func:`lv_imgfont_create` registers ``path_cb`` and receives ``user_data`` as the last
  argument. The callback :cpp:type:`lv_imgfont_get_path_cb_t` also receives the ``user_data`` as the last argument.

.. _more-information-1:

More Information
^^^^^^^^^^^^^^^^

- In the `Blog <https://blog.lvgl.io/2019-08-05/micropython-pure-display-driver#using-callbacks>`__
  and in the `README <https://github.com/lvgl/lv_binding_micropython#callbacks>`__
- `[v6.0] Callback conventions  #1036 <https://github.com/lvgl/lvgl/issues/1036>`__
- Various discussions: `here <https://github.com/lvgl/lvgl/pull/3294#issuecomment-1184895335>`__
  and `here <https://github.com/lvgl/lvgl/issues/1763#issuecomment-762247629>`__
  and`here <https://github.com/lvgl/lvgl/issues/316#issuecomment-467221587>`__