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
Du benötigst in deinem C#-Programm einen Parser, der den Datenstrom analysiert und in einer entsprechenden Form ablegt bzw. aufbereitet.
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.
siehe Kapitel 4 - Der Umgang mit Zeichenketten http://openbook.rheinwerk-verlag.de/javainsel9/index.htm
Ü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.
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?
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.
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.
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.
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?
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
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
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
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?
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?
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
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?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.