This is a little late to the comment thread, I came across this thread when researching nanopb on the Arduino. For anyone else going forward looking to solve similar problems, here ya go.
Nanopb is a great way of taking a bunch of data and wrapping it all together into a structure that you can send out over whichever protocol you desire and then unpack it in a variety of different programming languages/environments.
My current project takes a bunch of different data streams and bundles it all together and sends it out over Ethernet using MQTT. It also receives commands over the same pipe. Another client on the MQTT host analyzes the data and directs the arduino to do what it's supposed to do.
Here are a couple of things that I found helpful:
https://blog.noser.com/arduino-iot-with-mqtt-and-nanopb/
http://zakovinko.com/57-how-to-speak-from-arduino-to-python-and-back.html
As for getting it to encode and decode you are going to have to use a structure. The order is important of when you do things (e.g. submessages have to be handled first when decoding and last when encoding). The example here shows how to encode/decode for various data structures.
https://github.com/nanopb/nanopb/tree/master/tests/callbacks
for the example below, here are the pertinent globals and declarations:
// Header Files
#include "PubSubClient.h" // MQTT library
#include "pb_decode.h" // nanopb decode library
#include "pb_encode.h" // nanopb encode library
#include "RCB_Protocol_Buffer.pb.h" // nanopb generated protobuf library
// Create various objects
RCB_Protocol_Buffer_ClientMessage_MinerStatus minerStatus[2 * shelfNumber];
and from void setup():
// initialize protobuf structures
for (uint8_t i = 0; i < 2 * shelfNumber; i++) {
minerStatus[i] = RCB_Protocol_Buffer_ClientMessage_MinerStatus_init_zero;
minerStatus[i].Error = true; // set up so that the program assumes nothing installed
minerStatus[i].id = i;
}
Here is an example of encoding a message to be sent:
void mqtt_send_rsr() {
uint8_t sMsg[MSG_SIZE];
_RCB_Protocol_Buffer_ClientMessage msg = RCB_Protocol_Buffer_ClientMessage_init_zero;
pb_ostream_t buffer = pb_ostream_from_buffer(sMsg, MSG_SIZE);
Conv32 bigmac;
bigmac.bytes[0] = mac[5];
bigmac.bytes[1] = mac[4];
bigmac.bytes[2] = mac[3];
bigmac.bytes[3] = mac[2];
msg.mac = bigmac.value;
msg.BoardTemp = temp.readCelsius();
msg.InletTemp = temp.readInternal();
msg.Flow = digitalRead(flowSwitchPin);
msg.PowerAvail = digitalRead(powerAvailPin);
msg.IPRFlag = ipFlag;
msg.time = getTimestamp();
msg.miners.funcs.encode = &mqtt_encode_callback;
//Encode the message
if (!pb_encode(&buffer, RCB_Protocol_Buffer_ClientMessage_fields, &msg)) {
Serial.println(F("Encoding failed"));
Serial.println(PB_GET_ERROR(&buffer));
}
/*
// check message content
Serial.println(buffer.bytes_written);
for (uint8_t i = 0; i < buffer.bytes_written; i++) {
Serial.print(sMsg[i],HEX);
Serial.print(F(":"));
}
Serial.println();
*/
ethClient.disableInterrupt();
// publish the buffer
if(!client.publish("rcb", sMsg)) {
Serial.println(F("Publishing Failed"));
tReconnect.restart();
tGenerateRSR.enableDelayed(5000);
}
else {
ipFlag = false;
}
ethClient.enableInterrupt(); // restore interrupt
}
/*
* Callback function to read in the individual miner status
*/
bool mqtt_encode_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
for (uint8_t i = 0; i < 2 * shelfNumber; i++)
if (!pb_encode_submessage(stream, RCB_Protocol_Buffer_ClientMessage_MinerStatus_fields, &minerStatus[i]))
return false;
return true;
}
Here is an example of decoding a message:
int mqtt_callback(char* topic, byte* payload, unsigned int length){
mqtt_ping_reset(); // reset the ping timer
if (length > MQTT_MAX_PACKET_SIZE) {
payload = {'\0'}; // if garbage, flush the buffer
return 0;
}
RCB_Protocol_Buffer_BrokerMessage msg = RCB_Protocol_Buffer_BrokerMessage_init_default;
pb_istream_t stream = pb_istream_from_buffer(payload, length);
msg.miners.funcs.decode = &mqtt_decode_callback;
//msg.miners.funcs.arg = *minerTemp;
if (!pb_decode(&stream, RCB_Protocol_Buffer_BrokerMessage_fields, &msg)) // This has to follow the submessages
return 1;
Conv32 bigmac;
// enendianess is set properly here don't mess it up
bigmac.bytes[0] = mac[5];
bigmac.bytes[1] = mac[4];
bigmac.bytes[2] = mac[3];
bigmac.bytes[3] = mac[2];
if (msg.mac != bigmac.value)
return 0;
parseCommand(msg); // execute the control function
return 0;
}
bool mqtt_decode_callback(pb_istream_t *stream, const pb_field_t *field, void * const **arg) {
RCB_Protocol_Buffer_BrokerMessage_MinerCmdMessage minermsg = RCB_Protocol_Buffer_BrokerMessage_MinerCmdMessage_init_default;
if (!pb_decode(stream, RCB_Protocol_Buffer_BrokerMessage_MinerCmdMessage_fields, &minermsg))
return false;
bitWrite(minerIPReport, minermsg.id, minermsg.IPReport);
bitWrite(minerReset, minermsg.id, minermsg.Reset);
bitWrite(minerPower, minermsg.id, minermsg.Power);
return true;
}