miro
Click

 

Payload Format

1. 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 consits of two fields:

Field Description

L

Length of data struct, 1 byte, not including the length byte itself

T

Data struct type, 1 byte

1.1. Data Encoding

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

There are three distinct uplink payloads:

All uplinks are sento to LoRaWAN port 15.

2.1. 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 ambigious button press is off. If it is set to on one ore more bits might be set, indicationg, 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.

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

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

Used Charge

Rough approximation of used charge in uAh since last reset. Bytes are encoded in little endian format.

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 1. Button Press

04:01:51:A5:00:05:02:20:12:00:00

First Button

N (0b0001)

All Buttons

NS (0b0101)

Button Count

165

Used Charge

4640 uAh

2.2. 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 lenght (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 2. Status Message

05:02:10:00:00:00:03:03:AA:28:05:04:0F:F8:A0:05

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)

2.3. 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 3. Firmware Hash

05:05:23:52:D6:59

Git Hash

59d65223

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

All downlinks must be sent on the LoRaWAN port 3!

3.1. 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 = 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

6

1

Button debounce time in milliseconds

uint8

Example 4. Device Configuration

06:81:0F:F8:A0:05:1E

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

4. 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;
}