From 6f08b7d4ec4148dfc9bcb480f92d8ca163ad3494 Mon Sep 17 00:00:00 2001 From: "Michael (MicroD)" <72285408+michaeldufault@users.noreply.github.com> Date: Wed, 5 Jul 2023 23:14:52 +0100 Subject: [PATCH 1/4] Sensor.Community Updates Updates all language to reflect the change from Luftdaten > Sensor.Community which occured at the end of 2019. Also includes a 'nodisplay' version of the script which can be used for reduced power consumption once sensor data is confirmed to be transmitting successfully to Sensor.Community. --- examples/luftdaten.py | 46 +++---- examples/luftdaten_combined.py | 37 +++--- examples/sensorcommunity-nodisplay.py | 176 ++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 42 deletions(-) create mode 100644 examples/sensorcommunity-nodisplay.py diff --git a/examples/luftdaten.py b/examples/luftdaten.py index a78909f..b3f2dd4 100755 --- a/examples/luftdaten.py +++ b/examples/luftdaten.py @@ -20,15 +20,15 @@ level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') -logging.info("""luftdaten.py - Reads temperature, pressure, humidity, -#PM2.5, and PM10 from Enviro plus and sends data to Luftdaten, -#the citizen science air quality project. +logging.info("""sensorcommunity.py - Reads temperature, pressure, humidity, +#PM2.5, and PM10 from Enviro plus and sends data to Sensor.Community, +#a contributors driven sensor network that creates Open Environmental Data. -#Note: you'll need to register with Luftdaten at: -#https://meine.luftdaten.info/ and enter your Raspberry Pi +#Note: You'll need to register with Sensor.Community at: +#https://devices.sensor.community/ 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. +#Sensor.Community map. #Press Ctrl+C to exit! @@ -119,7 +119,7 @@ def display_status(): disp.display(img) -def send_to_luftdaten(values, id): +def send_to_sensorcommunity(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")) @@ -133,7 +133,7 @@ def send_to_luftdaten(values, id): resp_pm = requests.post( "https://api.sensor.community/v1/push-sensor-data/", json={ - "software_version": "enviro-plus 0.0.1", + "software_version": "enviro-plus 0.0.6", "sensordatavalues": pm_values_json }, headers={ @@ -145,17 +145,17 @@ def send_to_luftdaten(values, id): timeout=5 ) except requests.exceptions.ConnectionError as e: - logging.warning('Sensor.Community (Luftdaten) PM Connection Error: {}'.format(e)) + logging.warning('Sensor.Community PM Connection Error: {}'.format(e)) except requests.exceptions.Timeout as e: - logging.warning('Sensor.Community (Luftdaten) PM Timeout Error: {}'.format(e)) + logging.warning('Sensor.Community PM Timeout Error: {}'.format(e)) except requests.exceptions.RequestException as e: - logging.warning('Sensor.Community (Luftdaten) PM Request Error: {}'.format(e)) + logging.warning('Sensor.Community PM Request Error: {}'.format(e)) try: resp_bmp = requests.post( "https://api.sensor.community/v1/push-sensor-data/", json={ - "software_version": "enviro-plus 0.0.1", + "software_version": "enviro-plus 0.0.6", "sensordatavalues": temp_values_json }, headers={ @@ -167,26 +167,26 @@ def send_to_luftdaten(values, id): timeout=5 ) except requests.exceptions.ConnectionError as e: - logging.warning('Sensor.Community (Luftdaten) Climate Connection Error: {}'.format(e)) + logging.warning('Sensor.Community Climate Connection Error: {}'.format(e)) except requests.exceptions.Timeout as e: - logging.warning('Sensor.Community (Luftdaten) Climate Timeout Error: {}'.format(e)) + logging.warning('Sensor.Community Climate Timeout Error: {}'.format(e)) except requests.exceptions.RequestException as e: - logging.warning('Sensor.Community (Luftdaten) Climate Request Error: {}'.format(e)) + logging.warning('Sensor.Community Climate Request Error: {}'.format(e)) if resp_pm is not None and resp_bmp is not None: if resp_pm.ok and resp_bmp.ok: return True else: - logging.warning('Luftdaten Error. PM: {}, Climate: {}'.format(resp_pm.reason, resp_bmp.reason)) + logging.warning('Sensor.Community Error. PM: {}, Climate: {}'.format(resp_pm.reason, resp_bmp.reason)) return False else: return False -# Compensation factor for temperature -comp_factor = 2.25 +# Compensation factor for temperature default 2.25 +comp_factor = 1.0 -# Raspberry Pi ID to send to Luftdaten +# Raspberry Pi ID to send to Sensor.Community id = "raspi-" + get_serial_number() # Width and height to calculate text position @@ -204,7 +204,7 @@ def send_to_luftdaten(values, id): time_since_update = 0 update_time = time.time() -# Main loop to read data, display, and send to Luftdaten +# Main loop to read data, display, and send to Sensor.Community while True: try: values = read_values() @@ -212,10 +212,10 @@ def send_to_luftdaten(values, id): if time_since_update > 145: logging.info(values) update_time = time.time() - if send_to_luftdaten(values, id): - logging.info("Luftdaten Response: OK") + if send_to_sensorcommunity(values, id): + logging.info("Sensor.Community Response: OK") else: - logging.warning("Luftdaten Response: Failed") + logging.warning("Sensor.Community Response: Failed") display_status() except Exception as e: logging.warning('Main Loop Exception: {}'.format(e)) diff --git a/examples/luftdaten_combined.py b/examples/luftdaten_combined.py index 1d920db..96f0d86 100644 --- a/examples/luftdaten_combined.py +++ b/examples/luftdaten_combined.py @@ -22,20 +22,19 @@ except ImportError: import ltr559 -print("""luftdaten_combined.py - This combines the functionality of luftdaten.py and combined.py +print("""sensorcommunity_combined.py - This combines the functionality of sensorcommunity.py and combined.py ================================================================================================ -Luftdaten INFO -Reads temperature, pressure, humidity, -PM2.5, and PM10 from Enviro plus and sends data to Luftdaten, -the citizen science air quality project. +sensorcommunity.py - Reads temperature, pressure, humidity, +#PM2.5, and PM10 from Enviro plus and sends data to Sensor.Community, +#a contributors driven sensor network that creates Open Environmental Data. -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 Sensor.Community at: +#https://devices.sensor.community/ 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 +#Sensor.Community map. -Press Ctrl+C to exit! +#Press Ctrl+C to exit! ======================================================================== @@ -244,7 +243,7 @@ def display_everything(): st7735.display(img) -def send_to_luftdaten(values, id): +def send_to_sensorcommunity(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")) @@ -254,9 +253,9 @@ def send_to_luftdaten(values, id): for key, val in temp_values.items()] resp_1 = requests.post( - "https://api.luftdaten.info/v1/push-sensor-data/", + "https://api.sensor.community/v1/push-sensor-data/", json={ - "software_version": "enviro-plus 0.0.1", + "software_version": "enviro-plus 0.0.6", "sensordatavalues": pm_values_json }, headers={ @@ -268,9 +267,9 @@ def send_to_luftdaten(values, id): ) resp_2 = requests.post( - "https://api.luftdaten.info/v1/push-sensor-data/", + "https://api.sensor.community/v1/push-sensor-data/", json={ - "software_version": "enviro-plus 0.0.1", + "software_version": "enviro-plus 0.0.6", "sensordatavalues": temp_values_json }, headers={ @@ -290,7 +289,7 @@ def send_to_luftdaten(values, id): # Compensation factor for temperature comp_factor = 1 -# Raspberry Pi ID to send to Luftdaten +# Raspberry Pi ID to send to Sensor.Community id = "raspi-" + get_serial_number() @@ -318,7 +317,7 @@ def send_to_luftdaten(values, id): update_time = time.time() cpu_temps_len = float(len(cpu_temps)) -# Main loop to read data, display, and send to Luftdaten +# Main loop to read data, display, and send to Sensor.Community while True: try: curtime = time.time() @@ -347,7 +346,7 @@ def send_to_luftdaten(values, id): if time_since_update > 145: values = read_values(comp_temp, raw_press*100, raw_humid, raw_pm25, raw_pm10) - resp = send_to_luftdaten(values, id) + resp = send_to_sensorcommunity(values, id) update_time = curtime print("Response: {}\n".format("ok" if resp else "failed")) diff --git a/examples/sensorcommunity-nodisplay.py b/examples/sensorcommunity-nodisplay.py new file mode 100644 index 0000000..23ffa03 --- /dev/null +++ b/examples/sensorcommunity-nodisplay.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 + +import requests +import ST7735 +import time +from bme280 import BME280 +from pms5003 import PMS5003, ReadTimeoutError, ChecksumMismatchError +from subprocess import PIPE, Popen, check_output + +try: + from smbus2 import SMBus +except ImportError: + from smbus import SMBus +import logging + +logging.basicConfig( + format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s', + level=logging.INFO, + datefmt='%Y-%m-%d %H:%M:%S') + +logging.info("""sensorcommunity-nodisplay.py - Reads temperature, pressure, humidity, +#PM2.5, and PM10 from Enviro plus and sends data to Sensor.Community, +#a contributors driven sensor network that creates Open Environmental Data. +#nodisplay version removes the display funcationality once desired setup is working. + +#Note: You'll need to register with Sensor.Community at: +#https://devices.sensor.community/ 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 +#Sensor.Community map. + +#Press Ctrl+C to exit! + +#""") + +bus = SMBus(1) + +# Create BME280 instance +bme280 = BME280(i2c_dev=bus) + +# Create PMS5003 instance +pms5003 = PMS5003() + +# Read values from BME280 and PMS5003 and return as dict +def read_values(): + values = {} + cpu_temp = get_cpu_temperature() + raw_temp = bme280.get_temperature() + comp_temp = raw_temp - ((cpu_temp - raw_temp) / comp_factor) + values["temperature"] = "{:.2f}".format(comp_temp) + values["pressure"] = "{:.2f}".format(bme280.get_pressure() * 100) + values["humidity"] = "{:.2f}".format(bme280.get_humidity()) + 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, ChecksumMismatchError): + logging.info("Failed to read PMS5003. Reseting and retrying.") + 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 + +# Get the temperature of the CPU for compensation +def get_cpu_temperature(): + with open("/sys/class/thermal/thermal_zone0/temp", "r") as f: + temp = f.read() + temp = int(temp) / 1000.0 + return temp + +# Get Raspberry Pi serial number to use as ID +def get_serial_number(): + with open('/proc/cpuinfo', 'r') as f: + for line in f: + if line[0:6] == 'Serial': + return line.split(":")[1].strip() + +# Check for Wi-Fi connection +def check_wifi(): + if check_output(['hostname', '-I']): + return True + else: + return False + +def send_to_sensorcommunity(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")) + + 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_pm = None + resp_bmp = None + + try: + resp_pm = requests.post( + "https://api.sensor.community/v1/push-sensor-data/", + json={ + "software_version": "enviro-plus 0.0.6", + "sensordatavalues": pm_values_json + }, + headers={ + "X-PIN": "1", + "X-Sensor": id, + "Content-Type": "application/json", + "cache-control": "no-cache" + }, + timeout=5 + ) + except requests.exceptions.ConnectionError as e: + logging.warning('Sensor.Community PM Connection Error: {}'.format(e)) + except requests.exceptions.Timeout as e: + logging.warning('Sensor.Community PM Timeout Error: {}'.format(e)) + except requests.exceptions.RequestException as e: + logging.warning('Sensor.Community PM Request Error: {}'.format(e)) + + try: + resp_bmp = requests.post( + "https://api.sensor.community/v1/push-sensor-data/", + json={ + "software_version": "enviro-plus 0.0.6", + "sensordatavalues": temp_values_json + }, + headers={ + "X-PIN": "11", + "X-Sensor": id, + "Content-Type": "application/json", + "cache-control": "no-cache" + }, + timeout=5 + ) + except requests.exceptions.ConnectionError as e: + logging.warning('Sensor.Community Climate Connection Error: {}'.format(e)) + except requests.exceptions.Timeout as e: + logging.warning('Sensor.Community Climate Timeout Error: {}'.format(e)) + except requests.exceptions.RequestException as e: + logging.warning('Sensor.Community Climate Request Error: {}'.format(e)) + + if resp_pm is not None and resp_bmp is not None: + if resp_pm.ok and resp_bmp.ok: + return True + else: + logging.warning('Sensor.Community Error. PM: {}, Climate: {}'.format(resp_pm.reason, resp_bmp.reason)) + return False + else: + return False + +# Compensation factor for temperature default 2.25 +comp_factor = 1.0 + +# Raspberry Pi ID to send to Sensor.Community +id = "raspi-" + get_serial_number() + +# Log Raspberry Pi serial and Wi-Fi status +logging.info("Raspberry Pi serial: {}".format(get_serial_number())) +logging.info("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 Sensor.Community +while True: + try: + values = read_values() + time_since_update = time.time() - update_time + if time_since_update > 145: + logging.info(values) + update_time = time.time() + if send_to_sensorcommunity(values, id): + logging.info("Sensor.Community Response: OK") + else: + logging.warning("Sensor.Community Response: Failed") + # display_status() + except Exception as e: + logging.warning('Main Loop Exception: {}'.format(e)) From e3b4210e12f2d1bbe47c4d181c171ad0967fb2bb Mon Sep 17 00:00:00 2001 From: "Michael (MicroD)" <72285408+michaeldufault@users.noreply.github.com> Date: Wed, 5 Jul 2023 23:15:36 +0100 Subject: [PATCH 2/4] Rename luftdaten.py to sensorcommunity.py --- examples/{luftdaten.py => sensorcommunity.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{luftdaten.py => sensorcommunity.py} (100%) diff --git a/examples/luftdaten.py b/examples/sensorcommunity.py similarity index 100% rename from examples/luftdaten.py rename to examples/sensorcommunity.py From 6876272f8ec0ed09b01dbbd52d118d826994f3a4 Mon Sep 17 00:00:00 2001 From: "Michael (MicroD)" <72285408+michaeldufault@users.noreply.github.com> Date: Wed, 5 Jul 2023 23:15:52 +0100 Subject: [PATCH 3/4] Rename luftdaten_combined.py to sensorcommunity_combined.py --- examples/{luftdaten_combined.py => sensorcommunity_combined.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{luftdaten_combined.py => sensorcommunity_combined.py} (100%) diff --git a/examples/luftdaten_combined.py b/examples/sensorcommunity_combined.py similarity index 100% rename from examples/luftdaten_combined.py rename to examples/sensorcommunity_combined.py From c1579ec52c5fdbab11b4ad1dc7b70b69659c918a Mon Sep 17 00:00:00 2001 From: "Michael (MicroD)" <72285408+michaeldufault@users.noreply.github.com> Date: Wed, 5 Jul 2023 23:16:22 +0100 Subject: [PATCH 4/4] Update and rename sensorcommunity-nodisplay.py to sensorcommunity_nodisplay.py --- ...ensorcommunity-nodisplay.py => sensorcommunity_nodisplay.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename examples/{sensorcommunity-nodisplay.py => sensorcommunity_nodisplay.py} (98%) diff --git a/examples/sensorcommunity-nodisplay.py b/examples/sensorcommunity_nodisplay.py similarity index 98% rename from examples/sensorcommunity-nodisplay.py rename to examples/sensorcommunity_nodisplay.py index 23ffa03..5005e72 100644 --- a/examples/sensorcommunity-nodisplay.py +++ b/examples/sensorcommunity_nodisplay.py @@ -18,7 +18,7 @@ level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') -logging.info("""sensorcommunity-nodisplay.py - Reads temperature, pressure, humidity, +logging.info("""sensorcommunity_nodisplay.py - Reads temperature, pressure, humidity, #PM2.5, and PM10 from Enviro plus and sends data to Sensor.Community, #a contributors driven sensor network that creates Open Environmental Data. #nodisplay version removes the display funcationality once desired setup is working.