将Micropython嵌入Arduino项目

多天艰苦尝试,无数次失败后终于成功踏出的第一步

背景

一般情况下,Arduino项目和Micropython项目是不能同时运行在同一块esp32开发板上的

因为一个项目烧写进esp32时会擦除并覆写flash

我们的工作就是要将Micropython嵌入Arduino项目中,同时获得两者的特性

在前几年,很多前辈做过类似的尝试,如将Micropython嵌入到ESP-IDF,将Micropython嵌入到Linux C项目中

幸运的是Micropython官方看到了这个issue,并为这个嵌入工作做了很大的帮助

Micropython github地址: https://github.com/micropython/micropython

可以看到ports/embed和examples/embedding文件夹,其中官方给出了一个例子:

Example of embedding MicroPython in a standalone C application

将Micropython嵌入到一个单独的main.c中

我的工作就是参考这个例子,将Micropython嵌入到Arduino项目中

工作流程

  1. 准备Micropython运行所需的必要文件

在Linux中:

git clone https://github.com/micropython/micropython.

进入micropython/examples/embedding目录

make -f micropython_embed.mk

make

然后能看到多了个文件夹micropython_embed,这是micropython嵌入c项目中所要用到的必须文件,复制到Windows系统待用

将mpconfigport.h复制到Windows系统待用

然后在Windows下的C:\Users\Administrator\.platformio(Administrator替换为你的系统名)下新建文件夹lib\micropython_embed

将刚才复制到windows的micropython_embed下的所有内容复制到C:\Users\Administrator\.platformio\lib\micropython_embed中

然后再将mpconfigport.h复制到micropython_embed下

  1. 创建项目

打开VScode,创建Arduino项目,board选择Espressif esp32 dev

将main.cpp的内容替换如下

#include <Arduino.h>
extern "C"{
    #include "port/micropython_embed.h"
}


// This is example 1 script, which will be compiled and executed.
static const char *example_1 =
    "print('hello world!', list(x + 1 for x in range(10)), end='eol\\n')";

// This is example 2 script, which will be compiled and executed.
static const char *example_2 =
    "for i in range(10):\n"
    "    print('iter {:08}'.format(i))\n"
    "\n"
    "try:\n"
    "    1//0\n"
    "except Exception as er:\n"
    "    print('caught exception', repr(er))\n"
    "\n"
    "print('finish')\n"
    ;


void setup() {
    // Initialise MicroPython.
    //mp_embed_init(&heap[0], sizeof(heap));
    mp_embed_init();
    // Run the example scripts (they will be compiled first).
    mp_embed_exec_str(example_1);
    mp_embed_exec_str(example_2);
    // Deinitialise MicroPython.
    mp_embed_deinit();
}

void loop() {

}

example_1和example_2就是执行的micropython语句,也可以替换为其他语句

注意,我们刚才复制的C:\Users\Administrator\.platformio\lib\micropython_embed下的文件是需要根据项目需求修改的

micropython有一个特性是grabage collect(gc)

但由于刚才复制的依赖文件没有针对esp32进行设置,导致编译时会因为garbage collect出错,所以我将这些文件所有关于gc的部分都注释掉了

要启用的话需要将注释的所有gc的内容恢复,然后再阅读micropython/ports/esp32下的代码,然后进行一些针对的配置修改

  1. 编译项目

在编译项目的过程中我经历了无数次的编译失败,下面挑选一些作为案例:

c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: .pio\build\esp32dev\src\main.cpp.o:(.literal._Z5setupv+0x10): undefined reference to `mp_embed_init(void*, unsigned int)'
c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: .pio\build\esp32dev\src\main.cpp.o:(.literal._Z5setupv+0x14): undefined reference to `mp_embed_exec_str(char const*)'
c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: .pio\build\esp32dev\src\main.cpp.o:(.literal._Z5setupv+0x18): undefined reference to `mp_embed_deinit()'
c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: .pio\build\esp32dev\src\main.cpp.o: in function `setup()':
E:\Code\MicroPythonEmbedded2/src/main.cpp:26: undefined reference to `mp_embed_init(void*, unsigned int)'
c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: E:\Code\MicroPythonEmbedded2/src/main.cpp:29: undefined reference to `mp_embed_exec_str(char const*)'
c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: E:\Code\MicroPythonEmbedded2/src/main.cpp:30: undefined reference to `mp_embed_exec_str(char const*)'
c:/users/administrator/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/l
d.exe: E:\Code\MicroPythonEmbedded2/src/main.cpp:33: undefined reference to `mp_embed_deinit()'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\esp32dev\firmware.elf] Error 1

错误信息为"undefined reference to xxx_function",可我明明导入了头文件,这些函数已经有定义了才对?

经过查询,原来是“作为一种面向对象的语言,C支持函数重载,而过程式语言C则不支持。函数被C编译后在符号库中的名字与C语言的不同”

注意到micropython的所有文件其实是C文件而不是C文件,而我们的Arduino项目内都是C文件,这样引用时自然会报错

所以将

#include "port/micropython_embed.h"

修改为

extern "C"{
    #include "port/micropython_embed.h"
}

就能解决这个问题

  1. 成果展示

可以对照main.cpp的代码,看到micropython的代码成功执行

result

小总结

由于这是第一个micropython嵌入Arduino项目的Demo,复制的依赖文件只是micropython的子集

本Demo仅仅是一个嵌入的简单展示

所以想要使用micropython的某些特性暂时还无法使用(如上文提到的gc,还有我们需要的webrepl等其他功能)

要使用其他功能的话还需要阅读micropython的项目源代码,然后引入合适的文件,也可能需要对文件进行一定修改

项目地址

我已经将嵌入所需的库文件和这个Demo项目都上传到gitlab上

分别为lib/micropython-embed和MicroPythonEmbed-Demo

http://www.jtext.cn:23080/zhdyz/neoplc-plus/tree/master


本文章使用limfx的vscode插件快速发布