Anwendungen von Bluetooth

läuft auf Pico W
unter MicroPython !!



Bildbox 1 (klick hier)

Hardware / Software

- Raspberry Pi Pico W
- Relaismodul (sb-Components)
- Relaismodul- 4 Relais (sb-Components)
- Micro USB Kabel
- Firmware MicroPython
- Thonny IDE

Der Pico W hat ja von Hause aus einen Funk-Chip mit WLAN-Unterstützung und Bluetooth. Nur leider ist Bluetooth nicht standardmäßig aktiviert und die Fangemeinde musste lange warten, bis die Firmware dafür angepasst wurde. Zum Zeitpunkt, als ich das hier geschrieben habe, gab es die Bluetooth-Unterstützung außer für C/C++ nur für MicroPython und nicht CircuitPython, welches ich sonst bevorzuge. Also habe ich mich entschlossen, einige Experimente mit der aktuellen MicroPython Firmware zu unternehmen.

Bei meiner Suche im Internet, sind mir viele Beiträge wie etwa: "Pico W - jetzt mit Bluetooth" begegnet. Inhaltlich enthielten sie aber
1. Außer ein paar grundlegenden Erläuterungen kaum etwas für den Bastler Verwertbares.
2. Wenn, dann stand das Ein- und Ausschalten der Onboard-LED meist im Mittelpunkt.
Warum wurde z.B. nicht versucht ein oder mehrere elektrische Geräte (Netzspannung) über ein Relais zu schalten? Die Antwort findet man bei Amazon. Funksteckdosen mit WLAN und/oder Bluetooth gibt es dort ab ca. 10 Euro, so dass der Aufwand an Gehirnschmalz kaum lohnt. Eine andere Anwendung ist dann immer noch das allseits bekannte Roboter-Car. Aber auch dafür bietet Amazon die kompletten Bausätze an. Richtig "basteln" ist also nicht mehr so einfach.

Trotzdem habe ich mich entschlossen, über den Einstieg 'Onboard-LED', zur Steuerung eines Relais mit Hilfe des Smartphones und schließlich Steuerung des Relaismoduls mit vier Relais von sb-Components alles zum Nachmachen darzustellen.

Womit geht es los?

Zunächst geht es um den Download und die Einrichtung des Pico W mit der Firmware MicroPython. Laden Sie die neueste Version unter 'Firmware Releases' herunter und installieren sie wie folgt:

1. Nehmen Sie den Pico im ausgeschalteten Zustand zur Hand und drücken die BootSel-Taste (siehe Beschriftung auf der Platinenrückseite). Die wird gehalten und der Pico erst dann (!!) über das USB-Kabel mit dem Rechner verbunden. Nach dem Verbinden des USB-Kabels kann die Taste BootSel losgelassen werden. Nun öffnet sich im Filesystem ein Explorer-Fenster mit einem neuen Massenspeicher RPI-RP2.

2. Falls Ihr Pico W neu ist und noch nicht genutzt wurde, können Sie diesen Schritt überspringen. Wenn Sie den Pico W schon für andere Zwecke genutzt haben und in den Auslieferungszustand zurücksetzen wollen, sollten Sie vorher noch die uf2-Datei 'flash_nuke.uf2' übertragen. Den Download-Button dafür finden Sie auf der Adafruitseite (grüner Button am Seitenende) hier. Öffnen Sie auf dem Rechner den Download-Ordner und ziehen die flash_nuke.uf2 Datei auf den Massenspeicher RPI-RP2. Warten Sie, bis nach kurzer Zeit wieder der Massenspeicher RPI-RP2 im Explorer angezeigt wird. Der Speicher des Pico W ist jetzt vollständig gelöscht.

3. Öffnen Sie jetzt auf dem Rechner den Download-Ordner und ziehen die uf2-Datei mit der MicroPython Firmware auf den Massenspeicher RPI-RP2. Warten Sie bis die Firmware vollständig aufgespielt ist. Der Pico W wird ab jetzt nicht mehr im Dateiexplorer angezeigt und wir nutzen von nun an den Editor 'Thonny', um den Pico zu programmieren.

4. Falls bei der ersten Benutzung von Thonny kein Board erkannt wird, muss in den Interpreter-Einstellungen die Port-Auswahl überprüft werden und ggf. auf MicroPython (Raspberry Pi Pico W) eingestellt werden. Klicken Sie dazu ganz unten rechts auf den 'Button'. Das Fenster sollte jetzt z.B. so aussehen:

In der unteren Kommandozeile wird der Controler z.B. mit "MicroPython v1.22.2 on 2024-02-22; Raspberry Pi Pico W with RP2040" angezeigt.

Jetzt geht's mit Bluetooth los

Wir nutzen Bluetooth Low Energy, abgekürzt als BLE, eine Variante der drahtlosen Bluetooth-Technologie, die mit der Energieeinsparung als besonderem Merkmal entwickelt wurde. Im Verbindungsstatus kann ein Gerät eine von zwei Rollen einnehmen - die zentrale Rolle oder die periphere Rolle. Das Gerät, das eine Verbindung initiiert und vom Status "Initiating" in den Status "Connection" übergeht, übernimmt die zentrale Rolle. Das Gerät in der Peripherierolle befindet sich zunächst im Werbestatus. Wenn es eine Verbindungsanfrage vom Zentralgerät akzeptiert, wechselt es in den Verbindungsstatus. In dieser Anleitung richten wir den Raspberry Pi Pico W als Peripheriegerät ein und stellen später eine Punkt-zu-Punkt-Kommunikation mit einem Android-Gerät über Bluetooth Low Energy her.
Dazu brauchen wir die beiden Dateien 'ble_advertising.py' und 'ble_simple_peripheral.py' . Sie können sie aus den beiden Kästen unten kopieren oder von der Github Seite zu MicroPython Beispielen beziehen.

Zuerst der Quellcode von 'ble_advertising.py':
  
  

1  # Helpers for generating BLE advertising payloads.
2  from micropython import const
3  import struct
4  import bluetooth
5
6  # Advertising payloads are repeated packets of the following form:
7  #   1 byte data length (N + 1)
8  #   1 byte type (see constants below)
9  #   N bytes type-specific data
10
11 _ADV_TYPE_FLAGS = const(0x01)
12 _ADV_TYPE_NAME = const(0x09)
13 _ADV_TYPE_UUID16_COMPLETE = const(0x3)
14 _ADV_TYPE_UUID32_COMPLETE = const(0x5)
15 _ADV_TYPE_UUID128_COMPLETE = const(0x7)
16 _ADV_TYPE_UUID16_MORE = const(0x2)
17 _ADV_TYPE_UUID32_MORE = const(0x4)
18 _ADV_TYPE_UUID128_MORE = const(0x6)
19 _ADV_TYPE_APPEARANCE = const(0x19)
20
21 # Generate a payload to be passed to gap_advertise(adv_data=...).
22 def advertising_payload(limited_disc=False, br_edr=False, name=None, services=None, appearance=0):
23     payload = bytearray()
24
25     def _append(adv_type, value):
26         nonlocal payload
27         payload += struct.pack("BB", len(value) + 1, adv_type) + value
28
29     _append(
30         _ADV_TYPE_FLAGS,
31         struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)),
32     )
33
34     if name:
35         _append(_ADV_TYPE_NAME, name)
36
37     if services:
38         for uuid in services:
39             b = bytes(uuid)
40             if len(b) == 2:
41                 _append(_ADV_TYPE_UUID16_COMPLETE, b)
42             elif len(b) == 4:
43                 _append(_ADV_TYPE_UUID32_COMPLETE, b)
44             elif len(b) == 16:
45                 _append(_ADV_TYPE_UUID128_COMPLETE, b)
46
47     # See org.bluetooth.characteristic.gap.appearance.xml
48     if appearance:
49         _append(_ADV_TYPE_APPEARANCE, struct.pack("<h", appearance))
50
51     return payload
52
53 def decode_field(payload, adv_type):
54     i = 0
55     result = []
56     while i + 1 < len(payload):
57         if payload[i + 1] == adv_type:
58             result.append(payload[i + 2 : i + payload[i] + 1])
59         i += 1 + payload[i]
60     return result
61
62 def decode_name(payload):
63     n = decode_field(payload, _ADV_TYPE_NAME)
64     return str(n[0], "utf-8") if n else ""
65
66 def decode_services(payload):
67     services = []
68     for u in decode_field(payload, _ADV_TYPE_UUID16_COMPLETE):
69         services.append(bluetooth.UUID(struct.unpack("<h", u)[0]))
70     for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE):
71         services.append(bluetooth.UUID(struct.unpack("<d", u)[0]))
72     for u in decode_field(payload, _ADV_TYPE_UUID128_COMPLETE):
73         services.append(bluetooth.UUID(u))
74     return services
75
76 def demo():
77     payload = advertising_payload(
78         name="micropython",
79         services=[bluetooth.UUID(0x181A), bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")],
80     )
81     print(payload)
82     print(decode_name(payload))
83     print(decode_services(payload))
84
85 if __name__ == "__main__":
86     demo()
  

Und hier der Quellcode von 'ble_simple_peripheral.py':
  
  

1   # This example demonstrates a UART periperhal.
2   import bluetooth
3   import random
4   import struct
5   import time
6   from ble_advertising import advertising_payload
7
8   from micropython import const
9
10  _IRQ_CENTRAL_CONNECT = const(1)
11  _IRQ_CENTRAL_DISCONNECT = const(2)
12  _IRQ_GATTS_WRITE = const(3)
13
14  _FLAG_READ = const(0x0002)
15  _FLAG_WRITE_NO_RESPONSE = const(0x0004)
16  _FLAG_WRITE = const(0x0008)
17  _FLAG_NOTIFY = const(0x0010)
18
19  _UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
20  _UART_TX = (
21      bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
22      _FLAG_READ | _FLAG_NOTIFY,
23  )
24  _UART_RX = (
25      bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
26      _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE,
27  )
28  _UART_SERVICE = (
29      _UART_UUID,
30      (_UART_TX, _UART_RX),
31  )
32
33
34  class BLESimplePeripheral:
35      def __init__(self, ble, name="mpy-uart"):
36          self._ble = ble
37          self._ble.active(True)
38          self._ble.irq(self._irq)
39          ((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,))
40          self._connections = set()
41          self._write_callback = None
42          self._payload = advertising_payload(name=name, services=[_UART_UUID])
43          self._advertise()
44
45      def _irq(self, event, data):
46          # Track connections so we can send notifications.
47          if event == _IRQ_CENTRAL_CONNECT:
48              conn_handle, _, _ = data
49              print("New connection", conn_handle)
50              self._connections.add(conn_handle)
51          elif event == _IRQ_CENTRAL_DISCONNECT:
52              conn_handle, _, _ = data
53              print("Disconnected", conn_handle)
54              self._connections.remove(conn_handle)
55              # Start advertising again to allow a new connection.
56              self._advertise()
57          elif event == _IRQ_GATTS_WRITE:
58              conn_handle, value_handle = data
59              value = self._ble.gatts_read(value_handle)
60              if value_handle == self._handle_rx and self._write_callback:
61                  self._write_callback(value)
62
63      def send(self, data):
64          for conn_handle in self._connections:
65              self._ble.gatts_notify(conn_handle, self._handle_tx, data)
66
67      def is_connected(self):
68          return len(self._connections) > 0
69
70      def _advertise(self, interval_us=500000):
71          print("Starting advertising")
72          self._ble.gap_advertise(interval_us, adv_data=self._payload)
73
74      def on_write(self, callback):
75          self._write_callback = callback
76
77
78  def demo():
79      ble = bluetooth.BLE()
80      p = BLESimplePeripheral(ble)
81
82      def on_rx(v):
83          print("RX", v)
84
85      p.on_write(on_rx)
86
87      i = 0
88      while True:
89          if p.is_connected():
90              # Short burst of queued notifications.
91              for _ in range(3):
92                  data = str(i) + "_"
93                  print("TX", data)
94                  p.send(data)
95                  i += 1
96          time.sleep_ms(100)
97
98
99  if __name__ == "__main__":
100     demo()
  

Wenn Sie diese beiden Dateien auf ihrem Pico W gespeichert haben, können Sie mit dem Smartphone testen, ob Sie eine Bluetooth-Verbindung aufbauen können. Starten Sie auf dem Pico W die Datei ble_simple_peripheral.py. Dann gehen Sie auf dem Smartphone in "Einstellungen" -- "Verbindungen" -- "Bluetooth" und tippen auf "Scannen". Als verfügbares Gerät sollte "mpy-uart" angezeigt werden. Dafür wählen Sie koppeln aus. Wenn die Verbindung geklappt hat, werden in der Kommandozeile des Thonny-Explorers übertragene Pakete gedruckt, bis die Verbindung mit "Disconnected 64" beendet wird. Damit wissen Sie, dass die Sache grundsätzlich funktioniert.

Als nächstes steuern wir die Onboard-LED des Pico W, d.h. schalten diese 'ein' und 'aus'. Dazu schreiben wir das folgende kleine Programm. Kopieren Sie den Quellcode und speichern ihn z.B. unter 'onboard_led.py' auf dem Pico W.
  
  

1  from machine import Pin
2  import bluetooth
3  from ble_simple_peripheral import BLESimplePeripheral
4
5  led = Pin("LED", Pin.OUT)    # Create a Pin for the onboard LED as output
6  led.off()
7  led_state = 0
8
9  # Create a Bluetooth Low Energy (BLE) object
10 ble = bluetooth.BLE()
11 # Create an instance of the BLESimplePeripheral class with the BLE object
12 sp = BLESimplePeripheral(ble)
13
14 # Define a callback function to handle received data
15 def on_rx(data):
16     print("Data received: ", data)       # Print the received data
17     global led_state                     # Access the global variable led_state
18     if data == b'toggle\r\n':            # Check if the received data
19         led.value(not led_state)         # Toggle the LED state (on/off)
20         led_state = 1 - led_state        # Update the state
21
22 # Start an infinite loop
23 while True:
24     if sp.is_connected():  # Check if a BLE connection is established
25         sp.on_write(on_rx)  # Set the callback function for data reception
  

Hier ein paar Erläuterungen:
- In den Zeilen 1, 2 und 3 werden die notwendigen Module importiert.
- In Zeile 5 wird die LED definiert und in Zeile 6 ausgeschaltet (falls sie von einem vorhergehenden Durchgang noch leuchtet).
- In Zeile 7 wird ein Status festgelegt, der beim Ein- und Ausschalten zwischen '0' und '1' wechselt.
- Die Zeilen 9 bis 12 stellen die Bluetoothverbindung her.
- In den Zeilen 14 bis 20 ist die Funktion, welche die LED abwechselnd ein- und ausschaltet. Dieses 'Umschalten' nennt man 'toggle', wofür es in MicroPython auch den Befehl 'toggle' gibt.
- Schließlich bilden die Zeilen 22 bis 25 eine Endlosschleife.

Nun folgt der Teil, mit dem wir die LED (und später die Relais) schalten.
Installieren Sie auf Ihrem Smartphone aus dem Play Store die App: "Serial Bluetooth Terminal" und starten sie. Dann gehen Sie wie auf den Screenshots zu sehen vor.

    
    

Beim Pico W sollte jetzt bei jeder Betätigung des Button rechts unten die LED an- bzw. ausgeschaltet werden. Dafür sind Sie jetzt noch nicht bis an die Decke gesprungen. Deshalb folgt eine kleine Zugabe, die Ihnen sicher gefällt. Anstelle der einzelnen LED wird jetzt ein Neopixelring ein- und ausgeschaltet. Schauen Sie sich das zunächst im Video an.




Um den Neopixelring anzusprechen, habe ich das Programm 'onboard_led.py' etwas erweitert und unter dem Namen main.py gespeichert. Zweitens brauchen wir noch eine class Neopixel, welche die Funktionen zur Ansteuerung des Neopixelrings unter MicroPython enthält. Die befindet sich in der Datei neopixel.py. Beide Dateien können Sie hier kopieren und auf den gleichen Pico W übertragen, den wir bisher benutzt haben.
  
  

# Datei enthält den Quellcode von:
#
# neopixel.py
#
# Klicken Sie auf den Button 'Code kopieren' und setzen ihn im Thonny ein.
#
# Speichen Sie die Datei als 'neopixel.py'
  

  
  

# Datei enthält den Quellcode von:
#
# main.py
#
# Klicken Sie auf den Button 'Code kopieren' und setzen ihn im Thonny ein.
#
# Speichen Sie die Datei als 'main.py'
  

Die Datei 'main.py' startet, sobald der Pico W mit Spannung versorgt wird. Nutzen Sie die Smartphon-App genau wie beim Schalten der LED. Geben Sie in der Kommandozeile relay ein zum Ein - und Ausschalten. Im Video können Sie sehen, dass ich den 'Speicher-Button M1' nutze. Drücken Sie diesen so lange, bis sich ein Edit-Menue öffnet. Dort tragen Sie bei 'Value' den Befehl relay ein und bestätigen die Eingabe oben rechts mit dem Haken. Von nun an brauchen Sie den Befehl nicht mehr über die Tastatur eintragen. ( Warum den Befehl 'relay'? In Wahrheit hatte ich nicht die Datei 'onboard_led.py' erweitert, sondern eine Datei, in der ich das Reaismodul geschaltet habe. Aber dazu kommen wir ja noch.)

Bluetooth Steckdosenleiste

Bei den Suche nach einer sinnvollen Anwendung fiel mir eine 6-fach Steckdosenleiste mit Schalter in die Hände. Die Überlegung war, auf drei der Anschlüsse zu verzichten und an dem frei werdenden Platz alles nötige für die Blutooth-Steuerung unterzubringen.


Bildbox 2 (klick hier)

Zur Beruhigung evtl. DIN VDE 0100 Päpste: "Ich werde keine Anleitung zur Verkabelung geben, keine Fotos vom Innenleben zeigen und das Teil kommt auch nicht in den Verkauf!" Trotzdem ist alles so ausgeführt, dass man bei der Benutzung nicht mit der Netzspannung in Berührung kommen kann. Und jeder, der die erforderlichen elektrotechnischen Kenntnisse besitzt, kann den Aufbau eigenverantwortlich nachempfinden.
Was habe ich gemacht? Zunächst habe ich die von unten aufschraubbare Leiste so weit zerlegt, dass drei Steckdosen aus dem Gehäuse entfernt werden konnten. Die Funktion des Ein/Aus-Schalters ist unverändert geblieben. In dem frei gewordenen Bereich sind eine Anschlussleiste für die Kabel und ein 5V Mini-Netzteil für den Pico untergebracht. Das Relais des Pico-Moduls ist für 250V/10A ausgelegt. Der Pico W selbst befindet sich unter der Abdeckung und ist mithilfe der Pin Header mit dem Relaismodul verbunden.
Funktionsweise: Wenn der Schalter der Steckdosenleiste eingeschaltet wird, ist der Pico W mit der 5V Spannung versorgt und er versucht, die Bluetoothverbindung zum Smartphone aufzubauen. Dabei ist der Arbeitskontakt am Relais immer erst geöffnet, so dass an den Steckdosen keine Spannung anliegt. Über die Smartphone-App lässt sich diese mit 'relay_on' bzw. 'relay_off' schalten. Im unteren Kasten sehen Sie den Quellcode dafür. Speichern Sie die Datei als 'main.py' auf dem Pico W. Beachten Sie, dass sich auch weiterhin die beiden Dateien 'ble_advertising.py' und 'ble_simple_peripheral.py' auf dem Pico W befinden müssen.
  
  

1  from machine import Pin
2  import bluetooth
3  from ble_simple_peripheral import BLESimplePeripheral
4
5  led = Pin("LED", Pin.OUT)    # Create a Pin for the onboard LED as output
6  led.off()
7
8  relay = Pin(6, Pin.OUT)     #set pin GP18 for the Relay as output
9  relay(0)                    # Turn off Relay
10 relay_state = 0             # Initialize the relay state to 0 (off)
11
12 # Create a Bluetooth Low Energy (BLE) object
13 ble = bluetooth.BLE()
14 # Create an instance of the BLESimplePeripheral class with the BLE object
15 sp = BLESimplePeripheral(ble)
16
17 # Define a callback function to handle received data
18 def on_rx(data):
19     print("Data received: ", data)                     # Print the received data
20     global relay_state                                 # Access the global variable relay1_state
21     if data == b'relay_on\r\n' and relay_state == 0:   # Check if the received data is "lelay_on"
22         relay(1)                                       # Turn on Relay
23         relay_state = 1
24     if data == b'relay_off\r\n' and relay_state == 1:  # Check if the received data is "lelay_on"
25         relay(0)                                       # Turn off Relay
26         relay_state = 0
27
28 # Start an infinite loop
29 while True:
30     if sp.is_connected():  # Check if a BLE connection is established
31         sp.on_write(on_rx)  # Set the callback function for data reception
32
  

Schalten Sie jetzt die Steckdosenleiste ein und stellen Sie, wie bei den vorhergehenden Beispielen auch, eine Bluetoothverbindung über die App "Serial Bluetooth Terminal" her. Damit Sie nicht immer die Smartphone-Tastatur brauchen, nutzen Sie die beiden Speichertasten 'M1' und 'M2'. Geben Sie bei 'Value' entweder 'relay_on' bzw. 'relay_off' ein und bestätigen jeweils mit dem Haken. Danach können Sie mit 'M1' bzw. 'M2' ein- und ausschalten.


Viel Spass und Erfolg beim Ausprobieren. Wenn Ihnen diese Anleitung gefallen hat, tragen Sie hier bitte einen Kommentar ein.



Zurück