Android串口开发与硬件利用1K Xmodem协议远程升级

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

源码地址  https://download.csdn.net/download/one_bug/10574627

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

码大大

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值