用 Jupyter Notebook 控制 MaixPy

MicroPython在测试程序的时候各种好用,不过通过串口测试REPL程序后,测试完的程序就永远地消失在命令行界面里了。

熟悉Python的各位大佬们都知道有一个神奇的软件叫Jupyter Notebook,可以直接在浏览器里写不限于Python语言的程序,并且可以随时选择运行程序的一部分。MicroPython也被大佬做成了Jupyter Notebook的Kernel,可以将程序留在电脑上自由编辑,随时通过串口在MicroPython上执行。

从MicroPython port出来的MaixPy也可以用这样的方法进行控制。这里简单整理一下本菜鸡在往Maix Go上搬Jupyter Notebook的时候踩过的坑QAQ

Jupyter MicroPython Kernel 本体安装

原repo中大佬的流程很详细了
https://github.com/goatchurchprime/jupyter_micropython_kernel

按照 Installation 部分的过程就可以完成 Kernel 的下载安装,安装完后在 Jupyter Notebook 新建 notebook 时就可以选择使用 MicroPython USB 的 Kernel。

安装后Kernel会留在原来的位置上,因此如果需要修改 Kernel 内的代码,直接在安装时下载的文件夹内修改,在 Jupyter Notebook 使用了该 Kernel 的 notebook 中点 Restart Kernel 就可以载入修改后的 Kernel。

Jupyter MicroPython Kernel Maix Go 适(mo)配(gai)

理论上,目前 Kernel 和我们的板子间只相隔在单独的 Cell (notebook 中代码块)执行一句

%serialconnect --port=COMx

就可以建立连接,不过在 Maix Go 上直接运行会因为一些兼容性问题把可怜的板子直接卡在复位的地方。于是接下来就到了对 Kernel 开刀的时间。(对 Dan Dock 和 Maix Bit 流程未经测试仅供参考)

1.拉低流控信号

默认情况下 pyserial 打开串口时会把 RTS DTR 两个控制信号设为逻辑真,然而非常不巧的是在 Maix Go 上这会使模块进入下载复位状态,因此我们要让连接串口时两个控制信号保持在逻辑假。

修改位置在<clone位置>/jupyter_micropython_kernel/jupyter_micropython_kernel/deviceconnector.py 的 156 行

# 删除
self.workingserial = serial.Serial(portname, baudrate, timeout=serialtimeout)
# 替换为
# 原文件中缩进为空格,若编辑器默认使用Tab缩进需要手动替换
self.workingserial = serial.Serial(None, baudrate, timeout=serialtimeout)
self.workingserial.rts = False
self.workingserial.dtr = False
self.workingserial.port = portname
self.workingserial.open()

2.写入前后添加延时

Maix Go 上的 USB 串口桥目前的固件有一些奇妙的表现,连续分次灌数据可能会突然爆炸,而 Jupyter Notebook 中连续执行时,会有连续的短控制信号。于是祭出延时大法,在每个写入前后加入延时避免爆炸。

修改位置在
<clone位置>/jupyter_micropython_kernel/jupyter_micropython_kernel/deviceconnector.py 的 520 行和 543 行

# 延时时间理论上可以更短
# 520 行
time.sleep(0.01)
nbyteswritten = self.workingserial.write(bytestosend)
time.sleep(0.01)

# 543 行
time.sleep(0.01)
self.workingserial.write(line.encode("utf8"))
time.sleep(0.01)
self.workingserial.write(b'\r\n')
time.sleep(0.01)

经过如上修改后,再执行 %serialconnect 就可以得到连接OK的信息,之后就可以正常新建 Cell,在里面写程序并执行了。
(目前似乎无法直接在notebook中中止程序执行,运行无限循环后只能手动复位)

附上修改的diff

diff --git a/jupyter_micropython_kernel/deviceconnector.py b/jupyter_micropython_kernel/deviceconnector.py
index 42b7147..0178c31 100644
--- a/jupyter_micropython_kernel/deviceconnector.py
+++ b/jupyter_micropython_kernel/deviceconnector.py
@@ -151,8 +151,13 @@ class DeviceConnector:
                 portname = ("COM4" if sys.platform == "win32" else "/dev/ttyUSB0")
 
         self.sresSYS("Connecting to --port={} --baud={} ".format(portname, baudrate))
+        self.sresSYS("\nmodify: with rts/dtr low")
         try:
-            self.workingserial = serial.Serial(portname, baudrate, timeout=serialtimeout)
+            self.workingserial = serial.Serial(None, baudrate, timeout=serialtimeout)
+            self.workingserial.rts = False
+            self.workingserial.dtr = False
+            self.workingserial.port = portname
+            self.workingserial.open()
         except serial.SerialException as e:
             self.sres(e.strerror)
             self.sres("\n")
@@ -512,7 +517,9 @@ class DeviceConnector:
 
     def writebytes(self, bytestosend):
         if self.workingserial:
+            time.sleep(0.01)
             nbyteswritten = self.workingserial.write(bytestosend)
+            time.sleep(0.01)
             return ("serial.write {} bytes to {} at baudrate {}\n".format(nbyteswritten, self.workingserial.port, self.workingserial.baudrate))
         elif self.workingwebsocket:
             nbyteswritten = self.workingwebsocket.send(bytestosend)
@@ -533,8 +540,11 @@ class DeviceConnector:
 
     def writeline(self, line):
         if self.workingserial:
+            time.sleep(0.01)
             self.workingserial.write(line.encode("utf8"))
+            time.sleep(0.01)
             self.workingserial.write(b'\r\n')
+            time.sleep(0.01)
         elif self.workingwebsocket:
             self.workingwebsocket.send(line.encode("utf8"))
             self.workingwebsocket.send(b'\r\n')

发表评论