This is a simple demo written with reference to the open source project on Google's official website and the serial communication on the Internet. Just contacted NDK programming and serial communication, maybe a lot of writing is not very good, do not like spraying.
Most of the code is in this Google serial open source project: https://code.google.com/p/android-serialport-api/
First, create a new project with c.
Check C++ and build Next all the way.
Before writing code or installing the project, change the NDk used by the project to NDK14 version, otherwise there may be serial port problems (I do not know why, it may be environmental configuration problems), and there is no download of NDK14 (in the case of NDK14). https://developer.android.google.cn/ndk/downloads/older_releases.html Download) and then re-select the NDK path in app - OPen Module Settings - SDK Location in the Android view.
In MainActivity
static {
System.loadLibrary("native-lib");
}
Delete, add in creating SerialPort class
//To load the serial_port library at application startup
static {
System.loadLibrary("serial_port");//Seri_port is written based on the following MakeLists. TXT content.
}
Delete the Cpp file when creating the project, create a new jni folder, and then create a new serial_port. c file in the folder. The main methods of reading and writing serial ports are open and close methods, similar to file reading and writing.
Following the comments in the code below, the green and red arrows on the left indicate that the open method in java is related to the open method in c.
#include <jni.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.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 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;
default: return -1;
}
}
/*
* Java_com_smartlab_blue_serialdemo_SerialPort_open com_smartlab_blue_serialdemo in
* SerialPort_open Is private in the SerialPort class native static FileDescriptor open(...)Method, need to use"_"Connection not available"."
* It is possible to create an open method in this file by using Alt+Enter directly in the SerilPort class. Just change the name of the method I wrote in this class.
* Some need to change the method name according to the method I mentioned above. The following close method configuration is the same
* */
JNIEXPORT jobject JNICALL
Java_com_smartlab_blue_serialdemo_SerialPort_open(JNIEnv *env, jclass type, jstring path, jint baudrate, jint flags) {
int fd;
speed_t speed; //Quote#include <termios.h>
jobject mFileDescriptor;
/* Check arguments */
{
speed=getBaudrate(baudrate);
if (speed==-1){
LOGE("Invalid baudrate");
return NULL;
}
}
/* Opening equipment */
{
jboolean iscapy;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscapy);
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
fd=open(path_utf,O_RDWR | flags | O_NDELAY);//O_RDWR Introduce #include <fcntl.h>
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf );
if (fd==-1){
/* Trigger exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
}
/* Configuration of equipment */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd,&cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
cfmakeraw(&cfg);
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);
if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
}
/* Create a corresponding file descriptor */
{
jclass cFileDescriptor=(*env)->FindClass(env,"java/io/FileDescriptor");
jmethodID iFileDescriptor=(*env)->GetMethodID(env,cFileDescriptor,"<init>","()V");
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
}
return mFileDescriptor;
}
JNIEXPORT void JNICALL
Java_com_smartlab_blue_serialdemo_SerialPort_close(JNIEnv *env, jobject instance) {
jclass SerialPortClass = (*env)->GetObjectClass(env, instance);
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
jobject mFd = (*env)->GetObjectField(env, instance, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
LOGD("close(fd = %d)", descriptor);
close(descriptor);
}
First, annotate the following in the build of app, gradle
//externalNativeBuild {
// cmake {
// path "CMakeLists.txt"
// }
//}
Make the following changes in MakeLists. TXT generated when creating the project
add_library( # Sets the name of the library.
The serial_port // double quotation marks indicate the name here, which is used to load the serial_port library at application startup.
# Provides a relative path to your source file(s). src/main/jni/serial_port.c //The path and name of your new. c file.
target_link_libraries( # Specifies the target library.
The serial_port// double quotation marks say the name here.
app build, add something to gradle
defaultConfig {
applicationId "com.smartlab.blue.serialdemo"
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
/ / add
ndk { moduleName "serial_port" ldLibs "log", "z", "m" }
}
Switch to the Android view and right-click on the app, then select Link C++ Project with Gradle
The first option is CMake, the path is MakeLists. txt, which has just been modified, and then OK.
Note: Sometimes you may not be able to open the read and write permissions of serial ports on Android board. You need to add permissions manually by yourself.
In debugging, if you do not have serial hardware, you can use PC + simulator to complete debugging experiments. Specific operation methods:
1. Open cmd and enter tools folder under sdk directory developed by android.
2. Execute the command emulator @ (the name of your own simulator) - qemu -serial COM3 (COM3 is a serial port of your PC mounted on your simulator by command, of course you can also be COM1 corresponding to your computer); Example: My simulator is 123, the serial port of the computer I mounted on the simulator is COM3, then execute: emulator @123 -qemu -serial COM3
So you can see that the simulator has been started, and then you can deploy the program and run it.
If you open the serial port with the program, the prompt does not have read and write permission. You should use the linux command at the command prompt to give read and write permission: enter shell: adb shell into device directory: cd dev modification permission: chmod 777 ttyS2. (This I have written into the program, no need to add, if the LOG log print shows no permission, then you can only manually execute while explaining the following entry shell, this is the adb shell, your command execution directory must have more than adb shell.exe does not explain)
The modification is basically the same as the above, recommend a blog http://blog.csdn.net/qq_35071078/article/details/73065046 If you don't know the serial port, you can see it.
Here's the main code
public class SerialPort {
private static final String TAG = "SerialPort";
private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate, int flags)
throws SecurityException, IOException {
/* Check the permission to read and write again */
if (!device.canRead() || !device.canWrite()) {
/* If you do not have read and write permissions, try using chmod to give file permissions */
Process su = null;
try {
su = Runtime.getRuntime().exec("/system/xbin/su");
//su = Runtime.getRuntime().exec("su");
String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
Log.e(TAG, "su.waitFor == " + su.waitFor() + " device.canRead = " + device.canRead() + " device.canWrite = " + device.canWrite());
throw new SecurityException();
}
} catch (Exception e) {
Log.e(TAG, "Exception == " + e.toString());
e.printStackTrace();
throw new SecurityException();
} finally {
if (su != null) {
su.destroy();
}
}
}
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "Serial Port Failed to Open >>>>>>>> native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
Log.e(TAG, "****************** Successful serial port opening!**********************");
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
/**
* jni Method Open Serial Port
*
* @param path Path of Serial Port File
* @param baudrate baud rate
* @param flags port
* @return File descriptor
*/
private native static FileDescriptor open(String path, int baudrate,
int flags);
/**
* jni Method Close Serial Port
*/
public native void close();
//To load the serial_port library at application startup
static {
System.loadLibrary("serial_port");
}
}
public abstract class SerialPortActivity extends Activity {
protected Application mApplication;
protected SerialPort mSerialPort;
protected OutputStream mOutputStream;
private InputStream mInputStream;
private ReadThread mReadThread;
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
while(!isInterrupted()) {
int size;
try {
byte[] buffer = new byte[64];
if (mInputStream == null) return;
Log.e("Threads for reading data","233333");
size = mInputStream.read(buffer);
if (size > 0) {
onDataReceived(buffer, size);
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}
private void DisplayError(int resourceId) {
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.setTitle("Error");
b.setMessage(resourceId);
b.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SerialPortActivity.this.finish();
}
});
b.show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApplication = (Application) getApplication();
try {
mSerialPort = mApplication.getSerialPort();
mOutputStream = mSerialPort.getOutputStream();
mInputStream = mSerialPort.getInputStream();
/* Create a receiving thread */
mReadThread = new ReadThread();
mReadThread.start();
} catch (SecurityException e) {
DisplayError(R.string.error_security);
} catch (IOException e) {
DisplayError(R.string.error_unknown);
} catch (InvalidParameterException e) {
DisplayError(R.string.error_configuration);
}
}
protected abstract void onDataReceived(final byte[] buffer, final int size);
@Override
protected void onDestroy() {
if (mReadThread != null)
mReadThread.interrupt();
mApplication.closeSerialPort();
mSerialPort = null;
super.onDestroy();
}
}
public class SerialPortFinder {
private static final String TAG = "SerialPort";
private Vector<Driver> mDrivers = null;
private class Driver {
private Driver(String name, String root) {
mDriverName = name;
mDeviceRoot = root;
}
private String mDriverName;
private String mDeviceRoot;
Vector<File> mDevices = null;
private Vector<File> getDevices() {
if (mDevices == null) {
mDevices = new Vector<File>();
File dev = new File("/dev");
File[] files = dev.listFiles();
int i;
for (i = 0; i < files.length; i++) {
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
Log.d(TAG, "Found new device: " + files[i]);
mDevices.add(files[i]);
}
}
}
return mDevices;
}
public String getName() {
return mDriverName;
}
}
private Vector<Driver> getDrivers() throws IOException {
if (mDrivers == null) {
mDrivers = new Vector<Driver>();
LineNumberReader r = new LineNumberReader(new FileReader(
"/proc/tty/drivers"));
String l;
while ((l = r.readLine()) != null) {
// Issue 3:
// Since driver name may contain spaces, we do not extract
// driver name with split()
String drivername = l.substring(0, 0x15).trim();
String[] w = l.split(" +");
if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
Log.d(TAG, "Found new driver " + drivername + " on "
+ w[w.length - 4]);
mDrivers.add(new Driver(drivername, w[w.length - 4]));
}
}
r.close();
}
return mDrivers;
}
public String[] getAllDevices() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while (itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while (itdev.hasNext()) {
String device = itdev.next().getName();
String value = String.format("%s (%s)", device,
driver.getName());
devices.add(value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
public String[] getAllDevicesPath() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while (itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while (itdev.hasNext()) {
String device = itdev.next().getAbsolutePath();
devices.add(device);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
}
Source code: http://download.csdn.net/download/qq_36462112/10161978