在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得对应;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
通过serial接口可以使Chrome应用进行串口通信。使用serial接口需要在Manifest中声明serial权限: "permissions": [ "serial" ] 本章内容参考自https://crxdoc-zh.appspot.com/apps/serial。
设备通过串口RX,TX和OBLOQ进行通信,因为波特率的不同,发送的数据可能会乱码或者丢包,设备通过发送这条指令,检测设备串口和OBLOQ串口是否已经正常通信。建议设备通信之前先通过这条指令检查串口连接状态。 请求方式: "|1|1|\r" 返回值: "|1|1|\r"表示已经建立串口通信 Arduino样例: softSerial.print("|1|1|\r");
本文向大家介绍C#串口通信实现方法,包括了C#串口通信实现方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#串口通信实现方法。分享给大家供大家参考。具体方法如下: 通过COM1发送数据,COM2接收数据。当COM2接收完本次发送的数据后,向COM1发送信息通知COM1本次数据已发完,COM1接到通知后,再发下一段数据。这样可以确保每次发送的数据都可以被正确接收。 代码如下: 辅助代码
本文向大家介绍Android串口通信封装之OkUSB的示例代码,包括了Android串口通信封装之OkUSB的示例代码的使用技巧和注意事项,需要的朋友参考一下 本文介绍了Android串口通信封装之OkUSB的示例代码,分享给大家。具体如下: Github传送门:OkUSB OkUSB 一个简洁的Android串口通信框架。 功能简介 支持设置波特率 支持设置数据位 支持设置停止位 支持设置校验位
本文向大家介绍Python 串口读写的实现方法,包括了Python 串口读写的实现方法的使用技巧和注意事项,需要的朋友参考一下 1.安装pyserial https://pypi.python.org/pypi/pyserial Doc:http://pythonhosted.org/pyserial/ 使用Python Package Index (PyPi) 2. Demo 3.结果 以上这篇
我试图读取3温度设备使用WinForm和Modbus 485协议。基本上,我必须定期向每个设备写入命令,等待响应,当我得到响应时,处理它。每个设备都有一个唯一的通信地址。为了定期发送命令,我使用了计时器。这就是我发送命令的方式和处理响应的地方: 对于每个设备,等等。这是我的serialport数据接收事件: 所以我的沟通应该是: 然后再次发送命令device1。问题是,我有时得到通信超时错误,我知