Nanopb编码仅含子消息字段的消息时输出为空,求排查
Nanopb编码子消息失效问题排查
问题背景
用户定义了如下settings.proto文件:
syntax = "proto3"; message SerialPort { int32 baudrate = 3; } message Settings { SerialPort serialInterface = 1; int32 test = 2; }
使用Arduino结合Nanopb编写代码,尝试仅设置serialInterface.baudrate字段并编码消息,代码如下:
#include "Arduino.h" #include <stdio.h> #include <stdlib.h> #include "settings.pb.h" #include <pb_encode.h> #include <pb_decode.h> #include <pb_common.h> #include <pb.h> void setup() { // Create a Settings message with a non-zero baudrate Settings settings = Settings_init_zero; settings.serialInterface.baudrate = 9600; // Allocate a buffer to hold the encoded message uint8_t buffer[Settings_size] = {0}; // Encode the message into the buffer pb_ostream_t stream = pb_ostream_from_buffer(buffer, Settings_size); pb_encode(&stream, Settings_fields, &settings); printf("written %d\r\n", stream.bytes_written); // Print the contents of the buffer (for debugging purposes) for (int i = 0; i < Settings_size; i++) { printf("%X ", buffer[i]); } printf("\n"); delay(100000); } void loop(){}
问题现象
- 仅设置
baudrate时,编码输出为空:
written 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 添加
settings.test = 1000;后,编码输出仅包含test字段内容:
written 3 10 E8 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
而使用Python官方Protobuf库测试时,同时设置test和serialInterface.baudrate能正常编码出包含两个字段的结果:
import settings_pb2 import google.protobuf.message from google.protobuf.json_format import MessageToJson import binascii s = settings_pb2.Settings() s.test = 1000 s.serialInterface.baudrate = 9600 print(MessageToJson(s)) try: s_pb_encoded = s.SerializeToString() except google.protobuf.message.EncodeError as e: print(e) exit(1) print(f"encoded {(''.join(f'0x{c:02x} ' for c in s_pb_encoded))}")
输出:
{ "test": 1000, "serialInterface": { "baudrate": 9600 } } encoded 0x10 0xe8 0x07 0x52 0x03 0x18 0x80 0x4b
原因分析
Nanopb对proto3中子消息的处理逻辑和官方Protobuf库存在差异:
- 官方库会自动识别子消息内的字段是否被设置,只要子消息中有非默认值字段,就会编码整个子消息。
- Nanopb为了节省资源,要求显式标记子消息是否被主动设置。生成的
Settings结构体中会包含一个has_serialInterface布尔字段,只有当该字段设为true时,编码器才会处理serialInterface子消息的内容。
用户代码仅设置了serialInterface.baudrate,但未将has_serialInterface设为true,Nanopb因此判定serialInterface字段未被初始化,不会将其编码到输出中。
解决方案
在设置serialInterface.baudrate后,添加代码标记子消息为已设置:
settings.has_serialInterface = true;
修改后的setup函数片段:
void setup() { Settings settings = Settings_init_zero; settings.serialInterface.baudrate = 9600; // 显式标记子消息已设置 settings.has_serialInterface = true; uint8_t buffer[Settings_size] = {0}; pb_ostream_t stream = pb_ostream_from_buffer(buffer, Settings_size); pb_encode(&stream, Settings_fields, &settings); // ... 后续代码不变 }
修改后,Nanopb即可正常编码serialInterface子消息的内容。
内容的提问来源于stack exchange,提问作者Katu
相关产品推荐
相关产品推荐

