Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature suggestion: Micropython binding #557

Closed
amirgon opened this issue Nov 16, 2018 · 230 comments
Closed

Feature suggestion: Micropython binding #557

amirgon opened this issue Nov 16, 2018 · 230 comments

Comments

@amirgon
Copy link
Contributor

amirgon commented Nov 16, 2018

Micropython is becoming mainstream on IOT and embedded development in general. It is available for quite a few of embedded architectures (this and this, partial list), and can be ported to new ones.
Today there already exist several architecture-specific micropython display modules (for example this and this), but none of them as feature rich and general as littlevgl.
I think it could be useful to provide micropython API to littlevgl Objects and Styles.
Main benefits are:

  • Exposure to a larger audience in the embedded development world.
  • Interactive developement. Use the Micropython interpreter to exercise littlevgl over a live embedded board. This would allow rapid GUI development without the build-flash cycle.
  • Some would argue Python code is easier to develop and maintain (not sure I agree about this one...)

If it turns out there is an agreement about this feature, I might try sending PR if I find the time for it.

@kisvegabor
Copy link
Member

Hi,

I also see a big potential in it. Both reaching a larger audience and the benefits of Micropython sounds very well. Earlier I made some experiments with Lua binding but Micropython is more mainstream right now.

So this feature would be very welcome!
How can this binding look like? One or more c file(s) with the interface functions?

@turoksama
Copy link
Collaborator

turoksama commented Nov 18, 2018

@kisvegabor
Will it cost much more extra cpu time for the micro-Python?

@embeddedt
Copy link
Member

@turoksama There will definitely be overhead from using Python due to the need to convert objects back and forth. I guess the hope is that Python will be used on platforms which can afford the additional overhead.

@amirgon
Copy link
Contributor Author

amirgon commented Nov 18, 2018

Here is what I had in mind.
The lvgl micropython module would provide a constructor for each object type, and each object would provide its methods.
Here is the "Hello World" example:

import lvgl
label1 = lvgl.label(lvgl.get_scr_act())
label1.set_text("Hello world!")
label1.align(None, lvgl.ALIGN_CENTER, 0, 0)

Creating micropython extensions requires lots of boilerplate. I don't think we should write them manually. Writing them manually would be not only tedious to start with, but also hard to maintain when objects are changed and added.
Instead I suggest creating these extensions automatically from the source code (the C headers) relying on the naming conventions and maybe some hints.
During the make process a script will run, scan the lvgl object headers (with pycparser for example) and generate the Micropython extensions in a C file that can be later compiled and linked on Micropython-enabled projects.

@kisvegabor
Copy link
Member

@amirgon Sound good to me! I also vote for the automated way.
From your example, we can see how to call LittelvGL from Micropython. But how to call Micropython from LittelvGL? It is required when clicking a button and a user-defined action should be executed.

@amirgon
Copy link
Contributor Author

amirgon commented Nov 19, 2018

@kisvegabor Calling a Micropython function from C code is covered by Micropython.
You can use mp_call_function_* defined here, so we get this one for free.
In case of a button (or any other) set_action micropython extension, we can give it a python function as a parameter since functions are first-class objects in python, can be passed around, used in lists dicts etc. so I think this should be pretty simple.

One thing we'll need to figure out is how to separate display/board specific code from generic lvgl code.
We would need to provide Micropython extensions to register display driver, set IO pins, configure SPI, DMA, etc.

@embeddedt
Copy link
Member

@amirgon The only problem with your suggestion is that the performance overhead of handling the low-level display code through Python might be too high. What do you think?

@amirgon
Copy link
Contributor Author

amirgon commented Nov 19, 2018

@embeddedt It's true that Micropython is slower than C, and costs more resources (RAM, Flash).
However, I believe Micropython is still beneficial for platforms that can afford the extra resources (such as ESP32, for example):

  • Micropython would only be used for the Objects/Styles API layer (for setting object properties), not for the graphics rending itself which remains in C. Think about a button, for example. How many cycles are spent on creating the button object and setting its properties (action, state, style) vs. cycles spent on actually writing pixels to the display to render it? My guess is that rending would take most of the cycles (and rendering remains in C), while setting object properties would only consume a very small portion of the cycle budget.
    Also take into account that in most cases setting object properties is done once per GUI "screen" and rendering happens continuously while the user interacts with that certain "screen".

  • Micropython is optimized for architectures with limited resources (CPU and memory). More information can be found here. Micropython compiles code into bytecode and has an efficient VM that executes it. Micropython modules may also be implemented as frozen bytecode modules saving the need to compile them by the MCU before running them.

  • Micropython support for lvgl would be optional, and you would need to explicitly opt in. If you do nothing you don't pay any cycles or memory. If you are developing for a platform where you want to save cycles and memory, this feature will not get in your way, and you can still write all your code in C as today and get the same performance.

@kisvegabor
Copy link
Member

@amirgon I agree, calling lv_set_... functions from Micropython would have only a minor overhead if the rendering remains in C. I suppose we can keep the disp_fush function in C as well, right?

@amirgon
Copy link
Contributor Author

amirgon commented Nov 20, 2018

@kisvegabor I agree, disp_flush should remain a low level C function.

@kisvegabor
Copy link
Member

@amirgon
Did you already make some experiments with LittlevGL in Micropython?

@amirgon
Copy link
Contributor Author

amirgon commented Nov 21, 2018

@kisvegabor I've created a Micropython module and one object manually, and ran it on my HW setup. I didn't start working on the automatic generation of Micropython extensions yet, but I'm looking into doing this using pycparser.

@kisvegabor
Copy link
Member

@amirgon
Great! I'm really waiting for that :)

@amirgon
Copy link
Contributor Author

amirgon commented Nov 25, 2018

Just found this.
This is an emulated MCU running on the browser, with Micropython firmware.
When we have lvgl Micropython binding, something like this could be used for running lvgl interactively on the browser!

(btw, progress update, pycparser is great and I'm making some progress with it, creating some micropython lvgl extension functions automatically, but there's still a lot of work to be done)

@kisvegabor
Copy link
Member

Looks amazing! My old dream to make an interactive online GUI demo tool but didn't find the right way to do this.

I'm looking forward to try Micropython!

@embeddedt
Copy link
Member

@kisvegabor Have you considered Emscripten? It converts C to JavaScript and might be faster than using Unicorn and MicroPython for a demo.

@kisvegabor
Copy link
Member

@embeddedt
I tried Emscripten once but it was extremely slow and wasn't stable (sometimes not started properly). Probably it is worth an other try.

@embeddedt
Copy link
Member

@kisvegabor I can experiment with Emscripten if you'd like but it may take a while to get stuff working. I probably won't get to it before next week.

@amirgon
Copy link
Contributor Author

amirgon commented Nov 26, 2018

@embeddedt if you do have time to experiment with Emscripten, please try to build Micropython as part of lvgl sources.
This has been done before.

@kisvegabor
Copy link
Member

@embeddedt Some test would be welcome in this field. I never used Emscripten before the mentioned test so probably you will get better results than me :)

@amirgon
Copy link
Contributor Author

amirgon commented Nov 26, 2018

Not ready for a PR yet, but I have a working script.
It scans all *.h files in lv_objx dir and generates an Mpy module called lvgl which contains object for each lvgl object, method functions etc.
It relies heavily on naming conventions. (For example, it assumes that an object is constructed by lv_%s_create with two specific arguments.)
Luckily, these naming conventions are followed very well on lvgl sources.

Here is a working example I just run on my device:

>>> import lvgl
ILI9341 initialization.
Enable backlight.
>>> label1 = lvgl.label(lvgl.scr_act())
>>> label1.set_text("Hello world!")
>>> label1.align(None, 0, 0, 0)

A nice thing about Mpy REPL is the automatic completion (start typing and press tab), the help function etc.

For example:

>>> help(label1)
object lvgl label is of type label
  del -- <function>
  clean -- <function>
  invalidate -- <function>
  set_parent -- <function>
  set_pos -- <function>
  set_x -- <function>
  set_y -- <function>
  set_size -- <function>
  set_width -- <function>
  set_height -- <function>
  align -- <function>
  refresh_style -- <function>
  set_hidden -- <function>
  set_click -- <function>
  set_top -- <function>
  set_drag -- <function>
  set_drag_throw -- <function>
  set_drag_parent -- <function>
  set_opa_scale_enable -- <function>
  set_opa_scale -- <function>
  set_protect -- <function>
  clear_protect -- <function>
  refresh_ext_size -- <function>
  set_free_num -- <function>
  get_screen -- <function>
  get_parent -- <function>
  get_child -- <function>
  get_child_back -- <function>
  count_children -- <function>
  get_x -- <function>
  get_y -- <function>
  get_width -- <function>
  get_height -- <function>
  get_ext_size -- <function>
  get_hidden -- <function>
  get_click -- <function>
  get_top -- <function>
  get_drag -- <function>
  get_drag_throw -- <function>
  get_drag_parent -- <function>
  get_opa_scale -- <function>
  get_protect -- <function>
  is_protected -- <function>
  get_free_num -- <function>
  is_focused -- <function>
  set_text -- <function>
  set_array_text -- <function>
  set_static_text -- <function>
  set_long_mode -- <function>
  set_align -- <function>
  set_recolor -- <function>
  set_body_draw -- <function>
  set_anim_speed -- <function>
  get_text -- <function>
  get_long_mode -- <function>
  get_align -- <function>
  get_recolor -- <function>
  get_body_draw -- <function>
  get_anim_speed -- <function>
  ins_text -- <function>
  cut_text -- <function>

>>> help(lvgl)
object <module 'lvgl'> is of type module
  __name__ -- lvgl
  __init__ -- <function>
  obj -- <class 'obj'>
  arc -- <class 'arc'>
  cont -- <class 'cont'>
  btn -- <class 'btn'>
  label -- <class 'label'>
  bar -- <class 'bar'>
  btnm -- <class 'btnm'>
  cb -- <class 'cb'>
  line -- <class 'line'>
  chart -- <class 'chart'>
  page -- <class 'page'>
  ddlist -- <class 'ddlist'>
  lmeter -- <class 'lmeter'>
  gauge -- <class 'gauge'>
  img -- <class 'img'>
  kb -- <class 'kb'>
  led -- <class 'led'>
  list -- <class 'list'>
  mbox -- <class 'mbox'>
  preload -- <class 'preload'>
  roller -- <class 'roller'>
  slider -- <class 'slider'>
  sw -- <class 'sw'>
  win -- <class 'win'>
  tabview -- <class 'tabview'>
  ta -- <class 'ta'>
  font_init -- <function>
  anim_init -- <function>
  init -- <function>
  scr_act -- <function>
  layer_sys -- <function>
  disp_is_mem_fill_supported -- <function>
  tick_get -- <function>
  group_focus_obj -- <function>
  indev_enable -- <function>
  txt_ins -- <function>
  fs_init -- <function>
  fs_remove -- <function>
  fs_get_letters -- <function>
  fs_up -- <function>
  draw_aa_get_opa -- <function>

Next things to implement:

  • Add Enums to lvgl module to allow the user specify a name instead of a number
  • Callbacks (function pointers)
  • Styles
  • Structs (for example lv_color_t)
  • Missing conversions

If you want to see the code, have a look at:

  • gen_mpy.py - This script scans lvgl headers and generates the Mpy module.
  • lv_mpy.c - This is the generated file. In the future it should not be committed, but generated by the Makefile instead. It provides the Mpy module mp_module_lvgl which should then be registered in Mpy mpconfigport.h.
    It's already quite large, about 9000 generated source lines.

I'll be happy to receive comments.

@kisvegabor
Copy link
Member

kisvegabor commented Nov 28, 2018

@amirgon
It looks amazing!
Do you know how much flash space is required by lv_mpy.c?

I didn't use Micropython so far but I'd like to try it with my STM32F429 Discovery. I fond this: https://github.com/micropython/micropython/wiki/Board-STM32F429
Do you think lv_mpy.c can be integrated into that project? Or is it possible for me who is not familiar with Micropython? :)

@amirgon
Copy link
Contributor Author

amirgon commented Nov 28, 2018

@kisvegabor I didn't check how much flash it requires, however,

  • The first thing the script does it C preprocessing of the header files. So if you enable/disable features on lv_conf.h it would add/remove extensions from lv_mpy.c as well.
  • If you are using Micropython you probably have plenty of flash anyway...

I'm not familiar with STM32F429, my board is ESP32. But I don't see why it would matter, Micropython is the same.
Integrating it with Micropython is very simple:

  • Make sure lv_mpy.c compiles. Just add it to SRC_C in the Makefile.
  • Depending on your architecture you might need to edit your .ld file to make sure that lv_mpy.c goes to ROM and not RAM. On some architectures this would be automatic for const variables.
  • Edit mpconfigport.h and add mp_module_lvgl to MICROPY_PORT_BUILTIN_MODULES, this would register the module with Micropython.
  • I left one function to the user to implement: extern void lv_mp_init(). It is called when the module is loaded (after running import lvgl). I use it to initialize the LCD and lvgl, but if you do it some other way you can implement it as an empty function.

You can find more information here, let me know how it goes!

@kisvegabor
Copy link
Member

@amirgon Thank you! I hope I can ry it on the weekend

@kisvegabor
Copy link
Member

@amirgon
I'm planning to try Micropytthon on Linux with framebuffer as a display. I started to put the project together. Some parts of the build process are already working I don't know what is best way to link lvgl to micropython. Now I got a lot of similar errors:

build/../../../lvgl/mpy/lv_mpy.o: In function `mp_lv_txt_ins':
lv_mpy.c:(.text.mp_lv_txt_ins+0x31): undefined reference to `lv_txt_ins'

I suppose I need to add the source files of lvgl to the makefile but don't know what is the best to do it. Can you help with that?

@amirgon
Copy link
Contributor Author

amirgon commented Nov 30, 2018

@kisvegabor Yes you need to add lvgl sources to the Makefile.
First, I configured lvgl as an optional component, the following way:

  • Edit Kconfig.projbuild and add the following section under menu "Modules"
	    config MICROPY_USE_LVGL
	       bool "Use LVGL module"
	       default n
	       help
	       Include LittlevGL Embedded GUI Library
  • Run make menuconfig and enable use LVGL module under Micropython --> Modules. Alternatively you could edit sdkconfig directly and add CONFIG_MICROPY_USE_LVGL=y

  • Edit components/micropython/component.mk and add lvgl sources like this:

ifdef CONFIG_MICROPY_USE_LVGL
SRC_C += <...lvgl C files..>
endif

You don't have to explicitly specify all files, you can use wildcards to include C files from specific lvgl directories.
Alternatively you could add the source files recursively, but make sure you also read this. A better technique is something like this.
On ESP32 all it takes is adding component.mk (even empty one!) on every directory that contains files you want to include in the build (as explained here).
If you build Micropython on Linux that is probably different.

@amirgon
Copy link
Contributor Author

amirgon commented Nov 30, 2018

@kisvegabor Please make sure you take the latest version when you try it out.
Apart from bug fixes, I added the following features:

  • Object Inheritance. For example, when accessing a btn object, you can use btn methods, cont methods and obj methods, which are all the ancestors of btn. (The script deduces the heirarchy from the ..._ext_t structs)
  • Enums. Each enum is created as its own type, either as a child of an object type or of the lvgl module. For example,

ALIGN is a member of lvgl

>>> help(lvgl.ALIGN)
object <class 'LV_ALIGN'> is of type type
  OUT_LEFT_BOTTOM -- 17
  OUT_TOP_LEFT -- 9
  IN_RIGHT_MID -- 8
  OUT_RIGHT_MID -- 19
  IN_BOTTOM_MID -- 5
  CENTER -- 0
  OUT_RIGHT_TOP -- 18
  OUT_BOTTOM_MID -- 13
  OUT_LEFT_MID -- 16
  IN_TOP_MID -- 2
  IN_BOTTOM_LEFT -- 4
  IN_TOP_RIGHT -- 3
  OUT_TOP_MID -- 10
  OUT_BOTTOM_LEFT -- 12
  OUT_RIGHT_BOTTOM -- 20
  OUT_LEFT_TOP -- 15
  IN_BOTTOM_RIGHT -- 6
  OUT_TOP_RIGHT -- 11
  OUT_BOTTOM_RIGHT -- 14
  IN_TOP_LEFT -- 1
  IN_LEFT_MID -- 7

but STATE is a member of btn:

>>> help(lvgl.btn.STATE)
object <class 'LV_BTN_STATE'> is of type type
  TGL_REL -- 2
  NUM -- 5
  TGL_PR -- 3
  PR -- 1
  INA -- 4
  REL -- 0
>>> 

@kisvegabor
Copy link
Member

@amirgon Thank you very much! I will try it on Sunday!

@amirgon
Copy link
Contributor Author

amirgon commented Dec 1, 2018

@kisvegabor I have a problem with callbacks (lv_action_t).

What I'm missing is a context (also called "user_data" or "cookie" on some implementations).
The common practice is to allow the user provide a context (a void* parameter) when registering the callback and pass this context back to the user when calling his callback.
"context" is used by the user to know in which context the function was registered. Often the user wants to register the same function on different callbacks (for example, a single click handler for many button objects), and handle the the callback according to the context it was registered.
"context" is useful in many occasions. For example when using C++ it could be used to pass a pointer to the object and convert the callback from global function to object's method.

In the case of Micropython, I cannot "generate" new functions (code) on run-time. So I must register the same function for different instances of the same object. To save code size, I would prefer to have one callback globally that calls Micropython according to context.

I see several options to solve this:

  1. Add context to lv_action_t. That would require changing its signature to:
typedef lv_res_t (*lv_action_t) (struct _lv_obj_t * obj, void *context);

Then, to register the callback, the user would provide the context. For example:

void lv_btn_set_action(lv_obj_t * btn, lv_btn_action_t type, lv_action_t action, void *context);

lvgl code would need to save the context when registering the callback and pass it when calling the callback.

  1. Previous option is non backward compatible. I don't know how important this is on lvgl, but if it is we could add new callback type and new registration functions that mirror the original ones and include context. I don't like this option since it bloats lvgl code and makes it redundant.

  2. Keep the existing callbacks. I can still implement limited callbacks without it but it will cost more code space, more RAM and be less flexible. To do this I would need to add a member to lv_obj_t that correlates the lvgl object back to a Micropython object. When a callback is registered I would need to register different callback function for each callback type, and use this information (mpy object and callback type) to know which callback I should call. The disadvantages of this options are: requires more RAM, requires more ROM (many callback functions in the code), and it would allow only one callback per object per callback type (for example you can't register multiple callbacks and call them all when the event happens. I don't know if you do this anywhere on lvgl).

Until now I managed to create Micropython extensions without changing lvgl code. I think that in order to support callbacks some change to lvgl code would need to be done.

Please let me know what you think and which option you prefer.

@kisvegabor
Copy link
Member

kisvegabor commented Jan 22, 2019

It is the first case in LittlevGL when we can't agree on something. Obviously, as the leader of this project, I have a big responsibility in solving questions when the parties don't agree on something. I should know when to say no and when to leave things to be added even if I don't agree. In addition, a friendly atmosphere should be created. However, similarly to most of us, I'm a developer with only a few experiences in leadership. I still need to learn a lot and surely will make wrong decisions too. And unfortunately, it means that sometimes something needs to be rewritten or reverted. I truly feel sorry if I make extra work for somebody who pays his free time to develop LittlevGL.

Now... you changed your mind. Not based on new evidence (because both options, "parsing" and "numstr" were both known and understood at the time we discussed it), just because during discussion "it turned out for you" that source file parsing is better.

The new evidence was that we couldn't find a good way to add the colors and the related macros without source code parsing. In light of that, it seemed to me, the source code parsing need to introduced for colors. And once we have source code parsing it should be easy to add it for the symbols too which are quite similar.

I really want to merge your updates (with numstr) to lvgl and release it in v5.3. As we proceed in v6.0 we will see what is working and what isn't.

@rreilink
Copy link

When this is integrated into 5.3, I'd be happy to merge my code for 6.0, and also implement the separation of front-end / back-end for both Python and Micropython, as discussed earlier!

I think integrating my work before 5.3 would be too tight, I'd also like to put some effort into a more elaborate test case that we can run on both Python and Micropython

@amirgon
Copy link
Contributor Author

amirgon commented Jan 22, 2019

@kisvegabor Ok. See this PR.

I had to disable the following on lv.conf.h: USE_LV_TILEVIEW, USE_LV_TABLE, USE_LV_SPINBOX USE_LV_CANVAS. The problem is that these objects declare some functions on their .h file but do not implement them. Since the binding script parses the .h files, it creates wrappers for these functions and fails linking since the function definitions are missing.
This probably should be fixed on lvgl sources, either implement the functions or remove their declarations.

When this is integrated into 5.3, I'd be happy to merge my code for 6.0, and also implement the separation of front-end / back-end for both Python and Micropython, as discussed earlier!

@rreilink Good. Are you going to add support for generic structs? This would be required in order to have the same syntax on Python and Micropython.

I'd also like to put some effort into a more elaborate test case that we can run on both Python and Micropython

That would be great! Currently I have very few simple examples. A more complex example would be very helpful.

@kisvegabor
Copy link
Member

@amirgon

I had to disable the following on lv.conf.h: USE_LV_TILEVIEW, USE_LV_TABLE, USE_LV_SPINBOX USE_LV_CANVAS. The problem is that these objects declare some functions on their .h file but do not implement them. Since the binding script parses the .h files, it creates wrappers for these functions and fails linking since the function definitions are missing.
This probably should be fixed on lvgl sources, either implement the functions or remove their declarations.

Yes, it should be fixed on lvgl sources. I've checked lv_canvas but I didn't see the inconsistency. Can you name the missing function?

@rreilink

When this is integrated into 5.3, I'd be happy to merge my code for 6.0, and also implement the separation of front-end / back-end for both Python and Micropython, as discussed earlier!

Perfect, thank you!

@amirgon
Copy link
Contributor Author

amirgon commented Jan 23, 2019

Yes, it should be fixed on lvgl sources. I've checked lv_canvas but I didn't see the inconsistency. Can you name the missing function?

@kisvegabor To get the list of missing function definitions all you need to do is enable the relevant USE_LV_* macros on lv_conf.h and build lv_mpy. You'd get a long list of errors from the linker.
I don't have access to my development machine until the evening, so if you want I can send you this list tonight.

@kisvegabor
Copy link
Member

I'll check it. But please send an example of how to run gen_mpy.py. Simply python gen_mpy.py says too few arguments

@amirgon
Copy link
Contributor Author

amirgon commented Jan 23, 2019

But please send an example of how to run gen_mpy.py

@kisvegabor Please have a look at lv_mpy_example.c.
A file generated by gen_mpy.py contains the command line used to generate it in a comment at the beginning:

/*
 * Auto-Generated file, DO NOT EDIT!
 *
 * Command line:
 * ../../lib/lvgl/micropython/gen_mpy.py -X anim -X group -X task -I../../lib/berkeley-db-1.xx/PORT/include -I../../lib/lvgl -I. -I../.. -Ibuild -I../../lib/mp-readline -I ../../lib/lvgl/micropython/pycparser/utils/fake_libc_include ../../lib/lvgl/lvgl.h
 *

You can also find this in the makefile in lv_mpy:

$(BUILD)/micropython/lv_mpy.c: $(ALL_LVGL_SRC)
	$(ECHO) "LVGL-GEN $@"
	$(Q)mkdir -p $(BUILD)/micropython
	$(Q)$(PYTHON) $(TOP)/$(LVGL_DIR)/micropython/gen_mpy.py -X anim -X group -X task $(INC) -I $(TOP)/$(LVGL_DIR)/micropython/pycparser/utils/fake_libc_include $(TOP)/$(LVGL_DIR)/lvgl.h > $@

@kisvegabor
Copy link
Member

kisvegabor commented Jan 24, 2019

@amirgon
Thank you!

  1. I switched to lvgl to the dev-5.3 branch
  2. enabled the objects you mentioned in lv_conf.h
  3. run the script from ports/unix and redirected (>) it's output to lv_mpy.c.
  4. and got this output: lv_mpy.c.txt. It contains the mentioned objects and I didn't get errors.

@mattytrentini
Copy link

This has become a long thread! Thanks to everyone for putting in the effort, particularly @amirgon, @rreilink and @kisvegabor.

The MicroPython community really needs a decent UI library. LittlevGL is certainly an excellent option and I'm going to watch the integration carefully and help out where I can. I help run the Melbourne MicroPython Meetup so I'll be sure to feature LittlevGL integration at the next meetup...

I was more bullish about a library with the bulk of the code in (Micro)Python and a smaller core (primitives, font rendering, drivers) in C. I still think, longer-term, that this would be a more elegant solution. But the progress you guys have made is starting to convince me that wrapping up LittlevGL might be a more pragmatic approach...

In any case, I just wanted to say keep up the good work, this will be of real interest to a lot of MicroPython developers and I'll start to get more across the implementation you've been working on.

Thanks again!
Matt

@kisvegabor
Copy link
Member

@mattytrentini It's very good to hear that you are interested in the integration of LittlevGL! Soon we will have a stable project to test Micropython and LittlevGL with the Unix port.

We will keep you updated!

@amirgon
Copy link
Contributor Author

amirgon commented Feb 4, 2019

Related: micropython/micropython#4195.

On a related note, I love the look of littlevgl ! We're starting to use micropython at work and there's talk of projects wanting lcd gui's, at first glance this looks like a pretty nice fit. I may be able to find some time to try out configuring it as a cmodule with this pr (though not likely too soon), but if you're wanting to have a go at it I'm more than happy to support any way I can!

@andrewleech It's great to hear you are interested in LittlevGL and Micropython Bindings!

If you would like to try it, you can fork lv_micropython. There is a Micropython script example to get you started, it displays some controls, uses styles, callbacks, etc.

I would love to hear some feedback on lvgl Micropython bindings!
Micropython bindings is a new initiative and the one thing we are desperately missing is user feedback.

I'm still going to make some changes in the way the lvgl is registered with Micropython (lvgl/lv_binding_micropython#5) and I'm also planning to add an ESP32 support, as an example of adding LittlevGL to a Micropython port (currently only the unix port is working).

@amirgon
Copy link
Contributor Author

amirgon commented Feb 13, 2019

Progress update:
We have a working Micropython on ESP32 + LittlevGL + ILI9341.
See lvgl/lv_binding_micropython#7.

@mattytrentini
Copy link

That's quite exciting, I look forward to trying this out! I have an M5Stack which seems to be an ideal test device (ESP32+ILI9341).

The main concern I have with this approach - bindings to a C library - is that the API is likely to reflect the C API too closely. ie not feel very pythonic. The advantage, of course, is to be able to build on top of the large existing codebase... I guess we'll have to see how it progresses!

In any case thanks again for your efforts and I'll try it out as soon as I have some time.

@amirgon
Copy link
Contributor Author

amirgon commented Feb 14, 2019

That's quite exciting, I look forward to trying this out!

@mattytrentini That's great! I'd love to hear some user feedback.

The main concern I have with this approach - bindings to a C library - is that the API is likely to reflect the C API too closely. ie not feel very pythonic

Don't be fooled by the fact that it's written in C.
LittlevGL is an object oriented component-based library. There is a base object lv_obj which all other components inherit from, and a hierarchy between the components. Objects have their method functions, inherit their parent methods etc.
I made an effort to take advantage of this design and make it feel more natural (pythonic?) to the Micropython user. You can create your own (pure Python) composite components from existing LittlevGL components by inheritance.
Here is a simple example:

class SymbolButton(lv.btn):
    def __init__(self, parent, symbol, text):
        super().__init__(parent)
        self.symbol = lv.label(self)
        self.symbol.set_text(symbol)
        self.symbol.set_style(symbolstyle)
        self.symbol.align(self, lv.ALIGN.CENTER,0,0)
        
        self.label = lv.label(self)
        self.label.set_text(text)
        self.label.align(self, lv.ALIGN.CENTER,20,0)

In this example (originally taken from here, thanks @rreilink!) we create a reusable composite component that inherits from a native LittlevGL btn component. It consists of a button and two labels, one text and the other a symbol.

For more examples please have a look at the Micropython demo script.
The same script works on both "unix+SDL" and "ESP32+ILI9341" platforms. It detects the platform automatically by trying to import the driver and catch an ImportError exception if the driver is not supported.

So, I would like to hear more about your concern.
Could you provide some examples of things that should be done in a more "Pythonic" way?

I believe there are many advantages in starting from LittlevGL instead of building something totally new in pure MicroPython:

  • LittlevGL is a popular library and very well maintained. It has many existing components, bugs are fixed very fast, questions are answered, new releases are published in a consistent way. @kisvegabor and @embeddedt are doing a great job! They are also very open to changes and additions to the library, a lot of pull requests are accepted to the library after a short review cycle.
  • Performance: Eventually C is faster, lower footprint etc. I prefer to have GUI rendering logic in C and not in Python.
  • Exposure: The more people use a library, the better the library is.
    A pure Micropython library will not be used by architectures that are not using (or cannot use) Micropython.
    But C is supported everywhere, by almost every embedded device, and all higher-end architectures. So the same library is used by more architectures and more users. There is also an effort being made to provide LittlevGL bindings to more languages (Python and Ada) which has the potential of growing the user base even more.

@amirgon
Copy link
Contributor Author

amirgon commented Feb 25, 2019

Micropython is now an official LittlevGL feature!

I would like to thank @kisvegabor and @embeddedt for their support in this effort. It is a pleasure to work with you!

I would also like to thank @rreilink for his comments, his work on the Python binding and his advanced_demo.py example script which I use with minor changes as my primary example script.
I really hope you would find time to continue working on the Python binding!

Here is a blog post about Micropython + LittlevGL.

Closing the issue.

@amirgon amirgon closed this as completed Feb 25, 2019
@kisvegabor
Copy link
Member

kisvegabor commented Feb 25, 2019

Thank you very much for adding this awesome feature and the ideas and suggestions to make LittlevGL better! Having Micropython support really makes a difference! I'm sure that there is a big potential in it!

👏 👏 👏

@manison
Copy link
Contributor

manison commented Mar 1, 2019

There's a blogpost on Hackaday.

@kisvegabor
Copy link
Member

kisvegabor commented Mar 1, 2019

@manison
A few days ago I wrote to them about LittlevGL and it seems they have chosen the Micropython binding to highlight. Anyway, I was very happy to see it :)

@manison
Copy link
Contributor

manison commented Mar 1, 2019

@kisvegabor perfect marketing 👍

@kisvegabor
Copy link
Member

Actually, my friend suggested it so it's his merit. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants