Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
297605b81e | |||
5e673e0c0e | |||
3f083c0aed | |||
d1bb240080 | |||
b0a5903c6e | |||
6066af50ac | |||
3cb59cc688 | |||
d3b2cc4c89 | |||
cb50f230b6 | |||
e2fa06d94e | |||
d2825a32ae | |||
8d625264d1 | |||
df102d8e4e | |||
aff797bcb5 | |||
9715339c43 | |||
e44a78742e | |||
7f4eae67f6 |
6 changed files with 87 additions and 14 deletions
|
@ -5,3 +5,4 @@ uvicorn
|
||||||
fastapi-cors
|
fastapi-cors
|
||||||
Adafruit_DHT
|
Adafruit_DHT
|
||||||
requests
|
requests
|
||||||
|
mh_z19
|
|
@ -5,30 +5,34 @@ from config import climate_log_file, dht22_pin
|
||||||
|
|
||||||
from handler.dht22_climate import Dht22Climate
|
from handler.dht22_climate import Dht22Climate
|
||||||
from handler.matrix_display import MatrixDisplay
|
from handler.matrix_display import MatrixDisplay
|
||||||
|
from handler.mhz19_co2 import Mhz19Co2
|
||||||
|
|
||||||
|
|
||||||
climate_sensor = Dht22Climate(dht22_pin)
|
climate_sensor = Dht22Climate(dht22_pin)
|
||||||
|
co2_sensor = Mhz19Co2()
|
||||||
matrix_display = MatrixDisplay()
|
matrix_display = MatrixDisplay()
|
||||||
|
|
||||||
|
|
||||||
async def log_temperature():
|
async def log_climate(delay_sec: int = 60):
|
||||||
# If file does not exist, create it and write header
|
# If file does not exist, create it and write header
|
||||||
if not os.path.isfile(climate_log_file):
|
if not os.path.isfile(climate_log_file):
|
||||||
with open(climate_log_file, "w") as f:
|
with open(climate_log_file, "w") as f:
|
||||||
f.write("timestamp,temperature,humidity\n")
|
f.write("timestamp,temperature,humidity,co2\n")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
measurements = climate_sensor.read()
|
measurements = climate_sensor.read()
|
||||||
|
co2_density = co2_sensor.read()
|
||||||
if measurements is not None:
|
if measurements is not None:
|
||||||
with open(climate_log_file, "a") as f:
|
with open(climate_log_file, "a") as f:
|
||||||
f.write(
|
f.write(
|
||||||
"{},{},{}\n".format(
|
"{},{},{},{}\n".format(
|
||||||
datetime.now().isoformat(),
|
datetime.now().isoformat(),
|
||||||
measurements["temperature"],
|
measurements["temperature"],
|
||||||
measurements["humidity"],
|
measurements["humidity"],
|
||||||
|
co2_density,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await asyncio.sleep(60)
|
await asyncio.sleep(delay_sec)
|
||||||
|
|
||||||
|
|
||||||
async def display_time():
|
async def display_time():
|
||||||
|
|
|
@ -8,12 +8,12 @@ class Dht22Climate:
|
||||||
self.pin = pin
|
self.pin = pin
|
||||||
self.last_read = None
|
self.last_read = None
|
||||||
|
|
||||||
def get_last_read(self):
|
def get_last_read(self) -> dict | None:
|
||||||
if self.last_read is None:
|
if self.last_read is None:
|
||||||
return self.read()
|
return self.read()
|
||||||
return self.last_read
|
return self.last_read
|
||||||
|
|
||||||
def read(self):
|
def read(self) -> dict | None:
|
||||||
humidity, temperature = Adafruit_DHT.read_retry(self.sensor, self.pin)
|
humidity, temperature = Adafruit_DHT.read_retry(self.sensor, self.pin)
|
||||||
if humidity is not None and temperature is not None:
|
if humidity is not None and temperature is not None:
|
||||||
self.last_read = {"temperature": temperature, "humidity": humidity}
|
self.last_read = {"temperature": temperature, "humidity": humidity}
|
||||||
|
|
48
src/handler/mhz19_co2.py
Normal file
48
src/handler/mhz19_co2.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import serial
|
||||||
|
|
||||||
|
|
||||||
|
class Mhz19Co2:
|
||||||
|
def __init__(self):
|
||||||
|
self.last_read = None
|
||||||
|
|
||||||
|
self.serial_port = "/dev/serial0"
|
||||||
|
self.baud_rate = 9600
|
||||||
|
self.byte_size = 8
|
||||||
|
self.parity = "N"
|
||||||
|
self.stop_bits = 1
|
||||||
|
self.timeout = None
|
||||||
|
|
||||||
|
def get_last_read(self) -> int | None:
|
||||||
|
if self.last_read is None:
|
||||||
|
return self.read()
|
||||||
|
return self.last_read
|
||||||
|
|
||||||
|
def read(self) -> int | None:
|
||||||
|
ser = None
|
||||||
|
try:
|
||||||
|
ser = serial.Serial(
|
||||||
|
port=self.serial_port,
|
||||||
|
baudrate=self.baud_rate,
|
||||||
|
bytesize=self.byte_size,
|
||||||
|
parity=self.parity,
|
||||||
|
stopbits=self.stop_bits,
|
||||||
|
timeout=self.timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
# send "Read CO2" command
|
||||||
|
command_data = bytes([0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79])
|
||||||
|
ser.write(command_data)
|
||||||
|
|
||||||
|
# read "Return Value (CO2 concentration)"
|
||||||
|
data = ser.read(9)
|
||||||
|
concentration = data[2] * 256 + data[3]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading data: {e}")
|
||||||
|
finally:
|
||||||
|
if ser:
|
||||||
|
ser.close()
|
||||||
|
ser = None
|
||||||
|
|
||||||
|
self.last_read = concentration
|
||||||
|
return concentration
|
31
src/main.py
31
src/main.py
|
@ -8,9 +8,9 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||||
from actions import (
|
from actions import (
|
||||||
climate_sensor,
|
climate_sensor,
|
||||||
display_time,
|
display_time,
|
||||||
log_temperature,
|
|
||||||
matrix_display,
|
matrix_display,
|
||||||
display_pattern,
|
display_pattern,
|
||||||
|
co2_sensor,
|
||||||
)
|
)
|
||||||
from config import climate_log_file
|
from config import climate_log_file
|
||||||
from handler.action_queue import ActionQueue
|
from handler.action_queue import ActionQueue
|
||||||
|
@ -20,7 +20,6 @@ logging.getLogger().setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
# Start services
|
# Start services
|
||||||
asyncio.create_task(log_temperature())
|
|
||||||
queue = ActionQueue(matrix_display.show_current_time)
|
queue = ActionQueue(matrix_display.show_current_time)
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@ async def turn_off():
|
||||||
|
|
||||||
@app.post("/temperature")
|
@app.post("/temperature")
|
||||||
async def temperature():
|
async def temperature():
|
||||||
measurements = climate_sensor.get_last_read()
|
measurements = climate_sensor.read()
|
||||||
if measurements is None:
|
if measurements is None:
|
||||||
return {"message": "Failed to read temperature"}
|
return {"message": "Failed to read temperature"}
|
||||||
|
|
||||||
|
@ -75,7 +74,7 @@ async def temperature():
|
||||||
|
|
||||||
@app.post("/humidity")
|
@app.post("/humidity")
|
||||||
async def humidity():
|
async def humidity():
|
||||||
measurements = climate_sensor.get_last_read()
|
measurements = climate_sensor.read()
|
||||||
if measurements is None:
|
if measurements is None:
|
||||||
return {"message": "Failed to read humidity"}
|
return {"message": "Failed to read humidity"}
|
||||||
|
|
||||||
|
@ -86,6 +85,30 @@ async def humidity():
|
||||||
return measurements
|
return measurements
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/co2")
|
||||||
|
async def co2():
|
||||||
|
co2 = co2_sensor.read()
|
||||||
|
if co2 is None:
|
||||||
|
return {"message": "Failed to read co2"}
|
||||||
|
|
||||||
|
await queue.add_action_to_queue(matrix_display.show_text, f"{co2} ppm")
|
||||||
|
|
||||||
|
return {"co2": co2}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/climate")
|
||||||
|
async def climate():
|
||||||
|
measurements = climate_sensor.read()
|
||||||
|
if measurements is None:
|
||||||
|
return {"message": "Failed to read humidy and temperature"}
|
||||||
|
|
||||||
|
co2 = co2_sensor.read()
|
||||||
|
if co2 is None:
|
||||||
|
return {"message": "Failed to read co2"}
|
||||||
|
|
||||||
|
return {"co2": co2, **measurements}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/history")
|
@app.post("/history")
|
||||||
async def history():
|
async def history():
|
||||||
day_entry_count = 24 * 60
|
day_entry_count = 24 * 60
|
||||||
|
|
3
start.sh
3
start.sh
|
@ -1,3 +0,0 @@
|
||||||
cd /home/pi/matrix-clock/src
|
|
||||||
|
|
||||||
uvicorn main:app --reload --host 0.0.0.0
|
|
Loading…
Reference in a new issue