Android串口通信之串口读写实例

在Android串口通信:基本知识梳理的基础上,我结合我项目中使用串口的实例,进行总结;

Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目

下面是我项目中的相关代码及介绍:

1、SerialPort.cpp

/*
 * Copyright 2009 Cedric Priscal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdlib.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h> 

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h> 

#include "android/log.h"
static const char *TAG = "serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) 

static speed_t getBaudrate(jint baudrate) {
 switch (baudrate) {
 case 0:
  return B0;
 case 50:
  return B50;
 case 75:
  return B75;
 case 110:
  return B110;
 case 134:
  return B134;
 case 150:
  return B150;
 case 200:
  return B200;
 case 300:
  return B300;
 case 600:
  return B600;
 case 1200:
  return B1200;
 case 1800:
  return B1800;
 case 2400:
  return B2400;
 case 4800:
  return B4800;
 case 9600:
  return B9600;
 case 19200:
  return B19200;
 case 38400:
  return B38400;
 case 57600:
  return B57600;
 case 115200:
  return B115200;
 case 230400:
  return B230400;
 case 460800:
  return B460800;
 case 500000:
  return B500000;
 case 576000:
  return B576000;
 case 921600:
  return B921600;
 case 1000000:
  return B1000000;
 case 1152000:
  return B1152000;
 case 1500000:
  return B1500000;
 case 2000000:
  return B2000000;
 case 2500000:
  return B2500000;
 case 3000000:
  return B3000000;
 case 3500000:
  return B3500000;
 case 4000000:
  return B4000000;
 default:
  return -1;
 }
} 

/*
 * Class:  cedric_serial_SerialPort
 * Method: open
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT jobject JNICALL native_open(JNIEnv *env, jobject thiz, jstring path,jint baudrate) {
 int fd;
 speed_t speed;
 jobject mFileDescriptor; 

 LOGD("init native Check arguments");
 /* Check arguments */
 {
  speed = getBaudrate(baudrate);
  if (speed == -1) {
   /* TODO: throw an exception */
   LOGE("Invalid baudrate");
   return NULL;
  }
 } 

 LOGD("init native Opening device!");
 /* Opening device */
 {
  jboolean iscopy;
  const char *path_utf = env->GetStringUTFChars(path, &iscopy);
  LOGD("Opening serial port %s", path_utf);
//  fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
  fd = open(path_utf, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
  LOGD("open() fd = %d", fd);
  env->ReleaseStringUTFChars(path, path_utf);
  if (fd == -1) {
   /* Throw an exception */
   LOGE("Cannot open port %d",baudrate);
   /* TODO: throw an exception */
   return NULL;
  }
 } 

 LOGD("init native Configure device!");
 /* Configure device */
 {
  struct termios cfg;
  if (tcgetattr(fd, &cfg)) {
   LOGE("Configure device tcgetattr() failed 1");
   close(fd);
   return NULL;
  } 

  cfmakeraw(&cfg);
  cfsetispeed(&cfg, speed);
  cfsetospeed(&cfg, speed); 

  if (tcsetattr(fd, TCSANOW, &cfg)) {
   LOGE("Configure device tcsetattr() failed 2");
   close(fd);
   /* TODO: throw an exception */
   return NULL;
  }
 } 

 /* Create a corresponding file descriptor */
 {
  jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor");
  jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor,"<init>", "()V");
  jfieldID descriptorID = env->GetFieldID(cFileDescriptor,"descriptor", "I");
  mFileDescriptor = env->NewObject(cFileDescriptor,iFileDescriptor);
  env->SetIntField(mFileDescriptor, descriptorID, (jint) fd);
 } 

 return mFileDescriptor;
} 

/*
 * Class:  cedric_serial_SerialPort
 * Method: close
 * Signature: ()V
 */
JNIEXPORT jint JNICALL native_close(JNIEnv * env, jobject thiz)
{
 jclass SerialPortClass = env->GetObjectClass(thiz);
 jclass FileDescriptorClass = env->FindClass("java/io/FileDescriptor"); 

 jfieldID mFdID = env->GetFieldID(SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
 jfieldID descriptorID = env->GetFieldID(FileDescriptorClass, "descriptor", "I"); 

 jobject mFd = env->GetObjectField(thiz, mFdID);
 jint descriptor = env->GetIntField(mFd, descriptorID); 

 LOGD("close(fd = %d)", descriptor);
 close(descriptor);
 return 1;
} 

static JNINativeMethod gMethods[] = {
  { "open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },
  { "close", "()I",(void*) native_close },
}; 

/*
 * 为某一个类注册本地方法
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
  JNINativeMethod* gMethods, int numMethods) {
 jclass clazz;
 clazz = env->FindClass(className);
 if (clazz == NULL) {
  return JNI_FALSE;
 }
 if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
  return JNI_FALSE;
 } 

 return JNI_TRUE;
} 

/*
 * 为所有类注册本地方法
 */
static int registerNatives(JNIEnv* env) {
 const char* kClassName = "com/jerome/serialport/SerialPort"; //指定要注册的类
 return registerNativeMethods(env, kClassName, gMethods,
   sizeof(gMethods) / sizeof(gMethods[0]));
} 

/*
 * System.loadLibrary("lib")时调用
 * 如果成功返回JNI版本, 失败返回-1
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
 JNIEnv* env = NULL;
 jint result = -1; 

 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
  return -1;
 }
 assert(env != NULL); 

 if (!registerNatives(env)) { //注册
  return -1;
 }
 //成功
 result = JNI_VERSION_1_4; 

 return result;
}

在编译时注意修改const char* kClassName = "com/jerome/serialport/SerialPort";为你Java层与jni对应得包名;

2、Android.mk

LOCAL_PATH := $(call my-dir) 

include $(CLEAR_VARS) 

TARGET_PLATFORM := android-3
LOCAL_MODULE := serial_port
LOCAL_SRC_FILES := SerialPort.cpp
LOCAL_LDLIBS := -llog 

include $(BUILD_SHARED_LIBRARY)

如果要修改生成so文件的名称,请修改LOCAL_MODULE    := serial_port

3、SerialPort.java

package com.jerome.serialport; 

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; 

public class SerialPort { 

 private static final String TAG = "SerialPort";
 /*
  * Do not remove or rename the field mFd: it is used by native method close();
  */
 private FileDescriptor mFd;
 private FileInputStream mFileInputStream;
 private FileOutputStream mFileOutputStream; 

 public SerialPort(File device, int baudrate) throws SecurityException, IOException {
  mFd = open(device.getAbsolutePath(), baudrate);
  if (mFd == null) {
   throw new IOException();
  }
  mFileInputStream = new FileInputStream(mFd);
  mFileOutputStream = new FileOutputStream(mFd);
 } 

 public InputStream getInputStream() {
  return mFileInputStream;
 } 

 public OutputStream getOutputStream() {
  return mFileOutputStream;
 } 

 private native FileDescriptor open(String path, int baudrate);
 public native int close(); 

 static {
  System.loadLibrary("serial_port");
 }
}

4、SerialPortUtil.java

package com.jerome.serialport; 

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter; 

/**
 * 串口操作类
 *
 * @author Jerome
 *
 */
public class SerialPortUtil {
 private String TAG = SerialPortUtil.class.getSimpleName();
 private SerialPort mSerialPort;
 private OutputStream mOutputStream;
 private InputStream mInputStream;
 private ReadThread mReadThread;
 private String path = "/dev/ttyMT1";
 private int baudrate = 115200;
 private static SerialPortUtil portUtil;
 private OnDataReceiveListener onDataReceiveListener = null;
 private boolean isStop = false; 

 public interface OnDataReceiveListener {
  public void onDataReceive(byte[] buffer, int size);
 } 

 public void setOnDataReceiveListener(
   OnDataReceiveListener dataReceiveListener) {
  onDataReceiveListener = dataReceiveListener;
 } 

 public static SerialPortUtil getInstance() {
  if (null == portUtil) {
   portUtil = new SerialPortUtil();
   portUtil.onCreate();
  }
  return portUtil;
 } 

 /**
  * 初始化串口信息
  */
 public void onCreate() {
  try {
   mSerialPort = new SerialPort(new File(path), baudrate);
   mOutputStream = mSerialPort.getOutputStream();
   mInputStream = mSerialPort.getInputStream(); 

   mReadThread = new ReadThread();
   isStop = false;
   mReadThread.start();
  } catch (Exception e) {
   e.printStackTrace();
  }
  initBle();
 } 

 /**
  * 发送指令到串口
  *
  * @param cmd
  * @return
  */
 public boolean sendCmds(String cmd) {
  boolean result = true;
  byte[] mBuffer = (cmd+"\r\n").getBytes();
//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer
  try {
   if (mOutputStream != null) {
    mOutputStream.write(mBuffer);
   } else {
    result = false;
   }
  } catch (IOException e) {
   e.printStackTrace();
   result = false;
  }
  return result;
 } 

 public boolean sendBuffer(byte[] mBuffer) {
  boolean result = true;
  String tail = "\r\n";
  byte[] tailBuffer = tail.getBytes();
  byte[] mBufferTemp = new byte[mBuffer.length+tailBuffer.length];
  System.arraycopy(mBuffer, 0, mBufferTemp, 0, mBuffer.length);
  System.arraycopy(tailBuffer, 0, mBufferTemp, mBuffer.length, tailBuffer.length);
//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer
  try {
   if (mOutputStream != null) {
    mOutputStream.write(mBufferTemp);
   } else {
    result = false;
   }
  } catch (IOException e) {
   e.printStackTrace();
   result = false;
  }
  return result;
 } 

 private class ReadThread extends Thread { 

  @Override
  public void run() {
   super.run();
   while (!isStop && !isInterrupted()) {
    int size;
    try {
     if (mInputStream == null)
      return;
     byte[] buffer = new byte[512];
     size = mInputStream.read(buffer);
     if (size > 0) {
      if(MyLog.isDyeLevel()){
       MyLog.log(TAG, MyLog.DYE_LOG_LEVEL, "length is:"+size+",data is:"+new String(buffer, 0, size));
      }
      if (null != onDataReceiveListener) {
       onDataReceiveListener.onDataReceive(buffer, size);
      }
     }
     Thread.sleep(10);
    } catch (Exception e) {
     e.printStackTrace();
     return;
    }
   }
  }
 } 

 /**
  * 关闭串口
  */
 public void closeSerialPort() {
  sendShellCommond1();
  isStop = true;
  if (mReadThread != null) {
   mReadThread.interrupt();
  }
  if (mSerialPort != null) {
   mSerialPort.close();
  }
 } 

}

5、使用方法:

a、配置ndk开发环境,具体百度一下;
b、工程根目录下新建jni文件夹,将Android.mk和SerialPort.cpp放进去;
c、ndk中进入jni目录,编译生成so文件,默认so生成在libs/armeabi下;
d、新建com.jerom.serialport目录,将SerialPort和SerialPortUtil放进去;
 f、在你要使用的地方初始化SerialPortUtil,实现回调接口OnDataReceiveListener即可接受数据;

总结:

1、串口发送实质就是向串口设备(类似于文件操作)写入字节流,串口读取也是一样;
2、主要jni与Java native得对应;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Android串口开发之使用JNI实现ANDROID和串口通信详解
  • Android串口通信封装之OkUSB的示例代码
  • 详解Android USB转串口通信开发基本流程
  • Android USB转串口通信开发实例详解
  • Android Socket 线程连接openwrt与arduino单片机串口双向通信的实例解析
  • Android 串口通信编程及串口协议分析
(0)

相关推荐

  • 详解Android USB转串口通信开发基本流程

    好久没有写文章了,年前公司新开了一个项目,是和usb转串口通信相关的,需求是用安卓平板通过usb转接后与好几个外设进行通信,一直忙到最近,才慢慢闲下来,趁着这个周末不忙,记录下usb转串口通信开发的基本流程. 我们开发使用的是usb主机模式,即:安卓平板作为主机,usb外设作为从机进行数据通信.整个开发流程可以总结为以下几点: 1.发现设备 UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERV

  • Android Socket 线程连接openwrt与arduino单片机串口双向通信的实例解析

    废话不多说了,直接给大家贴代码了,具体代码如下所示: package zcd.netanything; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import android.app.Fragment; import android.content.BroadcastReceiver; import and

  • Android 串口通信编程及串口协议分析

    Android 串口通信编程:嵌入式编程和可穿戴设备及智能设备都会用到串口,这里就带大家分析下, 一,android串口通信 串口通信采用一个第三方开源项目,实现串口数据收发. 1. 使用了http://code.google.com/p/android-serialport-api/的项目的serialport api和jni: 2. 支持4串口同时收发,有定时自动发送功能,收发模式可选Txt或Hex模式: 3.  n,8,1,没得选: 4. 为减轻界面卡顿的情况,接收区的刷新采用单独的线程进

  • Android串口通信封装之OkUSB的示例代码

    本文介绍了Android串口通信封装之OkUSB的示例代码,分享给大家.具体如下: Github传送门:OkUSB OkUSB 一个简洁的Android串口通信框架. 功能简介 支持设置波特率 支持设置数据位 支持设置停止位 支持设置校验位 支持DTS和RTS 支持串口连接状态监听 用法简介 Gradle allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { compile '

  • Android串口开发之使用JNI实现ANDROID和串口通信详解

    一:串口通信简介 前段时间因为工作需要研究了一下android的串口通信,网上有很多讲串口通信的文章,我在做的时候也参考了很多文章,现在就将我学习过程中的一些心得分享给大家,由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以及第一个jni调用程序 ,串口通信和java操作io类似,先打开串口,然后向串口发送或者读取数据,最后关闭串口,所以基本思路就是: 1.对串口文

  • Android USB转串口通信开发实例详解

     Android USB转串口通信开发实例详解 好久没有写文章了,年前公司新开了一个项目,是和usb转串口通信相关的,需求是用安卓平板通过usb转接后与好几个外设进行通信,一直忙到最近,才慢慢闲下来,趁着这个周末不忙,记录下usb转串口通信开发的基本流程. 我们开发使用的是usb主机模式,即:安卓平板作为主机,usb外设作为从机进行数据通信.整个开发流程可以总结为以下几点: 1.发现设备 UsbManager usbManager = (UsbManager) context.getSystem

  • Android串口通信之串口读写实例

    在Android串口通信:基本知识梳理的基础上,我结合我项目中使用串口的实例,进行总结: Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化: Google串口开源项目 下面是我项目中的相关代码及介绍: 1.SerialPort.cpp /* * Copyright 2009 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "Li

  • java 串口通信详细及简单实例

    java 实现串口通信 最近做了一个与硬件相关的项目,刚开始听说用java和硬件打交道,着实下了一大跳.java也可以操作硬件? 后来接触到是用java通过串口通信控制硬件感觉使用起来还不错,也很方便. 特拿出来和大家一起分享一下. 准备工作: 首先到SUN官网下载一个zip包:javacomm20-win32.zip 其中重要的有这几个文件: win32com.dll comm.jar javax.comm.properties 按照说明配置好环境,如下: 将win32com.dll复制到<J

  • Android编程之文件的读写实例详解

    本文实例分析了Android编程之文件的读写方法.分享给大家供大家参考,具体如下: Android的文件读写与JavaSE的文件读写相同,都是使用IO流.而且Android使用的正是JavaSE的IO流,下面我们通过一个练习来学习Android的文件读写. 1.创建一个Android工程 Project name:File     BuildTarget:Android2.2     Application name:文件读写     Package name:test.file     Cre

  • Qt串口通信开发之QSerialPort模块详细使用方法与实例

    Qt串口通信基础及名词说明 串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据.它很简单并且能够实现远距离通信.比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米: 而对于串口而言,长度可达1200米.典型地,串口用于ASCII码字符的传输.通信使用3根线完成,分别是地线.发送.接收.由于串口通信

  • Android串口通信apk源码详解(附完整源码)

    1.SerialPortHelper「Android串口通信」介绍 原项目地址 https://github.com/freyskill/SerialPortHelper Android串口通讯助手可以用于需要使用串口通信的Android外设,该库有如下特点: 1.串口通信部分使用C++实现,在笔者接触的部分设备上实测,使用C++实现与Google官方提供的Demo的方式要快: 2.支持且必须设置串口接收最大数据长度,初始化库时填入该参数,这样设置的原因是考虑在实际使用中,规定的串口通信协议格式

  • Android实现串口通信

    本文实例为大家分享了Android实现串口通信的具体代码,供大家参考,具体内容如下 生成so文件 首先确保已经安装了NDK和CMake 然后创建一个SerialPort.java文件 主要用来处理so文件 注意包名一旦写好不要更改位置,具体代码: import android.util.Log; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.Fi

  • Python使用pyserial进行串口通信的实例

    安装pyserial pip install pyserial 查看可用的端口 # coding:utf-8 import serial.tools.list_ports plist = list(serial.tools.list_ports.comports()) if len(plist) <= 0: print("没有发现端口!") else: plist_0 = list(plist[0]) serialName = plist_0[0] serialFd = seri

随机推荐