惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Darknet – Hacking Tools, Hacker News & Cyber Security
NISL@THU
NISL@THU
S
Securelist
O
OpenAI News
S
Security Affairs
Cyberwarzone
Cyberwarzone
T
Threatpost
Simon Willison's Weblog
Simon Willison's Weblog
The Last Watchdog
The Last Watchdog
L
LINUX DO - 最新话题
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
SecWiki News
SecWiki News
S
Secure Thoughts
GbyAI
GbyAI
I
Intezer
AWS News Blog
AWS News Blog
F
Fortinet All Blogs
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
Google Online Security Blog
Google Online Security Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
S
Schneier on Security
P
Proofpoint News Feed
雷峰网
雷峰网
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
小众软件
小众软件
H
Heimdal Security Blog
Microsoft Security Blog
Microsoft Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs
V
V2EX
L
Lohrmann on Cybersecurity
Security Latest
Security Latest
A
Arctic Wolf
Apple Machine Learning Research
Apple Machine Learning Research
H
Hacker News: Front Page
Cisco Talos Blog
Cisco Talos Blog
Webroot Blog
Webroot Blog
T
Tenable Blog
MyScale Blog
MyScale Blog
博客园 - 司徒正美
S
SegmentFault 最新的问题
Y
Y Combinator Blog
腾讯CDC
Hacker News: Ask HN
Hacker News: Ask HN
M
MIT News - Artificial intelligence
G
GRAHAM CLULEY

夜行人

回家路上 第一期的直播演示项目 震动检测器 正能量 在线参观CodeLab Neverland 发布 CodeLab Adapter 3.3.1 DynamicTable 之 纸糊方向盘 CodeLab DynamicTable: 一个可实施的技术方案 CodeLab Insight 发布 Alpha 版 情人节 Home Assistant 周报 && IoT 周报 (02) Joplin: 关注隐私的 Evernote 开源替代软件 浏览器的未来与 Web 传感器 Home Assistant 周报 && IoT 周报 (01) 百宝箱(01) 论自由 介绍 WebThings Home Assistant 周报 && iot 周报 (00) 百宝箱(00) 毛姆读书心得 传世之作 周末徒步 CodeLab Adapter ❤️ Jupyter/Python 航班 躲雨 夏令营途中 [译]思想--作为一种技术 The future of coding 美国之行 三门问题的程序模拟 从Python转向Pharo https://blog.just4fun.site/post/iot/iot-open-source-projects/ Python异步编程笔记 https://blog.just4fun.site/post/iot/iot-open-source-hardware-community/ 万物积木化开发者社区 CodeLab ❤️ Blender Scratch3技术分析之云变量 API(第7篇) [译]对管道(Pipes)的偏爱 [译]提出正确的问题比得到正确答案更重要 蓝牙设备与Scratch3.0 创建你的第一个Scratch3.0 Extension Scratch3技术分析之项目内部数据(第6篇) Scratch3技术分析之社区 API(第5篇) Scratch3技术分析之User API(第4篇) Scratch3技术分析之项目主页API(第3篇) Scratch3技术分析之静态资源API(第2篇) Scratch3.0、micro:bit与Windows7 https://blog.just4fun.site/post/iot/zerynth-vs-micropython/ 核聚变、方所与半宅空间 可视化编程为何是个糟糕的主意 codelab.club周末聚会 关于codelab.club '下一件大事'是一个房间 Hungry Robot - Eat everything 编程作为一种思考方式 今日简史 史蒂夫·乔布斯传 罗素自选文集 https://blog.just4fun.site/post/edx/tianjin-scratch-ai/ https://blog.just4fun.site/post/edx/richie-cms-openedx/ 徒步武功山 WebUSB与micro:bit 积木化编程与3D场景 夜宿武功山顶 scratch3-adapter接入优必选Alpha系列机器人 https://blog.just4fun.site/post/edx/video-migration-note/ scratch3-adapter重构笔记 https://blog.just4fun.site/post/edx/edx-community-members/ 两种硬件编程风格的比较 使用micro:bit自制PPT翻页笔 柏拉图对话集 scratch3.0 + micro:bit 七月电影放映计划 非营利组织的管理 Screenly--用树莓派让任何屏幕变为可编程的数字标牌 以最佳实践开始你的Django项目 micro:bit与事件驱动 为Scratch3.0设计的插件系统(上篇) OCR应用一例 近两年读过的一些好书 blockly开发之使用python驱动浏览器中的turtle(2) 牛顿新传 文学理论入门 逻辑的引擎 人生的意义 blockly开发之生成并运行js代码(1) blockly开发之hello world(0) micro:bit使用笔记 神器之Termux https://blog.just4fun.site/post/iot/micropython-notes/ Cozmo what is this Scratch的前世今生 下段旅程 我行在远方 爆裂 途中杂记 https://blog.just4fun.site/post/edx/open-edx-startup/ cozmo系列之入门 - 有性格且可编程的机器人 PaperWeekly开发笔记 创业二三事
将基于 Scratch Link 的插件迁移到 Snap!
种瓜 · 2023-11-21 · via 夜行人

文章目录

前言

近期需要把 Scratch 的一些蓝牙(BLE)插件迁移到 Snap! 里。 – Snap! 蓝牙驱动库

虽然新的 BLE 设备喜欢采用 Web Bluetooth API(诸如 microbit more), 但依然有许多 BLE 设备是通过 Scratch Link 接入 Scratch 的。

我们在之前的文章里,已经将 Web Bluetooth API 包装为 Snap! BLE primitives。为了将 Scratch Link 兼容的设备接入 Snap! , 需要弄清楚 Scratch Link 采用的通信机制(包括编解码)。弄清这些 Scratch 插件在 write/read/notify 操作在字节层面的细节, 我们就能够使用 Snap! BLE primitives 制作相同功能。

让我们试着将 Scratch 官方的 micro:bit 插件 迁移到 Snap!

一旦完成这个实验, 迁移其他的 Scratch Link 插件就轻而易举了。

分析

The challenge is not building it but understanding it – Bret 《Seeing Spaces》

Scratch Link 是一个蓝牙消息中继器, 在系统蓝牙接口与 Scratch 之间中转蓝牙消息。

我们之前在 分析 scratch3.0 与 micro:bit 的通信一文中分析了消息中转过程的细节。其中的有一处需要注意: Scratch 与 Scratch Link 通信的数据采用 Base64 格式, 而 Scratch Link 与 蓝牙设备的通信数据采用 uint8array。

micro:bit more 的作者通过 ble-web.js 实现了基于 Web Bluetooth API 的 Scratch Link。 通过阅读 ble-web.js , 我们能够更加清楚地知道 Scratch Link 做了什么。

Snap! BLE primitives 能够直接操作 uint8array(映射为 list), 所以在 Snap! 编写 BLE 插件, 要比使用 Scratch Link, ble-web.js 简易清晰得多!

开始迁移

BLE API 主要围绕对 Characteristic 进行 read/write/notify 来操作而构建。 – Snap! 蓝牙驱动库

本文的重点是展示原理, 希望突出重点,并且希望篇幅简短。所以我们只迁移这三个典型的积木。

Scratch 中使用的大多数 BLE 插件都是推(push)模式(NOTIFY) – Snap! 蓝牙驱动库

前 2 个与按下按钮相关的积木都基于 BLE notify 实现; 显示文本积木, 基于 BLE write 实现。

建立连接

首先, 我们需要建立与 BLE 设备的连接, 并且弄清楚 read/notify/write 对应的 Characteristic

以下代码 给了我们这些信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// https://github.com/scratchfoundation/scratch-vm/blob/v2.1.16/src/extensions/scratch3_microbit/index.js#L53

/**
 * Enum for micro:bit protocol.
 * https://github.com/scratchfoundation/scratch-microbit-firmware/blob/master/protocol.md
 * @readonly
 * @enum {string}
 */
const BLEUUID = {
    service: 0xf005,
    rxChar: '5261da01-fa7e-42ab-850b-7c80220097cc',
    txChar: '5261da02-fa7e-42ab-850b-7c80220097cc'
};

显示文本积木

显示文本积木相关的 javascript 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * Enum for micro:bit BLE command protocol.
 * https://github.com/scratchfoundation/scratch-microbit-firmware/blob/master/protocol.md
 * @readonly
 * @enum {number}
 */
const BLECommand = {
    CMD_PIN_CONFIG: 0x80,
    CMD_DISPLAY_TEXT: 0x81,
    CMD_DISPLAY_LED: 0x82
};

...

/**
 * @param {string} text - the text to display.
 * @return {Promise} - a Promise that resolves when writing to peripheral.
 */
displayText(text) {
    const output = new Uint8Array(text.length);
    for (let i = 0; i < text.length; i++) {
        output[i] = text.charCodeAt(i);
    }
    return this.send(BLECommand.CMD_DISPLAY_TEXT, output);
}

...


/**
     * Send a message to the peripheral BLE socket.
     * @param {number} command - the BLE command hex.
     * @param {Uint8Array} message - the message to write
     */
send(command, message) {
    if (!this.isConnected()) return;
    if (this._busy) return;

    // Set a busy flag so that while we are sending a message and waiting for
    // the response, additional messages are ignored.
    this._busy = true;

    // Set a timeout after which to reset the busy flag. This is used in case
    // a BLE message was sent for which we never received a response, because
    // e.g. the peripheral was turned off after the message was sent. We reset
    // the busy flag after a while so that it is possible to try again later.
    this._busyTimeoutID = window.setTimeout(() => {
        this._busy = false;
    }, 5000);

    const output = new Uint8Array(message.length + 1);
    output[0] = command; // attach command to beginning of message
    for (let i = 0; i < message.length; i++) {
        output[i + 1] = message[i];
    }
    const data = Base64Util.uint8ArrayToBase64(output);

    this._ble.write(BLEUUID.service, BLEUUID.txChar, data, 'base64', true).then(
        () => {
            this._busy = false;
            window.clearTimeout(this._busyTimeoutID);
        }
    );
}

有了这些信息, 我们可以在 Snap! 中尝试制作相关积木:

以上便完成了显示文本积木的功能, 所有工作都构建在我们之前的 BLE primitives 里, 没有写一行 js 代码。让我们将其包装到一个自定义积木里:

按下按钮积木

按下按钮这一类的的事件, 通常在 Scratch 中会呈现为 2 中积木: hat 和 reporter。

这是一种冗余, 仅使用 reporter 积木便足够。 通过一个通用的 when 积木, reporter 可以转化为 hat 。 Snap! 采用这种更简洁和通用的设计。

所以在 Snap! 中只需要实现 reporter 版本的按下按钮积木即可.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
 * Process the sensor data from the incoming BLE characteristic.
 * @param {object} base64 - the incoming BLE data.
 * @private
 */
_onMessage(base64) {
    // parse data
    const data = Base64Util.base64ToUint8Array(base64);

    this._sensors.tiltX = data[1] | (data[0] << 8);
    if (this._sensors.tiltX > (1 << 15)) this._sensors.tiltX -= (1 << 16);
    this._sensors.tiltY = data[3] | (data[2] << 8);
    if (this._sensors.tiltY > (1 << 15)) this._sensors.tiltY -= (1 << 16);

    this._sensors.buttonA = data[4];
    this._sensors.buttonB = data[5];

    this._sensors.touchPins[0] = data[6];
    this._sensors.touchPins[1] = data[7];
    this._sensors.touchPins[2] = data[8];

    this._sensors.gestureState = data[9];

    // cancel disconnect timeout and start a new one
    window.clearTimeout(this._timeoutID);
    this._timeoutID = window.setTimeout(
        () => this._ble.handleDisconnectError(BLEDataStoppedError),
        BLETimeout
    );
}

...

/**
 * Test whether the A or B button is pressed
 * @param {object} args - the block's arguments.
 * @return {boolean} - true if the button is pressed.
 */
isButtonPressed(args) {
    if (args.BTN === 'any') {
        return (this._peripheral.buttonA | this._peripheral.buttonB) !== 0;
    } else if (args.BTN === 'A') {
        return this._peripheral.buttonA !== 0;
    } else if (args.BTN === 'B') {
        return this._peripheral.buttonB !== 0;
    }
    return false;
}

有了这些信息, 我们可以在 Snap! 中尝试制作相关积木。以下是 A 按键按下时, 收到的数据(第 5 位代表 A 按键状态, Snap! 列表计数从 1 开始):

将其包装到一个自定义积木里:

代码的清晰性和简易性, 都比 Scratch 提升一个数量级!

hat 积木

一旦完成 reporter 积木,也就完成了 hat 积木, 只需将其拉到通用的 when 积木里即可:

参考