我在Raspberry Pi 3上使用Raspbian。我正在学习如何使用Java编写代码(SE运行时版本1.8.0_65),我需要使用USB连接的Bill Acceptor传送原始数据。根据制造商的文档,USB单元模仿串行接口。当我插入设备时,它会显示在/dev/serial/by-id
中。我20多年前在SCO Unix设置上使用调制解调器编写了C代码。如果内存服务,我基本上将调制解调器/dev/ttyxx
视为用于读写的文件。我在10年前在Windows XP上编写了一个C ++程序,使用类似的Bill Acceptor单元(相同的制造商和型号)连接到串口而不是USB。文档显示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...
我是否遗漏了一些显而易见的事情,或者只是错过了这一切?谢谢你的任何建议!
答案 0 :(得分:0)
我能够使用RXTXComm library成功地与Raspberry Pi 3(运行Raspbian)的Bill Acceptor进行通信。使用9针串行/ RS232线束从旧的Windows XP计算机与设备通信时,我不得不使用9600/7 / E / 1。如果我没有将这些值用于setSerialPortParams(在我的示例代码中),我就无法正确发送/接收数据。因此,您需要知道设备的期望,以便将此方法与其他&#34; serial&#34;硬件通过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 ******
*****************************
我希望这个描述和示例代码可以帮助其他人。