Skip to content

Commit f42ef4b

Browse files
committed
Moved files around, started test coverage of the AM2302 component
1 parent 08abbec commit f42ef4b

File tree

6 files changed

+137
-4
lines changed

6 files changed

+137
-4
lines changed

shedpi_components/AM2302/__init__.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Code based on: https://github.com/Gozem/am2320/blob/45a20076efb9a19e91bd50f229d1cdd53f1134d4/am2320.py#L1
3+
License at time of code reference: MIT https://github.com/Gozem/am2320/blob/45a20076efb9a19e91bd50f229d1cdd53f1134d4/LICENSE#L1
4+
"""
5+
6+
import posix
7+
import time
8+
from dataclasses import dataclass
9+
from fcntl import ioctl
10+
11+
12+
@dataclass
13+
class AM2320Reading:
14+
temperature: float
15+
humidity: float
16+
17+
def __str__(self):
18+
return f"Temperature: {self.temperature}, Humidity: {self.humidity}"
19+
20+
21+
class AM2320:
22+
I2C_ADDR = 0x5C
23+
I2C_SLAVE = 0x0703
24+
25+
def __init__(self, i2cbus: int = 1):
26+
self._fd = posix.open("/dev/i2c-%d" % i2cbus, posix.O_RDWR)
27+
28+
ioctl(self._fd, self.I2C_SLAVE, self.I2C_ADDR)
29+
30+
def __del__(self):
31+
posix.close(self._fd)
32+
33+
@staticmethod
34+
def _calc_crc16(data):
35+
crc = 0xFFFF
36+
for x in data:
37+
crc = crc ^ x
38+
for bit in range(8):
39+
if (crc & 0x0001) == 0x0001:
40+
crc >>= 1
41+
crc ^= 0xA001
42+
else:
43+
crc >>= 1
44+
return crc
45+
46+
@staticmethod
47+
def _combine_bytes(msb, lsb):
48+
return msb << 8 | lsb
49+
50+
def read_sensor(self) -> AM2320Reading:
51+
# wake AM2320 up, goes to sleep to not warm up and affect the humidity sensor
52+
# This write will fail as AM2320 won't ACK this write
53+
try:
54+
posix.write(self._fd, b"\0x00")
55+
except:
56+
pass
57+
time.sleep(0.001) # Wait at least 0.8ms, at most 3ms
58+
59+
# write at addr 0x03, start reg = 0x00, num regs = 0x04 */
60+
posix.write(self._fd, b"\x03\x00\x04")
61+
time.sleep(0.0016) # Wait at least 1.5ms for result
62+
63+
# Read out 8 bytes of result data
64+
# Byte 0: Should be Modbus function code 0x03
65+
# Byte 1: Should be number of registers to read (0x04)
66+
# Byte 2: Humidity msb
67+
# Byte 3: Humidity lsb
68+
# Byte 4: Temperature msb
69+
# Byte 5: Temperature lsb
70+
# Byte 6: CRC lsb byte
71+
# Byte 7: CRC msb byte
72+
data = bytearray(posix.read(self._fd, 8))
73+
74+
# Check data[0] and data[1]
75+
if data[0] != 0x03 or data[1] != 0x04:
76+
raise ValueError("First two read bytes are a mismatch")
77+
78+
# CRC check
79+
if self._calc_crc16(data[0:6]) != self._combine_bytes(data[7], data[6]):
80+
raise ValueError("CRC failed")
81+
82+
# Temperature resolution is 16Bit,
83+
# temperature highest bit (Bit15) is equal to 1 indicates a
84+
# negative temperature, the temperature highest bit (Bit15)
85+
# is equal to 0 indicates a positive temperature;
86+
# temperature in addition to the most significant bit (Bit14 ~ Bit0)
87+
# indicates the temperature sensor string value.
88+
# Temperature sensor value is a string of 10 times the
89+
# actual temperature value.
90+
temp = self._combine_bytes(data[4], data[5])
91+
if temp & 0x8000:
92+
temp = -(temp & 0x7FFF)
93+
temp /= 10.0
94+
95+
humi = self._combine_bytes(data[2], data[3]) / 10.0
96+
97+
return AM2320Reading(
98+
temperature=temp,
99+
humidity=humi,
100+
)
101+
102+
103+
if __name__ == "__main__":
104+
am2320 = AM2320(1)
105+
reading = am2320.read_sensor()
106+
print(reading)

shedpi_components/AM2302/tests/__init__.py

Whitespace-only changes.

shedpi_components/AM2302/tests/integration/__init__.py

Whitespace-only changes.

shedpi_components/AM2302/tests/unit/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from unittest.mock import Mock, patch
2+
3+
import pytest
4+
5+
from shedpi_components.AM2302 import AM2320
6+
7+
8+
@patch("shedpi_components.AM2302.posix")
9+
@patch("shedpi_components.AM2302.ioctl")
10+
def test_probe_reading_no_reading(mocked_posix, mocked_ioctl):
11+
probe = AM2320()
12+
13+
with pytest.raises(ValueError) as err:
14+
probe.read_sensor()
15+
16+
assert err.value.args[0] == "First two read bytes are a mismatch"
17+
18+
19+
@patch("shedpi_components.AM2302.posix")
20+
@patch("shedpi_components.AM2302.ioctl")
21+
def test_probe_reading_happy_path(mocked_posix, mocked_ioctl):
22+
probe = AM2320()
23+
# probe.read_temp_raw = Mock(
24+
# return_value=[
25+
# "YES",
26+
# "t=12345",
27+
# ]
28+
# )
29+
30+
mocked_posix.read = Mock(return_value="0000000")
31+
reading = probe.read_sensor()

shedpi_components/am2302.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)