Skip to main content

RAK9154 Solar Battery Lite Data over SensorHub

Solar Battery Data Unit Definition

NameRAK IPSOData typeValue RangeUnit
Battery voltage0xBAU160-0x05 DC (15.00 V)0.01 V
Battery current0xB9S16-150~150 (1.5 A)0.01 A
SOC (State of charge)0xB8U161~1000.01
Average Temperature0x67S16-30~1050.1° C
Battery Errors0xF3U16Refer to below
ErrorMessageTable
bit
BMS Firmware version0xF3U16Vxx.xxbit

Example:

Cloud APP server gets a SensorHub uplink data in JSON format as below.

{
'deviceName': '494', 'timestamp': 1701921826,
'fCnt': 40, 'rssi': -65, 'loRaSNR': 13.5,
'freq': 903700000, 'dr': 3, 'fPort': 2,
'data': '007e09480715ba046416b9004317b84518670d2e19f300001af30002',
'adr': True
}

The key data represents the SensorHub uplink payload transmitted over the LoRa network. This payload consists of multiple sensor data units. Each data unit includes three fields:

  1. ID (1 byte)
  2. Data Type (RAK IPSO, 1 byte)
  3. Data Value (variable size)

Below is an example of a SensorHub uplink payload for a Battery device, which contains 7 data units. The first data unit corresponds to the SensorHub serial number (SN), while the remaining 6 units provide battery status data.

Battery Data UnitUplink Payload
IDIPSOValue
Serial Number1 Byte1 Byte3 Bytes
Voltage1 Byte1 Byte2 Bytes
Current1 Byte1 Byte2 Bytes
State of Charge1 Byte1 Byte1 Byte
Temperature1 Byte1 Byte2 Bytes
Battery Error1 Byte1 Byte2 Bytes
Battery FW Version1 Byte1 Byte2 Bytes

Decoder:

Battery Data UnitUplink PayloadDecode Data
IDIPSOValue
Serial Number007e09480794807
Voltage15ba046411.24 V
Current16b900430.67 A
State of Charge17b84569%
Temperature186700d221.0° C
Battery Error19f30000no error
Battery FW Version1af30002V00.02

Convert the sensor data from hexadecimal to decimal:

  • Serial Number: 007e
  • Data: 094807
SN: 94807
  • Battery Voltage: 15ba
  • Data: 0464
0464 (hex) = 1124 (dec)
1124 x 0.01 (conversion factor) = 11.24 V
  • Battery Current: 16b9
  • Data: 0043
0043 (hex) = 67 (dec)
67 x 0.01 (conversion factor) = 0.67 A
  • Battery SOC: 17b8
  • Data: 45
45 (hex) = 69 (dec)
69 x 0.01 (conversion factor) = 0.69 = 69%
  • Battery Temperature: 1867
  • Data: 00d2
00d2 (hex) = 210 (dec)
210 x 0.1 (conversion factor) = 21.0° C
  • Battery Error: 19f3
  • Data: 0000
Battery Error = no error
  • Battery FW Version: 1af3
  • Data: 0002
Battery FW Version = v00.02

Error Message Table

Bit#DescriptionValue
Bit0 ~ Bit5ReservedReserved
Bit6Over voltage protect0 : normal
1 : fault
Bit7Charge over current protect0 : normal
1 : fault
Bit8Charge low temperature protect0 : normal
1 : fault
Bit9Charge high temperature protect0 : normal
1 : fault
Bit10Charge short circuit protect0 : normal
1 : fault
Bit11Charge over current lock0 : normal
1 : fault
Bit12 ~ Bit15ReservedReserved

Decoder Sample Code

RAK IPSO Sensor type definition in JSON : filename snsr_type_def.json

Click to view the code
{
"7e": {
"name": "SSN",
"resolution" : 1,
"length": 3,
"unit": ""
},
"14": {
"name": "Dissolved-Oxygen",
"resolution" : 0.01,
"length": 2,
"unit": "mg/L"
},
"15": {
"name": "Oxidation Reduction Potential",
"resolution" : 0.1,
"length": 2,
"unit": "mV",
"sign": "-1000~+1000mv"
},
"67": {
"name": "Temperature",
"resolution" : 0.1,
"length": 2,
"unit": "°C"
},

"68": {
"name": "Humidity",
"resolution" : 1,
"length": 1,
"unit": "%"
},
"73": {
"name": "Barmoeter",
"resolution" : 0.1,
"length": 2,
"unit": "hPA"
},
"71": {
"name": "A",
"resolution" : 0.001,
"length": 6,
"unit": "G"
},
"be": {
"name": "Wind-Speed",
"resolution" : 0.01,
"length": 2,
"unit": "m/s"
},
"bf": {
"name": "Wind-Direction",
"resolution" : 1,
"length": 2,
"unit": "°"
},
"65": {
"name": "Illuminance",
"resolution" : 1,
"length": 4,
"unit": "lux"
},
"70": {
"name": "High-Precise-Humidity",
"resolution" : 0.1,
"length": 2,
"unit": "%"
},
"c1": {
"name": "High_Precision_pH",
"resolution" : 0.01,
"length": 2,
"unit": "pH"
},
"c2": {
"name": "pH",
"resolution" : 0.1,
"length": 2,
"unit": ""
},
"c3": {
"name": "Pyranometer",
"resolution" : 1,
"length": 2,
"unit": "W/m2"
},
"c0": {
"name": "EC",
"resolution" : 0.001,
"length": 2,
"unit": "mS/cm"
},
"7f": {
"name": "High-Precision-EC",
"resolution" : 0.001,
"length": 4,
"unit": "us/cm"
},
"13": {
"name": "Salt",
"resolution" : 1,
"length": 2,
"unit": "mg/L"
},
"b8": {
"name": "Capacity",
"resolution" : 1,
"length": 1,
"unit": "%"
},
"b9": {
"name": "Current",
"resolution" : 0.01,
"length": 2,
"unit": "A"
},
"ba": {
"name": "Voltage",
"resolution" : 0.01,
"length": 2,
"unit": "V"
},
"10": {
"name": "Nitrogen",
"resolution" : 1,
"length": 2,
"unit": "mg/kg"
},
"11": {
"name": "phosphorus",
"resolution" : 1,
"length": 2,
"unit": "mg/kg"
},
"12": {
"name": "potassium",
"resolution" : 1,
"length": 2,
"unit": "mg/kg"
},
"77": {
"name": "Dummy",
"resolution" : 1,
"length": 4,
"unit": "qq"
},
"7d": {
"name": "CO2",
"resolution" : 1,
"length": 2,
"unit": "ppm"
},
"82": {
"name": "Distance",
"resolution" : 0.001,
"length": 2,
"unit": "m"
},
"e5": {
"name": "ORIENTATION",
"resolution" : 0.1,
"length": 2,
"unit": "°",
"sign": "–90° to 90°"
},
"e9": {
"name": "Noise",
"resolution" : 0.1,
"length": 2,
"unit": "dB"
},
"f1": {
"name": "RS485",
"resolution" : 1,
"length": 1,
"unit": ""
},
"f2": {
"name": "BINARY",
"resolution" : 1,
"length": 2,
"unit": ""
},
"02": {
"name": "AIC",
"resolution" : 0.01,
"length": 2,
"unit": ""
},
"03": {
"name": "AIV",
"resolution" : 0.01,
"length": 2,
"unit": ""
}
}

Decoder example in Python

Click to view the code
import re, json 

data='007e09480715ba046416b9004317b84518670d2e19f300001af30002'
pattern = re.compile('.{2}')
handle_data = re.findall(pattern, data)

snsr_id_index = 0
ssn = ''
value = ''


def twos_complement_hex(hex_str):
decimal_num = int(hex_str, 16)
twos_complement = (decimal_num + (1 << (len(hex_str) * 4))) % (1 << (len(hex_str) * 4))
return twos_complement # Return the integer value


with open('snsr_type_def.json', 'r', encoding='UTF-8') as f:
unit_dict = json.load(f)

while (snsr_id_index+1) < len(handle_data):
ipso_index = snsr_id_index+1

if handle_data[ipso_index] not in unit_dict.keys():
pass
elif (len(handle_data[ipso_index-1:]) -
(2+unit_dict[handle_data[ipso_index]]['length']) ) < 0:
pass
else:
inner_dict = unit_dict[handle_data[ipso_index]]
name = inner_dict['name']
resolution = inner_dict['resolution']
unit = inner_dict['unit']
data_index = ipso_index+1
sensor_id_type = ''.join(i for i in handle_data[data_index-2:
data_index])
if name == 'RS485':
inner_dict['length'] = len(handle_data[data_index:])
hex_data = ''.join(i for i in handle_data[data_index:
data_index+inner_dict['length']])
dec_data = ''
if name == 'SSN':
dec_data = hex_data
ssn = hex_data
elif name == 'RS485':
dec_data = hex_data
elif name == 'A':
for A in range(3):
dec_data +=
'%s,'%round(twos_complement_hex(hex_data[A*4:(A+1)*4])*resolution,2)
dec_data = dec_data[0:-1]
else:
dec_data = round(twos_complement_hex(hex_data)*resolution,
len(str(resolution ))-1)
value += '%s: %s%s, ' %(name, dec_data, unit)
snsr_id_index += (1+inner_dict['length'])

snsr_id_index += 1

print(value)