Using protobuf/nanopb for communication between mbed and computer

08 Apr 2014

Hi,

I'm trying to create a simple and effective way of communicating between my mbed and my computer. To accomplish this, I'm using a lightweight version of googles protobuf library (which if you haven't heard of before is a library that in short words can serialize and deserialize structs). This lightweight version is called nanopb, basically protobuf made for microcontrollers with limited capacity in both speed and capacity.

I seem to have some problems which I think I can blame on me not fully understanding how streams work, or even what streams are.

Here is what I've made so far: http://mbed.org/users/Tomas/code/nanopb_test/

The mbed program starts and it runs the loop successfully one time, and I can see and decode the data in my c# application on my computer. The problem occurs from the second time the loop runs. The data it sends out does not change, even if my code has specified all the variables to increase each time the loop is run. Also, after running the loop a few times, the program stops with the error message "Stream full", which is defined in pb_encode.c line 92.

Do any of you nice people here have any clue what I am doing wrong, or what I'm missing? I'm guessing I need to delete what I've written in my buffer or stream or something in that direction. You can also see I've tested different solutions in my code (they are commented out now, as they did not work). I'm kind of clueless where to go next!

Thanks for reading!

09 Apr 2014

So I've rewritten the program to show the problem(s) by only using a mbed and a serial terminal. It stops after running the while loop a few times. Anyone know how to "empty" the stream after each loop? I have a feeling that could be my problem.

Program is located on this link: http://mbed.org/users/Tomas/code/nanopb_test/

I've also pasted the main.cpp file below.

main.cpp

#include "mbed.h"
#include "pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
#include "threeaxis.pb.h"
#include "MODSERIAL.h"

MODSERIAL pc(USBTX, USBRX, "modser"); //modified modser lib to be able to input custom stream

int main() {
    pc.baud(115200);
    gyro_message GyroOut, GyroIn;
    
    pc.claim();
    uint8_t bufferout[150];
    uint8_t bufferin[150];
    
    pb_ostream_t streamout = pb_ostream_from_buffer(bufferout, sizeof(bufferout));

    GyroOut.X=1.1;
    GyroOut.Y=2.1;
    GyroOut.Z=3.1;
    pc.printf("starting..\r\n");
    while(1){
        GyroOut.X+=0.1;
        GyroOut.Y+=0.2;
        GyroOut.Z+=0.3;
        
        pc.printf("Raw values: x: %4.2f, y: %4.2f, z: %4.2f\r\n", GyroOut.X, GyroOut.Y, GyroOut.Z); //print values before encoding
        
        if (pb_encode(&streamout, gyro_message_fields, &GyroOut)) { //encode message
            pc.printf("%s\r\n", bufferout); //print encoded message
        }
        
        else { //print error message if encoding fails
            pc.printf("Encoding failed: %s\n", PB_GET_ERROR(&streamout));
            return 0;
        }
        
        pc.printf("decoding...\r\n");
        pb_istream_t streamin = pb_istream_from_buffer(bufferin, sizeof(bufferin)); //create input stream
        for(int i=0;i<=streamout.bytes_written;i++) //copy output buffer to input buffer
            bufferin[i]=bufferout[i];
        if (pb_decode(&streamin, gyro_message_fields, &GyroIn)) { //decode message
            pc.printf("Decoded values: x: %4.2f, y: %4.2f, z: %4.2f\r\n", GyroIn.X, GyroIn.Y, GyroIn.Z); //print decoded values
        }
        
        else { //print error message if decoding fails
            pc.printf("Decoding failed: %s\n", PB_GET_ERROR(&streamin));
            return 0;
        }
        wait(1);
    }
}
07 Jan 2015

Tomas, did you ever end up figuring this out?

07 Jan 2015

Note: this is based purely on a quick look at the code rather than actually trying anything.

I can't see anywhere that the output buffer is cleared, each transmit (more specifically each call to pb_encode() is simply adding data on to the end of the buffer. The printf then only transmits the first string in the buffer.

This would give the reported effect, you'd keep getting the same data output until the buffer overflowed and things died.

It looks like resetting the buffer each time by moving the line streamout = pb_ostream_from_buffer(bufferout, sizeof(bufferout)); inside the while loop should fix the issue.

07 Jan 2015

I'll try this out and report back my findings, thanks Andy.

08 Jan 2015

Hi Mike,

as far as I can see I haven't done any changes on the nanopb code since 9th of April. I didn't end up using nanopb in my project (a multicopter-platform I'm developing) as I found a good alternative (MAVLink) which made the software development on the receiving end unnecessary.

It could be interesting to see if Andy's solution helps, so keep us updated if you don't mind!

08 Jan 2015

All,

I've been talking to Petteri, the developer behind protobufs, about my issues. The discussion is here https://groups.google.com/forum/#!topic/nanopb/JBXiR8csOio

I'll update this post if I can get something working. I'm still just printing out the same thing over and over. I'm using a Nucleo, so MODSerial won't work, and I haven't tried using buffered serial yet. I'm not sure if this will solve my problem or not.

09 Jan 2015

Hey guys, got something working finally. I had a problem with my Nucleo board receiving HEX over serial that took forever to figure out. Here's how I sent data:

/* Encode our message */
    {
        /* Allocate space on the stack to store the message data.
         *
         * Nanopb generates simple struct definitions for all the messages.
         * - check out the contents of simple.pb.h! */
        SensorData message;
        
        /* Create a stream that will write to our buffer. */
        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
        
        /* Fill in the tilt angle from p */
        message.tilt_angle = p;
        //pc.printf("message contains %f\n", message.tilt_angle);
        
        /* Now we are ready to encode the message! */
        status = pb_encode(&stream, SensorData_fields, &message);
        message_length = stream.bytes_written;

        
            for (int i = 0; i < sizeof(buffer); i++) {
            pc.putc(buffer[i]);

            }

        
        /* Then just check for any errors.. */
        if (!status)
        {
            pc.printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
            return 1;
        }

    }

And here's what I did for receiving:

while(1) {     
        if(device.readable()) {
 
            c = device.getc();
            buffer[i] = c;
            i++;
            i = i % 128;
                        
            // Create a stream that reads from the buffer. 
                
            if (i == 127){
                pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
        
                //Now we are ready to decode the message. 
                status = pb_decode(&stream, SensorData_fields, &message);
        
                // Check for errors... 
                if (!status)
                {
                    pc.printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
                    return 1;
                }
        
                // Print the data contained in the message. 
                pc.printf("Your lucky number was %f!\n", message.tilt_angle);
            }
        }     
    }