Merge branch 'master' of github.com:pimoroni/enviroplus-python
authorPhil Howard <phil@gadgetoid.com>
Thu, 13 Jun 2019 09:15:24 +0000 (10:15 +0100)
committerPhil Howard <phil@gadgetoid.com>
Thu, 13 Jun 2019 09:15:24 +0000 (10:15 +0100)
17 files changed:
examples/compensated-temperature.py [new file with mode: 0755]
examples/fonts/Asap/Asap-Bold.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-BoldItalic.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-Italic.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-Medium.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-MediumItalic.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-Regular.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-SemiBold.ttf [new file with mode: 0644]
examples/fonts/Asap/Asap-SemiBoldItalic.ttf [new file with mode: 0644]
examples/fonts/Asap/OFL.txt [new file with mode: 0644]
examples/gas.py
examples/lcd.py [new file with mode: 0755]
examples/light.py
examples/luftdaten.py [new file with mode: 0755]
examples/particulates.py [moved from examples/particles.py with 77% similarity]
examples/weather.py [new file with mode: 0755]
library/enviroplus/gas.py

diff --git a/examples/compensated-temperature.py b/examples/compensated-temperature.py
new file mode 100755 (executable)
index 0000000..87daf97
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import time
+from bme280 import BME280
+from subprocess import PIPE, Popen
+
+try:
+    from smbus2 import SMBus
+except ImportError:
+    from smbus import SMBus
+
+print("""compensated-temperature.py - Use the CPU temperature to compensate temperature
+readings from the BME280 sensor. Method adapted from Initial State's Enviro pHAT
+review: https://medium.com/@InitialState/tutorial-review-enviro-phat-for-raspberry-pi-4cd6d8c63441
+
+Press Ctrl+C to exit!
+
+""")
+
+bus = SMBus(1)
+bme280 = BME280(i2c_dev=bus)
+
+def get_cpu_temperature():
+    process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
+    output, _error = process.communicate()
+    return float(output[output.index('=') + 1:output.rindex("'")])
+
+factor = 0.6
+
+while True:
+    cpu_temp = get_cpu_temperature()
+    raw_temp = bme280.get_temperature()
+    comp_temp = raw_temp - ((cpu_temp - raw_temp) / factor)
+    print("Compensated temperature: {:05.2f} *C".format(comp_temp))
+    time.sleep(1.0)
diff --git a/examples/fonts/Asap/Asap-Bold.ttf b/examples/fonts/Asap/Asap-Bold.ttf
new file mode 100644 (file)
index 0000000..52a14e5
Binary files /dev/null and b/examples/fonts/Asap/Asap-Bold.ttf differ
diff --git a/examples/fonts/Asap/Asap-BoldItalic.ttf b/examples/fonts/Asap/Asap-BoldItalic.ttf
new file mode 100644 (file)
index 0000000..df29023
Binary files /dev/null and b/examples/fonts/Asap/Asap-BoldItalic.ttf differ
diff --git a/examples/fonts/Asap/Asap-Italic.ttf b/examples/fonts/Asap/Asap-Italic.ttf
new file mode 100644 (file)
index 0000000..b07a0bc
Binary files /dev/null and b/examples/fonts/Asap/Asap-Italic.ttf differ
diff --git a/examples/fonts/Asap/Asap-Medium.ttf b/examples/fonts/Asap/Asap-Medium.ttf
new file mode 100644 (file)
index 0000000..81ef310
Binary files /dev/null and b/examples/fonts/Asap/Asap-Medium.ttf differ
diff --git a/examples/fonts/Asap/Asap-MediumItalic.ttf b/examples/fonts/Asap/Asap-MediumItalic.ttf
new file mode 100644 (file)
index 0000000..6f8d906
Binary files /dev/null and b/examples/fonts/Asap/Asap-MediumItalic.ttf differ
diff --git a/examples/fonts/Asap/Asap-Regular.ttf b/examples/fonts/Asap/Asap-Regular.ttf
new file mode 100644 (file)
index 0000000..af00196
Binary files /dev/null and b/examples/fonts/Asap/Asap-Regular.ttf differ
diff --git a/examples/fonts/Asap/Asap-SemiBold.ttf b/examples/fonts/Asap/Asap-SemiBold.ttf
new file mode 100644 (file)
index 0000000..5328f3f
Binary files /dev/null and b/examples/fonts/Asap/Asap-SemiBold.ttf differ
diff --git a/examples/fonts/Asap/Asap-SemiBoldItalic.ttf b/examples/fonts/Asap/Asap-SemiBoldItalic.ttf
new file mode 100644 (file)
index 0000000..6415ef2
Binary files /dev/null and b/examples/fonts/Asap/Asap-SemiBoldItalic.ttf differ
diff --git a/examples/fonts/Asap/OFL.txt b/examples/fonts/Asap/OFL.txt
new file mode 100644 (file)
index 0000000..ad56d30
--- /dev/null
@@ -0,0 +1,93 @@
+Copyright 2016 The Asap Project Authors (omnibus.type@gmail.com)\r
+\r
+This Font Software is licensed under the SIL Open Font License, Version 1.1.\r
+This license is copied below, and is also available with a FAQ at:\r
+http://scripts.sil.org/OFL\r
+\r
+\r
+-----------------------------------------------------------\r
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\r
+-----------------------------------------------------------\r
+\r
+PREAMBLE\r
+The goals of the Open Font License (OFL) are to stimulate worldwide\r
+development of collaborative font projects, to support the font creation\r
+efforts of academic and linguistic communities, and to provide a free and\r
+open framework in which fonts may be shared and improved in partnership\r
+with others.\r
+\r
+The OFL allows the licensed fonts to be used, studied, modified and\r
+redistributed freely as long as they are not sold by themselves. The\r
+fonts, including any derivative works, can be bundled, embedded, \r
+redistributed and/or sold with any software provided that any reserved\r
+names are not used by derivative works. The fonts and derivatives,\r
+however, cannot be released under any other type of license. The\r
+requirement for fonts to remain under this license does not apply\r
+to any document created using the fonts or their derivatives.\r
+\r
+DEFINITIONS\r
+"Font Software" refers to the set of files released by the Copyright\r
+Holder(s) under this license and clearly marked as such. This may\r
+include source files, build scripts and documentation.\r
+\r
+"Reserved Font Name" refers to any names specified as such after the\r
+copyright statement(s).\r
+\r
+"Original Version" refers to the collection of Font Software components as\r
+distributed by the Copyright Holder(s).\r
+\r
+"Modified Version" refers to any derivative made by adding to, deleting,\r
+or substituting -- in part or in whole -- any of the components of the\r
+Original Version, by changing formats or by porting the Font Software to a\r
+new environment.\r
+\r
+"Author" refers to any designer, engineer, programmer, technical\r
+writer or other person who contributed to the Font Software.\r
+\r
+PERMISSION & CONDITIONS\r
+Permission is hereby granted, free of charge, to any person obtaining\r
+a copy of the Font Software, to use, study, copy, merge, embed, modify,\r
+redistribute, and sell modified and unmodified copies of the Font\r
+Software, subject to the following conditions:\r
+\r
+1) Neither the Font Software nor any of its individual components,\r
+in Original or Modified Versions, may be sold by itself.\r
+\r
+2) Original or Modified Versions of the Font Software may be bundled,\r
+redistributed and/or sold with any software, provided that each copy\r
+contains the above copyright notice and this license. These can be\r
+included either as stand-alone text files, human-readable headers or\r
+in the appropriate machine-readable metadata fields within text or\r
+binary files as long as those fields can be easily viewed by the user.\r
+\r
+3) No Modified Version of the Font Software may use the Reserved Font\r
+Name(s) unless explicit written permission is granted by the corresponding\r
+Copyright Holder. This restriction only applies to the primary font name as\r
+presented to the users.\r
+\r
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\r
+Software shall not be used to promote, endorse or advertise any\r
+Modified Version, except to acknowledge the contribution(s) of the\r
+Copyright Holder(s) and the Author(s) or with their explicit written\r
+permission.\r
+\r
+5) The Font Software, modified or unmodified, in part or in whole,\r
+must be distributed entirely under this license, and must not be\r
+distributed under any other license. The requirement for fonts to\r
+remain under this license does not apply to any document created\r
+using the Font Software.\r
+\r
+TERMINATION\r
+This license becomes null and void if any of the above conditions are\r
+not met.\r
+\r
+DISCLAIMER\r
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\r
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\r
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\r
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\r
+OTHER DEALINGS IN THE FONT SOFTWARE.\r
index 8717922cdfc9d4122d2e501f64c6e67f35dbb89c..faf6eac84611944ec9b908cefeb7b47e1e31518d 100755 (executable)
@@ -3,8 +3,7 @@
 import time
 from enviroplus import gas
 
-
-print("""gas.py - Print readings from the MICS6812 Gas sensor.
+print("""gas.py - Print readings from the MICS6814 Gas sensor.
 
 Press Ctrl+C to exit!
 
diff --git a/examples/lcd.py b/examples/lcd.py
new file mode 100755 (executable)
index 0000000..aa0e193
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+import ST7735
+from PIL import Image, ImageDraw, ImageFont
+
+print("""lcd.py - Hello, World! example on the 0.96" LCD.
+
+Press Ctrl+C to exit!
+
+""")
+
+# Create LCD class instance.
+disp = ST7735.ST7735(
+    port=0,
+    cs=1,
+    dc=9,
+    backlight=12,
+    rotation=270,
+    spi_speed_hz=10000000
+)
+
+# Initialize display.
+disp.begin()
+
+# Width and height to calculate text position.
+WIDTH = disp.width
+HEIGHT = disp.height
+
+# New canvas to draw on.
+img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
+draw = ImageDraw.Draw(img)
+
+# Text settings.
+font_size = 25
+font = ImageFont.truetype("fonts/Asap/Asap-Bold.ttf", font_size)
+text_colour = (255, 255, 255)
+back_colour = (0, 170, 170)
+
+message = "Hello, World!"
+size_x, size_y = draw.textsize(message, font)
+
+# Calculate text position
+x = (WIDTH - size_x) / 2
+y = (HEIGHT / 2) - (size_y / 2)
+
+# Draw background rectangle and write text.
+draw.rectangle((0, 0, 160, 80), back_colour)
+draw.text((x, y), message, font=font, fill=text_colour)
+disp.display(img)
+
+# Keep running.
+try:
+    while True:
+        pass
+
+# Turn off backlight on control-c
+except KeyboardInterrupt:
+    disp.set_backlight(0)
index 5700eb1f43fcbc42ccd1eca876375cc6d83fabea..216477a766db19768e9d92683f7984bc664f3fac 100755 (executable)
@@ -3,7 +3,6 @@
 import time
 import ltr559
 
-
 print("""light.py - Print readings from the LTR559 Light & Proximity sensor.
 
 Press Ctrl+C to exit!
diff --git a/examples/luftdaten.py b/examples/luftdaten.py
new file mode 100755 (executable)
index 0000000..2177a56
--- /dev/null
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+
+import time
+import json
+import requests
+import ST7735
+from bme280 import BME280
+from pms5003 import PMS5003
+from subprocess import PIPE, Popen, check_output
+from PIL import Image, ImageDraw, ImageFont
+
+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.
+
+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!
+
+""")
+
+bus = SMBus(1)
+
+# Create BME280 instance
+bme280 = BME280(i2c_dev=bus)
+
+# Create PMS5003 instance
+pms5003 = PMS5003()
+
+# Create LCD instance
+disp = ST7735.ST7735(
+    port=0,
+    cs=1,
+    dc=9,
+    backlight=12,
+    rotation=270,
+    spi_speed_hz=10000000
+)
+
+# Initialize display
+disp.begin()
+
+# 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())
+    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 CPU temperature to use for compensation
+def get_cpu_temperature():
+    process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
+    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():
+    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
+
+# Display Raspberry Pi serial and Wi-Fi status on LCD
+def display_status():
+    wifi_status = "connected" if check_wifi() else "disconnected"
+    text_colour = (255, 255, 255)
+    back_colour = (0, 170, 170) if check_wifi() else (85, 15, 15)
+    id = get_serial_number()
+    message = "{}\nWi-Fi: {}".format(id, wifi_status)
+    img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
+    draw = ImageDraw.Draw(img)
+    size_x, size_y = draw.textsize(message, font)
+    x = (WIDTH - size_x) / 2
+    y = (HEIGHT / 2) - (size_y / 2)
+    draw.rectangle((0, 0, 160, 80), back_colour)
+    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"))
+
+    resp_1 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/",
+        json={
+            "software_version": "enviro-plus 0.0.1",
+            "sensordatavalues": [{"value_type": key, "value": val} for key, val in pm_values.items()]
+        },
+        headers={
+            "X-PIN":    "1",
+            "X-Sensor": id,
+            "Content-Type": "application/json",
+            "cache-control": "no-cache"
+        }
+    )
+
+    resp_2 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/",
+        json={
+            "software_version": "enviro-plus 0.0.1",
+            "sensordatavalues": [{"value_type": key, "value": val} for key, val in temp_values.items()]
+        },
+        headers={
+            "X-PIN":    "11",
+            "X-Sensor": id,
+            "Content-Type": "application/json",
+            "cache-control": "no-cache"
+        }
+    )
+
+    if resp_1.ok and resp_2.ok:
+        return True
+    else:
+        return False
+
+# Compensation factor for temperature
+comp_factor = 1.2
+
+# Raspberry Pi ID to send to Luftdaten
+id = "raspi-" + get_serial_number()
+
+# Width and height to calculate text position
+WIDTH = disp.width
+HEIGHT = disp.height
+
+# Text settings
+font_size = 16
+font = ImageFont.truetype("fonts/Asap/Asap-Bold.ttf", 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"))
+
+# Main loop to read data, display, and send to Luftdaten
+while True:
+    try:
+        values = read_values()
+        print(values)
+        resp = send_to_luftdaten(values, id)
+        print("Response: {}\n".format("ok" if resp else "failed"))
+        display_status()
+    except Exception as e:
+        print(e)
similarity index 77%
rename from examples/particles.py
rename to examples/particulates.py
index 6123ad2934debe7b2b108455dc07716e02e9d111..99803ec05a96ff502fc440e115b5c7aa581888a3 100755 (executable)
@@ -3,8 +3,7 @@
 import time
 from pms5003 import PMS5003
 
-
-print("""particles.py - Print readings from the PM5003 Particle sensor.
+print("""particulates.py - Print readings from the PMS5003 particulate sensor.
 
 Press Ctrl+C to exit!
 
@@ -13,7 +12,6 @@ Press Ctrl+C to exit!
 pms5003 = PMS5003()
 time.sleep(1.0)
 
-
 try:
     while True:
         readings = pms5003.read()
diff --git a/examples/weather.py b/examples/weather.py
new file mode 100755 (executable)
index 0000000..17aba20
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+import time
+from bme280 import BME280
+
+try:
+    from smbus2 import SMBus
+except ImportError:
+    from smbus import SMBus
+
+print("""weather.py - Print readings from the BME280 weather sensor.
+
+Press Ctrl+C to exit!
+
+""")
+
+bus = SMBus(1)
+bme280 = BME280(i2c_dev=bus)
+
+while True:
+    temperature = bme280.get_temperature()
+    pressure = bme280.get_pressure()
+    humidity = bme280.get_humidity()
+    print("""Temperature: {:05.2f} *C
+Pressure: {:05.2f} hPa
+Relative humidity: {:05.2f} %
+""".format(temperature, pressure, humidity))
+    time.sleep(1)
index 4f61940a5eb17feee2196f88f56615e9811cfd0a..ee1ad80b04da340be013215539c20e373fd1c1dd 100644 (file)
@@ -1,17 +1,17 @@
-"""Read the MICS6812 via an ads1015 ADC"""
+"""Read the MICS6814 via an ads1015 ADC"""
 
 import atexit
 import ads1015
 import RPi.GPIO as GPIO
 
-MICS6812_HEATER_PIN = 24
+MICS6814_HEATER_PIN = 24
 
 
 ads1015.I2C_ADDRESS_DEFAULT = ads1015.I2C_ADDRESS_ALTERNATE
 _is_setup = False
 
 
-class Mics6812Reading(object):
+class Mics6814Reading(object):
     __slots__ = 'oxidising', 'reducing', 'nh3'
 
     def __init__(self, ox, red, nh3):
@@ -20,9 +20,9 @@ class Mics6812Reading(object):
         self.nh3 = nh3
 
     def __repr__(self):
-        return """Oxidising: {:05.02f}
-Reducing: {:05.02f}
-NH3: {:05.02f}
+        return """Oxidising: {:05.02f} Ohms
+Reducing: {:05.02f} Ohms
+NH3: {:05.02f} Ohms
 """.format(self.oxidising, self.reducing, self.nh3)
 
     __str__ = __repr__
@@ -41,13 +41,13 @@ def setup():
 
     GPIO.setwarnings(False)
     GPIO.setmode(GPIO.BCM)
-    GPIO.setup(MICS6812_HEATER_PIN, GPIO.OUT)
-    GPIO.output(MICS6812_HEATER_PIN, 1)
+    GPIO.setup(MICS6814_HEATER_PIN, GPIO.OUT)
+    GPIO.output(MICS6814_HEATER_PIN, 1)
     atexit.register(cleanup)
 
 
 def cleanup():
-    GPIO.output(MICS6812_HEATER_PIN, 0)
+    GPIO.output(MICS6814_HEATER_PIN, 0)
 
 
 def read_all():
@@ -61,7 +61,7 @@ def read_all():
     red = (red * 56000) / (3.3 - red)
     nh3 = (nh3 * 56000) / (3.3 - nh3)
 
-    return Mics6812Reading(ox, red, nh3)
+    return Mics6814Reading(ox, red, nh3)
 
 
 def read_oxidising():