Home > Enterprise >  Deserialize Uint8Array memssage for protobuf with deserializeBinary function
Deserialize Uint8Array memssage for protobuf with deserializeBinary function

Time:03-08

Attempting to read results of a protobuf message sent from ESP32 chip via BLE and unable to deserialize the incoming message. Using protobuf for javascript in an Ionic app for Android devices. Here's my code:

import * as goog from 'google-protobuf';
export class PrepareMessageService {
  protoMessages = require('../assets/proto-js/wifi_config_pb.js');
  cmdSetConfig = new this.protoMessages.CmdSetConfig();
  respSetConfig = new this.protoMessages.RespSetConfig();
  wiFiConfigPayload = new this.protoMessages.WiFiConfigPayload();

  deserializeToBinary(message: any){
    const binMsg = new Uint8Array(message);
    const deserializedMsg = this.protoMessages.RespSetConfig.deserializeBinary(binMsg);
    console.log(JSON.stringify(deserializedMsg));
  }

Output from console.log is: {"wrappers_":null,"arrayIndexOffset_":-1,"array":[],"pivot_":1.7976931348623157e 308,"convertedPrimitiveFields_":{}

I was expecting to see some content in "array", but it's empty. Instead, if I print each element of the message received (argument for function deserializeToBinary) from the EPS32 chip via BLE with the following code:

for (let i = 0; i < message.byteLength; i  ){
      console.log('message[', i, ']: ', message.getUint8(i));
}

Here's the output:

message[0]: 8
message[1]: 3
message[2]: 106
message[3]: 2
message[4]: 8
message[5]: 6

Now I know for a fact that message[5] correctly represents the status of RespSetConfig (shown in my main protobuf file below) because each time I change it on the ESP32 chip side, I get the correct code in message[5]. The constants.proto file shown below shows the various status codes for RespSetConfig. So why isn't my code in deserializeToBinary giving me the correct representation for all elements of the message argument? Even though the ESP32 sends me different status values for RespSetConfig, the console log from deserializeToBinary prints the exact same thing, with no clear indication of what the status value of RespSetConfig is. In this case, I can work with the different elements of the message argument, but that won't necessarily solve my problem in other cases where I need to correctly deserialize the incoming protobuf response.

Here's the main protobuf file:

syntax = "proto3";
package espressif;

import "constants.proto";
import "wifi_constants.proto";

message CmdGetStatus {}

message RespGetStatus {
    Status status = 1;
    WifiStationState sta_state = 2;
    oneof state {
        WifiConnectFailedReason fail_reason = 10;
        WifiConnectedState connected = 11;
    }
}

message CmdSetConfig {
    string ssid = 1;
    string passphrase = 2;
    bytes bssid = 3;
    int32 channel = 4;
}

message RespSetConfig {
    Status status = 1;
}

message CmdApplyConfig {}

message RespApplyConfig {
    Status status = 1;
}

enum WiFiConfigMsgType {
    TypeCmdGetStatus = 0;
    TypeRespGetStatus = 1;
    TypeCmdSetConfig = 2;
    TypeRespSetConfig = 3;
    TypeCmdApplyConfig = 4;
    TypeRespApplyConfig = 5;
}

message WiFiConfigPayload {
    WiFiConfigMsgType msg = 1;
    oneof payload {
        CmdGetStatus cmd_get_status = 10;
        RespGetStatus resp_get_status = 11;
        CmdSetConfig cmd_set_config = 12;
        RespSetConfig resp_set_config = 13;
        CmdApplyConfig cmd_apply_config = 14;
        RespApplyConfig resp_apply_config = 15;
    }
}

And here is the constants.proto file imported in the main proto file:

syntax = "proto3";
package espressif;

/* Allowed values for the status
 * of a protocomm instance */
enum Status {
    Success = 0;
    InvalidSecScheme = 1;
    InvalidProto = 2;
    TooManySessions = 3;
    InvalidArgument = 4;
    InternalError = 5;
    CryptoError = 6;
    InvalidSession = 7;
}

CodePudding user response:

Figured out why this wasn't working. Quite a few errors in my deserializeToBinary function. This works:

deserializeToBinary(message: any){
  const binMsg = new Uint8Array(message.byteLength);
  //for loop required to create a buffer array because `message` is a DataView format
  for (let i = 0; i < message.byteLength; i  ){
    binMsg[i] = message.getUint8(i);
    console.log('protobuf response from ESP32 chip: ', binMsg[i]); //this prints protobuf response elements stored in the binMsg array.  Output is as expected in question.
  };

Then, we can use deserializeBinary on binMsg to deserialize it and then uses protobuf generated functions on the deserialized message to get the data we need. For example, the following code will provide the status of the respSetConfig message type in the WifiConfigPayload object by first deserializing binMsg with deserializeBinary and then obtaining message status with getRestSetConfig().getStatus(), both of which are generated by the google protobuf compiler: this.messages.WiFiConfigPayload.deserializeBinary(binMsg).getRespSetConfig().getStatus()

To work with protobuf messages, it was essential to understand the encoding/decoding to and from protobuf messages. The following link on Google's website can get you started with understanding how to code/decode protobuf messages: https://developers.google.com/protocol-buffers/docs/encoding

That said, Google's own documentation isn't adequate for those new to protobufs. The following link does a significantly better job at explaining the coding/decoding of protobuf messages: https://medium.com/nerd-for-tech/protobuf-what-why-fcb324a64564

  • Related