Skip to content

Custom Message Parsing

Custom message type decoding grammar

If you are a developer and want to decode the 12 bytes manually, please refer to parser-factory details below.

To decode the 12bytes provided by devices, Sigfox uses a specific Grammar defining, the name, type, and length/offset of for each data segments. In Murano this definition is done for each Sigfox callback under the payloadConfig array. Each resource definition in this array defines an ordered segment of the 12 bytes including its resource location in Murano and its Sigfox decoding type using below grammar.

Each "definition format" grammar is as follows:

definition format = byte_index ":" type_def ;
byte_index = [digit*] ;
type_def = bool_def | char_def | float_def | uint_def ;
bool_def = "bool:" ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7") ;
char_def = "char:" length ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7");
float_def = "float:" ("32" | "64") [ ":little-endian" | ":big-endian" ] ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7");
uint_def = "uint:" ["1" - "64"] [ ":little-endian" | ":big-endian" ] ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7");
int_def = "int:" ["1" - "64"] [ ":little-endian" | ":big-endian" ] ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7");
length = number* ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

A field is defined by its position in the message bytes, its length and its type:

  • The byte index is the offset in the message buffer where the field is to be read from, starting at zero. If omitted, the position used is:
  • The current byte for boolean fields
  • The next byte for all other types if the previous element has no bit offset
  • The last byte used if the previous element has a bit offset.

    For the first field, an omitted position means zero (start of the message buffer)

Next comes the type name and parameters, which varies depending on the type:

  • boolean : parameter is the bit position in the target byte
  • char : parameter is the number of bytes to gather in a string, and optionally the bit offset where to start the reading of the first byte, Default value is 7 for the offset
  • float : parameters are the length in bits of the value, which can be either 32 or 64 bits, optionally the endianness for multi-bytes floats, and optionally the bit offset where to start the reading of the first byte. Default is big endian and 7 for the offset. Decoding is done according to the IEEE 754 standard.
  • uint (unsigned integer) : parameters are the number of bits to include in the value, optionally the endianness for multi-bytes integers, and optionally the bit offset where to start the reading of the first byte. Default is big endian and 7 for the offset.
  • int (signed integer) : parameters are the number of bits to include in the value, optionally the endianness for multi-bytes integers, and optionally the bit offset where to start the reading of the first byte. Default is big endian and 7 for the offset.

Examples

There are some examples to showcase how to define the payload config.

Example 1:

Payload config:

[
  {
    "resource": "int1",
    "definition": ":uint:8"
  },
  {
    "resource": "int2",
    "definition": ":uint:8"
  }
]

Message (in hex):

1234

Result:

{ "int1": 0x12, "int2": 0x34 }

Example 2:

Payload config:

[
  {
    "resource": "b1",
    "definition": ":bool:7"
  },
  {
    "resource": "b1",
    "definition": ":bool:6"
  },
  {
    "resource": "i1",
    "definition": "1:uint:16"
  }
]

Message (in hex):

C01234

Result:

{ "b1": true, "b2": true, "i1": 0x1234 }

Example 3:

Payload config:

[
  {
    "resource": "str",
    "definition": ":char:6"
  },
  {
    "resource": "i1",
    "definition": ":uint:16"
  },
  {
    "resource": "i2",
    "definition": "1:uint:32"
  }
]

Message (in hex):

41424344454601234567890A

Result:

{ "str": "ABCDEF", "i1": 0x123, "i2": 0x4567890A }

Example 4:

Payload config:

[
  {
    "resource": "str",
    "definition": ":char:6"
  },
  {
    "resource": "data.temperature",
    "definition": ":uint:16"
  },
  {
    "resource": "data.humidity",
    "definition": "1:uint:32"
  }
]

Message (in hex):

41424344454601234567890A

Result:

{ "str": "ABCDEF", "data": { "temperature": 0x123, "humidity": 0x4567890A } }

Sigfox Parser Factory

If you want to decode sigfox 12 bytes manually, you can use murano pre-defined parser factory. It can be found in your Sigfox IoT Connector solution: Modules - bytes.parser_factory

The raw 12 bytes is transmitted in the uplink device2 resource.

The following is a .lua example showing how to use it:

local parser_factory = require("bytes.parser_factory")
function transform.DecodeMode(uplink)
  -- Here decode data from uplink string (the 12 bytes transmitted by devices)
  -- Generated data in 'data_in' must be defined in the module 'vendor.configIO'
  -- In this example an integer in first byte is used as mode
  local mode = parser_factory.getuint(parser_factory.fromhex(uplink),0,8)
  if mode == 1 then
    -- Here decode the remaining bytes base on the mode. Example: temperature as float
    return {temperature = parser_factory.getfloat_32(parser_factory.fromhex(uplink),1)}
  elseif mode == 2 then
    -- Another mode example extracting a Boolean value
    return {button_enabled = parser_factory.getbool(parser_factory.fromhex(uplink),1,2)}    
  else
    log.error("Un-supported mode " .. mode .. " from uplink: " .. uplink)
    return {}
  end
end

-- Below an example of transforming uplink message from a device
-- decoded values in string will be then stored in data_in
state.data_in = json.stringify(transform.DecodeMode(state.uplink))

Last update: April 10, 2021