ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Android串口通信从入门到实战:UART、RS485与Modbus协议详解

Android串口通信从入门到实战:UART、RS485与Modbus协议详解 简介这是一个面向Android开发者的串口通信简化Demo基于android-serialport-api精简改编解决原版Demo常见不可用的问题仅保留一个Activity适合需要快速集成串口收发的项目或个人学习。资源包共43个文件、约78KB包含5个Java源文件、1个C底层文件、5份XML配置、6张PNG图标、预编译SO库及可直接安装的APK另有class、dex、project等工程中间文件与MK构建脚本从源码到安装包形成完整闭环目录结构清晰便于导入和二次开发。目前已有1551人学习下载。借助该Demo可以直观理解Android串口通讯中Java层与JNI层的调用流程开箱即用省去NDK环境搭建和交叉编译的繁琐步骤同时可参照原生C层与Activity代码修改串口路径、波特率、数据位、停止位与校验位等核心参数对串口移植、硬件调试以及上层通讯功能开发都有直接参考价值。对于刚接触串口编程的开发者这一份极简工程也能帮助看清串口打开、读写与关闭的完整实现链路减少踩坑。 Android串口通信这个需求最近找我咨询的人特别多。开发智能硬件、对接单片机、调试工控设备都离不开这个基础能力。网上相关的帖子零散得很很多文章看了也只是反复在讲如何引入一个第三方库真上手做项目时遇到的问题一个都没说清楚。今天把这个demo从选型到落地的完整过程拆开来讲包括哪些坑必须绕开、哪些参数必须留意。先一句话说清楚这东西能干什么Android设备通过串口协议和外部设备通信比如STM32、PLC、传感器模块、RS485总线设备或者带有UART接口的各种控制板。它解决的是Android系统和硬件设备之间建立物理链路、完成双向数据交互的问题。适合刚接触Android硬件开发的人也适合那些已经被各种串口库绕晕了的老手。1. 项目要解决什么问题为什么这个通道不能省1.1 Android设备对接硬件的三条路串口的不可替代性Android和外设通信常见的物理通道有蓝牙、WiFi、串口三种。蓝牙方便但协议栈复杂配对流程、连接状态管理、传输稳定性都要自己兜底。WiFi则要搭网络环境设备端得有TCP/IP协议栈的接入能力成本直接拉高。串口的优势在于协议足够底层、物理连接简单、延迟可控尤其对接工业设备和单片机的场景里很多控制器压根没有网络协议栈UART就是标配。这也就解释了为什么STM32、PLC、各类传感器模块的调试接口大多数都是串口。对Android开发来说串口虽然“原始”却是一条绕不开的硬通道。1.2 Android系统对串口的天然隔离权限是第一步Android基于Linux内核串口设备在系统中对应/dev/ttyS*、/dev/ttyUSB*这种节点文件。理论上有读写权限就能打开但真实情况是普通应用根本拿不到这些节点的访问权限。没有root的设备/dev/ttyS0这类节点的owner是root或系统用户应用直接打开会抛Permission denied。业界常用方案是在初始化脚本里给串口节点设置666权限例如修改/dev/ttyS0的权限为chmod 666 /dev/ttyS0或者在系统启动脚本里加入权限修改逻辑。还有一个常规做法是让应用运行在具备系统权限的进程中。开发demo时在root过的设备上或定制系统中通过su执行chmod命令即可先跑通流程后续上生产再考虑更稳妥的权限策略。2. 串口通信的核心细节参数不对神仙也调不通2.1 波特率、数据位、停止位、校验位一个都不能错串口通信中两端设备的四个参数必须完全一致少了任何一步匹配都收不到正确数据。波特率是每秒钟传输的比特数常见的有9600、115200。数据位通常选8停止位选1。校验位常见为NONE。这两个参数直接影响通信成败。等调试时发现自己收了一堆乱码第一反应一定是查波特率两端是否一致。115200能跑到但物理链路质量差时选9600更稳。工业现场往往有强电干扰RS485总线场景中更低的波特率意味着抗干扰能力更强这个选择决定了通信质量的上限。2.2 TTL、RS232、RS485三种电平别接错接口烧了板子Android的调试板和USB转串口模块引出的一般是TTL电平3.3V或5V。RS232是正负电压逻辑RS485则使用差分信号。三者物理电平不兼容接错就是烧芯片。做demo时最常用的USB转TTL模块是CH340和FTDI操作实践上要先确认模块的工作电压和主板的串口电平一致。如果目标设备是RS485总线需要加一个RS485转TTL的模块不能直接把USB转TTL接到RS485总线上。这里补充一句选型时的经验CH340驱动安装简单Windows和Linux下都方便下载时注意去正规渠道获取防止网上下到捆绑安装包的驱动。FTDI的驱动相对更全但价格贵一些兼容性也更好。CP2102也是常见选择一般开发调试用CH340就足够了。2.3 UART只是物理通道协议才是通信的灵魂串口只管把字节流从一个点搬到另一个点至于这些字节怎么解析由两端的协议约定。这就像快递只负责把包裹运到门口包裹里装着什么东西、怎么开箱是寄件人和收件人之间的约定。写demo时需要自定义一个简单的通信协议比如一帧数据包含帧头、长度、数据、校验值。帧头常用0xAA或0x55长度指明后面的数据字节数校验用来检测数据在传输过程中是否损坏。基于这一层约定才能判断收上来的数据是不是完整的一帧。3. 完整实现过程从零到能收发数据3.1 硬件准备用最小成本搭起调试环境搭一套串口调试环境需要的硬件无非这么几样一台Android设备最好是支持OTG且能root的或者直接用开发板一根OTG线一块USB转TTL模块和一个串口调试工具。总成本不过二三十块钱就能把开发环境搭建起来。调试目标如果手头没有STM32可以先用USB转TTL模块的TX和RX短接自己发给自己的数据拿来验证串口收发逻辑是否正常。这个“自发自收”的办法非常实用能快速排除硬件层面的问题特别适合阶段性的功能验证。3.2 Demo代码实现核心逻辑其实就三步先看打开串口的动作。Android中打开串口本质上就是打开一个文件描述符再通过ioctl函数配置串口参数之后就能读写这个文件描述符。核心调用逻辑简洁得很// 伪代码示意实际实现建议直接基于JNI封装 val fd FileInputStream(file).fd val serialPort SerialPort(fd) // 封装类内部完成open和configureconfigure阶段需要设置波特率、数据位、停止位、校验位背后用的是Linux的termios结构体。这些参数配置完成后读写就是普通的流操作。读写要放在独立线程里处理。串口通信数据是持续流入的如果放到主线程界面会被消息风暴卡死体验直接归零。标准做法是开一个收数据线程用while循环阻塞读读到的数据回调到主线程更新UI。关闭串口同样要严谨不需要时把文件描述符释放掉否则下次打开会失败。有一段时间我在demo里不停地打开关闭串口出现过资源耗尽的情况正是因为这个细节没处理好。3.3 核心技术库选型用成熟方案还是自己封装串口库的常见方案有google官方仓库里的android-serialport-api和民间活跃维护的licheedev/Android-SerialPort以及基于Modbus协议的专用库。android-serialport-api已经比较老但胜在稳定简单适合想深入了解原理的人去读源码。licheedev版本的封装更舒服带自动重连、数据回调封装等上手效率高。选型依据很简单只是做工具类app选封装好的直接跑如果是要在嵌入式板卡上定制系统最好自己基于JNI封装能把控制权牢牢握在手里。4. 与设备对接时的实战配置从STM32到RS4854.1 和STM32对接的调参套路STM32串口调试时建议用串口调试助手先把板子的收发调通确认板子的输出数据格式再让Android端去适配。否则两边同时排查定位问题的时间直接翻倍。这部分的关键是明确日志输出格式。MCU发上来的数据一般是Hex格式的字节数组比如01 03 02 00 7F这种调试助手里显示Hex、ASCII还是UTF-8字符串直接影响报文判断。很多人在板子上发的是HexAndroid端却用字符串解析结果收到一堆乱码这个对齐工作值得做在前面。4.2 RS485总线场景Android设备的接入方式RS485总线连接多个设备Android设备要接入得先过一个485转TTL模块。模块的A、B线并联到485总线上另一端输出TTL再接USB转TTL或直接进开发板的UART引脚。接好后还有件事容易忽略RS485是半双工通信数据发送和接收共用一对线发的时候不能收收的时候不能发。软件上需要控制收发切换。多数现成的485模块有自动收发切换电路如果用的模块需要手动切换Android端代码里得在发送完成后做个延时再切回接收模式否则首个字节丢失是必然的。4.3 Modbus RTU协议对接最常见的高频场景做工业项目绕不开Modbus RTU。它定义在RS485物理层之上格式固定从站地址、功能码、数据、CRC校验。Android端需要实现CRC16校验函数根据功能码拼装数据帧和解析响应帧。这里有个容易踩的坑很多做Android的人对CRC不熟建议直接搜成熟的CRC16-Modbus实现不要自己造轮子。Modbus RTU的字节间超时要求是“一帧数据内部各字节间隔不超过1.5个字符时间”这意味着接收端要及时处理收到的字节不能等到攒够了才去读否则帧已经断开了。5. 常见问题与排查技巧实录5.1 打开串口报Permission denied怎么办这一般就是权限问题。属于定制系统的检查启动脚本中串口节点权限有没有设置使用root设备的用su -c chmod 666 /dev/ttyS0临时改权限再测试。如果是通过USB转串口接入先看/dev/ttyUSB*节点有没有生成没生成说明驱动没加载另说。权限问题排查完还有个冷门注意点有些设备上有多个串口节点/dev/ttyS0、/dev/ttyS1不代表物理位置顺序对应的可能是不同的UART引脚或者蓝牙模块盲目改权限没意义。建议去查板子的原理图或者串口节点对应的设备树配置确定准确的节点名称。5.2 数据收发乱码的排查思路收发乱码优先级最高的方向是波特率是否匹配。把两端波特率都设成一样的重新测。其次看数据位、停止位、校验位。三者都一致仍乱码再检查USB转TTL模块的TX/RX是否接反。有一种情况容易忽略Android端用的是字符串编码解析字节流MCU发的却是二进制帧。这种场景不是乱码而是解析方式不对改成字节流方式逐帧解析即可。5.3 串口数据丢失首字节或者中间字节奇偶校验失败丢首字常见于RS485半双工切换时序不对发送完成立即切换接收而总线上还在返回最后一个字节的尾巴。解决方法是发送后加一个延时延时长度根据波特率计算波特率越低延时越长。以9600波特率为例一字节的时间大约是1.04ms发送完后等3-5字节的时间再切接收通常就能避开首字丢失。中间丢数据则是接收线程读取不及时导致的。建议把串口读缓冲区开得大一些并用单独线程持续读取避免等到有数据了才去读。踩过一次掉数据的坑后我现在写串口程序第一件事永远是确认读线程循环是否可靠。5.4 USB转串口模块不识别驱动排查手机OTG连接USB转TTL模块没反应优先级从模块本身开始排查。先用电脑测模块好坏确认模块是好的再排查手机是否支持OTG是否已开启OTG功能。个别手机需要手动开启OTG。驱动层面CH340在Android上通常有内核驱动如果手机内核没编译进去就得换一个内核包含此驱动的设备或板卡这个不是App层面能绕过的。5.5 串口无法关闭拔线后重连失败这个问题的根源多半是文件描述符未释放。空闲时要及时关闭输入输出流和串口对象尤其在做反复插拔测试时不主动关闭会导致节点一直被占用重连必然失败。再补充一点小经验打开串口前检查文件是否存在抛异常时把异常信息记录到日志里能省下很多不必要的排查时间。6. 动手实践中的几条心得6.1 调试串口日志先行做串口开发日志设计会直接影响排障效率。建议加一个调试开关把串口每次收发的原始字节都打到日志里。之前做过一个设备通信偶发不稳定靠日志连续打了两天最后定位到是某一帧数据偶尔多了一个字节导致的解析错位如果当时没有日志这个问题排查难度会大很多。日志格式也有讲究Hex和ASCII都要有平时看Hex联调时看ASCII两个视图并存能省很多事。6.2 先做通断测试再做协议解析新手习惯直接写完整代码然后一跑——“怎么数据不对”然后陷入迷茫。老手的顺序是先做自发自收验证串口链路通再通过串口调试助手和另一头设备通信最后再到Android代码里跑完整通信。一层一层排查问题始终能在最小的范围内被锁定。6.3 基于这个demo后面还能怎么改串口通信demo一旦跑通往上叠加的方向很多——后续可以做一个自动识别波特率的功能通过尝试常用波特率读取特征字节来猜测设备参数可以对接数据库把设备上报的数据落库展示趋势图也可以封装成Service常驻后台实现在App退到后台依然持续接收串口数据。这些方向都能让这个小demo变成一个真正可复用的工具。开发串口项目本质上是在和Linux的设备模型打交道。硬件的接线细节、驱动的存在与否、内核的权限配置、协议的字节定义每一环都可能是问题点而且调试手段有限只能靠经验和日志逐步逼近。但正因为如此把链路梳理清楚的方法论就变得特别值钱这也是我每次写串口代码都保持敬畏心的原因。本文还有配套的精品资源点击获取
返回列表