V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chai2010
V2EX  ›  程序员

凹语言再次点亮 Arduino nano 33

  •  
  •   chai2010 ·
    chai2010 · 60 天前 · 1506 次点击
    这是一个创建于 60 天前的主题,其中的信息可能已经有所发展或是发生改变。

    凹语言是国内首个面向 WebAssembly 设计通用编程语言,也是目前被 CNCF 基金会 wasm 全景图 收录的的唯一一个来自中国的开源编程语言项目。

    凹语言最初在 2022 年底增加了对 Arduino-wasm 平台的支持,后来在 2023 年底因为聚焦 MVP 开发临时去掉了 Arduino 目标的支持。最近在下个发布的 v0.17.0 版本中将恢复对 Arduino nano 33 开发版的支持,相对于之前的版本现在的凹语言特性更加丰富。

    1. Arduino-wasm 是什么

    Wasm3 是一个高性能的 WebAssembly 解释器,而 Arduino-wasm 则是 Wasm3 针对 Arduino 的定制版本。Wasm3 最小的硬件依赖是 ~64Kb Flash 和 ~10Kb RAM 。

    Github 仓库:https://github.com/wasm3/wasm3-arduino

    下面是 Wasm3 运行在 iOS 的截图:

    Arduino-wasm 则是运行在 Arduino Nano 33 等开发板上的 Wasm3 。

    2. Arduino Nano 33 开发板介绍

    Arduino Nano 33 ,是 Arduino Nano 的高配版本,是一款基于 nRF52840 SoC ARM 32 位处理器的微型开发板。其中 Arduino Nano BLE Sense 其主控芯片集成了蓝牙低功耗( BLE )。NANO 33 BLE 不仅保留了与经典款 NANO 同样的尺寸与管脚,且在此基础上配有多种高性能传感器等,当然最重要的是满足了 Arduino-wasm 的最低硬件要求。

    目前( 2024 年底),淘宝的价格大约在 200 元以内。

    3. 编写 Arduino 的闪灯例子

    首先用wa init生成一个 arduino 例子:

    $ wa init -arduino
    $ cd hello/
    $ tree
    .
    ├── README.
    ├── src
    │   └── main.wa
    └── wa.mod
    

    其中 main.wa 内容如下:

    // 版权 @2024 arduino 作者。保留所有权利。
    
    import "syscall/arduino"
    
    global LED = arduino.GetPinLED()
    
    func init {
    	arduino.PinMode(LED, 1)
    	arduino.Print("凹语言(Wa)/Arduino is running ...\n")
    	arduino.Print("https://wa-lang.org\n")
    
    	for {
    		arduino.DigitalWrite(LED, arduino.HIGH)
    		arduino.Delay(100)
    		arduino.DigitalWrite(LED, arduino.LOW)
    		arduino.Delay(900)
    	}
    }
    

    代码逻辑比较简单,只是换成了凹语言来写。我们直接使用了 syscall/arduino 包来使用 Arduino 的功能。

    4. syscall/arduino 包介绍

    让我们看看 syscall/arduino 包的代码:

    // 版权 @2022 凹语言 作者。保留所有权利。
    
    const (
    	LOW  :i32 = 0
    	HIGH :i32 = 1
    
    	INPUT        :i32 = 0
    	OUTPUT       :i32 = 1
    	INPUT_PULLUP :i32 = 2
    )
    
    #wa:import arduino millis
    func Millis() => i32
    
    #wa:import arduino delay
    func Delay(ms: i32)
    
    #wa:import arduino pinMode
    func PinMode(pin, mode: i32)
    
    #wa:import arduino digitalWrite
    func DigitalWrite(pin, value: i32)
    
    #wa:import arduino getPinLED
    func GetPinLED() => i32
    
    #wa:import arduino print
    func PrintRawString(ptr: i32, len: i32)
    
    func Print(s: string) {
    	print(s)
    }
    
    func Println(s: string) {
    	println(s)
    }
    

    主要是将常用的函数通过 WASM 方式导入到了代码空间,大部分函数并不在凹语言中实现。

    5. 编译到 Arduino-wasm 平台

    可以通过wa build命令构建程序:

    $ wa build
    $ tree
    .
    ├── README.md
    ├── output
    │   ├── arduino
    │   │   ├── app.wasm.h
    │   │   └── arduino.ino
    │   ├── hello.wasm
    │   └── hello.wat
    ├── src
    │   └── main.wa
    └── wa.mod
    

    output 目录存放编译的结果,output/arduino/arduino.ino是 Arduino 工程文件,output/arduino/app.wasm.houtput/hello.wasm文件对应的数据数组。

    // Auto Generate by Wa language. See https://wa-lang.org
    
    unsigned int app_wasm_len = 4608;
    
    unsigned char app_wasm[] = {
    	0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x37,
    	...
    	0x03, 0x24, 0x74, 0x30, 0x17, 0x00, 0x18, 0x00,
    };
    

    然后 Arduino 工程就可以通过 #include "app.wasm.h" 方式引用这个 WASM 程序,最终和 Wasm3 一起编译。

    6. Arduino 平台胶水代码初探

    最终还需要一个 arduino.ino 代码(其实就是针对 Arduino 简化的 C++ 程序)。代码结构如下:

    #include <wasm3.h>
    #include <m3_env.h>
    
    // 定义 WASM 和 本地栈大小
    #define WASM_STACK_SLOTS  1024
    #define NATIVE_STACK_SIZE (32*1024)
    
    // WASM 最大内存限制, 一般不得超过 64KB 大小
    #define WASM_MEMORY_LIMIT (32*1024)
    
    // 导入 凹语言 生成的 WASM 文件对应的二进制头文件
    #include "app.wasm.h"
    

    开头保护 Wasm3 和 WASM 程序对应的头文件,同时定义以下栈的大小。

    然后看看代码主体结构:

    // 执行 WASM 的函数
    void wasm_task(void*) { ... }
    
    // setup 作为 main 函数用户
    void setup() {
    	// 串口初始化
    	Serial.begin(115200);
    	delay(100);
    
    	// 等待串口初始化完成, 必须是 USB 串口
    	while(!Serial) {}
    
    	// 阻塞执行 wasm 程序, 不会返回
    	wasm_task(NULL);
    }
    
    // 该函数不会被执行
    // 定义该函数只是为了确保 Arduino 编译通过
    void loop() {
    	delay(100);
    }
    

    Arduino 的常规代码只有 setup 和 loop 两个函数。不过这里只用到了 setup 函数。在 setup 函数中首先初始化串口(方便打印调试信息),最后调用 wasm_task 执行凹语言写的亮灯代码,其中会加载 WASM 模块并执行。wasm_task 看起来是一个比较复杂的程序,不过核心逻辑和普通的 WASM 执行流程类似,细节可以下次文章再展开。

    总体来说,以上这些胶水代码是相对固定的。后面会自动生成全部这些代码,同时去掉对外部其他工具的依赖。目标是生成的 Arduino 工程文件可以直接打开构建。

    7. Arduino 构建 & 执行

    如果是第一次使用 Arduino Nano 33 开发板,打开 IDE 后会提示安装必要的工具。然后需要在库管理菜单手动安装 Wasm3 包:

    然后编译后上传的效果:

    执行的效果,除了可以看到 LED 闪烁,串口还可以看到输出信息:

    一切正常!

    8. 总结展望

    目前流行 Arduino 单片机的配置还是比较低的,可能难以运行 WASM 程序。不过可以乐观估计 Arduino Nano 33 将会很快普及。而且,Wasm3 不仅仅可以支持 Arduino ,还可以支持树莓派 Pico (淘宝价格 30 元)。因此,从长远看 WASM 是一个兼具灵活性和性价比的可选方案。

    原文: https://wa-lang.org/smalltalk/st0052.html

    1 条回复
    ZhcChen
        1
    ZhcChen  
       59 天前
    评论区也太冷清了吧。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1186 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 18:24 · PVG 02:24 · LAX 10:24 · JFK 13:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.