Android讀取串口數(shù)據(jù)的操作指南
在Android系統(tǒng)上讀取串口數(shù)據(jù)是一個(gè)常見的需求,特別是當(dāng)我們需要與硬件設(shè)備進(jìn)行通信時(shí)。通過(guò)開源項(xiàng)目 Crazy-MT/SerialAssistant,我們可以快速了解如何在Android上實(shí)現(xiàn)這一功能。以下是詳細(xì)的步驟和代碼示例,幫助你更好地理解和實(shí)現(xiàn)串口通信。
一、確定串口號(hào)和波特率
1. 查找設(shè)備文件
在Linux系統(tǒng)中(Android基于Linux),串口設(shè)備通常表示為 /dev/ttySx 或 /dev/ttyUSBx,其中 x 是數(shù)字。例如,/dev/ttyS0 代表第一個(gè)串口,/dev/ttyUSB0 代表第一個(gè)USB串口適配器。
2. 通過(guò)文件系統(tǒng)查看可用串口
你可以在Android設(shè)備的終端中使用 ls /dev/tty* 命令來(lái)查看可用的串口設(shè)備文件。使用ADB(Android Debug Bridge)來(lái)連接和訪問(wèn)設(shè)備終端:
adb shell ls /dev/tty*
二、確定波特率
波特率是串口通信的速率,單位是波特(baud)。常見的波特率有 9600、19200、38400、57600、115200 等。波特率的選擇通常由串口設(shè)備的規(guī)格或協(xié)議決定。你需要查閱設(shè)備手冊(cè)或與設(shè)備供應(yīng)商確認(rèn)。
三、讀取串口數(shù)據(jù)
在選擇正確的串口號(hào)和波特率后,可以通過(guò)輸入流讀取串口數(shù)據(jù)。以下是一個(gè)讀取線程的示例代碼:
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);//延時(shí)10ms } catch (InterruptedException e) { e.printStackTrace(); } } catch (Throwable e) { e.printStackTrace(); return; } } } }
四、數(shù)據(jù)包處理
以某品牌的電子秤為例,其數(shù)據(jù)協(xié)議如下:
取重 1、主動(dòng)/被動(dòng)模式的數(shù)據(jù)格式相同。 2、上位機(jī)指令(HEX): 1b 01 02 3、數(shù)據(jù)格式:(總共 24 字節(jié)) 01 02 000.000kg 000.000kg sta X 03 04 數(shù)據(jù)頭 凈重 皮重 狀態(tài) 校驗(yàn) 數(shù)據(jù)尾 SHead1 SOH(01H) 1 字節(jié),標(biāo)題開始 SHead2 STX(02H) 1 字節(jié),正文開始 Weight 1 XXX.XXX 7 字節(jié),凈重。 Weight Units U1U0 2 字節(jié),重量單位。如“kg” Weight2 XXX.XXX 7 字節(jié),皮重。 Weight Units U1U0 2 字節(jié),重量單位。如“kg” Status STA 1 字節(jié),狀態(tài) Check Sum BCC 1 字節(jié),使用 BCC 算法,除 SOH STX ETX EOT 及本字節(jié)外所有字符的 BCC 校驗(yàn)。 Tail1 ETX(03H) 1 字節(jié),標(biāo)題結(jié)束 Tail2 EOT(04H) 1 字節(jié),傳輸結(jié)束 重量格式(凈重/皮重),例如: 123.456kg 23.456kg 12.3456kg 0.012kg -12.345kg -1.234kg -0.0001kg (前面無(wú)數(shù)據(jù)則用空格填充。如果小數(shù)點(diǎn)后面有四位,則為精確到 0.1g) 狀態(tài): bit7:1 重量溢出;0 重量正常 bit6:1 開機(jī)后未歸零(開機(jī)時(shí)秤盤上有重物);0 開機(jī)后已歸零 bit5:1 當(dāng)前在去皮模式;0 當(dāng)前不是 去皮模式 bit4:1 當(dāng)前重量為 0;0 當(dāng)前重量不為 0 bit3:1 重量穩(wěn)定;0 重量不穩(wěn)定 bit2~bit0 0
在串口通信中,處理數(shù)據(jù)包并確保數(shù)據(jù)的完整性是一項(xiàng)重要的任務(wù)。在這篇博客中,我們將探討如何使用 Java 通過(guò)串口讀取數(shù)據(jù),并確保每條讀到的數(shù)據(jù)都是完整的。我們將介紹如何設(shè)計(jì)一個(gè)系統(tǒng)來(lái)處理數(shù)據(jù)包,包括數(shù)據(jù)包解析和驗(yàn)證的邏輯。
五、數(shù)據(jù)包解析類
定義一個(gè)抽象數(shù)據(jù)包類 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(); }
實(shí)現(xiàn)具體的數(shù)據(jù)解析類 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(); } }
六、數(shù)據(jù)包解析接口和實(shí)現(xiàn)類
定義數(shù)據(jù)包解析接口 PacketParser:
public interface PacketParser { int getDataLength(); boolean isValid(byte[] data); boolean checkChecksum(byte[] data); Packet parse(byte[] data); }
具體數(shù)據(jù)包解析類
DefaultPacketParser 實(shí)現(xiàn)了具體的數(shù)據(jù)包解析和驗(yàn)證邏輯:
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); } }
七、數(shù)據(jù)包輸入流類
PacketInputStream 類使用 PacketParser 來(lái)處理數(shù)據(jù)包的解析和驗(yàn)證,并累積無(wú)效數(shù)據(jù):
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 { // 將上次剩余的無(wú)效數(shù)據(jù)寫入緩沖區(qū) 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 表示無(wú)效數(shù)據(jù)包 } if (!parser.checkChecksum(buffer)) { byteArrayBuffer.write(buffer, 0, bufferPos); bufferPos = 0; return null; // 返回 null 表示校驗(yàn)失敗 } 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 來(lái)讀取和處理數(shù)據(jù)包:
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())); } }
總結(jié)
通過(guò)抽象數(shù)據(jù)包解析邏輯,我們可以更好地處理串口數(shù)據(jù)包的完整性問(wèn)題。我們定義了數(shù)據(jù)包類 Packet 和 DefaultPacket,并使用 PacketParser 接口來(lái)實(shí)現(xiàn)數(shù)據(jù)包的解析和驗(yàn)證。PacketInputStream 類負(fù)責(zé)處理數(shù)據(jù)包的讀取和無(wú)效數(shù)據(jù)的累積,而 ReadThread 負(fù)責(zé)讀取和處理有效數(shù)據(jù)包。這種設(shè)計(jì)使代碼更加模塊化、易于維護(hù)和擴(kuò)展,可以很容易地適應(yīng)不同格式的數(shù)據(jù)包。
以上就是Android讀取串口數(shù)據(jù)的操作指南的詳細(xì)內(nèi)容,更多關(guān)于Android讀取串口數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android通過(guò)HTTP協(xié)議實(shí)現(xiàn)上傳文件數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)HTTP協(xié)議實(shí)現(xiàn)上傳文件數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android實(shí)現(xiàn)彈出列表、單選、多選框
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)彈出列表、單選、多選框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10Android編程開發(fā)之RadioGroup用法實(shí)例
這篇文章主要介紹了Android編程開發(fā)之RadioGroup用法,結(jié)合實(shí)例形式分析了Android中RadioGroup單選按鈕的具體使用技巧,需要的朋友可以參考下2015-12-12Flutter實(shí)現(xiàn)頂部導(dǎo)航欄功能
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)頂部導(dǎo)航欄功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07基于Android開發(fā)支持表情的實(shí)現(xiàn)詳解
本篇文章是對(duì)在Android開發(fā)中支持表情的實(shí)現(xiàn)代碼進(jìn)行了介紹。需要的朋友參考下2013-05-05