分享刚造的轮子:MessagePack 的 C 库(little-endian 版本)
本帖最后由 dukelec 于 2021-10-19 03:56 编辑很早之前就一直在用 MessagePack(简称 msgpack),不过是在电脑和服务器,我一直觉得 msgpack 很适合 MCU,但之前一直没有机会在 MCU 里面用它
msgpack 和 json 差不多,不过 msgpack 可以包含二进制数据,MCU 处理起来比 json 方便超多,所以用来代替 json
最近需要在 MCU 里面使用 msgpack,找了一圈,没有相中的库,我的需求是足够简约且不使用 malloc
所以开始自己写,msgpack 对于 MCU 有一个让我很不爽的地方是,它是大端的,需要转换来转换去
思考了很久,打算创建一个小端版本的 MessagePack 分支:MessagePackEL,除了大小端,其它完全一致
我建的分支已经包含了 C、Python 和 Javascript 的实现: https://github.com/dukelec/msgpackel
其中 C 实现是我新写的,使用方法我贴过来:
(如果你想用于 大端 的 msgpack,库改起来也很简单)
譬如一个数据结构:
{
"dev": "abc123",
"data": {
"voltage": 3.6,
"angle":
}
}
转换成字节流的示范:
uint8_t msg_buf;
size_t prepare_data(void)
{
uint8_t *pos = msg_buf;
pos = mpk_w_map_hdr(pos, 2); // create top map
pos = mpk_w_str(pos, "dev"); // create first item
pos = mpk_w_str(pos, "abc123");
pos = mpk_w_str(pos, "data"); // create second item
pos = mpk_w_map_hdr(pos, 2); // create sub map
pos = mpk_w_str(pos, "voltage");
pos = mpk_w_float(pos, 3.6f);
pos = mpk_w_str(pos, "angle");
pos = mpk_w_array_hdr(pos, 2);
pos = mpk_w_int(pos, 45);
pos = mpk_w_int(pos, 90);
return pos - msg_buf; // return length
}
解析字节流的示范:
void check_data(void)
{
mpk_t st;
uint8_t *payload, *end;
size_t num;
float voltage;
st = mpk_map_search(msg_buf, NULL, NULL, (uint8_t *)&voltage, NULL,
"data", "voltage", NULL); // must end with NULL
if (st == MPK_FLOAT)
printf("found data.voltage: %f\n", voltage);
else
printf("parse data.voltage error: %d\n", st);
st = mpk_map_search(msg_buf, &payload, &end, NULL, NULL,
"dev", NULL);
if (st == MPK_STR) {
int str_len = end - payload;
printf("found dev: %.*s\n", str_len, payload);// print non-null terminated string
} else {
printf("parse dev error: %d\n", st);
}
st = mpk_map_search(msg_buf, &payload, NULL, NULL, &num,
"data", "angle", NULL);
if (st == MPK_ARRAY) {
uint8_t *pos = payload;
printf("data.angle[] length: %d\n", num);
for (int i = 0; i < num; i++) {
int32_t val; // val must be at least 4 bytes
st = mpk_parse(pos, NULL, &end, (uint8_t *)&val, NULL);
if (st != MPK_INT && st != MPK_UINT) {
printf("parse data.angle error\n");
break;
}
printf("data.angle[%d]: %ld\n", i, val);
pos = end;
}
} else {
printf("parse data.angle error: %d\n", st);
}
}
服务器也可以同时支持 json 和 msgpack,收到 json 就回覆 json,收到 msgpack 就回 msgpack,中间处理完全相同,譬如:
#!/usr/bin/env python3
import cgi, sys, os, json
import umsgpackel
mime = os.environ.get('CONTENT_TYPE', '')
is_mpk = True if mime.find('msgpackel') != -1 else False
if is_mpk:
print('Content-Type: application/msgpackel; charset=binary\n')
else:
print('Content-Type: application/json; charset=utf-8\n')
sys.stdout.flush()
if is_mpk:
data = sys.stdin.buffer.read(int(os.environ.get('CONTENT_LENGTH', 0)))
else:
data = sys.stdin.read(int(os.environ.get('CONTENT_LENGTH', 0))) # default=0
if data: # for POST
class Form(dict):
pass
if is_mpk:
unpk = umsgpackel.unpackb(data)
else:
unpk = json.loads(data)
form = Form(unpk)
form.getvalue = form.get
else: # for GET
form = cgi.FieldStorage()
#dev = form.getvalue('dev')
# do some work ...
print(f"receive: {unpk} |", file=sys.stderr)
if is_mpk:
sys.stdout.buffer.write(umsgpackel.packb({"status": "ok"}))
else:
print(json.dumps({"status": "ok"}))
这是 http(s) 的示范,msgpack 也可以用于 mqtt 传输,同样是取代 json 厉害!!! 大佬好像啥多会{:lol:} 2021的今天为啥要用message pack. 不学, 不研究, 不在意 BongBong 发表于 2021-10-19 01:57
2021的今天为啥要用message pack. 不学, 不研究, 不在意
有更好的推薦? message pack remark! BongBong 发表于 2021-10-19 01:57
2021的今天为啥要用message pack. 不学, 不研究, 不在意
可有合适的推荐?
JSON解析起来麻烦, modbus做板级数据交互能力不太够. 我都直接cjson lindabell 发表于 2021-10-19 08:33
我都直接cjson
回头试试看, 能json方便不少 本帖最后由 dukelec 于 2021-10-19 09:40 编辑
lindabell 发表于 2021-10-19 08:33
我都直接cjson
mcu 上用,我不喜欢依赖 malloc 的库,因为我不用动态内存分配,做为代替,我用链表来实现,避免内存碎片化
我手上一个 iot 项目,一开始平台方用的就是 json,我让他们改用 msgpack,对方没同意
我当时让对方换 msgpack 的理由是,虽然目前是一些简单的传感器数据,但日后会增加摄像头,需要传输图片,用 msgpack 可以传输二进制方便一点,协议改动也小,认证什么的都不用改
对方说要兼容其他家的硬件,不想改,而且对方觉得摄像头短期不会上马,所以懒得改
我说可以同时兼容 json 和 msgpack,不影响其他家硬件,对方还是没有动力改
我只能被迫先写了一个简单的不严谨的 json 解析器,凑合一下
这段时间已经上马摄像头,需要传输少量图片,还是要对方支持上 msgpack
我的其它开源项目,譬如 CDBUS GUI、CD-PnP 的 python 后端 和 网页前端 之间的交互,以及配置的导入导出,还有 CDEncrypt 加密工具输出文件打包的格式等等,都统一用的是 msgpack
和 json 相比,msgpack 更高效、用途更多,对于高级语言序列化更方便(譬如很多数据类型用 json 序列化则会报错),对于 mcu 上使用也更简单
(只有需要人工直接编辑的配置文件,我才会选择 json,譬如 CDBUS GUI 的按钮配置文件用的是 json5,或者直接用 python 文件当作配置文件)
至于数据调试,二进制直接阅读稍微麻烦一点,但是通常都可以用软件打印解析后的数据,还有很多工具可以用来解析,所以还好
(在线的转换工具譬如有: https://kawanet.github.io/msgpack-lite/ ) albert_w 发表于 2021-10-19 08:32
可有合适的推荐?
JSON解析起来麻烦, modbus做板级数据交互能力不太够.
自定义一个协议,同蓝牙物理底层一样。
例如:包头+SN+LEN+PAYLOAD+CHECKSUM
效率又高,并且PAYLOAD全部走结构体,要通用就cjson
2021年了,不走回头路。
BongBong 发表于 2021-10-19 13:30
自定义一个协议,同蓝牙物理底层一样。
例如:包头+SN+LEN+PAYLOAD+CHECKSUM
效率又高,并且PAYLOAD全部 ...
我们现在就类似这么干的, 但有很多麻烦, 和非C语言交换数据太麻烦了. albert_w 发表于 2021-10-19 13:43
我们现在就类似这么干的, 但有很多麻烦, 和非C语言交换数据太麻烦了.
那就json好了。 本帖最后由 dukelec 于 2021-10-19 22:21 编辑
BongBong 发表于 2021-10-19 13:30
自定义一个协议,同蓝牙物理底层一样。
例如:包头+SN+LEN+PAYLOAD+CHECKSUM
效率又高,并且PAYLOAD全部 ...
我搞的 CDBUS GUI 用的協議就是你說的這種,只適合高實時性、高性能、協議開銷必須很低的場合,譬如伺服控制
但是這種 schemafull 數據結構的修改和版本維護很麻煩,要通訊雙方的一致性非常高才行,並不是很靈活
所以和服務器通訊,爲了靈活,方便擴充字段什麼的,應該選擇 schemaless 的方案,隨手找了一篇文,你可以看看:
THE SEEDY WORLD OF MESSAGE SERIALIZATION - by: Kerry Scharfglass
https://hackaday.com/2020/06/10/the-ceedy-world-of-message-serialization dukelec 发表于 2021-10-19 22:19
我搞的 CDBUS GUI 用的協議就是你說的這種,只適合高實時性、高性能、協議開銷必須很低的場合,譬如伺服 ...
理解
我只是跳开技术来说
现在MCU内存大, 速度快, 开销无所谓
要么就精简, 要么就json一般都能覆盖了.
特殊的再说
毕竟时间也很贵
发明轮子的基础工程师是少数
应用工程师要赚钱就要搞主流的 本帖最后由 dukelec 于 2021-10-20 00:49 编辑
BongBong 发表于 2021-10-20 00:20
理解
我只是跳开技术来说
现在MCU内存大, 速度快, 开销无所谓
json 不方便傳輸二進制數據,擴展性不好,譬如傳輸波形數據、圖片、固件等,在 json 內使用 base64 的話效率太低
正是因爲時間寶貴,所以我才選擇更簡單方便擴展性好的 msgpack
msgpack 存在很久了,並不是小衆,很多大公司在用
對於 MCU,文本格式的協就沒有不操蛋的,除了 json 的解析有點麻煩之外,重點要點名一下 AT 命令,真是操蛋中的操蛋
(手上項目 AT 命令的解析也是我自己寫的,精簡的版本,同樣沒有使用 malloc,用鏈表代之,後續整理一下,可能也會分享出來) 目测没有校验的,存在干扰的场合就可能有问题
比如串口通信,还有电机的情况。 本帖最后由 dukelec 于 2021-10-20 14:32 编辑
lindabell 发表于 2021-10-20 14:15
目测没有校验的,存在干扰的场合就可能有问题
比如串口通信,还有电机的情况。 ...
要有分層的概念,msgpack 只是定義 payload 數據,你說的是更底層的封包
我在 14 樓說了,本地串口通訊沒必要用 msgpack
就算用,也可以配合底層的封包協議,譬如 cdbus / cdnet
我一樓說的適合 MCU 是特指替代 json 的場合,和遠程服務器通訊 markmsgpack 正在折腾初步的http东东 dukelec 发表于 2021-10-20 00:37
json 不方便傳輸二進制數據,擴展性不好,譬如傳輸波形數據、圖片、固件等,在 json 內使用 base64 的話 ...
其实了解下AT的历史,就知道AT是非常有意义的, 可打印, 直观的方案。
当有好AT解析工具(20年年前就有), 这都不算事
当然直观的还有json
对于串口(本身速率限制)来说,都差不多,效率提高不了多少。
网络就不一样了,应用场合(图像,视频)决定了一定要干二进制。
但为了直观还是会有http
总结:
1、直观非常有意义
2、一切以场景需求为准
正如,iphone出红色款就能再卖一次,因为我们真的在意颜色 BongBong 发表于 2021-11-10 12:47
其实了解下AT的历史,就知道AT是非常有意义的, 可打印, 直观的方案。
当有好AT解析工具(20年年前就有), ...
沒有意義,想要直觀,在工具上做文章就好,調試也是一樣,譬如收到二進制的 msgpack 數據,你完全可以按照 json 的字符串形態打印出來
特別是 AT,為了方便人肉敲命令,才設計成現在的模樣,問題是,現在誰沒事人肉敲 AT 命令?就算有人肉控制的需求,把這個需求做到調試軟件裡面不香嗎?為了這個偽需求,要頻繁的在字符串模式和二進制模式之間切換,切的不好還要強制復位模式,還有不同功能之間也經常相互干擾,簡直不可理喻,現在很多人用 opencpu 的移動通訊模組,很大的一個原因就是不想碰噁心的 AT。 dukelec 发表于 2021-11-10 13:11
沒有意義,想要直觀,在工具上做文章就好,調試也是一樣,譬如收到二進制的 msgpack 數據,你完全可以按 ...
一般人即使不太懂电脑,敲个AT5分钟教会
msgpack估计要5天或者半年 楼主,工程里直接放 c文件和h文集,编译报错 /msgpackel.c:26:9: warning: implicit declaration of function ‘put_unaligned16’ 这个函数是不是在其他c文件里 我是一个大白菜 发表于 2021-11-24 12:03
楼主,工程里直接放 c文件和h文集,编译报错 /msgpackel.c:26:9: warning: implicit declaration of functi ...
是的,readme 有說其它 h 的連接,要手動下載一下 楼主来个jsonMessagePack ProtocolBuf 的对比评测呗。 dukelec 发表于 2021-11-24 13:15
是的,readme 有說其它 h 的連接,要手動下載一下
好的,我下载了,发一份上来。 mmiker 发表于 2021-11-24 14:23
楼主来个jsonMessagePack ProtocolBuf 的对比评测呗。
json 是文本格式,mcu 解析麻烦,不能传输二进制数据
json 做为配置文件,人工编辑比较方便,但 json 不支持注释等特性,做配置文件可用其变种,譬如 json5
做为通讯,用文本较为方便 debug
ProtocolBuf 类似 c 语言的 struct 结构,数据本身的含义要查看协议文档才能知晓,譬如文档规定前 4 字节是一个 float 型数据,表示电压值
ProtocolBuf 的版本改动,通讯的双方都要判断版本,不同版本的协议要调用不同的「协议文档」来解析,很麻烦,但效率最高
MessagePack 是高度类似 json 的自解析格式,譬如 {"voltage": 3.23},没有协议文档,也能猜到含义
和 json 不同的是,MessagePack 是二进制,方便 mcu 解析,解析效率也非常高,但低于 ProtocolBuf
自解析格式由于包含了 "voltage" 这样的信息,数据量也比 ProtocolBuf 大很多
协议版本变化不大不影响通讯,譬如增加一些字段,接收方直接忽略即可
MessagePack 除了用来通讯,也可以用来保存数据,譬如做为某个软件导出的工程文件(譬如 CDEncrypt 开源加密小工具导出的文件(加密前))
做为通讯,debug 稍微麻烦一点,但也是可以解决的,譬如转成类似 json 的文本再打印
做为保存数据的文件,人工手动编辑不现实,但一般也没这个必要,因为哪个软件导出,往往也是用同一个软件打开编辑;也可以用第三方的 MessagePack 文件编辑工具
如果让我打分,0~5 分,数值越大表示越优秀:
数据大小方面:Json 0; ProtocolBuf 5; MessagePack 3
解析效率方面:Json 0; ProtocolBuf 5; MessagePack 4
方便程度方面:Json 5; ProtocolBuf 0; MessagePack 4
通用性方面 :Json 1; ProtocolBuf 3; MessagePack 5 (Json 不支持二进制,所以很多场合不能用;ProtocolBuf 需要提前告知协议格式,做为文件分享也不合适)
BongBong 发表于 2021-11-10 22:37
一般人即使不太懂电脑,敲个AT5分钟教会
msgpack估计要5天或者半年
AT或者结构体这些是面向mcu的方案,面向现代互联网得用json protobuf messagepack。 monkeynav 发表于 2021-11-24 19:09
AT或者结构体这些是面向mcu的方案,面向现代互联网得用json protobuf messagepack。 ...
也不存在所谓现代这个问题, 适合就好. 过来膜拜大佬!!!!
论坛里面除了。古大师 ,就属你在数字电路 程序设计里面最牛逼。 BongBong 发表于 2021-11-25 22:00
也不存在所谓现代这个问题, 适合就好.
开发效率天壤之别 monkeynav 发表于 2021-11-26 12:42
开发效率天壤之别
没有觉得开发效率高在哪里,世界比较大,适合就好,你现在觉得现代,再过几年就是落后。
页:
[1]