Forum: PC-Programmierung Java serielle Schnittstelle mit JSSC


von chris_ (Gast)


Angehängte Dateien:

Lesenswert?

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 )

: Verschoben durch User
von chris_ (Gast)


Lesenswert?

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?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von chris_ (Gast)


Lesenswert?

>> 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.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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
17
//    veröffentlichten Version, weiterverbreiten und/oder modifizieren.
18
//
19
//    Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber
20
//    OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
21
//    Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
22
//    Siehe die GNU General Public License für weitere Details.
23
//
24
//    Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
25
//    Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.
26
27
package test;
28
29
import java.io.BufferedReader;
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.InputStreamReader;
33
34
import jssc.SerialPort;
35
import jssc.SerialPortException;
36
37
/**
38
 * Example that shows how a serial port can be wrapped to an input stream
39
 */
40
public class SerialStreamExample extends InputStream {
41
42
  private final SerialPort serialPort;
43
44
  public SerialStreamExample(SerialPort serialPort) {
45
    this.serialPort = serialPort;
46
  }
47
48
  @Override
49
  public int read() throws IOException {
50
    //open or closing is up to the underlying application
51
    if (!serialPort.isOpened()) {
52
      throw new IOException("Port not open");
53
    }
54
    try {
55
      //read one byte to return.
56
      byte[] bytes = serialPort.readBytes(1);
57
      if (bytes == null || bytes.length == 0) {
58
        //nothing could be read, the API is unclear if this really could happen
59
        return 0;
60
      } else {
61
        //return the read byte
62
        return bytes[0];
63
      }
64
    } catch (SerialPortException e) {
65
      //convert serial exceptions to IO Exceptions using chaining
66
      throw new IOException("error reading from serial port", e);
67
    }
68
  }
69
70
  public static void main(String[] args) throws SerialPortException, IOException {
71
    SerialPort serialPort = new SerialPort("COM1");
72
    System.out.println("Port opened: " + serialPort.openPort());
73
    System.out.println("Params setted: " + serialPort.setParams(9600, 8, 1, 0));
74
    System.out.println("\"Hello World!!!\" successfully writen to port: "
75
        + serialPort.writeBytes("Hello World!!!".getBytes()));
76
    SerialStreamExample example = new SerialStreamExample(serialPort);
77
    BufferedReader reader = new BufferedReader(new InputStreamReader(example, "US-ASCII"));
78
    String line = reader.readLine();
79
    System.out.println("Read a line from serial: " + line);
80
    try {
81
      int zahl = Integer.parseInt(line);
82
      System.out.println("Number parsed is: " + zahl);
83
    } catch (NumberFormatException e) {
84
      System.out.println("can't be parsed as number");
85
    }
86
    System.out.println("Port closed: " + serialPort.closePort());
87
  }
88
89
}

von JavaJavaJava (Gast)


Lesenswert?

chris_ schrieb:

> 1. Wie kriege ich den Treiber in Eclipse
> Lösung==> http://stackoverflow.com/questions/20568602/how-to...


Das ist kein Treiber, das ist ganz einfach die lib. Schon vorher mal was 
mit Java gemacht? Oder wie kommt's zu diesem "Projekt"?

von Helge (Gast)


Lesenswert?

>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.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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.

von Helge (Gast)


Lesenswert?

> Nein, das Teil implementiert die JNDI Schnittstelle
Du meinst JNI:
http://de.wikipedia.org/wiki/Java_Native_Interface

von JavaJavaJave (Gast)


Lesenswert?

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).

von chris_ (Gast)


Lesenswert?

>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.

von marcus (Gast)


Lesenswert?

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.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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...

von PIC N. (eigo) Benutzerseite


Lesenswert?

Zum Thema RxTx: http://mfizz.com/oss/rxtx-for-java

Funktioniert wunderbar.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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.

von chris_ (Gast)


Lesenswert?

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?

von Kommandozeile vor dem Frühstück für Alle! (Gast)


Lesenswert?

> 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

von chris_ (Gast)


Lesenswert?

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?

von chris_ (Gast)


Lesenswert?

Für nachfolgende Empfangstest wird dieser Code verwendet:
1
// Arduino Testsender-Code
2
3
void setup() 
4
{
5
  Serial.begin(9600);
6
}
7
8
int Counter=0;
9
10
void loop() 
11
{
12
  Serial.println(Counter++);
13
  delay(10);        
14
}

von chris_ (Gast)


Lesenswert?

Wenn ich den SerialPort Eventlistener verwende, verliert er Zeichen.

https://gist.github.com/evgeniynickolaev/84c55d16478bd02a5edc

Woran könnte das liegen?

von Kommandozeile vor dem Frühstück für Alle! (Gast)


Lesenswert?

> 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

von Kommandozeile vor dem Frühstück für Alle! (Gast)


Lesenswert?

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)

von ch (Gast)


Lesenswert?

>(und dies alles vorm Sonntagsfrühstück :)

Danke für die frühen sonntäglichen Mühen :-) Du hast den Linux Nutzern 
auf jeden Fall was gutes getan.

von JavaTester (Gast)


Lesenswert?

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
import java.awt.event.WindowAdapter;
2
import java.awt.event.WindowEvent;
3
4
import javax.swing.JFrame;
5
import javax.swing.JPanel;
6
import javax.swing.JScrollPane;
7
import javax.swing.JTextArea;
8
9
import jssc.SerialPort;
10
import jssc.SerialPortEvent;
11
import jssc.SerialPortEventListener;
12
import jssc.SerialPortException;
13
import jssc.SerialPortList;
14
15
public class MainStart {
16
  
17
  String portName = "/dev/ttyUSB0";
18
  
19
  JFrame fenster;
20
    int textDimension_x=80;
21
    int textDimension_y=40;
22
    JTextArea TextBereich;
23
    
24
    static SerialPort serialPort;
25
  
26
    public void openSerialConnection()
27
    {
28
      TextBereich.append("trying to open port\n\r");
29
      TextBereich.append(portName+"\n\r");
30
      TextBereich.append("\n\r");
31
        
32
      serialPort = new SerialPort(portName);
33
        try {
34
          
35
            System.out.println("port open :" + serialPort.openPort());//Open port
36
            serialPort.setParams(SerialPort.BAUDRATE_9600,
37
                    SerialPort.DATABITS_8,
38
                    SerialPort.STOPBITS_1,
39
                    SerialPort.PARITY_NONE);
40
41
            int mask = SerialPort.MASK_RXCHAR;//Prepare mask
42
            serialPort.setEventsMask(mask);//Set mask
43
            serialPort.addEventListener(new SerialPortReader(TextBereich));//Add SerialPortEventListener
44
   
45
        } catch (SerialPortException ex) {
46
          TextBereich.append("port error");
47
            System.out.println(ex);
48
        }
49
    }
50
    
51
    public void closeSerialConnection()
52
    {
53
        try {
54
      serialPort.closePort();
55
    } catch (SerialPortException e) {
56
      System.out.println("closePort error");
57
      e.printStackTrace();
58
    }
59
    }
60
    
61
  public MainStart()
62
  {
63
    fenster=new JFrame("java serial terminal");
64
    fenster.addWindowListener(new WindowAdapter() {
65
          public void windowClosing(WindowEvent e) {
66
            closeSerialConnection();
67
              System.exit(0);        
68
          }        
69
      });
70
71
    //************************ create window with text **********************************
72
      JPanel lowerPanel = new JPanel();
73
74
      fenster.getContentPane().add(lowerPanel, "South");
75
76
      TextBereich = new JTextArea(textDimension_y, textDimension_x);
77
78
      TextBereich.setLineWrap(true);
79
      TextBereich.setWrapStyleWord(true);
80
      lowerPanel.add(new JScrollPane(TextBereich));
81
      
82
      fenster.pack();
83
      fenster.setVisible(true);
84
      
85
      //************************ show serial ports ****************************************
86
      TextBereich.append("serial port available on this system:\n\r");
87
      
88
        String[] portNames = SerialPortList.getPortNames();
89
        
90
        for(int i = 0; i < portNames.length; i++){
91
            System.out.println(portNames[i]);
92
            TextBereich.append(portNames[i]+"\n\r");
93
        }
94
        TextBereich.append("\n\r");
95
        //***********************************************************************************
96
        
97
        openSerialConnection();
98
99
  }
100
  
101
    static class SerialPortReader implements SerialPortEventListener {
102
      
103
      JTextArea outputTextBereich;
104
      
105
      public SerialPortReader(JTextArea outputTextBereich)
106
      {
107
        this.outputTextBereich=outputTextBereich;
108
      }
109
      
110
        public void serialEvent(SerialPortEvent event) 
111
        {
112
            if (event.isRXCHAR()) 
113
            {
114
              int numberOfBytes=event.getEventValue();
115
                if (numberOfBytes > 4) 
116
                {
117
                    try 
118
                    {
119
            byte buffer[] = serialPort.readBytes(numberOfBytes);
120
            String rxString=""+(char)buffer[0] + (char)buffer[1] + (char) buffer[2] + (char)buffer[3];
121
            System.out.println(rxString);
122
            outputTextBereich.append(rxString);
123
            
124
                    } catch (SerialPortException ex) 
125
                    {
126
                        System.out.println(ex);
127
                    }
128
                }
129
            } 
130
        }
131
    }
132
    
133
  public static void main(String[] args) 
134
  {
135
    new MainStart();
136
  }
137
138
}

von foo (Gast)


Lesenswert?

JavaTester schrieb:
> Die Systemkonsole zeigt an, dass alle Zeichen empfangen werden. Aber
> leider fehlen im Textfenster ein paar.
>
>                 if (numberOfBytes > 4)
>                 {

und sonst?

von chris_ (Gast)


Lesenswert?

>>                 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.

von foo (Gast)


Lesenswert?

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:
1
CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier("/dev/ttyUSB0");
2
SerialPort serport = (SerialPort) portID.open("Dingsbums", 2000);
3
serport.setSerialPortParams(19200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
4
inputStream = serport.getInputStream();
5
serport.addEventListener(this);
6
serport.notifyOnDataAvailable(true);

Was mir an deinem Code garnicht gefällt ist, dass du im Eventhandler 
aufs UI zugreifst, mach da mal ein SwingUtilities.invokeLater() drumrum.

von chris_ (Gast)


Lesenswert?

>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.

von Holger D. (Gast)


Lesenswert?

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

von chris_ (Gast)


Lesenswert?

>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.

von Holger D. (Gast)


Lesenswert?

Moin,

zu verbessern gibt es immer was. Aber so läuft es für mich ganz gut.


Holger



1
import java.io.IOException;
2
import java.io.InputStream;
3
import java.io.OutputStream;
4
import java.io.PrintWriter;
5
import java.text.SimpleDateFormat;
6
import java.util.ArrayList;
7
import java.util.Date;
8
import java.util.TooManyListenersException;
9
10
import jssc.SerialPort;
11
import jssc.SerialPortEvent;
12
import jssc.SerialPortEventListener;
13
import jssc.SerialPortException;
14
import jssc.SerialPortList;
15
16
public class SchnittStellenKlasse 
17
{
18
  private int         sensorNummer;
19
  private String         comPortNummer;
20
  private String         dwdNummer;
21
  private String         serienNummer;
22
  
23
  public SerialPort       serielle_schnittstelle;
24
  
25
  public  String        comBuffer ="";  
26
  
27
  private int          bufferpointer = 0;
28
  private byte[]         buffer = new byte[256];
29
  
30
  private  String         dtgString = "";
31
  private  SimpleDateFormat   df = new SimpleDateFormat("dd.MM.yyyy;HH:mm:ss");
32
  public String         protokollString;
33
34
  public SchnittStellenKlasse(int _sensorNummer, String _comPortNummer, String _dwdNummer, String _serienNummer) 
35
  {
36
    this.sensorNummer = _sensorNummer + 1; 
37
    this.comPortNummer = _comPortNummer;
38
    this.dwdNummer = _dwdNummer;
39
    this.serienNummer = _serienNummer;
40
    
41
    setComPortNummer(_comPortNummer);
42
    
43
    try 
44
    {
45
      serielle_schnittstelle = new SerialPort(comPortNummer);
46
      
47
      serielle_schnittstelle.openPort();
48
      
49
      serielle_schnittstelle.setParams(9600, 
50
          SerialPort.DATABITS_8,SerialPort.STOPBITS_1 , 
51
          SerialPort.PARITY_NONE);
52
      
53
      int mask = SerialPort.MASK_RXCHAR;
54
      serielle_schnittstelle.setEventsMask(mask);//Set mask
55
      
56
      serielle_schnittstelle.addEventListener(new serialEventListener());
57
        
58
    } 
59
    catch (SerialPortException e) 
60
    {
61
62
    }    
63
    }
64
65
66
  public String getComPortNummer() 
67
  {
68
    return comPortNummer;
69
  }
70
71
  public void setComPortNummer(String comPortNummer) 
72
  {
73
    this.comPortNummer = comPortNummer;
74
  }
75
76
77
  private class serialEventListener implements SerialPortEventListener 
78
  {
79
    public void serialEvent(SerialPortEvent arg0) 
80
    {
81
      if(arg0.isRXCHAR())
82
      {
83
        int numBytes = 0;
84
          boolean keep_running = true;
85
          numBytes = arg0.getEventValue();
86
        
87
        byte[] c = new byte[numBytes];
88
        
89
        try 
90
        {
91
          c = serielle_schnittstelle.readBytes(numBytes);
92
              
93
              while(keep_running)
94
              {
95
                for (int i=0;i < numBytes;i++)
96
                  {  
97
                  if (((int)c[i] & 0xff) == 10)
98
                    {  
99
                      byte[] bufferfinal = new byte[bufferpointer];
100
                          System.arraycopy(buffer, 0, bufferfinal, 0, bufferpointer);
101
                          String tangaString = new String(bufferfinal);
102
                          buffer = new byte[256];
103
                          bufferpointer = 0;
104
                          
105
                          ausgabeProtokoll(tangaString);
106
                          tangaString = "";
107
                      }                
108
                      else 
109
                      {
110
                          buffer[bufferpointer] = c[i];
111
                          bufferpointer++;
112
                      }
113
                  }
114
                  keep_running = false;                
115
              }
116
          }
117
        catch (SerialPortException e) 
118
        {
119
          e.printStackTrace();
120
        }
121
      }
122
    }
123
        
124
    private void ausgabeProtokoll(String _tangaString) 
125
    {
126
      protokollString = _tangaString;
127
      
128
      Date date = new Date();
129
      dtgString = df.format(date);      
130
131
      comBuffer = "EE33" + ";" + sensorNummer + ";" + dwdNummer + ";" + comPortNummer + ";" + dtgString +  ";" + protokollString.replace("\n","").replace("\r","");
132
    }
133
  }
134
  
135
  public String getComBuffer() 
136
  {
137
    return comBuffer;
138
  }
139
140
  public void setComBuffer(String comBuffer) 
141
  {
142
    this.comBuffer = comBuffer;
143
  }
144
145
  public void poll() 
146
  {
147
    sendeString("#00:P0:05:RE=11:7a:");
148
  }
149
150
  public void senden(String _sendeString) 
151
  {
152
    sendeString(_sendeString);
153
  }
154
155
  public String getProtokollString() 
156
  {
157
    return protokollString;
158
  }
159
160
  public void setProtokollString(String protokollString) 
161
  {
162
    this.protokollString = protokollString;
163
  }
164
  
165
  public String getDwdNummer() 
166
  {
167
    return dwdNummer;
168
  }
169
170
  public void setDwdNummer(String dwdNummer) 
171
  {
172
    this.dwdNummer = dwdNummer;
173
  }
174
175
  public String getSerienNummer() 
176
  {
177
    return serienNummer;
178
  }
179
180
  public void setSerienNummer(String serienNummer) 
181
  {
182
    this.serienNummer = serienNummer;
183
  }
184
  
185
  private void sendeString(String _string) 
186
  {
187
    _string = _string + "\r\n";
188
    try 
189
    {
190
      serielle_schnittstelle.writeBytes(_string.getBytes());
191
    } 
192
    catch (SerialPortException e) 
193
    {
194
      e.printStackTrace();
195
    }
196
  }
197
}

von chris_ (Gast)


Lesenswert?

Interessant, wenn ich es richtig sehe, sammelst Du die Daten im 
comBuffer und hollst sie dann per polling ab. Hast Du dazu eine 
zyklische Schleife mit konstanter Periode?

Ich suche noch einen passenden ComPortChooser, damit man auf der 
Oberfläche den Port einfach auswählen kann.
Hier gibt es so was ähnliches:
http://toupie.googlecode.com/svn-history/r173/trunk/ToupieClient/src/org/toupie/client/view/ComPortChooser.java

von Holger D. (Gast)


Lesenswert?

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
private void ausgabeProtokoll(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

von ChrisM (Gast)


Lesenswert?

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?

von foo (Gast)


Lesenswert?

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.

von ChrisM (Gast)


Lesenswert?

>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.

von ChrisM (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Holger D. (Gast)


Lesenswert?

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

von ChrisM (Gast)


Lesenswert?

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:
1
public int ping(int value) 
2
  {
3
    byte data[]={0};
4
    long timeout_ms=100; 
5
        
6
    try 
7
    {
8
        serialPort.purgePort(SerialPort.PURGE_RXCLEAR); // clear receiver buffer
9
        
10
        serPortReader.blockReceiverEvent();
11
        serialPort.writeByte((byte)value);
12
        
13
        long startTime=System.currentTimeMillis();
14
        long stopTime=startTime+timeout_ms;
15
        do
16
        {
17
          
18
        }while(serialPort.getInputBufferBytesCount()<1 && System.currentTimeMillis()<stopTime);
19
        
20
      data=serialPort.readBytes();
21
      serPortReader.enableReceiverEvent();
22
23
24
    } catch (SerialPortException e) 
25
    {
26
      byte dat[]={0};
27
      data=dat;
28
      e.printStackTrace();
29
    }
30
    
31
    if(data!=null)    return (int) data[0];
32
    else return 0;
33
  }

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.

von ChrisM (Gast)


Lesenswert?

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?

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.