Skip to content

Payload Format

Codec

A complete LoRaWAN payload codec with examples can be found in the TBD section. See also Decoder example for reference.

Overview

The payload of up and downlink messages consists of an arbitrary number of data structs (DS) of different types and lengths.

DS 1 DS 2 ... DS n

Each data struct is a combination of a header and the actual data payload:

L T payload

The header consists of two fields:

Field Description
L Length of data struct, 1 byte, not including the length byte itself
T Data struct type, 1 byte

Data Encoding

Signed integers use two’s complement for encoding.

Important

Unless otherwise noted, payloads will use little endian data encoding.

There are three distinct uplink payloads:

Important

All uplinks are sento to LoRaWAN port 15.

Button Press

On every button press a message is sent to the network. The button press message is either sent confirmed or unconfirmed depending on current settings. The message consists of two data sections: Button state and Used charge

Button State

The message reports to different button state bit fields. The first field represents the activating button(s), which is the button that has been registered first. Only one bit will be set if ambiguous button press is off. If it is set to on one or more bits might be set, indicating, that more than one button has been pressed at the same time. Only activated buttons will be reported.

After the button debounce time has passed, a second reading of all active buttons will is taken and reported int the bit field second reading.

Byte Size Description Format
0 1 Message length (0x04) uint8
1 1 Message type (0x01) uint8
2 1 Bit mask representing active buttons:
Bit 0..3: Bit mask for first button press (N E S W)
Bit 4..7: Bit mask for all buttons pressed (N E S W)
Bitfield
3-4 2 Counter since last reset (little endian) uint16

For all bit fields, buttons are encoded as in

Bit (within bit field) Button
0 North (N), top
1 East (E), right
2 South (S), bottom
3 West (W), left

Used Charge

Rough approximation of used charge in uAh since last reset.

Byte Size Description Format
0 1 Message length (0x05) uint8
1 1 Message type (0x02) uint8
2-5 4 Used charge in uAh (little endian) uint32

Example

Payload 04:01:51:A5:00:05:02:20:12:00:00 is reporting

Field Value
First Button N (0b0001)
All Buttons NS (0b0101)
Button Count 165
Used Charge 4640 uAh

First button that has been pressed is 'N' (top). Upon sending 'N' and 'S' buttons have been pressed. Total button count since reset is 165. Used charge since reset is 4640 uAh.

Status Message

The device sends a regular status messages at a configurable interval. The status message is always unconfirmed, regardless of the settings.

The Status message can be used to detect low batteries. An miro Click will stop working at 1.8V. 2.0V is therefore a good value to use for critical battery alerts. It may be useful to already create an alert at around 2.4V, in order to know when the device slowly reaches low battery levels.

The status message consists of the following data structs. Please note, depending on the LoRaWAN region, not all data structs may be present.

Used Charge

See data section used charge above.

Battery Voltage and Temperature

Report current battery voltage and MCU temperature.

Byte Size Description Format
0 1 Message length (0x03) uint8
1 1 Message type (0x03) uint8
2 1 Current battery Voltage in 10 mV with an offset of 170. To calculate volts: (x + 170)/100 uint8
3 1 Current MCU temperature in °C uint8

Current Configuration

Report current configuration.

Byte Size Description Format
0 1 Message length (0x05) uint8
1 1 Message type (0x04) uint8
2 1 Bit mask representing active buttons:
Bit 4..7: RFU
Bit 0..3: Button N E S W
Bitfield
3 1 Flags, bitwise or combination:
Bit 7 = Confirmed uplinks (0 = off, 1 = on)
Bit 6 = Buzzer (0 = off, 1 = on)
Bit 5 = Dury Cycle (0 = off, 1 = on)
Bit 4 = Amigious button press (0 = off, 1 = on)
Bit 3 = Join strategy (0 = fast DR, 1 = slow DR)
Bits 0-2: RFU
Bitfield
4-5 2 Status interval in minutes uint16

Example

Payload 05:02:10:00:00:00:03:03:AA:28:05:04:0F:F8:A0:05 is reporting the following values

Parameter Value
Used Charge 16 uAh
Battery Voltage 3.4 V
Internal Temperature 40° C
Button Configuration 0x0F -> All buttons enabled
Configuration Flags 0b11111100 -> Everything enabled
Status Message Intervall 1440 minutes (1 day)

Firmware Hash

The firmware hash message is sent once after successfully joining the network. The hash value can be used to identify the current firmware version of the device

Byte Size Description Format
0 1 Message length (0x05) uint8
1 1 Message type (0x05) uint8
2-5 4 Firmware hash uint32

Example

Payload 05:05:23:52:D6:59 is reporting git hash value 59d65223

Downlink messages are used to change the configuration of the device. They use the same general payload format as uplinks.

Important

All downlinks must be sent on the LoRaWAN port 3!

Device Configuration

The device configuration message type is used to set general device configuration.

Message type T = 0x80 and length L = 0x06.

Byte Size Description Format
0 1 Message lenght (0x06) uint8
1 1 Message type (0x81) uint8
2 1 Button configuration mask:
Bit 4..7: RFU
Bit 0..3: Button N E S W
Bitfield
3 1 Flags, combination of:
Bit 7 = Confirmed uplinks (0 = off, 1 = on)
Bit 6 = Buzzer (0 = off, 1 = on)
Bit 5 = Duty Cycle (0 = off, 1 = on)
Bit 4 = Ambiguous button press (0 = off, 1 = on)
Bit 3 = Join strategy (0 = fast DR, 1 = slow DR)
Bit 2 = Force join (0 = off, 1 = on)
Bits 0-1: RFU
Bitfield
4-5 2 Status interval in minutes uint16
6 1 Button debounce time in milliseconds uint8

Example

Payload 06:81:0F:F8:A0:05:1E will set the following config on the device

Parameter Value
Flags Duty Cycle: On
Buzzer: On
Confirmed uplinks: On
Join Strategy: slowest DR
Ambigious First Press: On
Status interval 1440 minutes (1 day, 0x05A0)
Button Debounce Time 30 ms

Decoder example

function decodeMiroClick(fPort, bytes) {
    // Decode an uplink message from a buffer
    // (array) of bytes to an object of fields.
    var decoded = {};

    function map(button) {
        r = "";
        if (button & 1) r += "N";
        if (button & 2) r += "E";
        if (button & 4) r += "S";
        if (button & 8) r += "W";
        return r;
    }

    if (fPort == 15) {
        n = bytes.length;
        idx = 0;
        while (n > idx) {
            s = bytes[idx++];
            decoded.type = bytes[idx];
            if (bytes[idx] == 1) {
                decoded.buttons_first = map(bytes[idx + 1] & 0xF);
                decoded.buttons = map((bytes[idx + 1] >> 4) & 0xF);
                decoded.count = bytes[idx + 2] + (bytes[idx + 3] << 8);
            } else if (bytes[idx] == 2) {
                decoded.uah = bytes[idx + 1] + (bytes[idx + 2] << 8) + (bytes[idx + 3] << 16) + (bytes[idx + 4] << 24);
            } else if (bytes[idx] == 3) {
                decoded.iTemp = bytes[idx + 2];
                decoded.vBatt = (bytes[idx + 1] + 170) / 100.0;
            } else if (bytes[idx] == 4) {
                decoded.buttonCfg = map(bytes[idx + 1] & 0xF);
                decoded.flags = bytes[idx + 2].toString(16);
                decoded.statusCycle = bytes[idx + 3] + (bytes[idx + 4] << 8);
            } else if (bytes[idx] == 5) {
                decoded.firmwareHash = (bytes[idx + 1] + (bytes[idx + 2] << 8) + (bytes[idx + 3] << 16) + (bytes[idx +4] << 24)).toString(16);
            }
            idx += s;
        }
    }
    return decoded;
}