Forum: PC-Programmierung (Android) Bluetooth Chat Example modifizieren


von Johnny E. (electricjohnny)


Lesenswert?

Hallo Forum,

ich habe mich bisher mit hardwarenaher Programmierung beschäftigt und 
will nun meine erste App umsetzen.

Zu diesem Zweck will ich ein Projekt auf Basis von BluetoothChat 
realisieren.
https://developer.android.com/samples/BluetoothChat/index.html

Konkret geht es um eine Mikrocontroller-Platine, die über ein 
BTM222-Bluetooth-Modul mit einem Smartphone/Tablet kommunizieren soll.

Über Sensoren auf der Platine werden Messwerte aufgenommen, und sollen 
auf dem Tablet dargestellt werden.

Die Anzeige soll allerdings nicht als einfacher Fließtext erfolgen, 
sondern die Messwerte sollen immer an einer bestimmten Stelle in der App 
angezeigt werden.

Der Datenstrom, der vom Mikrocontroller kommt, ist variabel und kann 
flexibel an die Anforerungen der App angepasst werden.
Die App müsste aus diesem Datenstrom nur die einzelnen Messwerte filtern 
und an der richtigen Stelle ausgeben.

Ich stelle mir das so vor:

Mikrocontroller sendet per Bluetooth: "T:27,1;RH:35;P:1013..."

Die App soll nun aus diesem Datenstrom erkennen: T ist die Temperatur, 
Ausgabe des Werts an der entsprechenden Stelle, RH ist die 
Luftfeuchtigkeit und so weiter.

Mit welcher Methode kann ich den ankommenden SPP-Datenstrom auf diese 
Art analysieren?

Vielen Dank,
Johnny

von Guddi (Gast)


Lesenswert?

Du benötigst in deinem C#-Programm einen Parser, der den Datenstrom 
analysiert und in einer entsprechenden Form ablegt bzw. aufbereitet.

von Johnny E. (electricjohnny)


Lesenswert?

danke für das Stichwort, allerdings schreibe ich die App in Java 
(Android Studio).

zum Stichwort "Android Parser" findet sich relativ schnell der Begriff 
XML-Parser.

Macht es sinn, den Datenstrom kommend vom µC XML-ähnlich zu formatieren?

Bsp: "<t>:27,1</t><rh>35</rh><p>1013</p>..."

ich will die Daten nicht speichern, sondern nur einmalig ausgeben.

Wie baue ich einen solchen simplen Parser am einfachsten auf?

Vielen Dank.

von Dieter (Gast)


Lesenswert?

siehe Kapitel 4 - Der Umgang mit Zeichenketten
http://openbook.rheinwerk-verlag.de/javainsel9/index.htm

von Tux (Gast)


Lesenswert?

Überleg dir halt wie du das im Kopf machen würdest ...

"T:27,1;RH:35;P:1013".split(;);
teilt den String bei den Strichpunkten. So erhältst du ein Array mit 
drei Einträgen:
T:27,1
RH:35
P:1013

Jeden dieser Einträge teilst du jetzt nochmal bei : und schon kannst du 
ganz einfach das erste Feld auswerten und entsprechend das zweite 
Anzeigen.

von Johnny E. (electricjohnny)


Lesenswert?

zunächst einmal Danke für die Ideen.

Zuvor steht allerdings noch ein grundsätzliches Problem:

Wenn ich zwei Smartphones mit der BluetoothChat-App verbinde, können 
unbegrenzt viele Zeichen in einer Nachricht übermittelt werden.

Bei einer Verbindung mit meinem Mikrocontroller-BT-Modul werden die 
Nachrichten unterbrochen.

Die Ausgabe sieht dann aus wie folgt:

"BTM222: T:2
BTM222: 7,2;R
BTM222: H:35;P:
BTM222: 1013 usw."

Ich kann nicht nachvollziehen, wie dieser unregelmäßige Umbruch 
entsteht.
Jemand einen Ansatz?

von uwe (Gast)


Lesenswert?

Ist doch egal, du entfernst alle "BTM222: ", die Umbrüche entfernst du 
auch, da die Umbrüche eh direkt vor dem "BTM222: " stehen machst du das 
in einem Schritt. Danach zerlegst du, wie schon geschrieben den string 
weiter.

von Till X. (till_n)


Lesenswert?

Schau dir mal JSON an. Das wäre genau das, was du suchst Außerdem gibt 
es dafür in Android schon einen Parser um die Strings dann zu 
dekodieren. Für den µC gibt es sicherlich auch ein Lib.

Dein andere Problem kommt mir bekannt vor. Ich habe selbst mal mit dem 
BluetoothChat rumexperimentiert.

von Johnny E. (electricjohnny)


Lesenswert?

Okay das Parsen gucke ich mir direkt an, wenn ich die Grundstruktur 
innerhalb des BluetoothChats etwas besser nachvollziehen kann.

Momentan knobel ich noch an den Umbrüchen, die erzeugt werden, wenn ich 
Daten des µCs empfange.
Mir ist noch nicht ganz klar, an welcher Stelle genau der Text 
zusammengesetzt wird, der letztendlich ausgegeben wird, also das: 
"BTM222: ...."

Den Namen des Kommunikationspartners, der eingeblendet wird, wird 
automatisch erkannt und voran gestellt.

Wenn ich das richtig sehe, werden die eingehenden Daten hier 
verarbeitet:
1
  private class ConnectedThread extends Thread {
2
        private final BluetoothSocket mmSocket;
3
        private final InputStream mmInStream;
4
        private final OutputStream mmOutStream;
5
 
6
        public ConnectedThread(BluetoothSocket socket, String socketType) {
7
            Log.d(TAG, "create ConnectedThread: " + socketType);
8
            mmSocket = socket;
9
            InputStream tmpIn = null;
10
            OutputStream tmpOut = null;
11
 
12
            // Get the BluetoothSocket input and output streams
13
            try {
14
                tmpIn = socket.getInputStream();
15
                tmpOut = socket.getOutputStream();
16
            } catch (IOException e) {
17
                Log.e(TAG, "temp sockets not created", e);
18
            }
19
 
20
            mmInStream = tmpIn;
21
            mmOutStream = tmpOut;
22
        }

Es macht wohl einen Unterschied, wenn statt

"private final OutputStream mmOutStream;"

"private final DataOutputStream mmOutStream;"

gesetzt wird.
Dieses Problem wird an anderer Stelle oft besprochen, nur der 
Unterschied ist mir noch nicht ganz klar.

von Till X. (till_n)


Lesenswert?

Das Lesen aus dem Stream findet aber in der "run()"-Methode hier statt:

// Read from the InputStream
bytes = mmInStream.read(buffer);

Ist der buffer vielleicht zu klein?

von Johnny E. (electricjohnny)


Lesenswert?

Die komplette Methode ist hier:
1
public void run() {
2
            Log.i(TAG, "BEGIN mConnectedThread");
3
            byte[] buffer = new byte[1024];
4
            int bytes;
5
6
            // Keep listening to the InputStream while connected
7
            while (true) {
8
                try {
9
                    // Read from the InputStream
10
                    bytes = mmInStream.read(buffer);
11
12
                    // Send the obtained bytes to the UI Activity
13
                    mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
14
                            .sendToTarget();
15
                } catch (IOException e) {
16
                    Log.e(TAG, "disconnected", e);
17
                    connectionLost();
18
                    // Start the service over to restart listening mode
19
                    BluetoothChatService.this.start();
20
                    break;
21
                }
22
            }
23
        }

Der Buffer ist ein Array aus 1024 "bytes".

ich hatte das probehalter schon deutlich größer gemacht, ohne 
erkennbaren Einfluss auf die Ausgabe.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Johnny E. schrieb:

> Der Buffer ist ein Array aus 1024 "bytes".
>
> ich hatte das probehalter schon deutlich größer gemacht, ohne
> erkennbaren Einfluss auf die Ausgabe.

Das ist ein generelles 'Problem'.

Du kannst bei jeglicher Verbindung an die du dich anklemmst 
normalerweise nie davon ausgehen, dass die Daten in genau derselben 
Strukturierung aus dem Kanal rauspurzeln, wie sie der Sender auf den Weg 
geschickt hat.

Der Sender sendet aus seiner Sicht eine komplette Zeile (die er zb mit 
einem \n abschliesst). Aber aus den Empfangsroutinen purzeln die 
Character eben nicht wieder als komplette Zeile raus. Allgemeine 
Empfangsroutinen wissen nichts von einer Zeilenstruktur in den Daten. 
Sie geben das weiter, was zum Zeitpunkt des Aufrufs der Funktion an 
Daten bereits empfangen wurde. Das heisst aber nicht, dass das zb eine 
komplette Zeile wäre. Du kannst ja die Empfangsfunktion ja auch schon 
aufgerufen haben noch ehe der Sender die komplette Zeile überhaupt 
weggeschickt hat.

D.h. du selbst bist dafür zuständig, die einzelnen Textfragmente wieder 
zu einer komplette Zeile zusammenzusetzen.

und das 'BTM222'. Na ja. Du hast doch den Code für die Routinen. Sieh 
halt nach, wer diesen Text in die zurückgegebenen Stringteile einfügt 
und wirf das raus.

: Bearbeitet durch User
von Johnny E. (electricjohnny)


Lesenswert?

Vielen Dank für die Anregung.

Ich bin nun schon einen Schritt weiter und habe mich für das 
Übertragungsprotokoll für ein JSON-artiges Format entschieden.

Dies soll in Java einfach zu parsen sein.

Die µC-Ausgabe sieht aus wie folgt:
1
{"T":"25.8","R":"33","E":"527","P":"1006.7","X":"0.05","Y":"0.00","Z":"1.00"}

Jetzt suche ich nach einer Möglichkeit, an die einzelnen Elemente zu 
gelangen um sie in einer eigenen Variablen für die Ausgabe abzulegen.

: Bearbeitet durch User
von Johnny E. (electricjohnny)


Lesenswert?

okay, das Problem ist gelöst.

Anderes Thema: ich will, dass sich die App nach dem Start automatisch 
mit genau meinem Modul verbindet.
Das Modul ist im System bekannt und bereits gepairt.

Die App muss diese Verbindung also nur aufgreifen.

Wie kann ich das realisieren?

von Johnny E. (electricjohnny)


Lesenswert?

edit:

ich habe zu diesem Thema folgenden Beitrag gefunden:

http://stackoverflow.com/a/17120498/4995080

nur ist mir nicht ganz klar, an welcher Stelle in dem Projekt dieser 
Code eingepflegt werden muss.

in die Klasse BluetoothChatService.java oder BluetoothChatFragment.java?

von Hans-Georg L. (h-g-l)


Lesenswert?

Hier ein paar Auszüge aus meinem Code den ich vor Jahren für einen 
Kunden geschrieben habe.

Hier zuerst einmal der Code wie man an ein bestimmtes Device gelangt

So findet man ein paired Device:
1
// get default Adapter
2
BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();
3
// get set of paired devices  
4
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
5
  
6
// process paired devices   
7
  if (pairedDevices.size() > 0) 
8
  {
9
      for (BluetoothDevice device : pairedDevices) 
10
      {
11
    // Name der Verbindung 
12
        // z.B. der Text, den du im BTM222 abspeichern kannst  
13
           String  mBTName = device.getName(); 
14
           
15
        // MAC Adresse der Verbindung
16
    String  mBtDeviceAddress = device.getAddress());
17
      }
18
  }
19
  
20
  // Mit der MAC Adresse device holen
21
  BluetoothDevice mBtDevice = mBtAdapter.getRemoteDevice(address);
22
   
23
// und nun kannst du es öffnen

Irgenwo im Bluetooth gewurschtel code findest du eine Zeile, die den BT 
Socket erzeugt:

tmp = mBtDevice.createInsecureRfcommSocketToServiceRecord(UUID);

Du musst jetzt nur noch mBtdevice im meinem Code durch deinen variablen 
Namen ersetzen.

weiter ..

Warum liest du immer den Kompletten Buffer ein und nicht nur soviel 
Zeichen wie du gerade zum parsen brauchst ..
InputStream kann das alles, aber da müsste man ja Doku lesen ...

Weil void run() keine "einfache" Methode sondern ein Thread ist, musst 
du deine Nachrichten an die GUI (Activity) mit Hilfe eines Handlers 
schicken.
Der Handler kann auch viele Formate und nicht nur buffer verschicken. 
Aber auch hier muss man die Doku lesen und sich nicht alles vorkauen 
lassen.

: Bearbeitet durch User
von Johnny E. (electricjohnny)


Lesenswert?

vielen Dank für deinen Beitrag.

Zunächst, das Problem mit dem BufferedReader und readLine ist, wie ich 
bereits geschrieben habe, gelöst.

Der ankommende Datenstrom wird Zeile für Zeile gelesen, gesplittet und 
entsprechend weiter verarbeitet.

Nun geht es um die Erweiterung/Automatisierung von Funktionen, deshalb 
meine Frage nach dem Autoconnect.

Im ursprünglichen BluetoothChat wird die Auswahl des BT-Partners in der 
Klasse DeviceListActivity vorgenommen.

Das Öffnen des BluetoothSockets geschieht in der Klasse 
BluetoothChatService.

Meinem Verständnis nach müsste das automatische Verbinden mit meinem 
BT-Modul im onCreate des BluetoothChatFragments geschehen, oder?

von Johnny E. (electricjohnny)


Lesenswert?

ich habe die Codezeilen in die Methode onResume der 
BluetoothChatFragment eingefügt.

Sie werden erfolgreich abgearbeitet, über Logcat kann ich mir die 
gepairten Geräte mit Namen und MAC-Adresse ausgeben lassen.

Die MAC Adresse meines Geräts habe ich folgendermaßen eingegeben:

1
String address = "XX:XX:XX:XX:XX:XX";
2
BluetoothDevice mBtDevice = mBtAdapter.getRemoteDevice(address);


jetzt muss ich das mBtDevice verwenden, um die Kommunikation aufzubauen.

Mir ist jetzt nicht ganz klar, wie ich das aus der Klasse 
BluetoothChatFragment heraus tue.

1
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,MY_UUID_SECURE);


wird ja ansonsten in der BluetoothChatService aufgerufen.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.