Android 中的串口开发

一:背景

本文着重讲安卓下的串口。

由于开源的Android在各种智能设备上的使用越来越多,如车载系统等。在我们的认识中,Android OS的物理接口一般只有usb host接口和耳机接口,但其实安卓支持各种各样的工业接口,如HDMI、usb、网口、串口等等。

下图就是一块Android工业板,标圈的DB9(也叫RS232串口)就是串口中的一种形态。

 

二、什么是串口?

串行端口 ,即:SerialPort,简称串口,主要用于数据被逐位按顺序传送的通讯方式称为串口通讯(简单来讲就是按顺序一位一位地传输数据)。

三、串口的一般形态

串口一般有RS232和RS485之分,485串口可以使用RS-232转RS-485串口的转换器转换。

RS232:

232协议的串口是全双工 的,它允许数据同时接收和发送,但RS232的理论传输距离只有10米。

RS-485:

485是半双工的,半双工意味着同一时间只能收/发,就像是独木桥,同时只能有一个方向的人流通过,如果对向有来人则会造成数据丢失,RS485的理论距离是1200峭,通常如果要远距离使用的话会使用485串口,短距离则可以使用232。

四、串口的使用

无论是Android、windows还是Linux,串口的使用都要以下几步:

  1. 打开串口

  2. 串口配置(一般为:波特率、数据位、停止位和奇偶校验

  3. 串口操作(读/写,无非就是输入输出流的操作罢了

  4. 关闭串口

五、代码实践

package com.xz.andfasterserialport;import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import tp.xmaihh.serialport.SerialHelper;
import tp.xmaihh.serialport.bean.ComBean;
import tp.xmaihh.serialport.stick.AbsStickPackageHelper;
import tp.xmaihh.serialport.utils.ByteUtil;public class MainActivity extends AppCompatActivity implements View.OnClickListener{private Button leftReduce;private Button leftAdd;private Button buyWine;private TextView wine_number;private String yellowWineType = "01";@BindView(R.id.rg_type)RadioGroup mRgType;private SerialHelper serialHelper;private boolean isHexType = false;private String text = "";private Handler mHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {ComBean comBean = (ComBean) msg.obj;String time = comBean.sRecTime;String rxText;rxText = new String(comBean.bRec);if (isHexType) {//转成十六进制数据rxText = ByteUtil.ByteArrToHex(comBean.bRec);}text += "Rx-> " + time + ": " + rxText + "\r" + "\n";//mEtReadContent.setText(text);return false;}});private RadioButton radioButton1;private RadioButton radioButton2;private RadioButton radioButton3;private RadioButton radioButton4;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);initSerialConfig();//下面的两行设置全屏,隐藏系统状态栏getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);leftReduce = findViewById(R.id.left_reduce);leftAdd = findViewById(R.id.left_add);buyWine = findViewById(R.id.buy_wine);wine_number = findViewById(R.id.wine_number);radioButton1 = findViewById(R.id.rb_mk);radioButton2 = findViewById(R.id.rb_ls);radioButton3 = findViewById(R.id.rb_lx);radioButton4 = findViewById(R.id.rb_fzz);leftReduce.setOnClickListener(this);leftAdd.setOnClickListener(this);buyWine.setOnClickListener(this);radioButton1.setOnClickListener(this);radioButton2.setOnClickListener(this);radioButton3.setOnClickListener(this);radioButton4.setOnClickListener(this);}private void initSerialConfig() {//初始化SerialHelper对象,设定串口名称和波特率serialHelper = new SerialHelper(Const.SPORT_NAME, Const.BAUD_RATE) {@Overrideprotected void onDataReceived(ComBean paramComBean) {Message message = mHandler.obtainMessage();message.obj = paramComBean;mHandler.sendMessage(message);}};/** 默认的BaseStickPackageHelper将接收的数据扩展成64位,一般用不到这么多位* 我这里重新设定一个自适应数据位数的*/serialHelper.setStickPackageHelper(new AbsStickPackageHelper() {@Overridepublic byte[] execute(InputStream is) {try {int available = is.available();if (available > 0) {byte[] buffer = new byte[available];int size = is.read(buffer);if (size > 0) {return buffer;}} else {SystemClock.sleep(50);}} catch (IOException e) {e.printStackTrace();}return null;}});}//设置数据格式为十六进制还是其他,我们希望isHexType = true@Overrideprotected void onDestroy() {super.onDestroy();serialHelper.close();serialHelper = null;}//打开串口方法public void openSerialPort() {if (serialHelper.isOpen()) {Toast.makeText(this, Const.SPORT_NAME + "串口已经打开", Toast.LENGTH_SHORT).show();return;}try {serialHelper.open();} catch (Exception e) {e.printStackTrace();}Toast.makeText(this, Const.SPORT_NAME + "串口打开成功", Toast.LENGTH_SHORT).show();}//关闭串口方法public void closeSerialPort() {if (serialHelper.isOpen()) {serialHelper.close();Toast.makeText(this, Const.SPORT_NAME + "串口已经关闭", Toast.LENGTH_SHORT).show();}}@Overridepublic void onClick(View v) {int id = v.getId();switch (id) {case R.id.left_reduce:String stringValue1 = wine_number.getText().toString();//Log.d("XTQXTQXTQ",stringValue1);int value1 = Integer.parseInt(stringValue1);//Log.d("XTQXTQXTQ",value1+"");if ((value1 > 50)) {int newValue1 = value1 - 10;wine_number.setText(newValue1 + "");}break;case R.id.left_add:String stringValue2 = wine_number.getText().toString();//Log.d("XTQXTQXTQ",stringValue2);int value2 = Integer.parseInt(stringValue2);//Log.d("XTQXTQXTQ",value2+"");if ((value2 < 150)) {int newValue2 = value2 + 10;wine_number.setText(newValue2 + "");}break;case R.id.buy_wine:Toast.makeText(this,"开始发送串口数据",Toast.LENGTH_LONG).show();openSerialPort();if (!serialHelper.isOpen()) {Toast.makeText(this, Const.SPORT_NAME + "串口没打开 发送失败", Toast.LENGTH_SHORT).show();return;}int sendContentInt = Integer.parseInt(wine_number.getText().toString());//Log.d("100转十六进制" ,String.format("%02x", 100));String sendContent = "FB01"+yellowWineType+"00"+String.format("%x", sendContentInt)+"00"+"00"+"FE";Log.d("100转十六进制内容" ,sendContent);isHexType = true;//String sendContent = "FB010200960000FE";if (isHexType) {serialHelper.sendHex(sendContent);} else {serialHelper.sendTxt(sendContent);}case R.id.rb_mk:yellowWineType = "01";break;case R.id.rb_ls:yellowWineType = "02";break;case R.id.rb_lx:yellowWineType = "03";break;case R.id.rb_fzz:yellowWineType = "04";break;default:break;}}//十六进制校验位checkSum
}
package com.xz.andfasterserialport;public class Const {//串口 波特率public static final String SPORT_NAME = "/dev/ttyS1";public static final int BAUD_RATE = 9600;public static final String TXT_TYPE_SEND = "hello";public static final String HEX_TYPE_SEND = "123ABC";
}

主要思路:

添加依赖

首先,你需要在你的Android项目中添加依赖。确保项目build.gradle(Module级别)文件中有以下配置:

allprojects {repositories {maven { url 'https://jitpack.io' } // 或者使用作者推荐的仓库地址}
}dependencies {implementation 'com.github.xiaozhuang799:AndFasterSerialPort:latest.version'
}

初始化并使用串口

接下来,在你的Activity或Service中,初始化SerialHelper对象,并配置必要的串口参数:

import com.example.andfasterserialport.SerialHelper;// 假设已经定义了串口名和波特率
final String SERIAL_PORT_NAME = "/dev/ttyS0"; // 根据实际情况更改
final int BAUD_RATE = 9600; // 设置波特率SerialHelper serialHelper = new SerialHelper(SERIAL_PORT_NAME, BAUD_RATE) {@Overrideprotected void onDataReceived(ComBean paramComBean) {// 处理接收到的数据byte[] data = paramComBean.getData();// 你的逻辑代码}
};// 打开串口
serialHelper.open();// 发送数据示例
byte[] sendBuffer = "Hello, Serial Port!".getBytes();
serialHelper.send(sendBuffer);// 不要忘记在适当的时候关闭串口
serialHelper.close();

 确保在使用串口前,已添加必要的权限到AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 对于Android 6.0及以上,还需要在运行时请求这些权限 -->

参考项目地址:
GitCode - 全球开发者的开源社区,开源代码托管平台

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/57543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

条码检测系统——基于MATLAB的一维条码识别

摘 要&#xff1a;条码技术是如今应用最广泛的识别和输入技术之一&#xff0c;由于其包含的信息量大&#xff0c;识别错误率低而在各个方面得到很大的重视。它发展迅速并被广泛应用于于工业、商业、图书出版、医疗卫生等各行各业。由我国目前发展现状来看&#xff0c;条码的正…

人工智能:重塑未来生活与工作的科技力量

方向一&#xff1a;介绍人工智能技术的发展历程和现状&#xff0c;指出它的应用领域和前景 一、人工智能技术的发展历程 人工智能&#xff08;Artificial Intelligence, AI&#xff09;作为一门学科&#xff0c;其起源可以追溯到20世纪50年代。最初&#xff0c;AI的研究主要集…

Mytatis-plus使用sl4j日志打印SQL

以下是关于使用 Spring Boot 起始器替换 slf4j-api 和 logback 依赖的详细步骤和注意事项&#xff0c;包括 MyBatis-Plus 的默认日志级别信息。 1、依赖项配置 在 pom.xml 中添加以下依赖项&#xff1a; <dependency><groupId>org.springframework.boot</gro…

字符串使用方法:

字符串: -- 拼接字符串 SELECT CONCAT(糯米,啊啊啊撒,删掉); -- 字符长度 SELECT LENGTH(asssssssggg); -- 转大写 SELECT UPPER(asdf); -- 转小写 SELECT LOWER(ASDFG); -- 去除左边空格 SELECT LTRIM( aaaasdrf ); -- 去除右边空格 SELECT RTRIM( aaaasdff ); -- 去除两端…

攻坚金融关键业务系统,OceanBase亮相2024金融科技大会

10月15-16日&#xff0c;第六届中新数字金融应用博览会与2024金融科技大会&#xff08;简称“金博会”&#xff09;在苏州工业园区联合举办。此次大会融合了国家级重要金融科技资源——“中国金融科技大会”&#xff0c;围绕“赋能金融高质量发展&#xff0c;金融科技创新前行”…

【C++指南】运算符重载详解

引言 C 提供了运算符重载这一特性&#xff0c;允许程序员为自定义类型&#xff08;如类和结构体&#xff09;定义运算符的行为。 通过运算符重载&#xff0c;可以使自定义类型对象像内置类型一样使用运算符&#xff0c;从而提高代码的可读性和易用性。 本文将详细介绍 C 中运算…

【状态机DP】力扣2786. 访问数组中的位置使分数最大

给你一个下标从 0 开始的整数数组 nums 和一个正整数 x 。 你 一开始 在数组的位置 0 处&#xff0c;你可以按照下述规则访问数组中的其他位置&#xff1a; 如果你当前在位置 i &#xff0c;那么你可以移动到满足 i < j 的 任意 位置 j 。 对于你访问的位置 i &#xff0c…

若依微服务15 - RuoYi-Vue3 实现前端独立运行

正文开始&#xff1a; RuoYi-Vue3 使用 Vue3 Element Plus Vite 技术栈。 GitHub 开源地址&#xff1a;https://github.com/yangzongzhuan/RuoYi-Vue3 本文介绍使用若依提供的在线后端接口&#xff0c;仅启动前端项目并进行界面开发&#xff0c;而无需启动后端服务。 一、克隆…

AI视听新体验!浙大阿里提出视频到音乐生成模型MuVi:可解决语义对齐和节奏同步问题

MuVi旨在解决视频到音乐生成(V2M)中的语义对齐和节奏同步问题。 MuVi通过专门设计的视觉适配器分析视频内容,以提取上下文 和时间相关的特征,这些特征用于生成与视频的情感、主题及其节奏和节拍相匹配的音乐。MuVi在音频质量和时间同步方面表现优于现有基线方法,并展示了其在风…

Clickhouse集群_Zookeeper配置的dataDir目录磁盘占有率接近100%时,该dataDir目录是否可以清理及如何清理的脚本

官方文档https://zookeeper.apache.org/doc/r3.1.2/zookeeperAdmin.html#OngoingDataDirectoryCleanup 监控报警发现clickhouse集群环境的数据库节点磁盘报警&#xff0c;检查下来发现/chdata/zookeeper/data/version-2/目录特别大&#xff0c;里面包含了log.*文件和snapshot.…

【前端】--- ES6上篇(带你深入了解ES6语法)

前言&#xff1a;ECMAScript是 JavaScript 的标准化版本&#xff0c;由 ECMA 国际组织制定。ECMAScript 定义了 JavaScript 的语法、类型、语句、关键字、保留字等。 ES6 是 ECMAScript 的第六个版本&#xff0c;于 2015 年发布&#xff0c;引入了许多重要的新特性&#xff0c;…

实现vuex源码,手写

实现vuex源码&#xff0c;手写 Vuex 是专门为 Vue.js 应用程序开发的状态管理模式 库&#xff0c;它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。 第一步&#xff1a;定义初始化Store类 创建文件夹store/vuex.js 1…

C++ 20 Concept

concept主要用来定义模板参数的约束&#xff0c;最明显的作用就是在模板参数不满足类型的约束时编译器不再给出几千行奇奇怪怪的错误。当然还有其它的作用&#xff0c;比如说concepts可以用来实现函数的重载、新的concepts可以基于已有的concepts定义从而进行扩展等等下面以实现…

k8s 部署 nexus3 详解

创建命名空间 nexus3-namespace.yaml apiVersion: v1 kind: Namespace metadata:name: nexus-ns创建pv&pvc nexus3-pv-pvc.yaml apiVersion: v1 kind: PersistentVolume metadata:name: nfs-pvnamespace: nexus-ns spec:capacity:storage: 3GiaccessModes:- ReadWriteM…

Redis的6.0以上为啥又支持多线程

Redis 在 6.0 版本之前一直采用单线程架构&#xff0c;这是因为 Redis 主要是内存操作&#xff0c;单线程模型足以应对大部分高性能场景。而单线程模型的优势在于避免了多线程带来的上下文切换和锁的开销&#xff0c;使得 Redis 保持极高的性能和简单性。 然而&#xff0c;随着…

C++:模板的特化与分离编译

之前我们在介绍模板的时候仅仅是简单的介绍了模板的用法&#xff0c;本篇文章我们来详细的介绍下模板中比较重要的几个点。 一&#xff0c;非类型模板参数 我们之前的c中&#xff0c;会将经常使用的而又确保在我们程序的运行过程中值不会改变的值进行#define&#xff1a; #d…

初入编程之路,启航代码海

#1024程序员节|征文# 前言 今天又是1024程序员节了&#xff0c;第一次听说这个节日是在我在23年刚刚上大一的时候听学长他们说的&#xff0c;如今已经是24年了&#xff0c;虽然只学习了一年的编程但我已经了解到了这条路上的不易。希望能够在这条路上面一路坚持下去&#xff0…

力扣_斐波那契数列

本题目本质和爬楼梯是一样的&#xff0c;主要运用的是递归来解题。 class Solution:my_dict {}def fib(self, n: int) -> int:if self.my_dict.get(n) is not None: # 先判断有没有计算过这个值return self.my_dict.get(n)tempResult 0if n > 2:tempResult self.fib…

Java基础15-Java高级(单元测试、反射、注解、动态代理)

十五、Java高级 单元测试、反射、注解、动态代理。 1、单元测试 定义&#xff1a;就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。 1.1 Junit单元测试框架 可以用来对方 法进行测试&#xff0c;它是第三方公司开源出来的(很多开发工具已经集成了Junit框架&…

075_基于springboot的万里学院摄影社团管理系统

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…