RFCOMM Verbindung Programmieren

  • Hallo zusammen,
    anlässlich eines Studium Projektes befasse ich mich neuerdings mit Java.
    Das geplante Projekt ist ein per Android App (Bluetooth) gesteuertes Modellauto mittels Raspberry Pi.


    Das Layout/Design und einige kleine Funktionen der App funktionieren bereits.
    Allerdings hänge ich seit Tagen an der Steuerung bzw. Verbindung des Raspberry Pis.
    Gedacht sei es eine RFCOMM Verbindung herzustellen (Raspberry = Server , Smartphone = Client) um Python Skripte am Pi über das Smartphone zu starten (Lenkung, Gas, Bremse).


    Nun nach etlicher Recherche im Internet bin ich auf ein Java Skript gestoßen welches eine Verbindung mit dem Pi herstellt und eine LED ein und ausschalten soll (mit jeweiligen Buttons)



    Das dazugehörige Python Skript auf dem Pi sieht folgendermaßen aus:



    Nun das ganze funktioniert soweit auch nur bedingt.
    Die Verbindung wird bei jedem einzelnen "Knopfdruck" getrennt und anschließend wartet er auf eine neue Verbindung.
    [Blockierte Grafik: https://1drv.ms/u/s!AtpcnnBEDRKmkxOBARjXY0rVEE2e]
    Meine zunächst erst Frage ist nun:
    Wie muss der Java Code geändert werden um nur eine einmalige RFCOMM Verbindung aufzubauen?
    Ich bitte um Hilfe ich kämpfe hier schon den ganzen Tag ohne eine Lösung.


    Mit freundlichen Grüßen

  • Hallo, erstmal Willkommen im Forum.



    Mit jedem Tasten klick startest du deinen „workerThread“ der Startet und beendet auch wieder die Verbindung.
    in der „sendBtMsg“ Methode erstellst du jedes Mal wieder ein neues Socket und auch einen neuen OutputStream und baust somit wieder eine Verbindung auf.



    In deinem WorkerThread baust du mit sendBTMsg eine Verbindung auf, sendest deine Daten. Wartest auf eine Antwort, in der while Schleife (Inputstream), nach erhalt einer Antwort wird die Verbindung in Zeile 136 beendet.
    Als Antwort reicht auch ein automatischer Respond vom Bluetooth-Stack denke das Python da was sendet auch wenn du selber nichst in deinen Python Programm sendest.
    Das wird wohl der Stack selber machen. OBEX ist doch Verbindung orientiert, wenn ich mich jetzt nicht täusche.



    Halte das Socket und die Streams solange wie du auch die Verbindung haben willst.



    Zitat

    Nun nach etlicher Recherche im Internet bin ich auf ein Java Skript gestoßen

    Bbringe bitte Java Skript und Java nicht durcheinander. Android ist Java, das sind zwei paar Schuhe.

  • Danke für die schnelle Antwort.


    Das klingt plausibel. Soweit hatte ich den Code auch verstanden. Kommentiere ich aber Zeile 136 aus ändert es nichts und die Verbindung wird trotzdem getrennt.

    Bbringe bitte Java Skript und Java nicht durcheinander. Android ist Java, das sind zwei paar Schuhe.

    Sorry da hatte ich mich falsch ausgerückt. Ich meinte nicht die Sprache javaskript sondern das Java Skript (Java Code).

  • Hi es reicht nicht nur die Zeile auszukommentieren.


    Du darfst nicht immer wieder eine neues Socket erstellen.
    das machst du auch mit auskommentierter zeile immer noch.


    Erstelle es in onCreate oder besser in der onResume und beim verlassen der App in onPause beendest du es wieder. Dazu musst du einiges in deinem Code ändern.
    In der onResume immer Prüfen ob es schon existiert.

  • Du hast doch für dein socket schon eine globale Klassen variable erstellt. Also reicht es doch wenn du beim App Start ein socket host und somit die Verbindung aufbaut. Beenden tust du sie beim verlassen der app oder activity.
    Somit bleibt die Verbindung über die gesamte Laufzeit erhalten. Du bachst somit nur noch in deinen outputstream schreiben.
    Dazu sind einige Änderungen. Notwendig die du bestimmt Schaft denn den Code scheinst du ja nun verstanden zu haben. Mache dir klar was passiert wen du auf einen Button klickst da rufst du jedes mal deine worker Methode auf und auch da wird jedesmal eine neue Verbindung aufgebaut. Das ist unnötig.


    Wozu hast du sonst globale Variablen.


    PS auch startest du jedesmal mit jeden Klick einen neuen thread ob der alte beendet wurde weißt du auch nicht. Denn du startest einen neuen thread und auch eine neue Verbindung socket.


    Bin im Moment nur am Handy und da ist mir coden echt zu mühsam. Aber du willst es doch sowieso selber schaffen.

  • So noch eine Frage bekommst du überhaupt im log die Meldung bytes available.?
    Wenn nicht wird nichts gesendet und der Thread wird wohl ewiglaufen. Setze ein log an das Ende der run Methode.
    Wird den auch deine Text View erneuert gesetzt. Ich denke dein thread läuft ewig.

  • Vielen Dank für die Tipps.


    Habe den Code nun einmal komplett bearbeitet:



    Hiermit habe ich einen Button "Connect" welcher mich mit meinem Pi verbindet und diese auch hält.


    Als nächstes werde ich mich etwas in InputStream und OutputStream einlesen müssen. Da ich momentan noch nicht weis was da genau passiert.
    Ich denke das beantwortet zunächst deine Fragen.

  • Hallo
    Leider nein das beantwort nicht meine Frage.
    Ich wollte wissen ob der Thread den du mit einem Tasten Klick startest auch beendet wird. Denn wenn dein Pi nichts sendet läuft der ewig den die While Schleife bekommt nur wenn etwas gesendet wird oder eine Exeption stattfindet eine Abbruch Bedingung . und wenn der Pi nichts sendet wird nie eines deiner „break“ aufgerufen und die Schleife und somit der Thread wird nie beendet . Deshalb habe ich nach dem Log gefragt was du drin hast in Zeile 103 hast. Wenn das durchlaufen wird und somit eine Meldung in der LogCat ausgibt wüstetest du das der Pi was gesendet und du ach etwas empfangen hast und somit die Möglichkeit für ein beenden der Thread gegeben ist.
    Da du in deinem Fall, gar keine Antwort auf einen einer deiner Befehle an dem PI, erwartest und auch nicht notwendig sind kannst du das Warten auf Antwort auch weglassen.


    Zu deinem Neuen Code.
    Die Verbindung bleibt zwar bestehen . Wie willst du darauf zugreifen oder sie beenden?
    Bendet wird sie nur wenn beim aufbau beim "Connect" etwas schief geht und eine Exeption geworfen wird . Da willst du sie beebden obwohl sie warschienlich garnicht erst zustande gekommen ist.



    Ich denke das du das Prinzip eines Thread noch nicht verstanden hast.
    Du kannst nicht auf dein Socket zugreifen. Da du es lokal in der Klasse Thread erstellt hast. Ein Thread wird erstellt und läuft einfach durch, ist er beendet kannst du nicht mehr auf variablen zugreifen die im Thread erstellt wurden, beim erneuten Aufruf des Threads wird eine neue Instanz erstellt und alles beginnt von vorne. Der Konstruktor wird wieder durchlaufen die Run wird gestartet… Auch dein Stocket wird neu erstellt.
    Das alte Stocket kannst du somit nicht mehr beenden.


    Benutze dafür eine globale Variable so wie dein BluetoothDevice in Zeile 3.
    Dies geht arber nur weil du eine Innerer Klasse hast und somit auf die Globalen variablen der Main zu greifen kannst.
    wenn du eine eigene Klasse in eigner Datei machst geht das nicht . dann must du das Soket dem Kostruktor mit übergeben.




    Um zu sehen Was wo wie wann passiert schaue dir mal Log und LogCat an .
    Füge dazu an dein entsprechenden stellen ein Log ein, und kannst verfolgen weher Code wann durchlaufen wird. Ist am anfing sehr hilfreich für das Verständnis.



    Habe mal deinen Ersten Code etwas geändert so sollte die Verbindung erhalten bleiben.


  • Du kannst nicht auf dein Socket zugreifen. Da du es lokal in der Klasse Thread erstellt hast.
    ….
    Benutze dafür eine globale Variable so wie dein BluetoothDevice in Zeile 3.

    Erledigt.


    Ich werde mich ausschlieslich nurnoch um den 2.ten Code kümmern da ich hier schritt für schritt verstehen möchte was geschieht.


    Die Verbindung wird getrennt sobald ich erneut auf den Connect Button klicke, ansich nicht falsch allerdings weis ich nicht woher dies geschieht. Die cancel Methode wird nie aufgerufen.


    ich habe folgende Methode in meinen onClickListener eingefügt um zu überprüfen ob verbunden ist (dann soll die Verbindung getrennt werden) oder ob getrennt ist (dann soll die Verbindung aufgebaut werden)


    Hier erstmal mein OnClickListener:


    Und hier die Methode enableDisableconnection():



    Hier habe ich mich nach den Log Meldungen orientiert.


    Beim ersten "klicken" wird die Log Meldung "Connecting" angezeigt.
    Beim zweiten "klicken" wird wieder die Log Meldung "Connecting" angezeigt allerdings wird die Verbindung dadurch unterbrochen. Das Versteh ich nicht eigentlich sollte er ConnectThread.cancel() starten und "Disconnect" in die Log schreiben.



    Ich wollte wissen ob der Thread den du mit einem Tasten Klick startest auch beendet wird.

    Soweit ich das richtig gelesen habe wird nach durchlauf der run() der Thread beendet solange es keine Endlosschleife beinhaltet. Somit muss ich den Thread nicht explizit beenden oder? Korrigier mich bitte wenn ich falsch liege.

  • Zitat

    Soweit ich das richtig gelesen habe wird nach durchlauf der run() der Thread beendet solange es keine Endlosschleife beinhaltet. Somit muss ich den Thread nicht explizit beenden oder? Korrigier mich bitte wenn ich falsch liege.


    Dies bezog sich auf dein ersten Code. Habe ja auch dann geschrieben zu deinem neuen Code.


    sollte eigentlich klar gewesen sein wie das gemeint war.


    Und In deinem alten erste Code hattest du eine Endlosschleife ob du es glaubst oder nicht. Da du dies nochmal aufgreifst, zeigt mir das du deinen ersten Cod nicht verstanden hast.


    Und ja wenn keine schleife vorhanden brauchst du ihn nicht extra beenden.
    Deshalb ist das auch mit der cancel() Methode etwas sinnlos. Bim neuen Code.



    So nun zu deinem aktuellen Problem:



    Erstmal hoffe ich das du in deiner ThreadKlasse nicht noch diese Zeile drin hast .
    private final BluetoothSocket mmSocket; wenn ja hast du zwei Variablen eine Lokale und eine Globale mit gleichen Namen.
    Auch solltest du im Kostruktor nicht wieder ein Socket erstellen.



    Zu deiner enableDisableconnection Methode:



    Es ist nicht verwunderlich das der else zweig nie durchlaufen wird.


    Mit jedem Tastenklick und somit Aufruf der Methode host du dir ein neues Socket.
    Danach erstellst du eine neue Instanz deiner Klasse und rufst somit den Kostruktor auf.


    Jetzst prüfst du ob das neue Socket verbunden ist. Ist es logischerweise nicht du hast es ja gerade eben erst erstellt.
    Was passiert? Dein if verzweigt in den true Block und connect wird ausgeführt. Dabei wird wohl die alte Verbindung beendet.
    So wie du das machst wirst du nie in den else Zweig kommen.
    Dein If ist immer True.




    Auch wird das beenden in dem Else Block so nicht gehen. Denn die Instanz vom vorhergehenden Thread ist beim zweiten Tasten klick nicht mehr gültig.


    Du brauchst eigentlich die cancel Methode in der Thread Klasse nicht. da dies sowie so im Main- Ui-Thread ausgeführt wird, kannst das auch gleich hier in der Methode machen. „mmSocket.close();“


    Eigentlich verstehe ich nicht den aufwand mit einer extra Klasse du willst doch nur das dein „connect in einem eigenen Thread gemacht wird . benutze doch dafür ein Runnable.


    Hier eine Variante one diese Klasse hat die gleiche Funktion und ist extrem Übersichtlicher.


  • C++ allerdings auch erst seit September.


    Danke für deine Code. Dieser Funktioniert aber auch nicht. OnClick = Verbunden. OnClick = Getrennt, OnClick = Log sagt "Connect" er verbindet aber nichtmehr.


    Wie gesagt bin Anfänger darum auch im Thread "Anfängerfragen". Ich hoffe es gibt keine "zu dummen Fragen". Bis hierhin hast du mir ja schon sehr viel geholfen dafür bin ich dir dankbar.

  • Sorry
    Die App stürzt ab weil es nicht so einfach geht aus dem Thread auf die UI zuzugreifen und einen Toast zu senden.
    Habe es eben bei mir selber getestet vorher nicht . Hatte den Code nur in einem Editor geschrieben ohne Test.
    Bei mir läuft er. Musst natürlich auch wieder die UUID und den Geräte Namen ändern.


    wenn die Verbindung beendet ist brauchst du ein neues Socket.
    Deshald setze ich mmSocket wieder auf null.




  • Vielen Dank. Funktioniert einwandfrei.


    Deutlich übersichtlicher. Auf anhieb den Code verstanden :D


    als nächstes würde ich Input und Output stream includieren.


    Vermute mal das es sich hierbei um senden und empfangen der daten handelt. Prinzipiell brauch ich nur einen outputstream da ich ja nur daten an den pi sende aber keine empfange. Ist das Richtig?


    Kann ich dies dann auch über eine andere Datei bzw. andere activity machen? Mein Connect button ist in meiner MainActivity.java. Die Steuerung erfolgt über eine andere activity.
    Oder wäre es vorerst einfacher beides in einer activity zu behalten?


    Gruß

  • Also vorerst würde ich sagen das alles in einer Activity zu machen.


    Schaue dir mal an was eine Activity eigentlich ist und was passiert, wenn du die Activity verlässt also eine neue Startest. Du kannst somit nicht mehr auf Daten (Socket) deiner ersten zugreifen, weil sie beendet wird und der GC bereinigt den Speicher.



    Wenn dann würde ich sagen du erstellst dir eine BT Klasse in der du alles macht Verbindung aufbauen, Daten senden …
    Am besten als Singleton Class dann kannst du dir in jeder Activity die gleiche Instanz der Klasse holen.



    Richtig ein Output stream ist zum Senden auf ein Gerät gedacht. Das gibt es in C++ auch.
    In Java benutzt man Streams zum schreiben von Daten auf Geräte oder Dateien. Die Nativen Methoden werden eher selten Benutz.


    Wie etwas sendest hast du in deinem ersten code schon drin oder in meiner korrigierten Version schau da mal nach.


    Wenn du das mit verschieden Activitys Bildschirm Seiten aufbauen willst würde ich aber auf der ersten Conectseite auch Prüfen ob BT eingeschaltet ist.
    Wenn nicht dem User die Möglichkeit geben es zu Avivieren. Und wenn noch nicht gepaart, auch das anbieten.
    Vielleicht auszuwählen welches gepaarte Gerät verwendet werden soll. (ListView der Gepaarten Geräte)


    So wie es jetzt ist wird wohl die App abstürzen, wenn BT ausgeschaltet ist.
    Das wäre nicht gerade User freundlich.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!