Decoder and firmware family
MOIZ publishes the official payload decoder harvestree_decoder.js for ChirpStack (v3/v4) and The Things Network. Download it from the payload decoding documentation and install it on your LNS as the FPort 1 application codec. MOIZ updates the decoder with firmware releases — align it with the firmware family deployed in the field (see Release notes).
Entry points:
- ChirpStack v4:
decodeUplink(input) - ChirpStack v3:
Decode(fPort, bytes) - The Things Network:
Decoder(bytes, port)
Common JSON fields (all frames)
| Key | Description |
|---|---|
serial_number | 24-bit device serial from payload |
boardTemperature | Board temp (°C) |
baseTemperature | Baseplate Pt1000 temp (°C) |
storageVoltage | Storage voltage (mV) |
thermogenVoltage | Thermogenerator voltage (mV) |
status | Frame type (lower 2 bits of the status nibble): 0 keepalive, 1 normal, 2 alarm — unchanged; alarm sources are separate fields below |
alarm_source_port | 1 if a port alarm is active; 0 otherwise. Present on every frame; meaningful when status = 2 |
alarm_source_system | 1 if a system alarm is active (HVT / HEAT / MOVE); 0 otherwise. Present on every frame; meaningful when status = 2 |
Frame types and ingestion rules
| status | Meaning | Ingestion |
|---|---|---|
| 0 | Keepalive | Health metrics only; no port measurement keys |
| 1 | Normal measurement | Health + dynamic port keys (pt_1, vib_2_rms_lf, …) |
| 2 | Alarm | Health + port keys + check alarm_source_port / alarm_source_system (one or both may be 1) |
Dynamic measurement keys
Port measurements use keys {family}_{port} or {family}_{port}_{suffix}. The decoder derives keys from port configuration bytes in the uplink header (wire bytes 1–4). Byte 0 is reserved (0xFF) and is not included in decoded JSON. Numeric values are already in engineering units (°C, mV, mm/s, …) — see Unit mapping in Data model. Full binary layout: Payload decoding.
Example decoded payloads
Keepalive
{
"serial_number": 23104,
"boardTemperature": 16,
"baseTemperature": 45,
"storageVoltage": 3600,
"thermogenVoltage": 52,
"status": 0,
"alarm_source_port": 0,
"alarm_source_system": 0
}
PT1000 on port 1
{
"serial_number": 23118,
"boardTemperature": 23,
"baseTemperature": 30,
"storageVoltage": 3600,
"thermogenVoltage": 49,
"status": 1,
"alarm_source_port": 0,
"alarm_source_system": 0,
"pt_1": 25.0
}
Weather on port 2 (multiple keys per port)
{
"serial_number": 23118,
"boardTemperature": 22,
"baseTemperature": 28,
"storageVoltage": 3580,
"thermogenVoltage": 48,
"status": 1,
"alarm_source_port": 0,
"alarm_source_system": 0,
"weatherA_2_temp": 21.4,
"weatherA_2_humidity": 58.2,
"weatherA_2_frost": 0
}
Alarm (port threshold)
{
"serial_number": 23118,
"boardTemperature": 23,
"baseTemperature": 30,
"storageVoltage": 3600,
"thermogenVoltage": 49,
"status": 2,
"alarm_source_port": 1,
"alarm_source_system": 0,
"pt_1": 85.2
}
Downlink ACK (optional — FPort 2)
Only needed if your platform sends configuration downlinks. The reference codec decodes FPort 1 only — parse ACKs in your integration service.
| Offset | Field | Description |
|---|---|---|
| 0 | CMD | Echo of the downlink command byte |
| 1 | STATUS | 0xAA = success; full status table in LoRaWAN remote configuration |
| 2+ | DATA | Optional; present for successful GET_PARAM |
Versioning
Align the LNS codec and deployed firmware using Release notes. Firmware version can be read over USB (Toolbox) or via downlink GET_VERSION — see LoRaWAN remote configuration if you use downlink.