Android技术进阶iOS逆向Android开发

Android应用开发转车载工程师——串口通信学习

2022-09-19  本文已影响0人  谁动了我的代码

串口简介

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。

串口参数

波特率:串口传输速率,用来衡量数据传输<typo id="typo-193" data-origin="的" ignoretag="true">的</typo>快慢,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。波特率与距离成反比,波特率越大传输距离相应的就越短。

数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。

停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。

串口地址

如下表不同操作系统的串口地址,Android是基于Linux的所以一般情况下使用Android系统的设备串口地址为/dev/ttyS0...


串行通讯特点

UART包含TTL电平的串口和RS232电平的串口。

通信基本格式

注:因为最大长度256不能满足有些指令的要求,所以对长度做了扩展,下面是扩展说明:

如果第一个字节的最高位为1,则表示扩展长度。在扩展长度状态下,其他15个字节通过16进制大端模式来保存长度。

比如:0x80 0x12表示长度为0x001 2,0x81 0x12表示长度为0x0112。

Android实战蓝牙串口通讯

连接蓝牙设备——蓝牙客户端:

Android手机一般以客户端的角色主动连接SPP协议设备(接上蓝牙模块的数字传感器),客户端连接流程是:

1.使用registerReceiver注册BroadcastReceiver来获取蓝牙状态、搜索设备等消息;

  **private** BroadcastReceiver searchDevices = **new** BroadcastReceiver() { 

•    **public** **void** onReceive(Context context, Intent intent) {

•      String action = intent.getAction();

•      Bundle b = intent.getExtras();

•      Object[] lstName = b.keySet().toArray(); 

•      // 显示所有收到的消息及其细节

•      **for** (**int** i = 0; i < lstName.length; i++) {

•       String keyName = lstName[i].toString();

•       Log.*e*(keyName, String.*valueOf*(b.get(keyName)));

•      }

•      //搜索设备时,取得设备的MAC地址

•      **if** (BluetoothDevice.*ACTION_FOUND*.equals(action)) {

•       BluetoothDevice device = intent

•           .getParcelableExtra(BluetoothDevice.*EXTRA_DEVICE*);

•       String str= device.getName() + "|" + device.getAddress();

•       

•       **if** (lstDevices.indexOf(str) == -1)// 防止重复添加

•         lstDevices.add(str); // 获取设备名称和mac地址

•       adtDevices.notifyDataSetChanged();

•      }

•    }

  };

2.使用BlueAdatper的搜索:btAdapt.startDiscovery();

3.在BroadcastReceiver的onReceive()里取得搜索所得的蓝牙设备信息(如名称,MAC,RSSI);

4.通过设备的MAC地址来建立一个BluetoothDevice对象;

5.由BluetoothDevice衍生出BluetoothSocket,准备SOCKET来读写设备;

6.通过BluetoothSocket的createRfcommSocketToServiceRecord()方法来选择连接的协议/服务,这里用的 是SPP(UUID:00001101-0000-1000-8000-00805F9B34FB);

**try** {   

•    *btSocket* = btDev.createRfcommSocketToServiceRecord(uuid);

  } **catch** (IOException e) {

  // **TODO** Auto-generated catch block

•    Log.*e*(*TAG*, "Low: Connection failed.", e);   

  }

成功后进行连接:

**try** {

  *btSocket*.connect();      

  Log.*e*(*TAG*, " BT connection established, data transfer link open.");

  mangeConnectedSocket(btSocket);//自定义函数进行蓝牙通信处理

} **catch** (IOException e) {

  Log.*e*(*TAG*, " Connection failed.", e); 

  setTitle("连接失败..");

} 

7.Connect之后(如果还没配对则系统自动提示),使用

BluetoothSocket的getInputStream()和getOutputStream()来读写蓝牙设备。

读写可以归到一个独立线程去实现~ 注意:读时必须一直循环读取串口缓冲区,写可以不需要。

按以上7步逐次走过后,你就会发现Android蓝牙模块是多么的坑爹了。

出现问题:

在第6步一般初学者都会报错: 执行.connect()发生异常Connection refused

此时执行不下去咯,怎么办怎么办呢?

于是边debug边网上找攻略,总算在Google出老外的一些做法,尝试了下,貌似还可行。也即把 btSocket的建立方法采用另一种方法替代,这里都使用端口1

Method m;

**try** {

  m = btDev.getClass().getMethod("createRfcommSocket", **new** Class[] {**int**.**class**});

  *btSocket* = (BluetoothSocket) m.invoke(btDev, Integer.*valueOf*(1));

•       } **catch** (SecurityException e1) {

•         // **TODO** Auto-generated catch block

•         e1.printStackTrace();

•       } **catch** (NoSuchMethodException e1) {

•         // **TODO** Auto-generated catch block

•         e1.printStackTrace();

•       } **catch** (IllegalArgumentException e) {

•         // **TODO** Auto-generated catch block

•         e.printStackTrace();

•       } **catch** (IllegalAccessException e) {

•         // **TODO** Auto-generated catch block

•         e.printStackTrace();

•       } **catch** (InvocationTargetException e) {

•         // **TODO** Auto-generated catch block

•         e.printStackTrace();

•      } 

至此,这个问题貌似倒也解决了,程序继续往下跑。

但这里请记住之前的异常,先别急着抛开~人家不一定一直都是异常哦。

接下来的任务是,让手机通过蓝牙跟单片机的蓝牙模块通信,并发送数据,通过电脑串口调试助手显示出来。具体实现,在mangeConnectedSocket(btSocket)方法中实现,里面通过启动另一个Activity实现。不是重点,略过。

直到这里,我们都只是把手机蓝牙模块充当客户端来使用,那什么时候会用到服务端呢?其实,之前手机蓝牙与单片机蓝牙模块的通信,单片机蓝牙模块就充当了服务端(处于监听状态,被手机蓝牙连接)。为了更好地搞清楚Android蓝牙通信,我们接下来使用2个手机的蓝牙进行通信。简单地说,就是做一个“手机蓝牙扣扣”,⊙﹏⊙b汗

一开始就想天真地把之前的程序同时烧到2部手机中,发现只有一部手机能正常建立socket连接(主动连接的那台),而另一部却迟迟没有响应。原因很简单,服务端的程序还没有编写!

于是,开始服务端程序:开辟一个新的线程实现

连接蓝牙设备——蓝牙服务端:

  **class** AcceptThread **extends** Thread {

  **private** **final** BluetoothServerSocket serverSocket;

  **public** AcceptThread() {

•    // Use a temporary object that is later assigned to mmServerSocket,

•    // because mmServerSocket is final 

  BluetoothServerSocket tmp=**null**;

  **try** {

//tmp = btAdapt.listenUsingRfcommWithServiceRecord("MyBluetoothApp", uuid);

  Log.*e*(*TAG*, "++BluetoothServerSocket established!++");

 Method listenMethod =     btAdapt.getClass().getMethod("listenUsingRfcommOn",

  **new**  Class[]{**int**.**class**});

  tmp = ( BluetoothServerSocket) listenMethod.invoke(btAdapt,                          Integer.*valueOf*( 1));

•     

•    } **catch** (SecurityException e) {

•      // **TODO** Auto-generated catch block

•      e.printStackTrace();

•    } **catch** (IllegalArgumentException e) {

•      // **TODO** Auto-generated catch block

•      e.printStackTrace();

•    } **catch** (NoSuchMethodException e) {

•      // **TODO** Auto-generated catch block

•      e.printStackTrace();

•    } **catch** (IllegalAccessException e) {

•      // **TODO** Auto-generated catch block

•      e.printStackTrace();

•    } **catch** (InvocationTargetException e) {

•      // **TODO** Auto-generated catch block

•      e.printStackTrace();

•    }

•    serverSocket=tmp;

  }  

  **public** **void** run() {

•    

•    // Keep listening until exception occurs or a socket is returned

•     //mState!=STATE_CONNECTED

  **while**(**true**) {//这里是一直循环监听,也可以设置mState来判断

•    **try** {

•      *socket* = serverSocket.accept();

•      Log.*e*(*TAG*, "++BluetoothSocket established! DataLink open.++");

•      } **catch** (IOException e) {

•        **break**;

•      }

•      // If a connection was accepted

•      **if** (*socket* != **null**) {

•        // Do work to manage the connection (in a separate thread)

•        manageConnectedSocket();  

•        **try** {

•         serverSocket.close();

•       } **catch** (IOException e) {

•         // **TODO** Auto-generated catch block

•         e.printStackTrace();

•       }

•        **break**;

•      }

•    }    

}

  **public** **void** cancel() {

•    **try** {

•      serverSocket.close();

•    } **catch** (IOException e) { }

  }

}

安装测试:当2部手机都装上并打开同样的程序后,通过蓝牙检索并连接,经测试可以成功连接上,双双进入“聊天界面”,嘿嘿

注意,这时候重新拾回之前那个异常,把socket连接建立的方法重新改为

*btSocket* = btDev.createRfcommSocketToServiceRecord(uuid);//客户端

对应的服务端程序:

*tmp* = btAdapt.listenUsingRfcommWithServiceRecord("MyBluetoothApp", *uuid*);//服务端

这样继续重新运行安装测试,在2部手机上运行发现之前那个bug消失了~2部手机又双双进入聊天界面。

本文主要介绍了通讯串口,与一个蓝牙通讯的实战演练。许多Android应用开发想转车载,想必大家都是聪明人;车载的市场可以根据生活发现,小轿车对于我们已经不陌生了,从0几年马路上的小轿车少之又少,到如今的一人一辆。再加上新能源的普及;对于我们Android车载开发行业来说,非常的友好。

车载学习路线图:

这绝对是一个黄金赛道,Android应用开发转车载确实是个不错的选择。如何学习车载,我这里可以推荐这个《Android车载技术手册》资料由吉利车载系统开发高级工程师制作的笔记文档。

文末

串口学习只是车载系统开发中的一小部分,还有Automotive系统、车载进程通信、多媒体开发、空调系统、系统开发。等等学习的技术还有很多。技术资料需要充分利用,车载系统开发是个明智的选择。

上一篇 下一篇

猜你喜欢

热点阅读