Merge pull request #62 from pimoroni/patch-noise-and-test-fixes
[EVA-2020-02-2.git] / examples / luftdaten.py
index 2177a56162539e755bda192a6b413e60a6949e82..84f111776c6f65c0e3d0b5e39b75c8d31625d60a 100755 (executable)
@@ -1,25 +1,28 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 
-import time
-import json
 import requests
 import ST7735
 import requests
 import ST7735
+import time
 from bme280 import BME280
 from bme280 import BME280
-from pms5003 import PMS5003
+from pms5003 import PMS5003, ReadTimeoutError
 from subprocess import PIPE, Popen, check_output
 from PIL import Image, ImageDraw, ImageFont
 from subprocess import PIPE, Popen, check_output
 from PIL import Image, ImageDraw, ImageFont
+from fonts.ttf import RobotoMedium as UserFont
 
 try:
     from smbus2 import SMBus
 except ImportError:
     from smbus import SMBus
 
 
 try:
     from smbus2 import SMBus
 except ImportError:
     from smbus import SMBus
 
-print("""luftdaten.py - Reads temperature, pressure, humidity, PM2.5, and PM10 from
-Enviro plus and sends data to Luftdaten, the citizen science air quality project.
+print("""luftdaten.py - Reads temperature, pressure, humidity,
+PM2.5, and PM10 from Enviro plus and sends data to Luftdaten,
+the citizen science air quality project.
 
 
-Note: you'll need to register with Luftdaten at: https://meine.luftdaten.info/ and
-enter your Raspberry Pi serial number that's displayed on the Enviro plus LCD
-along with the other details before the data appears on the Luftdaten map.
+Note: you'll need to register with Luftdaten at:
+https://meine.luftdaten.info/ and enter your Raspberry Pi
+serial number that's displayed on the Enviro plus LCD along
+with the other details before the data appears on the
+Luftdaten map.
 
 Press Ctrl+C to exit!
 
 
 Press Ctrl+C to exit!
 
@@ -30,9 +33,6 @@ bus = SMBus(1)
 # Create BME280 instance
 bme280 = BME280(i2c_dev=bus)
 
 # Create BME280 instance
 bme280 = BME280(i2c_dev=bus)
 
-# Create PMS5003 instance
-pms5003 = PMS5003()
-
 # Create LCD instance
 disp = ST7735.ST7735(
     port=0,
 # Create LCD instance
 disp = ST7735.ST7735(
     port=0,
@@ -46,6 +46,10 @@ disp = ST7735.ST7735(
 # Initialize display
 disp.begin()
 
 # Initialize display
 disp.begin()
 
+# Create PMS5003 instance
+pms5003 = PMS5003()
+
+
 # Read values from BME280 and PMS5003 and return as dict
 def read_values():
     values = {}
 # Read values from BME280 and PMS5003 and return as dict
 def read_values():
     values = {}
@@ -55,23 +59,32 @@ def read_values():
     values["temperature"] = "{:.2f}".format(comp_temp)
     values["pressure"] = "{:.2f}".format(bme280.get_pressure() * 100)
     values["humidity"] = "{:.2f}".format(bme280.get_humidity())
     values["temperature"] = "{:.2f}".format(comp_temp)
     values["pressure"] = "{:.2f}".format(bme280.get_pressure() * 100)
     values["humidity"] = "{:.2f}".format(bme280.get_humidity())
-    pm_values = pms5003.read()
-    values["P2"] = str(pm_values.pm_ug_per_m3(2.5))
-    values["P1"] = str(pm_values.pm_ug_per_m3(10))
+    try:
+        pm_values = pms5003.read()
+        values["P2"] = str(pm_values.pm_ug_per_m3(2.5))
+        values["P1"] = str(pm_values.pm_ug_per_m3(10))
+    except ReadTimeoutError:
+        pms5003.reset()
+        pm_values = pms5003.read()
+        values["P2"] = str(pm_values.pm_ug_per_m3(2.5))
+        values["P1"] = str(pm_values.pm_ug_per_m3(10))
     return values
 
     return values
 
+
 # Get CPU temperature to use for compensation
 def get_cpu_temperature():
 # Get CPU temperature to use for compensation
 def get_cpu_temperature():
-    process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
+    process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
     output, _error = process.communicate()
     return float(output[output.index('=') + 1:output.rindex("'")])
 
     output, _error = process.communicate()
     return float(output[output.index('=') + 1:output.rindex("'")])
 
+
 # Get Raspberry Pi serial number to use as ID
 def get_serial_number():
 # Get Raspberry Pi serial number to use as ID
 def get_serial_number():
-    with open('/proc/cpuinfo','r') as f:
+    with open('/proc/cpuinfo', 'r') as f:
         for line in f:
         for line in f:
-            if line[0:6]=='Serial':
-                return(line.split(":")[1].strip())
+            if line[0:6] == 'Serial':
+                return line.split(":")[1].strip()
+
 
 # Check for Wi-Fi connection
 def check_wifi():
 
 # Check for Wi-Fi connection
 def check_wifi():
@@ -80,6 +93,7 @@ def check_wifi():
     else:
         return False
 
     else:
         return False
 
+
 # Display Raspberry Pi serial and Wi-Fi status on LCD
 def display_status():
     wifi_status = "connected" if check_wifi() else "disconnected"
 # Display Raspberry Pi serial and Wi-Fi status on LCD
 def display_status():
     wifi_status = "connected" if check_wifi() else "disconnected"
@@ -96,30 +110,36 @@ def display_status():
     draw.text((x, y), message, font=font, fill=text_colour)
     disp.display(img)
 
     draw.text((x, y), message, font=font, fill=text_colour)
     disp.display(img)
 
+
 def send_to_luftdaten(values, id):
     pm_values = dict(i for i in values.items() if i[0].startswith("P"))
     temp_values = dict(i for i in values.items() if not i[0].startswith("P"))
 
 def send_to_luftdaten(values, id):
     pm_values = dict(i for i in values.items() if i[0].startswith("P"))
     temp_values = dict(i for i in values.items() if not i[0].startswith("P"))
 
-    resp_1 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/",
+    pm_values_json = [{"value_type": key, "value": val} for key, val in pm_values.items()]
+    temp_values_json = [{"value_type": key, "value": val} for key, val in temp_values.items()]
+
+    resp_1 = requests.post(
+        "https://api.luftdaten.info/v1/push-sensor-data/",
         json={
             "software_version": "enviro-plus 0.0.1",
         json={
             "software_version": "enviro-plus 0.0.1",
-            "sensordatavalues": [{"value_type": key, "value": val} for key, val in pm_values.items()]
+            "sensordatavalues": pm_values_json
         },
         headers={
         },
         headers={
-            "X-PIN":    "1",
+            "X-PIN": "1",
             "X-Sensor": id,
             "Content-Type": "application/json",
             "cache-control": "no-cache"
         }
     )
 
             "X-Sensor": id,
             "Content-Type": "application/json",
             "cache-control": "no-cache"
         }
     )
 
-    resp_2 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/",
+    resp_2 = requests.post(
+        "https://api.luftdaten.info/v1/push-sensor-data/",
         json={
             "software_version": "enviro-plus 0.0.1",
         json={
             "software_version": "enviro-plus 0.0.1",
-            "sensordatavalues": [{"value_type": key, "value": val} for key, val in temp_values.items()]
+            "sensordatavalues": temp_values_json
         },
         headers={
         },
         headers={
-            "X-PIN":    "11",
+            "X-PIN": "11",
             "X-Sensor": id,
             "Content-Type": "application/json",
             "cache-control": "no-cache"
             "X-Sensor": id,
             "Content-Type": "application/json",
             "cache-control": "no-cache"
@@ -131,8 +151,9 @@ def send_to_luftdaten(values, id):
     else:
         return False
 
     else:
         return False
 
+
 # Compensation factor for temperature
 # Compensation factor for temperature
-comp_factor = 1.2
+comp_factor = 2.25
 
 # Raspberry Pi ID to send to Luftdaten
 id = "raspi-" + get_serial_number()
 
 # Raspberry Pi ID to send to Luftdaten
 id = "raspi-" + get_serial_number()
@@ -143,19 +164,25 @@ HEIGHT = disp.height
 
 # Text settings
 font_size = 16
 
 # Text settings
 font_size = 16
-font = ImageFont.truetype("fonts/Asap/Asap-Bold.ttf", font_size)
+font = ImageFont.truetype(UserFont, font_size)
 
 # Display Raspberry Pi serial and Wi-Fi status
 print("Raspberry Pi serial: {}".format(get_serial_number()))
 print("Wi-Fi: {}\n".format("connected" if check_wifi() else "disconnected"))
 
 
 # Display Raspberry Pi serial and Wi-Fi status
 print("Raspberry Pi serial: {}".format(get_serial_number()))
 print("Wi-Fi: {}\n".format("connected" if check_wifi() else "disconnected"))
 
+time_since_update = 0
+update_time = time.time()
+
 # Main loop to read data, display, and send to Luftdaten
 while True:
     try:
 # Main loop to read data, display, and send to Luftdaten
 while True:
     try:
+        time_since_update = time.time() - update_time
         values = read_values()
         print(values)
         values = read_values()
         print(values)
-        resp = send_to_luftdaten(values, id)
-        print("Response: {}\n".format("ok" if resp else "failed"))
+        if time_since_update > 145:
+            resp = send_to_luftdaten(values, id)
+            update_time = time.time()
+            print("Response: {}\n".format("ok" if resp else "failed"))
         display_status()
     except Exception as e:
         print(e)
         display_status()
     except Exception as e:
         print(e)