{"id":1050,"date":"2021-07-21T08:51:10","date_gmt":"2021-07-21T01:51:10","guid":{"rendered":"https:\/\/bigdolphin.com.vn\/?p=1050"},"modified":"2024-03-26T15:25:42","modified_gmt":"2024-03-26T08:25:42","slug":"playing-with-maixduino-making-camera-gui-with-lvgl-and-micropython","status":"publish","type":"post","link":"https:\/\/bigdolphin.com.vn\/?p=1050","title":{"rendered":"Playing with Maixduino: making Camera GUI with LvGl and MicroPython"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">1. Prepare<\/h3>\n\n\n\n<ul><li>Maixduino<\/li><li>LCD 320&#215;240<\/li><li>Camera<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Enable LvGl<\/h3>\n\n\n\n<p>LvGl is not enabled as default module in Maixduino firmware. Go to this <a rel=\"noreferrer noopener\" href=\"https:\/\/bigdolphin.com.vn\/?p=981&amp;lang=en\" target=\"_blank\">post<\/a> to enable LvGl and rebuild firmware<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1002\" height=\"488\" src=\"https:\/\/bigdolphin.com.vn\/wp-content\/uploads\/2021\/07\/image-33.png\" alt=\"\" class=\"wp-image-1086\" srcset=\"https:\/\/bigdolphin.com.vn\/wp-content\/uploads\/2021\/07\/image-33.png 1002w, https:\/\/bigdolphin.com.vn\/wp-content\/uploads\/2021\/07\/image-33-300x146.png 300w, https:\/\/bigdolphin.com.vn\/wp-content\/uploads\/2021\/07\/image-33-768x374.png 768w\" sizes=\"(max-width: 1002px) 100vw, 1002px\" \/><figcaption>Enable LvGl in firmware source<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">3. Usage<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">3.1 Init LvGl<\/h4>\n\n\n\n<p>LCD module must be initialized before initializing LvGl. LCD can be rotated well but it will be bug if you want to rotate GUI based on LvGl. Therefore, this tutorial keeps LCD angle as 0 degree (no rotation).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\"># Init LCD\nlcd.init(freq=15000000)\nlcd.rotation(0)\nlcd.clear()\n# Init LvGl\nlv.init()\ndisp_buf1 = lv.disp_buf_t()\nbuf1_1 = bytearray(320*10)\nlv.disp_buf_init(disp_buf1,buf1_1, None, len(buf1_1)\/\/4)\ndisp_drv = lv.disp_drv_t()\nlv.disp_drv_init(disp_drv)\ndisp_drv.buffer = disp_buf1\ndisp_drv.flush_cb = lv_h.flush\ndisp_drv.hor_res = 320\ndisp_drv.ver_res = 240\nlv.disp_drv_register(disp_drv)\nscr = lv.obj()\n# Set background color\nscr_style = lv.style_t(lv.style_plain)\nscr_style.body.main_color = lv.color_hex(0)\nscr_style.body.grad_color = lv.color_hex(0)\nscr.set_style(scr_style)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">3.2 Add buttons and image frame to screen<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\"># Add elements to screen\n# Capture button\nbtn_capture = lv.btn(scr)\nbtn_capture.set_size(100,50)\nbtn_capture.align(lv.scr_act(), lv.ALIGN.IN_BOTTOM_MID, 0, 0)\nlabel_capture = lv.label(btn_capture)\nlabel_capture.set_text(\"Capture\")\nlabel_capture.set_size(20,20)\n# Left button\nbtn_left = lv.btn(scr)\nbtn_left.set_size(50,50)\nbtn_left.align(lv.scr_act(), lv.ALIGN.IN_BOTTOM_LEFT, 0, 0)\nlabel_left = lv.label(btn_left)\nlabel_left.set_text(\"&lt;&lt;\")\nlabel_left.set_size(50,50)\n# Right button\nbtn_right = lv.btn(scr)\nbtn_right.set_size(50,50)\nbtn_right.align(lv.scr_act(), lv.ALIGN.IN_BOTTOM_RIGHT, 0, 0)\nlabel_right = lv.label(btn_right)\nlabel_right.set_text(\"&gt;&gt;\")\nlabel_right.set_size(50,50)\n# Image frame\nvideo = lv.img(scr)\nvideo.align(scr, lv.ALIGN.IN_TOP_LEFT, (320 - (240-55))\/\/2, 0)\n# Reload screen\nlv.scr_load(scr)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">3.3 Timer for renderring LvGl<\/h4>\n\n\n\n<p>Timer interval should be based on FPS of capturing.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\"># Camera frame speed\ncamera_fps = 30\n# LvGl render interval\ntimer_period = 1000\/\/camera_fps\n# Timer counting flag for other functions\ntimer_flag = 0\ndef on_timer(timer):\n    global timer_flag\n    global timer_period\n    timer_flag = timer_flag + 1\n    # Notice to LvGL that timer_period passed\n    lv.tick_inc(timer_period)\n# Activate timer\ntimer = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, period=timer_period, unit=Timer.UNIT_MS, callback=on_timer, arg=None)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">3.4 Main function<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\"># Main task\nled_state = 0\n# Target rect 50x50 center of QVGA.\ntarget_rect = [(224-100)\/\/2, (224-100)\/\/2, 100, 100]\nwhile True:\n    # Capture camera\n    snapshot = sensor.snapshot()\n    # Draw target rectangle\n    snapshot.draw_rectangle(target_rect,color=(0,255,0),thickness=5)\n    # Resize to fit into image frame on screen\n    snapshot = snapshot.resize(240-55,240-55)\n    video_data = snapshot.to_bytes()\n    video_dsc= lv.img_dsc_t({\n        'header':{\n            'always_zero': 0,\n            'w':snapshot.width(),\n            'h':snapshot.height(),\n            'cf':lv.img.CF.TRUE_COLOR\n        },\n        'data_size': len(video_data),\n        'data': video_data\n    })\n    video.set_src(video_dsc)\n    lv.task_handler()\n    if timer_flag &gt; (500\/\/timer_period):\n        timer_flag = 0\n        led_state = ~led_state\n        led_rgb(1,1,led_state)\n    gc.collect()<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">3.5 Full script<\/h4>\n\n\n\n<p>Full script can be found at <a href=\"https:\/\/github.com\/bigdolphin\/maixduino\/tree\/main\/examples_micropython\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/bigdolphin\/maixduino\/tree\/main\/examples_micropython<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\"># Notice: LvGl must be enabled in firmware\nimport sensor, image, time, lcd, gc, micropython\nimport lvgl as lv\nimport lvgl_helper as lv_h\n\nfrom fpioa_manager import fm\nfrom board import board_info\nfrom machine import Timer\n\n###################################\nprint('\\n-----------------------------')\n# Check frequencies and overclock\nimport gc, micropython\nfrom Maix import freq, GPIO, utils\nfrom machine import reset\n\ncpu_frq, kpu_frq=freq.get()\nprint(\"\\nCPU Frq = %d MHz\" % (cpu_frq))\nprint(\"KPU Frq = %d MHz\" % (kpu_frq))\n\nif cpu_frq != 546 or kpu_frq != 450:\n    print(\"Removing old frequency...\")\n    os.remove(\"freq.conf\")\n    print(\"Overclocking CPU to 546 MHz and KPU to 450 MHz...\")\n    # kpu frequency is pll1\/kpu_div\n    freq.set (cpu=546, pll1=450, kpu_div=1)\n\ngc.enable()\ngc.collect()\ngc.threshold(gc.mem_free() \/\/ 4 + gc.mem_alloc())\nmicropython.mem_info()\nmem_heap = utils.gc_heap_size()\nheap_free = utils.heap_free()\nprint(\"Heap size: %d bytes, free: %d bytes\" % (mem_heap,heap_free))\nif mem_heap != 393216:\n    print(\"Decreasing GC heap size...\")\n    utils.gc_heap_size(393216)\n    reset()\nprint('-----------------------------')\n###################################\n\n# Register GPIO\nfm.register(board_info.LED_R, fm.fpioa.GPIO0)\nfm.register(board_info.LED_G, fm.fpioa.GPIO1)\nfm.register(board_info.LED_B, fm.fpioa.GPIO2)\nled_r=GPIO(GPIO.GPIO0, GPIO.OUT)\nled_g=GPIO(GPIO.GPIO1, GPIO.OUT)\nled_b=GPIO(GPIO.GPIO2, GPIO.OUT)\n\n# Function to control RGB led\ndef led_rgb(r,g,b):\n    led_r.value(r)\n    led_g.value(g)\n    led_b.value(b)\n\n# Function to init camera sensor\n# Maixduino can also work with OV5640\ndef init_sensor():\n    sensor.reset()\n    sensor.set_pixformat(sensor.RGB565)\n    sensor.set_framesize(sensor.QVGA)\n    # set to 224x224 input\n    sensor.set_windowing((224, 224))\n    sensor.set_vflip(0)\n    sensor.set_hmirror(0)\n    sensor.run(1)\n    sensor.skip_frames(30)\n\n# Camera frame speed\ncamera_fps = 30\n# LvGl render interval\ntimer_period = 1000\/\/camera_fps\n# Timer counting flag for other functions\ntimer_flag = 0\ndef on_timer(timer):\n    global timer_flag\n    global timer_period\n    timer_flag = timer_flag + 1\n    # Notice to LvGL that timer_period passed\n    lv.tick_inc(timer_period)\n\nled_rgb(0,1,1)\nclock = time.clock()\n\n# Init LCD\nlcd.init(freq=15000000)\nlcd.rotation(0)\nlcd.clear()\n# Init LvGl\nlv.init()\ndisp_buf1 = lv.disp_buf_t()\nbuf1_1 = bytearray(320*10)\nlv.disp_buf_init(disp_buf1,buf1_1, None, len(buf1_1)\/\/4)\ndisp_drv = lv.disp_drv_t()\nlv.disp_drv_init(disp_drv)\ndisp_drv.buffer = disp_buf1\ndisp_drv.flush_cb = lv_h.flush\ndisp_drv.hor_res = 320\ndisp_drv.ver_res = 240\nlv.disp_drv_register(disp_drv)\nscr = lv.obj()\n# Set background color\nscr_style = lv.style_t(lv.style_plain)\nscr_style.body.main_color = lv.color_hex(0)\nscr_style.body.grad_color = lv.color_hex(0)\nscr.set_style(scr_style)\n\ninit_sensor()\nled_rgb(1,0,1)\n\n# Add elements to screen\n# Capture button\nbtn_capture = lv.btn(scr)\nbtn_capture.set_size(100,50)\nbtn_capture.align(lv.scr_act(), lv.ALIGN.IN_BOTTOM_MID, 0, 0)\nlabel_capture = lv.label(btn_capture)\nlabel_capture.set_text(\"Capture\")\nlabel_capture.set_size(20,20)\n# Left button\nbtn_left = lv.btn(scr)\nbtn_left.set_size(50,50)\nbtn_left.align(lv.scr_act(), lv.ALIGN.IN_BOTTOM_LEFT, 0, 0)\nlabel_left = lv.label(btn_left)\nlabel_left.set_text(\"&lt;&lt;\")\nlabel_left.set_size(50,50)\n# Right button\nbtn_right = lv.btn(scr)\nbtn_right.set_size(50,50)\nbtn_right.align(lv.scr_act(), lv.ALIGN.IN_BOTTOM_RIGHT, 0, 0)\nlabel_right = lv.label(btn_right)\nlabel_right.set_text(\">>\")\nlabel_right.set_size(50,50)\n# Image frame\nvideo = lv.img(scr)\nvideo.align(scr, lv.ALIGN.IN_TOP_LEFT, (320 - (240-55))\/\/2, 0)\n# Reload screen\nlv.scr_load(scr)\n# Activate timer\ntimer = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, period=timer_period, unit=Timer.UNIT_MS, callback=on_timer, arg=None)\n###################################\n# Main task\nled_state = 0\n# Target rect 50x50 center of QVGA.\ntarget_rect = [(224-100)\/\/2, (224-100)\/\/2, 100, 100]\nwhile True:\n    clock.tick()\n    # Capture camera\n    snapshot = sensor.snapshot()\n    # Get real FPS\n    fps =clock.fps()\n    lcd.draw_string(2,2 ,(\"%2.1ffps\" %(fps)),lcd.WHITE,lcd.BLACK)\n    # Draw target rectangle\n    snapshot.draw_rectangle(target_rect,color=(0,255,0),thickness=5)\n    # Resize to fit into image frame on screen\n    snapshot = snapshot.resize(240-55,240-55)\n    video_data = snapshot.to_bytes()\n    video_dsc= lv.img_dsc_t({\n        'header':{\n            'always_zero': 0,\n            'w':snapshot.width(),\n            'h':snapshot.height(),\n            'cf':lv.img.CF.TRUE_COLOR\n        },\n        'data_size': len(video_data),\n        'data': video_data\n    })\n    video.set_src(video_dsc)\n    lv.task_handler()\n    if timer_flag > (500\/\/timer_period):\n        timer_flag = 0\n        led_state = ~led_state\n        led_rgb(1,1,led_state)\n    gc.collect()<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4. Test<\/h3>\n\n\n\n<figure class=\"wp-block-video aligncenter\"><video controls src=\"https:\/\/bigdolphin.com.vn\/wp-content\/uploads\/2021\/07\/maixduino_lvgl_ui_camera.mp4\"><\/video><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>A simple GUI for camera on Maixduino based on LvGl and MicroPython.<\/p>\n","protected":false},"author":2,"featured_media":1052,"comment_status":"open","ping_status":"open","sticky":false,"template":"single-with-sidebar","format":"standard","meta":{"gtb_hide_title":false,"gtb_wrap_title":false,"gtb_class_title":"","gtb_remove_headerfooter":false,"footnotes":""},"categories":[10],"tags":[44,43,31,42,30,32],"_links":{"self":[{"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/posts\/1050"}],"collection":[{"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1050"}],"version-history":[{"count":12,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/posts\/1050\/revisions"}],"predecessor-version":[{"id":1100,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/posts\/1050\/revisions\/1100"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=\/wp\/v2\/media\/1052"}],"wp:attachment":[{"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1050"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1050"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bigdolphin.com.vn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}