简体   繁体   English

使用Java在Raspberry Pi 3上读写USB设备

[英]Read and Write to USB Device on Raspberry Pi 3 using Java

I'm using Raspbian on a Raspberry Pi 3. I'm learning how to code in Java (SE runtime version 1.8.0_65), and I need to communicate raw data with a USB connected Bill Acceptor. 我在Raspberry Pi 3上使用Raspbian。我正在学习如何使用Java(SE运行时版本1.8.0_65)进行编码,并且我需要通过USB连接的Bill Acceptor传递原始数据。 According to the manufacturer's documentation, the USB unit mimics a serial-type interface. 根据制造商的文档,USB单元模拟串行类型的接口。 When I plug the device in, it appears in /dev/serial/by-id . 当我插入设备时,它会显示在/dev/serial/by-id I wrote C code 20+ years ago using modems on SCO Unix setups. 20多年前,我在SCO Unix设置上使用调制解调器编写了C代码。 If memory serves, I basically treated the modems /dev/ttyxx as a file for reading and writing. 如果有内存,我基本上将调制解调器/dev/ttyxx视为用于读取和写入的文件。 I wrote a C++ program 10+ years ago on Windows XP using a similar Bill Acceptor unit (same manufacturer and model) connected to a serial port instead of USB. 十多年前,我在Windows XP上编写了一个C ++程序,使用类似的Bill Acceptor装置(相同的制造商和型号)连接到串行端口而不是USB。 The documentation shows both USB and serial units use the same data strings for input and output, so I know the SendString data should be correct for triggering a valid response. 文档显示USB和串行单元都使用相同的数据字符串进行输入和输出,因此我知道SendString数据对于触发有效响应应该是正确的。 However, I'm not getting a reply from the unit, so I'm guessing I'm not connecting properly to it. 但是,我没有收到该部门的回复,所以我猜我没有正确连接到它。

Here is the code... 这是代码...

public static void main(String[] args) {
    try
    {
        System.out.println("*****************************");
        System.out.println("*****  Starting Program *****");
        System.out.println("*****************************");
        String strUsbDeviceDir = "/dev/serial/by-id";
        File myUsbDeviceDir = new File(strUsbDeviceDir);
        if(myUsbDeviceDir.exists())
        {
           String[] myUsbDevices = myUsbDeviceDir.list();
           for(int i=0; i<myUsbDevices.length; i++)
           {
               if(myUsbDevices[i].contains("EBDS_over_USB"))
               {
                   System.out.println("Connecting to " + myUsbDevices[i]);
                   funcBillAcceptor(strUsbDeviceDir + "/" + myUsbDevices[i]);
               }
               else
               {
                   System.out.println("Not using " + myUsbDevices[i]);
               }
           }
        }
    }
    catch (Exception ex)
    {
        System.err.println(ex.toString());
    }
}

public static void funcBillAcceptor(String strBillAcceptor)
{
    boolean bOddCount = false;
    byte[] SendString = new byte[8];
    byte[] RecvString = new byte[10];
    byte CheckDigit;
    int iSendStringCount, iRecvStringCount, iRecvEmptyCount;

    try
    {
        File fBillAcceptor = new File(strBillAcceptor);
        if(!fBillAcceptor.canRead())
        {
            System.out.println("No Read Permission for " + strBillAcceptor);
            return;
        }
        if(!fBillAcceptor.canWrite())
        {
            System.out.println("No Write Permission for " + strBillAcceptor);
            return;
        }

        RandomAccessFile rafBillAcceptor = new RandomAccessFile(strBillAcceptor, "rwd");
        if(rafBillAcceptor != null)
        {
            System.out.println("Successfully opened " + strBillAcceptor);
        }

        while(fBillAcceptor.exists())
        {
            SendString[0] = (byte) 0x02; //STX
            SendString[1] = (byte) 0x08;
            if(bOddCount)
            {
                SendString[2] = (byte) 0x10;
                bOddCount = false;
            }
            else
            {
                SendString[2] = (byte) 0x11;
                bOddCount = true;
            }
            SendString[3] = (byte) 0x1F;
            SendString[4] = (byte) 0x0C;
            SendString[5] = (byte) 0x00;
            SendString[6] = (byte) 0x03; //ETX

            //CheckDigit skips STX (byte 0) with byte 1 as seed/initial value
            //To calculate the check digit, start with next byte (2)
            CheckDigit = SendString[1];
            iSendStringCount = 2;
            while(SendString[iSendStringCount] != 0x03)
            {
                CheckDigit = (byte) (SendString[iSendStringCount]^CheckDigit); //XOR current CheckDigit value with next byte
                iSendStringCount++;
            }
            iSendStringCount++; //advance one more so we don't overwrite ETX
            SendString[iSendStringCount] = (byte) CheckDigit;

            try
            {
                rafBillAcceptor.write(SendString);
                System.out.println("Sent: " + DatatypeConverter.printHexBinary(SendString));
            }
            catch (Exception ex)
            {
                System.err.println("Write exception: " + ex.toString());
            }

            System.out.println("Reading...");
            iRecvStringCount = iRecvEmptyCount = 0;
            try
            {
                do
                {
                    iRecvStringCount = rafBillAcceptor.read(RecvString);
                    System.out.println("Read " + iRecvStringCount + " bytes.");
                    if(iRecvStringCount < 0)
                    {
                        iRecvEmptyCount++;
                        Thread.sleep(5);
                    }
                } while (iRecvStringCount < 0 && iRecvEmptyCount < 100);
                if(iRecvStringCount > 0)
                {
                    System.out.println("Received: " + DatatypeConverter.printHexBinary(RecvString));
                }
            }
            catch (Exception ex)
            {
                System.err.println("Read exception: " + ex.toString());
            }
        }
    }
    catch (Exception ex)
    {
        System.err.println(ex.toString());
    }
}

Here is the output I'm getting... 这是我得到的输出...

*****************************
*****  Starting Program *****
*****************************
Connecting to usb-Silicon_Labs_Series_2000_Bill_Acceptor__EBDS_over_USB__46580120748-if00-port0
Successfully opened /dev/serial/by-id/usb-Silicon_Labs_Series_2000_Bill_Acceptor__EBDS_over_USB__46580120748-if00-port0
Sent: 0208111F0C00030A
Reading...

Am I missing something obvious, or just going about this all wrong? 我是否遗漏了一些明显的东西,或者只是解决了所有这些错误? Thanks for any suggestions! 感谢您的任何建议!

I was able to successfully communicate with the Bill Acceptor from a Raspberry Pi 3 (running Raspbian) using the RXTXComm library . 我能够使用RXTXComm库从Raspberry Pi 3(运行Raspbian)成功地与Bill Acceptor通信。 When communicating with the device from an old Windows XP computer using a 9-pin serial/RS232 harness, I had to use 9600/7/E/1. 使用9针串行/ RS232线束从旧的Windows XP计算机与设备进行通信时,我必须使用9600/7 / E / 1。 If I don't use those values for setSerialPortParams (in my sample code), I'm not able to correctly send/receive data. 如果我没有在setSerialPortParams中使用这些值(在我的示例代码中),那么我将无法正确发送/接收数据。 So, you need to know what the device is expecting in order to use this approach with other "serial" hardware connected via USB. 因此,您需要了解设备的预期状况,才能将此方法与通过USB连接的其他“串行”硬件一起使用。 Below is code with the basic functionality. 以下是具有基本功能的代码。 I started with one of the sample programs that come with the RXTXComm library and built out from there. 我首先从RXTXComm库附带的示例程序之一开始,并从那里开始构建。 Since this is a sample program, I haven't yet added logic to verify the checksum digit for data coming from the device. 由于这是一个示例程序,因此我尚未添加逻辑来验证来自设备的数据的校验和数字。

You may notice at the bottom of the code that I'm able to determine the /dev/ttyUSBx using the canonical name of the device that I pull from the /dev/serial/by-id directory/folder. 您可能会注意到,在代码的底部,我能够使用从/dev/serial/by-id目录/文件夹中拉出的设备的规范名称来确定/ dev / ttyUSBx。 It finds the correct device name regardless of which USB port the unit is plugged in to or what order the USB devices get initialized by the system. 无论设备插入哪个USB端口或系统以何种顺序初始化USB设备,它都会找到正确的设备名称。 I have a card dispenser that is also a USB serial device, so I was able to test and confirm this functionality. 我有一个同时也是USB串行设备的自动提款机,因此我能够测试并确认此功能。

public TwoWaySerialComm()
{
    super();
}

static boolean bReadyToSend = false;

void connect ( String portName ) throws Exception
{
    CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
    if ( portIdentifier.isCurrentlyOwned() )
    {
        System.out.println("Error: " + portName + " is currently in use.");
    }
    else
    {
        CommPort commPort = portIdentifier.open(this.getClass().getName(), 2000);

        if ( commPort instanceof SerialPort )
        {
            SerialPort serialPort = (SerialPort) commPort;
            serialPort.setSerialPortParams(9600, SerialPort.DATABITS_7, SerialPort.STOPBITS_1, SerialPort.PARITY_EVEN);

            InputStream in = serialPort.getInputStream();
            OutputStream out = serialPort.getOutputStream();

            // Thread handling logic
            // https://www.javaspecialists.eu/archive/Issue056.html
            Thread readThread = new Thread(new SerialReader(in));
            readThread.start();
            Thread writeThread = new Thread(new SerialWriter(out));
            writeThread.start();

            // Running threads for 10 seconds then stopping to ensure threads are shutting down
            long threadCurrTime = System.currentTimeMillis();
            long threadStartTime = System.currentTimeMillis();
            while( (threadCurrTime - threadStartTime) < 10000)
            {
                try 
                {
                    Thread.sleep(100);
                }
                catch (InterruptedException ex)
                {
                    Logger.getLogger(TwoWaySerialComm.class.getName()).log(Level.SEVERE, null, ex); 
                }
                threadCurrTime = System.currentTimeMillis();
            }

            if(writeThread.isAlive())
            {
                //System.out.println("Sending interrupt to SerialWriter thread...");
                writeThread.interrupt();
                writeThread.join();
                //System.out.println("SerialWriter thread is shut down.");
            }
            //else
            //{
            //    System.out.println("SerialWriter thread is already shut down.");
            //}

            if(readThread.isAlive())
            {
                //System.out.println("Sending interrupt to SerialReader thread...");
                readThread.interrupt();
                readThread.join();
                //System.out.println("SerialReader thread is shut down.");
            }
            //else
            //{
            //    System.out.println("SerialReader thread is already shut down.");
            //}

            commPort.close();
        }
        else
        {
            System.out.println("Error: " + portName + " is not recognized as a valid serial device.");
        }
    }     
}

/* SerialReader thread logic */
public static class SerialReader implements Runnable 
{
    InputStream in;
    boolean bShuttingDown = false;

    public SerialReader ( InputStream in )
    {
        this.in = in;
    }

    public void run ()
    {
        byte[] RecvString = new byte[12];
        String strResponse;
        int len = -1;
        try
        {
            while (!bShuttingDown)
            {
                len = this.in.read(RecvString);

                if( len > -1 )
                {
                    strResponse = "";
                    if(RecvString[0] == 0x02 && RecvString[9] == 0x03)
                    {
                        if(RecvString[3] == 0x00 && RecvString[4] == 0x00 && RecvString[5] == 0x00 && RecvString[6] == 0x00)
                        {
                            strResponse = "Device not ready.";
                        }
                        else
                        {
                            //-- RecvString[3]------------------
                            if(RecvString[3] == 0x01)
                                strResponse = " - Idling";
                            else
                            if(RecvString[3] == 0x02)
                                strResponse = " - Accepting";
                            else
                            if(RecvString[3] == 0x04)
                            {
                                strResponse = " - Escrowed";

                                if(RecvString[5] == 0x08)
                                    strResponse += " $1";
                                else
                                if(RecvString[5] == 0x10)
                                    strResponse += " $2";
                                else
                                if(RecvString[5] == 0x18)
                                    strResponse += " $5";
                                else
                                if(RecvString[5] == 0x20)
                                    strResponse += " $10";
                                else
                                if(RecvString[5] == 0x28)
                                    strResponse += " $20";
                                else
                                    strResponse += " unrecognized bill inserted";
                            }
                            else
                            if(RecvString[3] == 0x08)
                                strResponse = " - Stacking";
                            else
                            if(RecvString[3] == 0x10)
                                strResponse = " - Stacked";
                            else
                            if(RecvString[3] == 0x11)
                                strResponse = " - Returning";
                            else
                            if(RecvString[3] == 0x12)
                                strResponse = " - Returned";

                            //-- RecvString[4]------------------
                            if(RecvString[4] == 0x01)
                                strResponse += " - Cheated";
                            else
                            if(RecvString[4] == 0x02)
                                strResponse += " - Rejected";
                            else
                            if(RecvString[4] == 0x04)
                                strResponse += " - Jammed";
                            else
                            if(RecvString[4] == 0x08)
                                strResponse += " - Bill Stacker Full";
                            else
                            if(RecvString[4] == 0x10)
                                strResponse += " - Removable Cassette Installed";
                            else
                            if(RecvString[4] == 0x11)
                                strResponse += " - Reserved";
                            else
                            if(RecvString[4] == 0x12)
                                strResponse += " - Calibration mode";

                            //-- RecvString[5]------------------
                            if(RecvString[5] == 0x01)
                                strResponse += " - Power Up Reset";
                            else
                            if(RecvString[5] == 0x02)
                                strResponse += " - Invalid Command";
                            else
                            if(RecvString[5] == 0x04)
                                strResponse += " - Non-recoverable fault";
                        }
                        if(!strResponse.contains("Idling"))
                            System.out.println("Recv: " + DatatypeConverter.printHexBinary(RecvString) + strResponse);
                    }
                    else
                    {
                        System.out.println("Recv (invalid): " + DatatypeConverter.printHexBinary(RecvString));
                    }

                    try
                    {
                        Thread.sleep(100); // need this delay before we send next polling message, otherwise the data doesn't come in correctly
                        bReadyToSend = true;
                    }
                    catch (InterruptedException ex)
                    {
                        Thread.currentThread().interrupt();
                        //System.out.println("Shut down SerialReader thread.");
                        bShuttingDown = true;
                        break;
                    }
                }
            }
        }
        catch ( IOException ex )
        {
            System.out.println("Recv exception: " + ex.toString());
        }            
    }
}

/* SerialWriter thread logic */
public static class SerialWriter implements Runnable 
{
    OutputStream out;
    long lastSendTime = System.currentTimeMillis() - 1001;
    long currSendTime;
    byte[] SendString = new byte[8];
    byte CheckDigit;
    int iSendStringCount;
    boolean bOddCount = true;
    boolean bShuttingDown = false;

    public SerialWriter ( OutputStream out )
    {
        this.out = out;
    }

    public void run ()
    {
        while(!bShuttingDown)
        {
            currSendTime = System.currentTimeMillis();
            if(currSendTime - lastSendTime > 1000) // if it's been more than a second, send query string
                bReadyToSend = true;

            if(bReadyToSend)
            {
                SendString[0] = (byte) 0x02; //STX
                SendString[1] = (byte) 0x08;
                if(bOddCount)
                {
                    SendString[2] = (byte) 0x10;
                    bOddCount = false;
                }
                else
                {
                    SendString[2] = (byte) 0x11;
                    bOddCount = true;
                }
                SendString[3] = (byte) 0x7F;
                SendString[4] = (byte) 0x1C;
                SendString[5] = (byte) 0x00;
                SendString[6] = (byte) 0x03; //ETX

                //CheckDigit skips STX (byte 0) with byte 1 as seed/initial value
                //To calculate the check digit, start with next byte (2)
                CheckDigit = SendString[1];
                iSendStringCount = 2;
                while(SendString[iSendStringCount] != 0x03)
                {
                    CheckDigit = (byte) (SendString[iSendStringCount]^CheckDigit); //XOR current CheckDigit value with next byte
                    iSendStringCount++;
                }
                iSendStringCount++; //advance one more so we don't overwrite ETX
                SendString[iSendStringCount] = (byte) CheckDigit;

                try
                {
                    lastSendTime = System.currentTimeMillis();
                    this.out.write(SendString);
                    //System.out.println("Sent: " + DatatypeConverter.printHexBinary(SendString));
                }
                catch ( IOException ex )
                {
                    System.out.println("Send exception: " + ex.toString());
                }

                try
                { 
                    Thread.sleep(1); // this is hear simply to catch an external interrupt
                }
                catch (InterruptedException ex)
                {
                    Thread.currentThread().interrupt();
                    //System.out.println("Shut down SerialWriter thread.");
                    bShuttingDown = true;
                    break;
                }
                bReadyToSend = false;
            }
        }
    }
}

public static void main ( String[] args )
{
    try
    {
        System.out.println("*****************************");
        System.out.println("*****  Starting Program *****");
        System.out.println("*****************************");

        String strUsbDeviceDir = "/dev/serial/by-id";
        File myUsbDeviceDir = new File(strUsbDeviceDir);
        if(myUsbDeviceDir.exists())
        {
           String[] myUsbDevices = myUsbDeviceDir.list();
           for(int i=0; i<myUsbDevices.length; i++)
           {
               if(myUsbDevices[i].contains("EBDS_over_USB"))
               {
                   File tempFile = new File(strUsbDeviceDir + "/" + myUsbDevices[i]);
                   String usbCanonicalName = tempFile.getCanonicalFile().toString(); //gives me /dev/ttyUSBx where 'x' is the USB device number
                   System.out.println("Connecting to " + usbCanonicalName + " (" + myUsbDevices[i] + ")");
                   (new TwoWaySerialComm()).connect(usbCanonicalName);
               }
               else
               {
                   System.out.println("Not using " + myUsbDevices[i]);
               }
           }
        }
    }
    catch ( Exception ex )
    {
        System.out.println("Connect exception: " + ex.toString());
    }

    System.out.println("*****************************");
    System.out.println("***** Program Finished ******");
    System.out.println("*****************************");
}

Output when I put a $1 bill in (I'm developing/compiling from NetBeans IDE 8.2 on Windows 10 Pro and running using remote debugging on the RPi3. I'm guessing that's the source of the RXTX Version mismatch warning): 当我放入一张1美元的钞票时输出(我正在Windows 10 Pro上从NetBeans IDE 8.2开发/编译,并在RPi3上使用远程调试运行。我猜这是RXTX版本不匹配警告的来源):

*****************************
*****  Starting Program *****
*****************************
Connecting to /dev/ttyUSB0 (usb-Silicon_Labs_Series_2000_Bill_Acceptor__EBDS_over_USB__46580120748-if00-port0)
Stable Library
=========================================
Native lib Version = RXTX-2.2pre2
Java lib Version   = RXTX-2.1-7
WARNING:  RXTX Version mismatch
    Jar version = RXTX-2.1-7
    native lib Version = RXTX-2.2pre2
Recv (invalid): 020000000000000000000000
Recv (invalid): 0B2001100800503603540000
Recv: 020B20041008005036035100 - Escrowed $1 - Removable Cassette Installed
*****************************
***** Program Finished ******
*****************************

I hope this description and sample code can help someone else. 我希望此描述和示例代码可以对其他人有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM