Hallo Zusammen,
vor einiger Zeit habe ich rxtx als Treiber für die serielle Schnittstell
in Java benutzt. Dieser scheint obsolet zu sein, statt dessen nimmt man
wohl JSSC.
Im Anhang ein kleines Beispiel für das ich einige Zeit "verbraten" habe.
Die Schwierigkeiten waren dabei folgende:
1. Wie kriege ich den Treiber in Eclipse
Lösung==>
http://stackoverflow.com/questions/20568602/how-to-add-java-jssc-library-to-eclipse
2. man hat unter Ubuntu 14.04 keine Rechte für die serielle
Schnittstelle
Lösung: sudo chmod 666 /dev/ttyACM0 ( bzw. statt ttyACM0 den Namen der
Schnittstelle )
viel Spaß damit.
Mich würde interessieren, ob es bei euch auch funktioniert ( z.b. auch
auf Windows )
Die Umwandlung der emfangenen Zeichen in Integer Werte stellt ein
größerers Problem dar, als ich ursprünglich dachte.
Es gibt scheinbar keine fertige Funktion, mit der eine ganze Zeile die
durch ein Carrige-Return abgeschlossen wird, eingelesen werden kann.
Hat jemand ein gute Idee, wie man elegant die empfangen Zeilen erkennen
und in Zahlen umwandeln kann?
Mir scheint es am einfachsten zu sein, die Schnittstelle auf einen
Inputstream zu hängen und dann mit einem BufferReader per readLine
auszulesen. Kennt jemand einen besseren Weg?
chris_ schrieb:> Es gibt scheinbar keine fertige Funktion, mit der eine ganze Zeile die> durch ein Carrige-Return abgeschlossen wird, eingelesen werden kann
Wozu auch? Eine Serielle Schnittstelle ist ein Strom von Bytes,
"Zeichen" sind deine Interpretation des ganzen.
chris_ schrieb:> Mir scheint es am einfachsten zu sein, die Schnittstelle auf einen> Inputstream zu hängen und dann mit einem BufferReader per readLine> auszulesen
Genau so tut man das.
chris_ schrieb:> Kennt jemand einen besseren Weg?
Was ist an diesem Weg den nicht "gut"?
Ansonsten solltest du deinen Beispielcode DRINGEND um Fehlerbehandlungen
erweitern...
chris_ schrieb:> Dieser scheint obsolet zu sein, statt dessen nimmt man wohl JSSC
Worraus schließt du das?
>> Mir scheint es am einfachsten zu sein, die Schnittstelle auf einen>> Inputstream zu hängen und dann mit einem BufferReader per readLine>> auszulesen>Genau so tut man das.
Wunderbar. Du bist ein Experte. Zeig mal bitte wie es geht. Nachdem ich
jetzt einige Stunden damit zugebracht habe, bin ich mir sicher, dass es
nicht leicht ist.
chris_ schrieb:> Nachdem ich jetzt einige Stunden damit zugebracht habe, bin ich mir> sicher, dass es nicht leicht ist.
Ist es aber, im übrigen hat es sich bewährt nicht die Leute dumm
anzumachen von denen man Hilfe erwartet!
Hier ein Beispiel welches das Prinzip verdeutlicht:
1
// This program is free software: you can redistribute it and/or modify
2
// it under the terms of the GNU General Public License as published by
3
// the Free Software Foundation, either version 3 of the License, or
4
// (at your option) any later version.
5
//
6
// This program is distributed in the hope that it will be useful,
7
// but WITHOUT ANY WARRANTY; without even the implied warranty of
8
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
// GNU General Public License for more details.
10
//
11
// You should have received a copy of the GNU General Public License
12
// along with this program. If not, see <http://www.gnu.org/licenses/>.
13
//
14
// Dieses Programm ist Freie Software: Sie können es unter den Bedingungen
15
// der GNU General Public License, wie von der Free Software Foundation,
16
// Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
>Das ist kein Treiber, das ist ganz einfach die lib.
In dem Jar File, dass Du als "lib" bezeichnest, ist allderdings auch
eine dll und ein *.so für die Unixoiden. Ich würde das als Treiber
bezeichnen.
Helge schrieb:> Ich würde das als Treiber bezeichnen.
Nein, das Teil implementiert die JNDI Schnittstelle, der "Treiber" für
die Serielle Schnittstelle muss vom System bereitgestellt werden.
Ja, ich bezeichne das als lib, weil es eine Library ist. Was in deinem
verlinkten Artikel erwähnt ist:
- Extract all file somewhere.
- In Eclipse: Project Properties -> Java Build path -> Libraries
- Press Add external jars and choose jssc.jar
- Enjoy
ist ganz normales Vorgehen für das Einbinden von libs in Eclipse. Ob da
jetzt dll, so, oder sonstwas drin ist, interessiert an der Stelle doch
garnicht.
Was ist denn jetzt eigentlich an rxtx schlecht? (ernsthafte Frage, ich
mach nicht soviel mit seriell in Java).
>Was ist denn jetzt eigentlich an rxtx schlecht
Ich hatte es vor ein paar Jahren mal für ein Programm verwendet, welches
Integer Zahlen gelesen und graphisch dargestellt hat. Irgendwie
funktioniert es aber nicht mehr und da rxtx.org aufgelöst wurde, ging
ich davon aus, dass es nicht mehr gepflegt wird.
Hat die Version von Läubi schon mal jemand ausprobiert?
Auf meinem Ubuntu 14.04 funktioniert der Empfang mit der Version von
chris, mit der Version von Läubi aber nicht.
marcus schrieb:> Hat die Version von Läubi schon mal jemand ausprobiert?> Auf meinem Ubuntu 14.04 funktioniert der Empfang mit der Version von> chris, mit der Version von Läubi aber nicht.
"funktioniert nicht" ist immer schwer zu sagen. Gibt es einen Fehler?
Sendest du auch ein \r\n am Ende der Zeile?
Da die Version 'von chris' (welche dem Beispiel auf der JSSC Seite
entspricht) überhaupt nichts von der Seriellen liest solltest du
außerdem mal benennen was du genau benutzt.
Helge schrieb:> Du meinst JNI
Ja genau...
PIC nico schrieb im Beitrag #3855120:
> Funktioniert wunderbar.
Ich habe auch "das Orginal" schon mehrfach ohne Probleme eingesetzt
daher ja auch meine Frage
Läubi .. schrieb:> chris_ schrieb:>> Dieser scheint obsolet zu sein, statt dessen nimmt man wohl JSSC>> Worraus schließt du das?
denn das JSSC ist auch nicht wirklich als Verbesserung zu bezeichnen
(soweit man bei einer Simplen Seriellen Schnittstelle überhaupt viel
verbessern kann), die Java PI ist auf jedenfall grausig und nicht gerade
ein Beispiel für guten Programmierstil.
Mittlerweile wir JSSC wohl in der Arduino IDE eingesetzt. Das spricht
meiner Meinung nach stark für JSSC, weil die Arduino IDE sehr gut
gewartet ist und millionenfach verwendet wird.
JSSC findet sich im Lib-Verzeichnis der Arduino IDE:
https://github.com/arduino/Arduino/tree/master/app/lib
Das heißt die Lib ist schon immer auf dem Computer, wenn man die Arduino
IDE installiert hat.
Man könnte die Lib also in die eigenen Javaprogramme einbinden
http://javabeginners.de/Grundlagen/Bibliothek_einbinden.php
wenn man das eigene Programm an einer festen Stelle relativ zum
Arduino-Verzeichnis ablegt. Oder gibt es auch eine Möglichkeit eine Lib
mit einerm erst zur Laufzeit bekannten Pfad einzubinden?
> 2. man hat unter Ubuntu 14.04 keine Rechte für die serielle> Schnittstelle> Lösung: sudo chmod 666 /dev/ttyACM0 ( bzw. statt ttyACM0 den Namen der> Schnittstelle )
kleines ja + Grosses NEIN - Weil:
- dies die Holzhammermethode ist,
- ein andermal die Schnittstelle /dev/ttyACM1 heissen kann und die
Rechte wieder anzupassen sind,
- dies nur wiederholt werden kann wenn jemand m. admin-/sudo-Rechte da
ist,
- dammit alles und jeder auf die Schnittstelle zugreifen kann und
Unfug damit machen (sei es nur sie zu belegen und nicht freigeben)
Der Weg zur richtigen Lösung geht über 1 bis 2 Stufen:
1. Jedem Account der die Schnittstelle (~nklasse) nutzen dürfen soll,
die entspr. Rechte zuteilen,
2. opt./ggfs. das (pluggable) Schnittstellengerät mit spezifischen
permissionbits/ownership versehen
Im Falle von "gewöhnlichen" seriellen Schnittstellen genügt i.a.R. 1.
Zu 1:
Was hinter den GUI-Kulissen passiert (und somit auch "zu Fuss" per
Kommandozeile gemacht werden kann) ist dass der Account (auch) Mitglied
jener Gruppe sein muss, zu der die Schnittstelle gehört. Also
"""
$ ls -ld /dev/ttyACM*
"""
wird "dialout" oder "modem" oder sowas nennen (hängt von der Distri ab).
Im Ergebnis muss also
"""
$ grep $USER /etc/group
"""
i.a.R. min. 2 Zeilen zeigen: die Standardgruppe welche gleich heisst
wie $USER und eben "dialout" (oder entspr.d. Distri)
Ob dies nun per sed/vim/nano/$EDITOR, Spezialbefehl oder via GUI
geschieht spielt keine Rolle.
Unter (L)Ubuntu ist das GUI dazu bei der
Accountverwaltung/-einstellungen zu finden und lautet so ähnlich wie
"darf Modems benützen"
Zu 2:
gehe ich hier nicht detailliert ein, da f. "gewöhnliche" serielle
Schnittstellen i.a.R. nicht nötig.
Es hat mit "udev", dem "Linux dynamic device management" zu tun; die
Konfiguration ist im wesentlichen in /etc/udev/udev.conf, Standardregeln
in /etc/udev/rules.d/, eigene Anpassungen in /etc/udev/user.rules
untergebracht.
Wozu? Es erlaubt so Feinheiten zu regeln wie z.B. "Nur $USER darf ein
Bestimmtes Gerät benutzen" oder z.B. "bei anstecken von Geräte der
Klasse zyx muss bla+bliff passieren"
--> $ man udev
Hallo Kommandozeile ;-)
sehr löblich, dass Du Dir die Arbeit gemacht hast.
Wenn man im Internet ein wenig sucht, scheint das Problem mit der
dail-out-Gruppe weit verbreitet zu sein.
Ich finde es etwas umständlich, dass jeder, der das verwenden will, die
obige Prozedur durchlaufen muss. Ich schätze, das kostet jeden
mindestens eine halbe Stunde Arbeit ( oder länger, falls was nicht
klappt ).
Gäbe es nicht die Möglichkeit, einfach ein Script zu schreiben, was das
erledigt?
> Ich finde es etwas umständlich, dass jeder, der das verwenden will, die> obige Prozedur durchlaufen muss. Ich schätze, das kostet jeden> mindestens eine halbe Stunde Arbeit
???
Meine Erläuterung ist ausführlich weil ich Antworten wie "42" (ohne
Hintergrundinfo) nicht mag...
Aber: beim Account anlegen einfach Hirn einschalten und überlegen was
für die Aufgaben der Konti nötig ist. Seriöses Accountmanagement hilft
bei Änderungen der Aufgabenbereiche.
Sonst:
"""
$ sudo sed -e '/^dialout:/s!$!,'"$USER"'!g'
"""
Erst üben, dann zu sed option -i lesen - ich hab sie bewusst nicht in
die Zeile geschrieben damit Unbedarfte nicht ihr /etc/group File
verwüsten.
Tip: Kontrolle m. grep vor und nach Änderung verschafft Zuversicht &
Sicherheit im Umgang.
(und dies alles vorm Sonntagsfrühstück :)
tl;dr: http://sed.sourceforge.net/sed1line.txt
unter Linux scheint
"""
$ useradd -G dialout $USER
"""
als Spezialbefehl üblich zu sein.
(ist nicht unbedingt in allen *nixen identisch, denke da an embedded
systeme m. Busybox)
Ein guter Test wäre ein Terminal Programm mit JSSC zu schreiben.
Theoretisch sollte es das zwar geben, aber ich habe leider keines
gefunden.
Hier das erste Experiment das ich mit dem obigen Testsender ausprobiert
habe.
In einem Textfenster werden die verfügbaren Ports angezeigt und danach
die empfangen Zeichen des Ports "/dev/ttyUSB0" im Textfenster
dargestellt.
Die Systemkonsole zeigt an, dass alle Zeichen empfangen werden. Aber
leider fehlen im Textfenster ein paar.
1
importjava.awt.event.WindowAdapter;
2
importjava.awt.event.WindowEvent;
3
4
importjavax.swing.JFrame;
5
importjavax.swing.JPanel;
6
importjavax.swing.JScrollPane;
7
importjavax.swing.JTextArea;
8
9
importjssc.SerialPort;
10
importjssc.SerialPortEvent;
11
importjssc.SerialPortEventListener;
12
importjssc.SerialPortException;
13
importjssc.SerialPortList;
14
15
publicclassMainStart{
16
17
StringportName="/dev/ttyUSB0";
18
19
JFramefenster;
20
inttextDimension_x=80;
21
inttextDimension_y=40;
22
JTextAreaTextBereich;
23
24
staticSerialPortserialPort;
25
26
publicvoidopenSerialConnection()
27
{
28
TextBereich.append("trying to open port\n\r");
29
TextBereich.append(portName+"\n\r");
30
TextBereich.append("\n\r");
31
32
serialPort=newSerialPort(portName);
33
try{
34
35
System.out.println("port open :"+serialPort.openPort());//Open port
JavaTester schrieb:> Die Systemkonsole zeigt an, dass alle Zeichen empfangen werden. Aber> leider fehlen im Textfenster ein paar.>> if (numberOfBytes > 4)> {
und sonst?
>> if (numberOfBytes > 4)>> {>und sonst?
Wenn es weniger als 4 Bytes sind, wird der Stream erst beim nächsten Mal
gelesen. Aber Du hast schon recht, der Fehler liegt ungefähr in der
Ecke. Das Problem ist die Zeile:
> String rxString=""+(char)buffer[0] + (char)buffer[1] + (char) buffer[2]>+ (char)buffer[3];
Es werden nur 4 Zeichen dargestellt, auch wenn es mehr im Buffer gibt.
Bei mir steht da eigtl. immer sowas hier in der Art (zwar rxtx, dürfte
aber ja keinen großen Unterschied machen) meine serielle Geräte liefern
aber auch immer "\r\n\":
1
public void serialEvent(SerialPortEvent event) {
2
switch (event.getEventType()) {
3
// --- evtl andere Events abfangen
4
case SerialPortEvent.DATA_AVAILABLE:
5
StringBuilder readBuffer = new StringBuilder();
6
int c;
7
8
while ((c = inputStream.read()) != 10) {
9
if (c != 13) {
10
readBuffer.append((char) c);
11
}
12
}
13
String scannedInput = readBuffer.toString();
14
15
break;
16
}
17
}
Der inputStream ist in dem Fall eine Klassenvariable, die ich bei der
Portinitialisiererei hol, etwa in der Art:
>Was mir an deinem Code garnicht gefällt ist, dass du im Eventhandler>aufs UI zugreifst, mach da mal ein SwingUtilities.invokeLater() drumrum.
Ah, das klingt nach einem sehr guten Tipp. Ich habe mich auch schon
gefragt, ob der Call der JTextBox nicht zu viel Rechenzeit frisst und
möglicherweise den Eventhandler zu lange blockiert.
Moin,
aufgrund dieses Threads habe ich heute meine ersten Programme von rxtx
auf jssc umgestellt.
Nicht das rxtx (ich verwendete die rxtx 2.2pre2
http://rxtx.qbang.org/wiki/index.php/Main_Page) nicht gut funktionierte.
jssc zickt meiner Meinung nach etwas weniger rum. Aber das ist nicht der
Grund für den Wechsel.
Wir haben hier in der Behörde an vielen Arbeitsplätzen Datalogger
laufen. Und dummerweise haben wir nicht überall Admin-Rechte auf den
Win7-Maschinen und was noch dummer ist, ständig bügeln die Admins mit
empirum ein neues Java auf die Rechner, so dass die rxtx jar- und
dll-Files immer wieder händisch hinterlegt werden müssen. Dieses Problem
ist mit jssc behoben.
Die eingehenden Daten hole ich mir immer mit einem Event-Listener ab.
Bei uns ist das Protokollende immer mit CHR(10) oder ETX. Das fange ich
ab und verarbeite dann die Daten. Bei Interesse kann ich den Code
posten.
Holger
>Wir haben hier in der Behörde an vielen Arbeitsplätzen Datalogger>laufen. Und dummerweise haben wir nicht überall Admin-Rechte auf den>Win7-Maschinen und was noch dummer ist, ständig bügeln die Admins mit>empirum ein neues Java auf die Rechner, so dass die rxtx jar- und>dll-Files immer wieder händisch hinterlegt werden müssen. Dieses Problem>ist mit jssc behoben.
Meiner Meinung nach ist JSSC auch viel besser. Da es in allen
Arduino-IDEs verwendet wird, ist ein ziemlich hoher Druck, dass es
sauber und einfach funktioniert.
>Die eingehenden Daten hole ich mir immer mit einem Event-Listener ab.>Bei uns ist das Protokollende immer mit CHR(10) oder ETX. Das fange ich>ab und verarbeite dann die Daten. Bei Interesse kann ich den Code>posten.
Der Code würde mich sehr interessieren. Bin gespannt, wie Du es gemacht
hast.
Moin richtig, über einen Scheduler polle ich in diesem Fall alle 10
Sekunden. Zwei Sekunden nach dem Poll hole ich mir die Daten ab. Aber es
gibt natürlich auch Sensoren, die z.B. vier- oder zehnmal pro Sekunde
Protokolle raushauen. Da erfolgt dann die Verarbeitung direkt in
1
privatevoidausgabeProtokoll(String_tangaString)
2
{
3
...
4
}
Die Comport-Chooser habe ich abgeschafft. Meine Programme tingeln nahezu
zeitgleich alle COM-Ports ab und schauen ob es da was gibt. Je nach
Sensor wird gepollt oder eben eine gewisse Zeit gewartet. Ich arbeite
zum Teil mit mehreren 4x- und 8x-MOXA-Konvertern. Da würde ich mich ja
zu Tode suchen.
Holger
Hier habe ich das einfache Beispiel mit dem GUI-Termialfenster auf
GitHub gelegt:
https://github.com/ChrisMicro/simpleTerm_with_JSSC/blob/master/src/Main_simpleTerm.java>Was mir an deinem Code garnicht gefällt ist, dass du im Eventhandler>aufs UI zugreifst, mach da mal ein SwingUtilities.invokeLater() drumrum.
Es scheint ohne "SwingUtilities.invokeLater()" zu funktionieren.
Wahrscheinlich könnte es aber zu "Verklemmungen" kommen. Weiß jemand,
wie man es korrekt einbindet?
ChrisM schrieb:> Es scheint ohne "SwingUtilities.invokeLater()" zu funktionieren.> Wahrscheinlich könnte es aber zu "Verklemmungen" kommen. Weiß jemand,> wie man es korrekt einbindet?
Ähm, natürlich nicht den ganzen Eventhandler verpacken.
Nur die Stellen, bei denen du auf die GUI zugreifst.
Du kannst dir ja z.B. auch eine Ausgabefunktion schreiben
1
private void outputText(final String rx){
2
SwingUtilities.invokeLater(new Runnable() {
3
@Override
4
public void run() {
5
outputTextBereich.setText(rx);
6
}
7
});
8
}
Du könntest auch mit einem SwingWorker arbeiten (dürfte sich einiges im
Zusammenhang mit jssc finden lassen), da dann aber am besten gleich per
polling. Oder wenns Eventbasiert sein muss, dann lass den Event in einen
Buffer schreiben und les den vom SwingWorker aus aus.
>Du kannst dir ja z.B. auch eine Ausgabefunktion schreiben
Danke, das ging ja schnell. Ich habe Deine Funktion einfach eingebaut
und noch eine Löschung es Textfeldes nach jeweils 40 Zeichen angefügt.
>Oder wenns Eventbasiert sein muss, dann lass den Event in einen>Buffer schreiben und les den vom SwingWorker aus aus.
Das weiß ich noch nicht. Eigentlich ist das Ziel, dass man die Zeilen
analysiert und dann die Daten an verschiedene Anzeigeinstrumente
ausgibt.
Jetzt habe ich mal weiter gebastelt. Das Programm ist jetzt ein
richtiges Terminalprogramm: Im vintage grünen Stil :-)
https://github.com/ChrisMicro/simpleTerm_with_JSSC
Eigentlich hätte ich noch gerne einen blinkende Cursor gehabt, das hat
aber irgendwie nicht funktioniert.
Im Anhang mal das "exekutierbare" Jar. Bei mir läuft es auf Ubuntu und
Win10.
ChrisM schrieb:>>Was mir an deinem Code garnicht gefällt ist, dass du im Eventhandler>>aufs UI zugreifst, mach da mal ein SwingUtilities.invokeLater() drumrum.
Moin,
bei komplexeren GUIs nehme ich natürlich Swingworker. Aber acht
COM-Ports abfragen und einfache Textausgaben sind kein Thema.
Holger
ChrisM schrieb nicht:
>>Was mir an deinem Code
Die Aussage war von "foo" nicht von mir. Bei mir war ja diese kleine
Unsauberkeit im Code.
Ich habe jetzt eine Wrapper für die serielle Schnittstelle geschrieben (
befindet sich im "develop branch" auf GitHub).
Der Wrapper kann jetzt beides: sowohl Events beim Empfang liefern als
auch Polling.
Hier die Polling-Funtkion mit Timeout für das PingPong eines Zeichens:
Das Polling passiert hier "Full Speed". Wie stark das die Rechenzeit
belastet, weiß ich nicht. Aber etwas Luft bekommt das Programm ja
außerhalb der Funktion.
Nachdem ich jetzt die "ping" Funktion etwas ausführlicher getestet habe,
treten leider Problem auf: Sie "verklemmt" sich so zusagen immer wieder.
Wenn man sie schnell hintereinander aufruft, kommt oft eine 0 zurück.
Hat jemand eine Ahnung, woran das liegen könnte?