On this page
Solar Bluetooth Gateway
Solar Bluetooth Gateway is designed based on LoRaWAN and Bluetooth 5.0 technology. It receives nearby Bluetooth beacon messages, and then transmit to a LoRaWAN gateways through LoRaWAN after restructure the data. It is powered with robust solar film and a 5300mAh rechargeable battery.
Prerequisites
To continue with this guide we will need the following:
Configuration
To create integration with the network server Chirpstack
Add a LoRaWAN Gateway on Chirpstack
First, we need to add a LoRaWAN gateway on the Chirpstack .
Step to add a gateway:
Log in to Chirpstack server. Go to the Gateways page and click the Add gateway button.
Enter the Name , Gateway ID (found in the gateway control panel), then scroll down and click the Submit button.
Once added, you can view the gateway's status under the Gateways tab.
Log in to Chirpstack server. Go to the Gateways page and click the Add gateway button.
Enter the Name , Gateway ID (found in the gateway control panel), then scroll down and click the Submit button.
Once added, you can view the gateway's status under the Gateways tab.
To connect the gateway and transmit data to ChirpStack, follow these steps:
Open the gateway control panel. Go to the Network Settings page and set the Mode to Packet Forwarder . Enter your server address in the Server Address field (e.g., 192.168.31.11 in our case), then click Save & Apply button.
Verify the gateway's status on ChirpStack, it should now appear online.
Open the gateway control panel. Go to the Network Settings page and set the Mode to Packet Forwarder . Enter your server address in the Server Address field (e.g., 192.168.31.11 in our case), then click Save & Apply button.
Verify the gateway's status on ChirpStack, it should now appear online.
Add a device profile on Chirpstack
In Chirpstack UI, go to the Device Profiles page and click Add device profile button.
Fill in the required fields.
Navigate to the Codec tab, select “JavaScript functions”, paste the Solar Bluetooth Gateway decoder , and click Submit .
In Chirpstack UI, go to the Device Profiles page and click Add device profile button.
Fill in the required fields.
Navigate to the Codec tab, select “JavaScript functions”, paste the Solar Bluetooth Gateway decoder , and click Submit .
Solar Bluetooth Gateway 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
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
// 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.
// Solar Bluetooth Gateway 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 : decodeDeviceReportRule ( bytes ) };
case 0x05 :
return { data : decodeWaterLevelDetection ( bytes ) };
case 0x08 :
return { data : decodeDeviceType1 ( bytes ) };
case 0x09 :
return { data : decodeDeviceType2 ( bytes ) };
case 0x0a :
return { data : decodeDeviceType3 ( bytes ) };
case 0x0e :
return { data : decodeMultiDeviceTypeMessage ( bytes ) };
case 0x0f :
return { data : decodeAcknowledgment ( bytes ) };
default :
return null ;
}
}
// type: 0x1 Registration
function decodeRegistration ( bytes ) {
var data = {};
data . type = " Registration " ;
// adr
data . adr = (( bytes [ 0 ] >> 3 ) & 0x1 ) == 0 ? " OFF " : " ON " ;
// mode
data . mode = bytes [ 0 ] & 0x07 ;
// loRaWANBand
var loRaWANBandValue = bytes [ 1 ];
if ( loRaWANBandValue == 0x00 ) {
data . loRaWANBand = " KR920 " ;
} else if ( loRaWANBandValue == 0x01 ) {
data . loRaWANBand = " AU915 " ;
} else if ( loRaWANBandValue == 0x04 ) {
data . loRaWANBand = " CN470 " ;
} else if ( loRaWANBandValue == 0x08 ) {
data . loRaWANBand = " AS923 " ;
} else if ( loRaWANBandValue == 0x10 ) {
data . loRaWANBand = " EU433 " ;
} else if ( loRaWANBandValue == 0x20 ) {
data . loRaWANBand = " EU868 " ;
} else if ( loRaWANBandValue == 0x40 ) {
data . loRaWANBand = " US915 " ;
}
// power
data . power = (( bytes [ 2 ] >> 3 ) & 0x1f ) + " dBm " ;
// continuousBleReceiveEnable
data . continuousBleReceiveEnable =
(( bytes [ 2 ] >> 1 ) & 0x1 ) == 0 ? " Disable " : " Enable " ;
// dr
data . dr = ( bytes [ 3 ] >> 4 ) & 0x0f ;
// positionReportInterval
data . positionReportInterval =
((( bytes [ 4 ] << 8 ) & 0xff00 ) | ( bytes [ 5 ] & 0xff )) * 5 + " s " ;
// heartbeatInterval
data . heartbeatInterval = bytes [ 6 ] * 30 + " s " ;
// bleReceivingDuration
data . bleReceivingDuration = bytes [ 7 ] + " s " ;
// networkReconnectionInterval
data . networkReconnectionInterval = bytes [ 8 ] * 5 + " min " ;
return data ;
}
// type: 0x2 Heartbeat
function decodeHeartbeat ( bytes ) {
var data = {};
// type
data . type = " Heartbeat " ;
// battery
var batteryValue = bytes [ 1 ];
if ( batteryValue > 100 ) {
data . battery = bytes [ 1 ] / 100 + 1.5 + " V " ;
} else {
data . battery = bytes [ 1 ] + " % " ;
}
// rssi
data . rssi = bytes [ 2 ] * - 1 + " dBm " ;
// snr
data . snr = ((( bytes [ 3 ] << 8 ) & 0xff00 ) | ( bytes [ 4 ] & 0xff )) / 100 + " dB " ;
// version
data . version = (( bytes [ 5 ] << 8 ) & 0xff00 ) | ( bytes [ 6 ] & 0xff );
// chargeState
var chargeStateValue = bytes [ 7 ] & 0xff ;
if ( chargeStateValue == 0x00 ) {
data . chargeState = " Not charging " ;
} else if ( chargeStateValue == 0x50 ) {
data . chargeState = " Charging " ;
} else if ( chargeStateValue == 0x60 ) {
data . chargeState = " Charging completed " ;
}
return data ;
}
// type: 0x3 DeviceReportRule
function decodeDeviceReportRule ( bytes ) {
var data = {};
data . type = " DeviceReportRule " ;
data . deviceTypeQuantity = bytes [ 1 ] & 0xff ;
data . deviceTypeId = ( bytes [ 2 ] >> 4 ) & 0x0f ;
data . filterAndDataBlockQuantity = bytes [ 2 ] & 0x0f ;
var filterBlock = [];
var dataBlock = [];
var macBlock = [];
var index = 3 ;
for ( let i = 0 ; i < data . filterAndDataBlockQuantity ; i ++ ) {
var ruleType = bytes [ index ++ ] & 0xff ;
var startAddress = bytes [ index ++ ] & 0xff ;
var endAddress = bytes [ index ++ ] & 0xff ;
var filter = {};
if ( ruleType == 1 ) {
filter . ruleType = " FilterBlock " ;
filter . startAddress = byteToHex ( startAddress );
filter . endAddress = byteToHex ( endAddress );
var len = endAddress - startAddress ;
var filterValue = "" ;
for ( let j = 0 ; j < len + 1 ; j ++ ) {
filterValue += byteToHex ( bytes [ index + j ]);
}
filter . value = filterValue ;
index = index + ( endAddress - startAddress + 1 );
filterBlock . push ( filter );
} else if ( ruleType == 2 ) {
filter . ruleType = " DataBlock " ;
filter . startAddress = byteToHex ( startAddress );
filter . endAddress = byteToHex ( endAddress );
dataBlock . push ( filter );
} else if ( ruleType == 3 ) {
filter . ruleType = " MACBlock " ;
filter . startAddress = byteToHex ( startAddress );
filter . endAddress = byteToHex ( endAddress );
macBlock . push ( filter );
}
}
data . filterBlock = filterBlock ;
data . dataBlock = dataBlock ;
data . macBlock = macBlock ;
return data ;
}
// type: 0x5 WaterLevelDetection
function decodeWaterLevelDetection ( bytes ) {
var data = {};
// type
data . type = " WaterLevelDetection " ;
data . waterLevel = ((( bytes [ 1 ] << 8 ) & 0xff00 ) | ( bytes [ 2 ] & 0xff )) + " mm " ;
return data ;
}
// type: 0x8 DeviceType1
function decodeDeviceType1 ( bytes ) {
var data = {
type : " DeviceType1 " ,
number : bytes [ 0 ] & 0x0f ,
};
var index = 1 ;
for ( let i = 0 ; i < data . number ; i ++ ) {
var major = (( bytes [ index ] << 8 ) | bytes [ index + 1 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var minor = (( bytes [ index + 2 ] << 8 ) | bytes [ index + 3 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var rssi = bytes [ index + 4 ] - 256 + " dBm " ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
index = index + 5 ;
}
return data ;
}
// type: 0x9 DeviceType2
function decodeDeviceType2 ( bytes ) {
var data = {
type : " DeviceType2 " ,
number : bytes [ 0 ] & 0x0f ,
};
var index = 1 ;
for ( let i = 0 ; i < data . number ; i ++ ) {
var major = (( bytes [ index ] << 8 ) | bytes [ index + 1 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var minor = (( bytes [ index + 2 ] << 8 ) | bytes [ index + 3 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var rssi = bytes [ index + 4 ] - 256 + " dBm " ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
index = index + 5 ;
}
return data ;
}
// type: 0xa DeviceType3
function decodeDeviceType3 ( bytes ) {
var data = {
type : " DeviceType3 " ,
number : bytes [ 0 ] & 0x0f ,
};
var index = 1 ;
for ( let i = 0 ; i < data . number ; i ++ ) {
var major = (( bytes [ index ] << 8 ) | bytes [ index + 1 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var minor = (( bytes [ index + 2 ] << 8 ) | bytes [ index + 3 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var rssi = bytes [ index + 4 ] - 256 + " dBm " ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
index = index + 5 ;
}
return data ;
}
// type: 0xe MultiDeviceTypeMessage
function decodeMultiDeviceTypeMessage ( bytes ) {
var data = {
type : " MultiDeviceTypeMessage " ,
number : bytes [ 0 ] & 0x0f ,
};
var index = 1 ;
for ( let i = 0 ; i < data . number ; i ++ ) {
var index = 1 + 6 * i ;
var deviceTypeId = bytes [ index ];
var major = (( bytes [ index + 1 ] << 8 ) | bytes [ index + 2 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var minor = (( bytes [ index + 3 ] << 8 ) | bytes [ index + 4 ])
. toString ( 16 )
. toUpperCase ()
. padStart ( 4 , " 0 " );
var rssi = bytes [ index + 5 ] - 256 + " dBm " ;
data [ " deviceTypeId " + ( i + 1 )] = deviceTypeId ;
data [ " beacon " + ( i + 1 )] = major + minor ;
data [ " rssi " + ( i + 1 )] = rssi ;
index = index + 6 ;
}
return data ;
}
// type: 0xf Acknowledgment
function decodeAcknowledgment ( bytes ) {
var data = {};
data . type = " Acknowledgment " ;
data . result = ( bytes [ 0 ] & 0x0f ) == 0 ? " Success " : " Failure " ;
data . msgId = ( bytes [ 1 ] & 0xff ). toString ( 16 ). toUpperCase ();
return data ;
}
function byteToHex ( str ) {
return str . toString ( 16 ). toUpperCase (). padStart ( 2 , " 0 " );
}
Add a device on Chirpstack
Go to the Applications page and click Add application button.
Enter application name and click Submit button.
Click Add device button.
Fill in the device configuration parameters.
Go to the Variables dection, enter the “ThingsBoardAccessToken” parameter, and then click the Submit button.
Enter your Application key in this field and click Submit button to save the device.
Go to the Applications page and click Add application button.
Enter application name and click Submit button.
Click Add device button.
Fill in the device configuration parameters.
Go to the Variables dection, enter the “ThingsBoardAccessToken” parameter, and then click the Submit button.
Enter your Application key in this field and click Submit button to save the device.
Go to the Integrations page and click on ThingsBoard .
Enter your ThingsBoard server address and click Submit .
Create device on ThingsBoard
Go to the Devices page.
Click the plus icon to add new device.
Enter the device information and click “Next: Credentials ” button.
Enter the Access token for the device and click Add button.
Click on the device to view detailed information.
Navigate to the Latest telemetry tab to view the device's reported data.
Go to the Devices page.
Click the plus icon to add new device.
Enter the device information and click “Next: Credentials ” button.
Enter the Access token for the device and click Add button.
Click on the device to view detailed information.
Navigate to the Latest telemetry tab to view the device's reported data.
Conclusion
With the information provided in this guide, you can easily connect your Solar Bluetooth Gateway and transmit data to ThingsBoard.
For further learning about key concepts and features, explore the platform's documentation . You can also configure alarm rules or set up dashboards .
© 2025 The ThingsBoard Authors