Skip to content

Commit

Permalink
First Upload
Browse files Browse the repository at this point in the history
  • Loading branch information
theBASTI0N committed Jan 13, 2020
1 parent 742aa6e commit 65689a4
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 1 deletion.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
# beacon-decoder
BLE Beacon Decoder

A Python library for decoding the following types of Bluetooth LE Beacons:

Eddystone TLM
Ruuvi RAWv2
Ruuvi RAWv1

The beacon-decoder will parse the packet whether it be a string or the raw packet. It will then check what type of packet and return a dictionary containint the decoded information.

It will return 4 formats of data:

Format 0
This is any beacon that was unable to be decoded.

Format 1
These are Eddystone TLM beacons.

Example:
```json
{"f": 1, "temp": 7.15625, "advCnt": 52054, "secCnt": 216, "battery": 2.602}
```

Format 3
These are Ruuvi RAWv1 beacons.
Example:
```json
{"f": 3, "temp": 26.09, "humidity": 58.0, "pressure": 1013.53, "x": 0.046, "y": -0.001, "z": 1.05,"tAcc": 1.051008,
"battery": 3.589,"dewPoint": 17.19235, "abHumidity": 14.19688, "airDensity": 0.003169262 }
```

Format 5
These are Ruuvi RAWv2 beaocns.
Example:
```json
{"f":5,"temp":26.15, "humidity":50.4575, "pressure":1012.84,"z":-0.072,"y":0.984,"x":-0.156,"tAcc": 1.051008,
"battery":2.911,"tx":0,"movementCounter":192,"measurementSequence":27348, "dewPoint": 17.19235, "abHumidity": 14.19688, "airDensity": 0.003169262}
```
1 change: 1 addition & 0 deletions beacon-decoder/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .decoder import decode
124 changes: 124 additions & 0 deletions beacon-decoder/decoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import math
from binascii import hexlify
import struct
#inspired from https://github.com/Scrin/RuuviCollector
def dewPoint(temperature, relativeHumidity):
v = math.log(relativeHumidity / 100 * equilibriumVaporPressure(temperature) / 611.2)
return -243.5 * v / (v - 17.67)

#inspired from https://github.com/Scrin/RuuviCollector
def absoluteHumidity(temperature, relativeHumidity):
return equilibriumVaporPressure(temperature) * relativeHumidity * 0.021674 / (273.15 + temperature)

#inspired from https://github.com/Scrin/RuuviCollector
def equilibriumVaporPressure(temperature):
return 611.2 * math.exp(17.67 * temperature / (243.5 + temperature))

#inspired from https://github.com/Scrin/RuuviCollector
def airDensity(temperature, relativeHumidity, pressure):
return 1.2929 * 273.15 / (temperature + 273.15) * (pressure - 0.3783 * relativeHumidity / 100 * equilibriumVaporPressure(temperature)) / 101300

def twos_complement(hexstr,bits):
value = int(hexstr,16)
if value & (1 << (bits-1)):
value -= 1 << bits
return value

def rshift(val, n):
return (val % 0x100000000) >> n

def decode(data):
format = 0
if '990405' in data:
format = 5
d = str(data)
d = d[14:]
temperature = twos_complement(d[2:6], 16) * 0.005
humidity = int(d[6:10], 16) * 0.0025
pressure = int(d[10:14], 16) + 50000
pressure = pressure / 100
x = twos_complement(d[14:18], 16)/1000
y = twos_complement(d[18:22], 16)/1000
z = twos_complement(d[22:26], 16)/1000
totalACC = math.sqrt(x * x + y * y + z * z)
power_bin = bin(int(d[26:30], 16))
battery_voltage = ((int(power_bin[:13], 2)) + 1600) / 1000
tx_power = int(power_bin[13:], 2) * 2 - 40
mC = int(d[30:32], 16)
measureSeq = int(d[32:36], 16)
aH = absoluteHumidity(temperature, humidity)
dP = dewPoint(temperature, humidity)
airD = airDensity(temperature, humidity, pressure)

dMSG = { 'f' : format,
'temp' : temperature,
'humidity' : humidity,
'x' : x, 'y' :y, 'z' : z,
'tAcc' : totalACC,
'pressure' : pressure,
'battery' : battery_voltage,
'movementCounter' : mC,
'measurementSequence' : measureSeq,
'dewPoint' : dP,
'abHumidity' : aH,
'airDensity' : airD,
'tx' : tx_power
}

return dMSG
elif '990403' in data: #Ruuvi RAWv1
format = 3
d = str(data)
d = d[14:]
humidity = int(d[2:4], 16) * 0.5
temperature = twos_complement(d[4:6], 16) + int(d[6:8], 16) / 100
if temperature > 128:
temperature -= 128
temperature = round(0 - temperature, 2)
pressure = int(d[8:12], 16) + 50000
pressure = pressure / 100
x = twos_complement(d[12:16], 16)/1000
y = twos_complement(d[16:20],16)/1000
z = twos_complement(d[20:24], 16)/1000
totalACC = math.sqrt(x * x + y * y + z * z)
battery_voltage = twos_complement(d[24:28], 16)/1000
aH = absoluteHumidity(temperature, humidity)
dP = dewPoint(temperature, humidity)
airD = airDensity(temperature, humidity, pressure)
dMSG = { 'f' : format,
'temp' : temperature,
'humidity' : humidity,
'pressure' : pressure,
'x' : x, 'y' :y, 'z' : z,
'tAcc' : totalACC,
'battery' : battery_voltage,
'dewPoint' : dP,
'abHumidity' : aH,
'airDensity' : airD
}
return dMSG
elif 'AAFE2000' in data:

format = 1
d = str(data)
d = d[36:]
battery_voltage = int(d[1:4], 16) / 1000
temp1= twos_complement(d[4:6], 8)
temp2 = int(d[6:8], 16) / 256
temperature = temp1 + temp2
advCnt = int(d[8:12], 16)
secCnt = int(d[12:16], 16)

dMSG = { 'f' : format,
'temp' : temperature,
'advCnt' : advCnt,
'secCnt' : secCnt,
'battery' :battery_voltage
}

return dMSG
else:
dMSG = { 'f' : format
}

return dMSG

0 comments on commit 65689a4

Please sign in to comment.