Protocol Buffers(简称 Protobuf)是 Google 研发的结构化数据序列化协议,具备跨平台、语言无关的特性,可高效应用于网络通信、数据存储、配置管理等领域,核心优势体现为数据体积小、解析速度快、扩展性良好。

LuatOS 针对 Protobuf 编解码底层逻辑做了轻量级封装,为开发者提供简洁的 API 接口。通过该封装,开发者能够快速实现结构化数据的序列化与反序列化,有效降低协议对接的开发难度,同时提升通信效率与代码的可维护性。

当前,Protobuf 库已完成对 LuatOS 全系列开发产品的支持,开发者可结合项目实际需求,自主选择适配并完成快速集成与开发。

一、Protobuf核心功能速览

LuatOS的Protobuf核心库支持加载由protoc编译生成的二进制描述文件(.pb),提供完整的数据编码(encode)、数据解码(decode)等功能,支持Proto2和Proto3协议版本,能够根据输入的protobuf定义自动识别并处理相应版本的消息格式,适用于设备与云端、模块间高效数据交换等典型应用场景。

最新API文档详见:https://docs.openluat.com/osapi/core/protobuf/

在LuatOS开发中,使用以下四个函数高效处理Protobuf数据。

1.1 protobuf.load(pbdata)

  • 函数功能:加载Protocol Buffers二进制定义数据到系统中,使其可以用于后续的编码和解码操作。
  • 注意事项:同一个文件只需要加载一次,除非调用过 protobuf.clear() 清除已加载的定义;加载的数据必须是通过protoc.exe程序转换得到的二进制数据。
  • 简要示例:
1--加载 pb文件,这个是从 pbtxt 转换得到的;1
2--下载资源到模块时不需要下载 pbtxt;
3-- 转换命令:protoc.exe -operson.pb person.pbtxt;
4--protoc.exe 下载地址: https://github.com/protocolbuffers/protobuf/releases;
local pb_file = "/luadb/person.pb"
local success, bytesRead = 
if io.exists(pb_file) then
        local success, bytesRead = protobuf.load(io.readFile(pb_file))
        if not success then
            log.info("protobuf", "加载 protobuf 定义失败,已读取 " .. bytesRead .. " 字节")
            return
        else
            log.info("protobuf", "加载 protobuf 定义成功,共解析 " .. bytesRead .. " 字节")
        end
    else
        log.info("protobuf", "pb 文件不存在")
        return
    end

1.2 protobuf.clear()

  • 函数功能:清除已加载的Protocol Buffers二进制定义数据。
  • 注意事项:清除所有定义数据后,可以通过调用protobuf.load()重新加载新的定义数据;清除后,任何依赖已删除 类 型的序列化/反序列化操作将失败;该函数总是成功执行,没有返回错误的情况。
  • 简要示例:
-- 清除所有已加载的定义数据;
    protobuf.clear()
    log.info("protobuf", "所有 protobuf 定义已清除")

1.3 protobuf.encode(tpname, data)

  • 函数功能:将符合Protocol Buffer消息定义的Lua表(table)数据,按照指定的消息类型进行序列化,生成二进制编码后的字符串。
  • 注意事项:编码前必须先使用protobuf.load()加载对应的 Protocol Buffer 定义数据;待编码的table内容必须符合pb文件里的定义;编码结果为二进制字符串,可能包含不可打印字符,调试时建议使用十六进制(如 :toHex())或Base64查看;如果编码失败,函数会返回nil。
  • 简要示例:
--准备待编编码数据
 local tbdata = {
        name = "wendal",
        id = 123,
        email = "abc@qq.com"
    }
    -- 编码数据;
    local pbdata = protobuf.encode("Person", tbdata)
    if pbdata then
        -- 编码成功,编码后的数据通常包含不可见字符;
        -- 打印长度和十六进制内容(便于调试);
        log.info("protobuf", "编码成功,数据长度:" .. #pbdata)
        log.info("protobuf", "十六进制内容:" .. pbdata:toHex())
    else
        log.info("protobuf", "编码失败:数据格式或类型不匹配")
    end

1.4 protobuf.decode(tpname, data)

  • 函数功能:将Protocol Buffer二进制编码的数据(字符串)按照指定的消息类型进行 反序列化 ,还原为Lua表(table)结构。
  • 注意事项:在调用此接口前,必须先通过protobuf.load()加载对应的Protocol Buffers定义数据;输入数据 data 必须是有效的protobuf二进制编码字符串,且与tpname对应的消息格式兼容;Lua table内容无法直接打印出来,建议搭配json.encode()查看。
  • 简要示例:
--假设已通过
--例如通过 protobuf.encode() 生成的返回值变量为 pbdata;
-- 数据解码
 local tbdata = protobuf.decode("Person", pbdata)
 if tbdata then解码后的数据为 Lua table 格式,需要转化为 json 进行显示;log.info("protobuf""解码成功,数据内容:"json.encode(tbdata))
--解码后的数据为 Lua table 格式,需要转化为 json 进行显示;
log.info("protobuf""解码成功,数据内容:"json.encode(tbdata))
else
log.info("protobuf""解码失败")
end

二、开源示例快速上手

理论结合实践,才能更快掌握。完整Protobuf示例已 开源 在LuatOS官方仓库,你可以直接参考并使用。

以低功耗模组Air780EHV(音频全能系列)为例,可选用配套核心板或开发板快速实操验证。

  1. 最新示例源码:https://gitee.com/openLuat/LuatOS/tree/master/module/Air780EHM_Air780EHV_Air780EGH/demo/protobuf

  2. 实操教程详见:*https://docs.openluat.com/air780ehv/luatos/app/common/protobuf/

请添加图片描述

  1. Protobuf示例功能要点:
  • 加载protobuf定义文件;

  • 将符合protobuf定义的Lua table数据编码为二进制数据;

  • 使用json编码同样的Lua table数据后,对比protobuf和json编码后数据的大小;

  • 将二进制数据解码为Lua table数据;

  • 清除所有已加载的定义数据。

  1. 核心代码如下图示,完整demo详见源码仓库最新文件。
本功能模块演示的内容为:
1.加载 protobuf 定义文件;
2.将符合 protobuf 定义的 Lua table 数据编码为二进制数据;
3.使用 json 编码同样的 Lua table 数据后,对比 protobuf 和 json 编码后数据的大小;
4.将二进制数据解码为 Lua table 数据;
5.清除所有已加载的定义数据;


本文件没有对外接口,直接在 main.lua 中 require "protobuf_app" 就可以加载运行;
]]


local function main_task()
    -- 加载 pb 文件, 这个是 proto 经过 protoc.exe 编译后生成的二进制文件
    -- 下载资源到模块时不需要下载 proto 文件
    -- 转换命令: protoc -o person.pb person.proto
    -- protoc.exe 下载地址: https://github.com/protocolbuffers/protobuf/releases
    local pb_file = "/luadb/person.pb"

    local tbdata = {
        name = "wendal",
        id = 123,
        email = "abc@qq.com"
    }

    if io.exists(pb_file) then
        local success, bytesRead = protobuf.load(io.readFile(pb_file))
        if not success then
            log.info("protobuf", "加载 protobuf 定义失败,已读取 " .. bytesRead .. " 字节")
            return
        else
            log.info("protobuf", "加载 protobuf 定义成功,共解析 " .. bytesRead .. " 字节")
        end
    else
        log.info("protobuf", "pb 文件不存在")
        return
    end

    -- 编码数据;
    local pbdata = protobuf.encode("Person", tbdata)
    if pbdata then
        -- 编码成功,编码后的数据通常包含不可见字符;
        -- 打印长度和十六进制内容(便于调试);
        log.info("protobuf", "编码成功,数据长度:" .. #pbdata)
        log.info("protobuf", "十六进制内容:" .. pbdata:toHex())
    else
        log.info("protobuf", "编码失败:数据格式或类型不匹配")
    end

    -- 对比 protobuf 编码和 json 编码的大小;
    local jdata = json.encode(tbdata)
    if jdata then
        log.info("json", "编码成功,数据长度:" .. #jdata)
        log.info("json", "数据内容:" .. jdata)
    else
        log.info("json", "编码失败:数据格式或类型不匹配")
    end
    -- 可见 protobuffs 比 json 节省很多空间;

    -- 数据解码;
    local tbdata = protobuf.decode("Person", pbdata)
    if tbdata then
        -- 解码后的数据为 Lua table 格式,需要转化为 json 进行显示;
        log.info("protobuf", "解码成功,数据内容:", json.encode(tbdata))
    else
        log.info("protobuf", "解码失败")
    end

    -- 清除所有已加载的定义数据;
    protobuf.clear()
    log.info("protobuf", "所有 protobuf 定义已清除")
end

-- 创建并启动一个 task
-- 用于运行 main_task 函数
sys.taskInit(main_task)
  1. 最后我们来看一下:
    Protobuf vs JSON:实测对比
    在示例中,同一条传感器数据:
  • JSON编码:约120字节,含大量重复键名
  • Protobuf编码:约30字节,字段以数字标识

流量节省75%,解析速度提升数倍——对于按量计费的蜂窝网络和低功耗设备,这意味着更低的运营成本和更长的电池续航。

Protobuf 是 Google 推出的跨语言、跨平台序列化协议,适用于网络通信、数据存储等场景,具备体积小、解析快、扩展性强的特点。LuatOS 提供轻量封装的 Protobuf 核心库,支持加载.pb 文件、完成 encode/decode 等操作,兼容 Proto2 与 Proto3。库包含 load、clear、encode、decode 四个核心接口,可实现 Lua 表与二进制数据的相互转换,满足设备与云端、模块间高效数据交互需求,相关示例与文档已开源,可直接用于项目开发与验证。

Air780E系列、Air8000工业平台,都支持在LuatOS Protobuf库直接集成。

今天的内容就分享到这里了。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐