temphum: support multiple temp/relhum sensors

This commit is contained in:
Evgeny Zinoviev 2022-04-29 16:41:59 +03:00
parent c412bf2ee0
commit e93ebaad0a
10 changed files with 122 additions and 26 deletions

View File

@ -10,7 +10,7 @@ endif
PROGRAMS = admin_bot inverter_bot pump_bot sensors_bot PROGRAMS = admin_bot inverter_bot pump_bot sensors_bot
PROGRAMS += inverter_mqtt_receiver inverter_mqtt_sender PROGRAMS += inverter_mqtt_receiver inverter_mqtt_sender
PROGRAMS += sensors_mqtt_receiver sensors_mqtt_sender PROGRAMS += sensors_mqtt_receiver sensors_mqtt_sender
PROGRAMS += si7021d PROGRAMS += temphumd
PROGRAMS += gpiorelayd PROGRAMS += gpiorelayd
PROGRAMS += gpiosensord PROGRAMS += gpiosensord
#PROGRAMS += web_api #PROGRAMS += web_api

View File

@ -0,0 +1,18 @@
from .base import SensorType, TempHumSensor
from .si7021 import Si7021
from .dht12 import DHT12
__all__ = [
'SensorType',
'TempHumSensor',
'create_sensor'
]
def create_sensor(type: SensorType, bus: int) -> TempHumSensor:
if type == SensorType.Si7021:
return Si7021(bus)
elif type == SensorType.DHT12:
return DHT12(bus)
else:
raise ValueError('unexpected sensor type')

25
src/home/temphum/base.py Normal file
View File

@ -0,0 +1,25 @@
import smbus
from abc import abstractmethod, ABC
from enum import Enum
class TempHumSensor:
@abstractmethod
def humidity(self) -> float:
pass
@abstractmethod
def temperature(self) -> float:
pass
class I2CTempHumSensor(TempHumSensor, ABC):
def __init__(self, bus: int):
super().__init__()
self.bus = smbus.SMBus(bus)
class SensorType(Enum):
Si7021 = 'si7021'
DHT12 = 'dht12'

22
src/home/temphum/dht12.py Normal file
View File

@ -0,0 +1,22 @@
from .base import I2CTempHumSensor
class DHT12(I2CTempHumSensor):
i2c_addr = 0x5C
def _measure(self):
raw = self.bus.read_i2c_block_data(self.i2c_addr, 0, 5)
if (raw[0] + raw[1] + raw[2] + raw[3]) & 0xff != raw[4]:
raise ValueError("checksum error")
return raw
def temperature(self) -> float:
raw = self._measure()
temp = raw[2] + (raw[3] & 0x7f) * 0.1
if raw[3] & 0x80:
temp *= -1
return temp
def humidity(self) -> float:
raw = self._measure()
return raw[0] + raw[1] * 0.1

View File

@ -0,0 +1,13 @@
from .base import I2CTempHumSensor
class Si7021(I2CTempHumSensor):
i2c_addr = 0x40
def temperature(self) -> float:
raw = self.bus.read_i2c_block_data(self.i2c_addr, 0xE3, 2)
return 175.72 * (raw[0] << 8 | raw[1]) / 65536.0 - 46.85
def humidity(self) -> float:
raw = self.bus.read_i2c_block_data(self.i2c_addr, 0xE5, 2)
return 125.0 * (raw[0] << 8 | raw[1]) / 65536.0 - 6.0

19
src/temphum.py Normal file
View File

@ -0,0 +1,19 @@
from argparse import ArgumentParser
from home.temphum import SensorType, create_sensor
if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('-t', '--type', choices=[item.value for item in SensorType],
required=True,
help='Sensor type')
parser.add_argument('-b', '--bus', type=int, default=0,
help='I2C bus number')
arg = parser.parse_args()
sensor = create_sensor(SensorType(arg.type), arg.bus)
temp = sensor.temperature()
hum = sensor.humidity()
print(f'temperature: {temp}')
print(f'rel. humidity: {hum}')

View File

@ -1,29 +1,26 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import smbus
import argparse
import asyncio import asyncio
import json import json
import logging import logging
from typing import Optional
from home.config import config from home.config import config
from home.util import parse_addr from home.util import parse_addr
from home.temphum import SensorType, create_sensor, TempHumSensor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
bus = None sensor: Optional[TempHumSensor] = None
lock = asyncio.Lock() lock = asyncio.Lock()
delay = 0.01 delay = 0.01
async def si7021_read(): async def get_measurements():
async with lock: async with lock:
await asyncio.sleep(delay) await asyncio.sleep(delay)
# these are still blocking... meh temp = sensor.temperature()
raw = bus.read_i2c_block_data(0x40, 0xE3, 2) rh = sensor.humidity()
temp = 175.72 * (raw[0] << 8 | raw[1]) / 65536.0 - 46.85
raw = bus.read_i2c_block_data(0x40, 0xE5, 2)
rh = 125.0 * (raw[0] << 8 | raw[1]) / 65536.0 - 6.0
return rh, temp return rh, temp
@ -41,7 +38,7 @@ async def handle_client(reader, writer):
if request == 'read': if request == 'read':
try: try:
rh, temp = await asyncio.wait_for(si7021_read(), timeout=3) rh, temp = await asyncio.wait_for(get_measurements(), timeout=3)
data = dict(humidity=rh, temp=temp) data = dict(humidity=rh, temp=temp)
except asyncio.TimeoutError as e: except asyncio.TimeoutError as e:
logger.exception(e) logger.exception(e)
@ -68,12 +65,14 @@ async def run_server(host, port):
if __name__ == '__main__': if __name__ == '__main__':
config.load() config.load()
host, port = parse_addr(config['server']['listen']) if 'measure_delay' in config['sensor']:
delay = float(config['sensor']['measure_delay'])
delay = float(config['smbus']['delay']) sensor = create_sensor(SensorType(config['sensor']['type']),
bus = smbus.SMBus(int(config['smbus']['bus'])) int(config['sensor']['bus']))
try: try:
host, port = parse_addr(config['server']['listen'])
asyncio.run(run_server(host, port)) asyncio.run(run_server(host, port))
except KeyboardInterrupt: except KeyboardInterrupt:
logging.info('Exiting...') logging.info('Exiting...')

View File

@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Sensors MQTT sender Description=Sensors MQTT sender
After=si7021d.service After=temphumd.service
[Service] [Service]
User=user User=user

View File

@ -1,10 +0,0 @@
[Unit]
Description=si7021 daemon
After=network-online.target
[Service]
Restart=on-failure
ExecStart=/home/user/homekit/src/si7021d.py --config /etc/si7021d.toml
[Install]
WantedBy=multi-user.target

10
systemd/temphumd.service Normal file
View File

@ -0,0 +1,10 @@
[Unit]
Description=temperature and humidity daemon
After=network-online.target
[Service]
Restart=on-failure
ExecStart=/home/user/homekit/src/temphumd.py --config /etc/temphumd.toml
[Install]
WantedBy=multi-user.target