Send and receive data communication between Android Virtual Device and serial debugging assistant

Achieving goal: Send and receive data communication between Android Virtual Device and serial debugging assistant

1. Code analysis
   AVD serial communication program mainly refers to google's open source serial port class android-serialport-api. The serial port operations mainly include: (1) Open the serial port; (2) ) read the serial port; (3) write the serial port; (4) close the serial port. In this demo, there is only one Activity, which includes the operations of opening the serial port, reading and writing the serial port, and opening the serial port and other operations using JNI, so that Java can call the library written in C language.

1.1 MainActivity.java, mainly includes the button to open the serial port, return the file input and output stream, send and receive buttons, and display the received and sent content in EditText

package com.charles.uart;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final String TAG=MainActivity.class.getSimpleName();   //log信息

    private Button btOpen;
    private Button btSend;
    private Button btRec;
    private EditText mSend;
    private EditText mReception;

    SerialPort sp;

    FileInputStream mInputStream;
    FileOutputStream mOutputStream;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView(R.layout.activity_main);

        //Initialize button and text display
        btOpen= (Button) findViewById(R.id.button);
        btSend= (Button) findViewById(R.id.button2);
        btRec= (Button) findViewById(R.id.button3);
        mSend= (EditText) findViewById(R.id.editText);
        mReception= (EditText) findViewById(R.id.editText2);
        Log.i(TAG,"Button and text display initialization completed");

        //Click to open the serial port
        btOpen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i(TAG,"Clicked the open button");

                /*
                * Open the serial port and return the file input and output stream
                * */
                sp=new SerialPort(new File("/dev/ttyS1"),9600);

                mInputStream= (FileInputStream) sp.getInputStream();
                mOutputStream= (FileOutputStream) sp.getOutputStream();

                Toast.makeText(getApplicationContext(),"open",Toast.LENGTH_SHORT).show();

            }
        });

        //click send
        btSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i(TAG,"clicked the send button");

                try {
                    // get send data
                    String send=mSend.getText().toString();

                    //Send the acquired data
                    mOutputStream.write(send.getBytes());
                    mOutputStream.write('\\n');
                } catch (IOException e) {
                    e.printStackTrace ();
                    return;
                }

                Toast.makeText(getApplicationContext(),"send",Toast.LENGTH_SHORT).show();
            }
        });

        //click to receive
        btRec.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i(TAG,"clicked the receive button");

                int size;

                // store the received data
                byte[] buffer=new byte[64];

                if (mInputStream==null){
                    Log.i(TAG, "No data received, return");
                    return;
                }

                //if data is received
                try {
                    size=mInputStream.read(buffer); //Read the received data

                    if (size>0){
                        onDataReceived(buffer,size); //If the read data is not empty, display the read data in EditText
                    }
                } catch (IOException e) {
                    e.printStackTrace ();
                    return;
                }
            }
        });
    }

    void onDataReceived(final byte[] buffer,final int size){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mReception!=null){
                    mReception.append(new String(buffer,0,size));
                }
            }
        });
    }
}



1.2 SerialPort.java class, mainly calls JNI to open the serial port and returns the file input and output stream

package com.charles.uart;

import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Created by charles.wang_cp on 2016/11/29.
*/

public class SerialPort {

    static {
            System.loadLibrary("serialport");
        }

    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;

    private FileDescriptor mFd;


    public SerialPort(File device,int baudrate){

        if (!device.canWrite()||!device.canWrite()){
            return; //If the node does not have read and write permissions
        }

        //Open the serial port and get the file input and output stream
        mFd=open(device.getAbsolutePath(),baudrate);
   
     mFileInputStream=new FileInputStream(mFd);
        mFileOutputStream=new FileOutputStream(mFd);

    }


    //Get the file input and output stream
    public InputStream getInputStream(){return mFileInputStream;}
    public OutputStream getOutputStream(){return mFileOutputStream;}

    //Call JNI to open the serial port
    private native static FileDescriptor open(String path,int baudrate);
}


1.3 Call JNI to open the serial port
1.3.1 When calling JNI, please make sure that NDK has been configured, there are SDK and NDK configuration paths in local.properties
1.3.2 Note:
The native open method added in SerialPort: there will be Cannot resolve here The corresponding JNI function java_com_charles_uart_SerialPort_open, you can ignore
1.3.3 Compile and generate the bytecode file
Build->Make Project, perform this step to verify that there are no other errors in the project, and generate the .class bytecode file, the generated bytecode file 1.3.4 Use Javah to generate header files under the
build\\intermediates\\classes\\debug folder in the project directory Open cmd and enter the project main directory: D:\\AndroidProjet\\HelloJni01\\app\\src\\ main>javah -d jni -classpath D:\\Users\\charle s.wang_cp\\AppData\\Local\\Android\\sdk\\platforms\\android-25\\android.jar;..\\. .\\build \\intermediates\\classes\\debug com.charles.uart.SerialPort Note: The android-25 here is selected according to the compile SDK API of the project. After executing this command, the jni folder and the corresponding package will be generated header file.h









1.3.5 Create the jni.c file in the jni folder and edit the c source code. Note that you should distinguish between creating a .c or .cpp file. If you create a .cpp and edit the .c source code, it may be compiled when There will be syntax errors, the methods in the C source code come from the definitions in com_charles_uart_SerialPort.h in the

jni.c source code
//
// Created by charles.wang_cp on 2016/11/29.
//

#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:com_charles_uart_SerialPort
*Method:open
*Signature: (Ljava/lang/String;I)Ljava/io/FileDescriptor
*/

JNIEXPORT jobject JNICALL Java_com_charles_uart_SerialPort_open
  (JNIEnv * env, jclass thiz, jstring path, jint baudrate) {

      int fd;
      speed_t speed;
      jobject mFileDescriptor;

      /*check arguments*/
      {
          speed=getBaudrate(baudrate);
          if (speed == -1) {
            /* TODO: throw an exception */
            LOGE("Invalid baudrate");
            return NULL;
          }
      }

      /*opening device*/
          {
              jboolean iscopy;
              const char *path_utf=(*env)->GetStringUTFChars(env,path,&iscopy);
              LOGD("Opening serial port %s", path_utf);
              fd = open(path_utf, O_RDWR);
              LOGD("open() fd = %d", fd);
              (*env)->ReleaseStringUTFChars(env, path, path_utf);
              if (fd == -1)
              {
              /* Throw an exception */
              LOGE("Cannot open port");
              /* TODO: throw an exception */
              return NULL;
              }
          }


      /*configure device*/
          {
              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;


}



1.3.6 Configure gradle.properties file and build.gradle file
First add at the end of gradle.properties: android.useDeprecatedNdk=true
Then add the following configuration in build.gradle


1.3.7 Build->Rebuild Project
This step compiles the code and will generate .so file, the generation path is in the \build\intermediates\ndk\lib folder

2. Create AVD
2.1 Tools->Android->AVD Manager->Create Virtual Device Create Android Virtual Device
Note: Nexus is under the system image in Marshmallow mode , every time the Activity starts and exits, the log will print an unknown error.
As in the figure, avd1 and avd2 are successfully created.

2.2 Start the AVD, and set the serial COM for the AVD. In this demo, set the COM2 for the AVD

. Read and write operations on the serial port
//Get root permission
>adb root
//View the serial port node
>ls -l ttyS*
//Modify system properties
>chown root:root ttyS*
//The node obtains read-write executable permission
>chmod 777 ttyS *
//Close Selinux
>setenforce 0


3. AVD and serial port debugging assistant for serial communication
3.1 Install the serial communication program apk to avd2

3.2 Open the serial port debugging assistant, and set the serial port to COM1, the baud rate is 9600 (the serial port program is set to 9600)

3.3 AVD click "Open the serial port"
3.4 Enter the data to send, for example: Zesusis, click "Send", you can see the received data on the receiving end of the serial debugging assistant

3.5 Click "Receive", and then write the data to be sent on the serial debugging assistant, such as : 2016, you can see the received data on the AVD end So

far , it means that the serial communication debugging between Android and the serial debugging assistant is successful.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326865838&siteId=291194637