# Abeeway Microtracker Decoder

**URL:** <https://www.thethingsnetwork.org/forum/t/abeeway-microtracker-decoder/22473>\
**Category:** End Devices (Nodes)\
**Created:** [February 9, 2019, 12:16pm UTC](https://www.thethingsnetwork.org/forum/t/abeeway-microtracker-decoder/22473 "2019-02-09T12:16:35Z")\
**Posts on this page:** 1\
**Showing post:** 2

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [February 9, 2019, 1:02pm UTC](https://www.thethingsnetwork.org/forum/t/abeeway-microtracker-decoder/22473/2 "2019-02-09T13:02:47Z")

</div>

Looking at the documentation, the payload you’re showing for “position type” 9 does not provide GPS coordinates. Instead, it gives you up to 4 MAC addresses of WiFi access points. I guess you should also get other payloads, that do provide the GPS data. Or maybe you need to move outside to get a GPS fix.

The following has only been tested with `032CD1890900C46E1FF44B9EC5C83A355A3898A6` and `0358D895090EC46E1FF44B9EB76466B3B87454AD500959CA1ED4AD525E67DA14A1AC`, the latter yielding:

```json
{
  "ack": 0,
  "age": 112,
  "battery": 3.99,
  "data": 9,
  "position_type": "WIFI BSSIDs",
  "stations": [
    {
      "mac_address": "c4:6e:1f:f4:4b:9e",
      "rssi": -73
    },
    {
      "mac_address": "64:66:b3:b8:74:54",
      "rssi": -83
    },
    {
      "mac_address": "50:09:59:ca:1e:d4",
      "rssi": -83
    },
    {
      "mac_address": "52:5e:67:da:14:a1",
      "rssi": -84
    }
  ],
  "status": {
    "mode": {
      "code": 2,
      "message": "Permanent tracking"
    },
    "moving": 0,
    "on_demand": 0,
    "periodic": 0,
    "sos": 1,
    "tracking": 1
  },
  "temperature": 31.46,
  "type": "POSITION"
}

```

So, most of the following is really untested, so you’ve got a lot of testing to do now (and I’d suggest you thoroughly compare the code to the documentation as well…).

For the GPS coordinates, in `function degree(bytes)`, you might actually need:

```javascript
var d = bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100;

```

But we need some test payload to confirm that. I guess some payload types (and the conditions for which you get them) would be nice for future readers as well.

> **Click for old version of the decoder, or see posts below for updates**
>
> ```javascript
> //
> // SEE UPDATED VERSION(S) IN FORUM POSTS BELOW
> //
> function Decoder(bytes, port) {
> 
> function step_size(lo, hi, nbits, nresv) {
> return 1.0 / ((((1<<nbits) - 1) - nresv) / (hi - lo));
> }
> 
> function mt_value_decode(value, lo, hi, nbits, nresv) {
> return ((value - nresv / 2) * step_size(lo, hi, nbits, nresv) + lo);
> }
> 
> // Gets the zero-based unsigned numeric value of the given bit(s)
> function bits(value, lsb, msb) {
> var len = (msb ? msb : lsb) - lsb + 1;
> var mask = (1<<len) - 1;
> return value>>lsb & mask;
> }
> 
> // Gets a hexadecimal representation with leading zeroes for each byte
> function hex(bytes, separator) {
> return bytes.map(function (b) {
> return ("0" + b.toString(16)).substr(-2);
> }).join(separator || "");
> }
> 
> // Decodes 4 bytes into a signed integer, MSB
> function int32(bytes) {
> // JavaScript bitwise operators always work with 32 bits signed integers
> return bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8 | bytes[3];
> }
> 
> // Decodes 4 bytes into an unsigned integer, MSB
> function uint32(bytes) {
> // Force an unsigned 32 bits integer using zero-fill right shift:
> return (bytes[0]<<24 | bytes[1]<<16 | bytes[2]<< 8 | bytes[3])>>>0;
> // Alternatively, don't use bitwise operators at all:
> // return bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100 + bytes[3];
> }
> 
> // Decodes the decimal degree for either latitude or longitude
> function degree(bytes) {
> // TODO This is probably an unsigned number, hence needs:
> // var d = (bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8)>>>0
> // or:
> // var d = bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100;
> var d = bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8;
> if (d > 0x7FFFFFFF) {
> d = d - 0x100000000;
> }
> return d / 1e7;
> }
> 
> // Decodes 1 to 4 MAC addresses and their RSSI
> function mac_rssi(bytes) {
> var items = [];
> for (var offset = 0; offset < bytes.length; offset += 7) {
> items.push({
> // Get hexadecimal bytes with a leading zero
> mac_address: hex(bytes.slice(offset, offset + 6), ":"),
> // Sign-extend to 32 bits to support negative values
> rssi: bytes[offset + 6]<<24>>24,
> });
> }
> return items;
> }
> 
> function message(value, messages) {
> return {
> code: value,
> message: value > messages.length ? "UNKNOWN" : messages[value]
> };
> }
> 
> var decoded = {};
> var i;
> var type = bytes[0];
> 
> if (type !== 0x00) {
> // All message types, except for Frame pending messages, share the same header
> decoded.status = {
> mode: message(bits(bytes[1], 5, 7), ["Standby", "Motion tracking", "Permanent tracking",
> "Motion start/end tracking", "Activity tracking", "OFF"]),
> sos: bits(bytes[1], 4),
> tracking: bits(bytes[1], 3),
> moving: bits(bytes[1], 2),
> periodic: bits(bytes[1], 1),
> on_demand: bits(bytes[1], 0)
> };
> 
> decoded.battery = Math.round(100 * mt_value_decode(bytes[2], 2.8, 4.2, 8, 2)) / 100;
> decoded.temperature = Math.round(100 * mt_value_decode(bytes[3], -44, 85, 8, 2)) / 100;
> decoded.ack = bits(bytes[4], 4, 7);
> decoded.data = bits(bytes[4], 0, 3);
> }
> 
> switch (type) {
> case 0x00:
> decoded.type = "FRAME PENDING";
> decoded.token = bytes[1];
> break;
> 
> case 0x03:
> decoded.type = "POSITION";
> switch (decoded.data) {
> case 0:
> decoded.position_type = "GPS fix";
> decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
> decoded.latitude = degree(bytes.slice[6]);
> decoded.longitude = degree(bytes.slice[9]);
> // Estimated Horizontal Position Error
> decoded.ehpe = mt_value_decode(bytes[12], 0, 1000, 8, 0);
> break;
> 
> case 1:
> decoded.position_type = "GPS timeout";
> decoded.timeout_cause = message(bytes[5], ["User timeout cause"]);
> for (i = 0; i < 4; i++) {
> // Carrier over noise (dBm) for the i-th satellite seen
> decoded["cn" + i] = mt_value_decode(bytes[6 + i], 0, 2040, 8, 0);
> }
> break;
> 
> case 2:
> // Documented as obsolete
> decoded.error = "UNSUPPORTED";
> break;
> 
> case 3:
> decoded.position_type = "WIFI timeout";
> for (i = 0; i < 6; i++) {
> decoded["v_bat" + (i + 1)] = mt_value_decode(bytes[5 + i], 2.8, 4.2, 8, 2);
> }
> break;
> 
> case 4:
> decoded.position_type = "WIFI failure";
> for (i = 0; i < 6; i++) {
> // Most of time a WIFI timeout occurs due to a low battery condition
> decoded["v_bat" + (i + 1)] = mt_value_decode(bytes[5 + i], 2.8, 4.2, 8, 2);
> }
> decoded.error = message(bytes[11], ["WIFI connection failure", "Scan failure",
> "Antenna unavailable", "WIFI not supported on this device"]);
> break;
> 
> case 5:
> case 6:
> decoded.position_type = "LP-GPS data";
> // Encrypted, not described in the documentation
> decoded.error = "UNSUPPORTED";
> break;
> 
> case 7:
> decoded.position_type = "BLE beacon scan";
> decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
> // Remaining data: up to 4 beacons
> decoded.beacons = mac_rssi(bytes.slice(6));
> break;
> 
> case 8:
> decoded.position_type = "BLE beacon failure";
> decoded.error = message(bytes[5], ["BLE is not responding", "Internal error", "Shared antenna not available",
> "Scan already on going", "No beacon detected", "Hardware incompatibility"]);
> break;
> 
> // Test with: 0358D895090EC46E1FF44B9EB76466B3B87454AD500959CA1ED4AD525E67DA14A1AC
> // or 032CD1890900C46E1FF44B9EC5C83A355A3898A6
> case 9:
> decoded.position_type = "WIFI BSSIDs";
> decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
> // Remaining data: up to 4 WiFi BSSIDs
> decoded.stations = mac_rssi(bytes.slice(6));
> break;
> 
> default:
> decoded.position_type = "UNSUPPORTED";
> }
> break;
> 
> case 0x04:
> decoded.type = "ENERGY STATUS";
> break;
> 
> case 0x05:
> decoded.type = "HEARTBEAT";
> break;
> 
> case 0x07:
> // Activity status message and configuration message share the same identifier
> var tag = bytes[5];
> switch (tag) {
> case 1:
> decoded.type = "Activity Status";
> decoded.activity_counter = uint32(bytes.slice(6, 10));
> break;
> 
> case 2:
> decoded.type = "Configuration";
> for (i = 0; i < 5; i++) {
> var offset = 6 + 5 * i;
> decoded["param" + i] = {
> type: bytes[offset],
> value: uint32(bytes.slice(offset + 1, offset + 5))
> };
> }
> break;
> 
> default:
> decoded.error = "UNSUPPORTED";
> }
> break;
> 
> case 0x09:
> decoded.type = "Shutdown";
> break;
> 
> case 0xFF:
> decoded.type = "Debug";
> break;
> 
> default:
> decoded.type = "UNSUPPORTED";
> }
> 
> // Just some redundant debug info
> decoded.debug = {
> payload: hex(bytes),
> length: bytes.length,
> port: port
> };
> 
> return decoded;
> }
> 
> ```

---

_[View the full topic](https://www.thethingsnetwork.org/forum/t/abeeway-microtracker-decoder/22473)._
