From b5743cab407131340e504913f3adaf99589849df Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 16 Jul 2019 15:58:29 +0200 Subject: [PATCH] examples/micropython: initial micropython example application --- examples/micropython/Makefile | 30 ++++++++++ examples/micropython/Makefile.ci | 25 +++++++++ examples/micropython/README.md | 6 ++ examples/micropython/boot.py | 1 + examples/micropython/main.c | 77 +++++++++++++++++++++++++ examples/micropython/tests/01-run.py | 84 ++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 examples/micropython/Makefile create mode 100644 examples/micropython/Makefile.ci create mode 100644 examples/micropython/README.md create mode 100644 examples/micropython/boot.py create mode 100644 examples/micropython/main.c create mode 100755 examples/micropython/tests/01-run.py diff --git a/examples/micropython/Makefile b/examples/micropython/Makefile new file mode 100644 index 0000000000..1bd38786ea --- /dev/null +++ b/examples/micropython/Makefile @@ -0,0 +1,30 @@ +# name of your application +APPLICATION = micropython + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# select MicroPython package +USEPKG += micropython + +# include boot.py as header +BLOBS += boot.py + +# configure MicroPython's heap size +MP_RIOT_HEAPSIZE ?= 8192U + +# MicroPython needs a larger stack +CFLAGS += '-DTHREAD_STACKSIZE_MAIN=THREAD_STACKSIZE_DEFAULT*4' + +# use miniterm (instead of the default pyterm) in order to support control +# characters (CTRL-D ...) +RIOT_TERMINAL ?= miniterm + +# enable modmachine support for peripherals if available +FEATURES_OPTIONAL += periph_adc +FEATURES_OPTIONAL += periph_spi + +include $(RIOTBASE)/Makefile.include diff --git a/examples/micropython/Makefile.ci b/examples/micropython/Makefile.ci new file mode 100644 index 0000000000..8f89fbf320 --- /dev/null +++ b/examples/micropython/Makefile.ci @@ -0,0 +1,25 @@ +BOARD_INSUFFICIENT_MEMORY := \ + blackpill \ + bluepill \ + calliope-mini \ + i-nucleo-lrwan1 \ + microbit \ + nrf51dongle \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + opencm904 \ + saml10-xpro \ + saml11-xpro \ + stm32f0discovery \ + spark-core \ + stm32f030f4-demo \ + stm32l0538-disco \ + # diff --git a/examples/micropython/README.md b/examples/micropython/README.md new file mode 100644 index 0000000000..56f5e68cfa --- /dev/null +++ b/examples/micropython/README.md @@ -0,0 +1,6 @@ +# Overview + +WARNING: RIOT's MicroPython port is currently quite incomplete! + +This application provides an example on how to use MicroPython with RIOT. +Please see the documentation of pkg/micropython. diff --git a/examples/micropython/boot.py b/examples/micropython/boot.py new file mode 100644 index 0000000000..3f3c50a4c8 --- /dev/null +++ b/examples/micropython/boot.py @@ -0,0 +1 @@ +print("boot.py: MicroPython says hello!") diff --git a/examples/micropython/main.c b/examples/micropython/main.c new file mode 100644 index 0000000000..be5b7c00d2 --- /dev/null +++ b/examples/micropython/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief micropython example application + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "thread.h" + +#include "micropython.h" +#include "py/stackctrl.h" +#include "lib/utils/pyexec.h" + +#include "blob/boot.py.h" + +static char mp_heap[MP_RIOT_HEAPSIZE]; + +int main(void) +{ + int coldboot = 1; + + /* let MicroPython know the top of this thread's stack */ + uint32_t stack_dummy; + mp_stack_set_top((char*)&stack_dummy); + + /* Make MicroPython's stack limit somewhat smaller than actual stack limit */ + mp_stack_set_limit(THREAD_STACKSIZE_MAIN - MP_STACK_SAFEAREA); + + while (1) { + /* configure MicroPython's heap */ + mp_riot_init(mp_heap, sizeof(mp_heap)); + + /* execute boot.py + * + * MicroPython's test suite gets confused by extra output, so only do + * this the first time after the node boots up, not on following soft + * reboots. + */ + if (coldboot) { + puts("-- Executing boot.py"); + mp_do_str((const char *)boot_py, boot_py_len); + puts("-- boot.py exited. Starting REPL.."); + coldboot = 0; + } + + /* loop over REPL input */ + while (1) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + puts("soft reboot"); + } + + return 0; +} diff --git a/examples/micropython/tests/01-run.py b/examples/micropython/tests/01-run.py new file mode 100755 index 0000000000..067029d74a --- /dev/null +++ b/examples/micropython/tests/01-run.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +import sys +from testrunner import run + + +def testfunc(child): + def get_time(): + child.sendline('utime.time()') + child.readline() + res = int(child.readline().rstrip()) + child.expect_exact('>>>') + return res + + child.expect_exact('boot.py: MicroPython says hello!') + child.expect_exact('>>>') + + child.sendline('print("echo this! " * 4)') + child.expect_exact('echo this! echo this! echo this! echo this!') + child.expect_exact('>>>') + + # test riot.thread_getpid() + child.sendline('import riot') + child.sendline('print(riot.thread_getpid())') + child.expect_exact('2') + child.expect_exact('>>>') + + # + # test xtimer integration + # + + child.sendline('import utime') + child.expect_exact('>>>') + + # testing timing over serial using the REPL is very inaccurate, thus + # we allow a *large* overshoot (100 by default). + def test_sleep(t, slack=100): + before = get_time() + child.sendline('utime.sleep_ms(%s)' % t) + child.expect_exact('>>>') + duration = get_time() - before + print("test_sleep(%s, %s): slept %sms" % (t, slack, duration)) + assert duration > t and duration < (t + slack) + return duration + + # get overhead from sleeping 0ms, add 10 percent + slack = int(test_sleep(0, 1000) * 1.1) + + test_sleep(50, slack) + test_sleep(250, slack) + test_sleep(500, slack) + + # test setting timers + child.sendline('import xtimer') + child.expect_exact('>>>') + child.sendline('a = 0') + child.expect_exact('>>>') + + child.sendline('def inc_a(): global a; a+=1') + child.expect_exact('...') + child.sendline('') + child.expect_exact('>>>') + + child.sendline('t = xtimer.xtimer(inc_a)') + child.expect_exact('>>>') + + before = get_time() + + child.sendline('t.set(500000)') + child.expect_exact('>>>') + + child.sendline('while a==0: pass') + child.expect_exact('...') + child.sendline('') + child.expect_exact('>>>') + + duration = get_time() - before + assert duration > 500 + + print("[TEST PASSED]") + + +if __name__ == "__main__": + sys.exit(run(testfunc))