Datenübertragung über Bluetooth


Für die Kommunikation zwischen einem Raspberry Pi und einem Computer, Raspberry Pi und einem Smartphone oder zwischen zwei Raspberry Pi kann auch das Bluetooth Protokoll verwendet werden. Dies ist einfacher als mit TCP/IP, weil in diesem Fall kein Router notwendig ist.

Die neusten Version der RaspiBrick-Firmware (raspibrick.zip), die unter dem MenüPunkt Installation oder von hier heruntergeladen werden kann, unterstützt sowohl den eingebauten Bluetooth-Chip des Raspberry Modell 3 und des Raspberry ZeroW, wie auch die meisten USB Bluetooth-Dongles auf dem Raspberry Pi Modell 2. Das Bluetooth wird automatisch beim Start des Pi2Go-Roboters aktiviert. Der Computer oder Smartphone erkennt den Raspberry Pi mit dem Namen raspberrypi und kann sich ohne Authentifizierung paaren. Bei der Paarung wird auch die die MAC-Adresse ermittelt und zusammen mit dem Bluetooth-Namen gespeichert.

Wie bei TCP/IP erfolgt die Datenübertragung auch bei Bluetooth unter Verwendung der Client-/Server-Technologie. Zuerst muss das Serverprogramm, das in der Regel auf dem Raspberry Pi läuft, gestartet werden und erst dann kann der Client eine Verbindung aufbauen.


Beispiel 1: Fernsteuerung mit Computer
Bluetooth-Server wird auf dem Pi2Go gestartet. Der Bluetooth-Client, der auf dem Computerläuft, erstellt zuerts eine Verbindung zum Server unter der Verwendung des Bluetooth-Name oder der Mac-Adresse des Raspberry Pi. Ist die Verbindung hergestellt, kann der Client die Befehle für vorwärts, rückwärts, links, rechts und stop zum Pi2Go senden und ihn dadurch fernsteuern.
 

Das Server-Programm BtRobot.py wird auf den Pi2Go heruntergeladen und dort ausgeführt:

# BtRobot.py

from raspibrick import *
from btpycom import *

def onStateChanged(state, msg):
    if state == "LISTENING":
        display.showText("LI")
    elif state == "CONNECTED":
        display.showText("conn")
    elif state == "MESSAGE":
        go(msg)
        display.showText("go")
        
def go(msg):
    if msg == "FORWARD":
        gear.forward()
    elif msg == "BACKWARD":
        gear.backward()
    elif msg == "RIGHT":
        gear.rightArc(0.1)
    elif msg == "LEFT":
        gear.leftArc(0.1)
    elif msg == "STOP":
        gear.stop()

robot = Robot()
gear = Gear()
display = Display()
serviceName = "BTRobot"
server = BTServer(serviceName, stateChanged = onStateChanged)
while not isEscapeHit():
    Tools.delay(100)
server.terminate()
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

Erklärungen zum Programmcode:
onStateChanged(): Im Callback onStateChanged() wird beim Empfang eines Befehls die entsprechende Funktion aufgerufen.
display.showText("LI"): Falls der Pi2Go über ein Display verfügt, können kurze Meldungen (4 Zeichen) angezeigt werden.
Noch viel besser ist es ein OLED 1306 anzuschliessen, mit dem mehrere Textzeilen angezeigt werden können. Das gleiche Programm mit OLED-Display finden Sie hier.

Die Fernsteuerung erfolgt mit dem Client-Programm BtRobotControl.py, welches auf dem Computerläuft. Die Befehle werden mit Klick auf Buttons in einem Dialogfenster gesendet. Es empfiehlt sich, das Client-Programm in einem zweiten TigerJython-Fenster auszuführen, damit man eventuelle Rückmeldungen beim Programmdownload auf den Pi2Go-Roboter im ersten TigeJython-Fenster lesen kann.

.

# BtRobotControl.py

from entrydialog import *
from btcom import *

def showDialog():
    global dlg, buttons
    buttons = [ButtonEntry("Left"), ButtonEntry("Forward"), ButtonEntry("Stop"),
               ButtonEntry("Backward"), ButtonEntry("Right")]
    pane = EntryPane(buttons[0], buttons[1], buttons[2], buttons[3], buttons[4])
    dlg = EntryDialog(pane)
    dlg.setTitle("Remote Control")
    dlg.show()

showDialog()
commands = ["LEFT", "FORWARD", "STOP", "BACKWARD", "RIGHT"]

def onStateChanged(state, msg):
    pass
       
serviceName = "BTRobot"
client = BTClient(stateChanged = onStateChanged)
dlg.setTitle("Searching for rover. Please wait...")
serverInfo = client.findService(serviceName, 30)
#serverInfo = ('B8:27:EB:B3:E7:A6', 1)
if serverInfo == None:
    dlg.setTitle("Robot not found")
else:
    if client.connect(serverInfo, 20):
        dlg.setTitle("Connected to " + serverInfo[0])
        while not dlg.isDisposed():
            for n in range(len(buttons)):
                if buttons[n].isTouched():
                    client.sendMessage(commands[n])
        client.disconnect()  
Programmcode markieren (Ctrl+C kopieren)

Erklärungen zum Programmcode:

serverInfo = client.findService(): Im Callback onStateChanged() wird beim Empfang eines Befehls die entsprechende Funktion aufgerufen.
Nachdem die Bluetooth-Verbindung aufgebaut wurde, wird die Bluetooth-Mac-Adresse in der Titelzeile des Dialogs angezeigt und man kann sie für die nächste Programmausführung im Programm festhalten. Anstelle von
serverInfo = client.findService(serviceName, 30)
schreibt man für unseren Roboter:
serverInfo = ("B8:27:EB:05:5A:F8", 1)
und die Verbindung wird schneller erstellt.


Beispiel 2: Fernsteuerung mit einer Android App
Auf dem Pi2Go läuft das gleiche Programm BtRobot.py wie im ersten Beispiel. Anstelle der Fernsteuerung mit einem Programm, welches auf dem Computer läuft, wird der Pi2Go mit einem Smartphone gesteuert. Vor der Verwendung muss das Smartphone mit dem Raspberry Pi gepaart werden. Die Paarung erfolgt vom Smartphone aus. Ist das Bluetooth aktiviert, wird der Pi2Go-Roboter als rasperrypi erkannt.

 


Die Android-App RobotControl.apk wurde dem Framework JDroidLib erstellt. Sie können die fertige App mit dem untenstehendem Link oder mit dem QR-Code herunterladen und auf ihrem Smartphone installieren. Die App kann mit unserem Online-Compiler modifiziert und neu erstellt werden, sodass kein lokales Android-Entwicklungssystem installiert werden muss.


App herunterladen

Open source im Online-Compiler

Android App RobotControl.apk
mit QR-Code installieren

 

Programmcode für Androd App:

// RobotControl.java

package robotcontrol.app;

import ch.aplu.android.*;
import android.graphics.Color;
import android.bluetooth.*;
import ch.aplu.android.bluetooth.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;


public class RobotControl extends GameGrid
  implements GGPushButtonListener
{
  private GGPushButton forward;
  private GGPushButton backward;
  private GGPushButton left;
  private GGPushButton right;
  private GGPushButton stop;
  private GGTextField tf;
  private BluetoothClient bc;
  private DataInputStream dis;
  private DataOutputStream dos;

  public RobotControl()
  {
    super(21, 21, 0);
    setScreenOrientation(PORTRAIT);
  }

  public void main()
  {
    String serverName = askName();
    getBg().clear(Color.BLUE);
    new GGTextField("RobotControl",
      new Location(0, 1), true).show();
    addButtons();
    tf = new GGTextField("Trying to connect...", new Location(4, 19), true);
    tf.show();
      
    // Search device   
    BluetoothDevice serverDevice = searchDevice(serverName);
    if (serverDevice != null)
    {
      bc = new BluetoothClient(serverDevice);
      boolean rc = bc.connect();
      if (!rc)
      {
         tf.hide();
         tf = new GGTextField("Connection failed", new Location(4, 19), true);
         tf.show();
         return;
      }
      tf.hide();
      tf = new GGTextField("Connection established", new Location(3, 19), true);
      tf.show();
      dis = new DataInputStream(bc.getInputStream());
      dos = new DataOutputStream(bc.getOutputStream());
      enableButtons();
  }  
  }
  
  private String askName()
  {
    GGPreferences prefs = new GGPreferences(this);
    String oldName = prefs.retrieveString("BluetoothName");
    String newName = null;
    while (newName == null || newName.equals(""))
    {
      newName = GGInputDialog.show(this, "RobotControl", 
        "Enter Bluetooth Name",
        oldName == null ? "raspberrypi" : oldName);
    }
    prefs.storeString("BluetoothName", newName);
    return newName;
  }  

  private void addButtons()
  {
    forward = new GGPushButton("forward");   
    addActor(forward, new Location(10, 5));
    backward = new GGPushButton("backward");
    addActor(backward, new Location(10, 15));
    left = new GGPushButton("left");
    addActor(left, new Location(3, 10));
    right = new GGPushButton("right");
    addActor(right, new Location(17, 10));
    stop = new GGPushButton("stop");
    addActor(stop, new Location(10, 10));
  }

  private void enableButtons()
  {
    forward.addPushButtonListener(this);
    backward.addPushButtonListener(this);
    left.addPushButtonListener(this);
    right.addPushButtonListener(this);
    stop.addPushButtonListener(this);
  }

  public void onPause()
  {
    super.onPause();
  }
  
   public void buttonPressed(GGPushButton button)
  {
    if (button == forward)
      sendMessage("FORWARD");
    if (button == backward)
      sendMessage("BACKWARD");
    if (button == left)
      sendMessage("LEFT");
    if (button == right)
      sendMessage("RIGHT");
    if (button == stop)
      sendMessage("STOP");
  }

  public void buttonReleased(GGPushButton button)
  {
  }

  public void buttonClicked(GGPushButton button)
  {
  }
  
  public void buttonRepeated(GGPushButton button)
  {   
  }
  
  private void sendMessage(String msg)
  {
    try
    {
     dos.writeBytes(msg + "\0");
     dos.flush();
    }
    catch (IOException ex)
    {
      tf.hide();
      tf = new GGTextField("Connection lost", new Location(3, 19), true);
      tf.show();
    }
  }  

  private BluetoothDevice searchDevice(String btName)
  {
    BluetoothAdapter adapter =
      BluetoothDiscovery.getBluetoothAdapter(this);
    if (adapter == null)
      return null;

    if (!adapter.isEnabled())
    {
      requestBluetoothEnable();
      if (!adapter.isEnabled())
        return null;
    }
    BluetoothDiscovery bd = new BluetoothDiscovery(this);
    BluetoothDevice device = bd.getPairedDevice(btName);
    return device;
  }
}
Programmcode markieren (Ctrl+C kopieren)


Erklärungen zum Programmcode:

Weitere Information zur Entwicklung von Android-Apps mit dem Framework JDroidLib findet man unter www.jdroid.ch .