Skip to content

Commit 5190168

Browse files
committed
Add configuration file and fix some issues
1 parent db17f7d commit 5190168

6 files changed

Lines changed: 149 additions & 38 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,5 @@ ENV/
8787

8888
# Rope project settings
8989
.ropeproject
90+
91+
/config.yml

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,37 @@
22

33
Python 3 program for the luftdaten.info sensor network.
44

5+
56
## Supported Hardware
67

78
* [Nova Fitness SDS011](http://aqicn.org/sensor/sds011/) or compatible connected via `ttyUSB` for dust
89
* [Bosch BME 2280](https://www.bosch-sensortec.com/bst/products/all_products/bme280) connected via I²C for temperature, humidity and pressure
910

11+
1012
## Used libraries
1113

1214
* https://gitlab.com/frankrich/sds011_particle_sensor
1315
* https://github.com/adafruit/Adafruit_Python_BME280
1416

17+
1518
## Dependencies
1619

17-
apt install python3-numpy python3-requests
20+
apt install python3-numpy python3-requests python3-yaml python3-serial
21+
22+
You also need to install [Adafruit_Python_BME280](https://github.com/adafruit/Adafruit_Python_BME280):
23+
24+
apt-get update
25+
apt-get install build-essential python-pip python-dev python-smbus git
26+
git clone https://github.com/adafruit/Adafruit_Python_GPIO.git
27+
cd Adafruit_Python_GPIO
28+
python3 setup.py install
29+
30+
31+
## Configuration
32+
33+
Copy the `config.yml.default` to `config.yml` and adjust the settings.
34+
1835

19-
## To-Do
36+
## Running a systemd unit
2037

21-
* submit data to luftdaten.info
38+
Take a look at the [dusty.unit](contrib/dusty.unit).

config.yml.default

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
luftdaten:
2+
enabled: true
3+
sensor: raspi-0000000000abcdef
4+
5+
influxdb:
6+
enabled: false
7+
url: 'http://example.com:8086/write?precision=s&db=sensors'
8+
username: 'sensors'
9+
password: 'secret'
10+
node: 'esp8266-123456'

contrib/dusty.unit

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# /etc/systemd/system/dusty.service
2+
3+
[Unit]
4+
Description=Dusty
5+
Wants=network-online.target
6+
After=network-online.target
7+
8+
[Service]
9+
User=dusty
10+
WorkingDirectoy=/home/dusty/python
11+
ExecStart=/home/dusty/python/main.py
12+
13+
[Install]
14+
WantedBy=multi-user.target

main.py

Lines changed: 96 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#!/usr/bin/env python3
22

33
import sys
4+
import os
5+
import yaml
46

57
# Import-Pfade setzen
6-
sys.path.append("sds011")
7-
sys.path.append("bme280")
8+
sys.path.append(os.path.join(sys.path[0],"sds011"))
9+
sys.path.append(os.path.join(sys.path[0],"bme280"))
810

911
import time
1012
import json
@@ -13,10 +15,13 @@
1315
from sds011 import SDS011
1416
from Adafruit_BME280 import *
1517

16-
# Logger initialisieren
17-
#import logging
18-
#logger = logging.getLogger()
19-
#logger.setLevel(logging.INFO)
18+
# Config
19+
with open("config.yml", 'r') as ymlfile:
20+
config = yaml.load(ymlfile)
21+
22+
# Logging
23+
import logging
24+
logging.basicConfig(level=logging.DEBUG)
2025

2126
bme280 = BME280(
2227
address=0x76,
@@ -34,33 +39,93 @@
3439
# Set dutycyle to nocycle (permanent)
3540
dusty.dutycycle = 0
3641

42+
class Measurement:
43+
def __init__(self):
44+
pm25_values = []
45+
pm10_values = []
46+
dusty.workstate = SDS011.WorkStates.Measuring
47+
try:
48+
for a in range(8):
49+
values = dusty.get_values()
50+
if values is not None:
51+
pm10_values.append(values[0])
52+
pm25_values.append(values[1])
53+
finally:
54+
dusty.workstate = SDS011.WorkStates.Sleeping
55+
56+
self.pm25_value = np.mean(pm25_values)
57+
self.pm10_value = np.mean(pm10_values)
58+
self.temperature = bme280.read_temperature()
59+
self.humidity = bme280.read_humidity()
60+
self.pressure = bme280.read_pressure()
61+
62+
def sendInflux(self):
63+
cfg = config['influxdb']
64+
65+
if not cfg['enabled']:
66+
return
67+
68+
data = "feinstaub,node={} SDS_P1={:0.2f},SDS_P2={:0.2f},BME280_temperature={:0.2f},BME280_pressure={:0.2f},BME280_humidity={:0.2f}".format(
69+
cfg['node'],
70+
self.pm10_value,
71+
self.pm25_value,
72+
self.temperature,
73+
self.pressure,
74+
self.humidity,
75+
)
76+
77+
requests.post(cfg['url'],
78+
auth=(cfg['username'], cfg['password']),
79+
data=data,
80+
)
81+
82+
def sendLuftdaten(self):
83+
if not config['luftdaten']['enabled']:
84+
return
85+
86+
self.__pushLuftdaten('https://api-rrd.madavi.de/data.php', 0, {
87+
"SDS_P1": self.pm10_value,
88+
"SDS_P2": self.pm25_value,
89+
"BME280_temperature": self.temperature,
90+
"BME280_pressure": self.pressure,
91+
"BME280_humidity": self.humidity,
92+
})
93+
self.__pushLuftdaten('https://api.luftdaten.info/v1/push-sensor-data/', 1, {
94+
"P1": self.pm10_value,
95+
"P2": self.pm25_value,
96+
})
97+
self.__pushLuftdaten('https://api.luftdaten.info/v1/push-sensor-data/', 11, {
98+
"temperature": self.temperature,
99+
"pressure": self.pressure,
100+
"humidity": self.humidity,
101+
})
102+
103+
104+
def __pushLuftdaten(self, url, pin, values):
105+
requests.post(url,
106+
json={
107+
"software_version": "python-dusty 0.0.1",
108+
"sensordatavalues": [{"value_type": key, "value": val} for key, val in values.items()],
109+
},
110+
headers={
111+
"X-PIN": str(pin),
112+
"X-Sensor": config['luftdaten']['sensor'],
113+
}
114+
)
115+
116+
37117

38118
def run():
39-
pm25_values = []
40-
pm10_values = []
41-
dusty.workstate = SDS011.WorkStates.Measuring
42-
try:
43-
for a in range(8):
44-
values = dusty.get_values()
45-
if values is not None:
46-
pm10_values.append(values[0])
47-
pm25_values.append(values[1])
48-
finally:
49-
dusty.workstate = SDS011.WorkStates.Sleeping
50-
51-
pm25_value = np.mean(pm25_values)
52-
pm10_value = np.mean(pm10_values)
53-
54-
print('pm2.5 = {:f} '.format(pm25_value))
55-
print('pm10 = {:f} '.format(pm10_value))
56-
57-
temperature = bme280.read_temperature()
58-
humidity = bme280.read_humidity()
59-
pressure = bme280.read_pressure()
60-
61-
print('Temp = {:0.2f} deg C'.format(temperature))
62-
print('Humidity = {:0.2f} %'.format(humidity))
63-
print('Pressure = {:0.2f} hPa'.format(pressure/100))
119+
m = Measurement()
120+
121+
print('pm2.5 = {:f} '.format(m.pm25_value))
122+
print('pm10 = {:f} '.format(m.pm10_value))
123+
print('Temp = {:0.2f} deg C'.format(m.temperature))
124+
print('Humidity = {:0.2f} %'.format(m.humidity))
125+
print('Pressure = {:0.2f} hPa'.format(m.pressure/100))
126+
127+
m.sendLuftdaten()
128+
m.sendInflux()
64129

65130

66131
starttime = time.time()

sds011/sds011.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class SDS011(object):
5555
Each serial message starts with 0xAA and ends with 0xAB.
5656
If the message will be send to the senor, the second byte is 0xB4, the 16th and 17th
5757
byte is 0xFF.
58-
If it is a response of the sensor of a message sended before, the second byte of the response
58+
If it is a response of the sensor of a message sent before, the second byte of the response
5959
is 0xC5. If its a response send automatic by the sensor in "Initiative" Report Mode,
6060
the second byte is 0xC0.
6161
The third byte is always the command byte except in response to a request command or a sensor
@@ -370,11 +370,14 @@ def __send(self, command, data):
370370
self.device.flush()
371371
if written_bytes != len(bytes_to_send):
372372
raise IOError("Not all bytes written")
373-
logging.debug("Sended and flushed: %s", bytes_to_send)
373+
logging.debug("Sent and flushed: %s", bytes_to_send)
374374
# proof the receive value
375375
received = self.__response(command)
376376
logging.debug("Received: %s", received)
377377

378+
if len(received) == 0:
379+
raise ValueError("nothing received")
380+
378381
# when no command or command is request command,
379382
# second byte has to be ReceiveByte
380383
if ((command is None or command == self.Command.Request) and
@@ -399,7 +402,7 @@ def __send(self, command, data):
399402

400403
def __response(self, command=None):
401404
'''gets and proofs the response from the senor. Response can be
402-
the response of a sended command or just the measured date while sensor
405+
the response of a sent command or just the measured date while sensor
403406
is in reportmode Initiative'''
404407
# receive the response while listening serial input
405408
bytes_received = bytearray(1)
@@ -437,7 +440,7 @@ def __response(self, command=None):
437440
if bytes_received[2] != command.value:
438441
raise IOError(
439442
"Third byte of serial data {0} received is not belonging \
440-
to prior sended command {1}".format(bytes_received[2], command.name))
443+
to prior sent command {1}".format(bytes_received[2], command.name))
441444

442445
if command is None or command is self.Command.Request:
443446
if bytes_received[1] is not self.__ReceiveByte:

0 commit comments

Comments
 (0)