mirror of
https://github.com/supleed2/ELEC60013-ES-CW1.git
synced 2024-12-22 21:45:48 +00:00
Merge remote-tracking branch 'rpi/main'
This commit is contained in:
commit
41f06a6533
161
.gitignore
vendored
Normal file
161
.gitignore
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# Matlab files
|
||||
.m
|
||||
|
||||
# Secrets file
|
||||
.secrets.yml
|
13
README.md
Normal file
13
README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# ELEC60013 Embedded Systems: Coursework 1
|
||||
|
||||
- Raspberry Pi Zero W IoT Device
|
||||
- Competing product: Fi Collar
|
||||
- Our features can outcompete:
|
||||
- Battery Life
|
||||
- Tracking Accuracy?
|
||||
- Much Larger Network
|
||||
- Health Metrics
|
||||
- Can do advanced features too:
|
||||
- Sleep Tracking
|
||||
- Step Counts
|
||||
- Escape Detection? As soon as out of range of owner device?
|
73
hci.py
Normal file
73
hci.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
import base64
|
||||
import subprocess
|
||||
from time import sleep
|
||||
from struct import pack
|
||||
|
||||
|
||||
class HCIBroadcaster:
|
||||
def __init__(self, b64key):
|
||||
self.key = base64.b64decode(b64key)
|
||||
|
||||
def _advertisement_template(self):
|
||||
adv = ""
|
||||
adv += "1e" # length (30)
|
||||
adv += "ff" # manufacturer specific data
|
||||
adv += "4c00" # company ID (Apple)
|
||||
adv += "1219" # offline finding type and length
|
||||
adv += "00" # state
|
||||
for _ in range(22): # key[6:28]
|
||||
adv += "00"
|
||||
adv += "00" # first two bits of key[0]
|
||||
adv += "00" # hint
|
||||
return bytearray.fromhex(adv)
|
||||
|
||||
def _bytes_to_strarray(self, bytes_, with_prefix=False):
|
||||
if with_prefix:
|
||||
return [hex(b) for b in bytes_]
|
||||
else:
|
||||
return [format(b, "x") for b in bytes_]
|
||||
|
||||
def _run_hci_cmd(self, cmd, hci="hci0", wait=1):
|
||||
cmd_ = ["hcitool", "-i", hci, "cmd"]
|
||||
cmd_ += cmd
|
||||
print(cmd_)
|
||||
subprocess.run(cmd_)
|
||||
if wait > 0:
|
||||
sleep(wait)
|
||||
|
||||
def start_advertising(self, interval_ms=2000):
|
||||
key = self.key
|
||||
addr = bytearray(key[:6])
|
||||
addr[0] |= 0b11000000
|
||||
|
||||
adv = self._advertisement_template()
|
||||
adv[7:29] = key[6:28]
|
||||
adv[29] = key[0] >> 6
|
||||
|
||||
print(f"key ({len(key):2}) {key.hex()}")
|
||||
print(f"address ({len(addr):2}) {addr.hex()}")
|
||||
print(f"payload ({len(adv):2}) {adv.hex()}")
|
||||
|
||||
# Set BLE address
|
||||
self._run_hci_cmd(
|
||||
["0x3f", "0x001"] + self._bytes_to_strarray(addr, with_prefix=True)[::-1]
|
||||
)
|
||||
subprocess.run(["systemctl", "restart", "bluetooth"])
|
||||
sleep(1)
|
||||
|
||||
# Set BLE advertisement payload
|
||||
self._run_hci_cmd(
|
||||
["0x08", "0x0008"] + [format(len(adv), "x")] + self._bytes_to_strarray(adv)
|
||||
)
|
||||
|
||||
# Set BLE advertising mode
|
||||
interval_enc = pack("<h", interval_ms)
|
||||
hci_set_adv_params = ["0x08", "0x0006"]
|
||||
hci_set_adv_params += self._bytes_to_strarray(interval_enc)
|
||||
hci_set_adv_params += self._bytes_to_strarray(interval_enc)
|
||||
hci_set_adv_params += ["03", "00", "00", "00", "00", "00", "00", "00", "00"]
|
||||
hci_set_adv_params += ["07", "00"]
|
||||
self._run_hci_cmd(hci_set_adv_params)
|
||||
|
||||
# Start BLE advertising
|
||||
self._run_hci_cmd(["0x08", "0x000a"] + ["01"], wait=0)
|
136
lis3dh.py
Normal file
136
lis3dh.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
"""Library for interacting with LIS3DH triple-axis accelerometer."""
|
||||
|
||||
from importlib.resources import read_text
|
||||
from re import S
|
||||
from tabnanny import check
|
||||
import smbus2
|
||||
from time import sleep
|
||||
|
||||
# Register addresses
|
||||
_CTRL_REG1 = bytes([0x20])
|
||||
_CTRL_REG2 = bytes([0x21])
|
||||
_CTRL_REG3 = bytes([0x22])
|
||||
_CTRL_REG4 = bytes([0x23])
|
||||
_CTRL_REG5 = bytes([0x24])
|
||||
_CTRL_REG6 = bytes([0x25])
|
||||
_REF_REG = bytes([0x26])
|
||||
_STATUS_REG = bytes([0x27])
|
||||
_OUT_X_L = bytes([0x28])
|
||||
_OUT_X_H = bytes([0x29])
|
||||
_OUT_Y_L = bytes([0x2A])
|
||||
_OUT_Y_H = bytes([0x2B])
|
||||
_OUT_Z_L = bytes([0x2C])
|
||||
_OUT_Z_H = bytes([0x2D])
|
||||
_INT1_CFG = bytes([0x30])
|
||||
_INT1_SRC = bytes([0x31])
|
||||
_INT1_THS = bytes([0x32])
|
||||
_INT1_DURATION = bytes([0x33])
|
||||
_INT2_CFG = bytes([0x34])
|
||||
_INT2_SRC = bytes([0x35])
|
||||
_INT2_THS = bytes([0x36])
|
||||
_INT2_DURATION = bytes([0x37])
|
||||
_CLICK_CFG = bytes([0x38])
|
||||
# Config flags
|
||||
SAMPLERATE_1HZ = bytes([0x17])
|
||||
SAMPLERATE_10HZ = bytes([0x27])
|
||||
SAMPLERATE_25HZ = bytes([0x37])
|
||||
HP_DISABLE = bytes([0x00])
|
||||
CTRL_REG3_V = bytes([0x40])
|
||||
CTRL_REG4_V = bytes([0x00]) # sensitivity set to 2g
|
||||
CTRL_REG5_V = bytes([0x08])
|
||||
INT1_THS_V = bytes([0x16]) # free-fall threshold at 350 mg
|
||||
INT1_DURATION_V = bytes([0x03])
|
||||
INT1_CFG_V = bytes([0x95])
|
||||
EMPTY = bytes([0x00])
|
||||
|
||||
class LIS3DH:
|
||||
def __init__(self, i2cBus, samplerate=10, i2cAddress=0x18):
|
||||
sleep(0.005)
|
||||
self.i2c = i2cBus
|
||||
self.addr = i2cAddress
|
||||
self.samplerate = samplerate
|
||||
i2cBus.pec = True # enable smbus2 Packet Error Checking
|
||||
sample_modes = {
|
||||
0:0x0, 1:0x1, 10:0x2, 25:0x3, 50:0x4,
|
||||
100:0x5, 200:0x6, 400:0x7
|
||||
}
|
||||
|
||||
# Check if user-entered values are correct
|
||||
if samplerate in sample_modes:
|
||||
self.samplerate = sample_modes[samplerate]
|
||||
else:
|
||||
raise Exception("Invalid sample rate.")
|
||||
|
||||
# Configure all registers in +-2g mode
|
||||
c0 = smbus2.i2c_msg.write(self.addr, [0x20,(sample_modes[samplerate]<<4)|0xF]) # Initialise in low power mode
|
||||
c1 = smbus2.i2c_msg.write(self.addr, [0x21,0x0A])
|
||||
c2 = smbus2.i2c_msg.write(self.addr, [0x22,0x40])
|
||||
c3 = smbus2.i2c_msg.write(self.addr, [0x23,0x00])
|
||||
c4 = smbus2.i2c_msg.write(self.addr, [0x24,0x0A])
|
||||
c5 = smbus2.i2c_msg.write(self.addr, [0x25,0x20])
|
||||
c6 = smbus2.i2c_msg.write(self.addr, [0x2E,0x00])
|
||||
c7 = smbus2.i2c_msg.write(self.addr, [0x30,0x95])
|
||||
c8 = smbus2.i2c_msg.write(self.addr, [0x32,0x16])
|
||||
c9 = smbus2.i2c_msg.write(self.addr, [0x33,0x03])
|
||||
c10 = smbus2.i2c_msg.write(self.addr, [0x34,0x3F])
|
||||
c11 = smbus2.i2c_msg.write(self.addr, [0x36,0x32])
|
||||
c12 = smbus2.i2c_msg.write(self.addr, [0x24,0x0A]) # Configure 0x24 again
|
||||
self.i2c.i2c_rdwr(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12)
|
||||
|
||||
def resetint1(self) -> bool:
|
||||
'''Read INT1_SRC to reset it after an interrupt event.'''
|
||||
int1_src_loc = smbus2.i2c_msg.write(self.addr, [0x31])
|
||||
read_int1_src = smbus2.i2c_msg.read(self.addr, 1)
|
||||
self.i2c.i2c_rdwr(int1_src_loc,read_int1_src)
|
||||
if read_int1_src.buf[0] != None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def resetint2(self) -> bool:
|
||||
'''Read INT2_SRC to reset it after an interrupt event.'''
|
||||
int2_src_loc = smbus2.i2c_msg.write(self.addr, [0x35])
|
||||
read_int2_src = smbus2.i2c_msg.read(self.addr, 1)
|
||||
self.i2c.i2c_rdwr(int2_src_loc,read_int2_src)
|
||||
if read_int2_src.buf[0] != None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def readAll(self) -> list:
|
||||
'''Read acceleration data from all axes. Returns values as a list [X,Y,Z].'''
|
||||
check_status = smbus2.i2c_msg.write(self.addr, [0x27])
|
||||
x = smbus2.i2c_msg.read(self.addr, 1)
|
||||
y = smbus2.i2c_msg.read(self.addr, 1)
|
||||
z = smbus2.i2c_msg.read(self.addr, 1)
|
||||
prepare_x = smbus2.i2c_msg.write(self.addr, [0x29])
|
||||
prepare_y = smbus2.i2c_msg.write(self.addr, [0x2B])
|
||||
prepare_z = smbus2.i2c_msg.write(self.addr, [0x2D])
|
||||
status = smbus2.i2c_msg.read(self.addr, 1)
|
||||
self.i2c.i2c_rdwr(check_status, status)
|
||||
|
||||
while status.buf[0][0] & 0b1111 != 0b1111: # Wait for data to be available
|
||||
sleep(0.001)
|
||||
self.i2c.i2c_rdwr(check_status, status)
|
||||
|
||||
if status.buf[0][0] & 0b1111 == 0b1111: # If data is available, read
|
||||
self.i2c.i2c_rdwr(prepare_x, x)
|
||||
self.i2c.i2c_rdwr(prepare_y, y)
|
||||
self.i2c.i2c_rdwr(prepare_z, z)
|
||||
X = int.from_bytes(x.buf[0],"big")
|
||||
Y = int.from_bytes(y.buf[0],"big")
|
||||
Z = int.from_bytes(z.buf[0],"big")
|
||||
|
||||
# Convert from binary 2s complement to useful data
|
||||
new_values = []
|
||||
for D in [X,Y,Z]:
|
||||
MSB = D >> 7
|
||||
if MSB == 1:
|
||||
res = (-128 + (D - 128))*self.resolution/128
|
||||
else:
|
||||
res = (D*self.resolution)/128
|
||||
new_values.append(res)
|
||||
|
||||
return new_values
|
||||
else:
|
||||
return None # Should never get here lol
|
62
main.py
Normal file
62
main.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
import os, sys
|
||||
from time import sleep
|
||||
import yaml
|
||||
import paho.mqtt.client as mqtt
|
||||
import json, smbus2, si7201, tmp006, lis3dh, hci, gpiozero
|
||||
|
||||
# Global Sensor Data Variables
|
||||
dailysteps = 0
|
||||
fallen = False
|
||||
|
||||
def incrementStepCount(interrupt, sensor) -> None:
|
||||
global dailysteps
|
||||
dailysteps += 1
|
||||
sensor.resetint2()
|
||||
|
||||
def setFallen(interrupt, sensor) -> None:
|
||||
global fallen
|
||||
fallen = True
|
||||
sensor.resetint1()
|
||||
|
||||
# Setup
|
||||
bus = smbus2.SMBus(1) # set up I2C bus 1
|
||||
|
||||
temphum = si7201.Si7201(bus, 0x40) # set up Si7201 sensor
|
||||
temphum.reset() # reset Si7201
|
||||
|
||||
irtemp = tmp006.TMP006(bus, 0x41, tmp006.SAMPLERATE_4HZ) # set up TMP006 sensor
|
||||
irtemp.active = 1 # turn on TMP006
|
||||
|
||||
accel = lis3dh.LIS3DH(bus, 10, 0x18) # set up LIS3DH sensor
|
||||
fall = gpiozero.Button(18, pull_up = False) # GPIO17: Freefall Interrupt (INT1)
|
||||
fall.when_activated = lambda: setFallen(fall, accel) # set fallen to True when Freefall Interrupt (INT1) is triggered
|
||||
step = gpiozero.Button(17, pull_up = False) # GPIO18: Step Counter Interrupt (INT2)
|
||||
step.when_activated = lambda: incrementStepCount(step, accel) # increment step count when Step Counter Interrupt (INT2) is triggered
|
||||
|
||||
with open(".secrets.yml", "r") as secrets:
|
||||
try:
|
||||
secrets = yaml.load(secrets, Loader = yaml.SafeLoader)
|
||||
key = secrets["key"] # Get Base64 encoded device public key from secrets file
|
||||
except ImportError as exc:
|
||||
print(exc)
|
||||
sleep(60) # 60s delay before restarting
|
||||
os.execl(sys.executable, os.path.abspath(__file__), *sys.argv) # Restart propgram
|
||||
btcast = hci.HCIBroadcaster(key) # set up HCI Broadcaster
|
||||
|
||||
client = mqtt.Client("RaspberryPi") # set up MQTT client
|
||||
client.connect("add8.duckdns.org", 8883, 60) # connect to MQTT broker
|
||||
client.loop_start() # Start a new thread to handle sending MQTT messages
|
||||
|
||||
# Main Loop
|
||||
while True:
|
||||
data = {
|
||||
"devID": "testdoggo",
|
||||
"air_temp": temphum.temperature,
|
||||
"day_steps": dailysteps,
|
||||
"hum_perc": temphum.humidity,
|
||||
"pet_temp": irtemp.temperature
|
||||
}
|
||||
mqtt_data = json.dumps(data)
|
||||
client.publish("/data", mqtt_data)
|
||||
btcast.start_advertising() # Send out BT advertisement
|
||||
sleep(5) # Sleep for 5 seconds to lower power consumption
|
8
requirements.txt
Normal file
8
requirements.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
colorzero==2.0
|
||||
gpiozero==1.6.2
|
||||
paho-mqtt==1.6.1
|
||||
python-dateutil==2.8.2
|
||||
PyYAML==6.0
|
||||
RPi.GPIO==0.7.1
|
||||
six==1.16.0
|
||||
smbus2==0.4.1
|
40
si7201.py
Normal file
40
si7201.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
"""Library for interacting with Si7201 Temperature & Humidity Sensor."""
|
||||
|
||||
import smbus2
|
||||
from time import sleep
|
||||
|
||||
|
||||
class Si7201:
|
||||
def __init__(self, i2cBus, i2cAddress=0x40):
|
||||
self.i2c = i2cBus
|
||||
self.addr = i2cAddress
|
||||
i2cBus.pec = True # enable smbus2 Packet Error Checking
|
||||
|
||||
@property
|
||||
def temperature(self, decimals=1):
|
||||
"""Measured temperature in degrees Celsius, with configurable decimal places, default 1."""
|
||||
measure_temp = smbus2.i2c_msg.write(self.addr, [0xF3])
|
||||
read_temp = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(measure_temp)
|
||||
sleep(0.1)
|
||||
self.i2c.i2c_rdwr(read_temp)
|
||||
temp_code = int.from_bytes(read_temp.buf[0] + read_temp.buf[1], "big")
|
||||
temp = round(((175.72 * temp_code) / 65536 - 46.85), decimals)
|
||||
return temp
|
||||
|
||||
@property
|
||||
def humidity(self, decimals=1):
|
||||
"""Measured relative humidity in percent, with configurable decimal places, default 1."""
|
||||
measure_hum = smbus2.i2c_msg.write(self.addr, [0xF5])
|
||||
read_rh = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(measure_hum)
|
||||
sleep(0.1)
|
||||
self.i2c.i2c_rdwr(read_rh)
|
||||
rh_code = int.from_bytes(read_rh.buf[0] + read_rh.buf[1], "big")
|
||||
hum = round((125 * rh_code) / 65536 - 6.0, decimals)
|
||||
return hum
|
||||
|
||||
def reset(self):
|
||||
"""Reset the sensor."""
|
||||
resetcmd = smbus2.i2c_msg.write(self.addr, [0xFE])
|
||||
self.i2c.i2c_rdwr(resetcmd)
|
123
tmp006.py
Normal file
123
tmp006.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
"""Library for interacting with TMP006 Thermopile (IR Temperature) Sensor."""
|
||||
|
||||
import smbus2
|
||||
from time import sleep
|
||||
|
||||
# Pointer Register Locations
|
||||
_REG_VOBJ = bytes([0x00])
|
||||
_REG_TAMB = bytes([0x01])
|
||||
_REG_CNFG = bytes([0x02])
|
||||
_REG_M_ID = bytes([0xFE])
|
||||
_REG_D_ID = bytes([0xFF])
|
||||
# Configuration Flags
|
||||
_MODE_ON = bytes([0x70])
|
||||
SAMPLERATE_4HZ = bytes([0x00])
|
||||
SAMPLERATE_2HZ = bytes([0x02])
|
||||
SAMPLERATE_1HZ = bytes([0x04])
|
||||
SAMPLERATE_0_5HZ = bytes([0x06])
|
||||
SAMPLERATE_0_25HZ = bytes([0x08])
|
||||
_DRDY_EN = bytes([0x01])
|
||||
|
||||
|
||||
class TMP006:
|
||||
def __init__(self, i2cBus, i2cAddress=0x40, samplerate=SAMPLERATE_1HZ):
|
||||
self.i2c = i2cBus
|
||||
self.addr = i2cAddress
|
||||
self.samplerate = samplerate
|
||||
i2cBus.pec = True # enable smbus2 Packet Error Checking
|
||||
self.config = bytes([0x00, 0x00])
|
||||
self.config = bytes(
|
||||
[self.config[0] | samplerate[0] | _MODE_ON[0] | _DRDY_EN[0], self.config[1]]
|
||||
)
|
||||
ptrConfig = smbus2.i2c_msg.write(self.addr, _REG_CNFG)
|
||||
writeConfig = smbus2.i2c_msg.write(self.addr, self.config)
|
||||
self.i2c.i2c_rdwr(ptrConfig, writeConfig)
|
||||
|
||||
@property
|
||||
def temperature(self) -> float:
|
||||
"""Measured temperature in degrees Celsius, to 2 decimel places"""
|
||||
Vobj = self.vObject()
|
||||
Tdie = self.tAmbient()
|
||||
# Values for Calculations
|
||||
S0 = 6.4e-14 # Calibration Factor TODO: Calibrate
|
||||
a1 = 1.75e-3
|
||||
a2 = -1.678e-5
|
||||
Tref = 298.15
|
||||
b0 = -2.94e-5
|
||||
b1 = -5.7e-7
|
||||
b2 = 4.63e-9
|
||||
c2 = 13.4
|
||||
# Calculate Sensitivity of Thermopile
|
||||
S = S0 * (1 + a1 * (Tdie - Tref) + a2 * ((Tdie - Tref) ** 2))
|
||||
# Calculate Coltage offset due to package thermal resistance
|
||||
Voffset = b0 + b1 * (Tdie - Tref) + b2 * ((Tdie - Tref) ** 2)
|
||||
# Calculate Seebeck coefficients
|
||||
fVobj = (Vobj - Voffset) + c2 * ((Vobj - Voffset) ** 2)
|
||||
# Calculate object temperature in Kelvin
|
||||
Tobj = (Tdie**4 + (fVobj / S)) ** 0.25
|
||||
# Convert from Kelvin to Celsius
|
||||
return round(Tobj - 273.15, 2)
|
||||
|
||||
@property
|
||||
def active(self) -> bool:
|
||||
"""Check if Sensor is powered on."""
|
||||
ptrPower = smbus2.i2c_msg.write(self.addr, _REG_CNFG)
|
||||
power = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrPower, power)
|
||||
return power.buf[0][0] & _MODE_ON[0] != 0
|
||||
|
||||
@active.setter
|
||||
def active(self, value: bool):
|
||||
"""Set the sensor to active or inactive."""
|
||||
if value:
|
||||
ptrPower = smbus2.i2c_msg.write(self.addr, _REG_CNFG)
|
||||
power = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrPower, power)
|
||||
newPower = bytes([power.buf[0][0] | _MODE_ON[0], power.buf[1][0]])
|
||||
updatePower = smbus2.i2c_msg.write(self.addr, newPower)
|
||||
self.i2c.i2c_rdwr(ptrPower, updatePower)
|
||||
else:
|
||||
ptrPower = smbus2.i2c_msg.write(self.addr, _REG_CNFG)
|
||||
power = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrPower, power)
|
||||
newPower = bytes([power.buf[0][0] & ~_MODE_ON[0], power.buf[1][0]])
|
||||
updatePower = smbus2.i2c_msg.write(self.addr, newPower)
|
||||
self.i2c.i2c_rdwr(ptrPower, updatePower)
|
||||
|
||||
def vObject(self) -> float:
|
||||
"""Reading from Sensor Voltage Register in Volts"""
|
||||
ptrVobject = smbus2.i2c_msg.write(self.addr, _REG_VOBJ)
|
||||
readVobject = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrVobject, readVobject)
|
||||
scaledVoltage = int.from_bytes(
|
||||
readVobject.buf[0] + readVobject.buf[1], byteorder="big", signed=True
|
||||
)
|
||||
return round(scaledVoltage * 156.25e-9, 1)
|
||||
# convert to Volts (156.25nV per LSB * 1e-9 for scaling from nV to Volts)
|
||||
|
||||
def tAmbient(self) -> float:
|
||||
"""Reading from Ambient Temperature Register in Degrees Celsius"""
|
||||
ptrTambient = smbus2.i2c_msg.write(self.addr, _REG_TAMB)
|
||||
readTambient = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrTambient, readTambient)
|
||||
scaledTemp = int.from_bytes(
|
||||
readTambient.buf[0] + readTambient.buf[1], byteorder="big", signed=True
|
||||
)
|
||||
return round(scaledTemp * 0.0078125, 1)
|
||||
# convert to degrees Celsius (1/32 for scaling * 1/4 for 2 bit shift)
|
||||
|
||||
@property
|
||||
def manID(self) -> bytes:
|
||||
"""Sensor manufacturer ID"""
|
||||
ptrManID = smbus2.i2c_msg.write(self.addr, _REG_M_ID)
|
||||
readManID = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrManID, readManID)
|
||||
return readManID.buf[0] + readManID.buf[1]
|
||||
|
||||
@property
|
||||
def devID(self) -> bytes:
|
||||
"""Sensor device ID"""
|
||||
ptrDevID = smbus2.i2c_msg.write(self.addr, _REG_D_ID)
|
||||
readDevID = smbus2.i2c_msg.read(self.addr, 2)
|
||||
self.i2c.i2c_rdwr(ptrDevID, readDevID)
|
||||
return readDevID.buf[0] + readDevID.buf[1]
|
Loading…
Reference in a new issue