gtohagan
Posts: 4
Joined: Tue Sep 29, 2015 4:31 pm

Pi4J Serial Comms NO Exception

Tue Sep 29, 2015 6:22 pm

Hi,
I wanted to ask if anyone has a solution for the following.

I have a USB dongle that I can communicate to using the PI4J software (version 1.0) on /dev/ttyUSB0. If I unplug the dongle and plug it back into the same or another USB, PI4J never throws an exception.

I traced the code and it looks like the filedescriptor always exist even when I remove the USB dongle. I was expecting an IlligealStateException when calling write API of the serial driver but nothing get thrown.

Can any one shed light on this and how to fix.
Thanks
Gary

User avatar
xranby
Posts: 540
Joined: Sat Mar 03, 2012 10:02 pm
Contact: Website

Re: Pi4J Serial Comms NO Exception

Wed Sep 30, 2015 7:01 am

I have built a project where i wanted to know when a serial usb dongle is attached and removed that is working reliable.

my solution composed of two parts:
1. I decided to add custom /etc/udev/rules.d/ udev rule to make the serial usb dongle always to get a fixed /dev/ttySerialUSB when attached.

My /etc/udev/rules.d/40-serial-usb.rules look like this:

Code: Select all

ACTION=="add",KERNEL=="ttyUSB*",SYSFS{idVendor}=="067b",SYSFS{idProduct}=="2303",SYMLINK+="ttyPROLIFIC067b2303"
ACTION=="add",KERNEL=="ttyUSB*",SYSFS{idVendor}=="0557",SYSFS{idProduct}=="2008",SYMLINK+="ttyPROLIFIC05572008"
ACTION=="add",KERNEL=="ttyUSB*",SYSFS{idVendor}=="0403",SYSFS{idProduct}=="6001",SYMLINK+="ttyFTDI04036001"
ACTION=="add",KERNEL=="ttyUSB*",SYSFS{idVendor}=="110a",SYSFS{idProduct}=="1110",SYMLINK+="ttyMOXA110a1110"
As you can see, i can assign different /dev/tty* names depending on the product and vendor id of the serial usb device.
the vendor and product id is easily discovered by running lsusb after you have attached the dongle


2. then I used the program ser2net to make the /dev/ttySerialUSB appear as a local telnet tcp/ip network port
The benefit of using ser2net compared to connecting directly to the serial port was that it was much easier to know when the connection was broken. ser2net closed the tcp/ip port if you removed the dongle hence you always got a network exception when the hardware was removed, also the port opened again when the hardware was inserted again.

My /etc/ser2net.conf looks like this:
127.0.0.1,5990:raw:0:/dev/ttyPROLIFIC067b2303:9600 8DATABITS XONXOFF NONE 1STOPBIT
example the above configuration makes the local tcp/ip port 5990 connect to the /dev/ttyPROLIFIC067b2303 dongle


And finally i create a Java class that can connect and read from this local network port.

Code: Select all

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * @author xerxes rånby
 */
public class TCPIPSerialConnection  {

    private String serverURL;
    private int serverPort;
    Socket myClientSocket;
    DataOutputStream output;
    DataInputStream input;

    private boolean connected;
    private boolean gotEOF;
    
    public TCPIPSerialConnection(String serverURL, int serverPort) {
        this.serverPort=serverPort;
        this.serverURL=serverURL;
        this.isServer=false;
        connected=false;
        myClientSocket=null;
        myServerSocket=null;
        gotEOF=false;
    }

    public boolean connect() {
        gotEOF=false;
        try {
           connected=false;

           // Client
           myClientSocket = new Socket(serverURL, serverPort);

           myClientSocket.setSoTimeout(200);
           myClientSocket.setTcpNoDelay(true);
           myClientSocket.setKeepAlive(true);
           connected=true;

           output = new java.io.DataOutputStream(myClientSocket.getOutputStream());
           input =  new java.io.DataInputStream(myClientSocket.getInputStream());
        } catch (IOException e) {
           disconnect();
           connected=false;
        }
        
        return connected;
    }

    synchronized public boolean put(byte[] b) {
       try {
           output.write(b);
           output.flush();
       } catch (IOException ex) {
          disconnect();
          return false;
       }  
        return true;
    }

   /*
    * returns a byte array containing data from the input socket
    * returns null in case that the socket has closed or if we recive end of file EOF or if no new data exist.
    */
    public byte[] get() {

       byte[] recived = null;
       byte[] data = new byte[65536];

       int count = 0;
       try {
           count =  input.read(data, 0, 65536);
           if(count>0){
              recived = getFirstBytes(data, count);
           } else if (count==0) {          
                  gotEOF=true;
                  // return null
           } else if (count==-1) {                 
                  gotEOF=true;
                 // return null
           }
        } catch (SocketTimeoutException ex) {
           // Ignore simply return null         
        }
       return recived;
    }

    public void disconnect() {  
        try {
            if(output!=null){
                output.close();
                output=null;
            }
            if(input!=null){
                input.close();
                input=null;
            }
            if(myClientSocket!=null){           
                myClientSocket.close();
                myClientSocket=null;
            }
        } catch (IOException ex) {
            Logger.getLogger(TCPIP.class.getName()).log(Level.SEVERE, null, ex);
        }
        connected=false;
    }

    /*
     * returns true if the connection is still open.
     * a tcp/ip socket can only detect if a connection is open when you read or send to it
     * the gotEOF flag is set when the connection is closed.
     */
    public boolean isOpen() {
        boolean result = false;
        if(myClientSocket!=null){              
            result=!gotEOF; 
        }
        return result;
    }

    public  static byte[] getFirstBytes(byte[] data, int length) {
        byte[] result = new byte[length]; 
        int i;
        int readlength;
        if(length<data.length){
            readlength=length;
        } else {
            readlength=data.length;
        } 
        for (i=0;i<readlength;i++) {
            result[i]=data[i];
        }
        return result;
    }
}
Xerxes Rånby @xranby I once had two, then I gave one away. Now both are in use every day!
twitter.com/xranby

gtohagan
Posts: 4
Joined: Tue Sep 29, 2015 4:31 pm

Re: Pi4J Serial Comms NO Exception

Wed Sep 30, 2015 9:39 am

Hi xranby,

thanks for your response. This is a very nice solution of using network connection and ser2net. I managed to it working with a slight hack.

What I did was when the connection is make to ttyUSB* I create a instance of java io File to this device. On each read/write I check

Code: Select all

file.exists()
This binds to the file descriptor once the file is opened and if the user unplugs and then re-attaches the dongle the file descriptor is still missing. At this point I reInitilise() the serialPort and on a success reassign the file to the new port.

I wanted to ask a couple of questions regarding your solution.
  • ser2net - how long have you used it and is it reliable?
  • ser2net - if the dongle is unplugged and plugged back in some time later does it auto pick this up again without a restart?
  • The udev rules do I simply change the SYMLINK to ttySerialUSB? I see you have different names there.

    Thanks.

User avatar
xranby
Posts: 540
Joined: Sat Mar 03, 2012 10:02 pm
Contact: Website

Re: Pi4J Serial Comms NO Exception

Wed Sep 30, 2015 10:20 am

gtohagan wrote:Hi xranby,

thanks for your response. This is a very nice solution of using network connection and ser2net. I managed to it working with a slight hack.

What I did was when the connection is make to ttyUSB* I create a instance of java io File to this device. On each read/write I check

Code: Select all

file.exists()
This binds to the file descriptor once the file is opened and if the user unplugs and then re-attaches the dongle the file descriptor is still missing. At this point I reInitilise() the serialPort and on a success reassign the file to the new port.

I wanted to ask a couple of questions regarding your solution.
  • ser2net - how long have you used it and is it reliable?
I have had ser2net deployed since 2010, used it before i ported my project to pi hardware, and it is still working.
gtohagan wrote:
  • ser2net - if the dongle is unplugged and plugged back in some time later does it auto pick this up again without a restart?
ser2net automatically picks it up again without a restart.
gtohagan wrote:
  • The udev rules do I simply change the SYMLINK to ttySerialUSB? I see you have different names there.
yes, i used the ttySerialUSB as an example name, in my project i have up to four different dongles attaches at the same time and i use the dongles for different purposes thats why i used different names for each dongle.
a simple rule like this one

Code: Select all

ACTION=="add",KERNEL=="ttyUSB*",SYMLINK+="ttySerialUSB"
would make all ttyUSB* dongles appear as ttySerialUSB
Last edited by xranby on Wed Sep 30, 2015 10:35 am, edited 1 time in total.
Xerxes Rånby @xranby I once had two, then I gave one away. Now both are in use every day!
twitter.com/xranby

gtohagan
Posts: 4
Joined: Tue Sep 29, 2015 4:31 pm

Re: Pi4J Serial Comms NO Exception

Wed Sep 30, 2015 10:26 am

Thanks xranby.

Return to “Java”