Digitales Prototyping: Raspberry Pi: Python, Motion, P5, Teensy / 2022-06-19 / Matthias Edler-Golla, CC BY-SA 4.0



Themen heute

Raspberry Pi

  • Raspi via Hardware-Button ausschalten (✴)
  • Foto mit Hardware-Button machen (✴)
  • Foto mit Bewegungsmelder machen (✴)
  • Foto mit Ultraschall-Abstandsmesser machen (✴)
  • Bei Knopfdruck einen zufälligen Sound abspielen (✴)
  • Motion: Livestream der Kamera
  • Livestream innerhalb einer Website
  • Arduino-Helligkeiswerte in CSV-Datei einlesen
  • CSV-Dateien: Werte abfragen
  • p5.js zur Darstellung von CSV-Werten
  • p5.js zur Darstellung der aktuellen Helligkeitswerte
  • USB-Stick einbinden (✴)
  • Chromium fullscreen starten (✴)
  • Keyboard-Events via Teensy auslösen (✴)
  • Buttons via Teensy als Keyboard-Events verwenden (✴)
  • Joystick via Teensy als Keyboard-Events verwenden (✴)
  • Sensor-Daten via Keyboard-Simulation übertragen (✴)

Raspi via Hardware-Button ausschalten

Wenn ein Raspi nicht mit Monitor und Tastatur verbunden ist, ist es oft praktisch diesen mit einem Hardware-Button auszuschalten.

Oft reicht es auch, 2 Kabel zu verwenden. Berühren sich diese, wird der Raspi ausgeschaltet.

Das Beispiel basiert auf dem Tutorial „Shutdown Button“

Erstellen des Codes

# Python-Sketch an der richtigen Stelle erstellen
nano /home/pi/bin/ausschalten.py

Python

# !/bin/python

# Simple script for shutting down the Raspberry Pi at the press of a button.
# by Inderpreet Singh

# https://www2.quartoknows.com/page/raspberry-pi-shutdown-button

import RPi.GPIO as GPIO
import time
import os

# Use the Broadcom SOC Pin numbers
# Setup the pin with internal pullups enabled and pin in reading mode.

GPIO.setmode(GPIO.BCM)

# oberste 2 Pins, direkt neben den USB-Ports
# GPIO 21 und GROUND
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Our function on what to do when the button is pressed
def Shutdown(channel):

    os.system("sudo shutdown -h now")

# Add our function to execute when the button pressed event happens
GPIO.add_event_detect(21, GPIO.FALLING, callback=Shutdown, bouncetime=2000)

# Now wait!
while 1:
    time.sleep(1)

Automatisches Starten des Codes

geschieht jeweils beim Neustart des Raspsi…

crontab -e öffnen

crontab -e

folgendes dort eintragen

# via 2 Kabel den Raspi ausschalten
@reboot python3 /home/pi/bin/ausschalten.py

Danach den Raspi neustarten, damit das Script ausschalten.py aktiviert wird!

Sobald sich jetzt die beiden Kabelende berühren, wird der Raspi ausgeschaltet…


Foto mit Hardware-Button machen

Sobald der Button gedrückt ist, wird ein Foto gemacht. Es reicht auch, wenn man zwei Kabel zusammenhält…

Damit habt Ihr sehr viele Möglichkeiten, Fotos auszulösen:

  • Ein Brett, auf das man tritt
  • Eine Tür, die sich schliesst

Aufbau

Erstellen des Codes

# Python-Sketch an der richtigen Stelle erstellen
nano /home/pi/bin/camera_button.py

Python

# https://gpiozero.readthedocs.io/en/stable/recipes.html#button
# https://github.com/nikrawlinson/timelapse

# Macht ein Foto, wenn ein Button gedrückt wird
# es geht auch, wenn man die 2 Kabel "kurzschliesst", 
# also die Enden zusammenhaelt!

import time
import os
from datetime import datetime
from gpiozero import Button
from signal import pause
from picamera import PiCamera

def fotoMachen():

    # Zeitstempel erstellen
    uhrzeitDatum = datetime.now()
    zeitstempel = uhrzeitDatum.strftime("%y-%m-%d-%H-%M-%S")

    # Benamung, Typ und Speicherort des Bildes
    name = "-foto-1024x768"
    pfad = "/home/pi/Austausch/"
    dateiendung = ".jpg"
    dateiname = pfad + zeitstempel + name + dateiendung

    camera = PiCamera()

    # Größe des Bildes in Pixel
    camera.resolution = (1024, 768)

    # 1 Sekunde Verzoegerung, bevor das Foto gemacht wird - nicht unbedingt noetig!
    time.sleep(1)
    camera.capture(dateiname)
    camera.close()

    meldung = "Foto gemacht!"
    print(zeitstempel + ": " + meldung)

# GPIO-PIN-2: siehe Abbildung!
# Ein Kabel bei PIN2, das andere bei GROUND
button = Button(2)

button.when_pressed = fotoMachen

# aus der Library "signal"
# Cause the process to sleep until a signal is received;
pause()

Code manuell starten

# Python starten
python3 /home/pi/bin/camera_button.py

Automatisches Starten des Codes

geschieht jeweils beim Neustart des Raspsi…

crontab -e öffnen

crontab -e

folgendes dort eintragen

# via python3 und den 2 Kabeln Fotos aufnehmen
@reboot python3 /home/pi/bin/camera_button.py &

Danach den Raspi neustarten, damit das Script camera_button.py aktiviert wird!

PS: Das Script speichert die Datei bei /home/pi/Austausch/. Dieser Ordner ist als Austausch-Server via Samba festgelegt und sollte direkt an Eurem Mac/PC auftauchen, wenn Ihr Euch im gleichen Wlan befindet. Siehe dazu mein damaliges Script…


Foto mit Bewegungsmelder machen

Der Bewegungsmelder macht ein Foto, sobald dieser eine Bewegung bemerkt…

MagPi Magazin

Ganz viel des Beispiels habe ich aus dem Artikel „Turn Your Photos Into Time-Lapse Videos“ im MagPi Magazin 118! Ihr könnt Euch dieses hier kostenlos herunterladen…

Verkabelung

Achtung

Da können ganz schön viele Bilder zusammenkommen. Da macht es wahrscheinlich Sinn, einen USB-Stick einzubinden und die Bilder dort zu speichern…

Python

# https://github.com/nikrawlinson/timelapse
# https://projects.raspberrypi.org/en/projects/getting-started-with-picamera

import time
import os
from datetime import datetime
from gpiozero import MotionSensor
from picamera import PiCamera

# Sensor an GPIO-Pin 4
pir = MotionSensor(4)

def thegrab():
    thetime = datetime.now()
    detectiontime = thetime.strftime("%y-%m-%d-%H-%M-%S")

    # Meldung nur sichtbar, wenn man das Script direkt via Command-Zeile startet
    # python3 /home/pi/bin/python/timelapse.py
    meldung = ": Motion detected!"
    print(detectiontime + meldung)

    # Benamung, Typ und Speicherort des Bildes
    name = "_bewegung_1200x1024"
    # Ablageort der Bilder
    pfad = "/home/pi/Austausch/"
    extension = ".jpg"
    # zusammenbau der Variablen
    filename = pfad + detectiontime + name + extension

    camera = PiCamera()

    # Größe des Bildes in Pixel
    camera.resolution = (1200, 1024)

    time.sleep(2)
    camera.capture(filename)
    camera.close()

while True:
    # Bewegungsmelder wartet, dass er Bewebung entdeckt
    pir.wait_for_motion()
    # Funktion "thegrap()" wird ausgeführt
    thegrab()
    time.sleep(30)

Speichern des Python-Codes

Speichert den Python-Code „timelapse.py“ an folgender Stelle:

nano /home/pi/bin/timelapse.py

Manuelles Starten des Codes

python3 /home/pi/bin/timelapse.py

Automatisches Starten des Codes

geschieht jeweils beim Neustart des Raspsi…

crontab -e öffnen

crontab -e

folgendes dort eintragen

# via python3 und dem PIR-Sensor Fotos aufnehmen
@reboot python3 /home/pi/bin/python/timelapse.py &

Danach den Raspi neustarten, damit das Script timelapse.py aktiviert wird!

PS: Das Script speichert die Datei bei /home/pi/Austausch/. Dieser Ordner ist als Austausch-Server via Samba festgelegt und sollte direkt an Eurem Mac/PC auftauchen, wenn Ihr Euch im gleichen Wlan befindet. Siehe dazu mein damaliges Script…


Foto mit Ultraschall-Abstandsmesser machen

Der Ultraschall-Abstandsmesser löst ein Foto aus, wenn jemand näher als einen festgelegten Abstand kommt…

Aufbau

Anordnung der GPIO-Pins

Python

# https://gpiozero.readthedocs.io/en/stable/recipes.html#distance-sensor
# https://tutorials-raspberrypi.de/entfernung-messen-mit-ultraschallsensor-hc-sr04/

import RPi.GPIO as GPIO
import time
import os
from datetime import datetime
from gpiozero import MotionSensor
from picamera import PiCamera

#GPIO Modus (BOARD / BCM)
GPIO.setmode(GPIO.BCM)

#GPIO Pins zuweisen
GPIO_TRIGGER = 18
GPIO_ECHO = 24

#Richtung der GPIO-Pins festlegen (IN / OUT)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)

# weitgehend identisch mit dem Code von "camera_button.py"
def fotoMachen():

    # Zeitstempel erstellen
    uhrzeitDatum = datetime.now()
    zeitstempel = uhrzeitDatum.strftime("%y-%m-%d-%H-%M-%S")

    # Benamung, Typ und Speicherort des Bildes
    name = "-foto-1024x768"
    pfad = "/home/pi/Austausch/"
    dateiendung = ".jpg"
    dateiname = pfad + zeitstempel + name + dateiendung

    camera = PiCamera()

    # Groesse des Bildes in Pixel
    camera.resolution = (1024, 768)

    # kurze Verzoegerung, bevor das Foto gemacht wird - nicht unbedingt noetig!
    time.sleep(0.3)
    camera.capture(dateiname)
    camera.close()

    meldung = "Foto gemacht!"
    print(zeitstempel + ": " + meldung)

def distanz():
    # setze Trigger auf HIGH
    GPIO.output(GPIO_TRIGGER, True)

    # setze Trigger nach 0.01ms aus LOW
    time.sleep(0.00001)
    GPIO.output(GPIO_TRIGGER, False)

    StartZeit = time.time()
    StopZeit = time.time()

    # speichere Startzeit
    while GPIO.input(GPIO_ECHO) == 0:
        StartZeit = time.time()

    # speichere Ankunftszeit
    while GPIO.input(GPIO_ECHO) == 1:
        StopZeit = time.time()

    # Zeit Differenz zwischen Start und Ankunft
    TimeElapsed = StopZeit - StartZeit
    # mit der Schallgeschwindigkeit (34300 cm/s) multiplizieren
    # und durch 2 teilen, da hin und zurueck
    distanz = (TimeElapsed * 34300) / 2

    # hier den gewuenschten Abstand festlegen,
    # ab wann ein Foto gemacht werden soll
    # aktuell sind es 30cm
    if distanz < 30:
        print("kleiner Abstand")
        # Aufruf der Funktion "fotoMachen()"
        fotoMachen()

    # gibt den akt. Abstand im Terminal aus
    # wenn nicht gewünscht, auskommentieren
    return distanz

try:
    while True:
        abstand = distanz()
        print ("Gemessene Entfernung = %.1f cm" % abstand)
        time.sleep(0.3)

    # Beim Abbruch durch STRG+C resetten
except KeyboardInterrupt:
    print("Messung vom User gestoppt")
    GPIO.cleanup()

Speichern des Python-Codes

Speichert den Python-Code „ultraschall_abstand_foto.py“ an folgender Stelle:

nano /home/pi/bin/ultraschall_abstand_foto.py

Manuelles Starten des Codes

python3 /home/pi/bin/ultraschall_abstand_foto.py

Automatisches Starten des Codes

geschieht jeweils beim Neustart des Raspsi…

crontab -e öffnen

crontab -e

folgendes dort eintragen

# via python3 und dem Ultraschall-Abstandsmesser Fotos aufnehmen
@reboot python3 /home/pi/bin/python/ultraschall_abstand_foto.py &

Danach den Raspi neustarten, damit das Script timelapse.py aktiviert wird!

PS: Das Script speichert die Datei bei /home/pi/Austausch/. Dieser Ordner ist als Austausch-Server via Samba festgelegt und sollte direkt an Eurem Mac/PC auftauchen, wenn Ihr Euch im gleichen Wlan befindet. Siehe dazu mein damaliges Script…

Für weitere Details siehe:


Bei Knopfdruck einen zufälligen Sound abspielen

Jedesmal wenn man den Knopf drückt (oder die Kabel kurzschließt), wird ein zufällig ausgewählter Sound abgespielt

Vorbereitung

Schliesst am Raspi einen Aktiv-Lautsprecher oder einen Kopfhörer an.

Damit Ihr Sounds hören könnt, müsst Ihr am Raspi die Lautstärke einstellen – dazu den Alsamixer starten und mit den senkrechten Pfeiltasten auf Eurer Tastatur die Lautstärke wie gewünscht einstellen.

alsamixer

Sound-Dateien

Es funktionieren hierbei nur Sounds, die als .wav-Dateien abgespeichert sind!

  • birds.wav
  • dogs.wav
  • horses.wav

Tip: Bei freesound.org könnt Ihr kostenlos viele Sounds herunterladen…

Sound probeweise abspielen

Gebt folgendes ein, um probeweise einen Sound abzuspielen und den Lautsprecher zu testen:

aplay /home/pi/Austausch/sounds/bird.wav*

* Hier richtigen Namen und Pfad eingeben!

Mit aplay könnt Ihr einiges mehr machen, hier findet Ihr die Optionen

# hat viele Optionen:
aplay -h

Speichert die Sounds bei Austausch – dann könnt Ihr diese direkt zwischen Raspi und MAC/PC austauschen, wenn Ihr Euch im gleichen Wlan-Netz befindet…

# Speicherort für die Sound-Dateien
/home/pi/Austausch/sounds/

Aufbau

wie bei Foto mit Hardware-Button machen

  • GPIO-PIN-2: siehe Abbildung!
  • Ein Kabel bei PIN2, das andere bei GROUND

Python

# https://pynative.com/python-random-choice/
# https://learn.sparkfun.com/tutorials/python-programming-tutorial-getting-started-with-the-raspberry-pi/experiment-2-play-sounds
# spielt zufällig einen Sound aus einer Liste ab

import time
import RPi.GPIO as GPIO
from pygame import mixer

import random

def zufallsSound():
    # liste der sounds ohne Dateiendung!
    # keine Sonderzeichen, keine Leerraeume!
    sounds_list = ['applause-1','barkingDogs','bird','carEngine','modemDial']

    # hiermit wird zufaellig eine Sounddatei aus der Liste ausgewaehlt
    randomSound = random.choice(sounds_list)

    # Speicherort der Dateien
    pfad = '/home/pi/Auswahl/sounds/'
    endung = '.wav'

    # Zusammenbau 
    mySound = pfad + randomSound + endung

    # globale Variable, sonst geht es nicht!
    global ton 
    ton = mixer.Sound(mySound)

    # Ausgabe im Terminal
    print(mySound)

    # Der Sound "ton" wird abgespielt
    ton.play()

# An welchem GPIO-Pin haengt der Button?
btn_pin = 2

# Set up pins
GPIO.setmode(GPIO.BCM)
GPIO.setup(btn_pin, GPIO.IN)

# Initialize pygame mixer
mixer.init()

# Remember the current and previous button states
current_state = True
prev_state = True

# If button is pushed, play sound
try:
    while True:
        current_state = GPIO.input(btn_pin)
        if (current_state == False) and (prev_state == True):
            # https://stackoverflow.com/questions/54444765/check-if-a-pygame-mixer-channel-is-playing-a-sound
            # solange der Sound laeuft, kann kein neuer Sound ausgewaehlt werden!
            if mixer.Channel(0).get_busy() == True:
                print("Sound spielt gerade")
            else:
                zufallsSound()
        prev_state = current_state

# When you press ctrl+c, this will be called
finally:
    GPIO.cleanup()

Speichern des Python-Codes

Speichert den Python-Code „random_sound.py“ an folgender Stelle:

nano /home/pi/bin/random_sound.py

Manuelles Starten des Codes

python3 /home/pi/bin/random_sound.py

Automatisches Starten des Codes

geschieht jeweils beim Neustart des Raspsi…

crontab -e öffnen

crontab -e

folgendes dort eintragen

# via python3 und einem Button zufaellig Sounds abspielen
@reboot python3 /home/pi/bin/python/random_sound.py &

Danach den Raspi neustarten, damit das Script random_sound.py aktiviert wird! Sobald Ihr jetzt die 2 Kabel kurzschliesst und Eurer Lautsprächer an ist, sollte ein zufälliger Sound ertönen…


Motion

Meine besten „Wildtieraufnahmen“ mit der Raspi-Kamera…

Motion is a highly configurable program that monitors video signals from many types of cameras. Set it up to monitor your security cameras, watch birds, check in on your pet, create timelapse videos and more.


Motion: Livestream der Kamera

Anschließen der Kamera

Bitte beachtet beim Anschliessen der Kamera, dass der Raspi ausgeschaltet ist! Ein detaillierte Anweisung zum Anschließen findet Ihr im letzten Script.

Motion

Mit Motion könnt Ihr die Kamera des Raspis nutzen, um live Videos zu streamen!

Starten von Motion

sudo motion

Anhalten von Motion

sudo killall motion

Ansehen des Livestreams

Ihr könnt Euch den Livestream direkt im Browser ansehen:

http://fk12pi-a.local:8081
http://fk12pi-b.local:8081
http://fk12pi-c.local:8081
http://fk12pi-d.local:8081

und local auf dem Raspi mit Desktop/Chromium

localhost:8081

Die eigenartige Endung :8081 ist der „Port“, auf dem die Kamera ihr Bild live streamt – auch dies kann man individuell einstellen…

andere Livestreams

Wenn die anderen auch Motion gestartet haben, könnt Ihr Euch deren Livestream ebenfalls im Browser ansehen, dazu einfach die URL ändern…


Livestream innerhalb einer Website

Der Live-Stream von Motion kann wie ein „normales“ Bild in einer HTML-Datei eingebunden werden:

Achtung: Motion muss dafür natürlich gestartet werden!

sudo motion

Einbinden des Livestreams innerhalb eines HTML-Dokumentes

Den Motion-Livestream könnte Ihr – wie ein „normales“ Bild – direkt im HTML-Code einbinden:

<!-- hier die richtige Adresse angeben! -->
<img src="http://fk12pi-a.local:8081" alt="Livestream Kamera">

Das komplette HTML-Dokument könnte dann so aussehen:

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Livestream Kamera</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="utf-8" />
    <style>
        body {font-family:Verdana,sans-serif;}
        img {width:100%;height:auto;}
        main {width:90%;max-width:1024px;margin:3% auto;}
        h1 {font-weight:normal;font-size:100%;}
    </style>
  </head>
  <body>
    <main>
        <!-- Anpassen -->
        <h1>Livestream Kamera fk12pi-a</h1>

        <!-- hier die richtige Adresse angeben! -->
        <img src="http://fk12pi-a.local:8081" alt="Livestream Kamera">
    </main>
  </body>
</html>

Übung

Probiert es doch selber mal aus! Erzeugt direkt auf dem Raspi im Ordner /var/www/html/ eine Datei demo-motion.html und fügt das oben gezeigte HTML dort ein – richtige URL Eures Raspis nicht vergessen…

cd /var/www/html/ && nano demo-motion.html

Browser-Ansicht

Anschliessend könnt Ihr im Browser schauen, ob es geklappt hat – auch hier die richtige Adresse angeben:

http://fk12pi-a.local/demo-motion.html
http://fk12pi-b.local/demo-motion.html
http://fk12pi-c.local/demo-motion.html

und wie immer local:

localhost/demo-motion.html

Motion konfigurieren

Motion hat eine sehr umfangreiche Konfigurationsdatei, in der Ihr sehr viele Aspekte von Motion einstellen könnt!

Falls Ihr etwas neu konfigurieren möchet: Bitte erstellt zuerst eine Sicherungskopie der exist. Datei, bevor Ihr daran etwas ändert!

Original-Datei als Kopie speichern

sudo cp /etc/motion/motion.conf /etc/motion/motion_ORIGINAL.conf

Config-Datei mit Nano editieren

sudo nano /etc/motion/motion.conf

Lest Euch in den Kommentaren inmnerhalb der Datei motion.conf durch, was alles möglich ist – hier ein paar wichtige Einstellungen zusammengefasst:

# folgende Einstellung solltet Ihr noch machen, damit das Terminal für andere Sachen "frei" wird
# habe ich vergessen, einzuschalten…
# Start in daemon (background) mode and release terminal (default: off)
daemon on

# Rotate image this number of degrees. The rotation affects all saved images as
# well as movies. Valid values: 0 (default = no rotation), 90, 180 and 270.
rotate 0

# Image width (pixels). Valid range: Camera dependent, default: 320
# Breite des Bildes, max. gehen 3280px, aber das wird SEHR langsam
width 1024

# Image height (pixels). Valid range: Camera dependent, default: 240
# Höhe des Bildes, max. gehen 2464px, aber das wird SEHR langsam
height 768

# Maximum number of frames to be captured per second.
# Valid range: 2-100. Default: 100 (almost no limit).
# mehr schafft der Raspi meist nicht
framerate 15

# Threshold for number of changed pixels in an image that
# triggers motion detection (default: 1500)
# wenn sich 1500px von einem Bild auf das nächste ändern,
# wird z.B. ein Standfoto aufgenommen
# kleiner Wert = Motion wird sensibler für Veränderungen
threshold 1500

# Event Gap is the seconds of no motion detection that triggers the end of an event.
# An event is defined as a series of motion images taken within a short timeframe.
# Recommended value is 60 seconds (Default). The value -1 is allowed and disables
# events causing all Motion to be written to one single movie file and no pre_capture.
# If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An
# event ends right after no more motion is detected and post_capture is over.
# verkürzt die Zeit, wenn Ihr häufiger Bilder von Events machen möchtet
# so "pausiert" Motion erst mal 60sek, nachdem sich ein Event ereignet hat…
event_gap 60

# Output 'normal' pictures when motion is detected (default: off)
# Valid values: on, off, first, best, center
# When set to 'first', only the first picture of an event is saved.
# Picture with most motion of an event is saved when set to 'best'.
# Picture with motion nearest center of picture is saved when set to 'center'.
# Can be used as preview shot for the corresponding movie.
# meine Einstellung ist "best", erzeugt eine vernünftige Anzahl von Standfotos von Events
# probiert die Einstellungen aus, teilweise entstehen sehr viele Fotos ohne Mehrwert…
output_pictures best

# The quality (in percent) to be used by the jpeg and webp compression (default: 75)
# Qualität des aufgenommenen Shots
quality 75

# Use ffmpeg to encode videos of motion (default: off)
# keine Filme speichern, sonst würden kurze Filmausschnitte des Events gespeichert
ffmpeg_output_movies off

# Make automated snapshot every N seconds (default: 0 = disabled)
# Motion kann regelmäßig ein Foto machen, auch wenn keine Bewegung erkannt wird
snapshot_interval 0

# Locate and draw a box around the moving object.
# Valid values: on, off, preview (default: off)
# Set to 'preview' will only draw a box in preview_shot pictures.
# zeichnet z.B. einen roten Rahmen um den Bereich, wo Bewegung erkannt wird
locate_motion_mode off

# Target base directory for pictures and films
# Recommended to use absolute path. (Default: current working directory)
# wichtige Einstellung, die festlegt, wohin die Shots gespeichert werden
# hier liegt der Ordner so, dass man auf die Bilder via Webserver online zugreifen kann
target_dir /var/www/html/motion/

# File path for motion triggered images (jpeg, ppm or .webp) relative to target_dir
# Default: %v-%Y%m%d%H%M%S-%q
# Default value is equivalent to legacy oldlayout option
# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q
# File extension .jpg, .ppm or .webp is automatically added so do not include this
# Set to 'preview' together with best-preview feature enables special naming
# convention for preview shots. See motion guide for details
# Einstellung, wie Bilder gespeicher werden: 
# Diese erhalten Datum und Uhrzeit in den Dateinamen
picture_filename %v-%Y%m%d%H%M%S-%q

# The mini-http server listens to this port for requests (default: 0 = disabled)
# wichtige Einstellung, legt fest, auf welchem "Port" Motion den Live-Stream "verschickt"
# hier Port 8081 – siehe URL im Browser…
stream_port 8081

# Maximum framerate for stream streams (default: 1)
# sollte gleichen Wert haben wie normale framerate (weiter oben)
stream_maxrate 15

# Restrict stream connections to localhost only (default: on)
# muss "off" sein, sonst kann man den Stream nicht von einem anderen Rechner aus sehen!
stream_localhost off

Um diese Einstellungen in Nano zu finden, könnt ich mit ctrl + w nach einem Begriff suchen…

Motion neustarten

Damit Eure Änderungen wirksam werden, müsst Ihr Motion neustarten

sudo killall motion && sudo motion

p5.js

Aufzeichnung Temperatur-Werte

p5.js kennt Ihr ja bestimmt noch aus dem Kurs Interaction Design Grundlagen

Mit p5.js ist es relativ einfach, Daten direkt im Browser zu visualisieren…


p5.js Webeditor

Bitte schaut Euch zum Auffrischen Eurer p5.js Kenntnisse noch mal meine Scripte aus dem Wintersemester'20 an!

Standardansicht

Das kennt Ihr vermutlich vom letzten Jahr noch…

File-Ansicht ausgeklappt

Tatsächlich braucht ein p5.js-Sketch mindestens die oben gezeigten Komponenten und zusätzlich:

p5.js Libraries

Die hier mit roten Pfeilen markierten Libraries, die p5.js erst ermöglichen, müssen im HTML-Dokument (hier "index.html") eingebunden werden.

p5.js komplett local auf dem eigenen Rechner

Hier sind alle Dateien local vorhanden – Ihr braucht also keine Internet-Verbindung, wenn Ihr damit auf Eurem Rechner arbeiten möchtet!

Demo

Schaut Euch die Demo an – oder ladet Euch alle benötigten Dateien als Paket herunter.

Umfang des Paketes

.
├── assets
│   ├── c
│   │   └── style.css                        <-- Stylesheet
│   ├── f
│   │   ├── roboto-bold-webfont.woff2        <-- Webfont
│   │   └── roboto-regular-webfont.woff2     <-- Webfont
│   └── js
│       ├── p5.min.js                        <-- p5 Library
│       ├── p5.sound.min.js                  <-- p5 Sound-Library
│       └── sketch.js                        <-- Euer p5-Sketch
├── index.html                               <-- die HTML-Datei
└── p
    └── dingbat.svg                          <-- Blumen-Grafik

Ihr könnt dieses Paket auch gerne als Template verwenden, wenn Ihr eigene p5-Sachen erstellen möchtet – die Sachen laufen auch auf Eurem Webserver…

Achtung

Die p5-Anweisung bei "setup()" ist etwas anders als beim p5-Webeditor, weil hier gezielt festlegt wird, wo der p5-Canvas "hingezeichnet" werden soll – nämlich innerhalb des "canvasContainer"…

Ihr müsst also Eure p5-Sketches, die Ihr im Webeditor erstellt habt, etwas anpassen:

p5.js

function setup() {
    // Canvas-Element mit dem Namen "meinCanvas" erzeugen
    // Breite (hier 900px) und Höhe (400px) festlegen
    const meinCanvas = createCanvas(900, 400);

    // das Figure-Element mit der ID "canvasContainer" ist das Elternelement
    // schaut Euch die dazugehörige "index.html"-Datei an!
    meinCanvas.parent('canvasContainer');

    // ………
}

HTML

<body>
    <!-- … -->
    <main>
        <!-- hier wird der Canvas via sketch.js eingefügt! -->
        <figure id="canvasContainer"></figure>
    </main>
    <!--  -->
</body>

p5.js auf dem Raspi installiert

Die gerade gezeigte p5.js-Animation ist auch auf Euren Raspis installiert – Ihr könnt sie somit für Eure Bedürfnisse anpassen!

Ihr finde diese an folgender Stelle:

cd /var/www/html/p5-js-local && tree

Browseransicht

http://fk12pi-a.local/p5-js-local/
http://fk12pi-b.local/p5-js-local/
http://fk12pi-c.local/p5-js-local/
http://fk12pi-d.local/p5-js-local/

und local, direkt auf dem Raspi:

localhost/p5-js-local/

CSV-Dateien

CSV-Datei in Numbers/Excel geöffnet

Das Dateiformat CSV steht für englisch Comma-Separated Values und beschreibt den Aufbau einer Textdatei zur Speicherung oder zum Austausch einfach strukturierter Daten. Die Dateinamenserweiterung lautet .csv.

Erzeugen von CSV-Dateien

CSV-Dateien sind auch vom Raspi sehr einfach zu erzeugen und dienen uns in den weiteren Schritten dazu, z.B. Sensordaten zu speichern. p5.js wiederum kann CSV-Dateien gut einlesen und visualisieren.

Beispiel einer einfachen CSV-Datei

2019-05-27,09:30:01,Helligkeit,354
2019-05-27,09:45:01,Helligkeit,371
2019-05-27,10:00:01,Helligkeit,329
2019-05-27,10:15:01,Helligkeit,131
2019-05-27,10:30:01,Helligkeit,144
2019-05-27,10:45:01,Helligkeit,138
2019-05-27,11:00:01,Helligkeit,209

Das sind die gleichen Werte, die oben im Screenshot gezeigt werden!


CSV-Dateien: Einzelwerte abfragen

CSV: Comma Separated Values

Eine CSV-Datei lässt sich gut mit Programmen wie z.B. Excel oder Numbers öffnen und zeigt dort eine tabellarische Darstellung

Um dort einzelne Zellen auszuwählen, muss man nur das Raster verstanden haben:

Code-Beispiele

// 1: Zeile 0, Eintrag 0
Eintrag =  data_log.get(0, 0);  // Ergebnis: 2019-05-27

// 2: Zeile 13, Eintrag 1
Eintrag =  data_log.get(13, 1); // Ergebnis: 11-45-01

// 3: Zeile 13, Eintrag 1
Eintrag =  data_log.get(19, 3); // Ergebnis: 104

CSV-Dateien: Spaltenwerte abfragen

Alle Werte der rot umrandeten Spalte sollen abgefragt werden

Code-Beispiele

// Zeile 0, Eintrag 3
Eintrag =  data_log.get(0, 3);  // Ergebnis: 354

// Zeile 1, Eintrag 3
Eintrag =  data_log.get(1, 3);  // Ergebnis: 371

// wenn man alle Einträge der Spalte angezeigt haben möchte
// geht das mit einer for-Schleife…
var anzahl = data_log.getRowCount(); // wieviele Spalten?
for (var i = 0; i < anzahl; i++) {
    // geht die Spalte 3 von oben bis unten durch
    Eintrag = data_log.get(i, 3);
    console.log("Wert: " + Eintrag); // gibt die Werte in der Konsole des Browsers aus
}

Javascript Arrays

Bitte schaut Euch diesen Film an!

Ein Javascript Array ist eine Datenstruktur, die mehrere Werte unter einem gemeinsamen Namen speichert. Auf diese Werte kann mit einem ganzzahligen Index zugegriffen werden. Die Reihenfolge der hinterlegten Daten wird durch deren Position im Array bestimmt.

Arrays kommen in praktisch allen Programmiersprachen vor und ermöglichen es z.B. viele einzelne Werte in einer einzigen Variable abzuspeichern.

Bei dem Beispiel mit den Helligkeitswerten verwenden wir Arrays, um die in der CSV-Datei abgespeicherten Werte so in p5.js zu „importieren“, dass man damit arbeiten kann.

p5.js

let data_log;
let hPunkte = []; // das Array der Helligkeitspunkte

function preload() {
  data_log = loadTable("data_log.csv");
}

function setup() {

  // liest alle Helligkeitswerte in das Array ein
  // diese befinden sich in jeder Zeile an der 4. Stelle
  // z.B. "2019-05-27,10:30:01,Helligkeit,144"
  // da hier bei Null angefangen wird zu zählen, hier "3" verwenden

  for (let i = 0; i < anzahl; i++) {
    hPunkte[i] = data_log.getNum(i, 3);
  }

  // nächste Zeile gibt das in der Console aus
  // was im Screenshot unten zu sehen ist
  console.log(hPunkte);
}

Ausgabe in der Console


CSV-Werte in p5.js einlesen

Das macht noch nicht viel her, zeigt aber, wie man eine CSV-Datei in p5.js importieren und von dort einzelne Werte auslesen kann…

Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

p5.js

let data_log;

function preload() {
  // einlesen der Werte in die Variable "data_log"
  // Relative Pfade beachten!
  data_log = loadTable("data_log.csv");
}

function setup() {

  // …

  // ======== 1 =========

  // Text-Ausgabe des Wertes der Zelle (0,0)
  text("1) Die Zelle an Stelle (0,0) hat folgenden Inhalt: " + data_log.get(0, 0), 24, 60);

  // ======== 2 =========

  // Text-Ausgabe des Wertes der Zelle (13,1)
  text("2) Die Zelle an Stelle (13,1) hat folgenden Inhalt: " + data_log.get(13, 1), 24, 80);

  // ======== 3 =========

  // Text-Ausgabe des Wertes der Zelle (19,3)
  text("3) Die Zelle an Stelle (19,3) hat folgenden Inhalt: " + data_log.get(19, 3), 24, 100);

  // ======== 4 =========

  text("4) Auflistung der ersten 15 Werte der 4. Spalte:", 550, 30);

  // liest die ersten 15 Helligkeitswerte aus;
  // diese befinden sich in jeder Zeile an der 4. Stelle
  for (let i = 0; i < 15; i++) {
    text(data_log.getNum(i, 3), 550, 60 + 20 * i);
  }
}

CSV-Datei einlesen und einzelne Werte darstellen

Hier werden ausgewählte Helligkeitswerte, die in der Datei data_log.csv enthalten sind, als Balken dargestellt.

Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

p5.js

let data_log;
let hPunkte = []; // das Array der Helligkeitspunkte

function preload() {
    data_log = loadTable('data_log.csv');
}

function setup() {
    // …
    // wieviele Zeilen enthält die Datei "data_log.csv"
    let anzahl = data_log.getRowCount();

    // liest alle Helligkeitswerte in das Array ein
    // diese befinden sich in jeder Zeile an der 4. Stelle
    // z.B. "2019-05-27,10:30:01,Helligkeit,144"
    // da hier bei Null angefangen wird zu zählen, hier "3" verwenden
    for (let i = 0; i < anzahl; i++) {
        hPunkte[i] = data_log.getNum(i, 3);
    }

    let b = 220; // Breite des Rechtecks
    let a = b + 10; // Abstand der Rechtecke

    // ==== Rechtecke, bei dem die Höhe dem Wert des jeweiligen Eintrags bei hPunkte entspricht ===

    // Rechteck mit Wert des Eintrags 0
    fill(255, 0, 0);
    rect(18, 50, b, hPunkte[0]);
    // Text-Ausgabe des Wertes
    fill(255);
    text(`Eintrag 0:\nHelligkeitswert: ${hPunkte[0]}`, 22, hPunkte[0] + 20);

    // Rechteck mit Wert des Eintrags 2
    fill(0, 255, 0);
    rect(18 + a, 50, b, hPunkte[2]);
    // Text-Ausgabe des Wertes
    fill(0);
    text(`Eintrag 2:\nHelligkeitswert: ${hPunkte[2]}`, 22 + a, hPunkte[2] + 20);

    // Rechteck mit Wert des Eintrags 4
    fill(0, 0, 255);
    rect(18 + a * 2, 50, b, hPunkte[4]);
    // Text-Ausgabe des Wertes
    fill(255);
    text(`Eintrag 4:\nHelligkeitswert: ${hPunkte[4]}`, 22 + a * 2, hPunkte[4] + 20);

    // Rechteck mit Wert des LETZTEN Eintrag
    fill('orange');
    rect(18 + a * 3, 50, b, hPunkte[anzahl - 1]);
    // Text-Ausgabe des Wertes
    fill(0);
    text(
        `Letzter Eintrag:\nHelligkeitswert: ${hPunkte[anzahl - 1]}`,
        22 + a * 3,
        hPunkte[anzahl - 1] + 20
    );
}

CSV-Datei einlesen und alle Werte darstellen

Hier werden alle Helligkeitswerte, die in der Datei data_log.csv enthalten sind, als Balken dargestellt.

Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

p5.js

let data_log;
let hPunkte = []; // das Array der Helligkeitspunkte

function preload() {
  data_log = loadTable("data_log.csv");
}

function setup() {
  // ∞

  // wieviele Zeilen enthält die Datei "data_log.csv"
  let anzahl = data_log.getRowCount();

  // liest alle Helligkeitswerte in das Array ein
  for (let i = 0; i < anzahl; i++) {
    hPunkte[i] = data_log.getNum(i, 3);
  }

  let b = 35; // Breite des Rechtecks
  let a = b + 4; // Abstand der Rechtecke
  let anfang = 50; // obere Kante der Rechtecke usw.

  // Rechtecke im Hintergrund hilft der Lesbarkeit
  fill(220);
  for (let i = 0; i < anzahl; i++) {
    rect(18 + i * a, anfang, b, 420);
  }

  for (let i = 0; i < anzahl; i++) {
    // zeichnen der Rechtecke, abhängig vom Helligkeitswert
    fill("orange");
    rect(18 + i * a, anfang, b, hPunkte[i]);

    // Nummerierung der Balken
    fill("black");
    textSize(16);
    text(i, 24 + i * a, anfang + 20);

    // Ausgabe des jeweiligen Wertes am unteren Rand des Graphes
    textSize(12);
    text(hPunkte[i], 24 + i * a, anfang + 412);
  }

  // 4 waagrechte Linien alle 100px ("skala") zur Orientierung
  // als letztes gezeichnet, damit oberhalb der Rechteck-Flächen
  stroke(240);
  let skala = 100;
  for (let i = 0; i < 4; i++) {
    line(18, anfang + skala * i, width, anfang + skala * i);
  }
}

Arduino und Raspi zusammen verwenden

Arduino und Raspi sind eine super Kombination, wenn man das einfache Einlesen von Sensordaten (Arduino) mit den umfangreichen Möglichkeiten des Raspis (Webserver, Datenvisualisierung via p5.js …) verbinden möchte!


Arduino am Raspi anschließen

Die Ardunio-IDE schaut auf dem Raspi fast genauso aus wie bei Mac/Windows

Arduino auf dem Raspi finden

  1. Schliesst dazu den Arduino am Raspi via USB an und startet auf dem Raspi die Arduino IDE – verbindet Euch dazu via VNC mit Eurem Raspi
  2. Kontrolliert – wie auf Mac/PC auch – das das richtige Board („Arduino Uno“) und der richtige Port ausgewählt ist
  3. Merkt Euch die Angaben bei Port: Wir brauchen diese gleich in einem Python-Script
  4. Bei mir heisst der Port /dev/ttyACM0

Bitte beachtet folgende Schritte, wenn der Arduino am Raspi nicht gefunden wird:

Abfragen, ob Arduino erkannt wird:

Da sollte in einer Zeile etwas mit „Arduino“ vorkommen, dann wird er schon mal erkannt!

lsusb

Abfragen, an welchem Port der Arduino angeschlossen ist:

Dazu folgenden Befehl einmal mit angeschlossenem Arduino ausführen und einmal ohne – in der Ausgabe sollte dadurch ein Eintrag wegfallen – dieser ist dann der Port des Arduinos! Hier also der Port /dev/ttyACM0

ls /dev/tty*

Arduino-Helligkeitswerte in CSV-Datei einlesen, Schritt 1

Neu an dem unten aufgeführten Script ist, dass dieses nicht ständig ausgeführt wird, sondern wartet, bis vom Raspi die Aufforderung dazu geschickt wird – dies geschieht durch das Verschicken des Buchstaben A. Wie der Raspi das macht, seht Ihr auf den nächsten Slides!

Details zum Arduino-Code und zum richtigen Breadboard-Aufbau findet Ihr im ersten Script zu Arduino

Bitte ladet und testet dieses Arduino-Script auf Eurem Mac/PC BEVOR Ihr das Arduino-Board in einem nächsten Schritt an den Raspi anschliesst!

Anmerkungen zum Arduino-Sketch

  • der Arduino übergibt die Werte erst an den Raspberry, wenn dieser ihn dazu auffordert!
  • Dies geschieht durch das Schicken des Buchstaben "A" an den Arduino via Python/Raspberry Pi
  • verwendete Python-Scripte (bei /home/pi/bin): serial_arduino_to_pi.py, serial_arduino_auffordern.py
  • "serial_arduino_to_pi.py" wird bei jedem Neustart des Raspis unter "crontab -e" aktiviert
  • "serial_arduino_auffordern.py" wird im gewollten Abstand (z.B. alle 15min) ebenfalls unter "crontab -e" aktiviert

Arduino

// Pin Zuweisung bei A0; Widerstand 10 kOhm
int fotozelle = A0; 

// für Abfrage, ob vom Raspi etwas via Serial geschickt wurde
int incomingByte = 0;

void setup() {
  pinMode(fotozelle, INPUT);
  Serial.begin(9600);
}

void loop() {

  // send data only when you receive data:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    // Erst wenn der Buchstabe "A" geschickt wurde, soll Arduino was an Raspi schicken
    if (incomingByte == 65) {

      int hell = analogRead(fotozelle); // Wert, den Fotozelle ausgibt, wird gelesen

      // das Komma ist sehr wichtig, damit die CSV-Datei korrekt formatiert ist!
      Serial.print("Helligkeit,");

      Serial.println(hell); // Ausgabe des Helligkeitswertes der Foto-Zelle

    }
  }
}

Tip

Ihr könnt testen, ob das Arduino-Script funktioniert, indem Ihr im Serial Monitor den Buchstaben A eingebt und auf Send klickt…

Speichern

Speichert den Arduino-Sketch mit dem Namen helligkeitswert_an_raspi_schicken


Arduino-Helligkeitswerte in CSV-Datei einlesen, Schritt 2

Achtung: Die Python-Scripte verwenden den Port /dev/ttyACM0 – wenn bei Euch ein andere Port von Arduino genutzt wird, müsst Ihr das in beiden Scripten noch anpassen!

Auf den Raspi müssen Ordner angelegt und Python-Skripte gespeichert werden:

Ordner anlegen

legt folgende zwei Ordner im „Webbereich“ das Raspis an

mkdir /var/www/html/fotoresistor && mkdir /var/www/html/fotoresistor/daten

Python-Scripte

Damit der Raspi die Werte des Arduino einlesen kann, werden zwei Python-Scripte benötigt:

Speichern und Aktivieren der Python-Skripte

Speichert die beide Skripte serial_arduino_to_pi.py und serial_arduino_auffordern.py im Ordner home>pi>bin

nano /home/pi/bin/serial_arduino_to_pi.py

nano /home/pi/bin/serial_arduino_auffordern.py

Das Script serial_arduino_to_pi.py liest die Werte, die vom Arduino kommen, alle 60sek ein und speichert diese als CSV im Ordner /var/www/html/fotoresistor/daten

serial_arduino_to_pi.py

#!/user/bin/env python
# -*- coding: UTF-8 -*-

import os
import time
from time import sleep
from datetime import datetime
import serial

# hier richtigen Serial-Port des Arduinos angeben!
# Arduino Uno
port = "/dev/ttyACM0"

# Achtung, hier die richtige Bautrate fuer den Arduino eintragen
ser = serial.Serial(port, 9600)
ser.flushInput()

# Pfad und Dateiname
pfadDatei = "/var/www/html/fotoresistor/daten/data_log.csv"

# Geschwindigkeit muss identisch wie bei Arduino-Script sein!
s1 = serial.Serial(port,9600)
s1.flushInput()

file = open(pfadDatei, "a")

while True:
    if s1.inWaiting()>0:

        # formatierte Datum- und Zeit-Kombination
        now = datetime.now().strftime('%Y-%m-%d,%H:%M:%S')

        # gibt den Helligkeiswert jeweil in einer Zeile aus
        inputValue =s1.readline()

        # "\r" geht, macht aber eigenartiges Sonderzeichen auf naechster Zeile!
        # "\n" macht jeweils eine Leerzeile
        file.write(str(now)+","+inputValue+"\n")
        file.flush()

        # Script wartet 1 Minute (60), bis es sich wiederholt
        time.sleep(60)

file.close()

serial_arduino_auffordern.py

#!/user/bin/env python
# -*- coding: UTF-8 -*-

# https://classes.engineering.wustl.edu/ese205/core/index.php?title=Serial_Communication_between_Raspberry_Pi_%26_Arduino

import serial
import sys

# Achtung, hier den richtigen Port fuer den Arduino eintragen
ser = serial.Serial('/dev/ttyACM0', 9600)
ser.flushInput()

# das Python-Script verschickt via SerialPort nur den Buchstaben "A"
# auf dem Arduino wird dieses ausgelesen und nur dann eine Messung von Temp und Feuchtigkeit
# an den Raspi geschickt

string1 = "A"
string1_encode = string1.encode()
ser.write(string1_encode)

sys.exit()

Wenn das Interval für das Script „serial_arduino_auffordern.py“ länger (z.B. 15min) ist, spielt das keine Rolle.

Online-Tutorial

https://classes.engineering.wustl.edu/ese205/core/index.php?title=Serial_Communication_between_Raspberry_Pi_%26_Arduino


Arduino-Helligkeitswerte in CSV-Datei einlesen, Schritt 3

Achtung: Ihr müsst den Raspi neustarten, nachdem Ihr folgende Sachen bei crontab -e eingetragen habt!

Um die Python-Scripte (vorheriges Slide) automatisch auszuführen, müssen diese im crontab -e angegeben werden

crontab -e

crontab -e

# Script beim Starten des Raspi
# liest die Werte, die vom Arduino kommen, alle 60sek ein und speichert diese als CSV
# im Ordner "/var/www/html/fotoresistor/daten"
# wenn das Interval fuer das Script "serial_arduino_auffordern.py" laenger (z.B. 15min) ist, spielt das keine Rolle
@reboot /usr/bin/python /home/pi/bin/serial_arduino_to_pi.py

# fordert den Arduino jede 15 Minute auf, Werte zu schicken
# einfach hier die gewuenschten Zeitabstaende einstellen,
# es muss NICHTS in den Python- oder Arduino-Scripts geaendert werden
# das kuerzeste Intervall, das man einstellen kann, ist 1 min!
*/15 * * * * /usr/bin/python /home/pi/bin/serial_arduino_auffordern.py

Neustart erforderlich

…sonst würde das Script bei „@reboot“ nicht ausgeführt…

sudo reboot

Damit erhält man eine CSV-Datei, die z.B. alle 15min mit einem weiteren Helligkeitswert befüllt wird:

2022-05-02,09:45:01,Helligkeit,371
2022-05-02,10:00:01,Helligkeit,329
2022-05-02,10:15:01,Helligkeit,131
2022-05-02,10:30:01,Helligkeit,144
2022-05-02,10:45:01,Helligkeit,138
2022-05-02,11:00:01,Helligkeit,209
2022-05-02,11:15:01,Helligkeit,157
2022-05-02,11:30:02,Helligkeit,168
2022-05-02,11:45:01,Helligkeit,131
2022-05-02,12:00:01,Helligkeit,134

Das Python-Script serial_arduino_to_pi.py fügt dabei automatisch Datum und Uhrzeit ein, so dass man gleich einen Timestamp hat, wann die Messung durchgeführt wurde…


Helligkeitswerte kontrollieren

Nachdem Arduino und Raspi eine Zeitlang gelaufen sind, könnt Ihr schon mal kontrollieren, ob Helligkeitswerte aufgezeichnet wurden:

Verbinden Euch dazu via SSH mit Eurem Raspi und lasst Euch dann die Werte direkt im Terminal anzeigen

Terminal

# die letzten 30 Zeilen
tail -n 30  /var/www/html/fotoresistor/daten/data_log.csv

Beachtet, dass bei der Datei jeweils eine Zeile leer ist – zum Glück stört p5.js das nicht beim Einlesen der Werte…


Arduino-Helligkeitswerte in p5.js visualisieren

Nachdem jetzt die Helligkeitswerte als CSV-Datei auf dem Raspi vorliegen, können diese wieder mit p5.js visualisiert und im Browser angezeigt werden.

Vorbereiteter Code

Ladet die vorbereiteten fotoresistor_p5_sachen.zip herunter und fügt diesen auf Euren Raspis an der richtigen Stelle ein. Ihr könnt dies z.B. gut via SFTP machen:

Entpacken der Daten und Löschen der Zip-Datei

cd /var/www/html/fotoresistor && unzip fotoresistor_p5_sachen.zip && rm fotoresistor_p5_sachen.zip

Datenstruktur

Nach erfolgreichem Einfügen sollte Eure Datenstruktur bei /var/www/html/fotoresistor folgendermaßen aussehen:

.
|-- assets
|   |-- c
|   |   `-- style.css
|   |-- f
|   |   |-- roboto-bold-webfont.woff2
|   |   `-- roboto-regular-webfont.woff2
|   `-- js
|       `-- p5.min.js
|-- daten
|   `-- data_log.csv    <-- diese Datei wird ständig aktualisert und enthält die Helligkeitswerte
|-- demo.txt
|-- graph.js
`-- index.html

Probiert es mal aus:

tree /var/www/html/fotoresistor

Im Browser ansehen

http://fk12pi-a.local/fotoresistor/
http://fk12pi-b.local/fotoresistor/
http://fk12pi-c.local/fotoresistor/
http://fk12pi-d.local/fotoresistor/

und lokal auf dem Raspi:

localhost/fotoresistor/

Beachtet, dass sich die Anzahl der Datensätze z.B. beim Reload des Browserfensters aktualisiert!

Automatischer „Refresh“ der HTML-Seite

Im <head>-Bereich der HTML-Datei habe ich folgende Angaben eingefügt, die dafür sorgen, dass die Seite alle 15min aktualisiert wird und das keine Dateien im Cache gespeichert werden:

HTML

<head>
  <!-- … -->

  <!-- alle 15 min aktualisieren -->
  <meta http-equiv="refresh" content="900">

  <!-- verhindert, dass Daten im Cache gespeichert werden -->
  <meta http-equiv=”Pragma” content=”no-cache”>
  <meta http-equiv=”Expires” content=”-1″>
  <meta http-equiv=”CACHE-CONTROL” content=”NO-CACHE”>

  <!-- … -->
</head>

Demo (ohne Live-Daten!)

Schaut Euch die Demo an – oder ladet Euch diese herunter!


USB-Stick einbinden

Der Raspi hat 4 USB-Ports – z.B. für USB-Sticks, um große Datenmengen zu speichern. Linux bietet die Möglichkeit, den USB-Stick direkt einem beliebigen Ordner (beispielsweise home/pi/vieleFotos) zuzuweisen. Alles, was in diesen Ordner gespeichert wird, landet direkt auf dem USB-Stick.

Vorbereitung

den gewünschten Ordner anlegen und gleich dorthin wechseln

mkdir /home/pi/vieleFotos && cd /home/pi/vieleFotos

Pfad zum gewünschten Ordner abfragen

pwd

Format des USB-Sticks

Der Stick darf nicht für Mac formatiert sein! Zuverlässig geht es, wenn Ihr entweder einen neuen USB-Stick verwendet (diese sind passend für Windows formatiert) oder Ihr nutzt z.B. das Festplatten-Dienstprogramm am Mac um den Stick zu formatieren:

UUID des USB-Sticks abfragen

den USB-Stick einstecken und mit folgendem Befehl die UUID des Sticks abfragen – diese ist für jeden Stick einmalig

sudo blkid -o list -w /dev/null

Stick mit dem Ordner /home/pi/vieleFotos verbinden

sudo nano /etc/fstab

dort folgendes eingeben:

# USB-Stick einbinden
UUID=F2D5-160D /home/pi/vieleFotos vfat auto,nofail,noatime,sync,utf8,uid=pi,gid=pi,rw   0 

Erläuterungen dazu

Danach den Raspi neustarten. Jetzt sollte der USB-Stick bei /home/pi/vieleFotos eingebunden sein – Ihr könnt das kontrollieren, indem Ihr noch mal folgendes eingebt:

sudo blkid -o list -w /dev/null

Jetzt werden alle Sachen, die Ihr bei /home/pi/vieleFotos speichert, direkt auf dem USB-Stick gesichert. Probiert es mal aus, schaltet den Raspi aus und steckt diesen an Euren Mac/PC – die abgespeicherten Sachen sind dort ebenfalls sichtbar.

Das ist z.B. auch praktisch, wenn Ihr viele Fotos mit dem Raspi macht. Dann könnt Ihr diese einfach auf Euren Mac/PC übertragen…


Chromium fullscreen starten

Startet man den Browser Chromium fullscreen, wird der Inhalt des Browserfensters bildschirmfüllend angezeigt – alle Buttons, die Adressleiste usw. werden komplett ausgeblendet.

Locale Dateien anzeigen

bitte schaut Euch dazu den Punkt Webserver des Raspis verwenden an!

Chromium via Terminal starten

das geht auch via SSH!

# locale Website
chromium-browser --display=:0 --kiosk http://localhost

# Süddeutsche Zeitung
chromium-browser --display=:0 --kiosk http://sz.de

Zum Beenden des Browser ctrl c eingeben…

Chromium automatisch bei jedem Neustart starten

das muss wieder bei crontab -e gemacht werden

crontab -e

dort folgendes eingeben

@reboot chromium-browser --display=:0 --kiosk http://localhost

# oder z.B.
@reboot chromium-browser --display=:0 --kiosk http://sz.de

dann den Raspi neustarten.

Nach kurzer Zeit wird auf dem Monitor, der direkt am Raspi angeschlossen ist, die gewünschte Website fullscreen angezeigt.

Habt Ihr eine Tastatur direkt am Raspi angeschlossen, könnt Ihr mit alt F4 Chromium beenden.


Keyboard-Events via Teensy auslösen

Teensy ist eine kostengünstige Weiterentwicklung von Arduino. Man kann damit einfach eine Tastatur simulieren und somit einem PC (oder Raspi) Befehle erteilen.

Für die hier gezeigten Beispiele verwende ich den Teensy LC, der kostengünstig zu bekommen ist.

Tastatur-Simulation

Teensys und auch ein paar andere Arduino-Microprozessoren können einem angeschlossenen Computer (Mac, Windows, Linux…) „vorgaukeln“, dass sie eine Tastatur sind. Damit kann man am Teensy z.B. einfach Hardware-Buttons oder – wie hier gezeigt – „Leitfähigkeitssensoren“ anschliessen, die dann Buchstaben (hier j, l, u) an den PC schicken. Am PC ist es dann z.B. via Javascript (oder p5.js) relativ einfach, auf diese Tastatureingaben zu reagieren.

Teensyduino-Einstellungen

Wenn Ihr mit den Teensys arbeitet, ladet Euch bitte deren Entwicklungsumgebung herunter!

Besonders wichtig ist die Angabe bei USB Type – dort müsst Ihr Keyboard auswählen!

Arduino

// http://little-scale.blogspot.com/2017/05/teensy-36-basics-touchread.html
// wenn die Werte hoch sind, soll dem Computer ein Keyboard.print-Event
// geschickt werden, der dann z.B. auf einer Website eine Veränderung via Javascript auslöst

// WICHTIG! You must select Keyboard from the "Tools > USB Type" menu!

const int ledPin = 13;

const int grenzWert = 3000;

// PIN-Layout: https://www.pjrc.com/teensy/card6a_rev4_web.pdf

int touchRead_pin_1 = 0; // Pin 0, Ausgabe von 'j'
int touchRead_pin_2 = 23; // Pin 23, Ausgabe von 'u'
int touchRead_pin_3 = 16; // Pin 16, Ausgabe von 'l' (loeschen)

int data;

void setup() {
  Serial.begin(57600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // Buchstaben mit Single-Quotes angeben!
  // hier könnten beliebig viele pin_auslese_Events festgelegt werden
  // der Teensy LC hat 11 Touch-Pins
  pin_auslesen(touchRead_pin_1, 'j');
  pin_auslesen(touchRead_pin_2, 'u');
  pin_auslesen(touchRead_pin_3, 'l');

  // nicht zu kurze Pause einbauen!
  delay(500);
}

// geht der Kapazitätswert des jeweiligen Pins über den festgelegten Grenzwert
// wird der jeweils gewünschte Buchstabe via "Keyboard" versendet
void pin_auslesen(int welchePin, char buchstabe) {
  data = touchRead(welchePin);
  if (data > grenzWert) {
    // nicht nötig, zeigt aber, wenn Grenzwert überschritten ist
    digitalWrite(ledPin, HIGH);

    // Ausgabe im Serial-Monitor, auch optional
    Serial.println(data);

    Keyboard.println(buchstabe);
  } else {
    digitalWrite(ledPin, LOW);
  }
}

Javascript

// hier die richtigen Buchstaben angeben,
// die Ihr bei "Teensyduino" festgelegt habt
const buchstabeLinks = "j";
const buchstabeRechts = "u";
const buchstabeReset = "l";

// ==================== ab hier müsst Ihr nichts mehr ändern ====================

// wenn Ihr die Benamung der Sektionen beibehaltet…

const linkesKabelErgebnis = document.querySelector("#linkesKabel span");
const rechtesKabelErgebnis = document.querySelector("#rechtesKabel span");

// sobald jemand eine der oben genannten Tasten drückt,
// zählt der Zähler links oder rechts um "1" hoch
// das könnt Ihr auch direkt an Eurem Mac/PC probieren
// auch dort die index-Datei im Browser öffnen und dann
// auf Eurer Tastatur "j" oder "u" eingeben

// wenn Teensy angeschlossen ist und die Website den Focus hat,
// zählen die Elemente hoch, sobald Teensy einen der Buchstaben verschickt

// optional habe ich noch das "l" als Reset-Button eingefügt,
// damit könnten beide Zähler weider auf "0" gesetzt werden
window.addEventListener(
  "keyup",
  function (event) {
    let taste = event.key;
    if (taste === buchstabeLinks) {
      let aktWertLinks = parseInt(linkesKabelErgebnis.textContent);
      linkesKabelErgebnis.textContent = aktWertLinks + 1;
    } else if (taste === buchstabeRechts) {
      let aktWertRechts = parseInt(rechtesKabelErgebnis.textContent);
      rechtesKabelErgebnis.textContent = aktWertRechts + 1;
    } else if (taste === buchstabeReset) {
      linkesKabelErgebnis.textContent = 0;
      rechtesKabelErgebnis.textContent = 0;
    }
  },
  true
);

Online-Demo

Schaut Euch die Demo an oder ladet den Code herunter.

Falls Ihr kein Teensy zur Verfügung habt, könnt Ihr auf Eurer PC-Tastatur die Buchstaben j, l, u eingeben und sehen, was passiert…


Buttons via Teensy als Keyboard-Events verwenden

Hardware-Buttons können mit Teensy ebenfalls als Keyboard-Events an Mac/PC (oder Raspi) übergeben werden – und dann abhängig von der gerade offenen Application verwendet werden, z.B. auch als Shortcuts in Programmen wie Photoshop…

Bitte beachtet für die grundlegenen Einstellungen von Teensy das Beispiel „Keyboard-Events via Teensy auslösen“ (vorherige Seite)!

Besonders wichtig ist wieder die Angabe bei USB Type – dort müsst Ihr Keyboard auswählen!

Arduino

/*
  Buttons to USB Keyboard Example
  You must select Keyboard from the "Tools > USB Type" menu
  This example code is in the public domain.

  das Original-Beispiel mit allen 10 Buttons befindet sich bei:
  File>Examples>Teensy>USB_Keyboard>Buttons

  PIN 0, 1, 2 befinden sich auf der linken Seite des Teensys,
  wenn der USB-Stecker oben liegt
  https://www.pjrc.com/store/teensylc.html
*/

#include <Bounce.h>

/*
  Create Bounce objects for each button.  The Bounce object
  automatically deals with contact chatter or "bounce", and
  it makes detecting changes very simple.
*/
Bounce button0 = Bounce(0, 10);
Bounce button1 = Bounce(1, 10);  // 10 = 10 ms debounce time
Bounce button2 = Bounce(2, 10);  // which is appropriate for

void setup() {
  /*
    Configure the pins for input mode with pullup resistors.
    The pushbuttons connect from each pin to ground.  When
    the button is pressed, the pin reads LOW because the button
    shorts it to ground.  When released, the pin reads HIGH
    because the pullup resistor connects to +5 volts inside
    the chip.  LOW for "on", and HIGH for "off" may seem
    backwards, but using the on-chip pullup resistors is very
    convenient.  The scheme is called "active low", and it's
    very commonly used in electronics... so much that the chip
    has built-in pullup resistors!
  */
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
}

void loop() {
  /*
    Update all the buttons.  There should not be any long
    delays in loop(), so this runs repetitively at a rate
    faster than the buttons could be pressed and released.
  */
  button0.update();
  button1.update();
  button2.update();

  /*
    Check each button for "rising" edge
    Type a message on the Keyboard when each button releases.
    For many types of projects, you only care when the button
    is pressed and the release isn't needed.
    rising = low (pressed - button connects pin to ground)
             to high (not pressed - voltage from pullup resistor)
  */

  /*
    Ihr könnt entscheiden, ob die Keyboard-Botschaft beim Drücken ODER beim Loslassen
    des jeweiligen Buttons übertragen wird
    in der Praxis ist es bestimmt besser, wenn Ihr nur einen einzelen Buchstaben sendet
    also z.B. Keyboard.println("a");
  */
  if (button0.risingEdge()) {
    Keyboard.println("B0 gedrueckt");
  }
  if (button1.risingEdge()) {
    Keyboard.println("B1 gedrueckt");
  }
  if (button2.risingEdge()) {
    Keyboard.println("B2 gedrueckt");
  }

  /*
    Check each button for "falling" edge.
    Type a message on the Keyboard when each button presses
    Update the Joystick buttons only upon changes.
    falling = high (not pressed - voltage from pullup resistor)
              to low (pressed - button connects pin to ground)
  */
  if (button0.fallingEdge()) {
    Keyboard.println("B0 losgelassen");
  }
  if (button1.fallingEdge()) {
    Keyboard.println("B1 losgelassen");
  }
  if (button2.fallingEdge()) {
    Keyboard.println("B2 losgelassen");
  }
}

Joystick via Teensy verwenden

Teensy kann auch einen (Gamer-)Joystick bzw. eine Mouse simulieren. Ihr müsst in Teensyduino bei USB-Type auswählen, was simuliert werden soll.

Der hier gezeigte Arduino-Code gibt über den Serial Monitor lediglich aus, wie die Werte sich ändern, wenn Ihr den Joystick benutzt!

Arduino

int SensorWertX = 0;
int SensorWertY = 0;
int SensorWertT = 0;

int xAchse = A0; // Auslesen des Wertes, der an A0 anliegt. Hier : VRx - X-Achse
int yAchse = A1; // Auslesen des Wertes, der an A1 anliegt. Hier : VRy - Y-Achse
int TasterMitte = A2; // Auslesen des Wertes, der an A2 anliegt. Hier : Taster (zum Herunterdrücken)

void setup() {
  Serial.begin(9600);
}

void loop() {
  SensorWerteAusgabe();

  delay(500);
}

void SensorWerteAusgabe(){
  SensorWertX = analogRead(xAchse);
  Serial.print("xAchse:");
  Serial.println(SensorWertX, DEC);

  SensorWertY = analogRead(yAchse);
  Serial.print("yAchse:");
  Serial.println(SensorWertY, DEC);

  SensorWertT = analogRead(TasterMitte);
  Serial.print("TasterMitte:");
  Serial.println(SensorWertT, DEC);

  Serial.println("-------------------------------------");
}

Online-Tutorials

die eventuell relevant sein könnten…


Sensor-Daten via Keyboard-Simulation übertragen

Die Teensy-Keyboard-Simulation kann man auch verwenden, um Sensordaten in Echtzeit an einen Mac/PC (oder Raspi) zu übertragen.

Hier gezeigt an der Übertragung der Ausgabewerte des Mikrofons „KY-037“.

Bitte beachtet für die grundlegenen Einstellungen von Teensy das Beispiel „Keyboard-Events via Teensy auslösen“ (vorherige Seite)!

Vorsichtsmaßnahme

Damit der Teensy nicht sofort anfängt, Werte an den Mac/PC/Raspi zu übertragen, habe ich einen Hardware-Button eingebaut. Erst wenn man auf diesen drückt, fängt die Übertragung an. So kann z.B. erst das Browserfenster (oder eine leeres Texteditor-Datei) in den Focus schieben, bevor die Daten via Keyboard übertragen werden. Drückt man noch mal auf den Knopf, wird die Übertragung wieder gestoppt.

Bei Fritzing gab es nicht das Mikrofon „KY-037“, das 4 Anschlüsse hat. Für den gezeigten Sketch könnt Ihr den Anschluss „D0" leer lassen.

Arduino

// WICHTIG! You must select Keyboard from the "Tools > USB Type" menu!

#include <Bounce.h>
Bounce button0 = Bounce(0, 10);

int wert = 0;
bool uebertragen = 0; // toggelt bei Tastendruck zwischen 0 und 1, nur bei "1" wird "wert" via Keyboard uebertragen
const int ledPin = 13; // LED soll leuchten, wenn Übertragung stattfindet

void setup() {
  pinMode(0, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  uebertragungAnAus();

  // wenn Uebertragung aktiv ist, wird der Sensorwert des Microfons
  // via Keyboard an den PC/Mac/Raspi übertragen
  if (uebertragen == 1) {
    wert = analogRead(A0);
    // Ausgabe der "Lautstaerke" als Zahlenreihe
    Keyboard.println(wert);
  }

  delay(100);
}

// toggelt, ob die Übertragung stattfinden soll oder nicht
// wird übertragen, leuchtet die LED auf PIN13
void uebertragungAnAus() {
  button0.update();

  if (button0.risingEdge()) {
    if (uebertragen == 0) {
      uebertragen = 1;
      digitalWrite(ledPin, HIGH);
    } else {
      uebertragen = 0;
      digitalWrite(ledPin, LOW);
    }
  }
}

Javascript

Javascript „fängt“ die übergebenen Zahlenwerte ab und verwendet diese, um die Breite des roten Balkens damit anzupassen:

// ===================== Keyboard-Funktionalität =====================

// Teensy schickt Daten (hier "Mikrofon") an PC
// dabei simuliert Teensy das Keyboard

// speichert die Werte, die vom Teensy kommen, in ein Array
let buffer = [];
// der TextString aus dem "buffer"-Werten
let teensyWerte = "";

// Graph, der die Werte anzeigt
const teensyGraph = document.querySelector("#teensyGraph");

// Textausgabe des aktuellen Wertes
const werte = document.querySelector("#werte");

window.addEventListener(
  "keydown",
  function (event) {
    let taste = event.key;
    // die Übergabe vom Teensy endet mit einem "ENTER" (neue Zeile)
    // das wird abgefragt und an dieser Stelle wird
    // der Graph aktualisiert und buffer und teensyWerte wieder zurückgesetzt
    if (event.code == "Enter") {
      for (let i = 0; i < buffer.length; i++) {
        teensyWerte = teensyWerte + buffer[i];
      }
      // console.log(teensyWerte);

      // die CSS-Angabe "width" wird hier via Javascript angepasst
      // '0.9' = Skalierung des Graphen
      teensyGraph.style.width = `${teensyWerte * 0.9}px`;
      werte.textContent = teensyWerte;

      // zurücksetzten der Variablen
      buffer = [];
      teensyWerte = "";
    } else {
      buffer.push(taste);
    }
  },
  true
);

Online-Demo

Schaut Euch die Demo an oder ladet den Code herunter.


Danke

Alle Scripte durchsuchen

Weitere Vorträge: