- 一、为什么需要进制转换
- 二、App Inventor 2 内置的进制相关积木块
- 三、常用进制转换方法
- 四、位运算积木块
- 五、补码(Two’s Complement)计算
- 六、字节与整数互转
- 七、浮点数与字节互转
- 八、ASCII 码与文本互转
- 九、UTF-8 编码与文本转换
- 十、实战案例:串口通信数据解析
- 十一、常用工具过程汇总
- 十二、积木块速查表
- 参考资料
- 参考资料与版权声明
一、为什么需要进制转换
在 App Inventor 2 开发中,进制转换和位运算在以下场景中非常常见:
- 串口通信:蓝牙、BLE、USB 串口收发的数据通常是十六进制或字节流
- 物联网协议:Modbus、MQTT 等协议的数据帧涉及多字节拼合和解析
- 传感器数据:温度、加速度等传感器返回的原始数据需要按字节解析
- 颜色编码:ARGB 颜色值本质上是十六进制表示的 32 位整数
App Inventor 2 内置了丰富的数学积木块来支持这些操作,本文将逐一详解。
只是想收发设备的数据帧(拼包、把两个字节还原成温度、算 CRC 校验)?内置的二进制组件一块积木就能做,见二进制数据专题。本文讲的是背后的原理和纯积木写法。
二、App Inventor 2 内置的进制相关积木块
2.1 基本数字块(Basic Number Block)
基本数字块 number 不仅仅支持十进制,还支持通过 C 语言风格前缀输入其他进制:
| 前缀 | 进制 | 示例 | 十进制值 |
|---|---|---|---|
0b |
二进制(Base-2) | 0b1010 |
10 |
0o |
八进制(Base-8) | 0o14 |
12 |
0x |
十六进制(Base-16) | 0xFF |
255 |
使用方法:直接在数字块中输入带前缀的数字,App Inventor 会自动将其识别为对应进制并转换为十进制值。
数字块: 0xFF → 值为 255
数字块: 0b1010 → 值为 10
数字块: 0o17 → 值为 15
注意:十六进制字母不区分大小写,
0xFF和0xff等价。
2.2 基数数字块(Radix Number Block)
基数数字块提供了一种更直观的进制输入方式。点击下拉菜单可以选择输入进制:
- decimal(十进制,Base-10):支持正数、负数、小数
- binary(二进制,Base-2):仅支持非负整数
- octal(八进制,Base-8):仅支持非负整数
- hexadecimal(十六进制,Base-16):仅支持非负整数
基数数字块 [hex] FF → 值为 255
基数数字块 [binary] 11111111 → 值为 255
基数数字块 [decimal] 255 → 值为 255
三个积木块返回的值完全相同,只是输入方式不同。
2.3 convert number 积木块(进制转换核心)
这是进制转换最核心的积木块:
global ret1 = convert_number("255", 10, 16)
global ret2 = convert_number("10", 10, 2)
global ret3 = convert_number("FF", 16, 10)
关键特性:
- 输入和输出都是文本类型(text),不是数字类型(number)
- 支持的进制:base-2(二进制)、base-8(八进制)、base-10(十进制)、base-16(十六进制)
- 转换后的十六进制字母为大写(如
FF,不是ff)
2.4 format as decimal 积木块
将数字格式化为指定小数位数的文本:
global ret4 = format_as_decimal(3.14159, 2)
global ret5 = format_as_decimal(255, 0)
三、常用进制转换方法
3.1 十进制转十六进制
procedure 十进制转十六进制(十进制数) {
global ret_val = convert_number(十进制数, 10, 16)
}
示例:
| 输入(十进制) | 输出(十六进制) |
|---|---|
| 255 | “FF” |
| 16 | “10” |
| 10 | “A” |
| 0 | “0” |
3.2 十六进制转十进制
procedure 十六进制转十进制(十六进制文本) {
global ret_val = convert_number(十六进制文本, 16, 10)
}
注意:convert number 返回的是文本,如果需要参与数学运算,需要用 number 函数转换:
procedure 十六进制文本转数值(十六进制文本) {
global ret_val = convert_number(十六进制文本, 16, 10)
}
3.3 十进制转二进制
procedure 十进制转二进制(十进制数) {
global ret_val = convert_number(十进制数, 10, 2)
}
示例:
| 输入(十进制) | 输出(二进制) |
|---|---|
| 10 | “1010” |
| 255 | “11111111” |
| 128 | “10000000” |
3.4 二进制转十进制
procedure 二进制转十进制(二进制文本) {
global ret_val = convert_number(二进制文本, 2, 10)
}
3.5 十六进制与二进制互转
App Inventor 的 convert number 不直接支持 base-16 到 base-2 的转换,但可以中转:
procedure 十六进制转二进制(十六进制文本) {
global 十进制文本 = convert_number(十六进制文本, 16, 10)
global ret_val = convert_number(十进制文本, 10, 2)
}
3.6 字符串左侧补零
在很多通信协议中,数据需要固定位宽(如 8 位二进制、2 位十六进制),这就需要左侧补零:
procedure 左侧补零(原始文本, 目标长度) {
global 补零数 = 目标长度 - lengthOfList(原始文本)
if (补零数 > 0) {
global ret_val = join(重复文本("0", 补零数), 原始文本)
} else {
global ret_val = 原始文本
}
}
示例:
| 原始文本 | 目标长度 | 输出 |
|---|---|---|
| “FF” | 4 | “00FF” |
| “A” | 2 | “0A” |
| “1010” | 8 | “00001010” |
完整过程——十进制转 2 位十六进制:
procedure 十进制转两位十六进制(十进制数) {
global 十六进制文本 = convert_number(十进制数, 10, 16)
global ret_val = 左侧补零(十六进制文本, 2)
}
四、位运算积木块
App Inventor 2 内置了三个位运算积木块,它们在处理二进制数据时非常有用。
4.1 Bitwise And(按位与)
global res1 = bitwise_and(数值A, 数值B)
逐位比较,两个对应位都为 1 时结果位为 1,否则为 0。
6 = 0110
3 = 0011
----------
AND = 0010 = 2
典型用途:提取特定位、掩码操作。
// 提取低 4 位
global res2 = bitwise_and(数值, 15) // 0xF is 15
// 提取高 4 位
global res3 = bitwise_and(数值, 240) // 0xF0 is 240
4.2 Bitwise Or (Inclusive)(按位或)
global res4 = bitwise_ior(数值A, 数值B)
逐位比较,任一对应位为 1 时结果位为 1。
6 = 0110
3 = 0011
----------
OR = 0111 = 7
典型用途:合并标志位。
// 设置第 0 位为 1
global res5 = bitwise_ior(数值, 1)
4.3 Bitwise Or (Exclusive / XOR)(按位异或)
global res6 = bitwise_xor(数值A, 数值B)
逐位比较,两个对应位不同时结果位为 1,相同时为 0。
6 = 0110
3 = 0011
----------
XOR = 0101 = 5
典型用途:数据校验、简单加密、翻转特定位。
4.4 位运算的组合应用:提取字节
将一个 16 位整数拆分为高字节和低字节:
procedure 提取高字节(十六位整数) {
global ret_val = bitwise_and(十六位整数, 65280) // 0xFF00
}
注意:App Inventor 2 没有内置的位移(shift)积木块,但可以通过除法和乘法模拟:
- 右移 n 位 = 除以 2^n 后取 floor
- 左移 n 位 = 乘以 2^n
procedure 右移8位(数值) {
global ret_val = math.floor(数值 / 256)
}
procedure 提取高字节(十六位整数) {
global ret_val = math.floor(十六位整数 / 256)
}
procedure 提取低字节(十六位整数) {
global ret_val = bitwise_and(十六位整数, 255)
}
五、补码(Two’s Complement)计算
5.1 什么是补码
在计算机中,有符号整数使用补码(Two’s Complement)来表示负数。对于一个 N 位有符号整数:
- 最高位(MSB)为 0:表示正数,值就是其二进制表示
- 最高位(MSB)为 1:表示负数,值的绝对值 = 按位取反再加 1
以 8 位有符号整数为例:
| 二进制 | 无符号值 | 有符号值(补码) |
|---|---|---|
| 00000000 | 0 | 0 |
| 01111111 | 127 | 127 |
| 10000000 | 128 | -128 |
| 11111111 | 255 | -1 |
| 11111110 | 254 | -2 |
5.2 在 App Inventor 2 中计算补码
场景1:将无符号字节转换为有符号值
从串口收到的数据是 0~255 的无符号字节,但某些传感器返回的是有符号温度值(-128~127)。需要将无符号值转为有符号值:
procedure 无符号字节转有符号(无符号字节) {
if (无符号字节 > 127) {
global ret_val = 无符号字节 - 256
} else {
global ret_val = 无符号字节
}
}
示例:
| 无符号值 | 有符号值 |
|---|---|
| 255 | -1 |
| 254 | -2 |
| 128 | -128 |
| 127 | 127 |
| 0 | 0 |
场景2:将有符号值转换为无符号字节(用于发送)
procedure 有符号转无符号字节(有符号数值) {
if (有符号数值 < 0) {
global ret_val = 有符号数值 + 256
} else {
global ret_val = 有符号数值
}
}
示例:
| 有符号值 | 无符号字节 |
|---|---|
| -1 | 255 |
| -128 | 128 |
| 0 | 0 |
| 127 | 127 |
场景3:16 位有符号整数(Int16)的补码
procedure 无符号16位转有符号(无符号数值) {
if (无符号数值 > 32767) {
global ret_val = 无符号数值 - 65536
} else {
global ret_val = 无符号数值
}
}
场景4:求一个数的补码二进制表示
将一个有符号整数转为 N 位二进制补码字符串:
procedure 求补码二进制(负整数) {
global 无符号值 = 256 + 负整数 // 对于8位
global ret_val = 十进制转二进制(无符号值)
}
示例:
| 有符号值 | 位数 | 补码二进制 |
|---|---|---|
| 5 | 8 | “00000101” |
| -5 | 8 | “11111011” |
| -1 | 8 | “11111111” |
| -128 | 8 | “10000000” |
| 127 | 8 | “01111111” |
原理说明:
以 -5 的 8 位补码为例:
- -5 + 256 = 251
- 251 的二进制 = 11111011
- 这就是 -5 的 8 位补码表示
六、字节与整数互转
6.1 多字节拼合为整数(解析收到的数据帧)
在串口通信中,经常需要将多个字节拼合成一个 16 位或 32 位整数。
大端序(Big-Endian,高位在前)
procedure 两字节大端拼合(高字节, 低字节) {
global ret_val = 高字节 * 256 + 低字节
}
示例:收到字节 0x1A, 0x2B → 结果 = 0x1A * 256 + 0x2B = 6699
小端序(Little-Endian,低位在前)
procedure 两字节小端拼合(高字节, 低字节) {
global ret_val = 低字节 * 256 + 高字节
}
示例:收到字节 0x2B, 0x1A → 低字节=0x2B, 高字节=0x1A → 结果 = 0x1A * 256 + 0x2B = 6699
四字节拼合为 32 位整数
procedure 四字节大端拼合(字节0, 字节1, 字节2, 字节3) {
global ret_val = 字节0 * 16777216 + 字节1 * 65536 + 字节2 * 256 + 字节3
}
其中 16777216 = 2^24,65536 = 2^16
6.2 整数拆分为字节(构建发送数据帧)
procedure 整数拆两字节大端(数值) {
global 高字节 = math.floor(数值 / 256)
global 低字节 = modulo(数值, 256)
global ret_val = [高字节, 低字节]
}
procedure 整数拆四字节大端(数值) {
global 字节0 = math.floor(数值 / 16777216)
global 字节1 = math.floor(modulo(数值, 16777216) / 65536)
global 字节2 = math.floor(modulo(数值, 65536) / 256)
global 字节3 = modulo(数值, 256)
global ret_val = [字节0, 字节1, 字节2, 字节3]
}
七、浮点数与字节互转
7.1 问题描述
很多传感器(如温度传感器、IMU)返回的数据是 IEEE 754 浮点数,需要将 4 个字节还原为浮点数,或将浮点数转为 4 字节用于发送。
App Inventor 2 没有内置浮点数与字节的互转积木块,但可以通过 Web浏览器1 执行 JavaScript 来实现。
7.2 使用 Web浏览器1 进行浮点数转换
步骤1:在 Screen 中添加一个 Web浏览器1 组件(可以设为不可见)。
步骤2:编写 HTML 文件,包含浮点数转换的 JavaScript 函数:
<!-- float_converter.html -->
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body>
<script>
// 浮点数转4字节(小端序)
function floatToBytes(value) {
var buf = new ArrayBuffer(4);
new Float32Array(buf)[0] = value;
var bytes = new Uint8Array(buf);
return JSON.stringify([bytes[0], bytes[1], bytes[2], bytes[3]]);
}
// 4字节转浮点数(小端序)
function bytesToFloat(b0, b1, b2, b3) {
var buf = new ArrayBuffer(4);
var bytes = new Uint8Array(buf);
bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3;
return new Float32Array(buf)[0];
}
</script>
</body>
</html>
步骤3:在 App Inventor 中调用:
when 蓝牙客户端1.BytesReceived(numberOfBytes) {
global 字节列表 = 蓝牙客户端1.ReceiveSignedBytes(numberOfBytes)
// 字节列表形如:[-1, 85, 10, ...]
}
7.3 利用 Web 组件进行转换
另一种方式是通过 Web 组件调用本地 JavaScript:
when 蓝牙客户端1.BytesReceived(numberOfBytes) {
global 无符号字节列表 = 蓝牙客户端1.ReceiveUnsignedBytes(numberOfBytes)
// 无符号字节列表形如:[255, 85, 10, ...]
}
八、ASCII 码与文本互转
8.1 文本转 ASCII 码
App Inventor 2 没有直接的 ord() 函数,但可以使用文本积木块组合实现:
procedure 文本转ASCII码列表(文本) {
global 字节列表 = []
global i = 1
while (i <= length(文本)) {
global 字符 = segment(文本, i, 1)
// 这里需要扩展或自定义方法转换字符为ASCII,示意
addItemsTo(字节列表, CharToAscii(字符))
global i = i + 1
}
global ret_val = 字节列表
}
注意:App Inventor 的文本块中没有直接的 charAt 或 ord 积木块,但可以通过以下方式实现:
segment(文本, 起始位置, 长度)— 截取子串- 对于 ASCII 码转换,可以使用 JavaScript 通过 Web浏览器1 调用
charCodeAt()
8.2 ASCII 码转文本
使用 characterFromCode 积木块(在文本相关操作中)将 Unicode 码点转为字符。
九、UTF-8 编码与文本转换
9.1 问题场景
通过蓝牙或串口收到 UTF-8 编码的字节流,需要还原为中文文本。
9.2 解决方案
App Inventor 2 内部的文本编码为 UTF-8,所以:
- 如果使用蓝牙
ReceiveText方法,App Inventor 会自动处理 UTF-8 解码 - 如果使用
ReceiveBytes或Signed/Unsigned Byte接收,需要手动处理
对于手动字节流到 UTF-8 文本的转换,推荐使用 Web浏览器1 + JavaScript:
function bytesToUTF8(byteArray) {
var uint8 = new Uint8Array(byteArray);
var decoder = new TextDecoder('utf-8');
return decoder.decode(uint8);
}
十、实战案例:串口通信数据解析
10.1 场景描述
通过蓝牙串口接收传感器数据,协议格式如下:
帧头: 0xAA 0x55
长度: 1字节(数据区字节数)
数据区: 温度(2字节, 有符号, 大端序) + 湿度(2字节, 无符号, 大端序)
校验: 1字节(数据区所有字节异或值)
10.2 数据帧示例
假设收到以下十六进制字节:
AA 55 04 00 C8 01 90 58
解析:
AA 55— 帧头04— 数据长度(4字节)00 C8— 温度:0x00C8 = 200 → 有符号 = +200(0.1°C单位,即 20.0°C)01 90— 湿度:0x0190 = 400(0.1%单位,即 40.0%)58— 校验:0x00 ⊕ 0xC8 ⊕ 0x01 ⊕ 0x90 = 0x59… 实际需要验证
10.3 App Inventor 2 解析代码
global 字节列表 = [255, 1, 10]
蓝牙客户端1.SendBytes(字节列表)
10.4 提取温度和湿度值
procedure 处理有效数据(字节列表) {
if (lengthOfList(字节列表) >= 5) {
global 帧头 = selectListItem(字节列表, 1)
global 地址 = selectListItem(字节列表, 2)
global 命令 = selectListItem(字节列表, 3)
global 数据 = selectListItem(字节列表, 4)
global 校验和 = selectListItem(字节列表, 5)
}
}
10.5 发送十六进制命令
procedure 发送十六进制命令(hexString) {
global 字节列表 = 十六进制文本转字节列表(hexString)
蓝牙客户端1.SendBytes(字节列表)
}
十一、常用工具过程汇总
以下是将本文所有工具过程整理在一起,方便直接使用:
左侧补零
procedure 左侧补零(原始文本, 目标长度) {
global 补零数 = 目标长度 - length(原始文本)
if (补零数 > 0) {
global ret_val = join(重复文本("0", 补零数), 原始文本)
} else {
global ret_val = 原始文本
}
}
十进制转固定位宽十六进制
procedure 十进制转固定位十六进制(十进制数, 长度) {
global 十六进制文本 = convert_number(十进制数, 10, 16)
global ret_val = 左侧补零(十六进制文本, 长度)
}
无符号转有符号
procedure 无符号转有符号(无符号字节) {
if (无符号字节 > 127) {
global ret_val = 无符号字节 - 256
} else {
global ret_val = 无符号字节
}
}
异或校验
procedure 异或校验(字节列表) {
global 校验值 = 0
for item in 字节列表 {
global 校验值 = bitwise_xor(校验值, item)
}
global ret_val = 校验值
}
十二、积木块速查表
| 需求 | 使用哪个积木块 | 所在分类 |
|---|---|---|
| 输入十六进制数 | 基本数字块 0xFF 或基数数字块 |
Math |
| 输入二进制数 | 基本数字块 0b1010 或基数数字块 |
Math |
| 十进制 ↔ 十六进制 | convert number |
Math |
| 十进制 ↔ 二进制 | convert number |
Math |
| 十六进制 ↔ 二进制 | 先转十进制再转目标进制(中转法) | Math |
| 按位与(掩码) | Bitwise And |
Math |
| 按位或(置位) | Bitwise Or |
Math |
| 按位异或(校验) | Bitwise Or (Exclusive) |
Math |
| 右移 n 位 | floor(数值 / 2^n) |
Math |
| 左移 n 位 | 数值 * 2^n(用 Bitwise And 截断) |
Math |
| 补码转有符号 | if 值 > 2^(n-1)-1 then 值 - 2^n |
Math + Control |
| 浮点数 ↔ 字节 | Web浏览器1 + JavaScript(IEEE 754) | Web浏览器1 |
| ASCII 码 ↔ 字符 | segment + Unicode 操作 | Text |
参考资料
- MIT App Inventor Math Blocks 官方文档
- MIT App Inventor Text Blocks 官方文档
- App Inventor 中文网
- App Inventor 2 蓝牙BLE扩展源码分析
| *文档版本:2026.05 | 作者:App Inventor 2 中文网 www.fun123.cn* |
参考资料与版权声明
原文来源
- MIT App Inventor Math Blocks - MIT CML
- MIT App Inventor GitHub - MIT CML
版权声明
本文档基于 MIT App Inventor 官方文档整理编写,参考 Apache 2.0 授权内容。 本文档由 ai2claw 🐝 编写整理,App Inventor 2 中文网(www.fun123.cn)原创发布,仅供学习参考。
扫码添加客服咨询