--- /dev/null
+#!/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)
--- /dev/null
+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
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!
--- /dev/null
+#!/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)
import time
import ltr559
-
print("""light.py - Print readings from the LTR559 Light & Proximity sensor.
Press Ctrl+C to exit!
--- /dev/null
+#!/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)
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!
pms5003 = PMS5003()
time.sleep(1.0)
-
try:
while True:
readings = pms5003.read()
--- /dev/null
+#!/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)
-"""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):
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__
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():
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():