`

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

 
阅读更多
在Android串口通信:基本知识梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基础上,我结合我项目中使用串口的实例,进行总结;

Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目见:https://code.google.com/p/android-serialport-api/

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

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得对应;
4
0
分享到:
评论
8 楼 严健严健 2016-11-02  
非常感谢!!!!!!!!!
7 楼 manlan123 2016-10-12  
请问 sendBuffer  sendCmds 里面传入的数据是什么样的啊? 能贴一下吗
6 楼 huangzongwu 2016-05-17  
稍微改改就可以用在自己的项目,实在是太感谢了,csdn上看到的另一篇用HAL的根本没法编译
5 楼 gqdy365 2016-02-15  
zyzyzy123 写道
gqdy365 写道
zyzyzy123 写道
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊

完成的jni部分已经都贴出来了,你要自己编译一下,生成SO文件才能用,其他代码都是我项目相关的代码,没办法分享;

SerialPortUtil.java,这个文件代码贴进去有错误,我主要是通过串口向gprs发送数据,有没有相关的代码啊


跟GPRS通信的没有现成的代码,不过jni串口通信这一块可以共用
4 楼 zyzyzy123 2015-12-18  
gqdy365 写道
zyzyzy123 写道
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊

完成的jni部分已经都贴出来了,你要自己编译一下,生成SO文件才能用,其他代码都是我项目相关的代码,没办法分享;

SerialPortUtil.java,这个文件代码贴进去有错误,我主要是通过串口向gprs发送数据,有没有相关的代码啊
3 楼 gqdy365 2015-12-18  
zyzyzy123 写道
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊

完成的jni部分已经都贴出来了,你要自己编译一下,生成SO文件才能用,其他代码都是我项目相关的代码,没办法分享;
2 楼 zyzyzy123 2015-12-17  
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊
1 楼 YY793924556 2015-11-23  
能否提供一下源码啊 谢谢

相关推荐

    Android串口通信实例

    在Android开发中,串口通信(Serial Port Communication)是一种重要的技术,它允许设备间通过串行数据线进行双向通信。这个“Android串口通信实例”项目是基于Android Studio的一个实战项目,已经过测试,能够有效...

    Android串口通信(Android Studio)

    在Android开发中,串口通信(Serial Port Communication)是一种重要的技术,它允许设备之间通过串行接口进行数据交换。在Android Studio环境下实现串口通信,开发者可以构建与硬件设备交互的应用,例如读取传感器...

    安卓串口通信实例附源码(合集)

    在Android系统中,串口通信通常涉及到UART(通用异步收发传输器)接口,它允许设备间进行全双工通信。Android设备通常不直接支持串口API,但可以通过Java的`java.io`包中的`SerialPort`类或第三方库如Android ...

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

    Android串口通信之串口读写实例 Android串口通信之串口读写实例是Android开发中的一个重要知识点。本文将对串口读写实例进行详细的介绍,包括串口通信的基本知识、串口读写的实现方式、jni直接进行串口设备的读写等...

    Android串口(AndroidSerialPort)通信代码,快速学会串口通信的demo。注释超全

    总之,AndroidSerialPort库为Android开发者提供了一个简洁、易用的接口,用于处理串口通信。结合其详尽的注释,即使没有太多串口通信经验的开发者也能迅速上手。通过实践,你可以将这个知识应用于各种项目,如远程...

    Android串口通信Demo

    在Android开发中,串口通信(Serial Communication)是一种常见的硬件接口技术,用于设备间的通信,例如在嵌入式系统、物联网(IoT)项目或移动设备与外部硬件设备交互时。"Android串口通信Demo"是指一个示例项目,...

    Android 串口编程实例

    这些库提供了API接口,使得应用程序可以读写串口数据。 4. **`SerialDemo`项目解析**: `SerialDemo`是一个示例项目,用于演示如何在Android应用中实现串口通信。项目可能包含以下几个关键部分: - **初始化串口*...

    Android串口通信demo(AS)

    《Android串口通信实战教程——基于Android Studio的Demo解析》 在移动开发领域,尤其是在物联网应用中,Android设备与外部硬件设备的交互是常见的需求,其中串口通信扮演着重要的角色。本文将深入探讨如何在...

    Android串口开发框架

    为了克服这一难题,开发者们构建了各种Android串口开发框架,以便于应用程序能够方便地与外部硬件设备(如传感器、打印机等)通过串行接口进行数据交换。本文将深入探讨“Android串口开发框架”的核心概念、应用场景...

    Android 串口通信实例 基于eclipse开发

    基于开源的android_serialport_api,实现串口数据的读写 设置设备打开的串口号和波特率 如:/dev/ttyUSB0 115200 public interface Reader { public boolean open() throws SecurityException, IOException; //...

    Android 串口开发demo

    "Android 串口开发demo"是一个实例,它展示了如何在Android应用中通过串行通信接口(Serial Port)发送命令到硬件设备,并接收其反馈。下面将详细阐述Android串口开发的相关知识点。 1. **Android权限管理**: 在...

    c++串口读写实例源代码

    本资源“c++串口读写实例源代码”是针对C++编程语言和Visual C++(VC)环境设计的,用于实现串口的读取与写入功能。下面我们将详细探讨这个主题涉及的知识点。 1. **C++编程语言**:C++是一种通用的、面向对象的...

    Android OTG USB 串口通信 示例,不需要使用root 权限

    对于串口通信,通常需要一个USB转串口芯片(如FTDI或CH340),使得Android设备能够通过USB接口与串行设备交互。 描述中提到的"广播注册"和"meta-data注册"是Android应用程序生命周期中的重要部分。在不获取root权限...

    串口通信:大家最常用的串口精灵

    - **串口通信的基本概念**:包括串行接口、波特率、数据位、停止位、奇偶校验等。 - **MSComm控件的使用**:熟悉控件的属性、方法和事件,如`Open`、`Input`、`Output`、`SetPortOpen`等。 - **C++编程基础**:包括...

    android串口通信google官方demo

    在Android平台上进行串口通信是实现设备间交互或者与硬件设备连接的重要手段。Google官方提供了一个名为"android-serialport-api"的项目,用于帮助开发者更方便地在Android应用中集成串口通信功能。这个项目的主要...

    android串口读写

    在Android系统中,串口通信(Serial Communication)是一种常见的硬件接口技术,用于设备间的通信。在标题"android串口读写"中,我们关注的是如何在Android平台上实现串口的数据发送与接收。描述中提到的"demo"是一...

    串口通信实例 (C#)

    串口通信,也称为串行通信,是一种古老但仍然广泛使用的通信方式,尤其在嵌入式系统、工业设备以及物联网(IoT)设备中。在本文中,我们将深入探讨如何利用C#语言进行串口通信,并通过实例演示来加深理解。 C#提供了...

    Android SerialPort串口读取demo

    总之,"Android SerialPort串口读取demo"是一个实现Android与硬件设备串口通信的实例,涉及了Android平台的串口配置、数据读取和设备交互等多个关键环节。通过这个项目,开发者可以学习到如何在Android上实现串口...

    Android studio 串口通信

    在Android开发中,有时我们需要与外部硬件设备进行通信,例如Arduino、Raspberry Pi等,这时就需要用到串口通信技术。本文将详细讲解如何在Android Studio环境下实现串口通信,并提供一个名为`SerialPort_Test`的...

    Android 串口通信库源码

    **Android 串口通信库源码详解** 在Android开发中,串口通信是连接外部硬件设备,如传感器、Arduino等微控制器,进行数据交互的重要手段。Android 串口通信库源码提供了一套完整的解决方案,涵盖了原生层(C/C++)...

Global site tag (gtag.js) - Google Analytics