Skip to content

Commit 1365e87

Browse files
committed
Initial implementation
0 parents  commit 1365e87

10 files changed

+393
-0
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
*.swp
2+
*.pyc
3+
*.cpp
4+
*.c
5+
*.egg-info/
6+
dist/
7+
build/

Makefile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.PHONY: all build install test clean uninstall
2+
3+
all: install test
4+
5+
build:
6+
python3 setup.py build
7+
8+
install:
9+
python3 setup.py install --user
10+
11+
uninstall:
12+
pip3 uninstall am2302 -y
13+
14+
test:
15+
pytest
16+
17+
clean:
18+
rm -r build/ dist/ *.egg-info/ *.cpp *.c

am2302.pxd

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from libc.stdint cimport uint8_t, uint16_t, uint32_t
2+
from libcpp cimport bool
3+
4+
cdef extern from 'wiringPi.h':
5+
int wiringPiSetup()
6+
7+
int INPUT
8+
int OUTPUT
9+
void pinMode(int, int)
10+
11+
int HIGH
12+
int LOW
13+
void digitalWrite(int, int)
14+
int digitalRead(int)
15+
16+
void delay(unsigned int)
17+
void delayMicroseconds(unsigned int)
18+
19+
cdef extern from 'am2302_py.hpp':
20+
21+
cdef cppclass CReader:
22+
int m_pin
23+
uint32_t m_start1, m_start2, m_start3
24+
uint16_t m_temperature, m_humidity
25+
uint8_t m_parity
26+
bool m_tempDone, m_humidityDone, m_parityDone
27+
int m_awaitLevel
28+
int m_awaitBit
29+
uint32_t m_awaitDuration
30+
31+
bool run()

am2302.pyx

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from libc.stdint cimport uint8_t
2+
from libcpp cimport bool
3+
4+
cimport am2302
5+
6+
cdef bool _initialized = False
7+
8+
def setup():
9+
global _initialized
10+
if not _initialized:
11+
wiringPiSetup()
12+
_initialized = True
13+
14+
def initialized():
15+
return _initialized
16+
17+
cdef class Reader:
18+
cdef am2302.CReader m_reader
19+
20+
def __init__(self, pin=7):
21+
self.m_reader.m_pin = pin
22+
23+
def run(self):
24+
return self.m_reader.run()
25+
26+
# Main output properties
27+
28+
@property
29+
def temperature(self):
30+
t = self.m_reader.m_temperature
31+
if t & 0x8000:
32+
t = (~t + 1) | 0x8000
33+
return float(t) / 10.0
34+
35+
@property
36+
def humidity(self):
37+
return float(self.m_reader.m_humidity) / 10.0
38+
39+
@property
40+
def parity(self):
41+
return self.m_reader.m_parity
42+
43+
@property
44+
def valid(self):
45+
h = self.m_reader.m_humidity
46+
t = self.m_reader.m_temperature
47+
p = self.m_reader.m_parity
48+
return (((h >> 8) + h + (t >> 8) + t) & 0xFF) == p
49+
50+
# Debug properties
51+
52+
@property
53+
def start1(self):
54+
return self.m_reader.m_start1
55+
@property
56+
def start2(self):
57+
return self.m_reader.m_start2
58+
@property
59+
def start3(self):
60+
return self.m_reader.m_start3
61+
62+
@property
63+
def tempDone(self):
64+
return self.m_reader.m_tempDone
65+
@property
66+
def humidityDone(self):
67+
return self.m_reader.m_humidityDone
68+
@property
69+
def parityDone(self):
70+
return self.m_reader.m_parityDone
71+
72+
@property
73+
def awaitLevel(self):
74+
return self.m_reader.m_awaitLevel
75+
76+
@property
77+
def awaitDuration(self):
78+
return self.m_reader.m_awaitDuration
79+
80+
@property
81+
def awaitBit(self):
82+
return self.m_reader.m_awaitBit
83+
84+
class am3202_data:
85+
def __init__(self, temperature, humidity):
86+
self.temperature = temperature
87+
self.humidity = humidity
88+
89+
def read(pin=7, retries=10):
90+
setup()
91+
reader = Reader(pin)
92+
for r in range(retries):
93+
if reader.run() and reader.valid:
94+
return am3202_data(
95+
temperature=reader.temperature,
96+
humidity=reader.humidity,
97+
)

inc/am2302_py.hpp

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include <wiringPi.h>
2+
#include <cstdint>
3+
4+
class CTimer
5+
{
6+
private:
7+
const uint32_t m_t0;
8+
9+
public:
10+
11+
CTimer() : m_t0(micros()) {}
12+
13+
uint32_t duration() const {
14+
const uint32_t t = micros();
15+
// check if we wrapped
16+
if (t < m_t0)
17+
return 0xFFFFFFFF - m_t0 + t + 1;
18+
else
19+
return t - m_t0;
20+
}
21+
};
22+
23+
template <typename T>
24+
struct CBitLength { static const uint8_t bitLength = 0U; };
25+
26+
template <>
27+
struct CBitLength<uint8_t> { static const uint8_t bitLength = 8U; };
28+
29+
template <>
30+
struct CBitLength<uint16_t> { static const uint8_t bitLength = 16U; };
31+
32+
class CReader
33+
{
34+
public:
35+
int m_pin;
36+
37+
uint32_t m_start1, m_start2, m_start3;
38+
39+
uint16_t m_temperature, m_humidity;
40+
uint8_t m_parity;
41+
42+
bool m_tempDone, m_humidityDone, m_parityDone;
43+
44+
int m_awaitLevel;
45+
int m_awaitBit;
46+
uint32_t m_awaitDuration;
47+
48+
CReader()
49+
: m_pin(0)
50+
, m_start1(0U)
51+
, m_start2(0U)
52+
, m_start3(0U)
53+
, m_temperature(0U)
54+
, m_humidity(0U)
55+
, m_parity(0U)
56+
, m_tempDone(false)
57+
, m_humidityDone(false)
58+
, m_parityDone(false)
59+
, m_awaitLevel(-1)
60+
, m_awaitBit(-1)
61+
, m_awaitDuration(0U)
62+
{}
63+
64+
private:
65+
66+
bool await(int level, uint32_t min, uint32_t max, uint32_t *count = nullptr) {
67+
const CTimer l_timer;
68+
while (l_timer.duration() < max) {
69+
if (digitalRead(m_pin) == level) {
70+
const auto duration = l_timer.duration();
71+
if (count != nullptr)
72+
*count = duration;
73+
if (duration >= min)
74+
return true;
75+
else {
76+
// store debug info
77+
m_awaitLevel = level;
78+
m_awaitDuration = duration;
79+
return false;
80+
}
81+
}
82+
delayMicroseconds(1);
83+
}
84+
85+
// store debug info
86+
m_awaitLevel = level;
87+
m_awaitDuration = l_timer.duration();
88+
return false;
89+
}
90+
91+
void low() { digitalWrite(m_pin, LOW); }
92+
void high() { digitalWrite(m_pin, HIGH); }
93+
void input() { pinMode(m_pin, INPUT); }
94+
void output() { pinMode(m_pin, OUTPUT); }
95+
96+
template <typename T>
97+
bool receive(T *f_val) {
98+
for (int i = 0; i < CBitLength<T>::bitLength; ++i) {
99+
if (!await(HIGH, 0U, 100U)) {
100+
m_awaitBit = i;
101+
return false;
102+
}
103+
104+
uint32_t l_count;
105+
if (!await(LOW, 0U, 100U, &l_count))
106+
return false;
107+
108+
if (l_count > 50)
109+
*f_val |= (1U << (CBitLength<T>::bitLength - 1 - i));
110+
}
111+
return true;
112+
}
113+
114+
bool start()
115+
{
116+
output();
117+
low();
118+
delay(3);
119+
input();
120+
return await(LOW, 0U, 100U, &m_start1)
121+
&& await(HIGH, 50U, 120U, &m_start2)
122+
&& await(LOW, 50U, 120U, &m_start3);
123+
}
124+
125+
bool receiveTemp() {
126+
const bool l_res = receive(&m_temperature);
127+
m_tempDone = true;
128+
return l_res;
129+
}
130+
bool receiveHumidity() {
131+
const bool l_res = receive(&m_humidity);
132+
m_humidityDone = true;
133+
return l_res;
134+
}
135+
bool receiveParity() {
136+
const bool l_res = receive(&m_parity);
137+
m_parityDone = true;
138+
return l_res;
139+
}
140+
141+
public:
142+
143+
bool run() {
144+
return start()
145+
&& receiveHumidity()
146+
&& receiveTemp()
147+
&& receiveParity();
148+
}
149+
};

readme.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Install
2+
#######
3+
4+
Install all requirements:
5+
6+
pip3 install -r requirements.txt
7+
8+
Then run `setup.py`:
9+
10+
python3 setup.py install
11+
12+
Run with the `--user` flag to install without root rights.

requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
cython
2+
pytest

setup.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import setuptools
2+
import Cython.Build
3+
4+
class BinaryDistribution(setuptools.Distribution):
5+
def is_pure(self):
6+
return False
7+
8+
setuptools.setup(
9+
name='am2302',
10+
version='0.1.0',
11+
distclass=BinaryDistribution,
12+
13+
ext_modules=Cython.Build.cythonize([
14+
setuptools.Extension(
15+
name='am2302',
16+
language='c++',
17+
sources=['am2302.pyx'],
18+
include_dirs=['inc'],
19+
#library_dirs=['lib'],
20+
libraries=['wiringPi'],
21+
#define_macros=[],
22+
#extra_compile_args=[],
23+
),
24+
], language_level=3),
25+
)

test/test_read.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import time
2+
import am2302
3+
4+
def test_read():
5+
# sleep a lil so we don't overload the sensor
6+
time.sleep(1)
7+
8+
assert not am2302.initialized()
9+
10+
# read data
11+
d = am2302.read()
12+
13+
# everything should be initialized
14+
assert am2302.initialized()
15+
16+
# result should be valid
17+
assert d is not None
18+
19+
# some simple range checks
20+
assert d.humidity >= 0.0
21+
assert d.humidity <= 100.0
22+
assert d.temperature >= -50.0
23+
assert d.temperature <= 100.0

0 commit comments

Comments
 (0)