-2

Recently started exploring nanopb - apology if my questions sound sily. I faced some issues while assigning and retrieval of strings and integers when I modified simple example of nanopb. Let me give my steps before my questions -

  1. I defined simple.proto file
message Device{
optional string devid =1; 
optional string mac = 2; 
optional string cpu=3 [default = "x86"] ; 
optional bool isSecured=4;
optional int32 uptime = 5 [default = 1234];
}

  1. Also defined simple.option
Device.devid max_size:64
Device.cpu max_size:64

Then I compiled as usual : protoc -osimple.pb simple.proto

  1. Here is my code :

3a) Using same string encode decode utility function as in How to encode a string when it is a pb_callback_t type

//string encode decode to pb
bool encode_string(pb_ostream_t* stream, const pb_field_t* field, void* const* arg)
{
    const char* str = (const char*)(*arg);

    if (!pb_encode_tag_for_field(stream, field))
        return false;

    return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}

bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
    uint8_t buffer[1024] = {0};

    /* We could read block-by-block to avoid the large buffer... */
    if (stream->bytes_left > sizeof(buffer) - 1)
        return false;

    if (!pb_read(stream, buffer, stream->bytes_left))
        return false;

    /* Print the string, in format comparable with protoc --decode.
     * Format comes from the arg defined in main().     */
    printf((char*)*arg, buffer);
    return true;
}

3b) here is my main code snippet based on simple.c of example -

    /* This is the buffer where we will store our message. */
    uint8_t buffer[128];
    size_t message_length;
    bool status;

    /* Encode our message */
    {
        /* Allocate space on the stack to store the message data, check out the contents of simple.pb.h
         * good to always initialize your structures so that no garbage data from RAM in there.   */

        //Device message = Device_init_zero;   //init zero   for empty
        Device message = Device_init_default;  //init default for default

        /* Create a stream that will write to our buffer. */
        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));

        /* Fill in the data */
        message.devid.arg = "device1";
        message.devid.funcs.encode = &encode_string;


        //strcpy(message.devid,"device1");  // no easier way like this ?

        message.isSecured = true;   // should be 1 if printed with %d
        message.uptime=9876;        // should change, why it is not working ?

        /* Now we are ready to encode the message! */
        // encode stream to buffer , also get the buffer length
        status = pb_encode(&stream, Device_fields, &message);
        message_length = stream.bytes_written;

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

    /* Now we could transmit the message over network, store it in a file etc.   */
    /* just decode it immediately. */
    {
         /* Allocate space for the decoded message. */
        Device message = Device_init_zero;

        /* Create a stream that reads from the buffer. */
        pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);

        message.devid.funcs.decode = &print_string;
        message.devid.arg = "before decode Device - devid: %s \n"; //works here

        message.cpu.funcs.decode = &print_string;
        message.cpu.arg = "before decode Device -cpu: %s \n";   //where in op?

        printf("before decode isSecured %d\n",message.isSecured);  // doesn't work
        
        printf("before decode uptime %d\n",message.uptime); //doesn't work

        /* Now ready to decode the message. */
        // decode  stream buffer into message
        status = pb_decode(&stream, Device_fields, &message);

        /* Check for errors... */
        if (!status)
        {
            printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
            return 1;
        }

        /* Print the data contained in the message. */
        
        message.devid.funcs.decode = &print_string;
        message.devid.arg = "after decode Devic - devid: %s \n";  // doesn't work here

        printf(" after decode isSecured %d\n",message.isSecured);  // prints default 0        

    printf(" after decode uptime %d\n",(int)message.uptime);   //prints default assigned in proto
    }
  1. The output after build and run :
$ ./simple
before decode isSecured 0
before decode uptime 0
before decode Device - devid: device1
 after decode isSecured 0
 after decode uptime 1234

My queries ( also added my inline comments in code) :

  1. In original simple.c message.lucky_number=13 assignment works but here message.uptime assignment is not working , it is taking default value. Similarly assigning boolean value to message.isSecured is not working. Please tell where is my fault.
  2. I used Device_init_default before pb_encode as some have default values and Device_init_zero before pb_decode call as it will populate after decode. Is my approach correct ?
  3. Is there any simpler way to assign string value using strcpy and printing it in C way by printf("%s",strvar) apart from the encode_string and decode_string util ?
  4. The string is printed only before pb_decode call but uptime default value is printed after pb_decode call. Also boolean value assignment is not working. Why ? What is my mistake ?
  5. I saw encode string and int functions in https://github.com/nanopb/nanopb/blob/master/tests/callbacks/encode_callbacks.c How to encode and decode float and boolean ?

Thanks in anticipation

Dhiman
  • 59
  • 6

1 Answers1

1
  1. In original simple.c message.lucky_number=13 assignment works but here message.uptime assignment is not working , it is taking default value. Similarly assigning boolean value to message.isSecured is not working. Please tell where is my fault.

If you look into the generated .pb.h file, you'll find that every optional field has a boolean has_field. You'll have to set that to true also, to signify that the field is present.

  1. I used Device_init_default before pb_encode as some have default values and Device_init_zero before pb_decode call as it will populate after decode. Is my approach correct ?

That's fine.

  1. Is there any simpler way to assign string value using strcpy and printing it in C way by printf("%s",strvar) apart from the encode_string and decode_string util ?

Because you have already set a max_size for your string fields, they should be generated as a char array instead of callbacks. You can try passing -v switch like: ../generator/nanopb_generator.py -v simple.pb to see more verbose messages that can point out why the option is not applying. Perhaps the file name is incorrect or the message name is incorrect.

The string is printed only before pb_decode call but uptime default value is printed after pb_decode call. Also boolean value assignment is not working. Why ? What is my mistake ?

I saw encode string and int functions in https://github.com/nanopb/nanopb/blob/master/tests/callbacks/encode_callbacks.c How to encode and decode float and boolean ?

Well, usually you wouldn't have to resort to callbacks. But if you decide you need them, you can encode booleans with pb_encode_varint() and floats with pb_encode_fixed32(). For studying callbacks, the protobuf encoding documentation and this test case can be helpful.


You may find the network_server example useful to study.

Also, the Stack Overflow format works best when you have only a single question per post. That way the questions and answers remain focused and easy to follow.

jpa
  • 10,351
  • 1
  • 28
  • 45
  • Thanks.Setting has_field to true makes assignment work. Also recompiling proto made char array instead of pb_callback struct so could use strcpy. – Dhiman Aug 28 '20 at 12:50