1.首先说明一下Android里面请单文件的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2.串口的设置
/**
* 初始化设备列表
*/
private void initDevice() {
SerialPortFinder serialPortFinder = new SerialPortFinder();
// 设备
mDevices = serialPortFinder.getAllDevicesPath();
if (mDevices.length == 0) {
mDevices = new String[] {
getString(R.string.no_serial_device)
};
}
// 波特率
mBaudrates = getResources().getStringArray(baudrates);
mDeviceIndex = PrefHelper.getDefault().getInt(PreferenceKeys.SERIAL_PORT_DEVICES, 0);
mDeviceIndex = mDeviceIndex >= mDevices.length ? mDevices.length - 1 : mDeviceIndex;
mBaudrateIndex = PrefHelper.getDefault().getInt(PreferenceKeys.BAUD_RATE, 0);
mDevice = new Device("/dev/ttyS4", "115200");
}
2.数据的传输通讯
/**
* 打开串口
*
* @param device
* @return
*/
public SerialPort open(Device device) {
return open(device.getPath(), device.getBaudrate());
}
/**
* 打开串口
*
* @param devicePath
* @param baudrateString
* @return
*/
public SerialPort open(String devicePath, String baudrateString) {
if (mSerialPort != null) {
close();
}
try {
File device = new File(devicePath);
int baurate = Integer.parseInt(baudrateString);
mSerialPort = new SerialPort(device, baurate, 0);
mReadThread = new SerialReadThread(mSerialPort.getInputStream());
mReadThread.start();
mOutputStream = mSerialPort.getOutputStream();
mWriteThread = new HandlerThread("write-thread");
mWriteThread.start();
mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());
return mSerialPort;
} catch (Throwable tr) {
LogPlus.e(TAG, "打开串口失败", tr);
close();
return null;
}
}
/**
* 关闭串口
*/
public void close() {
if (mReadThread != null) {
mReadThread.close();
}
if (mOutputStream != null) {
try {
mOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mWriteThread != null) {
mWriteThread.quit();
}
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
}
/**
* 发送数据
*
* @param datas
* @return
*/
private void sendData(byte[] datas) throws Exception {
Log.e("正常发送数据","//.///"+new String(datas));
mOutputStream.write(datas);
}
/**
* (rx包裹)发送数据
*
* @param datas
* @return
*/
private Observable<Object> rxSendData(final byte[] datas) {
return Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
try {
sendData(datas);
emitter.onNext(new Object());
} catch (Exception e) {
LogPlus.e("发送:" + ByteUtil.bytes2HexStr(datas) + " 失败", e);
if (!emitter.isDisposed()) {
emitter.onError(e);
return;
}
}
emitter.onComplete();
}
});
}
/**
* 发送命令包
*/
public void sendCommand(final String command) {
// TODO: 2018/3/22
LogPlus.i("发送命令:" + command);
// byte[] bytes = ByteUtil.hexStr2bytes(command);
rxSendData(command.getBytes()).subscribeOn(mSendScheduler).subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object o) {
LogManager.instance().post(new SendMessage(command));
}
@Override
public void onError(Throwable e) {
LogPlus.e("发送失败", e);
}
@Override
public void onComplete() {
}
});
}
/**
* 读串口线程
*/
public class SerialReadThread extends Thread {
public SerialReadThread(InputStream is) {
mInputStream = new BufferedInputStream(is);
}
@Override
public void run() {
byte[] received = new byte[1024];
int size;
LogPlus.e("开始读线程");
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
try {
int available = mInputStream.available();
if (available > 0) {
size = mInputStream.read(received);
if (size > 0) {
Log.e("设备的数据","=========="+ByteUtil.hexStr2Str(ByteUtil.bytes2HexStr(received, 0, size)));
onDataReceive(received, size);
}
} else {
// 暂停一点时间,免得一直循环造成CPU占用率过高
SystemClock.sleep(1);
}
} catch (IOException e) {
LogPlus.e("读取数据失败", e);
}
//Thread.yield();
}
LogPlus.e("结束读进程");
}
/**
* 处理获取到的数据
*
* @param received
* @param size
*/
private void onDataReceive(byte[] received, int size) {
// TODO: 2018/3/22 解决粘包、分包等
hexStr =ByteUtil.hexStr2decimals(ByteUtil.bytes2HexStr(received, 0, size)) ;
LogPlus.e("获取的信息","==================="+ByteUtil.bytes2HexStr(received, 0, size));
LogManager.instance().post(new RecvMessage(ByteUtil.hexStr2Str(ByteUtil.bytes2HexStr(received, 0, size))));
}
/**
* 停止读线程
*/
public void close() {
try {
mInputStream.close();
} catch (IOException e) {
LogPlus.e("异常", e);
} finally {
super.interrupt();
}
}
}
3.1K Xmodem协议介绍
1K Xmodem文件传输协议并不需要向接收客户端发送待发送文件的名称 ,该文件将会被接收方授予一个局部有效的文件名。
要开始文件传输的过程,首先接收方向发送方发送一个轮询字符,来提示发送方客户端已经准备好了。如果接收方支持check-sum (CRC)校验,则轮询字符可以用0x43 (ASCII 字母 C);如果接收方只支持旧的checksum (求总和)校验,则轮询字符可以用0x15 (NACK线控制字符)。
发送方响应接收方的启动字符,开始发送文件数据包,每个包都是1024字节的固定长度。如果最后一个文件数据包不足1024字节,则利用NACK(0x1A)填充到1024字节。接收方通过ACK(正常接收)或者NACK(非正接收)来响应每一个包。
如果发送方收到ACK,则发送下一个包知道文件传输完成;乳沟发送方收到NACK,则重新发送上一个包。
当发送方发送完成后,发送EOT通知接收方文件传输结束;接收方发送ACK来响应;发送方收到ACK后则文件传输完成。
文件数据包的结构:
<SOH><包序号><包序号反码><文件数据(1024字节)><校验位>
0x01 | SOH: 包头 |
0x04 | EOT: 结束文件传输 |
0x06 | ACK: 正常响应,如:数据包正确接收 |
0x15 | NACK: 非正常响应 |
0x18 | CAN: 取消文件传输 |
0x43 | C: ASCII字符C |
4.1K Xmodem的流程
/**/
protected static final byte SOH = 0x01; /* Start Of Header */
protected static final byte STX = 0x02; /* Start Of Text (used like SOH but means 1024 block size) */
protected static final byte EOT = 0x04; /* End Of Transmission */
protected static final byte ACK = 0x06; /* ACKnowlege */
protected static final byte NAK = 0x15; /* Negative AcKnowlege */
protected static final byte CAN = 0x18; /* CANcel character */
protected static final byte CPMEOF = 0x1A;
protected static final byte ST_C = 'C';
protected static final int MAXERRORS = 10;
protected static final int BLOCK_TIMEOUT = 1000;
protected static final int REQUEST_TIMEOUT = 3000;
protected static final int WAIT_FOR_RECEIVER_TIMEOUT = 30000;
protected static final int SEND_BLOCK_TIMEOUT = 30000;
/**
* Wait for receiver request for transmission
*
* @param timer
* @return TRUE if receiver requested CRC-16 checksum, FALSE if 8bit checksum
* @throws java.io.IOException
*/
protected boolean waitReceiverRequest(Timer timer) throws IOException {
String character;
while (true) {
try {
character = readByte(timer);
if (character.equals(String.valueOf(NAK))) {
hexStr = null;
return false;
}
if (character.equals(String.valueOf(ST_C))) {
hexStr=null;
return true;
}
} catch (TimeoutException e) {
throw new IOException("Timeout waiting for receiver");
}
}
}
/**
* Send a file. <br/>
* <p>
* This method support correct thread interruption, when thread is interrupted "cancel of transmission" will be send.
* So you can move long transmission to other thread and interrupt it according to your algorithm.
*
* @param file
* @throws IOException
*/
public void send(final String file) throws IOException {
new Thread(){
@Override
public void run() {
super.run();
try (DataInputStream dataStream = new DataInputStream(new FileInputStream(file))) {
Timer timer = new Timer(WAIT_FOR_RECEIVER_TIMEOUT).start();
boolean useCRC16 = waitReceiverRequest(timer);
if (useCRC16){
CRC crc= new CRC16();
byte[] block = new byte[1024];
sendDataBlocks(dataStream, 1, crc, block);
sendEOT();
Log.e("传输完成","++++++数据升级成功+++++");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
//open file
}
protected void sendDataBlocks(DataInputStream dataStream, int blockNumber, CRC crc, byte[] block) throws IOException {
int dataLength;
while ((dataLength = dataStream.read(block)) != -1) {
sendBlock(blockNumber++, block, dataLength, crc);
}
}
protected void sendEOT() throws IOException {
int errorCount = 0;
Timer timer = new Timer(BLOCK_TIMEOUT);
int character;
while (errorCount < 10) {
sendByte(EOT);
try {
character = Integer.parseInt(readByte(timer.start()));
if (character == ACK) {
hexStr=null;
return;
} else if (character == CAN) {
hexStr=null;
throw new IOException("Transmission terminated");
}
} catch (TimeoutException ignored) {
}
errorCount++;
}
}
protected void sendBlock(int blockNumber, byte[] block, int dataLength, CRC crc) throws IOException {
int errorCount;
int character;
Timer timer = new Timer(SEND_BLOCK_TIMEOUT);
if (dataLength < block.length) {
block[dataLength] = CPMEOF;
}
errorCount = 0;
while (errorCount < MAXERRORS) {
timer.start();
if (block.length == 1024) {
mOutputStream.write(STX);
}else { //128
mOutputStream.write(SOH);
}
mOutputStream.write(blockNumber);
Log.e("包序号","==---blockNumber--==="+blockNumber);
mOutputStream.write(~blockNumber);
Log.e("包反序号","==---~blockNumber--==="+~blockNumber);
mOutputStream.write(block);
Log.e("发送设备的数据","==---block--==="+ByteUtil.bytes2HexStr(block)+block.length);
writeCRC(block, crc);
mOutputStream.flush();
while (true) {
try {
character = Integer.parseInt(readByte(timer));
if (character == ACK) {
hexStr=null;
return;
} else if (character == NAK) {
hexStr=null;
Log.e("收到错误数据","+++++++++++"+character+"====NAK===="+NAK);
errorCount++;
break;
} else if (character == CAN) {
hexStr=null;
throw new IOException("Transmission terminated");
}
} catch (TimeoutException e) {
errorCount++;
break;
}
}
}
throw new IOException("Too many errors caught, abandoning transfer");
}
private void writeCRC(byte[] block, CRC crc) throws IOException {
byte[] crcBytes = new byte[crc.getCRCLength()];
long crcValue = crc.calcCRC(block);
for (int i = 0; i < crc.getCRCLength(); i++) {
crcBytes[crc.getCRCLength() - i - 1] = (byte) ((crcValue >> (8 * i)) & 0xFF);
}
Log.e("发送设备的数据","==---crcBytes--==="+ByteUtil.bytes2HexStr(crcBytes));
mOutputStream.write(crcBytes);
}
protected void sendByte(byte b) throws IOException {
Log.e("传输数据","++++++回去包尾+++++"+EOT);
mOutputStream.write(b);
mOutputStream.flush();
}
private void shortSleep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
try {
interruptTransmission();
} catch (IOException ignore) {
}
throw new RuntimeException("Transmission was interrupted", e);
}
}
/**
* send CAN to interrupt seance
*
* @throws IOException
*/
protected void interruptTransmission() throws IOException {
sendByte(CAN);
sendByte(CAN);
}
private long readCRC(CRC crc) throws IOException {
long checkSumma = 0;
for (int j = 0; j < crc.getCRCLength(); j++) {
checkSumma = (checkSumma << 8) + mInputStream.read();
}
return checkSumma;
}
private String readByte(Timer timer) throws IOException, TimeoutException {
while (true) {
if (hexStr!=null) {
return hexStr;
}
if (timer.isExpired()) {
throw new TimeoutException();
}
shortSleep();
}
}