Inhaltsverzeichnis

Microbit programmieren

1. Übersicht

Der micro:bit ist ein Microcontroller (eine Art Mini-Computer), der speziell für den Schulbetrieb entwickelt wurde und mit Python programmiert werden kann.

Er verfügt über eine Vielzahl an Komponenten, die man mit seinem Code ansteuern kann. Mit Display und Lautsprecher kann der Microbit Bild und Ton ausgeben. Zur Eingabe von Daten sind auf dem Microbit zwei Tasten, das Touch-Logo und mehrere Sensoren eingebaut. Auf dieser Seite lernst du die wichtigsten Funktionen kennen, um Display, Lautsprecher, Tasten und Sensoren anzusteuern.

Eine kurze Übersicht zu den wichtigsten Funktionen in englischer Sprache findest du im Microbit-Python-Guide

In der Microbit-Micropython-Dokumentation findest du alle Informationen zur Programmierung des Micro-Bit.

2. Getting started

Für die Programmierung des Microbits nutzen wir den Online-Editor:

  1. Online-Editor:
    1. Verwende Chrome Browser (oder Edge)
    2. Tipp zum Speichern::
      1. Kopiere deinen Code jeweils in ein Word Dokument.
      2. Verwende z.B. folgende Vorlage: Template Aufgaben
      3. Bonus bei diesem Vorgehen: schöne Übersicht von allen deinen Codes!

Mu Editor

from microbit import *
display.scroll("Hello, World!")

3. Display

Text anzeigen

Wir schauen uns kurz die beiden Code-Zeilen des Hello-World-Beispiels an:

from microbit import *
display.scroll("Hello, World!")

Über das display-Objekt kannst du auf weitere Display-Funktionen zugreifen. Wenn du im Mu-Editor display und dann einen Punkt eintippst, erhältst du eine Liste mit allen Display-Funktionen. Die wichtigsten sind clear() zum Löschen des Displays und show() zur Anzeige von einem oder mehreren Bildern.

Bilder anzeigen

Folgender Code zeigt ein Herz auf dem Display:

from microbit import *
display.show(Image.HEART)

Über das Image-Objekt kannst du auf eine Reihe von vordefinierten Bildern zugreifen.

Hier findest du eine Übersicht über die Bilder und eine Anleitung zum Erstellen von eigenen Bildern und Animationen.

Animationen mit Bilder-Listen

In Python kannst du auf einfache Weise Listen erstellen, das hast du hier gelernt. Im folgenden Code wird eine Liste von Bildern erstellt und angezeigt.

from microbit import *
 
my_images = [Image.HAPPY, Image.SMILE, Image.SAD, Image.CONFUSED]
 
display.show(my_images)

Die Funktion show() merkt, dass sie es hier mit einer Liste zu tun hat und zeigt nun jedes Bild der Liste nacheinander.

Du kannst der Funktion weitere Parameter übergeben:

display.show(my_images, delay = 500, loop = True)

4. Tasten

Der Microbit kann über die Tasten A und B sowie per Touch-Logo bedient werden. Mit den Funktionen is_pressed() bzw. is_touched() kannst du fragen, ob die jeweilige Taste gedrückt ist. Das folgende Programm zeigt je nach betätigtem Bedienelement ein anderes Bild auf dem Display:

Beispiel 1
from microbit import *
 
while True:
    if button_a.is_pressed():
        display.show(Image.HAPPY)
    if button_b.was_pressed():
        display.show(Image.SAD)
    if pin_logo.is_touched():
        display.show(Image.HEART)

Mit while True: wird eine Endlos-Schleife eröffnet: Der Code darunter wird endlos wiederholt:

Diese If-Abfragen erfolgen so schnell, dass die Endlos-Schleife jede Sekunde mehrere tausend Mal durchlaufen wird. Du kannst also jederzeit eine Taste drücken und das Bild wird sofort angepasst – probiere es aus!


Beispiel 2

Beachte, dass es für Tasten drei Befehle gibt: is_pressed(), was_pressed() und get_presses(). Um diese besser zu verstehen, hier ein weiteres Beispiel – Lade den Code auf deinen Microbit und probiere es aus:

from microbit import *
 
while True:
    display.scroll("Hallo")
    if button_a.is_pressed():
        display.show(Image.DUCK) # Zeige Ente
        sleep(1000)
    if button_a.was_pressed():
        display.show(button_a.get_presses()) # Zeige, wie oft Taste A gedrückt
        sleep(1000)


Zusammenfassung

Hier nochmal die drei Funktion zusammengefasst:

Pins

Zusätzlich befinden sich unten am micro:bit Pins, die man verwenden kann. Probiere folgenden Code aus:

# Imports go at the top
from microbit import *
 
while True:
    if pin0.is_touched():
        display.show(Image.HAPPY)
    else:
        display.show(Image.SAD)

Sobald du nun den Stromkreis zwischen Ground (GND) und Pin 0 schliesst (beide gleichzeitig berühren oder mit Kabel verbinden), sollte nun ein Happy-Smile angezeigt werden.

Für mehr Infos siehe hier: https://microbit-micropython.readthedocs.io/en/latest/pin.html

5. Beschleunigungssensor

Auf der Rückseite des Microbits befindet sich unten links ein Beschleunigungssensor. Der Beschleunigungssensor misst die Beschleunigung, die der Microbit in x-, y- und in z-Richtung erfährt. Du kannst dir eine Drohne vorstellen: Wenn sie immer vorwärts und rückwärts fliegt, beschleunigt sie in x-, wenn sie seitwärts hin und her fliegt in y- und wenn sie hoch oder runter fliegt in z-Richtung.

Beschleunigung messen

Folgender Code liest alle 100 ms die Werte aus dem Beschleunigungssensor und sendet sie über USB:

from microbit import *
 
while True:
    sleep(100)
    print(accelerometer.get_values())

Innerhalb der Endlos-Schleife while True werden zwei Befehle ständig wiederholt:

Probiere es aus: Lade obigen Code auf den Microbit und öffne im Python Editor die Konsole („Show serial“). Wenn du den Microbit wild schüttelst – achte auf das Kabel und darauf, dass du nicht deinen Laptop mitreisst – dann verändern sich die drei Werte.

Versuche jetzt, den Microbit so zu bewegen, dass jeweils nur einer der drei Werte ausschlägt.

Zur Info:

Gesten erkennen

Mit dem Beschleunigungssensor können auch bestimmte Gesten (engl. gestures) erkannt werden – zum Beispiel, ob der Microbit geschüttelt wurde („shake“), ob er nach oben („face up“) oder nach unten („face down“) gedreht ist usw. Folgender Code zeigt ein Happy Face, wenn der Microbit nach oben gedreht ist, sonst ein Sad Face:

from microbit import *
 
while True: 
    if accelerometer.is_gesture("face up"):
        display.show(Image.HAPPY)
    else:
        display.show(Image.SAD) 

Die Funktion is_gesture() gibt True zurück, wenn der Beschleunigungssensor die in den Klammern angegebene Geste erkannt hat. Sonst gibt sie False zurück.

Hier findest du eine Übersicht über Funktionen des Beschleunigungssensors.

Hier findest du eine Übersicht über alle Gesten und eine Anleitung dazu, wie du aus deinem Microbit einen Magic8Ball programmieren kannst.

6. Sounds

Auf der Rückseite des Microbits befindet sich in der Mitte der „Lautsprecher“ (engl. speaker). Dieser klingt nicht wirklich laut, aber dank folgender Software-Module kannst du viel damit machen:

Einzelne Töne abspielen

Folgender Code spielt einen Ton, wenn die Taste A gedrückt wird:

from microbit import *
import music
 
while True:
    if button_a.is_pressed():
        music.pitch(440, 100)

Melodien erstellen und abspielen

Das Modul music enthält schon ein paar vordefinierte Melodien. Folgender Code spielt eine solche Melodie ab:

from microbit import*
import music
 
music.play(music.PRELUDE)

Melodien selber erstellen

Du kannst auch selbst Melodien erstellen, wie in folgendem Code:

from microbit import*
import music
 
my_melody = ['g3:4', 'g3:4', 'b3:4', 'g3:4','c4:4', 'f4:4', 'c4:4', 'g3:8']
 
music.set_tempo(bpm = 180) # Standardeinstellung: 120 Schläge pro Minute (bpm)
music.set_tempo(ticks = 8) # Standardeinstellung: 4 Schläge pro Takt
 
music.play(my_melody)

Probiere das Programm aus und verändere die Melodie, du kannst auch Noten hinzufügen oder weglassen.

Melodien anders abspielen

Dir ist vielleicht aufgefallen, dass manche Melodien nicht so klingen, wie sie sollten. Hier zum Beispiel die Melodie für das Lied Frère Jacques (Bruder Jakob):

# Frère Jacques:
melody_jacques = ['c4:4', 'd4:4', 'e4:4', 'c4:4','c4:4', 'd4:4', 'e4:4', 'c4:4',
                 'e4:4', 'f4:4', 'g4:8', 'e4:4', 'f4:4', 'g4:8',
                 'g4:2', 'a4:2', 'g4:2', 'f4:2', 'e4:4', 'c4:4',
                 'g4:2', 'a4:2', 'g4:2', 'f4:2', 'e4:4', 'c4:4',
                 'c4:4', 'g3:4', 'c4:8', 'c4:4', 'g3:4', 'c4:8']

Wenn die Funktion play() Melodien abspielt, werden die einzelnen Töne unmittelbar aufeinanderfolgend, also ohne Zwischenpause wiedergegeben. Falls zwei gleiche Töne aufeinanderfolgen (wie in Frère Jacques beispielsweise der 4. und 5. Ton), klingt das wie ein einziger, langer Ton. Hierzu gibt es mindestens zwei Lösungen:

  1. Mit der Note r (für engl. rest) können Pausen in die Melodie eingebaut werden: 'r:2' macht eine Pause von zwei Schlägen. Dann sollten aber auch die anderen Ton-Dauern angepasst werden.
  2. Da die Melodie als Liste vorliegt, kannst du sie Ton für Ton abspielen und jeweils dazwischen eine ganz kurze Pause einbauen. Zum Beispiel so:
note = 0
while True:
    music.play(melody_jacques[note])
    sleep(30)
    if(note < len(melody_jacques)-1):
        note += 1
    else:
        note = 0

Mikrophon

Mit microphone.sound_level() kann die Umgebungslautstärke abgefragt werden. Der Rückgabewert ist zwischen 0 (Stille) und 255 (sehr laut).

7. Radio

Grundlagen

Auf der Rückseite des Microbits siehst du oben links eine goldene Leitung, die rechtwinklig hin- und her verläuft (mäanderförmig).

BLE-Antenne auf dem Microbit

Das ist die Antenne, mit der dein Microbit elektromagnetische Wellen (Funkwellen, engl.: radio waves) senden und empfangen und damit über BLE kommunizieren kann. BLE steht für Bluetooth Low Energy, eine Weiterentwicklung der Bluetooth-Funktechnologie, die dein Smartphone nutzt, um sich beispielsweise mit kabellosen Kopfhörern zu verbinden. BLE braucht weniger Energie als Bluetooth, weil es weniger Daten sendet. BLE ist daher nicht geeignet, Musik zu senden – das wären zu viele Daten. Für kleine Textnachrichten aber eignet sich BLE sehr gut.

Hier ein einfaches Programm, das einen Text senden, empfangen und anzeigen kann:

from microbit import *
import radio
 
while True:
    message_in = radio.receive()
    if message_in:
        display.scroll(message_in)
 
    if button_a.is_pressed():
        radio.send("Ciao!")

Versenden von Bildern

Mit radio.send() und radio.receive() können nur Strings versendet und empfangen werden. Aus diesem Grund kann man also Bilder nicht direkt versenden. Zum Beispiel resultiert radio.send(Image.HAPPY) in einem Fehler. Es gibt aber einen Workaround:

  1. Konvertiere das Bild in einen String: s = repr(Image.HAPPY)
  2. Entferne alles, so dass String noch die Form s = '00000:00000:00900:00000:00000' hat.
  3. Versende diesen String.
  4. Wandle den empfangenen String mit Image() wieder in ein Bild um und zeige es an.

Signalstärke & Distanzmessungen

Bisher haben wir mit message_in = radio.receive() Nachrichten empfangen. Es gibt aber noch eine zweite Möglichkeit: Mit radio.receive_full() empfängt man nicht nur die eigentliche Textnachricht, sondern auch die Signalstärke. Diese ist auch ein Indiz für die Distanz zwischen zwei micro:bits.

Das folgende Code-Beispiel zeigt, wie man die Textnachricht und die Signalstärke aus der erhaltenen Nachricht herauslesen kann:

message_full = radio.receive_full()
if message_full:
    text = message_full[0]
    signal_strength = message_full[1]

Die Signalstärke (hier signal_strength) ist einfach eine Zahl, die grösser wird, je stärker das Signal ist. Tipp: Printe die Signalstärke in die Konsole (print(signal_strength) und Show serial klicken, um Konsole anzuzeigen) und verfolge, wie sich der Wert verändert, wenn die Distanz verändert wird.

Erstes Programm

Probiert das Programm zu zweit aus:

8. Maqueen

In diesem Kapitel lernst du die wichtigsten Funktionen kennen, um mit dem Microbit das Fahrwerk Maqueen zu steuern. Du kannst den Microbit einfach in den dafür vorgesehenen Steckplatz stecken. Sobald du den Schalter hinten am Fahrwerk auf On stellst, wird der Microbit mit Strom versorgt.

8.1 Ultraschall-Sensor

Wenn du den Ultraschall-Sensor auf das Maqueen-Fahrwerk steckst, sieht es so aus, als hätte der kleine Roboter nun zwei Augen:

Technisch gesehen sind die beiden Zylinder aber keine Augen: Der linke Zylinder ist eher ein Mund und der rechte ein Ohr. Mit dem linken werden Schallwellen ausgesendet (engl.: transmitted, deshalb steht da ein T); es wird quasi gerufen. Mit dem rechten Zylinder wird dann gehört; es werden also Schallwellen empfangen (engl.: received, deshalb steht da ein R).

Du kennst das vom Echo-Effekt: Du rufst ganz laut „Haaallooo!“ gegen eine Felswand und nach kurzer Zeit hörst du das Echo deines Rufs. Je weiter die Felswand entfernt ist, desto länger dauert es, bis du das Echo hörst, denn der Schall braucht seine Zeit, um von deinem Mund zur Felswand und zurück zu deinem Ohr zu gelangen.

Das heisst: Du könntest die Zeit zwischen Ruf und Echo stoppen und so auf die Distanz zwischen dir und der Felswand schliessen. Genau so funktioniert der Ultraschallsensor:

Auf der linken Seite des obigen Bildes siehst du, wie der Microbit mit dem Ultraschall-Sensor verbunden ist: Pin 1 ist mit dem Signal „Trigger“ verbunden, Pin 2 mit dem Signal „Echo“.

Auf der rechten Seite siehst du, wie es funktioniert: Wenn wir einen kurzen Impuls (10 µs = zehn Mikrosekunden) auf das Trigger-Signal geben, dann sendet der Ultraschall-Sensor Schallwellen in hoher, nicht hörbarer Frequenz (40 kHz) aus: Er „ruft“. Wenn der Ultraschall-Sensor nun das Echo seines Rufes „hört“, sendet er über das Echo-Signal einen Impuls zurück. Dieser Echo-Impuls ist so lange, wie es gedauert hat von „rufen“ bis „hören“.

Um nun eine Distanz zu messen, müssen wir nur ein kurzes Signal auf Pin 1 (Trigger) geben und dann an Pin 2 messen, wie lange das Echo-Signal dauert. Diese Zeit können wir in eine Distanz umrechnen: Der Schall legt ca. 340 Meter pro Sekunde zurück, das sind 0.034 cm pro Mikrosekunde. Angenommen, der Echo-Impuls ist 700 µs lang: Dann beträgt die Distanz 700 * 0.034 / 2 = 11.9 cm. Wir rechnen durch 2, weil der Schall die Distanz zweimal zurücklegt. Folgender Code misst die Distanz und zeigt sie auf dem Display an:

from microbit import *
import utime
import machine
 
while True:
    # Distanz messen
    pin1.write_digital(1)   # Pin 1 (Trigger) HIGH für...
    utime.sleep_us(10)      # ...10 µs...
    pin1.write_digital(0)   # ...und wieder LOW.
    echo_pulse = machine.time_pulse_us(pin2, 1) # Messe, wie lange der Echo-Impuls an Pin 2 dauert.
    distance = echo_pulse * 0.017 # Rechne Zeit in Distanz um.
 
    # Ausgabe
    display.scroll(distance) # Ausgabe auf LED-Matrix
    print(distance) # Ausgabe in Konsole
 
    sleep(500)

Für die Funktion sleep_us wird das Modul utime benötigt; für die Funktion time_pulse_in wird das Modul machine benötigt. Verstehst du jede Zeile des Codes? Dann probiere den Code aus und überprüfe, ob die angezeigten Distanzen stimmen.

8.2 Motor

Die beiden Räder des Maqueen-Fahrwerks werden von je einem Motor angetrieben. Die beiden Motoren werden über einen Motor Controller (MC) gesteuert:

Über zwei Leitungen (Pins zwischen „3V“ und „GND“) ist der Microbit mit dem Motor-Controller (MC) verbunden: Diese beiden Leitungen dienen der Kommunikation über die sogenannte I2C-Schnittstelle (I2C). Das ist eine serielle Schnittstelle, ähnlich wie USB. Auf diesem Weg sendet der Microbit dem Motor-Controller (MC) Befehle. Damit das funktioniert, muss der Microbit erst nach dem Motor-Controller suchen. Folgender Code ist dazu nötig:

I2C_ADDR = 16 # Adresse des Motor-Controllers
while I2C_ADDR not in i2c.scan():
    display.show(Image.SAD)
display.show(Image.HAPPY)

Obiger Code ruft die Funktion i2c.scan() so lange auf, bis der Motor-Controller an seiner Adresse gefunden wurde. Während der Suche wird ein Sad Face angezeigt; nach (erfolgreicher) Suche wird ein Happy Face angezeigt.

Jetzt können über I2C einzelne Nachrichten an die Adresse des Motor-Controllers gesendet werden. In diesen Nachrichten können wir dem MC mitteilen:

Folgende Funktion sendet abgängig von den Parametern die richtigen Nachrichten:

def motor_run(motors=0, direction=0x00, speed=0):
    # direction: 0 = forward, 1 = backward
    # speed range: 0...255
    i2c_buf = bytearray([motors, direction, speed])
    if motors == 0:  # left motor
        i2c_buf[0] = 0x00
        i2c.write(I2C_ADDR, i2c_buf)
    if motors == 1:  # right motor
        i2c_buf[0] = 0x02
        i2c.write(I2C_ADDR, i2c_buf)
    if motors == 2:  # both motors
        i2c_buf[0] = 0x00
        i2c.write(I2C_ADDR, i2c_buf)
        i2c_buf[0] = 0x02
        i2c.write(I2C_ADDR, i2c_buf)

Du musst nicht jede Zeile dieser Funktion verstehen. Wichtig ist, dass du sie richtig verwendest: Hier eine kurze Übersicht:

Wenn du zum Beispiel willst, dass der linke Motor mit der Geschwindigkeit 200 rückwärts dreht, dann rufst du die Funktion wie folgt auf:

motor_run(0, 1, 200)

8.3 Maqueen Interface

Für die einfachere Steuerung des Maqueen inklusive seiner Sensoren und Aktoren gibt es eine Schnittstelle auf Github.

Installation

Zur Installation des Codes lädst du maqueen.hex herunter und auf den Microbit drauf (an einfachsten: Datei auf die Seite ziehen).

Beachte: Du benötigst jeweils zwei Dateien in deinem Projekt: maqueen.py ist die Bibliothek für die Motorensteuerung, diese musst du weder lesen noch verstehen. In der Hauptdatei main.py ist dein eigener Code, der die Motorensteuerung benützt (mit from maqueen import *).

Distanzmesser

Der Ultraschallsensor gibt die ungefähre Distanz bis zum nächsten Objekt wieder. Lies hier mehr dazu. Wenn du die distance() Funktion einbaust, kannst du vermeiden, dass der Roboter in Gegenstände fährt…

from maqueen import *
from microbit import *
 
robot = Maqueen()
front_sensor = robot.front_sensor
 
dist = 10
while True:
    newdist = int(front_sensor.distance() / 10)
    if newdist != dist:
        dist = newdist
        # Show distance in decimeters
        print(str(dist) + "0cm") # print on console
        display.show(dist)       # ... and on display
    sleep(100)

Motorsteuerung

Es gibt zwei Arten, wie die Motoren des Maqueen gesteuert werden können. Am einfachsten ist der Driver - die Funktionen drive, left, right setzen den Roboter in Bewegung und blockieren die Ausführung, bis die Bewegung zu Ende ist. Achtung: die Angaben zu Distanz (in cm) und Winkel (in Grad) werden je nach Batterieladestand nicht immer genau eingehalten!

from microbit import *
from maqueen import *
 
robot = Maqueen()
 
# High-level Driver interface:
# Fahre 20cm geradeaus, dann ein paar Kurven.
# Der Roboter stoppt automatisch nach jeder Funktion.
driver = robot.driver
driver.drive(20)
driver.left(90)
driver.right(180)
driver.left(90)

Die Driver-Steuerung ist ähnlich wie die Turtle-Bewegungen aus Programmieren 1, aber eignet sich weniger gut, um auf Ereignisse zu reagieren. Zum Beispiel möchten wir stoppen, wenn der Ultraschall-Sensor ein Hindernis detektiert.

Mit der Chassis-Schnittstelle kannst du den Roboter bis auf Weiteres in Bewegung setzen und erst auf ein bestimmtes Ereignis hin wieder stoppen:

from microbit import *
from maqueen import *
 
robot = Maqueen()
# Die Chassis-Funktionen setzen den Roboter in Bewegung 
# und kommen dann sofort zurück. Vergiss nicht, den Roboter
# wieder zu stoppen! Dazu könntest du den Ultraschall-
# Sensor benützen:
chassis = robot.chassis
 
chassis.forward(speed=100) # Vorwärts fahren mit Geschwindigkeit 100
sleep(1000)     # 1s weiterfahren
chassis.stop() # Stoppen
chassis.left() # Links drehen bis auf weiteres
sleep(500)     # Wenden während einer halben Sekunde
chassis.stop() # Wieder anhalten

Front LEDs

Schalte die zwei roten LEDs auf der Vorderseite ein und aus mit der front_lights.set_lights Funktion:

from microbit import *
from maqueen import *
 
robot = Maqueen()
 
# Licht an
robot.front_lights.set_lights(1,1)
 
# Licht aus
robot.front_lights.set_lights(0,0)

Bottom LEDS

Du kannst die Farb-LEDs auf der Unterseite des Maqueens wie folgt kontrollieren. Die LEDs können im RGB-Farbraum (rot-grün-blau) programmiert werden. Jede Farbe kann Werte von 0 (aus) bis 255 annehmen.

from maqueen import *
 
robot = Maqueen()
leds = robot.bottom_leds
 
# Setze das erste LED auf rot:
leds[0] = (255,0,0)
# Setze das zweite LED auf weiss (= alle Farben auf voll):
leds[1] = (255,255,255)
# Die LEDs werden erst geändert, wenn show() aufgerufen wird:
leds.show()
 
# Alle 4 LEDS in magenta:
leds.fill((255,0,255))
leds.show()
 
# Licht aus!
leds.clear()

Boden-Farbe erkennen

Die read() Funktion gibt die Helligkeit des Bodens wieder. Beide Werte sind entweder 0 (= Boden ist schwarz oder kein Boden sichtbar) oder 1 (= Boden ist weiss / hell). Auf der Oberseite zeigen zwei kleine blaue LEDs den Zustand der Sensoren an. Kannst du den Roboter einer Linie folgen lassen, wenn du diesen Code einbaust?

from microbit import *
from maqueen import *
 
robot = Maqueen()
floor_sensor = robot.floor_sensor
 
left, right = floor_sensor.read() # left/right nehmen Wert 0 oder 1 an