You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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(){}

问题现象

  1. 仅设置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 
  1. 添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:52:43