Raspberry Pi Pico W - Smart Robot Car mit dem Smartphone steuern - AZ-Delivery

Meine Begeisterung für den Raspberry Pi Pico W hält an. Schon mit dem Modell ohne WLAN konnte man eine ganze Menge Projekte umsetzen. Deshalb hier noch einmal die Verlinkung zu den bisherigen Beiträgen:

Im ersten Teil hatten wir die Installation von Thonny, die Einrichtung des Raspberry Pi Pico sowie erste Anwendungsprogramme zur Nutzung der Ein- und Ausgänge kennengelernt. Im zweiten Teil ging es um die Programmierung der bekannten Schnittstellen OneWire, UART und I2C, allesamt Zweitbelegungen der Pins. Und im dritten Teil hatten wir eine Anwendung für die neueste und schnellste Schnittstelle SPI oder Serial Peripheral Interface aka 4-wire bus vorgestellt. Bereits im Herbst 2021 hatte Jörn Weise eine Wetterstation mit dem Sensor BME280 und dem 0,96 Zoll OLED SSD1306 Display gezeigt.

In dem Beitrag vom 3. Januar 2023 hatte ich die Vorteile der WLAN-Schnittstelle genutzt und Temperatur und relative Luftfeuchtigkeit mit dem Web Server im heimischen Netz bereitgestellt. Darin hatte ich bereits angekündigt, dass ich damit auch eine Smartphone-Steuerung für meine Flotte von Robot Cars realisieren möchte. Auf geht‘s

Verwendete Hardware

1

Raspberry Pi Pico W oder WH

Beim Modell WH sind die Pinleisten bereits angelötet

1

Smart Car Kit

 

1

Robot Car Chassis plus Motoren/Räder

alternativ

1

Motor Controller L298N

alternativ

div.

Breadboard/Lochrasterplatine, Jumper-Kabel

1

beliebiges Smartphone mit Browser APP


Vorbereitung

Wer die Entwicklungsumgebung Thonny noch nicht installiert oder aktualisiert hat, hier noch einmal der Link:

Download von https://github.com/thonny/thonny/releases

Aktuell im Januar 2023: thonny-4.0.1.exe

Als Quellen für den Beitrag nutze ich vor allem die Veröffentlichungen der Raspberry Pi Foundation:

Raspberry Pi Pico W Datasheet und

Connecting to the Internet with Raspberry Pi Pico W

sowie das Buch von Paul Fuchs „HTML5 und CSS3 für Einsteiger“, mit dem mir der Einstieg in die Auszeichnungssprache HTML geglückt ist.

Nun endlich zur Smart Car Steuerung. In den vergangenen zwei Jahren hatte ich stets Fahrtstufen und einen einfachen Code für die Funkübertragung verwendet. Die Fahrtstufen haben sich bewährt, weil der Code nicht komplex sein soll und Spannungen unterhalb von 50% der Nennspannung der Motoren nur dazu führen, dass der Motor brummt, aber nicht dreht. Aktuell verwende ich einen vierstelligen Code, bei dem die ersten beiden Stellen für die Fahrt einschließlich Kurvenfahrt verwendet werden und die dritte und vierte Stelle für Zusatzfunktionen, die durch Taster ausgelöst werden können.

Beispiele für den Steuerungscode

Code 9500 für schnellste Geradeausfahrt, 5500 für Stillstand, 7700 für Vorwärtsfahrt nach rechts, Werte unter 5 stehen für Rückwärtsfahrt (1. Stelle) bzw. Linkskurven (2. Stelle)

y ↓ 0  x→

1

2

3

4

5

6

7

8

9

9

8

7

6

5

0

4

3

2

1


Mit der dritten und vierten Stelle kann man wie gesagt Zusatzfunktionen aktivieren, die durch bis zu 7 Taster ausgelöst werden können. Dazu wird je nach Taster der Wert der Zweierpotenz addiert. Achtung: Zählung der Taster beginnt bei null.

Taster

0

1

2

3

4

5

6

Wert

1

2

4

8

16

32

64


Beispiel

Bei diesem Projekt verwende ich nur zwei Taster/Buttons, also die dritte Stelle des Codes wird nicht benötigt. Vierte Stelle Wert=0 bedeutet kein Taster gedrückt, Wert=1 bedeutet Taster 0 (hier Button für blue light) gedrückt und Wert=3 bedeutet Taster 0 und 1 (blue light und Sirene) gedrückt.

Dieser vierstellige Code kann sowohl mit dem Joystick Shield bzw. Joystick Modul als auch mit einem Keypad-Modul (z.B. dem LCD Keypad Shield) benutzt werden. Im ersten Fall werden die Werte der Joystick-Potis auf den Bereich 1 bis 9 „ge-map-ped“, im zweiten Fall werden die Werte für die Fahrtstufen jeweils bei Tastendruck erhöht bzw. vermindert. In Anlehnung an das Keypad werde ich Buttons im Browser programmieren, um die Fahrtkommandos über WLAN einzugeben.

Verkabelung

Mikrocontroller, da macht der Raspberry Pi Pico W keine Ausnahme, liefern an den Pins „Signale“, aber kaum genügend Strom für richtige Verbraucher wie unsere Motoren. Deshalb benötigen wir sogenannte Motortreiber mit ICs wie den L293 oder L298. Ich habe mich aus mehreren Gründen für die kleine Platine aus dem Smart Car Kit mit dem Motor Controller L298N entschieden. Erstens benutze ich nur zwei Motoren, warum also für vier Motoren bezahlen? Zweitens ist der IC mit einem Kühlkörper versehen und drittens kann die Platine die Spannung für den Pico W liefern.

L298N beschriftet

Damit wir den Mikrocontroller an +5V und GND anschließen können, muss der JUMPER gesteckt bleiben. Hingegen werden die Kurzschlussbrücken an ENABLE A und ENABLE B entfernt. Alle sechs Steuerleitungen werden an Ausgangspins des Pico W angeschlossen. Dabei werden die ENABLE-Pins als PWM-Pins definiert, um darüber die Drehzahl der Motoren zu regeln.

Fritzing Schaltplan

Die Leitungen für Input 1 bis 4 werden an normale Pins angeschlossen und mit HIGH bzw. LOW beaufschlagt, um die Drehrichtung festzulegen. Die Enable-Pins werden als PWM deklariert. Falls ein Motor falsch dreht, kann man entweder den Motor umpolen oder die Pinnummer im Code tauschen.

Quellcode

 from machine import Pin, PWM
 
 m1e = PWM(Pin(11))
 m11 = Pin(13,Pin.OUT)
 m12 = Pin(12,Pin.OUT)
 m2e = PWM(Pin(20))
 m21 = Pin(19,Pin.OUT)
 m22 = Pin(18,Pin.OUT)

Die Funktion motor(c1, c2) habe ich aus früheren Programmen übernommen. Dabei sind die Parameter c1 und c2 die ersten beiden Stellen des Codes.

Anpassen musste ich den Wert für

 factor = 65535 * 6/vBatt   # max PWm * 6 / vBatt

weil die Methode duty_u16 für die PWM einen 16-Bit-Wert erwartet und die Versorgungsspannung heruntergeregelt werden muss auf die zulässigen 6V des Motors.

Der (für mich) schwierige Teil liegt beim HTML-Code, um nicht nur Daten ins WLAN zu übertragen, sondern auch Steuersignale ans Smart Car zu übermitteln.

Mit den Zeilen

 html = """<!DOCTYPE html><html>
 <head><meta name="viewport" content="width=device-width, initial-scale=1">
 <link rel="icon" href="data:,">
 <style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
 .buttonGreen { background-color: #4CAF50; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 .buttonRed { background-color: #D11D53; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 .buttonBlue { background-color: #1E90FF; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 .buttonLightRed { background-color: #FF4500; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 
 
 text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
 </style></head>
 <body><center><h1>Smart Car Control Panel</h1></center><br><br>
 <form><center>
 <center> <button class="buttonGreen" name="Up" value="1" type="submit">Up</button>
 <br><br>
 <center> <button class="buttonGreen" name="Left" value="2" type="submit">Left</button>
  <button class="buttonRed" name="Stop" value="0" type="submit">Stop</button>
  <button class="buttonGreen" name="Right" value="4" type="submit">Right</button>
 <br><br>
 <center> <button class="buttonGreen" name="Down" value="3" type="submit">Down</button>
 <br><br>
 <center> <button class="buttonBlue" name="BlueLightON" value="5" type="submit">Blue Light ON</button>
  <button class="buttonLightRed" name="OFF" value="6" type="submit">OFF</button>
 <br><br>
 <center> <button class="buttonBlue" name="SirenON" value="7" type="submit">Siren ON</button>
  <button class="buttonLightRed" name="OFF" value="8" type="submit">OFF</button>
 </form>
 <br><br>
 </body></html>
 """

erzeuge ich im Browser (entweder PC oder Smartphone) die benötigten Buttons für die Steuerung. Als Rückmeldung wird ganz unten der aktuelle Code angezeigt. Hier ein Screenshot von meinem Smartphone:

Screenshot Android Chrome

Was passiert, wenn ein Button angeklickt wird? Der jeweilige Steuerbefehl wird mit Schrägstrich / Fragezeichen ? an die IP-Adresse des Smart Cars angehängt. Da HTML nur eine Auszeichnungssprache, aber keine Programmiersprache ist, erfolgt die Auswertung Im (Micro)- Python-Programm.

Beispiele:

http://192.168.178.28/?Up=1

http://192.168.178.28/?Stop=0

Hier nun der Programmcode im Ganzen (Download). Dabei habe ich das Blinken der blauen LED und die Sirene noch nicht realisiert.

 # HTTP Server Example
 # Control a Robot Car and blue light/siren using a web browser
 
 
 import time
 import network
 import socket
 from machine import Pin, PWM
 
 
 # define 4 digits of code
 c1 = 5
 c2 = 5
 c3 = 0
 c4 = 0
 
 
 # Initialisation of blue LED
 led_blue = Pin(14, Pin.OUT)
 
 
 # Initialisation of motors
 vBatt = 9
 m1e = PWM(Pin(11))
 m11 = Pin(13,Pin.OUT)
 m12 = Pin(12,Pin.OUT)
 m2e = PWM(Pin(20))
 m21 = Pin(19,Pin.OUT)
 m22 = Pin(18,Pin.OUT)
 m1e.freq(1000)
 m2e.freq(1000)
 factor = 65535 * 6/vBatt   # max PWm * 6 / vBatt
 
 
 
 
 def motor(c1, c2):
     y = c1 - 5   # forward/backward
     x = c2 - 5   # left/right
     leftWheel = y + 0.5 * x
     rightWheel = y - 0.5 * x
     if leftWheel > 4:
         leftWheel = 4
     if leftWheel < -4:
         leftWheel = -4
     if rightWheel > 4:
         rightWheel = 4
     if rightWheel < -4:
         rightWheel = -4
     if leftWheel < 0:
         m11.off()
         m12.on()
     elif leftWheel > 0:
         m11.on()
         m12.off()
     else:
         m11.off()
         m12.off()
 #       m1e.duty_u16(0)
     if rightWheel < 0:
         m21.off()
         m22.on()
     elif rightWheel > 0:
         m21.on()
         m22.off()
     else:
         m21.off()
         m22.off()
 #       m2e.duty_u16(0)
     leftPWM = int(factor * (0.2 + 0.2*abs(leftWheel)))
     print("leftPWM = ", leftPWM)    
     rightPWM = int(factor * (0.2 + 0.2*abs(rightWheel)))
     print("rightPWM = ", rightPWM)    
     m1e.duty_u16(leftPWM)
     m2e.duty_u16(rightPWM)
 
 
 ssid = '……' # your Wifi SSID
 password = '……’ # your Wifi password
 
 
 wlan = network.WLAN(network.STA_IF)
 wlan.active(True)
 wlan.connect(ssid, password)
 
 
 html = """<!DOCTYPE html><html>
 <head><meta name="viewport" content="width=device-width, initial-scale=1">
 <link rel="icon" href="data:,">
 <style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
 .buttonGreen { background-color: #4CAF50; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 .buttonRed { background-color: #D11D53; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 .buttonBlue { background-color: #1E90FF; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 .buttonLightRed { background-color: #FF4500; border: 2px solid #000000;; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }
 
 
 text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
 </style></head>
 <body><center><h1>Smart Car Control Panel</h1></center><br><br>
 <form><center>
 <center> <button class="buttonGreen" name="Up" value="1" type="submit">Up</button>
 <br><br>
 <center> <button class="buttonGreen" name="Left" value="2" type="submit">Left</button>
  <button class="buttonRed" name="Stop" value="0" type="submit">Stop</button>
  <button class="buttonGreen" name="Right" value="4" type="submit">Right</button>
 <br><br>
 <center> <button class="buttonGreen" name="Down" value="3" type="submit">Down</button>
 <br><br>
 <center> <button class="buttonBlue" name="BlueLightON" value="5" type="submit">Blue Light ON</button>
  <button class="buttonLightRed" name="OFF" value="6" type="submit">OFF</button>
 <br><br>
 <center> <button class="buttonBlue" name="SirenON" value="7" type="submit">Siren ON</button>
  <button class="buttonLightRed" name="OFF" value="8" type="submit">OFF</button>
 </form>
 <br><br>
 </body></html>
 """
 
 
 # Wait for connect or fail
 max_wait = 10
 while max_wait > 0:
     if wlan.status() < 0 or wlan.status() >= 3:
         break
     max_wait -= 1
     print('waiting for connection...')
     time.sleep(1)
     
 # Handle connection error
 if wlan.status() != 3:
     raise RuntimeError('network connection failed')
 else:
     print('Connected')
     status = wlan.ifconfig()
     print( 'ip = ' + status[0] )
     
 # Open socket
 addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
 s = socket.socket()
 s.bind(addr)
 s.listen(1)
 print('listening on', addr)
 
 
 # Listen for connections, serve client
 while True:
     try:      
         cl, addr = s.accept()
         print('client connected from', addr)
         request = cl.recv(1024)
         print("request:")
 #       print(request)
         request = str(request)
         StopButton = request.find('Stop=0')
         if StopButton==8:
             print( 'StopButton is pressed')
             c1 = c2 = 5
         UpButton = request.find('Up=1')
         if UpButton==8:
             print( 'UpButton is pressed')
             if c1 < 9: c1 +=1
         LeftButton = request.find('Left=2')
         if LeftButton==8:
             print( 'LeftButton is pressed')
             if c2 > 1: c2 -=1
         RightButton = request.find('Right=4')
         if RightButton==8:
             print( 'RightButton is pressed')
             if c2 < 9: c2 +=1
         DownButton = request.find('Down=3')
         if DownButton==8:
             print( 'DownButton is pressed')
             if c1 > 1: c1 -=1
         BlueLightON = request.find('BlueLightON=5')
         if BlueLightON==8:
             print( 'Blue Light ON is pressed')
             c4 = 1
         BlueLightOFF = request.find('OFF=6')
         if BlueLightOFF==8:
             print( 'Blue Light OFF is pressed')
             c4 =0
         SirenON = request.find('SirenON=7')
         if SirenON==8:
             print( 'SirenON is pressed')
             c4 = 3
         SirenOFF = request.find('OFF=8')
         if SirenOFF==8:
             print( 'SirenOFF is pressed')
             c4 = 1                        
 
 
         if c4==1 or c4==3:
             led_blue.on()
         else:
             led_blue.off()
 
 
         code = 1000*c1 + 100*c2 + 10*c3 + c4
         print("Code = ",code)
         Code = str(code)
         
         response = html + Code
         cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
         cl.send(response)
         cl.close()
         motor(c1,c2)
         
     except OSError as e:
         cl.close()
         print('connection closed')

Hier das Bild meines Versuchsaufbaus. Ich habe mich für eine Lochrasterplatine mit Buchsenleisten für den Pico W entschieden. Der Motor Controller L298N liegt unterhalb der Platine. Das rote Kabel (rechts unten im Bild) kommt vom 5V-Anschluss des L298N und wird beim Fahrbetrieb mit VSYS verbunden.

Versuchsaufbau Robot Car

Der Programmcode ist sicher noch nicht perfekt, zeigt aber die aktiven Möglichkeiten mit HTML auf. Die blaue LED blinkt noch nicht, weil dies natürlich „non-blocking“, also ohne time.sleep(1), erfolgen soll und die Methode Timer() aus dem Modul machine bei jedem Aufruf dazu geführt hat, dass die LED schneller blinkt, so dass der Pico am Ende nichts anderes mehr gemacht hat. Die Funktion/Methode Timer.deinit() hat nicht zum Beenden des Blinkens geführt. Ob dies ein Bug ist oder mein Fehler, habe ich nicht abschließend klären können.

Dennoch können Sie den Code für die LED z.B. benutzen, um ein Relais für die Kaffeemaschine zu schalten. Also auch Hausautomation / IOT-Anwendungen sind möglich.

Während der Entwicklung habe ich das µPython-Programm am PC gestartet und am Ende auf dem Pico unter dem Namen main.py für den Autostart gespeichert. Bei Änderungen habe ich das Programm gestoppt und die Änderungen vorgenommen. Beim erneuten Start habe ich dann gelegentlich folgende Fehlermeldung in der Kommandozeile (shell) erhalten:

 Traceback (most recent call last):
  File "<stdin>", line 133, in <module>
 OSError: [Errno 98] EADDRINUSE

Dann hilft nur, den USB-Stecker zu ziehen und den Pico wieder neu zu verbinden. Denn der Taster auf dem Pico ist kein Reset-Button, sondern heißt BOOTSEL und wird nur gedrückt, wenn man zum Beispiel µPython installieren möchte.

Kritische Bewertung der Fahreigenschaften meines Robot Cars

In unserem Wohnzimmer sind zu viele Hindernisse, um das Smart Car mit dem Smartphone unfallfrei zu steuern. Es funktioniert grundsätzlich, aber meine Funkfernsteuerung mit Joystick Shield und 433 MHz-Transceiver HC-12 funktioniert besser. Wie oben erwähnt kann man den HTML-Teil des Programms leicht modifizieren und die Buttons auf dem Smartphone für das Schalten von LEDs oder Relais benutzen.

Projekte für anfängerRaspberry pi

Laat een reactie achter

Alle opmerkingen worden voor publicatie gecontroleerd door een moderator

Aanbevolen blogberichten

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery