Ein kleiner daumengrosser Mikroprozessr, der Raspberry Pi zero W, kann mit einer Kamera ausgerüstet und über die digitalen Pins an einen Feinstaubsensor SDS011 angeschlossen werden. Die Messwerte für PM2.5 und PM10 stehen im Bild !

So sieht der Pi zero im Gehäuse mit Kamera aus. Im Hintergrund ist der Feinstaubsensor zu sehen.

Montiert am Balkongeländer:

Hier von hinten betrachtet, mit den angelöteten Verbindungen und der Stromversorgung aus einer Mini-Powerbank

Der Pi zero stellt gleichzeitig einen Webserver Lighttpd zur Verfügung. Dort kann die Messung betrachtet werden:

 

Trickreich war das dynamische Einfügen der Messergebnisse in eine HTML-Seite.

Hier der Python Code:

 # -*- coding: utf-8 -*-
#  SDS011_Feinstaub_Sensor.py
#  Copyright 2017 by luetzel <webmaster_at_raspberryblog.de>
#  modifiziert Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!
#  macht ein Bild (auf Tastendruck)
#  und misst danach 3 Minuten lang die Feinstaubwerte
#  trägt Ergebnisse ins Log ein


from __future__ import print_function
import picamera, time, pygame
import serial, struct, sys
from time import sleep
from shutil import copyfile

# Feinstaubsensor seriell an UART Pins
ser = serial.Serial()
ser.port = "/dev/serial0"
ser.baudrate = 9600
ser.open()
ser.flushInput()

logfile = '/home/pi/feinstaubmessung.log'
indexfile = '/var/www/html/index.html'
messfile = 'datendatei.txt'

# Funktion zum Anlegen der Logs
def log(msg):
  file = open(logfile,"a")
  # write log message with timestamp to log file
  file.write("%s: %s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"), msg))
  file.close

# Funktion zum Eintragen der Messergebnisse in eine externe HTML-Seite
def web(msg):
  file = open(messfile,"a")
  file.write(msg)
  file.close

# erstellt die HTML-Basis und löscht Datendatei
def header():
  file = open(messfile,"w")
  file.write("<h1>Messwerte erscheinen alle 60 Sekunden</h1>")
  file.close()
  copyfile(messfile,"/var/www/html/datendatei.html")
  file = open(indexfile,"w")
  file.write('<!DOCTYPE html\n\n')
  file.write("<html>\n")
  file.write("<head\n")
  file.write("<title><h1>Feinstaub-Bild vom RPi zero</h1></title>\n")
  file.write('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n')
  file.write('<meta http-equiv="expires" content="0"> \n')
  file.write('<meta http-equiv="pragma" content="no-cache"> \n')
  file.write('<meta http-equiv="refresh" content="5">\n')
  file.write("</head>\n")
  file.write("<body>\n")
  file.write('<img id="view" src="/camera.gif" alt="[webcam]" title="Pi-zero-cam" />\n')
  file.write("\n\n<h1>Tabelle der Messwerte: </h1>")
  file.write("<p> \n")
  file.write('<object data="datendatei.html" type="text/plain" width="800" height="300"> \n')
  file.write("</object> \n")
  file.write("</p> \n")
  file.write('<form method="post" action="ende.php">\n')
  file.write('<input type="submit" name="aus">\n')
  file.write('</form>\n')
  file.write("</body>\n")
  file.write("</html>\n")
  file.close()
  
# Feinstaub Routinen --------------------------------------------
def dump_data(d):
    print(' '.join(x.encode('hex') for x in d))

def process_frame(d):
    #dump_data(d)
    r = struct.unpack('<HHxxBBB', d[2:])
    pm25 = r[0]/10.0
    pm10 = r[1]/10.0
    checksum = sum(ord(v) for v in d[2:8])%256
    print("PM 2.5: {} μg/m^3  PM 10: {} μg/m^3 CRC={}".format(pm25, pm10, "OK" if (checksum==r[2] and r[3]==0xab) else "NOK"))
    result=" PM25: {} / PM10: {} ".format(pm25, pm10)
    print("Feinstaub: " + result)
    return result

def sensor_read():
    print("lese den Sensor aus")
    byte = 0
    while byte != "\xaa":
        byte = ser.read(size=1)
    d = ser.read(size=10)
    if d[0] == "\xc0":
        prozessdaten = process_frame(byte + d)
        return prozessdaten

# 0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB
def sensor_wake():
    print("wecke Sensor auf...")
    bytes = ['\xaa', #head
            '\xb4', #command 1
            '\x06', #data byte 1
            '\x01', #data byte 2 (set mode)
            '\x01', #data byte 3 (sleep)
            '\x00', #data byte 4
            '\x00', #data byte 5
            '\x00', #data byte 6
            '\x00', #data byte 7
            '\x00', #data byte 8
            '\x00', #data byte 9
            '\x00', #data byte 10
            '\x00', #data byte 11
            '\x00', #data byte 12
            '\x00', #data byte 13
            '\xff', #data byte 14 (device id byte 1)
            '\xff', #data byte 15 (device id byte 2)
            '\x05', #checksum
            '\xab'] #tail

    for b in bytes:
        ser.write(b)

# xAA, 0xB4, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB
def sensor_sleep():
    print("lege den Sensor schlafen")
    bytes = ['\xaa', #head
            '\xb4', #command 1
            '\x06', #data byte 1
            '\x01', #data byte 2 (set mode)
            '\x00', #data byte 3 (sleep)
            '\x00', #data byte 4
            '\x00', #data byte 5
            '\x00', #data byte 6
            '\x00', #data byte 7
            '\x00', #data byte 8
            '\x00', #data byte 9
            '\x00', #data byte 10
            '\x00', #data byte 11
            '\x00', #data byte 12
            '\x00', #data byte 13
            '\xff', #data byte 14 (device id byte 1)
            '\xff', #data byte 15 (device id byte 2)
            '\x05', #checksum
            '\xab'] #tail

    for b in bytes:
        ser.write(b)

# Feinstaub Routinen Ende --------------------------------------------

# Kamera initialisieren
print("initialisiere Kamera")
log("Kamera initialisiert")
camera = picamera.PiCamera()
camera.resolution = (800, 600)
camera.vflip = False
camera.hflip = False
camera.brightness = 50

# Bildschirmfenster aufbauen, Hintergrund schwarz, Schrift weiss
pygame.init()
screen = pygame.display.set_mode((800,600))
black = pygame.Color(0, 0, 0)
screen.fill(black)

# später hier Auslöser abfragen
while 1:
    #Basis HTML schreiben
    header()
    datumname = time.strftime("%Y-%m-%d_%H-%M-%S")
    print("mache Bild " + datumname + ".gif")
    log("mache Bild " + datumname + ".gif")
    # Bild machen, speichern, gleiche Größe wie Fenster
    #camera.start_preview()
    camera.annotate_text = datumname
    camera.annotate_text = "Vorschaubild"
    camera.capture('Vorschau.gif', format='gif', resize=(800,600))
    copyfile("Vorschau.gif","/var/www/html/camera.gif")
    #camera.capture('/dev/shm/webcam.gif', format='gif', resize=(800,600))
    #camera.stop_preview()

    # Bild einlesen und anzeigen
    img = pygame.image.load('Vorschau.gif')
    screen.blit(img, (0, 0))
    pygame.display.update()
    i=0
    #Sensor 3 * abfragen
    while i < 3:
        # Messergebnisse holen
        sensor_wake()
        time.sleep(20)
        ser.flushInput()
        messwerte=sensor_read()
        camera.annotate_text = datumname+messwerte
        camera.capture('/home/pi/bilder/'+datumname+'.gif', format='gif', resize=(800,600))
        copyfile('/home/pi/bilder/'+datumname+".gif","/var/www/html/camera.gif")
        log(str(i+1)+".Messwert: "+messwerte)
        web("\n<h1>"+str(i+1)+". von 3 Messwerten: "+messwerte+"</h1>")
        copyfile(messfile,"/var/www/html/datendatei.html")
        time.sleep(5)
        sensor_sleep()
        time.sleep(10)
        i += 1

        
# aus die Maus
camera.close()
pygame.quit()

Für Verbesserungen am Code bin ich dankbar !