java - 使用 Java 在 Raspberry Pi 3 上读取和写入 USB 设备

标签 java linux usb raspberry-pi3 raspbian

我在 Raspberry Pi 3 上使用 Raspbian。我正在学习如何使用 Java(SE 运行时版本 1.8.0_65)进行编码,并且我需要与 USB 连接的 Bill Acceptor 通信原始数据。根据制造商的文档,USB 单元模拟串行接口(interface)。当我插入设备时,它出现在 /dev/serial/by-id 中。 20 多年前,我在 SCO Unix 设置上使用调制解调器编写了 C 代码。如果没记错的话,我基本上把调制解调器 /dev/ttyxx 当作一个文件来读写。 10 多年前,我使用连接到串行端口而不是 USB 的类似 Bill Acceptor 单元(相同的制造商和型号)在 Windows XP 上编写了一个 C++ 程序。文档显示 USB 和串行单元都使用相同的数据字符串进行输入和输出,因此我知道 SendString 数据应该正确才能触发有效响应。但是,我没有收到设备的回复,所以我猜我没有正确连接到它。

这是代码...

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());
    }
}

这是我得到的输出...

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

我是不是遗漏了一些明显的东西,或者只是做错了?感谢您的任何建议!

最佳答案

我能够使用 RXTXComm library 从 Raspberry Pi 3(运行 Raspbian)成功地与 Bill Acceptor 通信。 .当使用 9 针串口/RS232 线束从旧的 Windows XP 计算机与设备通信时,我不得不使用 9600/7/E/1。如果我不将这些值用于 setSerialPortParams(在我的示例代码中),我将无法正确发送/接收数据。因此,您需要知道设备期望什么,以便将此方法与通过 USB 连接的其他“串行”硬件一起使用。下面是具有基本功能的代码。我从 RXTXComm 库附带的示例程序之一开始,并从那里构建。由于这是一个示例程序,我还没有添加逻辑来验证来自设备的数据的校验和数字。

您可能会注意到在代码的底部,我能够使用从 /dev/serial/by-id 中提取的设备的规范名称来确定/dev/ttyUSBx > 目录/文件夹。无论设备插入哪个 USB 端口或系统初始化 USB 设备的顺序如何,它都能找到正确的设备名称。我有一个发卡器,它也是一个 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("*****************************");
}

当我投入 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 ******
*****************************

我希望这个描述和示例代码可以帮助到其他人。

关于java - 使用 Java 在 Raspberry Pi 3 上读取和写入 USB 设备,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50494456/

相关文章:

java - 如何从 Spring Security 的 CustomUser 获取用户 ID

java - Quartz Scheduler - Cron 触发器表达式不起作用

linux - Docker - EACCES : permission denied, mkdir '/app/node_modules/.cache'

linux - 将USB连接到linux emb设备时如何打开localhost

.net - 通过 MTP C#/VB.net 将文件写入 WPD 设备

c - 从 usb 读取作为一个 comport

java - Android 从 url 播放 mp3

linux - Bash 输出重定向的问题

linux - 为什么在停止 Apache 服务后,我的 IP 地址仍然可以看到 Apache 默认页面?

java - 使用 if else 语句调用不同的类