Seeed Studio S2100 Data Logger Payload Decoder

Decode Seeed SenseCAP S2100 LoRaWAN Data Logger payloads online: measurement packets 0x30/0x31/0x32/0x33, the 0x39 battery packet and the 0x80000000 sentinel, from the official big-endian frame format. Includes verified examples and a decodeUplink codec for ChirpStack v4 and The Things Stack.

Type: Data logger hub: 4–20 mA / 0–10 V analog, RS485 Modbus-RTU, GPIO LoRaWAN class: Class A FPort: 2 (measurements) / 3 (battery) Payload: Variable (packet IDs) Endianness: Big-endian

Example payloads

311001000067DE80000000 One measurement: 26.59 (0x000067DE = 26590 ÷ 1000); second slot empty (0x80000000) — official manual example
311202000067DE0000C26A Two measurements: 26.59 and 49.77 — official manual example
301202000067DE0000C2A633340205F98A8800019A28 Four measurements across first (0x30) + final (0x33) packets: 26.59 · 49.83 · 100240.008 · 105 — official manual example
39640101000200050000 Battery packet: 100 % · SW 1.1 · HW 0.2 · interval 5 min — official manual example

Payload structure

Bytes Field Type / scale Description
0x31 (11 B) Measurement packet (≤ 2 values) [ID][m|n][flag][val m 4B][val n 4B] Byte 2 nibbles select measurement numbers m and n (1–10). Values are int32 BE ÷ 1000. 0x80000000 = no measurement.
0x30 / 0x32 / 0x33 Multi-packet series (> 2 values) first / middle / final Each carries 2 measurements. 0x30 and 0x33 include a flag byte (11 B); 0x32 does not (10 B) — a fixed-offset decoder breaks here.
0x39 (10 B) Battery packet [ID][%][SW 2B][HW 2B][interval 2B][rsv 2B] Battery %, software/hardware version and measurement uplink interval in minutes. Arrives on FPort 3.
0x34 (5 B) Datalogger status protocol / GPIO / analog config Configuration echo; the manual says it can be ignored. This decoder parses and skips it safely.
value encoding Measurement values int32 BE ÷ 1000, signed The physical meaning of each measurement number depends on how you configured the channel (4–20 mA current, 0–10 V voltage, Modbus register, GPIO count) and the Y = Ax + B scaling set in the SenseCAP Mate app.

Ready-to-use codec

ChirpStack v4 and The Things Stack share the same decodeUplink(input) signature: paste this into the device profile codec (ChirpStack) or the payload formatter (TTN).

function decodeUplink(input) {
  var b = input.bytes;
  var data = {};
  var i = 0;
  function i32(o) { return (b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]; }
  function meas(ch, o) {
    if (ch === 0) return;
    if ((i32(o) >>> 0) === 0x80000000) return; // no measurement
    data["measurement_" + ch] = i32(o) / 1000;
  }
  while (i < b.length) {
    var id = b[i];
    if (id === 0x30 || id === 0x31 || id === 0x33) {        // 2 values + flag byte
      meas(b[i + 1] >> 4, i + 3); meas(b[i + 1] & 0x0F, i + 7); i += 11;
    } else if (id === 0x32) {                                // middle packet: no flag byte
      meas(b[i + 1] >> 4, i + 2); meas(b[i + 1] & 0x0F, i + 6); i += 10;
    } else if (id === 0x39) {                                // battery packet
      data.battery = b[i + 1];
      data.software_version = b[i + 2] + "." + b[i + 3];
      data.hardware_version = b[i + 4] + "." + b[i + 5];
      data.interval_min = (b[i + 6] << 8) | b[i + 7];
      i += 10;
    } else if (id === 0x34) { i += 5; }                      // datalogger status: skip
    else if (id === 0x03) { data.battery = b[i + 1]; i += 2; }
    else if (id === 0x06) { data.sensor_error = b[i + 1]; i += 2; }
    else if (id === 0x10) { i += 10; }                       // sensor EUI: skip
    else { break; }                                          // unknown: stop safely
  }
  return { data: data };
}

S2100 vs. S210x: two different protocols

Despite the similar names, the S2100 Data Logger does not use the S210x sensor frame format. The S210x series (S2101–S2110) packs little-endian 7-byte frames with global measurement IDs like 0x1001; the S2100 uses big-endian packet-ID frames where measurements are numbered 1–10 according to how you configured the logger’s channels. Decoders are not interchangeable — that’s why this tool lists them as separate devices.

The S2100 is a battery-powered hub that turns industrial sensors into LoRaWAN nodes: two 4–20 mA / 0–10 V analog inputs, RS485 Modbus-RTU (up to 10 measurements) and GPIO (level or pulse counting). Points that matter when decoding:

  • Values have no intrinsic unit. A “measurement” is whatever the channel reads — milliamps, a Modbus register, a pulse count — optionally transformed by the Y = Ax + B scaling configured in the SenseCAP Mate app. The decoder gives you the numbers; the meaning is in your config.
  • 0x80000000 means “no measurement”, not a huge negative number. The official manual highlights it; naive decoders show −2147483.648.
  • Packet 0x32 is one byte shorter than 0x30/0x31/0x33 (it has no flag byte). A decoder that assumes a fixed frame size corrupts every value after the first middle packet.
  • Measurements arrive on FPort 2, battery on FPort 3 (0x39 packet, with versions and the uplink interval in minutes). A status packet 0x34 may piggyback after it and can be ignored.

References

  • SenseCAP S2100 LoRaWAN Data Logger User Guide, chapter 10 “Payload Decoder” (packet tables and all worked examples) — Seeed Studio
  • Official decoder: github.com/Seeed-Solution/SenseCAP-Decoder, S2100/