产品定价 立即试用
目录
如何将 UWB Badge Tracker 连接至 ThingsBoard?
  • Hardware type: Trackers
  • Connectivity: LoRaWAN
  • Industry: Security, Transportation & Logistics, Smart Cities
  • Use cases: Fleet Tracking, Health Care
  • Platforms: Community Edition, Professional Edition, Cloud

UWB徽章追踪器采用LoRaWAN和UWB技术设计。
与锚点计算距离,并将距离信息转发至LoRaWAN网关,由服务器计算其位置。精度可达10cm。

前置条件

继续本指南前,需准备以下内容:

配置

仅当该设备通过MQTT直接与ThingsBoard通信时,需使用ThingsBoard Cloud。

使用ThingsBoard集成时,可使用本地部署的ThingsBoard PE或ThingsBoard Cloud。

若要与网络服务器创建集成,请先选择所支持的网络服务器之一:

在ChirpStack上添加网关

我们需要在 ChirpStack 上添加网关。

要添加网关,请按以下步骤操作:

  • 登录ChirpStack服务器。进入“网关”页面,点击“添加网关”按钮。

  • 填写 名称网关ID(根据您的网关而定,可在网关控制面板上查看),向下滚动并点击“提交”按钮。

  • 网关已添加。在网关选项卡中可查看其状态。

在ChirpStack上添加设备配置

  • 登录ChirpStack服务器。进入“设备配置”页面,点击 添加设备配置 按钮。

  • 填写必填字段。

  • 进入 Codec 选项卡,在 Payload codec 下拉菜单中选择 JavaScript functions,粘贴解码器函数。然后点击 提交 按钮。

解码函数:

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// UWB Badge Tracker decoder
function decodeUplink(input) {
// bytes
    var bytes = input.bytes;
// type
    var type = (bytes[0] >> 4) & 0x0f;
    switch (type) {
        case 0x01:
            return {data: decodeRegistration(bytes)};
        case 0x02:
            return {data: decodeHeartbeat(bytes)};
        case 0x03:
            return {data: decodeGNSSPosition(bytes)};
        case 0x04:
            return {data: decodeBeacon(bytes)};
        case 0x05:
            return {data: decodeAlarm(bytes)};
        case 0x06:
            return {data: decodeSingleConfigurationParameter(bytes)};
        case 0x07:
            return {data: decodeRuleBeacon(bytes)};
        case 0x08:
            return {data: decodeDistance(bytes)};
        default:
            return;
    }
}

// type: 0x1 Registration
function decodeRegistration(bytes) {
    var data = {};
    data.type = "RegistrationMessage";
    data.softwareVersion = ((bytes[1] << 8) & 0xff00) | (bytes[2] & 0xff);
    data.hardwareVersion = ((bytes[3] << 8) & 0xff00) | (bytes[4] & 0xff);
    data.deviceState =
        (bytes[5] << 24) | (bytes[6] << 16) | (bytes[7] << 8) | bytes[8];
    data.lorSynPeriod = bytes[9];
    data.lorSynFreq =
        (bytes[10] << 24) | (bytes[11] << 16) | (bytes[12] << 8) | bytes[13];
    data.lorSynDr = bytes[14];
    data.heartbeatPeriod = ((bytes[15] << 8) & 0xff00) | (bytes[16] & 0xff);
    data.messageId = ((bytes[17] << 8) & 0xff00) | (bytes[18] & 0xff);
    return data;
}

// type: 0x2 Heartbeat
function decodeHeartbeat(bytes) {
    var data = {};
    data.type = "HeartbeatMessage";
    data.deviceState =
        (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
    data.powerVol = bytes[5];
    data.powerPersent = bytes[6];
    data.uwbRangeCount = bytes[7];
    data.bleOnCount = bytes[8];
    data.gnssOnCount = bytes[9];
    data.temperature = ((bytes[10] << 8) & 0xff00) | (bytes[11] & 0xff);
    data.movementTime = ((bytes[12] << 8) & 0xff00) | (bytes[13] & 0xff);
    data.wearTime = ((bytes[14] << 8) & 0xff00) | (bytes[15] & 0xff);
    data.chargeTime = ((bytes[16] << 8) & 0xff00) | (bytes[17] & 0xff);
    data.messageId = ((bytes[18] << 8) & 0xff00) | (bytes[19] & 0xff);
    return data;
}

// type: 0x03 GNSSPosition
function decodeGNSSPosition(bytes) {
    var data = {};
    data.type = "GNSSPosition";
    data.gnssState =
        (bytes[0] & 0x01) === 0 ? "GNSS location success" : "GNSS location
    failed
    ";
// longitude
    let longitude =
        (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
    data.longitude = hex2float(longitude);
// latitude
    let latitude =
        (bytes[5] << 24) | (bytes[6] << 16) | (bytes[7] << 8) | bytes[8];
    data.latitude = hex2float(latitude);
// time
    let time =
        (bytes[9] << 24) | (bytes[10] << 16) | (bytes[11] << 8) | bytes[12];
    data.time = timestampToTime((time + 8 * 60 * 60) * 1000);
// wearState
    data.wearState = bytes[13] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x4 Beacon
function decodeBeacon(bytes) {
    var data = {};
    data.type = "BeaconMessage";
    data.beaconType =
        (bytes[0] & 0x01) === 0 ? "PositioningBeacon" : "AssetBeacon";
    data.beaconCount = bytes[1] & 0x0f;
    for (let i = 0; i < data.beaconCount; i++) {
        var index = 2 + 5 * i;
        var major = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var minor = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] &
            0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var rssi = bytes[index + 4] - 256 + "dBm";
        data["beacon" + (i + 1)] = major + minor;
        data["rssi" + (i + 1)] = rssi;
    }
// wearState
    var wearIndex = data.beaconCount * 5 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x5 Alarm
function decodeAlarm(bytes) {
    var data = {};
    data.type = "AlarmMessage";
    var alarmValue = bytes[1] & 0x0f;
    if (alarmValue == 1) {
        data.alarm = "SOS";
    } else if (alarmValue == 2) {
        data.alarm = "Fall";
    } else if (alarmValue == 3) {
        data.alarm = "Special area";
    } else if (alarmValue == 4) {
        data.alarm = "Search";
    } else if (alarmValue == 5) {
        data.alarm = "Magnet remove";
    }
    return data;
}

// type: 0x6 Single Configuration Parameter
function decodeSingleConfigurationParameter(bytes) {
    var data = {};
    data.type = "ConfigurationParameterResponse";
    var parameter = [];
    let byteLength = bytes.length;
    var index = 0;
    while (index + 1 < byteLength) {
        var commandBitField = {};
        var parameterType = bytes[index + 1] & 0xff;
        commandBitField.parameterType = parameterType;
        var commandBitFieldLength = getCommandBitFieldLength(parameterType);
        var parameterValue = getParameterValue(bytes, index,
            commandBitFieldLength);
        commandBitField.parameterValue = parameterValue;
        commandBitField.name = getParameterName(parameterType);
        commandBitField.parameterDefinition = getParameterDefinition(
            parameterType,
            parameterValue
        );
        index = index + commandBitFieldLength;
        parameter.push(commandBitField);
    }
    data.parameter = parameter;
    return data;
}

// type: 0x7 Rule Beacon
function decodeRuleBeacon(bytes) {
    var data = {};
    data.type = "RuleBeaconMessage";
    var deviceTypeId = bytes[0] & 0x03;
    if (deviceTypeId == 1) {
        data.deviceTypeId = "deviceTypeId1";
    } else if (deviceTypeId == 2) {
        data.deviceTypeId = "deviceTypeId2";
    } else if (deviceTypeId == 3) {
        data.deviceTypeId = "deviceTypeId3";
    }
    data.beaconCount = bytes[1] & 0x0f;
    for (let i = 0; i < data.beaconCount; i++) {
        var index = 2 + 5 * i;
        var major = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var minor = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] &
            0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var rssi = bytes[index + 4] - 256 + "dBm";
        data["beacon" + (i + 1)] = major + minor;
        data["rssi" + (i + 1)] = rssi;
    }
// wearState
    var wearIndex = data.beaconCount * 5 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x8 Distance
function decodeDistance(bytes) {
    var data = {};
    data.type = "Distance";
    data.uwbBeaconCount = bytes[1] & 0x0f;
// data.uwbBeaconCount = 3;
    for (let i = 0; i < data.uwbBeaconCount; i++) {
        var index = 2 + 7 * i;
        var dev = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var eui = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] &
            0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        data["majorminor" + (i + 1)] = dev + eui;
        data["distance" + (i + 1)] =
            (bytes[index + 4] << 16) | (bytes[index + 5] << 8) | bytes[index + 6];
    }
// wearState
    var wearIndex = data.uwbBeaconCount * 7 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// getParameterValue hexString
function getParameterValue(bytes, index, length) {
    var hexString = "";
    for (var i = 2; i <= length; i++) {
        var hex = (bytes[index + i] & 0xff).toString(16).toUpperCase();
        hexString += hex.padStart(2, "0");
    }
    return hexString;
}

// getCommandBitFieldLength
function getCommandBitFieldLength(parameterType) {
    let lengths = {
        0x00: 3,
        0x01: 3,
        0x02: 3,
        0x03: 3,
        0x04: 3,
        0x05: 2,
        0x06: 2,
        0x07: 2,
        0x08: 2,
        0x0a: 17,
        0x0b: 17,
        0x0e: 9,
        0x1e: 2,
        0x1f: 2,
        0x20: 2,
        0x29: 2,
        0x2a: 2,
        0x2b: 2,
        0x2e: 2,
        0x2f: 2,
        0x30: 2,
        0x31: 2,
    };
    return lengths[parameterType] ?? 0;
}

// Parameter Name
function getParameterName(parameterType) {
    let name = {
        0x00: "SoftwareVersion",
        0x01: "HBPeriod",
        0x02: "BlePositionReportInterval",
        0x03: "GNSSPositionReportInterval",
        0x04: "AssetBeaconReportInterval",
        0x05: "BlePositionReceivingDuration",
        0x06: "GNSSPositionReceivingDuration",
        0x07: "AssetBleReceivingDuration",
        0x08: "FallThreshold",
        0x0a: "PosBeaconUUID",
        0x0b: "AssetBeaconUUID",
        0x0e: "ISMI",
        0x1e: "FallDetectionAlarmEnable",
        0x1f: "SOSAlarmEnable",
        0x20: "PosMode",
        0x29: "AssetManagementEnable",
        0x2a: "GNSSFailureReportEnable", 0x2b: "AssetBeaconSortEnable",
        0x2e: "PowerSwitchEnable",
        0x2f: "NetworkStatusCheck",
        0x30: "GNSSEnableState",
        0x31: "BleEnable",
    };
    return name[parameterType] ?? "No matching parameter names";
}

// Parameter Definition
function getParameterDefinition(parameterType, parameterValue) {
    var val = parseInt(parameterValue, 16);
    let name = {
        0x00: "SoftwareVersion",
        0x01: val * 30 + "s, The interval of the heartbeat message, unit: 30s",
        0x02: val * 5 + "s, The interval of Bluetooth position repor, unit: 5s",
        0x03: val * 5 + "s, The interval of GNSS position report, unit: 5s",
        0x04: val * 5 + "s, The interval of asset beacons report, unit: 5s",
        0x05:
            val * 1 + "s, The duration of BLE position beacon receiving, unit: 1s",
        0x06: val * 5 + "s, The duration of GNSS position receiving, unit: 5s",
        0x07: val * 1 + "s, The duration of asset beacon receiving, unit 1s",
        0x08:
            val / 2 +
            "s, The threshold of the fall detection, the unit is 0.5 meters",
        0x0a: "PosBeaconUUIDFilter",
        0x0b: "AssetBeaconUUIDFilter",
        0x0e: "ISMI",
        0x1e: val == 0 ? "Disable" : "Enable",
        0x1f: val == 0 ? "Disable" : "Enable",
        0x20:
            val == 0
                ? "Period Mode"
                : val == 1
                    ? " Autonomous Mode"
                    : "On-demand Mode",
        0x29: val == 0 ? "Disable" : "Enable",
        0x2a: val == 0 ? "Disable" : "Enable",
        0x2b: val == 0 ? "Disable" : "Enable",
        0x2e: val == 0 ? "Disable" : "Enable",
        0x2f: val == 0 ? "Disable" : "Enable",
        0x30: val == 0 ? "Disable" : "Enable",
        0x31: val == 0 ? "Disable" : "Enable",
    };
    return name[parameterType] ?? "No matching parameter names";
}

// Floating point conversion
function hex2float(num) {
    var sign = num & 0x80000000 ? -1 : 1;
    var exponent = ((num >> 23) & 0xff) - 127;
    var mantissa = 1 + (num & 0x7fffff) / 0x7fffff;
    return sign * mantissa * Math.pow(2, exponent);
}

function asciiToHex(str) {
    var hexString = "";
    for (let i = 0; i < str.length; i++) {
        var hex = str.charCodeAt(i).toString(16);
        hexString += hex.padStart(2, "0");
    }
    return hexString;
}

function hexStringToByteArray(hexString) {
    var byteArray = [];
    for (let i = 0; i < hexString.length; i += 2) {
        byteArray.push(parseInt(hexString.substr(i, 2), 16));
    }
    return new Uint8Array(byteArray);
}

function timestampToTime(timestamp) {
    const date = new Date(timestamp);
    const year = date.getFullYear();
    const month = (date.getMonth() + 1).toString().padStart(2, "0");
    const day = date.getDate().toString().padStart(2, "0");
    const hour = date.getHours().toString().padStart(2, "0");
    const minute = date.getMinutes().toString().padStart(2, "0");
    const second = date.getSeconds().toString().padStart(2, "0");
    return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}

在ChirpStack上添加设备

  • 进入 应用 页面,点击 添加应用 按钮。

  • 输入名称并点击 提交 按钮。

  • 点击 添加设备

  • 使用设备信息填写必填字段,并指定之前创建设备配置。

  • 进入 Variables 选项卡,输入 ThingsBoardAccessToken 的值,点击 提交 按钮。

  • 应用密钥 填入对应字段,点击 提交 按钮保存设备。

配置应用与ThingsBoard的集成

  • 进入 集成 页面,找到并选择 ThingsBoard

  • 输入 ThingsBoard服务器URL,点击 提交 按钮。

在ThingsBoard上创建设备

  • 进入 设备 页面。

  • 点击 添加设备 按钮。

  • 填写设备名称并点击 下一步:凭证 按钮。

  • 输入设备 访问令牌(先前复制的 Device EUI 值)并点击 添加 按钮。

  • 点击设备打开设备信息窗口。

  • 进入 最新遥测数据 选项卡查看设备上报的数据。

在The Things Stack社区版上添加网关

我们需要在 The Things Stack社区版 上添加网关。

要添加网关,请按以下步骤操作:

  • 登录云端并打开控制台。

  • 进入 主页,点击“注册网关”按钮。

  • 填写网关信息(网关EUI)并点击“注册网关”按钮。

  • 网关已添加。您可看到其状态为已断开连接。

在The Things Stack社区版上添加设备

我们需要在 The Things Stack社区版 上添加设备。

要添加设备,请按以下步骤操作:

  • 进入 应用 页面。然后选择您的应用并点击其名称。

  • 点击 注册终端设备 按钮。

  • APP EUI 值填入 JoinEUI 字段。然后点击 确认 按钮。

  • 填写其余参数并点击 注册终端设备 按钮。

配置Payload Formatter

进入 Payload formatters 页面,为 Formatter type 选择 Custom Javascript formatter。粘贴解码函数并点击 Save changes 按钮。

解码函数:

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// UWB Badge Tracker decoder
function decodeUplink(input) {
// bytes
    var bytes = input.bytes;
// type
    var type = (bytes[0] >> 4) & 0x0f;
    switch (type) {
        case 0x01:
            return {data: decodeRegistration(bytes)};
        case 0x02:
            return {data: decodeHeartbeat(bytes)};
        case 0x03:
            return {data: decodeGNSSPosition(bytes)};
        case 0x04:
            return {data: decodeBeacon(bytes)};
        case 0x05:
            return {data: decodeAlarm(bytes)};
        case 0x06:
            return {data: decodeSingleConfigurationParameter(bytes)};
        case 0x07:
            return {data: decodeRuleBeacon(bytes)};
        case 0x08:
            return {data: decodeDistance(bytes)};
        default:
            return;
    }
}

// type: 0x1 Registration
function decodeRegistration(bytes) {
    var data = {};
    data.type = "RegistrationMessage";
    data.softwareVersion = ((bytes[1] << 8) & 0xff00) | (bytes[2] & 0xff);
    data.hardwareVersion = ((bytes[3] << 8) & 0xff00) | (bytes[4] & 0xff);
    data.deviceState =
        (bytes[5] << 24) | (bytes[6] << 16) | (bytes[7] << 8) | bytes[8];
    data.lorSynPeriod = bytes[9];
    data.lorSynFreq =
        (bytes[10] << 24) | (bytes[11] << 16) | (bytes[12] << 8) | bytes[13];
    data.lorSynDr = bytes[14];
    data.heartbeatPeriod = ((bytes[15] << 8) & 0xff00) | (bytes[16] & 0xff);
    data.messageId = ((bytes[17] << 8) & 0xff00) | (bytes[18] & 0xff);
    return data;
}

// type: 0x2 Heartbeat
function decodeHeartbeat(bytes) {
    var data = {};
    data.type = "HeartbeatMessage";
    data.deviceState =
        (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
    data.powerVol = bytes[5];
    data.powerPersent = bytes[6];
    data.uwbRangeCount = bytes[7];
    data.bleOnCount = bytes[8];
    data.gnssOnCount = bytes[9];
    data.temperature = ((bytes[10] << 8) & 0xff00) | (bytes[11] & 0xff);
    data.movementTime = ((bytes[12] << 8) & 0xff00) | (bytes[13] & 0xff);
    data.wearTime = ((bytes[14] << 8) & 0xff00) | (bytes[15] & 0xff);
    data.chargeTime = ((bytes[16] << 8) & 0xff00) | (bytes[17] & 0xff);
    data.messageId = ((bytes[18] << 8) & 0xff00) | (bytes[19] & 0xff);
    return data;
}

// type: 0x03 GNSSPosition
function decodeGNSSPosition(bytes) {
    var data = {};
    data.type = "GNSSPosition";
    data.gnssState =
        (bytes[0] & 0x01) === 0 ? "GNSS location success" : "GNSS location failed";
// longitude
    let longitude =
        (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
    data.longitude = hex2float(longitude);
// latitude
    let latitude =
        (bytes[5] << 24) | (bytes[6] << 16) | (bytes[7] << 8) | bytes[8];
    data.latitude = hex2float(latitude);// time
    let time =
        (bytes[9] << 24) | (bytes[10] << 16) | (bytes[11] << 8) | bytes[12];
    data.time = timestampToTime((time + 8 * 60 * 60) * 1000);
// wearState
    data.wearState = bytes[13] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x4 Beacon
function decodeBeacon(bytes) {
    var data = {};
    data.type = "BeaconMessage";
    data.beaconType =
        (bytes[0] & 0x01) === 0 ? "PositioningBeacon" : "AssetBeacon";
    data.beaconCount = bytes[1] & 0x0f;
    for (let i = 0; i < data.beaconCount; i++) {
        var index = 2 + 5 * i;
        var major = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var minor = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var rssi = bytes[index + 4] - 256 + "dBm";
        data["beacon" + (i + 1)] = major + minor;
        data["rssi" + (i + 1)] = rssi;
    }
// wearState
    var wearIndex = data.beaconCount * 5 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x5 Alarm
function decodeAlarm(bytes) {
    var data = {};
    data.type = "AlarmMessage";
    var alarmValue = bytes[1] & 0x0f;
    if (alarmValue == 1) {
        data.alarm = "SOS";
    } else if (alarmValue == 2) {
        data.alarm = "Fall";
    } else if (alarmValue == 3) {
        data.alarm = "Special area";
    } else if (alarmValue == 4) {
        data.alarm = "Search";
    } else if (alarmValue == 5) {
        data.alarm = "Magnet remove";
    }
    return data;
}

// type: 0x6 Single Configuration Parameter
function decodeSingleConfigurationParameter(bytes) {
    var data = {};
    data.type = "ConfigurationParameterResponse";
    var parameter = [];
    let byteLength = bytes.length;
    var index = 0;
    while (index + 1 < byteLength) {
        var commandBitField = {};
        var parameterType = bytes[index + 1] & 0xff;
        commandBitField.parameterType = parameterType;
        var commandBitFieldLength = getCommandBitFieldLength(parameterType);
        var parameterValue = getParameterValue(bytes, index, commandBitFieldLength);
        commandBitField.parameterValue = parameterValue;
        commandBitField.name = getParameterName(parameterType);
        commandBitField.parameterDefinition = getParameterDefinition(
            parameterType,
            parameterValue
        );
        index = index + commandBitFieldLength;
        parameter.push(commandBitField);
    }
    data.parameter = parameter;
    return data;
}

// type: 0x7 Rule Beacon
function decodeRuleBeacon(bytes) {
    var data = {};
    data.type = "RuleBeaconMessage";
    var deviceTypeId = bytes[0] & 0x03;
    if (deviceTypeId == 1) {
        data.deviceTypeId = "deviceTypeId1";
    } else if (deviceTypeId == 2) {
        data.deviceTypeId = "deviceTypeId2";
    } else if (deviceTypeId == 3) {
        data.deviceTypeId = "deviceTypeId3";
    }
    data.beaconCount = bytes[1] & 0x0f;
    for (let i = 0; i < data.beaconCount; i++) {
        var index = 2 + 5 * i;
        var major = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var minor = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var rssi = bytes[index + 4] - 256 + "dBm";
        data["beacon" + (i + 1)] = major + minor;
        data["rssi" + (i + 1)] = rssi;
    }// wearState
    var wearIndex = data.beaconCount * 5 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x8 Distance
function decodeDistance(bytes) {
    var data = {};
    data.type = "Distance";
    data.uwbBeaconCount = bytes[1] & 0x0f;
// data.uwbBeaconCount = 3;
    for (let i = 0; i < data.uwbBeaconCount; i++) {
        var index = 2 + 7 * i;
        var dev = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var eui = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        data["majorminor" + (i + 1)] = dev + eui;
        data["distance" + (i + 1)] =
            (bytes[index + 4] << 16) | (bytes[index + 5] << 8) | bytes[index + 6];
    }
// wearState
    var wearIndex = data.uwbBeaconCount * 7 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// getParameterValue hexString
function getParameterValue(bytes, index, length) {
    var hexString = "";
    for (var i = 2; i <= length; i++) {
        var hex = (bytes[index + i] & 0xff).toString(16).toUpperCase();
        hexString += hex.padStart(2, "0");
    }
    return hexString;
}

// getCommandBitFieldLength
function getCommandBitFieldLength(parameterType) {
    let lengths = {
        0x00: 3,
        0x01: 3,
        0x02: 3,
        0x03: 3,
        0x04: 3,
        0x05: 2,
        0x06: 2,
        0x07: 2, 0x08: 2,
        0x0a: 17,
        0x0b: 17,
        0x0e: 9,
        0x1e: 2,
        0x1f: 2,
        0x20: 2,
        0x29: 2,
        0x2a: 2,
        0x2b: 2,
        0x2e: 2,
        0x2f: 2,
        0x30: 2,
        0x31: 2,
    };
    return lengths[parameterType] ?? 0;
}

// Parameter Name
function getParameterName(parameterType) {
    let name = {
        0x00: "SoftwareVersion",
        0x01: "HBPeriod",
        0x02: "BlePositionReportInterval",
        0x03: "GNSSPositionReportInterval",
        0x04: "AssetBeaconReportInterval",
        0x05: "BlePositionReceivingDuration",
        0x06: "GNSSPositionReceivingDuration",
        0x07: "AssetBleReceivingDuration",
        0x08: "FallThreshold",
        0x0a: "PosBeaconUUID",
        0x0b: "AssetBeaconUUID",
        0x0e: "ISMI",
        0x1e: "FallDetectionAlarmEnable",
        0x1f: "SOSAlarmEnable",
        0x20: "PosMode",
        0x29: "AssetManagementEnable",
        0x2a: "GNSSFailureReportEnable",
        0x2b: "AssetBeaconSortEnable",
        0x2e: "PowerSwitchEnable",
        0x2f: "NetworkStatusCheck",
        0x30: "GNSSEnableState",
        0x31: "BleEnable",
    };
    return name[parameterType] ?? "No matching parameter names";
}

// Parameter Definition
function getParameterDefinition(parameterType, parameterValue) {
    var val = parseInt(parameterValue, 16);
    let name = {
        0x00: "SoftwareVersion",
        0x01: val * 30 + "s, The interval of the heartbeat message, unit: 30s",
        0x02: val * 5 + "s, The interval of Bluetooth position repor, unit: 5s",
        0x03: val * 5 + "s, The interval of GNSS position report, unit: 5s",
        0x04: val * 5 + "s, The interval of asset beacons report, unit: 5s", 0x05:
            val * 1 + "s, The duration of BLE position beacon receiving, unit: 1s",
        0x06: val * 5 + "s, The duration of GNSS position receiving, unit: 5s",
        0x07: val * 1 + "s, The duration of asset beacon receiving, unit 1s",
        0x08:
            val / 2 +
            "s, The threshold of the fall detection, the unit is 0.5 meters",
        0x0a: "PosBeaconUUIDFilter",
        0x0b: "AssetBeaconUUIDFilter",
        0x0e: "ISMI",
        0x1e: val == 0 ? "Disable" : "Enable",
        0x1f: val == 0 ? "Disable" : "Enable",
        0x20:
            val == 0
                ? "Period Mode"
                : val == 1
                    ? " Autonomous Mode"
                    : "On-demand Mode",
        0x29: val == 0 ? "Disable" : "Enable",
        0x2a: val == 0 ? "Disable" : "Enable",
        0x2b: val == 0 ? "Disable" : "Enable",
        0x2e: val == 0 ? "Disable" : "Enable",
        0x2f: val == 0 ? "Disable" : "Enable",
        0x30: val == 0 ? "Disable" : "Enable",
        0x31: val == 0 ? "Disable" : "Enable",
    };
    return name[parameterType] ?? "No matching parameter names";
}

// Floating point conversion
function hex2float(num) {
    var sign = num & 0x80000000 ? -1 : 1;
    var exponent = ((num >> 23) & 0xff) - 127;
    var mantissa = 1 + (num & 0x7fffff) / 0x7fffff;
    return sign * mantissa * Math.pow(2, exponent);
}

function asciiToHex(str) {
    var hexString = "";
    for (let i = 0; i < str.length; i++) {
        var hex = str.charCodeAt(i).toString(16);
        hexString += hex.padStart(2, "0");
    }
    return hexString;
}

function hexStringToByteArray(hexString) {
    var byteArray = [];
    for (let i = 0; i < hexString.length; i += 2) {
        byteArray.push(parseInt(hexString.substr(i, 2), 16));
    }
    return new Uint8Array(byteArray);
}

function timestampToTime(timestamp) {
    const date = new Date(timestamp);
    const year = date.getFullYear();
    const month = (date.getMonth() + 1).toString().padStart(2, "0");
    const day = date.getDate().toString().padStart(2, "0");
    const hour = date.getHours().toString().padStart(2, "0");
    const minute = date.getMinutes().toString().padStart(2, "0");
    const second = date.getSeconds().toString().padStart(2, "0");
    return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}

在ThingsBoard中创建集成

接下来,我们将在ThingsBoard中创建“The Things Stack”(TTS)集成。

首先,复制以下代码,创建上行数据转换器时需要用到:

1
2
3
4
5
6
7
8
9
10
11
var data = decodeToJson(payload);
var deviceName = data.end_device_ids.device_id;
var deviceType = data.end_device_ids.application_ids.application_id;

var result = {
  deviceName: deviceName,
  deviceType: deviceType,
  telemetry: data.uplink_message.decoded_payload
};

return result;

在“连接”步骤中,您需要以下参数:

  • Region(区域)eu1(在The Things Stack社区中注册应用所在区域)
  • Username(用户名)thingsboard-application-2025-06@ttn(使用TTS集成中的 用户名
  • Password(密码):使用The Things Stack社区集成中的 密码

现在,进入“集成中心”部分下的“集成”页面,按以下步骤操作:

  • 点击右上角的“加号”图标添加新集成。选择类型“The Things Stack社区”。然后点击“下一步”按钮。

  • 将先前复制的脚本粘贴到解码器函数区域。点击“下一步”按钮。

  • 将“下行数据转换器”字段留空。点击“跳过”按钮。

  • 填写您的参数,然后点击“添加”按钮。

在The Things Industries上添加网关

我们需要在 The Things Industries云 上添加网关。

要添加网关,请按以下步骤操作:

  • 登录云端并打开控制台。

  • 进入 主页,点击“注册网关”按钮。

  • 填写网关信息(网关EUI)并点击“注册网关”按钮。

  • 网关已添加。您可看到其状态为已断开连接。

在The Things Industries上添加设备

我们需要在 The Things Industries云 上添加设备。

要添加设备,请按以下步骤操作:

  • 进入 应用 页面。然后选择您的应用并点击其名称。

  • 点击 注册终端设备 按钮。

  • APP EUI 值填入 JoinEUI 字段。然后点击 确认 按钮。

  • 填写其余参数并点击 注册终端设备 按钮。

配置Payload Formatter

进入 Payload formatters 页面,将 Formatter type 选择为 Custom JavaScript formatter。将解码函数粘贴到编辑器中,点击 Save changes 按钮。

解码函数:

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
// UWB Badge Tracker decoder
function decodeUplink(input) {
// bytes
    var bytes = input.bytes;
// type
    var type = (bytes[0] >> 4) & 0x0f;
    switch (type) {
        case 0x01:
            return {data: decodeRegistration(bytes)};
        case 0x02:
            return {data: decodeHeartbeat(bytes)};
        case 0x03:
            return {data: decodeGNSSPosition(bytes)};
        case 0x04:
            return {data: decodeBeacon(bytes)};
        case 0x05:
            return {data: decodeAlarm(bytes)};
        case 0x06:
            return {data: decodeSingleConfigurationParameter(bytes)};
        case 0x07:
            return {data: decodeRuleBeacon(bytes)};
        case 0x08:
            return {data: decodeDistance(bytes)};
        default:
            return;
    }
}

// type: 0x1 Registration
function decodeRegistration(bytes) {
    var data = {};
    data.type = "RegistrationMessage";
    data.softwareVersion = ((bytes[1] << 8) & 0xff00) | (bytes[2] & 0xff);
    data.hardwareVersion = ((bytes[3] << 8) & 0xff00) | (bytes[4] & 0xff);
    data.deviceState =
        (bytes[5] << 24) | (bytes[6] << 16) | (bytes[7] << 8) | bytes[8];
    data.lorSynPeriod = bytes[9];
    data.lorSynFreq =
        (bytes[10] << 24) | (bytes[11] << 16) | (bytes[12] << 8) | bytes[13];
    data.lorSynDr = bytes[14];
    data.heartbeatPeriod = ((bytes[15] << 8) & 0xff00) | (bytes[16] & 0xff);
    data.messageId = ((bytes[17] << 8) & 0xff00) | (bytes[18] & 0xff);
    return data;
}

// type: 0x2 Heartbeat
function decodeHeartbeat(bytes) {
    var data = {};
    data.type = "HeartbeatMessage";
    data.deviceState =
        (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
    data.powerVol = bytes[5];
    data.powerPersent = bytes[6];
    data.uwbRangeCount = bytes[7];
    data.bleOnCount = bytes[8];
    data.gnssOnCount = bytes[9];
    data.temperature = ((bytes[10] << 8) & 0xff00) | (bytes[11] & 0xff);
    data.movementTime = ((bytes[12] << 8) & 0xff00) | (bytes[13] & 0xff);
    data.wearTime = ((bytes[14] << 8) & 0xff00) | (bytes[15] & 0xff);
    data.chargeTime = ((bytes[16] << 8) & 0xff00) | (bytes[17] & 0xff);
    data.messageId = ((bytes[18] << 8) & 0xff00) | (bytes[19] & 0xff);
    return data;
}

// type: 0x03 GNSSPosition
function decodeGNSSPosition(bytes) {
    var data = {};
    data.type = "GNSSPosition";
    data.gnssState =
        (bytes[0] & 0x01) === 0 ? "GNSS location success" : "GNSS location failed";
// longitude
    let longitude =
        (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
    data.longitude = hex2float(longitude);
// latitude
    let latitude =
        (bytes[5] << 24) | (bytes[6] << 16) | (bytes[7] << 8) | bytes[8];
    data.latitude = hex2float(latitude);
// timelet time =
    (bytes[9] << 24) | (bytes[10] << 16) | (bytes[11] << 8) | bytes[12];
    data.time = timestampToTime((time + 8 * 60 * 60) * 1000);
// wearState
    data.wearState = bytes[13] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x4 Beacon
function decodeBeacon(bytes) {
    var data = {};
    data.type = "BeaconMessage";
    data.beaconType =
        (bytes[0] & 0x01) === 0 ? "PositioningBeacon" : "AssetBeacon";
    data.beaconCount = bytes[1] & 0x0f;
    for (let i = 0; i < data.beaconCount; i++) {
        var index = 2 + 5 * i;
        var major = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var minor = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var rssi = bytes[index + 4] - 256 + "dBm";
        data["beacon" + (i + 1)] = major + minor;
        data["rssi" + (i + 1)] = rssi;
    }
// wearState
    var wearIndex = data.beaconCount * 5 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x5 Alarm
function decodeAlarm(bytes) {
    var data = {};
    data.type = "AlarmMessage";
    var alarmValue = bytes[1] & 0x0f;
    if (alarmValue == 1) {
        data.alarm = "SOS";
    } else if (alarmValue == 2) {
        data.alarm = "Fall";
    } else if (alarmValue == 3) {
        data.alarm = "Special area";
    } else if (alarmValue == 4) {
        data.alarm = "Search";
    } else if (alarmValue == 5) {
        data.alarm = "Magnet remove";
    }
    return data;
}// type: 0x6 Single Configuration Parameter
function decodeSingleConfigurationParameter(bytes) {
    var data = {};
    data.type = "ConfigurationParameterResponse";
    var parameter = [];
    let byteLength = bytes.length;
    var index = 0;
    while (index + 1 < byteLength) {
        var commandBitField = {};
        var parameterType = bytes[index + 1] & 0xff;
        commandBitField.parameterType = parameterType;
        var commandBitFieldLength = getCommandBitFieldLength(parameterType);
        var parameterValue = getParameterValue(bytes, index, commandBitFieldLength);
        commandBitField.parameterValue = parameterValue;
        commandBitField.name = getParameterName(parameterType);
        commandBitField.parameterDefinition = getParameterDefinition(
            parameterType,
            parameterValue
        );
        index = index + commandBitFieldLength;
        parameter.push(commandBitField);
    }
    data.parameter = parameter;
    return data;
}

// type: 0x7 Rule Beacon
function decodeRuleBeacon(bytes) {
    var data = {};
    data.type = "RuleBeaconMessage";
    var deviceTypeId = bytes[0] & 0x03;
    if (deviceTypeId == 1) {
        data.deviceTypeId = "deviceTypeId1";
    } else if (deviceTypeId == 2) {
        data.deviceTypeId = "deviceTypeId2";
    } else if (deviceTypeId == 3) {
        data.deviceTypeId = "deviceTypeId3";
    }
    data.beaconCount = bytes[1] & 0x0f;
    for (let i = 0; i < data.beaconCount; i++) {
        var index = 2 + 5 * i;
        var major = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var minor = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var rssi = bytes[index + 4] - 256 + "dBm";
        data["beacon" + (i + 1)] = major + minor;
        data["rssi" + (i + 1)] = rssi;
    }
// wearStatevar wearIndex = data.beaconCount * 5 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// type: 0x8 Distance
function decodeDistance(bytes) {
    var data = {};
    data.type = "Distance";
    data.uwbBeaconCount = bytes[1] & 0x0f;
// data.uwbBeaconCount = 3;
    for (let i = 0; i < data.uwbBeaconCount; i++) {
        var index = 2 + 7 * i;
        var dev = (((bytes[index] << 8) & 0xff00) | (bytes[index + 1] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        var eui = (((bytes[index + 2] << 8) & 0xff00) | (bytes[index + 3] & 0xff))
            .toString(16)
            .toUpperCase()
            .padStart(4, "0");
        data["majorminor" + (i + 1)] = dev + eui;
        data["distance" + (i + 1)] =
            (bytes[index + 4] << 16) | (bytes[index + 5] << 8) | bytes[index + 6];
    }
// wearState
    var wearIndex = data.uwbBeaconCount * 7 + 2;
    data.wearState = bytes[wearIndex] === 0 ? "no wear" : "wear";
    return data;
}

// getParameterValue hexString
function getParameterValue(bytes, index, length) {
    var hexString = "";
    for (var i = 2; i <= length; i++) {
        var hex = (bytes[index + i] & 0xff).toString(16).toUpperCase();
        hexString += hex.padStart(2, "0");
    }
    return hexString;
}

// getCommandBitFieldLength
function getCommandBitFieldLength(parameterType) {
    let lengths = {
        0x00: 3,
        0x01: 3,
        0x02: 3,
        0x03: 3,
        0x04: 3,
        0x05: 2,
        0x06: 2,
        0x07: 2,
        0x08: 2, 0x0a: 17,
        0x0b: 17,
        0x0e: 9,
        0x1e: 2,
        0x1f: 2,
        0x20: 2,
        0x29: 2,
        0x2a: 2,
        0x2b: 2,
        0x2e: 2,
        0x2f: 2,
        0x30: 2,
        0x31: 2,
    };
    return lengths[parameterType] ?? 0;
}

// Parameter Name
function getParameterName(parameterType) {
    let name = {
        0x00: "SoftwareVersion",
        0x01: "HBPeriod",
        0x02: "BlePositionReportInterval",
        0x03: "GNSSPositionReportInterval",
        0x04: "AssetBeaconReportInterval",
        0x05: "BlePositionReceivingDuration",
        0x06: "GNSSPositionReceivingDuration",
        0x07: "AssetBleReceivingDuration",
        0x08: "FallThreshold",
        0x0a: "PosBeaconUUID",
        0x0b: "AssetBeaconUUID",
        0x0e: "ISMI",
        0x1e: "FallDetectionAlarmEnable",
        0x1f: "SOSAlarmEnable",
        0x20: "PosMode",
        0x29: "AssetManagementEnable",
        0x2a: "GNSSFailureReportEnable",
        0x2b: "AssetBeaconSortEnable",
        0x2e: "PowerSwitchEnable",
        0x2f: "NetworkStatusCheck",
        0x30: "GNSSEnableState",
        0x31: "BleEnable",
    };
    return name[parameterType] ?? "No matching parameter names";
}

// Parameter Definition
function getParameterDefinition(parameterType, parameterValue) {
    var val = parseInt(parameterValue, 16);
    let name = {
        0x00: "SoftwareVersion",
        0x01: val * 30 + "s, The interval of the heartbeat message, unit: 30s",
        0x02: val * 5 + "s, The interval of Bluetooth position repor, unit: 5s",
        0x03: val * 5 + "s, The interval of GNSS position report, unit: 5s",
        0x04: val * 5 + "s, The interval of asset beacons report, unit: 5s",
        0x05: val * 1 + "s, The duration of BLE position beacon receiving, unit: 1s",
        0x06: val * 5 + "s, The duration of GNSS position receiving, unit: 5s",
        0x07: val * 1 + "s, The duration of asset beacon receiving, unit 1s",
        0x08:
            val / 2 +
            "s, The threshold of the fall detection, the unit is 0.5 meters",
        0x0a: "PosBeaconUUIDFilter",
        0x0b: "AssetBeaconUUIDFilter",
        0x0e: "ISMI",
        0x1e: val == 0 ? "Disable" : "Enable",
        0x1f: val == 0 ? "Disable" : "Enable",
        0x20:
            val == 0
                ? "Period Mode"
                : val == 1
                    ? " Autonomous Mode"
                    : "On-demand Mode",
        0x29: val == 0 ? "Disable" : "Enable",
        0x2a: val == 0 ? "Disable" : "Enable",
        0x2b: val == 0 ? "Disable" : "Enable",
        0x2e: val == 0 ? "Disable" : "Enable",
        0x2f: val == 0 ? "Disable" : "Enable",
        0x30: val == 0 ? "Disable" : "Enable",
        0x31: val == 0 ? "Disable" : "Enable",
    };
    return name[parameterType] ?? "No matching parameter names";
}

// Floating point conversion
function hex2float(num) {
    var sign = num & 0x80000000 ? -1 : 1;
    var exponent = ((num >> 23) & 0xff) - 127;
    var mantissa = 1 + (num & 0x7fffff) / 0x7fffff;
    return sign * mantissa * Math.pow(2, exponent);
}

function asciiToHex(str) {
    var hexString = "";
    for (let i = 0; i < str.length; i++) {
        var hex = str.charCodeAt(i).toString(16);
        hexString += hex.padStart(2, "0");
    }
    return hexString;
}

function hexStringToByteArray(hexString) {
    var byteArray = [];
    for (let i = 0; i < hexString.length; i += 2) {
        byteArray.push(parseInt(hexString.substr(i, 2), 16));
    }
    return new Uint8Array(byteArray);
}

function timestampToTime(timestamp) {
    const date = new Date(timestamp);
    const year = date.getFullYear();
    const month = (date.getMonth() + 1).toString().padStart(2, "0");
    const day = date.getDate().toString().padStart(2, "0");
    const hour = date.getHours().toString().padStart(2, "0");
    const minute = date.getMinutes().toString().padStart(2, "0");
    const second = date.getSeconds().toString().padStart(2, "0");
    return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}

在ThingsBoard中创建集成

接下来,我们将在 ThingsBoard 中创建“The Things Industries”集成。

首先,复制以下代码,创建上行数据转换器时需要用到:

1
2
3
4
5
6
7
8
9
10
11
var data = decodeToJson(payload);
var deviceName = data.end_device_ids.device_id;
var deviceType = data.end_device_ids.application_ids.application_id;

var result = {
  deviceName: deviceName,
  deviceType: deviceType,
  telemetry: data.uplink_message.decoded_payload
};

return result;

在“连接”步骤中,您需要以下参数:

  • Region(区域)eu1(在The Things Industries控制台中注册应用所在区域);
  • Username(用户名)thingsboard-application-2025-05@lansitec-testplan(使用The Things Stack Industries集成中的 用户名);
  • Password(密码):使用The Things Industries集成中的 密码

现在,进入“集成中心”部分下的“集成”页面,按以下步骤操作:

  • 点击右上角的“加号”图标添加新集成。选择类型“The Things Industries集成”。然后点击“下一步”按钮。

  • 将先前复制的脚本粘贴到解码器函数区域。点击“下一步”按钮。

  • 将“下行数据转换器”字段留空。点击“跳过”按钮。

  • 填写您的参数,然后点击“添加”按钮。

在Loriot上添加网关

我们需要在 Loriot 上添加网关。

要添加网关,请按以下步骤操作:

  • 登录Loriot服务器。在“网络”部分打开“示例网络”或创建新网络。

  • 点击“添加网关”按钮。

  • 向下滚动并选择“MultiTech Conduit AEP”。

  • 向上滚动,将网关的 MAC地址(从 网关EUI 中去掉中间的 FFFFFFFE 后得到)填入 eth0 MAC address 字段,将网关EUI填入 Custom EUI 字段。

  • 网关已添加。您可看到其状态为已断开连接。

在Loriot上添加设备

我们需要在 Loriot 上添加设备。

要添加设备,请按以下步骤操作:

  • 登录Loriot服务器。我们使用 eu2.loriot.io,具体取决于注册时选择的区域。

  • 在左侧菜单中进入“应用”页面。

  • 打开您的应用,我们的示例为“SampleApp”。

  • 进入“注册设备”页面。使用设备配置中的值填写字段,然后点击“注册”按钮。

在ThingsBoard中创建集成

接下来,我们将在ThingsBoard中创建与Loriot的集成。

首先,复制以下代码,创建上行数据转换器时需要用到:

1
2
3
4
5
6
7
8
9
10
11
var data = decodeToJson(payload);
var deviceName = data.end_device_ids.device_id;
var deviceType = data.end_device_ids.application_ids.application_id;

var result = {
  deviceName: deviceName,
  deviceType: deviceType,
  telemetry: data.uplink_message.decoded_payload
};

return result;

现在,进入“集成中心”部分下的“集成”页面,按以下步骤操作:

  • 点击右上角的“加号”图标按钮添加新集成。选择类型“Loriot”。然后点击“下一步”按钮。

  • 将先前复制的脚本粘贴到解码器函数区域。点击“下一步”按钮。

  • 将“下行数据转换器”字段留空。点击“跳过”按钮。

  • 填写您的参数,然后点击“添加”按钮。

在ThingsBoard上检查数据

设备已添加后,若其发送了数据,应会显示在设备中。 要检查数据,您可以在 实体 部分打开 设备 页面。设备应出现在设备列表中。您可以通过点击设备,然后打开 属性最新遥测数据 选项卡来查看数据。

为了获得更友好的视图,您可以使用仪表板。 您可以为此设备下载简单仪表板,其已配置为显示名称为“Devices”的设备的“latitude”和“longitude”遥测键数据。

ThingsBoard支持创建和自定义用于监控和管理数据与设备的交互式可视化(仪表板)。 通过ThingsBoard仪表板,您可以高效地管理和监控IoT设备及数据。接下来,我们将为设备创建仪表板。

要将仪表板添加到ThingsBoard,需要导入它。导入仪表板请按以下步骤操作:

  • 进入“仪表板”页面。默认会进入仪表板组“全部”。点击右上角的“加号”图标。选择“导入仪表板”。

  • 在仪表板导入窗口中,上传JSON文件并点击“导入”按钮。

  • 仪表板已导入。

要打开已导入的仪表板,请点击它。然后需要在仪表板的实体别名中指定您的设备。

请按以下步骤操作:

  • 打开仪表板并进入编辑模式。点击“实体别名”图标,在弹出窗口中点击别名旁的“编辑别名”图标。

  • 在编辑别名窗口中从下拉列表选择您的设备并保存实体别名。

  • 应用所有更改。

此时您应能看到来自设备的数据。

带数据的仪表板示例:

结论

借助本指南所述内容,您可以轻松连接资产管理追踪器并向ThingsBoard发送数据。

浏览平台文档以了解更多核心概念和功能。例如,配置告警规则仪表板

发现即插即用硬件,助力您的解决方案
合作伙伴图标