An Example of Serial Port Reading and Writing in Android Serial Port Communication

  • 2021-08-31 09:24:39
  • OfStack

On the basis of Android serial communication: basic knowledge combing, I combined with the example of using serial port in my project, summarized;

Android uses jni to read and write serial devices directly. There are already open source projects on the Internet. This paper is based on the adjustment and optimization of the use of open source projects on the Internet in actual projects;
Google Serial Open Source Project

The following is the relevant code and introduction in my project:

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 }, 
}; 
 
/* 
 *  For sth 1 Classes register local methods  
 */ 
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; 
} 
 
/* 
 *  Register local methods for all classes  
 */ 
static int registerNatives(JNIEnv* env) { 
 const char* kClassName = "com/jerome/serialport/SerialPort"; // Specify the class to register  
 return registerNativeMethods(env, kClassName, gMethods, 
   sizeof(gMethods) / sizeof(gMethods[0])); 
} 
 
/* 
 * System.loadLibrary("lib") Called when  
 *  If you return successfully JNI Version ,  Failure return -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)) { // Registration  
  return -1; 
 } 
 // Success  
 result = JNI_VERSION_1_4; 
 
 return result; 
} 

Pay attention to modifying const char* kClassName = "com/jerome/serialport/SerialPort" at compile time; For your Java layer and jni corresponding package name;

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) 

If you want to modify the name of the generated so file, modify 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; 
 
/** 
 *  Serial port operation class  
 * 
 * @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; 
 } 
 
 /** 
  *  Initialize serial port information  
  */ 
 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(); 
 } 
 
 /** 
  *  Send instructions to serial port  
  * 
  * @param cmd 
  * @return 
  */ 
 public boolean sendCmds(String cmd) { 
  boolean result = true; 
  byte[] mBuffer = (cmd+"\r\n").getBytes(); 
// Note: In my project, you need to add after each send \r\n You can modify it according to the project, or you can remove it and send it directly 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); 
// Note: In my project, you need to add after each send \r\n You can modify it according to the project, or you can remove it and send it directly 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; 
    } 
   } 
  } 
 } 
 
 /** 
  *  Close the serial port  
  */ 
 public void closeSerialPort() { 
  sendShellCommond1(); 
  isStop = true; 
  if (mReadThread != null) { 
   mReadThread.interrupt(); 
  } 
  if (mSerialPort != null) { 
   mSerialPort.close(); 
  } 
 } 
  
}

5. Usage:

a, configure ndk development environment, specific Baidu 1;
b, create a new jni folder under the project root directory, and put Android. mk and SerialPort. cpp in it;
Enter the jni directory in c and ndk, compile and generate so files, and the default so is generated under libs/armeabi;
d, create a new com. jerom. serialport directory, and put SerialPort and SerialPortUtil in it;
f, initialize SerialPortUtil where you want to use it, and realize the callback interface OnDataReceiveListener to accept data;

Summary:

1. Serial port sending is essentially writing byte stream to serial port equipment (similar to file operation), and serial port reading is also 1;
2. The correspondence between jni and Java and native;


Related articles: