MicroPython——有用但不多

引言

之前做过一个树莓派驱动墨水屏的项目,本来想整理出来与大家分享的,但是由于树莓派已经成了理财产品,所以为了让这个项目更加具有实践意义,最近我打算把这个项目移植到ESP32上。在树莓派上我使用的是Python编写的代码,所以出于简便考虑(后来发现其实也不是很简便) 我决定用ESP32的MicroPython框架来编写代码。
正如标题所言,在我最近的MicroPython学习和实践过程中,我发现MicroPython框架有那么点意思,但同时也有很多的问题和缺点,这篇文章我将分享最近在MicroPython学习和实践过程中的一些看法和见解。

MicroPython简介

在MicroPython出现之前,C/C++几乎是给单片机写程序的唯一的高级语言。2013年,澳大利亚程序员和物理学家Damien George通过众筹活动发布了MicroPython框架和第一个支持该框架的pyboard开发板1,让开发者能够使用Python语言来开发单片机。
时至今日,MicroPython框架已经支持包含ESP32在内的多种架构的单片机,但是MicroPython框架一直处于一种不温不火的状态,下面我将会从一个计算机专业的学生、一个开源硬件爱好者、半个STEM教育从业者的角度去探讨MicroPython。

从计算机专业学生角度看MicroPython

对于一个计算机专业的大学生来说,对于MicroPython的认识肯定不能只停留在“别的单片机只能拿C语言开发,装了MicroPython框架以后就能运行Python代码”这么简单,我们还是要大致了解一下它的原理的。

解释型语言与单片机

众所周知,C语言是编译型语言,而Python是解释型语言。当我们在个人电脑上运行这两种语言编写的程序的时候并不能感受到很大的区别,无非前者是将代码整体编译成机器码再执行,后者是将代码逐句解释成机器码执行。

但是当我们将目光转向单片机的时候,情况就有所不同了:我们需要考虑编译(解释)源代码这个操作到底在哪里完成?对于C语言来说,将C源代码编译成单片机能够执行的机器码的工作是在我们的PC机上完成的,我们只需要将编译好的机器码烧录到单片机上,单片机就能够执行我们的程序;而对于MicroPython来说,单片机可以实时执行我们发送的Python代码,这就意味着解释Python源码的工作是在单片机上完成的,而运行在单片机上的这套Python解释器就是我们所说的MicroPython框架。

解释型语言在单片机上的缺点

对比编译型语言,解释型语言的一大缺点就是运行速度慢,这个问题不论是在PC上还是在单片机上都是存在的。而解释型语言在运行时是直接解释源码这一特性,在PC机上看似并没有什么问题,但到了单片机上就成为了一个新的缺点。
目前的PC机的硬盘容量少说也是GB级别起步的,几KB哪怕是几MB的源代码根完全可以忽略不计,而对于MicroPython而言,我们需要把源代码存储在单片机的FLASH上,以ESP32系列单片机为例,其FLASH容量只有2-4MB,这个时候源代码的大小就是一个无法忽略的事情了。
我们不得不在有限的空间里面尽量想办法减小源代码的大小,比如说删除所有的注释,变量名和函数名起的尽量简短一些,这些都可以有效地减小源代码的大小。但作为一种高级语言,为了程序的可读性,我们却又是是希望在代码中添加注释、给各类标识符(变量名、函数名、类名等)起一个有意义的名字,这两者之间便产生了不可调和的矛盾
如果说注释和变量名在源码中并不能占据太多的空间的话,在MicroPython中存储位图则是更大的一笔开销:例如一张40x40像素的黑白位图需要40*40 = 1600bit = 200Byte的空间,在MicroPython中可以这样表示:

myimg = bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\x3f\xe7\xfc\xff\xff\x1f\xe7\xf8\xff\xff\x8f\xff\xf1\xff\xff\xc7\xff\xe3\xff\xff\xe7\x81\xe7\xff\xff\xfe\x00\x7f\xff\xff\xfc\x00\x3f\xff\xff\xf8\x00\x1f\xff\xff\xf8\x00\x1f\xff\xff\xf0\x00\x0f\xff\xff\xf0\x00\x0f\xff\xe0\x30\x00\x0c\x07\xe0\x30\x00\x0c\x07\xff\xf0\x00\x0f\xff\xff\xf0\x00\x0f\xff\xff\xf8\x00\x1f\xff\xff\xf8\x00\x1f\xff\xff\xfc\x00\x3f\xff\xff\xfe\x00\x7f\xff\xff\xe7\x81\xe7\xff\xff\xc7\xff\xe3\xff\xff\x8f\xff\xf1\xff\xff\x1f\xe7\xf8\xff\xff\x3f\xe7\xfc\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xe7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff')

在上面的例子中,一个\xff表示1Byte,但是\xff本身是4个字符,占用了4Byte的空间。所以说用想在MicroPython中硬编码一个图片,至少需要花费图片本身大小4倍的空间,这无疑是对本不富裕的FLASH空间雪上加霜。但是如果我们在C语言中硬编码一个图片,却不会导致空间的浪费,这是因为编译的过程将“源代码中用字符表示的字节”转换成了“真正的字节”再烧进单片机里的。
当然,MicroPython提供了基础的文件系统,如果不用硬编码的方式把图片存储在代码里面,而是单独作为一个文件存放,就可以避免这个问题,不过这也会引来新的问题,我后续的博客中会详细分享我的解决方案。所以从存储图片这个角度来说,MicroPython并不比用C语言开发方便。
总之,从一个计算机专业学生角度来看,我无时无刻不在怀念C编译器给我们带来的便利,让我们能够随心所欲加注释、起变量名、定义常量、将字节数组硬编码进程序,无需担心源代码的大小,只要把源代码交给编译器,最后烧录进单片机的是编译后的机器码。

从开源硬件爱好者角度看MicroPython

对于开源硬件爱好者(创客)来说,最关心的问题是能否方便快捷地实现我们的创意。如果想实现一个功能不太复杂的小玩意的话,我猜大多数人都会选择使用Arduino,这是因为Arduino的生态十分完善,网上不仅有各种教程、例程的分享,还有各种传感器、硬件模块的第三方库可以供我们直接使用,用Arduino编程与其说是在“写”代码,不如说是在“拼”代码
而对于MicroPython,它处在一个十分尴尬的境地:它自己的生态没有发展起来,真Python的代码也不能直接照搬。

MicroPython自己的生态

MicroPython自己的生态目前的生态并不完善,主要体现在:缺少相应的模块库可以直接“拼”代码,那么想用MicroPython的人就少,相应的用MicroPython做的项目也少,能够参考的项目代码也少。

扩展库

Arduino官网上收录了上千个扩展库(如下图2),如果想用Arduino驱动一个硬件模块,通常我们只需要找到对应型号的库,然后参考例程就可以开始愉快地“拼代码”了。

而MicroPython只提供了标准库的文档(并且MicroPython的标准库只是Python标准库的子集),同时官方也没有对扩展库作出统一的收录,如果我们想用MicroPython驱动一个硬件模块,只能尝试使用搜索引擎手动搜集别人写好的库,更大概率的情况下我们只能从轮子造起,翻芯片的数据手册,然后先写驱动再实现上层的功能(显然有这个闲工夫我肯定去用隔壁的Arduino了)。

项目

搜索关键词“Arduino 项目”和“MicroPython 项目”,前者的搜索结果数就是后者的10倍,并且可以看到Arduino有很多系统的适合初学者的入门教程,但是MicroPython只有一些爱好者的相对来说比较复杂的项目分享,这也为新手入门MicroPython设置了一个较高的门槛。

用户活跃度

上述的各种问题最终导致的结果就是MicroPython的用户很少,在著名IT问答网站Stack Overflow上搜索包含Arduino标签和MicroPython标签的问题,两者的数量差异也达两倍之多(Arduino还有专门的Stack Exchange板块3,而MicroPython没有)。提问的人越少,也就意味着帮你踩坑的前人越少,碰到问题没有现成的解决方案,通常只能自己想办法;同时也是因为活跃的用户少,即使提出一个问题,能够得到及时的回答的可能性也越少。

借用Python生态的难度

Python之所以好用不仅是因为它简洁的语法和列表、字典等好用的数据结构,更是因为它有很多强大的第三方扩展库。写一个Python项目几乎不可能只用标准库而不使用第三方的扩展库。
以我做的Python驱动墨水屏的项目为例,我用到了pillow库来绘制墨水屏上需要显示的图像,用了datetime库将时间戳格式化为日期字符串。当我尝试将Python程序移植到MicroPython中时,我发现MicroPython没有这些库,将这些库移植到MicroPython上几乎是不可能的,唯一的办法就是使用MicroPython仅有的东西去重新实现一遍所需要的功能
在Python中使用datetime库将“时间戳格式化为日期字符串”只需:

import datetime
timestamp = 1668521610
dt = datetime.datetime.fromtimestamp(timestamp)
print(dt.strftime('%m月%d日 %H:%M'))

而在MicroPython中并不能使用datetime库,只能使用内置的time.mktime()函数将时间戳转换为一个(年,月,日,时,分,秒,星期,一年中的第几天)的元组。所以要实现"时间戳格式化为日期字符串"则需要自己实现:

import utime as time
class MyDateTime:
    def __init__(s, timestamp, tz=8):
        timestamp = int(timestamp)
        timestamp += tz * 3600
        tt = time.gmtime(timestamp)
        s.m, s.d, s.H, s.M, s.s= tt[1:6]
    def strftime(s, str_):
        return str_.replace('%m', str(s.m)).replace('%d', str(s.d)).replace('%H', str(s.H).zfill(2)).replace('%M', str(s.M).zfill(2))
        
timestamp = 1668521610
dt = MyDateTime(timestamp)
print(dt.strftime('%m月%d日 %H:%M'))

这只是最简单的一个移植的例子,在MicroPython中要实现绘图功能需要使用自带的framebuf模块而这个模块相比pillow库的提供的功能少得多,移植起来更加麻烦。
也就是说,想要让一个Python程序成功在MicroPython上运行,也是需要进行大量的移植和适配工作,MicroPython并不能直接吃到Python生态所带来的红利。

从STEM教育角度看MicroPython

虽然说了那么多MicroPython的问题和缺点,但在我看来MicroPython依然有其存在的价值,就目前而言我认为MicroPython在STEM教育领域的意义是更大的。

MicroPython对于STEM教育的意义

不论是在STEM教育机构、国际学校、高中信息技术课,Python在已经逐渐成为孩子们接触编程时的第一种语言了,而MicroPython提供了一个从虚拟世界的Python走向物理世界的硬件的一个桥梁。
目前也有一些STEM教育领域的公司开发了使用MicroPython作为编程语言的套件,如由英国广播电视公司(BBC)推出的micro:bit开发板4、国内公司Makeblock推出的光环板等开发板5,都可以使用MicroPython进行编程。这类开发板通常板载常用的传感器和一组LED矩阵,这方便教学时不用拼接额外的模块就能够实现基本的项目。

在此之前,如果想要给孩子们启蒙硬件,只能使用类似于arduinoblocks的图形化编程或者重写学习C语言,对于刚入门编程的孩子们来说,这不仅会增加学习成本,而且会降低他们的学习动机。而MicroPython为硬件提供了统一的语言,让孩子们可以把学会的Python技能在单片机上得到实践,让孩子们明白编程不仅可以在电脑屏幕上算个数字、画个图,还可以操控硬件、点亮LED、驱动小车。我认为尤其是在STEM教育领域,对于兴趣的培养要远比能力的培养重要得多


  1. https://www.kickstarter.com/projects/214379695/micro-python-python-for-microcontrollers ↩︎

  2. https://www.arduino.cc/reference/en/libraries/ ↩︎

  3. https://arduino.stackexchange.com/ ↩︎

  4. https://microbit.org/code/ ↩︎

  5. https://www.makeblock.com/cn/steam-kits/halocode ↩︎

物联沃分享整理
物联沃-IOTWORD物联网 » MicroPython——有用但不多

发表评论