串口设备和 Arduino
Serial* 内置函数可以与任何显示为 COM 端口的设备通信:通过 USB 连接的 Arduino、ESP32 或 Raspberry Pi Pico,USB 转串口适配器,GPS 接收器,处于串口模式的条码扫描器,小票或标签打印机,接受 G 代码的 3D 打印机或 CNC 控制器,实验室仪器,或者自制的按钮盒、宏键盘、旋钮或 LED 控制器。设备收发的是一行行文本;您的脚本可以把这些行变成 Input.Observer 能做的任何其他事情。
一些思路:
- 按钮盒或宏键盘。Arduino 读取其按钮并打印
B1、B2……串口监视器将每一行变成一个快捷键、一个媒体键或一条窗口命令。 - 音量或滚动旋钮。电位器或旋转编码器打印其位置;监视器据此设置音量或进行滚动。
- 状态灯。手势或热键向驱动 LED 灯带的开发板发送
LED 255 0 0,用灯光表示正在录制或构建失败。 - 传感器。计时器向开发板询问温度或光照强度,然后显示该值、将其记录到文件中,或者调暗屏幕。
- 扫描器和读卡器。读取器发送的每个条码或 RFID 标签都会变成键入的文本、一次搜索,或在文件中的一次查找。
- 机器。向打印机或绘图仪发送 G 代码行,每条命令一次
SerialWriteTextLine。
查找端口
Windows 会为每个设备分配一个类似 COM3 的名称。设备管理器在 端口 (COM 和 LPT) 下列出它;Arduino IDE 在其开发板菜单中显示它。脚本也可以列出这些端口:
n = SerialEnumeratePorts();
if (n == 0) UtilityPrint("No COM ports. Is the board plugged in?");
for (i = 0; i < n; i = i + 1) {
UtilityPrint(SerialGetEnumeratedPortAt(i));
}
列表按编号排序,因此 COM3 排在 COM10 之前。当设备换到另一个 USB 插口时,编号可能会改变。
速率、帧格式和行结束符
- 速率(
baudRate)必须与设备一致:即 Arduino 草图(sketch)中Serial.begin(...)里的数字,通常为9600或115200。 - 帧格式几乎总是 8 个数据位、无校验、1 个停止位:
8、SerialParity.None、SerialStopBits.One。请写常量:作为停止位参数的普通数字1表示 1.5 个停止位。 - 行结束符。接收到的一行在您指定的
terminator处结束,该结束符会从行中移除。空文本表示 CR LF,即 Arduino 的Serial.println所发送的内容。对于只以 LF 结束行的设备,请使用"\n";只以 CR 结束行的设备,请使用"\r"。SerialWriteTextLine发送的内容以同一个结束符结尾;如果是该调用自己打开端口,则以 CR LF 结尾。使用 CR LF 时,用Serial.readStringUntil('\n')读取的 Arduino 草图得到的行末尾会带有一个 CR,可用trim()去除;使用"\n"时则没有需要去除的 CR。 - 信号。打开端口时会开启其 DTR 和 RTS 信号,与 Arduino IDE 的做法相同。这会使大多数 Arduino Uno 和 Nano 开发板以及采用相同复位电路的其他开发板重启。
与设备通信的三种方式
-
一问一答。在未打开的端口上调用
SerialWriteTextLine和SerialGetTextLine时,会以该调用的baudRate、8-N-1 打开该端口,仅用于这一次调用。端口已打开时,将忽略baudRate参数。适用于打开端口时不会重启的设备。请参阅向串口设备提问。 -
保持端口打开。
SerialOpenPort以您的设置打开端口,直到调用SerialClosePort为止,并收集设备发送的每一行,供SerialGetTextLine读取。大多数 Arduino Uno 和 Nano 开发板在端口每次打开时都会重启,因此请在 Load 全局事件中只打开一次端口,然后让手势和热键向其写入。对于不存在、已拔出或已被其他程序打开的端口,SerialOpenPort返回false;对于 Input.Observer 已打开或被监视器占用的端口,它会使操作因错误而停止。// Load global event: open the port once, at the sketch's speed. An Uno or Nano// restarts every time its port opens, so a port opened per command would lose// the command while the board boots.if (!SerialOpenPort("COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "")) {UtilityPrint("COM4 isn't available: unplugged, or open in the Arduino IDE's Serial Monitor?");}// Any gesture or hotkey: send a command line and read the board's reply.// (The sketch reads a line with Serial.readStringUntil('\n') and answers OK.)if (SerialWriteTextLine("COM4", "LED 255 0 0", 115200)) {reply = SerialGetTextLine("COM4", 1, 115200);if (reply != "OK") UtilityPrint(StringFormat("Board said: {0}", reply, ""));} -
监视器。
SerialMonitorCreate在后台为设备发送的每一行运行一段脚本,直到调用SerialMonitorDelete为止。该行由ContextGetSerialTextLine()获取。请参阅将串口设备的按钮映射到媒体键以及:// Arduino side: a potentiometer on A0, one reading per line.// void setup() { Serial.begin(115200); }// void loop() { Serial.println(analogRead(A0)); delay(50); }// Empty text as the terminator means CR LF, which Serial.println sends.SerialMonitorCreate("knob", "COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "","line = ContextGetSerialTextLine(); if (StringIsNumber(line, true)) MultimediaSetVolume(AudioEndpoint.Playback, StringToNumber(line, true) / 1023.0);");
关于监视器需要了解的事项。
- 各行按到达顺序逐一运行:上一行的脚本结束后,下一行的脚本才会开始。最多可有 256 行等待;超出后会丢弃最早的行。
- 停止所有操作会停止正在为某一行运行的脚本,并丢弃等待中的行。监视器本身继续运行。停止所有操作热键则更进一步:它会删除每个监视器并关闭其端口。
- 任何脚本(包括监视器自身的脚本)都可以用
SerialWriteTextLine向被监视的端口发送内容,因此监视器可以应答其设备。SerialGetTextLine无法读取被监视的端口:每一行都交给监视器。 - 每个监视器名称只属于一个端口。使用该端口当前监视器的名称调用
SerialMonitorCreate会替换该监视器。如果该名称已在监视另一个端口、端口已用SerialOpenPort打开,或另一个监视器占用了该端口,它会使操作因错误而停止。不存在、已拔出或已被其他程序打开的端口会得到false。 - 拔下设备会结束监视器,并在控制台的 系统 选项卡中写入一行。设备重新连接时不会自动重启监视器。请再次运行
SerialMonitorCreate(通过热键,或在下次启动时通过 Load 事件)。
同一时间只能有一个程序打开某个 COM 端口。当 Arduino IDE 的串口监视器或串口绘图仪处于打开状态时,Input.Observer 无法打开该端口;而当 Input.Observer 打开了该端口时,IDE 无法上传草图。上传之前请先关闭端口(SerialClosePort 或 SerialMonitorDelete)。