Stand with Ukraine flag
Pricing Try it now
On this page
RN-320 BTH LoRaWAN Temperature and Humidity sensor

Introduction

The Radionode RN320 series is a battery-powered LoRaWAN® environmental sensor designed for long-term, low-maintenance deployments. The RN320-BTH model features a high-capacity 17,000 mAh battery, enabling up to 10 years of operation under typical conditions.

The device integrates a high-accuracy temperature and humidity sensor and supports data reliability mechanisms, including retransmission to prevent sample loss. Measurements can also be stored locally on a microSD card for redundancy and offline access.

User interaction is provided via an E-paper display, audible buzzer, and LED status indicator. The device supports simplified onboarding and remote monitoring through QR-code-based registration with the Radionode365 service.

Features of RN-320 BTH device

  • LoRaWAN® long-range wireless communication
  • High-accuracy temperature and humidity sensor
  • E-paper display
  • Loud buzzer (97 dBA)
  • 3-color LED indicator (Best / Moderate / Bad)
  • Long battery life (17,000 mAh, up to 10 years)
  • MicroSD card for local data storage
  • Magnetic and wall-mount installation options

Prerequisites

Before starting, ensure the following requirements are met:

Hardware

Accounts & Access

Device Information

  • DevEUI (from device label)
  • JoinEUI and AppKey (for OTAA activation)
  • LoRaWAN region (e.g. EU868, US915)

The Things Stack Configuration

Create an Application

  • Log in to The Things Stack Console.
  • Create an application in TheThingsStack console.
  • Go to the console, open Applications section, press the Add application tab.
  • Fill the application ID, application name
  • Click create the application.

RN320-BTH transmits sensor data in binary format. Decoding must be performed before forwarding data to ThingsBoard.

  • Open your application in TTS.
  • Navigate to Payload formatters → Uplink.
  • Paste the following decoder:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
function decodeUplink(input) {
    const res = Decoder(input.bytes, input.fPort);
    if (res.error) {
      return { errors: [res.error] };
    }
    return { data: res };
  }

function Decoder(bytes, port) {
    const readUInt8 = b => b & 0xFF;
    const readUInt16LE = b => (b[1] << 8) + b[0];
    const readInt16LE = b => {
      const ret = readUInt16LE(b);
      return (ret > 0x7ffff) ? ret - 0x10000 : ret;
    }
    const readUInt32LE = b => (b[3] << 24) + (b[2] << 16) + (b[1] << 8) + b[0];
    const readInt32LE = b => {
      const ret = readUInt32LE(b);
      return (ret > 0x7FFFFFFF) ? ret - 0x100000000 : ret;
    }
    const readFloatLE = b => {
      const buf = new ArrayBuffer(4);
      const view = new DataView(buf);
      for (let i = 0; i < 4; i++) view.setUint8(i, b[i]);
      return view.getFloat32(0, true); // ieee754 float
    };

    const head = readUInt8(bytes[0]);
    const model = readUInt8(bytes[1]);

    if (head === 11) {
      // Check-in frame
      const timestamp = readUInt32LE(bytes.slice(2, 6));
      const date = new Date(timestamp * 1000);
      const yyyy = date.getUTCFullYear();
      const mm = (date.getUTCMonth() + 1).toString().padStart(2, '0');
      const dd = date.getUTCDate().toString().padStart(2, '0');
      const verFormatted = parseInt(`${yyyy}${mm}${dd}`);

      const interval = readUInt16LE(bytes.slice(6, 8));
      const splrate = interval;
      const bat = readUInt8(bytes[8]);
      const millivolt = readUInt16LE(bytes.slice(9, 11));
      const volt = (millivolt / 1000).toFixed(3);
      const freqband = readUInt8(bytes[11]);
      const subband = readUInt8(bytes[12]);

      return {
        head,
        ver: verFormatted,
        interval,
        splrate,
        bat,
        volt,
        freqband,
        subband
      };
    }

    else if (head === 12 || head === 13) {
      // Sensor / Hold
      const tsmode = readUInt8(bytes[2]);
      const timestamp = readUInt32LE(bytes.slice(3, 7));
      const splfmt = readUInt8(bytes[7]);

      if (splfmt !== 2) {
        return { error: "Unsupported Sensor Data Format: " + splfmt };
      }

      const raw_size = 4;
      const data = bytes.slice(8);
      const ch_count = data.length / raw_size;
      const data_size = data.length;

      let offset = 0;
      let temperature = null, humidity = null;

      if (ch_count < 2) {
        return { error: "Unsupported Sensor Data Size: " + ch_count };
      }

      temperature = parseFloat(readFloatLE(data.slice(offset, offset + raw_size)).toFixed(2));
      if (temperature <= -9999.0) temperature = null;
      offset += raw_size;

      humidity = parseFloat(readFloatLE(data.slice(offset, offset + raw_size)).toFixed(2));
      if (humidity <= -9999.0) humidity = null;

      return {
        head,
        model,
        tsmode,
        timestamp,
        splfmt,
        data_size,
        temperature,
        humidity
      };

    }

    return { error: "Unsupported head frame: " + head };
}

Register the End Device

Next step is an End Device creation in the TTS. Navigate to End devicesRegister end device.

To register the end Device enter these details as shown in the image below:

  • Select From The LoRaWAN Device Repository.
  • Input method: Select the end device in the LoRaWAN Device Repository (Radionode devices are already registered in the Thing stack platform).
  • End device brand: choose:
    • Brand: Dekist Co., Ltd
    • Model: RN320-BTH
  • Select the correct frequency plan.
  • Enter the DevEUI correctly in the slot provided there. You can find DevEUI on the sticker on the side of the device.
  • Add an End device ID in the slot given.
  • Click Register end device to complete the end device registration.

ThingsBoard configuration

  • Log in to ThingsBoard.
  • Navigate to Integrations ⇾ Add integration.
  • Select HTTP Integration as we will be receiving data from the webhooks created in TTS (Webhook configuration we will explain later in this documentation) .
  • In the payload decoder for the message received from the network server, copy and paste the following TBEL decoder code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
var data = {};
if (payload instanceof String) {
        data = JSON.parse(payload);
} else if (payload instanceof java.util.ArrayList || payload instanceof java.util.List) {
   var str = bytesToString(payload);
       data = JSON.parse(str);
} else {
    data = payload;
}


var deviceName = data.?end_device_ids.?device_id;
if (deviceName == null){
    deviceName = "Unknown_Device";
} 

var applicationId = data.?end_device_ids.?application_ids.?application_id;
if (applicationId == null) applicationId = "Unknown_App";

var uplink = data.?uplink_message;
var decoded = {};
if (uplink != null && uplink.decoded_payload != null) {
    decoded = uplink.decoded_payload;
}

var rx = {};
if (uplink != null && uplink.rx_metadata != null && uplink.rx_metadata.size() > 0) {
    rx = uplink.rx_metadata[0];
}

var telemetry = {};

if (decoded.?temperature != null) telemetry.temperature = decoded.temperature;
if (decoded.?humidity != null) telemetry.humidity = decoded.humidity;
if (decoded.?model != null) telemetry.model = decoded.model;
if (decoded.?timestamp != null) telemetry.timestamp = decoded.timestamp;

if (rx.?rssi != null) telemetry.rssi = rx.rssi;
if (rx.?snr != null) telemetry.snr = rx.snr;

var tsString = uplink.?received_at;
var tsMillis = new Date().getTime(); 

if (tsString != null) {
    tsMillis = new Date(tsString).getTime();
}


var attributes = {
    "app_id": applicationId,
    "f_port": uplink.?f_port,
    "frequency": uplink.?settings.?frequency,
    "spreading_factor": uplink.?settings.?data_rate.?lora.?spreading_factor,
    "gateway_id": rx.?gateway_ids.?gateway_id,
    "dev_eui": data.?end_device_ids.?dev_eui
};

var result = {
    "deviceName": deviceName,
    "deviceType": "RN320-BTH",
    "telemetry": {
        "ts": tsMillis,
        "values": telemetry
    },
    "attributes": attributes
};

return result;
  • Copy and save the generated HTTP endpoint URL.
  • Click Add to create the integration.

Create Webhook in The Things Stack

To create a webhook in the TTS platform:

  • Open your application in TTS.
  • Navigate to Webhooks.
  • Select Custom webhook.

General settings

  • Add a webhook ID - name for your webhook.
  • In the Base URL add the endpoint URL you copied from the ThingsBoard platform.
  • In the Additional header option add application/json
  • Save the webhook.

Now the communication will start between the network server and the ThingsBoard platform to send data from your LoRaWAN devices to the ThingsBoard.

Check data on ThingsBoard

  • Navigate to Devices.
  • Confirm the rn320-bth device is created automatically.
  • Check Latest Telemetry for temperature and humidity values.

Dashboard (Optional)

  • Navigate to Dashboards → Create new dashboard.
  • Add widgets for:
    • Temperature
    • Humidity
    • RSSI / SNR
  • Bind widgets to the RN320-BTH device.

A sample of the dashboard created is shown in the pictures below.