Skip to content

Commit

Permalink
Merge pull request RIOT-OS#2968 from kaspar030/micropython
Browse files Browse the repository at this point in the history
pkg: micropython: initial commit
  • Loading branch information
bergzand authored Dec 5, 2019
2 parents b77ff84 + b5743ca commit 2287867
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 0 deletions.
30 changes: 30 additions & 0 deletions examples/micropython/Makefile
Original file line number Diff line number Diff line change
@@ -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
25 changes: 25 additions & 0 deletions examples/micropython/Makefile.ci
Original file line number Diff line number Diff line change
@@ -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 \
#
6 changes: 6 additions & 0 deletions examples/micropython/README.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions examples/micropython/boot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("boot.py: MicroPython says hello!")
77 changes: 77 additions & 0 deletions examples/micropython/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*
* @}
*/

#include <stdio.h>

#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;
}
84 changes: 84 additions & 0 deletions examples/micropython/tests/01-run.py
Original file line number Diff line number Diff line change
@@ -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))
14 changes: 14 additions & 0 deletions pkg/micropython/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
PKG_NAME=micropython
PKG_URL=https://github.com/kaspar030/micropython
PKG_VERSION=5c45688d431a4d0f626d86478ad490cfb6d8ac30
PKG_LICENSE=MIT

CFLAGS += -Wno-implicit-fallthrough -Wno-unused-parameter -Wno-error

.PHONY: all

all:
@mkdir -p $(PKG_BUILDDIR)/tmp
BUILD=$(PKG_BUILDDIR) "$(MAKE)" -C $(PKG_BUILDDIR)/ports/riot

include $(RIOTBASE)/pkg/pkg.mk
11 changes: 11 additions & 0 deletions pkg/micropython/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
USEMODULE += xtimer
USEMODULE += stdin

# MicroPython doesn't compile for <32bit platforms
FEATURES_BLACKLIST += arch_8bit arch_16bit

# This port currently requires ISR_STACKSIZE and thread_isr_stack_start
FEATURES_BLACKLIST += arch_arm7 arch_esp32 arch_esp8266 arch_riscv

# The port currently doesn't compile for mips
FEATURES_BLACKLIST += arch_mips32r2
12 changes: 12 additions & 0 deletions pkg/micropython/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# configuration
MP_RIOT_HEAPSIZE ?= 16384U

CFLAGS += -DMP_RIOT_HEAPSIZE=$(MP_RIOT_HEAPSIZE)

# include paths
INCLUDES += -I$(RIOTBASE)/pkg/micropython/include
INCLUDES += -I$(PKGDIRBASE)/micropython
INCLUDES += -I$(PKGDIRBASE)/micropython/ports/riot

# The port currently doesn't build with llvm
TOOLCHAINS_BLACKLIST += llvm
87 changes: 87 additions & 0 deletions pkg/micropython/doc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* @defgroup pkg_micropython MicroPython RIOT port
* @ingroup pkg
* @brief MicroPython - Python for microcontrollers
*
* # MicroPython RIOT package
*
* "MicroPython is a lean and efficient implementation of the Python 3
* programming language that includes a small subset of the Python standard
* library and is optimised to run on microcontrollers and in constrained
* environments."
*
* @see https://micropython.org
*
* ## Status
*
* MicroPython on RIOT has to be considered experimental. While the basic
* interpreter works fairly well on native and Cortex-M, it has not seen much
* testing.
*
* ## Configuration options
*
* Use the following environment variables in the application Makefile
* or from the command line to configure MicroPython:
*
* MP_RIOT_HEAPSIZE: heap size for MicroPython, in bytes. Defaults to 16KiB.
*
* Example on the command line:
* ```
* MP_RIOT_HEAPSIZE=2048 make -C examples/micropython
* ```
*
* ## Implementation details
*
* The RIOT port of MicroPython currently resides in a fork at
* https://github.com/kaspar030/micropython (in branch add_riot_port). It is
* based on Micropython's "ports/minimal" with some extra modules enabled.
* It re-uses the gc_collect code from ports/unix, which has special support
* for i386 and Cortex-M. On other platforms, it uses setjmp() to collect
* registers.
*
* ## MicroPython's test suite
*
* It is possible to run MicroPython's test suite for testing this port.
*
* Steps:
*
* 1. make -Cexamples/micropython flash
* 2. cd examples/micropython/bin/pkg/${BOARD}/micropython
* 3. git apply ports/riot/slow_uart_writes.patch
* 4. cd tests
* 5. ./run-tests --target pyboard --device ${PORT}
*
* ## MicroPython modules
*
* Currently, the port enables only a subset of the available MycroPython
* modules. See "ports/riot/mpconfigport.h" for details.
*
* For now, the utime module has RIOT specific code and should work as expected.
*
* ## RIOT specific modules
*
* Currently, these are implemented:
*
* ### thread_getpid()
*
* >>> import riot
* >>> print(riot.thread_getpid())

* ### xtimer
*
* >>> import xtimer
* >>>
* >>> a = 0
* >>> def inc_a():
* >>> global a
* >>> a += 1
* >>>
* >>> t = xtimer.xtimer(inc_a)
* >>> t.set(100000)
* >>> print(a)
*
* ## How to use
*
* See examples/micropython for example code.
*
*/
Loading

0 comments on commit 2287867

Please sign in to comment.