Example of Serializing and Deserializing Data

This topic walks you through sample code that serializes (encodes) and deserializes (decodes) directives and events for each Alexa Gadgets Toolkit interface. Here we use a C language implementation, but you can extend the concepts to other languages such as Python or Java.

Step 1: Get the resources

The first step is to get the following resources:

  1. Download the nanopb .proto compiler – To use protocol buffers with C, we recommend that you use nanopb. Nanopb is a small code-size protocol buffer implementation in ANSI C. It is especially suitable for use in microcontrollers, but fits any memory-restricted system. To install nanopb, do the following:
    1. Download the latest nanopb version at Nanopb Downloads.
    2. Extract the contents of the nanopb download. The extracted nanopb directory will contain an executable to compile .proto files (that is, get *.c and *.h files). It will also contain helper files that contain functions and data structures to encode and decode any protobuf-encoded data.
  2. Download the Alexa Gadgets sample code repository – Get the Alexa Gadgets Sample Code GitHub repository. This repository contains Alexa Gadget .proto files and sample code.

Step 2: Copy the utility files

Copy the following files from your nanopb directory into the /examples directory of the Alexa Gadgets repository that you downloaded in the previous step. These files are the utility files that define the methods and the data structures required to encode and decode the protobuf-encoded messages.

  • pb.h
  • pb_common.c
  • pb_common.h
  • pb_decode.c
  • pb_decode.h
  • pb_encode.c
  • pb_encode.h

Step 3: Compile the .proto files

You now need to compile the Alexa Gadgets .proto files into classes that you can reference from your gadget code, as follows:

  1. Navigate to the /examples directory of the Alexa Gadgets sample code repository that you downloaded.
  2. Open compile_nanos.sh (on Mac or Linux) or compile_nanos.bat (on Windows) and make sure that your nanopb .proto compiler is located at the specified PROTO_COMPILE_PATH.
  3. From the /examples directory, run compile_nanos.sh (on Mac or Linux) or compile_nanos.bat (on Windows). This compiles all of the .proto files in the Alexa Gadgets sample code repository and puts the resulting C and header source files into the /examples directory.

Step 4: Compile the sample

From the /examples directory, compile proto_sample.c by using the following command:

Copied to clipboard.

gcc -I. -DPB_FIELD_16BIT notificationsSetIndicatorDirectivePayload.pb.c notificationsSetIndicatorDirective.pb.c alexaDiscoveryDiscoverResponseEventPayload.pb.c alexaDiscoveryDiscoverResponseEvent.pb.c alexaDiscoveryDiscoverDirective.pb.c alexaDiscoveryDiscoverDirectivePayload.pb.c alexaGadgetStateListenerStateUpdateDirective.pb.c alexaGadgetStateListenerStateUpdateDirectivePayload.pb.c alexaGadgetSpeechDataSpeechmarksDirective.pb.c alexaGadgetSpeechDataSpeechmarksDirectivePayload.pb.c pb_common.c pb_decode.c pb_encode.c directiveHeader.pb.c eventHeader.pb.c directiveParser.pb.c eventParser.pb.c proto_sample.c -o proto_sample

Step 5: Run the sample

Execute ./proto_sample, which encodes and decodes Alexa Gadgets directives and events to and from binary, and prints out the results. Functions for encoding directives and decoding events are for example purposes only. In a real application, your gadget will decode directives and encode events, rather than vice versa.

The following table shows the functions that the sample code contains.

Function Description
decode_directive() Decodes all Alexa Gadgets directives. If this function does not recognize the interface namespace and directive name, it prints an error. You can add your own directive parsing code to this function.
decode_event() Decodes all Alexa Gadgets events. If this function does not recognize the interface namespace and event name, it prints an error. You can add your own event parsing code to this function.
encode_set_indicator_directive()

Puts the following Notifications.SetIndicator directive into binary form.

 {
   "directive": {
      "header": {
         "namespace": "Notifications",
         "name": "SetIndicator",
         "messageId": "messageID1"
      },
      "payload": {
         "persistVisualIndicator": true,
         "playAudioIndicator": true,
         "asset": {
            "assetId": "assetID1",
            "url": "url1"
         }
      }
   }
}
encode_sample_discover_response_event()

Puts the following Alexa.Discovery.Discover.Response event into binary form.

{
   "event": {
      "header": {
         "namespace": "Alexa.Discovery",
         "name": "Discover.Response",
         "messageId": ""
      },
      "payload": {
         "endpoints": [{
            "endpointId" : " test id",
            "friendlyName": " friendly name",
            "capabilities": [
               {
                  "type": "test type 1",
                  "interface": " Test interface 1",
                  "version":  "1.0"
               },
               {
                  "type": "test type 2",
                  "interface": " Test interface 2",
                  "version":  "1.0"
               },
               {
                  "type": "test type 3",
                  "interface": " Test interface 3",
                  "version":  "1.1"
               }],   
               "additionalIdentification": {
                  "firmwareVersion": "19",
                  "deviceToken": "xxxxxxxxx",
                  "deviceTokenEncryptionType": "yyy",
                  "amazonDeviceType": "aabbccd",
                  "modelName": "mock model name",
                  "radioAddress": "1234567890"
               }
         }]
      }
   }
}
encode_sample_discover_directive()

Puts the following Alexa.Discovery.Discover directive into binary form.

{
   "directive": {
      "header": {
         "namespace": "Alexa.Discovery",
         "name": "Discover",
         "messageId": ""
      },
      "payload": {
         "scope": {
            "type": "",
            "token": ""
         }
      }
   }
}
encode_sample_state_update_directive()

Puts the following StateListener.StateUpdate directive, which notifies the gadget that a timer was set, into binary form.

{
   "directive": {
      "header": {
         "namespace": "Alexa.Gadget.StateListener",
         "name": "StateUpdate",
         "messageId": ""
      },
      "payload": {
         "states": [{
            "name": "timers",
            "value": "active"
         }]
      }
   }
}
encode_sample_speechmarks_directive()

Puts the following SpeechData.Speechmarks directive into binary form.

{
   "directive": {
      "header": {
         "namespace": "Alexa.Gadget.SpeechData",
         "name": "Speechmarks",
         "messageId": ""
      },
      "payload": {
         "playerOffsetInMilliSeconds": 0,
         "speechmarksData": [{
            "type": "viseme",
            "value": "s",
            "startOffsetInMilliSeconds": 130
         }]
      }
   }
}
main() Calls the functions listed previously.

Output

When you run the sample, you will get the following output:

Notifications - SetIndicator Example

Parsing Directive: name = SetIndicator, namespace=Notifications visualIndicator:1, audioIndicator=1, assetId=assetID1, url=url1

Creating setindicator directive: bytes written:57 0x0a 0x37 0x0a 0x1d 0x0a 0x0d 0x4e 0x6f 0x74 0x69 0x66 0x69 0x63 0x61 0x74 0x69 0x6f 0x6e 0x73 0x12 0x0c 0x53 0x65 0x74 0x49 0x6e 0x64 0x69 0x63 0x61 0x74 0x6f 0x72 0x12 0x16 0x08 0x01 0x10 0x01 0x1a 0x10 0x0a 0x08 0x61 0x73 0x73 0x65 0x74 0x49 0x44 0x31 0x12 0x04 0x75 0x72 0x6c 0x31

Alexa.Discovery - Discover.Response Example

Creating discover response event: bytes written:245 0x0a 0xf2 0x01 0x0a 0x24 0x0a 0x0f 0x41 0x6c 0x65 0x78 0x61 0x2e 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72 0x79 0x12 0x11 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72 0x2e 0x52 0x65 0x73 0x70 0x6f 0x6e 0x73 0x65 0x12 0xc9 0x01 0x0a 0xc6 0x01 0x0a 0x07 0x74 0x65 0x73 0x74 0x20 0x69 0x64 0x12 0x0d 0x66 0x72 0x69 0x65 0x6e 0x64 0x6c 0x79 0x20 0x6e 0x61 0x6d 0x65 0x5a 0x24 0x0a 0x0b 0x74 0x65 0x73 0x74 0x20 0x74 0x79 0x70 0x65 0x20 0x31 0x12 0x10 0x54 0x65 0x73 0x74 0x20 0x69 0x6e 0x74 0x65 0x72 0x66 0x61 0x63 0x65 0x20 0x31 0x1a 0x03 0x31 0x2e 0x30 0x5a 0x24 0x0a 0x0b 0x74 0x65 0x73 0x74 0x20 0x74 0x79 0x70 0x65 0x20 0x32 0x12 0x10 0x54 0x65 0x73 0x74 0x20 0x69 0x6e 0x74 0x65 0x72 0x66 0x61 0x63 0x65 0x20 0x32 0x1a 0x03 0x31 0x2e 0x30 0x5a 0x24 0x0a 0x0b 0x74 0x65 0x73 0x74 0x20 0x74 0x79 0x70 0x65 0x20 0x33 0x12 0x10 0x54 0x65 0x73 0x74 0x20 0x69 0x6e 0x74 0x65 0x72 0x66 0x61 0x63 0x65 0x20 0x33 0x1a 0x03 0x31 0x2e 0x31 0x62 0x3a 0x0a 0x02 0x31 0x39 0x12 0x09 0x78 0x78 0x78 0x78 0x78 0x78 0x78 0x78 0x78 0x1a 0x03 0x79 0x79 0x79 0x22 0x07 0x61 0x61 0x62 0x62 0x63 0x63 0x64 0x2a 0x0f 0x6d 0x6f 0x63 0x6b 0x20 0x6d 0x6f 0x64 0x65 0x6c 0x20 0x6e 0x61 0x6d 0x65 0x32 0x0a 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30

Parsing Event: name = Discover.Response, namespace=Alexa.Discovery endpoint count : 1 endpoint id: test id friendly name: friendly name description: manufacturer name: capability type: test type 1 capability interface: Test interface 1 capability version: 1.0 capability type: test type 2 capability interface: Test interface 2 capability version: 1.0 capability type: test type 3 capability interface: Test interface 3 capability version: 1.1 additional id - firmware version: 19 additional id - device token: xxxxxxxxx additional id - device token encryption type: yyy additional id - amazon device type: aabbccd additional id - model name: mock model name additional id - radioAddress: 1234567890

Alexa.Discovery - Discover Example

Creating discover directive: bytes written:31 0x0a 0x1d 0x0a 0x1b 0x0a 0x0f 0x41 0x6c 0x65 0x78 0x61 0x2e 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72 0x79 0x12 0x08 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72

Parsing Directive: name = Discover, namespace=Alexa.Discovery scope type: scope token:

Alexa.Gadget.StateListener - StateUpdate Example

Creating state update directive: bytes written:65 0x0a 0x3f 0x0a 0x29 0x0a 0x1a 0x41 0x6c 0x65 0x78 0x61 0x2e 0x47 0x61 0x64 0x67 0x65 0x74 0x2e 0x53 0x74 0x61 0x74 0x65 0x4c 0x69 0x73 0x74 0x65 0x6e 0x65 0x72 0x12 0x0b 0x53 0x74 0x61 0x74 0x65 0x55 0x70 0x64 0x61 0x74 0x65 0x12 0x12 0x0a 0x10 0x0a 0x06 0x74 0x69 0x6d 0x65 0x72 0x73 0x12 0x06 0x61 0x63 0x74 0x69 0x76 0x65

Parsing Directive: name = StateUpdate, namespace=Alexa.Gadget.StateListener state name: timers state value: active

Alexa.Gadget.SpeechData - Speechmarks Example

Creating speechmarks directive: bytes written:60 0x0a 0x3a 0x0a 0x26 0x0a 0x17 0x41 0x6c 0x65 0x78 0x61 0x2e 0x47 0x61 0x64 0x67 0x65 0x74 0x2e 0x53 0x70 0x65 0x65 0x63 0x68 0x44 0x61 0x74 0x61 0x12 0x0b 0x53 0x70 0x65 0x65 0x63 0x68 0x6d 0x61 0x72 0x6b 0x73 0x12 0x10 0x12 0x0e 0x0a 0x01 0x73 0x12 0x06 0x76 0x69 0x73 0x65 0x6d 0x65 0x18 0x82 0x01

Parsing Directive: name = Speechmarks, namespace=Alexa.Gadget.SpeechData player offset: 0 speechmark type: viseme speechmark value: s speechmark start offset: 130

(Optional) Step 6: Modify the statically allocated data size

In the Alexa Gadgets sample code repository, there is an options file that corresponds to the .proto file of each directive and event that contains one or more fields in its payload.

The options files specify the maximum size for each field in the payload. This instructs the .proto compiler to statically allocate memory for the fields. For example, the options file for the Discover.Response event defines the size of the capabilities array as follows:

alexaDiscovery.DiscoverResponseEventPayloadProto.Endpoints.capabilities    max_count:32

When the compiler generates the code, it will therefore allocate memory for 32 capabilities. You can try to change max_count and recompile the code, but keep in mind that if you increase the maximum value, decoding might fail.

If you want to optimize memory usage, you can decrease the max_count (or max_size, where that is specified instead). This causes the compiler to statically allocate less memory to the .proto messages that you create.