Raspberry Pi

Aus m-wiki
Zur Navigation springen Zur Suche springen

Einführung

Der RasPi ist ein Mini-Computer, der meist unter Linux betrieben wird. Dies im Gegensatz zu einem Mikrokontroller wie etwa dem Arduino. Er kann in diversen Sprachen programmiert werden, wobei vielfach c oder Python verwendet wird. Er besitzt eine Steckleiste ähnlich dem Userport des Commodore 64. Diese Steckleiste wird GPIO genannt und bedeutet General Purpose Input Output. Dies heisst, dass an die Anschlüsse für eigene Bedürfnisse anpassen kann. Einige der Anschlüsse kann man auch verwenden, um spezifische Protokolle wie UART (seriell), I2C oder SPI zu benutzen.

Auch wenn es Bücher gibt, welche eine Einspeisung direkt über die GPIO-Pins empfehlen (etwa für den Batteriebetrieb), wird dringend davon abgeraten, da es dann keinen Spannungsschutz mehr gibt. Stattdessen sollte immer über den vorgesehenen Micro-USB eingespiesen werden, auch wenn man ein Batterie-Pack verwendet.

Betriebsystem

Es gibt spezifische Betriebsysteme, welche explizit auf die Bedürfnisse des RaspPi zugeschnitten sind wie kleiner Speicherverbrauch und Vorkompilierung der verwendeten Kernelmodule. Entsprechend eine Liste der Distributionen, wobei die Liste länger ist: RasPi Betriebsysteme im Kurzportrait

Betriebssystem Eigenschaften Erscheinungsjahr SD-Karte
Raspian
  • Basiert auf Debian
  • Grosse Community und Wikis
2012 4GB (8GB wenn GUI)
Pidora
  • Basiert auf Fedora
  • Unterstützt den Headless-Modus
  • Grosse Community und Wikis
2014 2GB
Windows 10 IoT Core
  • Windows 10 IoT Core stützt sich auf die hauseigene „Universal Windows Platform“-API (UWP) und wird über die Community-Edition von Microsoft Visual Studio bedient
  • Bitlocker-Verschlüsselung und "Secure Boot"-Funktionen
  • Braucht ein aktuelles Windows 10 zur Entwicklung
  • Unterstützung von Pulsweitenmodulation (PWM) für Elektromotoren
  • Community-Forum mit eigenem Raspberry-Pi-Bereich
2015 ?
Ubuntu Core
  • minimalistische Abwandlung der Server-Edition
  • jedes Softwarepaket ist eine einzelne Einheit ("Snap") -> soll mehr Sicherheit bringen
  • Hilfe entweder direkt bei Canonical oder in den Community-Foren
  • Braucht einen Ubuntu SSO-Account
2014 ?
Risc OS
  • ausgerichtet auf ARM (seit den 1980er Jahren)
  • setzt stark auf "drag'n'drop", so werden Dateien geöffnet, indem man diese ins Programm zieht
2014? 2GB
Arch Linux ARM
  • ausgerichtet auf Profis, daher eher aufwendig
  • Rolling Release und daher vielfach kurze Testphasen
2015? 2GB
FreeBSD
  • Für den RaspPi angepasste Version von FreeBSD
  • sicher und schnell
  • grosse Community
2015? ?

Daten lokal speichern

Messwerte eines Sensors können für eine Auswertung lokal gespeichert werden.

# https://github.com/simonmonk/raspberrypi_cookbook_ed2/blob/master/temp_log.py
import os, glob, time, datetime

log_period = 10 # seconds 

logging_folder = glob.glob('/media/*')[0]
dt = datetime.datetime.now()
file_name = "temp_log_{:%Y_%m_%d}.csv".format(dt)
logging_file = logging_folder + '/' + file_name

os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

# Hier wird der 1-Wire-Temperatursensor definiert
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'
 
def read_temp_raw():
   f = open(device_file, 'r')
   lines = f.readlines()
   f.close()
   return lines
 
# Daten aus der generierten Datei extrahieren
def read_temp():
   lines = read_temp_raw()
   while lines[0].strip()[-3:] != 'YES':
       time.sleep(0.2)
       lines = read_temp_raw()
   equals_pos = lines[1].find('t=')
   if equals_pos != -1:
       temp_string = lines[1][equals_pos+2:]
       temp_c = float(temp_string) / 1000.0
       temp_f = temp_c * 9.0 / 5.0 + 32.0
       return temp_c, temp_f

# Temperaturwerte abspeichern
def log_temp():
   temp_c, temp_f = read_temp()
   dt = datetime.datetime.now()
   f = open(logging_file, 'a')
   f.write('\n"{:%H:%M:%S}",'.format(dt))
   f.write(str(temp_c))
   f.close()

print("Logging to: " + logging_file)
while True:
   log_temp()
   time.sleep(log_period) # Wert alle x Sekunden auslesen

GPIO verwenden

Je nach Revision und Gneration sind die Pins etwas anders. Daher unbedingt schauen, welche Version eingesetzt wird. Die Belegung ist zwar gleich, doch die Pins haben andere Nummern, so dass man dann evtl. den "falschen" Pin anspricht. Entsprechend hier die Revision B der neueren Boards.

Pinnummer Funktion - Funktion Pinnummer
- 3.3V o o 5V -
2 SDA o o 5V -
3 SCL o o GND -
4 I/O o o TX 14
- GND o o RX 15
17 I/O o o I/O 18
27 I/O o o GND -
22 I/O o o I/O 23
- 3.3V o o I/O 24
10 MOSI o o GND -
9 MISO o o I/O 25
11 SCKL o o I/O 8
- 3.3V o o I/O 7

Bedeutung der Bezeichnungen

  • 3.3V -> stellt 3.3V mit max. 50mA zur Verfügung
  • 5V -> stellt 5V mit max. 250mA zur Verfügung -> Darf nie an einen GPIO-Pin gelangen, da dieser sonst sofort zerstört wird!
  • GND -> Ground-Pins
  • I/O -> Reine Input/Output-Pins, welche nicht für andere Zwecke vorbelegt sind. Im Gegensatz zum Arduino sind keine analogen Ein-/Ausgänge vorhanden. Sollten nicht mehr als 3mA-Strom ziehen. Es geht zwar mehr (max. 16 mA), doch auf Kosten der Lebensdauer des Chips.
  • TX -> Send (transmit) der seriellen Schnittstelle (UART)
  • RX -> Empfang (receive) der seriellen Schnittstelle (UART)
  • SDA -> Daten? der I2C-Schnittstelle
  • SCL -> Clock der I2C-Schnittstelle
  • MOSI -> Master Out der SPI-Schnittstelle (Daten vom Server zu den Slaves) (Die Kabel werden nicht gekreuzt)
  • MISO ->Master In der SPI-Schnittstelle (Daten von den Slaves zum Server) (Die Kabel werden nicht gekreuzt)
  • SCKL -> Clock der SPI-Schnittstelle

Bei [Doktor Monk] gibt es eine Schablone zum Ausdrucken der Pinbelegung, damit man diese direkt auf dem RasPi sieht. Möchte man aber nicht immer Kabel manuell stecken, gibt es eine Adapterplatte mit Flachbandkabel; den Pi-Cobbler, den es in diversen Elektronik-Geschäften gibt. Etwas vergleichen, sonst kauft man ihn überteuert.

Spanungen wandeln (5V -> 3.3V)

Wenn man den RasPi mit anderen Geräten koppelt, kann es sein, dass diese mit 5V arbeiten. In diesem Fall muss die Spannung heruntergesetzt werden, damit der RasPi nicht beschädigt wird. Dies geschieht entweder mit einem Spannungsteiler oder mit einem Pegelwandler-Modul. Verwendet man Widerstände, so sollten diese im Verhältnis 1:2 sein, damit die Spannung den geforderten 3.3V entspricht. Dies erreicht man etwa mit einem 270 und einem 470 Widerstand. Da dies aber verhältnismässig viel Strom braucht, wäre es idealer, höhere Werte wie 2.7k und 4.7k zu verwenden. Falls es von der Signalstabilität her reicht, ginge entsprechend auch ein 27k und ein 47k Widerstand.

Ein Wandlermodul hingegen ist intern schon auf die gewünschten Potentiale aufgebaut und muss nur noch mit den gewünschten Spannungen angeschlossen werden. Die Wandler gibt es etwa mit 4 oder 8 Kanälen. Kostengünstiger ist es natürlich, wenn man nur wenige Potentiale wandeln muss, dies über Widerstände zu lösen, doch bei einer grösseren Menge lohnt sich der Einsatz von BOB-11978, BOB-12009 oder Adafruit 395.

GPIO ohne Root-Rechte nutzen

Für den Zugriff auf die GPIO braucht man normalerweise Root-Rechte. Während manche empfehlen, einfach das entsprechende Skript/Programm unter Sudo auszuführen, kann man mit nachfolgender Rule die I/O auch für normale Benutzer verfügbar machen und so auf Root zu verzichten.

Die Datei wird im Verzeichnis /etc/udev/rules.d abgelegt. Die Zahl im Dateinamen kann angepasst werden. Es soll nur sichergestellt werden, dass es eine eindeutige Zahl ist.

# /etc/udev/rules.d/88-gpio-without-root.rules - GPIO without root on Raspberry Pi  # <1>
# Copyright http://BotBook.com
#
# Gruppe dialout mit Besitzer root setzen.
SUBSYSTEM=="gpio", RUN+="/bin/chown -R root.dialout /sys/class/gpio/"
SUBSYSTEM=="gpio", RUN+="/bin/chown -R root.dialout /sys/devices/virtual/gpio/"
#
# Sticky-Bit, damit neue Dateien auch dieser Gruppe angehören.
SUBSYSTEM=="gpio", RUN+="/bin/chmod g+s /sys/class/gpio/" 
SUBSYSTEM=="gpio", RUN+="/bin/chmod g+s /sys/devices/virtual/gpio/" 
#
# Lese- und Schreibberechtigung setzen
SUBSYSTEM=="gpio", RUN+="/bin/chmod -R ug+rw /sys/class/gpio/" 
SUBSYSTEM=="gpio", RUN+="/bin/chmod -R ug+rw /sys/devices/virtual/gpio/"

Die Datei kann mit sudoedit Dateiname bearbeitet werden. Die Datei wird mit Ctrl-x -> y -> Enter gespeichert und geschlossen. Um die Regeln zu übernehmen wird entweder der Raspi neu gestartet oder man liest die Regeln neu ein:

sudo service udev restart
sudo udevadm trigger -subsystem-match=pgio

GPIO-Zugriff von der Konsole

Um etwa den Tasterzustand von GPIO3 zu lesen, wird folgendermassen vorgegangen:

$ echo 3|tee /sys/class/gpio/export # Datei gpio3 erzeugen
$ echo in|tee /sys/class/gpio/gpio3/direction # Den Pin als Input setzen
$ cat /sys/class/gpio/gpio3/value # Tasterzustand zurücklesen

Da der interne Pull-Up-Widerstand aktiv ist, liefert offen eine 1, während geschlossen eine 0 liefert.

Module freischalten

Damit das entsprechende Modul vom Kernel geladen wird, muss man es folgendermassen einrichten:

  1. Kontrolle, dass es nicht in der /etc/modprobe.d/raspi-blacklist.conf aktiv ist -> einfach mit einem # am Anfang auskommentieren
  2. Das Modul in /etc/modules hinzufügen
  3. Bibliotheken installieren, falls notwendig
  4. udev-Regeln hinzufügen, für die Verwendung ohne Root
  5. Den RaspPi neu starten

I2C

I2C wird für langsame Verbindungen (Standard 100kHz, max 5MHz) verwendet, benötigt aber nur 2 Leitungen. Die Verbindung wird immer vom Master eröffnet und dient daher nur zum Abfragen. Vorteilhaft für Peripheriegeräte, die nicht schnell zu sein brauchen wie Lautstärkeregler, Analog-Digital- oder Digital-Analog-Wandler mit niedriger Abtastrate, Echtzeituhren, kleine, nichtflüchtige Speicher oder bidirektionale Schalter und Multiplexer. Während des Betriebes können Chips zum Bus hinzugefügt oder entfernt werden (Hot-Plugging). Das Protokoll ist aber sehr anfällig auf Übersprechen, Rauschen oder EMV. Entsprechend kann es nur auf kurzen Distanzen eingesetzt werden und wenn man nicht Buchsen und Stecker verwenden muss, die das Signal stören können. Das Protokoll beinhaltet aber ein Kontroll-Bit, mit dem der Slave angeben kann, dass er die Daten verstanden hat.

Das freizuschaltende Modul heisst i2c-bcm2708. Zusätzlich sollte auch i2c-dev geladen werden. Für den Zugriff sollte man auch die i2c-tools installieren:

$ sudo apt-get install i2c-tools

Damit man I2C auch ohne Root-Rechte nutzen kann, sollte man im Verzeichnis /etc/udev/rules.d/ die Datei 99-i2c.rules anlegen, die nur eine Zeile enthält:

SUBSYSTEM=="i2c-dev", MODE="0666"

Beim Verwenden darauf achten, wie viel Strom die Geräte brauchen und diese sonst extern einspeisen. Hat man ein Gerät verbunden, kann man nun seine Adresse herausfinden. Jedes angeschlossene Gerät braucht eine eindeutige Adresse:

 $ sudo i2cdetect -y 1

Bei ganz alten Boards (schwarzer Lautsprecherausgang) muss der Bus mit 0 anstatt 1 angegeben werden. Das Tool listet die Adressen (hex) entsprechend auf. Will man mehrere Geräte anschliessen, sollte man immer eines nach dem anderen anschliessen und die Adresse dann entsprechend eindeutig gestalten.

Achtung: Beim Anschluss von Geräten beachten, ob diese Pull-Up-Widerstände verwenden und mit 5V gespiesen werden. In diesem Fall muss man entweder die Widerstände entfernen oder Pegelwandler einsetzen, da sonst der Raspi zerstört wird.

SPI

SPI (Serial Peripheral Interface) verwendet (mindestens) 3 (meistens 4 oder mehr) Leitungen und verwendet kein Kontroll-Bit. Das Protokoll ist sehr flexibel. So kann je nach Ausführung die Chipselect-Leitung pro Chip oder gemeinsam geführt werden. Unterschiedliche Taktfrequenzen bis in den MHz-Bereich sind zulässig. Da die Spezifikation für die Taktflanke, die Wortlänge oder die Übertragung (MSB oder LSB zuerst) nicht genau festgelegt sind, gibt es diverse Einstellungsmöglichkeiten. Trotzdem kann es sein, dass manche Kombinationen inkompatibel sind. Man sollte sich daher vor dem Kauf die Spezifiationen und Einstellungsmöglichkeiten der Chips anschauen. Wenn man mit nur 4 Leitungen auskommen möchte, wird Daisy-Chain verwendet, wie es im Artikel So funktioniert SPI erklärt wird. Da dabei die Daten von Slave zu Slave weitergereicht werden, sind bei längeren Ketten Bitlänge*Slave-Position Taktzyklen zur Verarbeitung notwendig. Bei 5 Slaves in einem 8-Bit-System wären dies für den letzten Slave 40 Taktzyklen. Dieses Prinzip der Weiterleitung beherrschen aber nicht alles Slaves. Sonst sind durch die hohe Taktfrequenz auch grössere Ketten möglich; auch für Geräte mit hohem Durchsatz/Abtastfrequenz.

Das freizuschaltende Modul heisst spi-bcm2708. Zusätzlich sollte auch spidev geladen werden.

Für den Zugriff mit Python auf SPI muss man je nachdem noch ein Git-Repository einbinden, auch wenn dies wohl meist unterdessen schon hinzugefügt wurde. Falls nicht, geht dies auf nachfolgende Art:

$ sudo apt-get install git
$ sudo apt-get install python-dev
$ git clone git://github.com/doceme/py-spidev
$ cd py-spidev/
$ sudo python setup.py install

Damit man SPI auch ohne Root-Rechte nutzen kann, sollte man im Verzeichnis /etc/udev/rules.d/ die Datei 99-spi.rules anlegen, die nur eine Zeile enthält:

SUBSYSTEM=="spidev", MODE="0666"

Nach einem Neustart kann SPI über Python angesprochen werden.

PiFace Digital Interface Board

Das PiFace erweitert den RasPi um zusätzliche Ein- und Ausgänge (8 Outputs, wovon 2 mit Relais, 8 Inputs, wovon 4 mit Taster). Es arbeitet über SPI, welches entsprechend freigeschaltet werden muss.

$ wget http://pi.cs.man.ac.uk/download/install.txt
$ bash install.txt
$ sudo reboot

Mit dem Treiber wird auch ein Emulator ausgeliefert, mit welchem man nicht nur den Zustand sehen, sondern auch Outputs schalten kann.

Die Programmierung mit Python geschieht etwa folgendermassen zum Ein- und Ausschalten des ersten Outputs:

$ python
>>> import piface.pfio as pfio
>>> pfio.init()
>>> pfio.digital_write(0,1)
>>> pfio.digital_write(0,0)

Möchte man den Taster am ersten Eingang abfragen, wird digital_read verwendet:

$ python
>>> import piface.pfio as pfio
>>> import time
>>> pfio.init()
>>> while True:
...    print(pfio.digital_read(0))
...    time.sleep(1)

Echtzeituhr (DS1307)

RTC-Modul aus Raspi-Kochbuch

Die Uhr ist dann von Vorteil, wenn man den Raspi nicht am Internet hängen hat, da sonst der Zeitserver jeweils die korrekte Zeit (genauer) liefert.

Es gibt verschiedene Chips und man sollte schauen, dass das gewünschte Modul auch für 3.3V ausgelegt ist. Sonst gibt es auch die Möglichkeit, einen Arduino mit Echtzeitmodul (wenn vorhanden) anzuschliessen und von diesem die Zeit zu übernehmen. Das Modul wird meist über die I2C -Schnittstelle angesprochen, welche entsprechend freigeschaltet sein muss.

Wie man sieht, wird hier das Modul an 5V angeschlossen. Damit die Spannung auf den I2C-Pins nicht hochgezogen wird, darf man auf dem Modul keine Pull-Up-Widerstände verwenden.

Nach dem Anschluss und der Freigabe des I2C-Busses wird nun die Adresse des Moduls gesucht und danach der Treiber für den Kernel geladen:

$ sudo i2cdetect -y 1        # Alle i2c-Geräte ausgeben lassen
$ ...68...                   # Ausgabe der Adresse des Moduls
$ sudo modprobe rtc-ds1307   # Kernel-Modul versuchen zu laden
$ sudo bash
$ sudo echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device # Gerät mit der Adresse von oben hinzufügen
$ date                       # Kontrolle der Uhrzeit -> Wird hier die falsche Zeit ausgegeben, muss man die Uhrzeit erst korrekt (übers Internet) setzen
$ sudo hwclock -w            # Schreibt die aktuelle Uhrzeit ins HW-Modul
$ sudo hwclock -r            # Auslesen der gespeicherten Zeit zur Kontrolle

Um die Uhr beim Start einzulesen, müssen folgende Schritte durchgeführt werden:

  • /etc/modules um das neue Modul erweitern -> Auf der untersten Zeile folgendes hinzufügen: rtc-ds1307
  • /etc/rc.local erweitern, um das Modul einzulesen -> Zusatzzeilen vor dem abschliessenden "exit 0":
 echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
 sudo hwclock -s

Gertboard

Das [Gertboard] ist eine Erweiterungsplatine, welche neben gepufferten digitalen I/O unter anderem A/D- (10 Bit) und D/A-Wandler (8, 10 oder 12 Bit) bietet und auch eine Motorsteuerung (Maximum 18V and 2A) enthält. Zusätzlich befindet sich noch eine ATmega328ATmega328 MCU (gleicher Chip wie auf dem Arduino Uno) auf dem Board, mit der man unabhängige Arduino Programme laufen lassen kann. Die gepufferten Eingänge schützen unter anderem die I/O des RasPi. Die Ausgänge können übrigens mit bis zu 50V mit 0.5A betrieben werden.

Für das Gertbourd gibt es Beispielprogramme, die man folgendermassen installiert:

$ wget http://raspi.tv/download/GB_Python.zip
$ unzip GB_Python.zip
$ cd GB_Python

Die Programme teilen beim Start mit, welche Jumper wie gesteckt werden müssen, damit die notwendigen Bereiche auf dem Board korrekt verbunden sind:

$ sudo python buttons-rg.py

RaspiRobot-Board

Das Board liefert Ausgänge mit denen man entweder einen Schrittmotor oder 2 Gleichstrommotoren antreiben kann. Dazu gibt es zwei Low-Power-Ausgänge und zwei digitale Eingänge. Das Board kann über die Standard-Bibliotheken bedient werden:

$ sudo apt-get install python-rpi.gpio
$ sudo apt-get install python-serial
$ wget https://github.com/simonmonk/raspirobotboard/archive/master.zip
$ unzip master.zip
$ cd raspirobotboard-master
$ sudo python setup.pi install

Nun ein Beispiel für einen Zugriff auf das Board:

$ sudo python
>>> from raspirobotboard import *
>>> rr = RaspiRobot()
>>> rr.set_led1(1) # Schaltet Led 1 ein
>>> rr.set_led1(0)
>>> rr.set_oc1(1)
>>> rr.set_oc1(0) # Schaltet Ausgang 1 aus
>>> print(rr.sw1_closed()) # Liest den Status des Drückers 1 ein.

Die Verwendung der Motoren (forward, reverse, left, right, stop) wird unter anderem auf der Projektseite von [RaspiRobot] beschrieben.

Prototyping-Boards (Humble Pi / Pi Plate / Paddle Board)

Diese Boards sind reine Entwicklungsplatten ohne Elektronik und dienen dazu, Schaltungen zu entwickeln. Sie besitzen einen Stecker für die GPIO und Lötpins, damit man eigene Schaltungen entwerfen kann. Fürs Prototyping kann natürlich auch ein Steckboard verwendet werden.

Humble Pi

  • Besitzt Aussparungen für die grossen Stecker des RasPi.
  • besondere Bereiche für Spannungsregler und Transistoren
  • Hersteller: [Ciseco]

Pi Plate

  • keine Aussparungen sondern höhere Sockel
  • Bereich für SDM und Schraubklemme
  • Schraubklemmen, welche direkt mit den GPIOs verbunden sind -> Alternative ist das Paddle-Board zum Klemmen der Drähte.

Paddle Board

  • Wird über ein Flachbandkabel angeschlossen
  • Alle GPIO-Verbindungen können einfach mittels Draht eingeführt werden, so dass keine Female-Adapter notwendig sind.
  • Mehr Platz als direkt auf dem Board
  • Alternative ist das Pi Plate, bei dem auch Litzen verwendet werden können, weil die Anschlüsse geschraubt werden.

Wichtige Bibliotheken

Damit man auf die Hardware zugreifen kann, muss je nach Sprache und Anwendungsfall die entsprechende Bibliothek installiert werden. Die Bibliotheken abstrahieren auch den Zugriff auf die GPIO-Dateien, so dass man nicht mehr die Dateien abfragen muss, sondern direkt die Zustände abfragen kann. Hier ein Beispiel ohne GPIO-Bibliothek:

# led_hello.py - blink external LED to test GPIO pins
# (c) BotBook.com - Karvinen, Karvinen, Valtokari

import time, os

def writeFile(filename, contents):	# Funktion zum Schreiben in eine Datei
  with open(filename, 'w') as f:	# Dateihandle erstellen. Mit "with" wird sichergestellt, dass der Dateihandle am Schluss geschlossen wird.
    f.write(contents)                  # Inhalt schreiben

print("Blinking LED on GPIO 27 once...")

if not os.path.isfile("/sys/class/gpio/gpio27/direction"): # Testen ob es eine Datei für den Pin27 gibt.
  writeFile("/sys/class/gpio/export", "27")                # Datei erstellen

writeFile("/sys/class/gpio/gpio27/direction", "out")       # Pin als Output definieren
writeFile("/sys/class/gpio/gpio27/value", "1")             # Pin einschalten
time.sleep(2)
writeFile("/sys/class/gpio/gpio27/value", "0")

Nun das Gleiche mit der GPIO-Bibliothek:

import time, os, RPi.GPIO as GPIO

print("Blinking LED on GPIO 27 once...")

GPIO.setmode(GPIO.BCM)   # Pin im Broadcom-Modus ansprechen
GPIO.setup(27, GPIO.OUT) # Pin als Output definieren
GPIO.output(27, 1)       # Pin einschalten
time.sleep(2)
GPIO.output(27, 0)

Wie man sieht, ist es nicht nur kürzer, sondern auch aufs Wesentliche konzentriert.

RPi.GPIO

Diese Bibliothek ist bei den meisten Distributionen schon vorinstalliert. Sollte dies nicht der Fall sein, muss diese mit dem Paketmanager installiert werden. Allgemeine Infos zum Umgang mit Linux im Artikel Allgemeine Daten zu Linux, Tipps und Tricks und Systemeinstellungen.

Ist apt im Einsatz, kann man die Bibliothek folgendermassen installieren:

$ sudo apt-get install python-dev
$ sudo apt-get install python-rpi.gpio

WiringPi

WiringPi ist eine Bibliothek, welche unter c / c++ verwendet wird, auf die man mit den entsprechenden Wrappern auch von anderen Sprachen aus zugreifen kann wie etwa über Python. Mit gpio kann direkt von der Shell aus auf die Pins zugegriffen werden und dies ermöglicht auch den Zugriff aus Shell-Skripten heraus.
Wie man der Entwickler-Webseite entnehmen kann, wird die Bibliothek nicht mehr weiter entwickelt und ist auch nicht für Anfänger gedacht, sondern für Leute, die sich auskennen.

$ sudo apt-get install wiringpi

Test der Installation:

$ gpio -v
$ gpio readall

PySerial

Notwendig, um die serielle Schnittstelle mit Python zu verwenden.

$ suto apt-get install python-serial

Nicht vergessen, die serielle Konsole zu deaktivieren.

bottle (Webserver)

Mit Einbindung dieser Bibliothek können GPIOs über einen Webserver abgefragt (dargestellt) und gesetzt werden. Die Installation von bottle wird im Artikel Python -> bottle beschrieben.

from bottle import route, run
import RPi.GPIO as GPIO

hostIP = '192.168.1.199' # Die IP des Raspi entsprechend anpassen
portNr = 80              # Standard-http Port. https verwendet 443. Ohne Portangabe wartet der Webbrowser auf 8080.

GPIO.setmode(GPIO.BCM)   # BCM Nummerierungsschema verwenden
led_pins = [18, 23, 24]  # 3 LEDs werden auf den PINs angesteuert
led_states = [0, 0, 0]   # Status ist ausgeschaltet
switch_pin = 25          # 1 Taster auf diesem Pin einlesen

GPIO.setup(led_pins[0], GPIO.OUT)                          # Pin auf Output setzen
GPIO.setup(led_pins[1], GPIO.OUT)                          # Pin auf Output setzen
GPIO.setup(led_pins[2], GPIO.OUT)                          # Pin auf Output setzen
GPIO.setup(switch_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)  # Pin auf Input setzen und den internen  Pull-Up Widerstand aktivieren

def switch_status():              # Funktion zum Einlesen des Tasters
   state = GPIO.input(switch_pin) # Status des Tasters einlesen
   if state:                      # Wenn aktiv
       return 'Nicht gedrückt'    # Status zurückliefern. Durch den Pull-Up Widerstand muss man die Logik umkehren.
   else:                          # Wenn nicht aktiv
       return 'Gedrückt'          # entsprechend anderen Status liefern.

def html_for_led(led):            # Funktion zum Setzen der LEDs
   led = str(led)                 # Sicherstellen, dass die gewünschte LED als String ausgewertet wird
   result = " <input type='button' onClick='changed(" + led + ")' value='LED " + led + "'/>" # JavaScript-Routine aufrufen, um den Status-Button zum Schalten der LED zu verwenden
   return result                  # Den neuen Status der LED rückmelden.

def update_leds():                         # Funktion, um den Status der LED effektiv zu setzen
   for i, value in enumerate(led_states):  # LED-Feld einzeln auslesen
       GPIO.output(led_pins[i], value)     # Pins entsprechend ihres Werts setzen

@route('/')                                # Der Aufruf geschieht über localhst und man muss nicht eine Subadresse eingeben (http://localhost/subadresse), um die Webseite sehen zu können.
@route('/<led>')                           # Es werden noch Unterseiten für jede LED erstellt
def index(led = "n"):                      # Index-Seite erstellen. Je nachdem, welche Nummer übergeben wurde, wird diese dann im Skript ausgewertet. Ohne Angabe bekommt die Variable den Wert "n"
   print(led)                              # ?
   if led != "n":                          # Wurde in der URL die Nummer der LED mitgegeben (localhost/1), so wird diese ausgewertet -> Es gibt keine Prüfung auf zu grosse oder kleine Zahlen
       led_num = int(led)                  # sicherstellen, dass die Eingabe ein Int war
       led_states[led_num] = not led_states[led_num] # Status der gewählten LED invertieren
       update_leds()                       # Funktion zum Setzen des Status aufrufen
   response = "<script>"                   # JavaScript-Routine dynamisch erstellen
   response += "function changed(led)"
   response += "{"
   response += "  window.location.href='/' + led" # Es wird auf die Unterseite verzweigt, deren Button gedrückt wurde und dann wird der Code oben für diese LED ausgeführt
   response += "}"
   response += "</script>"
   
    response += "<h1>GPIO Control</h1>"     # Überschrift h1 generieren
    response += "<h2>Button=" + switch_status() + "</h2>" # Status des Tasters auslesen und als Überschrift h2 darstellen
    response += "<h2>LEDs</h2>"             # Überschrift h2
    response += html_for_led(0)             # Einbindung der ersten LED
    response += html_for_led(1)             # Einbindung der zweiten LED
    response += html_for_led(2)             # Einbindung der dritten LED
    return response                         # Darstellung der Webseite
  
run(host = hostIP, port = portNr, debug=True, reloader=True) # Starten des Scripts. Der Parameter debug ist optonal und sollte entfernt werden, wenn das Script produktiv ist.
                                                             # Dies gilt auch für reloader, der dafür sorgt, dass Änderungen an der Quelldatei ohne Neustart des Web-Servers übernommen werden

Das Programm stammt von Simon Monk und kann auf github heruntergeladen werden.

Serielle Schnittstelle

Der RasPi ist normalerweise so konfiguriert, dass er die Ein-/Ausgabe der Konsole parallel auch auf die Pins der seriellen Schnittstelle leitet. Dies ist zwar nützlich bei einer Analyse ohne Monitor, doch störend, wenn man mit einem externen Gerät "ungestört" kommunizieren möchte. Entsprechend muss man diese Weiterleitung in /etc/inittab auskommentieren.

$ sudo nano /etc/inittab
...
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

Speichern mit Ctrl-X gefolgt von Y. Danach den RasPi neu starten. Zum Einschalten muss die Zeile entsprechend wieder entkommentiert werden.

Um die serielle Schnittstelle über Python zu erreichen, wird die entsprechende Bibliothek installiert. Anschliessend kann man noch [minicom] installieren, um die Verbindung zu testen.

$ sudo apt-get install minicom
$ minicom -b 9600 -o -D /dev/ttyAMA0

Die Baudrate muss natürlich mit den Angaben der Gegenstelle übereinstimmen. Im minicom kann die Hilfe mit Ctrl-A und danach Z aufgerufen werden. Mit der Taste E wird das lokale Echo eingeschaltet, so dass man die eigenen Eingaben auch sieht.

externe Computerchips (ICs)

Externe Chips können entweder über die obigen Schnittstellen angeschlossen werden oder sie arbeiten unabhängig und brauchen nur direkt angeschlossene Steuerleitungen, um die gewünschten Funktionen auszuführen. Nachfolgend einige ICs, die in Verwendung mit dem RasPi nützlich sind.
Damit man weiss, welche PINs sich wo befinden, haben elle ICs entweder eine Einkerbung auf einer Seite oder einen Punkt über PIN 1. Normalerweise werden die Pins im Gegenuhrzeigersinn nummeriert, doch dies steht im entsprechenden Datenblatt.

Für die Anbindung braucht es meist noch elektronische Bauteile wie Transistoren, Widerstände oder Kondensatoren. Diese Bauteile werden auf der Seite Elektronik beschrieben.

A/D-Wandler MCP 300x oder MCP 320x

Diese Bausteine liefern je nach Serie eine andere Auflösung und entsprechende Eingänge. Die Anbindung geschieht über SPI. Die 300x-Serie liefert eine Auflösung von 10 Bit und die 320x eine von 12 Bit. Die letzte Ziffer bestimmt die Anzahl der möglichen Kanäle von 1 (3x01) bis 8 (3x08). Wie die Anbindung des Chips geschieht und dessen Auswertung läuft, ist im Abschnitt über den Potentiometer sichtbar.

AND-Gates MC14081BCP

Der MC14081BCP enthält vier 2-fach AND-Gates und arbeitet mit 5-15V. Bei 5V werden Werte unter 2.3V als Low und über 2.7 als High ausgewertet.

Pinbelegung MC14081B 2-Fach AND-Gate

GPS-Modul

GPS-Module können fertig gekauft und ausgewertet werden. Der Anschluss an den Raspi geschieht entweder seriell oder über eine andere Schnittstelle. Um die Daten auszuwerten gibt es Dienste, welche auf dem Raspi installiert werden können. Ein Beispiel liefert das Rezept 11.10 im Raspi-Kochbuch.

Darlington-Treiber ULN280x

Pinbelegung ULN280x

Diese Motortreiberbaustein-Reihe unterscheidet sich in der letzten Ziffer, welche die Eingangsspannung und damit den Einsatz beschreibt. 1 = PMOS-CMOS (allgemeiner Einsatz mit Widerstand zur Strombegrenzung), 2 = 14-25V PMOS (10.5k mit Zener-Diode), 3 = 5V TTL/CMOS (mit 2.7k) und 4 = 6-15V CMOS/PMOS (10.5k).

Der Chip enthält neben dem Transistor eine Diode, um die Induktivitäten abzufangen.

Jeder Pin schaltet bis zu 50V und erlaubt 0.5A mit Peaks bis 0.6A. Die Outputs können parallel geschaltet werden, um grössere Ströme zu erlauben.

H-Brücke L293D

Pinbelegung L293D

Der L293D-Chip kann 2 Motoren gleichzeitig ansteuern. Dabei darf er mit max. 600mA Dauerstrom belastet werden (pro Kanal) und er verträgt Spitzen bis 1A. Bei einer H-Brücke werden die Anschlüsse über Transistoren so geschaltet, dass die Richtung gedreht wird, ohne einen Kurzschluss zu verursachen. Wenn man den Chip betrachtet, so kann man ihn links und rechts teilen. Einzig die Anschlüsse für die Spannungen sind gemeinsam auszuführen. Entsprechend wird nur die "linke" Seite beschrieben.
Die Einspeisung für den Motor (3V-36V) geschieht über Vs (Pin 8). Die Speisung des IC (5-36V) geschieht über Vss (Pin 16). Damit der IC arbeitet, muss man den Enable-Pin (1) setzen. Die Geschwindigkeit kann man ensprechend mit PWM auf diesen Pin steuern. Die Drehrichtung wird über die zwei Input-Pins (2+7) gesteuert. Hierbei muss für Uhrzeigersinn der Input1 (Pin 2) aktiv sein und für Gegenuhrzeigersinn der Input2 (Pin 7). Der Motor selber wird an Output1 und Output2 angeschlossen. Ist die Drehrichtung verkehrt, müssen die Kabel hier getauscht werden.
Damit die Masse stimmt, wird das Minus der Einspeisung und ein GND-Pin des Raspi auf einen der GND des L293D geführt (Pin 4,5,12,13).
Der Chip kann entsprechend für die Richtungsumschaltung bei Gleichstrommotoren oder bei bipolaren Schrittmotoren verwendet werden.

H-Brücken-Motorsteuerungs-Modul

Es gibt von verschiedenen Herstellern Module zum Ansteuern von Motoren. Je nach Modul müssen diese anders verdrahtet werden, doch das Prinzip bleibt gleich. Entsprechend muss man das Beiblatt der Schaltung studieren, auch was die Belastbarkeit des Moduls angeht.

NAND-Gates MC14093B

Der MC14093B enthält vier 2-fach NAND-Gates und arbeitet mit 5-15V. Bei 5V werden Werte unter 1.9V als Low und über 2.9 als High ausgewertet. Die Pinbelegung ist gleich wie beim MC14081B 2-Fach AND-Gate.

Timerbaustein 555

Der CA555E, NE555 oder ähnliche Timer-Bausteine bieten die Möglichkeit, Ausgänge in definierten Zeitabständen zu schalten, eine Weile beibehalten oder nach Vorgabe umzuschalten.

Pin Name Bedeutung
1 GND Masse
2 TRIG Fällt der Pin unter 3V, so wird Pin 3 auf die Eingangsspannung gesetzt.
3 OUT 0V oder VCC. Je nach Zustand des IC.
4 RESET Wenn auf High, wird das Zeitintervall nicht zurückgesetzt.
5 CTRL ?
6 THRES Steigt die Sapnnung am Pin über 2/3 der Eingangsspannung, wird Pin3 auf 0V gesetzt.
7 Disc ?
8 VCC Eingangsspannung (4.5-15V)
Blinkschaltung aus dem Buch "Das Sensor-Buch"

Zähler

(4-Bit) MC74HC193N

Der MC74HC193N ist ein setzbarer binärer 4-Bit Auf-/Abwärts-Zähler mit Reset-Möglichkeit. Er ist pinkompatibel mit dem LS193. Wie man der Info entnehmen kann, ist es möglich, den Zähler zu kaskadieren, um grössere Bereiche zu zählen.

MC74HC193N Belegung und Diagramm

Dekadenzähler MC14017BCP

Dieser Zähler repräsentiert an seinen Ausgängen jeweils die 10er-Zahl. So können entsprechend dezimale Anwendungen angesteuert werden.

Verwendung MC14017BCP

Sensoren

Ein Sensor liefert Daten. Dies kann entweder digital erfolgen oder analog. Da der Raspi keine analogen Eingänge hat, müssen diese über einen A/D-Wandler eingelesen werden. Wenn die Werte nicht exakt sein müssen, können die Werte auch digital mittels eines Kondensators und der resultierenden Sprungantwort berechnet werden.
Die Pegel an den Pins sollten unter 0.8V für Low und mindestens 1.3V für High haben, um undefinierte Zustände zu vermeiden.

Sprungantwort (analoge Werte an digitalem Pin)

Aufbau Schaltung für Sprungantwort vom Raspi-Kochbuch
# https://github.com/simonmonk/raspberrypi_cookbook_ed2/blob/master/pot_step.py
import RPi.GPIO as GPIO
import time, math

C = 0.33 # uF
R1 = 1000 # Ohms

GPIO.setmode(GPIO.BCM)

# Pin a charges the capacitor through a fixed 1k resistor and the thermistor in series
# pin b discharges the capacitor through a fixed 1k resistor 
a_pin = 18
b_pin = 23 

# empty the capacitor ready to start filling it up
def discharge():
   GPIO.setup(a_pin, GPIO.IN)
   GPIO.setup(b_pin, GPIO.OUT)
   GPIO.output(b_pin, False)
   time.sleep(0.1)

# return the time taken (uS) for the voltage on the capacitor to count as a digital input HIGH
# than means around 1.65V
def charge_time():
   GPIO.setup(b_pin, GPIO.IN)
   GPIO.setup(a_pin, GPIO.OUT)
   GPIO.output(a_pin, True)
   t1 = time.time()
   while not GPIO.input(b_pin):
       pass
   t2 = time.time()
   return (t2 - t1) * 1000000

# Take an analog reading as the time taken to charge after first discharging the capacitor
def analog_read():
   discharge()
   t = charge_time()
   discharge()
   return t

# Convert the time taken to charge the cpacitor into a value of resistance
# To reduce errors, do it 100 times and take the average.
def read_resistance():
   n = 10
   total = 0;
   for i in range(1, n):
       total = total + analog_read()
   t = total / float(n)
   T = t * 0.632 * 3.3
   r = (T / C) - R1
   return r
try:
   while True:
       print(read_resistance())
       time.sleep(0.5)
finally:
   GPIO.cleanup()

Beschleunigung

Bewegung

Mit einem PIR (Passive Infrarot) Bewegungsmelder kann man erkennen, falls eine Bewegung im Umfeld des Sensors stattgefunden hat. Je nach Sensor kann man mit einem Potentiometer die Empfindlichkeit des Sensors einstellen, wenn man etwa ein Aquarium mit Fischen hat und nicht möchte, dass der Sensor deswegen auslöst. Es gibt auch Sensoren, bei denen man einstellen kann, wie lange das Ausgangssignal aktiv bleiben soll, wenn der Sensor ausgelöst hat.

Es gibt verschiedene Sensoren auf dem Markt. Entsprechend muss man schauen, ob diese eine Einspeisung von 3.3V/5V oder eine andere Einspeisung brauchen. Zusätzlich ist auch wichtig, welche Ausgangsspannung sie generieren. Ist diese höher als 3.3V, so muss man entsprechend einen Spannungsteiler einsetzen, um den Pin zu schützen.

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

while True:
   input_state = GPIO.input(18)
   if input_state == False:
       print('Bewegung entdeckt')
   time.sleep(0.5)

CO2

Drehung/Drehgeber

Um eine Drehung zu erfassen, gibt es mehrere Möglichkeiten. In diesem Abschnitt soll der Drehgeber betrachtet werden, welcher über ein Schaltmuster aufzeigt, in welche Richtung (und wie viel) er gedreht wird. Meist wird ein Quadratur-Verfahren verwendet mit zwei unterschiedlichen Spulen, so dass sich das Bewegungsmuster nach 4 Schritten wiederholt. Schlussendlich ergibt sich ein fixes 2-Bit-Muster mit definierten Übergängen, wodurch man erkennt, ob nach links oder rechts gedreht wurde. Damit keine Sprünge entstehen, muss man schneller einlesen, als der Drehgeber die übernächste Position erreicht hat. Im "schlimmsten" Fall detektiert man sonst einen Schritt nach links, obwohl man drei Schritte nach rechts (oder umgekehrt) gedreht hat. Zusätzlich sollte man aber auch beim Einlesen eine kurze Pause einlegen (von 1ms), damit bei Übergängen kein "Prellen" entsteht.

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

input_A = 18
input_B = 23

GPIO.setup(input_A, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(input_B, GPIO.IN, pull_up_down=GPIO.PUD_UP)

old_a = True
old_b = True
x = 0

def get_encoder_turn():
   # return -1, 0, or +1
   global old_a, old_b                    # globale Variablen müssen in der Funktion gekennzeichnet werden, wenn man sie nicht nur lesen, sondern beschreiben will
   result = 0
   new_a = GPIO.input(input_A)
   new_b = GPIO.input(input_B)
   if new_a != old_a or new_b != old_b :  # Logik für die Auswertung, ob vorwärts oder rückwärts gedreht wurde
       if old_a == 0 and new_a == 1 :
           result = (old_b * 2 - 1)
       elif old_b == 0 and new_b == 1 :
           result = -(old_a * 2 - 1)
   old_a = new_a
   old_b = new_b
   time.sleep(0.001)                      # Verhindert "prellen"
   return result

while True:
   change = get_encoder_turn()
   if change != 0 :                       # Bei 0 wurde nichts geändert, daher nur zählen, wenn gedreht wurde
     x = x + change
     print(x)

Druck (Kraft)

Hier kann etwa der FlexiForce eingesetzt werden, welcher auf Druck reagiert. Auch wenn dieser WIderstand 3 Anschlüsse hat, muss man ihn je nachdem mit einem Spannungsteiler anschliessen. Weil der mittlere Anschluss je nachdem nicht belegt ist. Dies erfährt man im Datenblatt. Wie man den Spannungsteiler aufbaut, steht im Abschnitt Helligkeit und ein Programm zum Einlesen kann man beim Arduino nachschauen.

Entfernung

mit Infrarot

Hier gibt es zwei Varianten siehe beim Arduino. Die digitale schaltet, wenn etwas in den Bereich des Sensors kommt, während die analoge einen Wert für den Abstand liefert. Hier der Code für einen "Schalter".

import RPi.GPIO as GPIO
import time

def my_callback(channel): # selbst definierte Interrupt-Routine
  print('Gegenstand in der Nähe des Sensors!')

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.add_event_detect(18, GPIO.FALLING, callback = my_callback, bouncetime = 100) # Aufruf der Interruptroutine "my_callback", sobald am Pin 18 die Flanke abfällt. 
                                                                                  # Um Prellen zu vermeiden, wird 100ms gewartet

while True:
  i = i + 1
  print(i)
  time.sleep(1) # Dank dem Interrupt wird die Flanke auch "im Schlafen" detektiert.
Infrarot-Sensor an Spannungsteiler

Um die Signale sauber zu verarbeiten, wird hier mit einem Interrupt gearbeitet. Auf dem Bild aus dem Sensorbuch sieht man den Anschluss eines 5V-Sensors, der mittels eines Spannungsteilers angeschlossen wird. Da keine grossen Ströme fliessen sollen und der Raspi alles höher als 1.3V als HIGH erkennt, kann man 2 gleiche Widerstände verwenden, was die Spannung auf 2.5V halbiert. Damit die Verlustleistung nicht zu gross wird, kann man 1k oder 2.2k Widerstände der E-Reihe verwenden.

mit Schall

Hier kann etwa der Parallax PING (kostet 30$) oder der HC-SR04 eingesetzt werden, welcher wesentlich preisgünstiger ist. Je nach Sensor ist die Erkennung erschwert, wenn die Fläche abgewinkelt ist oder sehr weich/porös, so dass die Schallwellen verschluckt werden. Mögliche Anwendungen sind neben den Abstandserkennungen auch Füllstandssensoren, wenn der Sensor die Flüssigkeit detektiert. Beispiele für den Arduino finden sich auf der verlinkten Seite zum HC-SR04. Diese Sensoren senden kein analoges Signal mit der Entfernung, sondern man sendet einen Impuls, indem man die entsprechende Flanke auf den Trigger-Pin sendet und nachher schaut, wann das Modul den Echo-Pin aktiviert. Die entsprechend vergangene Zeit kann man dann für die Entfernungsberechnung verwenden. Beim Berechnen kann man entweder fixe Werte verwenden, oder man macht diese abhängig von der Höhe, der Temperatur und der Luftfeuchtigkeit, da diese die Schallgeschwindigkeit beeinflussen. Da die Werte für praktische Aufgaben und die Genauigkeit der Sensoren nicht relevant sind, kann man auch einfach eine entsprechende Konstante verwenden wie z.B. 58s pro cm für den HC-SR04.
Beim Arduino ist auch ein Beispiel aufgeführt.

# https://github.com/simonmonk/raspberrypi_cookbook_ed2/blob/master/ranger.py
import RPi.GPIO as GPIO
import time

trigger_pin = 18  # Aktiviert das Echo
echo_pin = 23     # Liest das Echo ein

GPIO.setmode(GPIO.BCM)
GPIO.setup(trigger_pin, GPIO.OUT)
GPIO.setup(echo_pin, GPIO.IN)

def send_trigger_pulse():
   GPIO.output(trigger_pin, True)
   time.sleep(0.0001)              # Echo-Puls ganz kurz aktiviert lassen
   GPIO.output(trigger_pin, False)

def wait_for_echo(value, timeout): # Zähler starten für die Zeitberechnung
   count = timeout
   while GPIO.input(echo_pin) != value and count > 0: # Wenn die Zeit 0 ist, Zählung abbrechen (Echo wurde nicht empfangen)
       count = count - 1

def get_distance():
   send_trigger_pulse()
   wait_for_echo(True, 10000) # Die Länge der Distanz wird nicht mit Zeit Trigger bis Zeit Echo berechnet, sondern der Echo-Pin bleibt, je grösser die Distanz, umso länger aktiv
   start = time.time()
   wait_for_echo(False, 10000)
   finish = time.time()
   pulse_len = finish - start
   distance_cm = pulse_len / 0.000058 # Parameter fix aus Schallgeschwindigkeit. Beim Arduino wird gezeigt, wie der Parameter berechnet wird.
   return (distance_cm)
   
while True:
   print("cm=%f" % get_distance())
   time.sleep(1)

GPS

Helligkeit (Licht)

Spannungsteiler mit Fotowiderstand

Hier kann ein Fotowiderstand verwendet werden. Bei den meisten verringert sich der Widerstand mit zunehmender Lichtmenge. Da der Widerstand analog arbeitet, wird er über einen A/D-Wandler angeschlossen. Beim Arduino findet sich dazu ein Beispielcode. Man muss einfach statt einem Poti einen Spannungsteiler (etwa 10k bis 1M) mit dem Fotowiderstand bauen. Der Widerstand muss sich am Sensor orientieren, so dass der Teiler den ganzen Spektrumsbereich liefert. Beim Raspi ist es das gleiche und wird im Abschnitt vom Potentiometer gezeigt.

Möchte man auf den IC verzichten, so kann man die Bereiche (hell / dunkel) auch mit einem RC-Glied und der entsprechenden Sprungantwort auswerten.

Gas (CO2, Methan usw.)

Es gibt Sensoren, welche das Vorhandensein von entsprechenden Gasen (oder ähnlichen) detektieren können. Hierbei wird mit einem Heizelement und einem Katalysator der Widerstand an Hand des gewünschten Gases verändert. Sparkfun führt mehrere Gas-Sensoren. Die Sensoren können entsprechend je nach notwendiger Genauigkeit über ein A/D-Modul oder ein RC-Glied ausgewertet werden.

Kamera

Magnetismus (Hall-Sensor)

Dieser Sensor erkennt, wenn ein Magnet in seine Nähe kommt. Er schaltet dann den Ausgang um. Somit hat man einen digitalen Schalter, der etwa für eine Geschwindigkeitsmessung eingesetzt werden kann (Umdrehungen/Minute) oder als Einbruchsalarm (Fenster öffnet sich).

Bauteilbezeichnung: NJK-5002 A oder C. Der Unterschied besteht darin, dass schwarz bei A auf Plus und beim C auf Minus schaltet, wenn ein Magnet in die Nähe kommt. Sonst gibt es noch den braunen Anschluss für Plus und blau für Minus. Dieser Sensor besitzt eine eingebaute LED, welche dessen Funktion anzeigt.

Matrix-Tastatur (Pinpad/Code-Tastatur)

Diese Tastatur verfügen meist über ein 4x3 Eingabefeld für Zahlen und zwei Sonderzeichen, wobei dies nur eine mögliche Beschriftungsart darstellt da die Auswertung dem Programm überlassen wird, welches lediglich feststellt, welche Taste(n) gedrückt wurde(n). Da für die Erkennung der Spalte und Reihe die Pins korrekt angeschlossen sein müssen, damit man keinen Kurzschluss fabriziert, sollte man das Datenblatt genau lesen. Für die 4 Reihen und 3 Spalten braucht man gesamthaft 7 Pins. Hat die Tastatur einen I2C oder ähnlichen Anschluss, sollte man natürlich diesen verwenden, um Pins zu sparen.

Im nachfolgenden Programm werden die Pins auf Grund der Verdrahtung des SparkFun-Zehnerblocks eingesetzt, doch wie erwähnt, muss man die Pins zwingend aus dem Datenblatt herausfinden. Um keinen Tastendruck zu verpassen, empfiehlt es sich, diese mittels Interrupt einzulesen.

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

rows = [17, 25, 24, 23]  # Pins für die Reihen
cols = [27, 18, 22]      # Pins für die Spalten
keys = [['1', '2', '3'], # Array für die einzulesenden Tasten
        ['4', '5', '6'],
        ['7', '8', '9'],
        ['*', '0', '#']]

for row_pin in rows:    # Die Reihen werden als Input definiert
   GPIO.setup(row_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

for col_pin in cols:    # Die Spalten werden als Output definiert.
   GPIO.setup(col_pin, GPIO.OUT)

def get_key():
   key = 0
   for col_num, col_pin in enumerate(cols):     # Schlaufe durch die Spalten
       GPIO.output(col_pin, 1)                  # Spalte aktivieren
       for row_num, row_pin in enumerate(rows): # Schlaufe durch die Reihen
           if GPIO.input(row_pin):              # Kontrolle, ob "Durchgang"
               key = keys[row_num][col_num]     # Zeichen(Position) aus Array auslesen und speichern.
       GPIO.output(col_pin, 0)                  # Spalte deaktivieren
   return key

while True:
   key = get_key()   # Tastatur abfragen und Rückgabewert speichern.
   if key :          # Wenn key = 0, dann false und somit keine Auswertung.
       print(key)    # Zeichen ausgeben. Der Stringwert '0' ist etwas anderes als die Zahl 0.
   time.sleep(0.3)   # Programm verlangsamen -> Mit Interrupt verpasst man keine Tasten und kann trotzdem länger schlafen

(USB)-Maus

Mit der pygame-Bibliothek können auch Maus-Bewegungen und Klicks (MOUSEBUTTONDOWN/MOUSEBUTTONUP) ausgewertet werden. Dies allerdings nur auf der grafischen Oberfläche:

# https://github.com/simonmonk/raspberrypi_cookbook_ed2/blob/master/mouse_pygame.py
import pygame
import sys
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mouse.set_visible(0)

while True:
   for event in pygame.event.get():
       if event.type == QUIT:
           sys.exit()
       if event.type == MOUSEMOTION:
           print("Mouse: (%d, %d)" % event.pos)

Die Referenzseite der Bibliothek ist pygame doc

Neigung

Potentiometer

Poti über MCP 3002 einlesen aus dem Sensorbuch

Dieser Drehwiderstand besitzt meist 3 Pins, wobei sich Links/Mitte und Mitte/Rechts ergänzend verhalten und Links/Rechts immer den maximalen Widerstand liefern. Ein Beispiel findet sich auch beim Arduino. Im Gegensatz zu diesem müssen wir beim Raspi einen AD-Wandler einsetzen wie etwa den MCP 3002. Damit dieser angesprochen werden kann, muss SPI freigschaltet sein.

# botbook_mcp3002.py - read analog values from mcp3002
# (c) BotBook.com - Karvinen, Karvinen, Valtokari

import spidev
import time

def readAnalog(device = 0, channel = 0): # Funktion, um Daten über SPI vom MCP 3002 auszulesen. Der channel ist der gewünschte Kanal. Der 3002 hat zwei Kanäle.
  assert device in (1, 0)                # Test, dass das device 0 oder 1 ist -> sonst Abbruch
  assert channel in (1, 0)               # Test auf den Kanal. Bei mehr Kanälen z.B. MCP 3008 müsste man erweitern
  spi = spidev.SpiDev()                  # spi-Objekt generieren
  spi.open(0, device)                    # spi-Verbindung öffnen
  command = [1, (2 + channel) << 6, 0]   # Es wird ein 3 * 8Bit Array gesendet. Im ersten Array kommt das Startbit (letzte Stelle). Im zweiten folgen die Modi für den Austausch.
                                         # Das erste Bit sagt, ob das Signal selber (1) oder die Differenz (0) gesendet werden soll. Das zweite Bit ist abhängig vom ausgelesen Kanal.
                                         # Danach könnte man noch sagen ob MSB oder LSB aktiv sein soll. Dies bleibt aber 0. Entsprechend
                                         # ergeben sich nach dem Shift um 6 Stellen und dem letzten Byte 0 -> 0000 0001 1x00 0000 0000 0000 (das x ist 0 oder 1 je nach Kanal).
  reply = spi.xfer2(command)             # Es wird ein 3 * 8Bit Array zurückgeliefert.
  value = reply[1] & 31                  # Das erste Paket (reply[0]) wird nicht verwendet. Beim zweiten Paket werden nur die hinteren 5 Bit verwendet (31 = 0000 0001 1111).
  value = value << 6                     # Nun werden die Bits um 6 Positionen nach vorne geschoben, damit diese nicht vom Inhalt
                                         # des dritten Paketes überschrieben werden (0000 000D DDDD << 6 = 0DDD DD00 0000).
  value = value + (reply[2] >> 2)        # Vom letzten Paket braucht es nur die höheren 6 Bits. Da die letzten 2 Bits nicht relevant sind (Der MCP 3002 liefert nur 10 Bits Auflösung),
                                         # schiebt man daher die Bits nach rechts (DDDD DDXX >> 2 = 00DD DDDD) und fügt diese dann an (0DDD DD00 0000 + 0000 00DD DDDD = 0DDD DDDD DDDD).
  spi.close()                            # spi-Verbindung schliessen
  return value

while(True):
  print(readAnalog(0, 0)
  time.sleep(0.5)

Weitere Beispiele für die Anbindung des Wandlers im Netz.

Schalter

Der Schalter behält im Gegensatz zum Taster seine Position. Je nach Ausführung gibt es Schalter mit 2 oder mit mehr Positionen. Manche Schalter haben auch eine "Tasterposition", welche man zwar anwählen kann, die dann aber wieder "zurückspringt", so dass diese Position nicht dauerhaft beibehalten wird.
Je nach Einsatz besitzen Schalter statt 2 3 Pins, wodurch eine Umschaltung erreicht wird, wenn etwa mehrere Schalter in Reihe einen Gesamtzustand schalten sollen (Licht im Treppenhaus mit mehreren Schaltern).
Je nach Ausführung kann der Schalter eine gewisse Spannung und Leistung schalten, die man aus dem Datenblatt entnimmt. Die Überwachung eines Schalters geschieht programmtechnisch gleich wie beim Taster.

(USB)-Tastatur

Um Tastendrücke einzulesen gibt es die Systemfunktion sys.stdin.read() oder die grafische Bibliothek pygame.

import sys, tty, termios
       
def read_ch():
   fd = sys.stdin.fileno()
   old_settings = termios.tcgetattr(fd)
   try:
       tty.setraw(sys.stdin.fileno())
       ch = sys.stdin.read(1)
   finally:
       termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
   return ch

while True:
   ch = read_ch()
   if ch == 'x':
       break
   print("Taste: " + ch)

Die grafische Ausgabe mit pygame. In diesem Programm werden nur ASCII-Zeichen ausgegeben. Steuerzeichen werden entsprechend nicht ausgegeben.

import pygame
import sys
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mouse.set_visible(0)

while True:
   for event in pygame.event.get():
       if event.type == QUIT:
           sys.exit()
       if event.type == KEYDOWN:
           print("Code: " + str(event.key) + " Zeichen: " + chr(event.key))

Taster

Im Gegensatz zum Schalter verbleibt der Taster nicht in seiner geschalteten Position, sondern kehrt selbständig wieder in seine Ruheposition zurück. Normalerweise besitzt ein Taster nur 2 Postionen, wobei auch hier mehrere Positionen möglich sind.

Damit der Raspi sauber entscheiden kann, ob der Taster gedrückt ist oder nicht, braucht es einen Widerstand, welcher das Signal bei einer ungedrückten Taste entweder nach +5V oder GND zieht. Dafür besitzt der Raspi interne Pull-Widerstände, welche man bei der Pin-Definition mitgeben kann. So braucht es keinen externen Widerstand, um das Signal sauber zu halten. Je nach Leitungslänge ist ein solcher trotzdem notwendig, da die internen Widerstände relativ hoch sind (etwa 40k dies ergibt einen Strom von nur 82A). Falls es aber mehr Strom braucht, so kann man einfach einen externen Widerstand verwenden. Bei einem 100 Widerstand fliessen 33mA, was noch innerhalb der erlaubten 50mA pro Pin liegt, doch sollte man nach Möglichkeit den Verluststrom so tief als möglich halten.

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Den Widerstand als "Pull up" definieren. Dies, damit möglichst kleine Verlustströme auftreten.

while True:
   input_state = GPIO.input(18)
   if input_state == False:                       # "Pull up" zieht im nicht gedrückten Zustand nach High, so dass gedrückt == False bedeutet.
       print('Button Pressed')
       time.sleep(0.2)                            # In einem richtigen Programm sollte zur Überwachung statt einer "Schlafroutine" ein Interrupt verwendet werden.

Temperatur (1-Wire-Anbindung)

Für die Temperaturmessung gibt es den LM35 oder TMP36 im Transistorgehäuse, der sich direkt als analoger Sensor anschliessen lässt und nicht einmal einen Pull-Up Widerstand braucht. Der gelesene Wert kann direkt mittels gelesene Spannung * 100°C/V umgerechnet werden.

Wenn man ihn von vorne anschaut (flache Seite), so befinden sich links die Referenzspannung (5V (4-30V)) und rechts GND, während die Mitte an den analogen Anschluss geführt wird. Da der LM35 macx. 155°C messen kann und dabei 1.5V abgibt, ist dessen Verwendung problemlos. Es gibt auch Sensoren, welche direkt für 3.3V ausgelegt sind, wie den TMP36. Ein Programm zum Verarbeiten der Werte gibt es beim Arduino. Für den Raspi nur die folgenden Zeilen zum umrechnen der Werte in eine Temperatur, da das Programm fürs einlesen gleich ist wie beim Potentiometer.

percent = data / 1023.0
volts = percent * 3.3
celsius = 100.0 * volts

Es gibt natürlich auch genauere Sensoren wie den DS18B20 oder den DHT22(AM2302), der über eine 1-Wire-Schnittstelle angebunden wird.

# https://github.com/simonmonk/raspberrypi_cookbook_ed2/blob/master/temp_DS18B20.py
import glob, time

base_dir = '/sys/bus/w1/devices/'              # Der Eindraht-Bus verwendet zur Kommunikation Dateien
device_folder = glob.glob(base_dir + '28*')[0] # Alle Sensoren befinden sich immer in diesem Unterverzeichnis
device_file = device_folder + '/w1_slave'      # Das Programm setzt voraus, dass es nur einen Slave gibt. Hier müsste man entsprechend konfigurieren bei mehreren Sensoren.

def read_temp_raw():                           # Die Datei einlesen
   f = open(device_file, 'r')
   lines = f.readlines()
   f.close()
   return lines

def read_temp():                               # Die Datei auswerten und dabei die Temperatur extrahieren
   lines = read_temp_raw()
   while lines[0].strip()[-3:] != 'YES':
     time.sleep(0.2)
     lines = read_temp_raw()
   equals_pos = lines[1].find('t=')
   if equals_pos != -1:
     temp_string = lines[1][equals_pos+2:]
     temp_c = float(temp_string) / 1000.0
     temp_f = temp_c * 9.0 / 5.0 + 32.0
     return temp_c, temp_f

while True:
  print("temp C=%f\ttemp F=%f" % read_temp())	# Die Temperatur in C und F ausgeben
  time.sleep(1)

Aktoren

Lautsprecher / Piezo-Summer

Kleine Summer können direkt an einen Pin angeschlossen werden. Wenn man nicht sicher ist, ob nicht doch zu viel Strom fliesst, kann wie bei einer LED ein 470 Widerstand angeschlossen werden.

import RPi.GPIO as GPIO
import time

buzzer_pin = 18

GPIO.setmode(GPIO.BCM)
GPIO.setup(buzzer_pin, GPIO.OUT)

dev buzz(pitch, duration): # pitch = Tonhöhe, duration = Tonlänge
    period = 1.0 / pitch
    delay = period / 2
    cycles = int(duration * pitch)
    for i in range(cycles):
        GPIO.output(buzzer_pin, True)
        time.sleep(delay)
        GPIO.output(buzzer_pin, False)
        time.sleep(delay)

   while True:
        pitch_s = input("Tonhöhe eingeben (200 bis 2000 Hz): ")
        pitch = float(pitch_s)
        duration_s = input("Tonlänge eingeben (in Sekunden): ")
        duration = float(duration_s)
        buzz(pitch, duration)

Diese Summer haben keine gute Tonqualität und auch die Tonhöhe ist nur relativ. Das Programm dient daher nur zu Demonstationszwecken und kann keinen Lautsprecher ersetzen. Es stammt von Simon Monk und kann auf [github heruntergeladen werden.

LC-Display

Hier gibt es verschiedene Anbieter und Chips. Auch ist die Darstellung unterschiedlich. Die meisten stellen nur Zeichen auf 1 oder mehr Zeilen dar, während andere auch Grafiken erlauben. Beim Modul von Adafruit kann man nach dem herunterladen der Bibliothek (Python-Code) folgendes kleines Programm starten:

from Adafruit_CharLCD import Adafruit_CharLCD
from time import sleep

lcd = Adafruit_CharLCD() # Instanz generieren
lcd.begin(16,2)          # Definieren, wie viele Zeichen und Zeilen das Display unterstützt
i = 0

while True:
  lcd.clear()
  lcd.message("Counting: " + str(i))
  sleep(1)
  i = i + 1

LED

LED anschliessen

Um die LED und den RasPi zu schützen, muss jeweils ein [Vorwiderstand] angeschlossen werden. Im Link steht, wie man die [Farbcodes] liest, wenn man kein Multimeter zur Verfügung hat. Je nach Leuchtstärke und [Farbe der LED] ergeben sich sich folgende Werte:

LED-Farbe Widerstand Stromfluss (mA)
rot 470 2.7-3.5
rot 680 1.9-2.2
rot 1 k 1.3-1.5
orange / gelb / grün 470 2
orange / gelb / grün 1 k 1
blau / weiss 470 3
blau / weiss 1 k 1

Im oben verlinkten Artikel zum Vorwiderstand wird auf die Vorwärtsspannung der LED hingewiesen, welche dem Datenblatt entnommen werden muss, da die LED bauartbedingt unterschiedliche Spannungen haben und damit auch unterschiedliche Widerstandswerte brauchen. Die Widerstandswerte sind der [E6-Widerstandsreihe] entnommen.

Da LEDs Dioden sind, besitzen sie eine Sperr- und eine Durchgangsrichtung. In Sperrrichtung vertragen sie eine Spannung von 25V bis zu 30V. Somit geschieht bei Experimenten mit dem RasPi nichts, wenn man diese falsch gepolt einsetzt. Damit sie leuchtet, muss die Anode am Plus und die Kathode am GND angeschlossen werden. Die Kathodenseite ist normalerweise abgeflacht, kürzer und manchmal auch "abgeknickt".

LED blinken lassen

Das folgende Python-Programm verwendet die RPi.GPIO Bibliothek. Hier wird mit sleep gearbeitet, was man in einem "richtigen" Programm vermeiden sollte, da etwa Tastendrücke damit nicht eingelesen werden. Hier sollten besser Interrupts eingesetzt werden.

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BPM)     # Die Nummerierung der Pins nach dem BPM-Schema verwenden
GPIO.setup(18, GPIO.OUT)   # Pin 18 als Output definieren

while(True):
    GPIO.output(18, True)  # Pin einschalten
    time.sleep(0.5)        # 1/2 Sekunde warten
    GPIO.output(18, False) # Pin ausschalten
    time.sleep(0.5)

Es ist auch möglich, die LED manuell über ein GUI mit tkinter anzusteuern. Das Programm stammt von Simon Monk und kann auf [github heruntergeladen werden.

LED dimmen

Durch Pulsweitenmodulation lässt man eine LED in hoher Frequenz ein- und ausschalten, so dass es für das Auge aussieht, als sei die LED dunkler oder heller. Bei der [PWM] wird ein Puls nur zu % Prozenz der Pulslänge aktiviert.

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BPM)     # Die Nummerierung der Pins nach dem BPM-Schema verwenden
GPIO.setup(18, GPIO.OUT)   # Pin 18 als Output definieren

pwm_led = GPIO.PWM(18, 500)# PWM-Frequenz vom Pin 18 auf 500 Hertz setzen
pwm_led.start(100)         # mit 100% starten?

while(True):
    arg = input("Helligkeit in %:") # Wert einlesen
    arg = int(arg)         # nach int konvertieren
    pwm_led.ChangeDutyCycle(arg) # Prozentwert übergeben
    time.sleep(0.5)

Das Programm stammt von Simon Monk und kann auf github heruntergeladen werden.

Je nach Chip und Frequenz weicht die effektive Frequenz von der übergebenen Frequenz ab. Je schneller die gewünschte Freq. umso grösser ist der Abstand. Bei 10kHz kann es sein, dass es effektiv nur 4.4kHz sein. Dies könnte mit einem [Oszilloskop] gemessen werden. Im tieferen Frequentbereich wie bei 500 Hz sind es etwa 470 Hz, was völlig ausreichend ist. Wenn man die entsprechende Abweichungskurve kennt, kann man dieser natürlich entgegenwirken, indem man die Vorgabe entsprechend erhöht.

Im Kapitel Timerbaustein 555 sieht man eine alternative Möglichkeit der Dimmung mit einem Transistor.

Wenn man PWM grafisch einsetzen möchte, kann man dies auch mit tkinter umsetzen.

Hochleistungs-LED-Feld

Um grössere Leistungen schalten zu können, wie etwa ein ganzes LED-Feld mit Hochleistungs-LEDs genügt der maximale Strom eines GPIO-Pins nicht mehr. Hier braucht es einen Transistor, welcher die Leistung durchlässt und mit dem Ausgang des GPIO geschaltet wird. Für die Einspeisung der LED muss je nach Leistung eine externe Spannungsquelle (5V / 12V) verwendet werden.

LED-Feld mit Transistor ansteuern aus dem Buch "Raspberry Pi Kochbuch"
  • Die programmtechnische Ansteuerung (Ein/Aus, Dimmung) geschieht genau gleich wie bei einer einzelnen LED.
  • Als Transistor wird ein MOSFET wie der FQP30N06 verwendet, der bis zu 30 Ampere durchschalten kann.
  • Als Vorwiderstand für die Basis genügt ein 1k Widerstand.

Mit dieser Anordnung können auch andere Niederspannungsgeräte angesteuert werden. Ausnahmen sind Relais und Motoren, die auf Grund von Spannungsspitzen und/oder spezieller Ansteuerung gesondert angesteuert werden müssen.

7-Segment-LED-Display

Diese Anzeigen können direkt angesteuert werden. Meist verwendet man aber eine serielle Schnittstelle wie I2C, was natürlich entsprechend eingerichtet sein muss. Für die Ansteuerung der Anzeige von Adafruit gibt es eine Bibliothek. Beispiele und das Projekt finden sich auf der Homepage von Adafruit.

RGB-LED

Eine RGB-LED besteht aus einzelnen LEDs und braucht daher 3 PINs, welche dann kombiniert werden. Um mehr als nur die Grundfarben zu kombinieren, kann man mit PWM auch Zwischenfarben und Farbverläufe erziehlen. Im Artikel zu Python ist ein Beispielprogramm mit Schiebereglern zu finden.
Bei einer RGB-LED verwendet man die gleichen Vorwiderstände (1k) wie bei einer normalen LED. Bei gemeinsamer Kathode schliesst man diese an GND und die Anoden über die Widerstände an die jeweiligen PINs. Die Belegung der LED ist dem Datenblatt zu entnehmen. Wenn die Beinchen nicht gekürzt wurden, ist die Kathode etwas länger als die Anoden.

Motor

Gleichstrommotor

Anschluss Gleichstrommotor

Diese Motorart kann man entweder digital (Ein/Aus) ansteuern oder man bestimmt die Geschwindigkeit über PWM. Hierbei muss man aber berücksichtigen, dass sich die Leistung nicht linear verhält. Vielfach werden die kleinen Motoren daher mit einem Getriebe untersetzt. Um die Elektronik vor Stromspitzen beim Abschalten des Motors durch die Induktion zu schützen, wird eine Freilauf-Diode (1N4001) zwischengeschaltet, welche auf den Motor abgestimmt sein sollte. Zusätzlich wird auch der Pin des Raspi über einen Widerstand (1k) angeschlossen, so dass nicht zu viel Strom fliesst.
Je nach Motor kann dieser direkt über den Raspi eingespeist werden. Zieht er zu viel Strom, so dass dieser abstürzt oder sonstwie unstabil wird, muss er extern gespiesen werden. Als Steuerungstransistor kann der 2N3904 oder einer mit ähnlicher Spezifikation verwendet werden. Wichtig ist hier vor allem die Hitze-Entwicklung, da der Strom des Motors auch durch den Transistor fliesst.

Richtung steuern mit H-Brücken-Modul (L293D-Chip)

Die Drehrichtung wird über den Anschluss gesteuert. Je nachdem, wie + und GND angeschlossen werden, dreht der Motor nach links oder nach rechts. Da die hardwaremässige Umverdrahtung natürlich nicht gewünscht ist, wurden entsprechende Module entwickelt, welche auf entsprechenden Steuerimpuls die Richtung umschalten. Die Ansteuerung der Geschwindigkeit geschieht weiterhin transparent über PWM. Ein entsprechender Baustein ist der L293D.

import RPi.GPIO as GPIO
import time

enable_pin = 18 # Aktiviert den Chip. Durch PWM auf diesem Pin wird auch die Geschwindigkeit festgelegt
in1_pin = 23    # Drehrichtung Uhrzeigersinn
in2_pin =24     # Drehrichtung Gegenuhrzeigersinn

GPIO.setmode(GPIO.BCM)
GPIO.setup(enable_pin, GPIO.OUT)
GPIO.setup(in1_pin, GPIO.OUT)
GPIO.setup(in2_pin, GPIO.OUT) 

pwm = GPIO.PWM(enable_pin, 500) # PWM auf 500 Hz setzen
pwm.start(0)

def clockwise():
   GPIO.output(in1_pin, True)    
   GPIO.output(in2_pin, False)

def counter_clockwise():
   GPIO.output(in1_pin, False)
   GPIO.output(in2_pin, True)

while True:
   cmd = input("Befehl (vorwärts(v), rückwärts(r), Geschwindigkeit 0-9 -> r7 oder v3:") # Richtung und Geschwindikeit einlesen
   direction = cmd[0]          # Das erste Zeichen ist die Richtung
   if direction == "v":        # Nur bei v geht es vorwärts
       clockwise()
   else: 
       counter_clockwise()     # Bei einer richtigen Auswertung müsste man natürlich auch auf r prüfen und sonst den Motor anhalten
   speed = int(cmd[1]) * 11    # Berechnet zu wieviel Prozent der Motor angesteuert werden soll
   pwm.ChangeDutyCycle(speed)  # Geschwindigkeit setzen
                               # Bei einem richtigen Programm müssten bei einem Abbruch noch alle Pins rückgesetzt werden

Schrittmotor

Schrittmotoren haben mehrere Wicklungen. Die Art der Magnetisierung dieser Wicklungen bestimmt, ob es sich um einen unipolaren oder bipolaren Schrittmotor handelt. Die folgenden Links zeigen detailliert die Ansteuerung und Unterschiede auf:

Beide Motorarten können mit PWM angesteuert werden, doch Halb- und Viertelschritt beherrscht nur der bipolare Schrittmotor. Vielfach werden 2-phasige Schrittmotoren verwendet, wobei es aber auch 3- oder 5-phasige gibt, welche entsprechend genauere Positionen erlauben (Schrittwinkel tiefer). Durch elektonische Beeinflussung des Ansteuerungssignals sind neben Halb- und Viertelschritt auch Mikroschritte möglich. Dies geht soweit, dass man mit einem Drehfeld arbeitet und so einen Drehstrommotor simuliert. Hierbei ist zwar die Laufruhe extrem gross, doch die Positionsmessung ohne externe Rückmeldung ist nicht mehr einfach möglich.

unipolar
Anschluss Schrittmotor aus dem Buch Raspberry Pi Kochbuch

Die Ansteuerung dieser Variante ist einfacher und wird daher häufig im Niedrigpreis-Segment verwendet. Unipolare Schrittmotoren haben 5 oder 6 Kabel für den Anschluss. Beim 5 kabligen Anschluss wurden die Mittelabgriffe zusammengefasst. Entsprechend kann dieser Motor nur unipolar betrieben werden. Beim Motor mit einer geraden Anzahl Kabel (4, 6, 8) kann man die Ansteuerung auch bipolar gestalten. Durch die Art der Ansteuerung wird immer nur die eine Hälfte der Wicklungen bestromt, wodurch der Wirkungsgrad bei tiefer Drehzahl viel tiefer ist als bei bipolaren Motoren. Bei der Ansteuerung wird der Mittelteil der Spulen an die Bestriebsspannung angeschlossen und anschliessend werden abwechselnd die Enden der Spulen auf Masse geschaltet, so dass das Magnetfeld sich bewegt und sich der Motor in die entsprechende Richtung bewegt. Dadurch, dass nur die halbe Spule verwendet wird, ist aber bei hohen Drehzahlen ein grösseres Moment möglich.

Mit einem Multimeter lassen sich die Anschlüsse (Mittelabgriff, Spulenenden) schnell bestimmen, wenn kein Datenblatt vorhanden ist. Die Ansteuerung kann mit einem Darlington-Chip erfolgen. Der Chip ist für die Ansteuerung von 2 Motoren ausgelegt oder man kann ihn für einen grösseren Strombedarf parallel schalten.

import RPi.GPIO as GPIO
import time
 
GPIO.setmode(GPIO.BCM)
 
coil_A_1_pin = 18
coil_A_2_pin = 23
coil_B_1_pin = 24
coil_B_2_pin = 17
 
GPIO.setup(coil_A_1_pin, GPIO.OUT)
GPIO.setup(coil_A_2_pin, GPIO.OUT)
GPIO.setup(coil_B_1_pin, GPIO.OUT)
GPIO.setup(coil_B_2_pin, GPIO.OUT) 

forward_seq = ['1010', '0110', '0101', '1001'] # Ansteuerungsreihenfolge der Spulen für einen Vollschritt
reverse_seq = list(forward_seq)                # Liste kopieren
reverse_seq.reverse()                          # Dreht die Reiehenfolge der Ansteuerung um
 
def forward(delay, steps):  
  for i in range(steps):
    for step in forward_seq:
      set_step(step)                           # Schritt ausführen -> wie viele Schritte es für eine Umdrehung braucht, ist von der Verzahnung abhängig
      time.sleep(delay)                        # Wartezeit (sollte je nach Motor um die 2 ms betragen. Wenn zu klein dreht sich der Motor nicht.)

def backwards(delay, steps):  
  for i in range(steps):
    for step in reverse_seq:
      set_step(step)
      time.sleep(delay)

 
def set_step(step):
 GPIO.output(coil_A_1_pin, step[0] == '1')
 GPIO.output(coil_A_2_pin, step[1] == '1')
 GPIO.output(coil_B_1_pin, step[2] == '1')
 GPIO.output(coil_B_2_pin, step[3] == '1')

while True:
 set_step('0000')                              # Spulen ausschalten, damit kein Strom fliesst
 delay = input("Wartezeit zwischen den Schritten in ms?")
 steps = input("Wie viele Schritte vorwärts? ")
 forward(int(delay) / 1000.0, int(steps))
 set_step('0000')
 steps = input("Wie viele Schritte rückwärts? ")
 backwards(int(delay) / 1000.0, int(steps))
 set_step('0000')
bipolar
Anschluss bipolarer Schrittmotor aus dem Buch Raspberry Pi Kochbuch

Hier werden die Spulen abwechselnd nicht nur ein- und ausgeschaltet, sondern die Ansteuerung wird auch noch umgedreht. Dadurch wird vor allem bei tiefer Drehzahl ein hohes Drehmoment ermöglicht. Weil aber die ganze Spule eingesetzt wird, ist auch die Impendanz höher, was sich bei hoher Drehzahl auswirkt, weil dann die Magnetisierung nicht mehr komplett abgebaut werden kann und so die Leistung abnimmt. Durch Nutzung der ganzen Wicklung, kann aber eine höhere Leistung bei gleicher Baugrösse oder eine kleine Baugrösse bei gleicher Leistung erreicht werden. Um eine möglichst hohe Effizienz zu erreichen, wird wenn möglich mit Konstantstrom gearbeitet, was aber eine entsprechende Logik voraussetzt, da hierbei gemessen werden muss, wie viel Strom fliesst und diesen dann rechtzeitig abzustellen, um ihn nachher wieder anzustellen, sobald dies auf Grund der Motoreigenschaften wieder notwendig ist. Diese Art der Ansteuerung wird von Motortreiber-Bausteinen übernommen, wodurch die Komplexität der Schaltung gekapselt wird, wobei man "von aussen" nur noch die Signale für die Drehrichtung und die Geschwindigkeit einspeisen muss.
Um die Ansteuerung umzukehren, werden H-Brücken eingesetzt. Der maximale Strom wird meist für den unipolaren Betrieb angegeben (wenn nicht explizit vermerkt) und muss für den bipolaren Betrieb um den Faktor (0.7) verringert werden.
Um Halb- und Viertelschritt zu erreichen, werden neben den unterschiedlichen Ansteuerungen beim Vollschritt noch Zwischenschritte eingebaut, bei denen jeweils nur eine Spule angesteuert wird. Durch die Zwischenschritte ergeben sich zwar genauere Positionen, doch dies wird mit einem kleineren möglichen Drehmoment erkauft. Da die Leistung je nach Ansteuerung der Stulen unterschiedlich ist, ergibt sich bei niedriger Drehzahl ein unruhiger Lauf, der durch gute Motorsteuerungen mit entsprechendem Anstieg des Stromes automatisch kompensiert wird.

Ohne Datenblatt kann man die Kabel ausmessen, um die Spulen zu bestimmen. Hat der Motor 8 Anschlüsse, so sind 2 Spulenpaare vorhanden, welche je nach gewünschter Eigenschaft in Reihe oder parallel angeschlossen werden können. Beim Parallelbetrieb ergibt sich mehr Drehmoment im oberen Drehzahlbereich, doch dafür muss der Stromregler höheren Anforderungen gerecht werden.

Für die Ansteuerung kann durch die Verwendung des L293D das gleiche Programm wie beim unipolaren Motor verwendet werden. Möchte man Halb- oder Viertelschritt implementieren, so muss man die Schrittsequenz verfeinern. Die notwendigen Tabellen finden sich in den Links zum Schrittmotor.

Für die Ansteuerung von bipolaren Schrittmotoren kann auch das RasPi Robot Board verwendet werden. Wichtig ist dort, dass man nur das Robot-Board einspeist und nicht zusätzlich den Raspi, damit keine Ausgleichsströme fliessen, welche die Elektronik zerstören könnten. Bei Verwendung des Boards muss man das Programm etwas modifizieren, da der Chip etwas anders verdrahtet wird. Auf der Webseite ist es das Programm stepper_rrb.py.

Stellmotor / Servo

Diese Motorart beschreibt eine Motoranwendung. Daher ist sie nicht an einen bestimmten Motortyp gebunden, sondern kann aus verschiedenen Motortypen gebildet werden. Wichtig ist dabei nur, dass die Ansteuerung über PWM geschieht und die Motorstellung über eine Messeinrichtung kontrolliert wird. Entsprechend kann der gewünschte Stellwinkel über das PWM-Signal definiert werden. Dies bedeutet aber auch, dass der Motor nicht mehrere (unendliche) Umdrehungen macht, sondern sich nur im Bereich seines Endanschlags bewegt (z.B. 0°-180°). Es gibt aber auch Möglichkeiten, die Servo-Einheit zu modifizieren, so dass er sich wie ein Getriebemotor verhält: Servomodifikation.

Für den Anschluss an den Raspi sollte man eine externe Stromquelle verwenden, um den Raspi nicht zu überlasten. Um Spannungsspitzen beim Anlauf zu vermeiden, sollte der Steuer-Pin mit einem 1k Widerstand geschützt werden. Die Verdrahtung selbst ist simpel, da der Motor nur 3 Anschlüssel hat. 1 wird für die Einspeisung verwendet und der andere für GND und der dritte wird über den Widerstand an den gewünschten GPIO-Pin angeschlossen, welcher das PWM-Signal liefert. Beim Raspi ist dies der Pin 18. Das Programm kann natürlich auch ohne GUI eingesetzt werden.

from tkinter import *     # grafische Bibliothek tkinter laden
import RPi.GPIO as GPIO   # RPi-Bibliothek laden
import time               # Zeit-Bibliothek laden

GPIO.setmode(GPIO.BCM)    # BCM-Modus einschalten
GPIO.setup(18, GPIO.OUT)  # Pin 18 als Output definieren
pwm = GPIO.PWM(18, 100)   # Frequenz auf 100 Hz setzen
pwm.start(5)              # Startstellung ist bei 5%?

class App:                # Klasse definieren

   def __init__(self, master)  : # Konstruktor generieren
       frame = Frame(master)     # Vaterklasse aufrufen
       frame.pack()              # Layoutmanager pack verwenden
       scale = Scale(frame, start = 0, end = 180, orient = HORIZONTAL, command = self.update) # Schieberegler definieren
       scale.grid(row=0)         # Schieberegler positionieren

   def update(self, winkel):     # Funktion zur Anpassung des Winkels erstellen
       position = float(winkel) / 10.0 + 2.5 # PWM-Signal aus Position berechnen
       pwm.ChangeDutyCycle(position) # Neues Signal an den Pin übergeben

root = Tk()                      # Fenster generieren 
root.wm_title('Servo Control')   # Titel setzen
app = App(root)                  # App-Abjekt aus der Klasse erstellen
root.geometry("200x50+0+0")      # Fenstergrösse festlegen
root.mainloop()                  # Start der Routine

Das Programm stammt von Simon Monk und kann auf github heruntergeladen werden.

mehrere Stellmotoren ansteuern

Für die genaue Ansteuerung von (mehreren) Servo-Motoren gibt es von Adafruit ein entsprechendes Modul, das über I2C angesprochen wird.

Relais

Da beim Relais beim Ausschalten eine hohe Spannungsspitze entsteht, muss diese mit einer Diode abgefangen werden, da sonst die gekoppelten elektronischen Bauteile zerstört werden. Da Relais mechanische Bauteile sind, unterliegen sie einer entsprechenden Abnutzung und können auch nicht für hochfrequente Schaltvorgänge benutzt werden. Der Vorteil liegt in getrennten Stromkreisen, so dass man auch eine Wechselspannung (230V) schalten kann, ohne dass diese mit der Schaltung gekoppelt würde. Für grosse Leistungen werden hingegen die verwandten Schütze verwendet. Im Gegensatz zu einem Schalter arbeiten Relais wie Taster, das heisst, sie sind monostabil, wobei es auch bistabile Relais mit zwei Spulen gibt. Da ein Relais rund 50mA Strom benötigt, um den Schalter zu schliessen, muss ein Transistor den Ausgang des Raspi unterstützen.

Relaisansteuerung aus dem Buch "Raspberry Pi Kochbuch"
  • Für den Schutz des GPIO-Pins wird ein 1k Widerstand eingesetzt.
  • Als Schalter genügt ein 2N3904 Transistor ( 200mA, 40V, 625mW).
  • Als Diode wird eine 1N4001 eingesetzt, welche 1A bei bis 50V verträgt. Die letzte Ziffer bestimmt hier die Sperrspannung, welche bei der 1N4007 1000V beträgt.
  • Als Relais kann ein beliebiges 5V Exemplar verwendet werden. Je nach Belegung muss der Aufbau auf dem Steckbrett angepasst werden. Bei der gezeigten Schaltung ist +5V oben links und GND unten links, während sich der geschaltete Ausgang unten rechts und mittig links befindet.
  • Als Ansteuerung kann das gleiche Programm wie bei "LED blinken" verwendet werden. PWM ist wegen der Mechanik aber nicht möglich.

Wechselspannung schalten

Um gefahrlos Wechselspannung zu schalten, gibt es Bausätze oder fertige Elemente, welche ein Relais mit einem Optokoppler verwenden, so dass man den GPIO-Pin direkt ans Gerät anschliessen kann. Solche Produkte und/oder Bausätze sind unter der Bezeichnung "Powerswitch tail" oder "IoT Power Relay" zu finden.