Android串口通信:串口读写

在不久前,接触到了物联网的开发,当时一脸懵逼了,后来问了度娘和有幸遇到大神的指导,最终实现了功能。

首先先弄懂跟硬件交互时的一些协议(这协议都是硬件公司提供的),就在jni直接进行串口设备的读写,而且Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;

Google串口开源项目见:https://code.google.com/p/android-serialport-api/

下面是我项目中的相关代码及介绍:(因GitHub账号最近有些问题,只能直接把代码贴上来)

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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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)

staticspeed_t getBaudrate(jint baudrate) {

switch(baudrate) {

case0:

returnB0;

case50:

returnB50;

case75:

returnB75;

case110:

returnB110;

case134:

returnB134;

case150:

returnB150;

case200:

returnB200;

case300:

returnB300;

case600:

returnB600;

case1200:

returnB1200;

case1800:

returnB1800;

case2400:

returnB2400;

case4800:

returnB4800;

case9600:

returnB9600;

case19200:

returnB19200;

case38400:

returnB38400;

case57600:

returnB57600;

case115200:

returnB115200;

case230400:

returnB230400;

case460800:

returnB460800;

case500000:

returnB500000;

case576000:

returnB576000;

case921600:

returnB921600;

case1000000:

returnB1000000;

case1152000:

returnB1152000;

case1500000:

returnB1500000;

case2000000:

returnB2000000;

case2500000:

returnB2500000;

case3000000:

returnB3000000;

case3500000:

returnB3500000;

case4000000:

returnB4000000;

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) {

intfd;

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");

returnNULL;

}

}

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 */

returnNULL;

}

}

LOGD("init native Configure device!");

/* Configure device */

{

struct termios cfg;

if(tcgetattr(fd,&cfg)) {

LOGE("Configure device tcgetattr() failed 1");

close(fd);

returnNULL;

}

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 */

returnNULL;

}

}

/* Create a corresponding file descriptor */

{

jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor");

jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor,"","()V");

jfieldID descriptorID = env->GetFieldID(cFileDescriptor,"descriptor","I");

mFileDescriptor = env->NewObject(cFileDescriptor,iFileDescriptor);

env->SetIntField(mFileDescriptor,descriptorID,(jint) fd);

}

returnmFileDescriptor;

}

/*

* 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);

return1;

}

staticJNINativeMethod gMethods[] = {

{"open","(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },

{"close","()I",(void*) native_close },

};

/*

*为某一个类注册本地方法

*/

static intregisterNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, intnumMethods) {

jclass clazz;

clazz = env->FindClass(className);

if(clazz == NULL) {

returnJNI_FALSE;

}

if(env->RegisterNatives(clazz,gMethods,numMethods) <0) {

returnJNI_FALSE;

}

returnJNI_TRUE;

}

/*

*为所有类注册本地方法

*/

static intregisterNatives(JNIEnv* env) {

const char* kClassName ="com/jerome/serialport/SerialPort";//指定要注册的类

returnregisterNativeMethods(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;

returnresult;

}


SerialPort.cpp部分代码片

在编译时注意修改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)


Android.mk

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

3、SerialPort.java

importjava.io.File;

importjava.io.FileDescriptor;

importjava.io.FileInputStream;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.OutputStream;

public classSerialPort {

private static finalStringTAG="SerialPort";

/*

* Do not remove or rename the field mFd: it is used by native method close();

*/

privateFileDescriptormFd;

privateFileInputStreammFileInputStream;

privateFileOutputStreammFileOutputStream;

publicSerialPort(File device, intbaudrate)throwsSecurityException,IOException {

mFd= open(device.getAbsolutePath(),baudrate);

if(mFd==null) {

throw newIOException();

}

mFileInputStream=newFileInputStream(mFd);

mFileOutputStream=newFileOutputStream(mFd);

}

publicInputStreamgetInputStream() {

returnmFileInputStream;

}

publicOutputStreamgetOutputStream() {

returnmFileOutputStream;

}

private nativeFileDescriptoropen(String path, intbaudrate);

public native intclose();

static{

System.loadLibrary("serial_port");

}

}

4、SerialPortUtil.java

importjava.io.BufferedWriter;

importjava.io.File;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.OutputStream;

importjava.io.OutputStreamWriter;

importjava.io.PrintWriter;

/**

*串口操作类

*

*@authorJerome

*

*/

public classSerialPortUtil {

privateStringTAG= SerialPortUtil.class.getSimpleName();

privateSerialPortmSerialPort;

privateOutputStreammOutputStream;

privateInputStreammInputStream;

privateReadThreadmReadThread;

privateStringpath="/dev/ttyMT1";

private intbaudrate=115200;

private staticSerialPortUtilportUtil;

privateOnDataReceiveListeneronDataReceiveListener=null;

private booleanisStop=false;

public interfaceOnDataReceiveListener {

public voidonDataReceive(byte[] buffer, intsize);

}

public voidsetOnDataReceiveListener(

OnDataReceiveListener dataReceiveListener) {

onDataReceiveListener= dataReceiveListener;

}

public staticSerialPortUtilgetInstance() {

if(null==portUtil) {

portUtil=newSerialPortUtil();

portUtil.onCreate();

}

returnportUtil;

}

/**

*初始化串口信息

*/

public voidonCreate() {

try{

mSerialPort=newSerialPort(newFile(path),baudrate);

mOutputStream=mSerialPort.getOutputStream();

mInputStream=mSerialPort.getInputStream();

mReadThread=newReadThread();

isStop=false;

mReadThread.start();

}catch(Exception e) {

e.printStackTrace();

}

initBle();

}

/**

*发送指令到串口

*

*@paramcmd

*@return

*/

public booleansendCmds(String cmd) {

booleanresult =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;

}

returnresult;

}

public booleansendBuffer(byte[] mBuffer) {

booleanresult =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;

}

returnresult;

}

private classReadThreadextendsThread {

@Override

public voidrun() {

super.run();

while(!isStop&& !isInterrupted()) {

intsize;

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:"+newString(buffer,0,size));

}

if(null!=onDataReceiveListener) {

onDataReceiveListener.onDataReceive(buffer,size);

}

}

Thread.sleep(10);

}catch(Exception e) {

e.printStackTrace();

return;

}

}

}

}

/**

*关闭串口

*/

public voidcloseSerialPort() {

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即可接受数据;


6、温馨提醒:在使用流的时候记得要及时关闭流,没有关闭的话因为流资源就会被他一直占用,这样别人想用就没有办法用了,所以这回造成资源浪费,而且要关闭资源,最好写在finally中,如果这个东西出现一个异常,你就关不掉了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容