目录
如何将 Badge Tracker 连接至 ThingsBoard?
Hardware type: Trackers
Connectivity: LoRaWAN
Industry: Security, Smart Buildings
Use cases: Fleet Tracking , Health Care
Platforms:
Community Edition,
Professional Edition,
Cloud
Badge Tracker achieve seamless personnel and asset tracking with the Lansitec Badge Tracker.
Combining GNSS, Bluetooth 5.0, and LoRaWAN technology, this sleek device provides accurate real-time positioning both indoors and outdoors—perfect for managing workforce, visitors, and critical assets across large facilities.
The built-in 3-axis accelerometer intelligently detects movement or falls, conserving battery when the device is idle and alerting you to unauthorized or emergency situations.
With a maximum of five months standby time and no additional network fees, the Badge Tracker offers a cost-effective, high-precision solution for securing sensitive areas, optimizing resource usage, and improving operational workflows.
前置条件
继续本指南前,需准备以下内容:
配置
仅当该设备通过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
// Decode uplink function.
//
// Input is an object with the following fields:
// - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
// - fPort = Uplink fPort.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - data = Object representing the decoded payload.
// Badge Tracker decoder
function decodeUplink ( input ) {
// bytes
var bytes = input . bytes ;
// type
var uplinkType = ( bytes [ 0 ] >> 4 ) & 0x0f ;
switch ( uplinkType ) {
case 0x01 :
return { data : decodeRegistration ( bytes )};
case 0x02 :
return { data : decodeHeartbeat ( bytes )};
case 0x03 :
return { data : decodeGNSSPosition ( bytes )};
case 0x07 :
return { data : decodeBeacon ( bytes )};
case 0x08 :
return { data : decodeAlarm ( bytes )};
default :
return null ;
}
}
// type: 0x01 Registration
function decodeRegistration ( bytes ) {
var data = {};
data . type = " Registration " ;
data . adr = (( bytes [ 0 ] >> 3 ) & 0x1 ) == 0 ? " OFF " : " ON " ;
data . power = (( bytes [ 2 ] >> 3 ) & 0x1f ) + " dBm " ;
data . dr = ( bytes [ 3 ] >> 4 ) & 0x0f ;
data . gnssEnable = (( bytes [ 3 ] >> 3 ) & 0x01 ) == 0 ? " Disable " : " Enable " ;
var positionModeValue = ( bytes [ 3 ] >> 1 ) & 0x03 ;
if ( positionModeValue == 0 ) {
data . positionMode = " Period " ;
} else if ( positionModeValue == 1 ) {
data . positionMode = " Autonomous " ;
} else if ( positionModeValue == 2 ) {
data . positionMode = " Demand " ;
}
data . bleEnable = ( bytes [ 3 ] & 0x01 ) == 0 ? " Disable " : " Enable " ;
data . blePositionReportInterval =
((( bytes [ 4 ] << 8 ) & 0xff00 ) | ( bytes [ 5 ] & 0xff )) * 5 + " s " ;
data . gnssPositionReportInterval =
((( bytes [ 6 ] << 8 ) & 0xff00 ) | ( bytes [ 7 ] & 0xff )) * 5 + " s " ;
data . heartbeatReportInterval = ( bytes [ 8 ] & 0xff ) * 30 + " s " ;
data . version =
( bytes [ 9 ] & 0xff ). toString ( 16 ). toUpperCase () +
" . " +
( bytes [ 10 ] & 0xff ). toString ( 16 ). toUpperCase ();
data . cfmsg = " 1 Confirmed every " + ( bytes [ 11 ] & 0xff ) + " Heartbeat " ;
data . hbCount = " Disconnect Judgement " + ( bytes [ 12 ] & 0xff );
data . fallDetection = ( bytes [ 13 ] & 0xff ) * 0.5 + " meters " ;
return data ;
}
// type: 0x02 Heartbeat
function decodeHeartbeat ( bytes ) {
var data = {};
data . type = " Heartbeat " ;
data . battery = bytes [ 1 ] + " % " ;
data . rssi = bytes [ 2 ] * - 1 + " dBm " ;
data . snr = ((( bytes [ 3 ] << 8 ) & 0xff00 ) | ( bytes [ 4 ] & 0xff )) / 100 + " dB " ;
var gnssStateValue = ( bytes [ 5 ] >> 4 ) & 0x0f ;
if ( gnssStateValue == 0 ) {
data . gnssState = " Off " ;
} else if ( gnssStateValue == 1 ) {
data . gnssState = " Boot GNSS " ;
} else if ( gnssStateValue == 2 ) {
data . gnssState = " Locating " ;
} else if ( gnssStateValue == 3 ) {
data . gnssState = " Located " ;
} else if ( gnssStateValue == 9 ) {
data . gnssState = " No signal " ;
}
data . moveState = bytes [ 5 ] & 0x0f ;
var chargeStateValue = ( bytes [ 6 ] >> 4 ) & 0x0f ;
if ( chargeStateValue == 0 ) {
data . chargeState = " Power cable disconnected " ;
} else if ( chargeStateValue == 5 ) {
data . chargeState = " Charging " ;
} else if ( chargeStateValue == 6 ) {
data . chargeState = " Charge complete " ;
}
return data ;
}
// type: 0x03 GNSSPosition
function decodeGNSSPosition ( bytes ) {
var data = {};
data . type = " GNSSPosition " ;
// 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 );
return data ;
}
// type: 0x07 Beacon
function decodeBeacon ( bytes ) {
const data = {
type : " Beacon " ,
length : bytes [ 0 ] & 0x0f ,
};
for ( let i = 0 ; i < data . length ; i ++ ) {
const index = 6 + 5 * i ;
const major = (( bytes [ index ] << 8 ) | bytes [ index + 1 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
const minor = (( bytes [ index + 2 ] << 8 ) | bytes [ index + 3 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
const rssi = bytes [ index + 4 ] - 256 + " dBm " ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
}
return data ;
}
// type: 0x08 Alarm
function decodeAlarm ( bytes ) {
var data = {};
data . type = " Alarm " ;
var alarmValue = bytes [ 1 ] & 0xff ;
if ( alarmValue === 1 ) {
data . alarm = " SOS " ;
} else if ( alarmValue === 2 ) {
data . alarm = " Fall " ;
} else {
data . alarm = " Unknown " ;
}
return data ;
}
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 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 的值,点击 提交 按钮。
将 应用密钥 填入对应字段,点击 提交 按钮保存设备。
进入 应用 页面,点击 添加应用 按钮。
输入名称并点击 提交 按钮。
点击 添加设备 。
使用设备信息填写必填字段,并指定之前创建设备配置。
进入 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 formatters 页面,为 Formatter type 选择 Custom Javascript formatter 。粘贴解码函数并点击 Save changes 按钮。
进入 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
// Decode uplink function.
//
// Input is an object with the following fields:
// - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
// - fPort = Uplink fPort.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - data = Object representing the decoded payload.
// Badge Tracker decoder
function decodeUplink ( input ) {
// bytes
var bytes = input . bytes ;
// type
var uplinkType = ( bytes [ 0 ] >> 4 ) & 0x0f ;
switch ( uplinkType ) {
case 0x01 :
return { data : decodeRegistration ( bytes )};
case 0x02 :
return { data : decodeHeartbeat ( bytes )};
case 0x03 :
return { data : decodeGNSSPosition ( bytes )};
case 0x07 :
return { data : decodeBeacon ( bytes )};
case 0x08 :
return { data : decodeAlarm ( bytes )};
default :
return null ;
}
}
// type: 0x01 Registration
function decodeRegistration ( bytes ) {
var data = {};
data . type = " Registration " ;
data . adr = (( bytes [ 0 ] >> 3 ) & 0x1 ) == 0 ? " OFF " : " ON " ;
data . power = (( bytes [ 2 ] >> 3 ) & 0x1f ) + " dBm " ;
data . dr = ( bytes [ 3 ] >> 4 ) & 0x0f ;
data . gnssEnable = (( bytes [ 3 ] >> 3 ) & 0x01 ) == 0 ? " Disable " : " Enable " ;
var positionModeValue = ( bytes [ 3 ] >> 1 ) & 0x03 ;
if ( positionModeValue == 0 ) {
data . positionMode = " Period " ;
} else if ( positionModeValue == 1 ) {
data . positionMode = " Autonomous " ;
} else if ( positionModeValue == 2 ) {
data . positionMode = " Demand " ;
}
data . bleEnable = ( bytes [ 3 ] & 0x01 ) == 0 ? " Disable " : " Enable " ;
data . blePositionReportInterval =
((( bytes [ 4 ] << 8 ) & 0xff00 ) | ( bytes [ 5 ] & 0xff )) * 5 + " s " ;
data . gnssPositionReportInterval =
((( bytes [ 6 ] << 8 ) & 0xff00 ) | ( bytes [ 7 ] & 0xff )) * 5 + " s " ;
data . heartbeatReportInterval = ( bytes [ 8 ] & 0xff ) * 30 + " s " ;
data . version =
( bytes [ 9 ] & 0xff ). toString ( 16 ). toUpperCase () +
" . " +
( bytes [ 10 ] & 0xff ). toString ( 16 ). toUpperCase ();
data . cfmsg = " 1 Confirmed every " + ( bytes [ 11 ] & 0xff ) + " Heartbeat " ;
data . hbCount = " Disconnect Judgement " + ( bytes [ 12 ] & 0xff );
data . fallDetection = ( bytes [ 13 ] & 0xff ) * 0.5 + " meters " ;
return data ;
}
// type: 0x02 Heartbeat
function decodeHeartbeat ( bytes ) {
var data = {};
data . type = " Heartbeat " ;
data . battery = bytes [ 1 ] + " % " ;
data . rssi = bytes [ 2 ] * - 1 + " dBm " ;
data . snr = ((( bytes [ 3 ] << 8 ) & 0xff00 ) | ( bytes [ 4 ] & 0xff )) / 100 + " dB " ;
var gnssStateValue = ( bytes [ 5 ] >> 4 ) & 0x0f ;
if ( gnssStateValue == 0 ) {
data . gnssState = " Off " ;
} else if ( gnssStateValue == 1 ) {
data . gnssState = " Boot GNSS " ;
} else if ( gnssStateValue == 2 ) {
data . gnssState = " Locating " ;
} else if ( gnssStateValue == 3 ) {
data . gnssState = " Located " ;
} else if ( gnssStateValue == 9 ) {
data . gnssState = " No signal " ;
}
data . moveState = bytes [ 5 ] & 0x0f ;
var chargeStateValue = ( bytes [ 6 ] >> 4 ) & 0x0f ;
if ( chargeStateValue == 0 ) {
data . chargeState = " Power cable disconnected " ;
} else if ( chargeStateValue == 5 ) {
data . chargeState = " Charging " ;
} else if ( chargeStateValue == 6 ) {
data . chargeState = " Charge complete " ;
}
return data ;
}
// type: 0x03 GNSSPosition
function decodeGNSSPosition ( bytes ) {
var data = {};
data . type = " GNSSPosition " ;
// 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 );
return data ;
}
// type: 0x07 Beacon
function decodeBeacon ( bytes ) {
const data = {
type : " Beacon " ,
length : bytes [ 0 ] & 0x0f ,
};
for ( let i = 0 ; i < data . length ; i ++ ) {
const index = 6 + 5 * i ;
const major = (( bytes [ index ] << 8 ) | bytes [ index + 1 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
const minor = (( bytes [ index + 2 ] << 8 ) | bytes [ index + 3 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
const rssi = bytes [ index + 4 ] - 256 + " dBm " ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
}
return data ;
}
// type: 0x08 Alarm
function decodeAlarm ( bytes ) {
var data = {};
data . type = " Alarm " ;
var alarmValue = bytes [ 1 ] & 0xff ;
if ( alarmValue === 1 ) {
data . alarm = " SOS " ;
} else if ( alarmValue === 2 ) {
data . alarm = " Fall " ;
} else {
data . alarm = " Unknown " ;
}
return data ;
}
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 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 formatters 页面,将 Formatter type 选择为 Custom JavaScript formatter 。将解码函数粘贴到编辑器中,点击 Save changes 按钮。
进入 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
// Decode uplink function.
//
// Input is an object with the following fields:
// - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
// - fPort = Uplink fPort.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - data = Object representing the decoded payload.
// Badge Tracker decoder
function decodeUplink ( input ) {
// bytes
var bytes = input . bytes ;
// type
var uplinkType = ( bytes [ 0 ] >> 4 ) & 0x0f ;
switch ( uplinkType ) {
case 0x01 :
return { data : decodeRegistration ( bytes )};
case 0x02 :
return { data : decodeHeartbeat ( bytes )};
case 0x03 :
return { data : decodeGNSSPosition ( bytes )};
case 0x07 :
return { data : decodeBeacon ( bytes )};
case 0x08 :
return { data : decodeAlarm ( bytes )};
default :
return null ;
}
}
// type: 0x01 Registration
function decodeRegistration ( bytes ) {
var data = {};
data . type = " Registration " ;
data . adr = (( bytes [ 0 ] >> 3 ) & 0x1 ) == 0 ? " OFF " : " ON " ;
data . power = (( bytes [ 2 ] >> 3 ) & 0x1f ) + " dBm " ;
data . dr = ( bytes [ 3 ] >> 4 ) & 0x0f ;
data . gnssEnable = (( bytes [ 3 ] >> 3 ) & 0x01 ) == 0 ? " Disable " : " Enable " ;
var positionModeValue = ( bytes [ 3 ] >> 1 ) & 0x03 ;
if ( positionModeValue == 0 ) {
data . positionMode = " Period " ;
} else if ( positionModeValue == 1 ) {
data . positionMode = " Autonomous " ;
} else if ( positionModeValue == 2 ) {
data . positionMode = " Demand " ;
}
data . bleEnable = ( bytes [ 3 ] & 0x01 ) == 0 ? " Disable " : " Enable " ;
data . blePositionReportInterval =
((( bytes [ 4 ] << 8 ) & 0xff00 ) | ( bytes [ 5 ] & 0xff )) * 5 + " s " ;
data . gnssPositionReportInterval =
((( bytes [ 6 ] << 8 ) & 0xff00 ) | ( bytes [ 7 ] & 0xff )) * 5 + " s " ;
data . heartbeatReportInterval = ( bytes [ 8 ] & 0xff ) * 30 + " s " ;
data . version =
( bytes [ 9 ] & 0xff ). toString ( 16 ). toUpperCase () +
" . " +
( bytes [ 10 ] & 0xff ). toString ( 16 ). toUpperCase ();
data . cfmsg = " 1 Confirmed every " + ( bytes [ 11 ] & 0xff ) + " Heartbeat " ;
data . hbCount = " Disconnect Judgement " + ( bytes [ 12 ] & 0xff );
data . fallDetection = ( bytes [ 13 ] & 0xff ) * 0.5 + " meters " ;
return data ;
}
// type: 0x02 Heartbeat
function decodeHeartbeat ( bytes ) {
var data = {};
data . type = " Heartbeat " ;
data . battery = bytes [ 1 ] + " % " ;
data . rssi = bytes [ 2 ] * - 1 + " dBm " ;
data . snr = ((( bytes [ 3 ] << 8 ) & 0xff00 ) | ( bytes [ 4 ] & 0xff )) / 100 + " dB " ;
var gnssStateValue = ( bytes [ 5 ] >> 4 ) & 0x0f ;
if ( gnssStateValue == 0 ) {
data . gnssState = " Off " ;
} else if ( gnssStateValue == 1 ) {
data . gnssState = " Boot GNSS " ;
} else if ( gnssStateValue == 2 ) {
data . gnssState = " Locating " ;
} else if ( gnssStateValue == 3 ) {
data . gnssState = " Located " ;
} else if ( gnssStateValue == 9 ) {
data . gnssState = " No signal " ;
}
data . moveState = bytes [ 5 ] & 0x0f ;
var chargeStateValue = ( bytes [ 6 ] >> 4 ) & 0x0f ;
if ( chargeStateValue == 0 ) {
data . chargeState = " Power cable disconnected " ;
} else if ( chargeStateValue == 5 ) {
data . chargeState = " Charging " ;
} else if ( chargeStateValue == 6 ) {
data . chargeState = " Charge complete " ;
}
return data ;
}
// type: 0x03 GNSSPosition
function decodeGNSSPosition ( bytes ) {
var data = {};
data . type = " GNSSPosition " ;
// 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 );
return data ;
}
// type: 0x07 Beacon
function decodeBeacon ( bytes ) {
const data = {
type : " Beacon " ,
length : bytes [ 0 ] & 0x0f ,
};
for ( let i = 0 ; i < data . length ; i ++ ) {
const index = 6 + 5 * i ;
const major = (( bytes [ index ] << 8 ) | bytes [ index + 1 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
const minor = (( bytes [ index + 2 ] << 8 ) | bytes [ index + 3 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
const rssi = bytes [ index + 4 ] - 256 + " dBm " ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
}
return data ;
}
// type: 0x08 Alarm
function decodeAlarm ( bytes ) {
var data = {};
data . type = " Alarm " ;
var alarmValue = bytes [ 1 ] & 0xff ;
if ( alarmValue === 1 ) {
data . alarm = " SOS " ;
} else if ( alarmValue === 2 ) {
data . alarm = " Fall " ;
} else {
data . alarm = " Unknown " ;
}
return data ;
}
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 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 中去掉中间的 FFFF 或 FFFE 后得到)填入 eth0 MAC address 字段,将网关EUI填入 Custom EUI 字段。
网关已添加。您可看到其状态为已断开连接。
登录Loriot服务器。在“网络 ”部分打开“示例网络 ”或创建新网络。
点击“添加网关 ”按钮。
向下滚动并选择“MultiTech Conduit AEP ”。
向上滚动,将网关的 MAC地址 (从 网关EUI 中去掉中间的 FFFF 或 FFFE 后得到)填入 eth0 MAC address 字段,将网关EUI填入 Custom EUI 字段。
网关已添加。您可看到其状态为已断开连接。
在Loriot上添加设备
我们需要在 Loriot 上添加设备。
要添加设备,请按以下步骤操作:
登录Loriot服务器。我们使用 eu2.loriot.io ,具体取决于注册时选择的区域。
在左侧菜单中进入“应用 ”页面。
打开您的应用,我们的示例为“SampleApp ”。
进入“注册设备 ”页面。使用设备配置中的值填写字段,然后点击“注册 ”按钮。
登录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发送数据。
浏览平台文档 以了解更多核心概念和功能。例如,配置告警规则 或仪表板 。