Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > Android读取串口数据

Android读取串口数据的操作指南

作者:Crazy_MT

在Android系统上读取串口数据是一个常见的需求,特别是当我们需要与硬件设备进行通信时,本文给大家介绍了Android读取串口数据的操作指南,文中有详细的步骤和代码示例,帮助你更好地理解和实现串口通信,需要的朋友可以参考下

在Android系统上读取串口数据是一个常见的需求,特别是当我们需要与硬件设备进行通信时。通过开源项目 Crazy-MT/SerialAssistant,我们可以快速了解如何在Android上实现这一功能。以下是详细的步骤和代码示例,帮助你更好地理解和实现串口通信。

一、确定串口号和波特率

1. 查找设备文件

在Linux系统中(Android基于Linux),串口设备通常表示为 /dev/ttySx 或 /dev/ttyUSBx,其中 x 是数字。例如,/dev/ttyS0 代表第一个串口,/dev/ttyUSB0 代表第一个USB串口适配器。

2. 通过文件系统查看可用串口

你可以在Android设备的终端中使用 ls /dev/tty* 命令来查看可用的串口设备文件。使用ADB(Android Debug Bridge)来连接和访问设备终端:

adb shell
ls /dev/tty*

二、确定波特率

波特率是串口通信的速率,单位是波特(baud)。常见的波特率有 9600、19200、38400、57600、115200 等。波特率的选择通常由串口设备的规格或协议决定。你需要查阅设备手册或与设备供应商确认。

三、读取串口数据

在选择正确的串口号和波特率后,可以通过输入流读取串口数据。以下是一个读取线程的示例代码:

private class ReadThread extends Thread {
    @Override
    public void run() {
        super.run();
        while(!isInterrupted()) {
            try
            {
                if (mInputStream == null) return;
                int size = mInputStream.read(tempBuff);
                if (size > 0){
                    onDataReceived(Arrays.copyOfRange(tempBuff, 0, size));
                }
                try
                {
                    Thread.sleep(10);//延时10ms
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            } catch (Throwable e)
            {
                e.printStackTrace();
                return;
            }
        }
    }
}

四、数据包处理

以某品牌的电子秤为例,其数据协议如下:

取重

1、主动/被动模式的数据格式相同。
2、上位机指令(HEX): 1b 01 02
3、数据格式:(总共 24 字节)

01 02  000.000kg 000.000kg sta  X   03 04
数据头  净重       皮重      状态 校验 数据尾

SHead1        SOH(01H)   1 字节,标题开始
SHead2        STX(02H)   1 字节,正文开始
Weight 1      XXX.XXX    7 字节,净重。
Weight Units  U1U0       2 字节,重量单位。如“kg”
Weight2       XXX.XXX    7 字节,皮重。
Weight Units  U1U0       2 字节,重量单位。如“kg”
Status        STA        1 字节,状态
Check Sum     BCC        1 字节,使用 BCC 算法,除 SOH STX ETX EOT 及本字节外所有字符的 BCC 校验。
Tail1         ETX(03H)   1 字节,标题结束
Tail2         EOT(04H)   1 字节,传输结束

重量格式(净重/皮重),例如:
123.456kg
23.456kg
12.3456kg
0.012kg
-12.345kg
-1.234kg
-0.0001kg
(前面无数据则用空格填充。如果小数点后面有四位,则为精确到 0.1g)

状态:
bit7:1 重量溢出;0 重量正常
bit6:1 开机后未归零(开机时秤盘上有重物);0 开机后已归零
bit5:1 当前在去皮模式;0 当前不是    去皮模式
bit4:1 当前重量为 0;0 当前重量不为 0 
bit3:1 重量稳定;0 重量不稳定 
bit2~bit0  0

在串口通信中,处理数据包并确保数据的完整性是一项重要的任务。在这篇博客中,我们将探讨如何使用 Java 通过串口读取数据,并确保每条读到的数据都是完整的。我们将介绍如何设计一个系统来处理数据包,包括数据包解析和验证的逻辑。

五、数据包解析类

定义一个抽象数据包类 Packet:

public abstract class Packet {
    protected byte[] data;

    public Packet(byte[] data) {
        this.data = data;
    }

    public byte[] getData() {
        return data;
    }

    public abstract String getNetWeight();
    public abstract String getTareWeight();
    public abstract byte getStatus();
}

实现具体的数据解析类 DefaultPacket:

public class DefaultPacket extends Packet {

    public DefaultPacket(byte[] data) {
        super(data);
    }

    @Override
    public String getNetWeight() {
        return new String(data, 2, 7);
    }

    @Override
    public String getTareWeight() {
        return new String(data, 11, 7);
    }

    @Override
    public byte getStatus() {
        return data[20];
    }

    public static String parseStatus(byte status) {
        StringBuilder sb = new StringBuilder();
        sb.append("Weight Overflow: ").append((status & 0x80) != 0).append("\n");
        sb.append("Not Zeroed on Power-up: ").append((status & 0x40) != 0).append("\n");
        sb.append("Tare Mode: ").append((status & 0x20) != 0).append("\n");
        sb.append("Weight is Zero: ").append((status & 0x10) != 0).append("\n");
        sb.append("Weight Stable: ").append((status & 0x08) != 0).append("\n");
        return sb.toString();
    }
}

六、数据包解析接口和实现类

定义数据包解析接口 PacketParser:

public interface PacketParser {
    int getDataLength();
    boolean isValid(byte[] data);
    boolean checkChecksum(byte[] data);
    Packet parse(byte[] data);
}

具体数据包解析类

DefaultPacketParser 实现了具体的数据包解析和验证逻辑:

public class DefaultPacketParser implements PacketParser {
    @Override
    public int getDataLength() {
        return 24;
    }

    @Override
    public boolean isValid(byte[] data) {
        return data[0] == 0x01 && data[1] == 0x02 && data[22] == 0x03 && data[23] == 0x04;
    }

    @Override
    public boolean checkChecksum(byte[] data) {
        byte checksum = 0;
        for (int i = 2; i < 21; i++) {
            checksum ^= data[i];
        }
        return checksum == data[21];
    }

    @Override
    public Packet parse(byte[] data) {
        return new DefaultPacket(data);
    }
}

七、数据包输入流类

PacketInputStream 类使用 PacketParser 来处理数据包的解析和验证,并累积无效数据:

import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

public class PacketInputStream extends FilterInputStream {
    private PacketParser parser;
    private byte[] buffer;
    private int bufferPos = 0;
    private ByteArrayOutputStream byteArrayBuffer = new ByteArrayOutputStream();

    public PacketInputStream(InputStream in, PacketParser parser) {
        super(in);
        this.parser = parser;
        this.buffer = new byte[parser.getDataLength()];
    }

    public Packet readPacket() throws IOException {
        // 将上次剩余的无效数据写入缓冲区
        if (byteArrayBuffer.size() > 0) {
            byte[] invalidData = byteArrayBuffer.toByteArray();
            System.arraycopy(invalidData, 0, buffer, 0, invalidData.length);
            bufferPos = invalidData.length;
            byteArrayBuffer.reset();
        }

        while (bufferPos < parser.getDataLength()) {
            int read = in.read(buffer, bufferPos, parser.getDataLength() - bufferPos);
            if (read == -1) {
                return null; // EOF reached
            }
            bufferPos += read;
        }

        int start = findPacketStart(buffer);
        while (start == -1 && bufferPos >= 2) {
            System.arraycopy(buffer, 1, buffer, 0, bufferPos - 1);
            bufferPos--;
            int read = in.read(buffer, bufferPos, 1);
            if (read == -1) {
                return null; // EOF reached
            }
            bufferPos += read;
            start = findPacketStart(buffer);
        }

        if (start != 0) {
            byte[] remainingData = Arrays.copyOfRange(buffer, start, bufferPos);
            System.arraycopy(remainingData, 0, buffer, 0, remainingData.length);
            bufferPos = remainingData.length;
            return null;
        }

        if (!parser.isValid(buffer)) {
            byteArrayBuffer.write(buffer, 0, bufferPos);
            bufferPos = 0;
            return null; // 返回 null 表示无效数据包
        }

        if (!parser.checkChecksum(buffer)) {
            byteArrayBuffer.write(buffer, 0, bufferPos);
            bufferPos = 0;
            return null; // 返回 null 表示校验失败
        }

        Packet packet = parser.parse(Arrays.copyOf(buffer, parser.getDataLength()));
        bufferPos = 0;
        return packet;
    }

    private int findPacketStart(byte[] data) {
        for (int i = 0; i < data.length - 1; i++) {
            if (data[i] == 0x01 && data[i + 1] == 0x02) {
                return i;
            }
        }
        return -1;
    }
}

八、读取线程类

ReadThread 使用 PacketInputStream 和 PacketParser 来读取和处理数据包:

import java.io.InputStream;

private class ReadThread extends Thread {
    private PacketInputStream packetInputStream;

    public ReadThread(InputStream inputStream, PacketParser parser) {
        this.packetInputStream = new PacketInputStream(inputStream, parser);
    }

    @Override
    public void run() {
        super.run();
        while (!isInterrupted()) {
            try {
                Packet packet = packetInputStream.readPacket();
                if (packet != null) {
                    if (packet instanceof DefaultPacket) {
                        onDataReceived((DefaultPacket) packet);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        }
    }

    private void onDataReceived(DefaultPacket packet) {
        System.out.println("Net Weight: " + packet.getNetWeight());
        System.out.println("Tare Weight: " + packet.getTareWeight());
        System.out.println("Status: " + DefaultPacket.parseStatus(packet.getStatus()));
    }
}

总结

通过抽象数据包解析逻辑,我们可以更好地处理串口数据包的完整性问题。我们定义了数据包类 Packet 和 DefaultPacket,并使用 PacketParser 接口来实现数据包的解析和验证。PacketInputStream 类负责处理数据包的读取和无效数据的累积,而 ReadThread 负责读取和处理有效数据包。这种设计使代码更加模块化、易于维护和扩展,可以很容易地适应不同格式的数据包。

以上就是Android读取串口数据的操作指南的详细内容,更多关于Android读取串口数据的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文