Serial programming principles and implementation methods for Android Development

Source: Internet
Author: User

When talking about serial programming, we have to mention JNI. we have to mention the file descriptor class in Java API: FileDescriptor. Below I will analyze and explain some knowledge points and source code of JNI, FileDescriptor, and serial port respectively. Here we mainly refer to the open-source project android-serialport-api.

Basic knowledge about serial port programming: for serial port programming, we only need to set a series of serial ports, and then open the serial port. For these operations, we can refer to the source code of the serial port debugging assistant for learning. In Java, if you want to implement the serial port read/write function, you only need to operate the file device class: FileDescriptor. Other things are done by the driver! Of course, if you want to know, you have to look at the driver code. Here, we do not want to describe the driver. We only briefly describe the implementation method of the application layer.

(1) JNI:
There are many articles about JNI on the Internet, so I don't want to explain more. If you want to know more, you can view the technical articles on the cloud walk. They are well written and fully analyzed, in this article, I emphasize three points:
1. How to package the compiled SO file into the APK? (The method is simple. Create the libs/armeabi folder in the project directory and Copy the SO file to this directory)
2. What should I pay attention to when naming? (In the compiled SO file, rename the file to libfilename. so. Here, filename. so is the file generated after compilation)
3. Compile the MakeFile file. (For more information, see the JNI-related project Writing Method in the package/apps directory)
This is the key code:Copy codeThe Code is as follows: <span style = "font-size: 18px;"> 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 with flags 0x % x", path_utf, O_RDWR | flags );
Fd = open (path_utf, O_RDWR | flags );
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 ("logging ing 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;
}
}
</Span>

(2) FileDescritor:
An instance of the file descriptor class is used as an opaque handle to a structure related to the underlying machine. This structure represents another source or receiver of an open file, open socket, or byte. The main purpose of a file descriptor is to create a FileInputStream or FileOutputStream containing the structure. This is an API description, which is not easy to understand. In fact, FileDescritor is used to read and write a file.
(3) Implementation of serial communication details
1) create a project: SerialDemo package name: org. winplus. serial, and create two jni and libs folders and one org. winplus. serial. utils folder under the project directory, such:
2) create a class: SerialPortFinder and add the following code:Copy codeThe Code is as follows: <span style = "font-size: 18px;"> package org. winplus. serial. utils;

Import java. io. File;
Import java. io. FileReader;
Import java. io. IOException;
Import java. io. LineNumberReader;
Import java. util. Iterator;
Import java. util. Vector;

Import android. util. Log;

Public class SerialPortFinder {

Private static final String TAG = "SerialPort ";

Private Vector <Driver> mDrivers = null;

Public class Driver {
Public Driver (String name, String root ){
MDriverName = name;
MDeviceRoot = root;
}

Private String mDriverName;
Private String mDeviceRoot;
Vector <File> mDevices = null;

Public 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;
}
}

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 ()]);
}
}
</Span>

The above class is described in detail in "android-serialport-api serial port Tool Test essay", so I will not talk about it.
3) create a new SerialPort class. This class is mainly used to load SO files. Open and Close the serial port through JNI.Copy codeThe Code is as follows: <span style = "font-size: 18px;"> package org. winplus. serial. utils;

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;

Import android. util. Log;

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, int flags)
Throws SecurityException, IOException {

/* Check access permission */
If (! Device. canRead () |! Device. canWrite ()){
Try {
/* Missing read/write permission, trying to chmod the file */
Process su;
Su = runtime.getruntime(cmd.exe c ("/system/bin/su ");
String cmd = "chmod 666" + device. getAbsolutePath () + "\ n"
+ "Exit \ n ";
Su. getOutputStream (). write (cmd. getBytes ());
If (su. waitFor ()! = 0) |! Device. canRead ()
|! Device. canWrite ()){
Throw new SecurityException ();
}
} Catch (Exception e ){
E. printStackTrace ();
Throw new SecurityException ();
}
}

MFd = open (device. getAbsolutePath (), baudrate, flags );
If (mFd = null ){
Log. e (TAG, "native open returns null ");
Throw new IOException ();
}
MFileInputStream = new FileInputStream (mFd );
MFileOutputStream = new FileOutputStream (mFd );
}

// Getters and setters
Public InputStream getInputStream (){
Return mFileInputStream;
}

Public OutputStream getOutputStream (){
Return mFileOutputStream;
}

// JNI
Private native static FileDescriptor open (String path, int baudrate,
Int flags );

Public native void close ();

Static {
System. loadLibrary ("serial_port ");
}
}
</Span>

4) Create a New MyApplication that inherits android. app. Application to initialize and disable the serial port.Copy codeThe Code is as follows: <span style = "font-size: 18px;"> package org. winplus. serial;

Import java. io. File;
Import java. io. IOException;
Import java. security. InvalidParameterException;

Import org. winplus. serial. utils. SerialPort;
Import org. winplus. serial. utils. SerialPortFinder;

Import android. content. SharedPreferences;

Public class MyApplication extends android. app. Application {
Public SerialPortFinder mSerialPortFinder = new SerialPortFinder ();
Private SerialPort mSerialPort = null;

Public SerialPort getSerialPort () throws SecurityException, IOException, InvalidParameterException {
If (mSerialPort = null ){
/* Read serial port parameters */
SharedPreferences sp = getSharedPreferences ("android_serialport_api.sample_preferences", MODE_PRIVATE );
String path = sp. getString ("DEVICE ","");
Int baudrate = Integer. decode (sp. getString ("BAUDRATE", "-1 "));

/* Check parameters */
If (path. length () = 0) | (baudrate =-1 )){
Throw new InvalidParameterException ();
}

/* Open the serial port */
MSerialPort = new SerialPort (new File (path), baudrate, 0 );
}
Return mSerialPort;
}

Public void closeSerialPort (){
If (mSerialPort! = Null ){
MSerialPort. close ();
MSerialPort = null;
}
}
}
</Span>

5) Create an abstract inherited Activity class, which is mainly used to read information from the serial port.Copy codeThe Code is as follows: <span style = "font-size: 18px;"> package org. winplus. serial;

Import java. io. IOException;
Import java. io. InputStream;
Import java. io. OutputStream;
Import java. security. InvalidParameterException;

Import org. winplus. serial. utils. SerialPort;

Import android. app. Activity;
Import android. app. AlertDialog;
Import android. content. DialogInterface;
Import android. content. DialogInterface. OnClickListener;
Import android. OS. Bundle;

Public abstract class SerialPortActivity extends Activity {
Protected MyApplication 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;

/**
* Special attention should be paid to the read here. It will always wait for the data and wait until it is deserted. If you want to determine whether the acceptance is complete, you can only set the end identifier or perform other special operations.
*/
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 OnClickListener (){
Public void onClick (DialogInterface dialog, int which ){
SerialPortActivity. this. finish ();
}
});
B. show ();
}

@ Override
Protected void onCreate (Bundle savedInstanceState ){
Super. onCreate (savedInstanceState );
MApplication = (MyApplication) getApplication ();
Try {
MSerialPort = mApplication. getSerialPort ();
MOutputStream = mSerialPort. getOutputStream ();
MInputStream = mSerialPort. getInputStream ();

/* Create a processing ing 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 ();
}
}
</Span>

6) Compile the string. xml and baudrates. xml files.
Add the following content to the string. xml file:Copy codeThe Code is as follows: <span style = "font-size: 18px;"> <string name = "error_configuration"> Please configure your serial port first. </string>
<String name = "error_security"> You do not have read/write permission to the serial port. </string>
<String name = "error_unknown"> The serial port can not be opened for an unknown reason. </string>
</Span>

In the baudrates. xml file, addCopy codeThe Code is as follows: <span style = "font-size: 18px;"> <? Xml version = "1.0" encoding = "UTF-8"?>
<Resources>

<String-array name = "baudrates_name">
<Item> 50 </item>
<Item> 75 </item>
<Item> 110 </item>
<Item> 134 </item>
<Item> 150 </item>
<Item> 200 </item>
<Item> 300 </item>
<Item> 600 </item>
<Item> 1200 </item>
<Item> 1800 </item>
<Item> 2400 </item>
<Item> 4800 </item>
<Item> 9600 </item>
<Item> 19200 </item>
<Item> 38400 </item>
<Item> 57600 </item>
<Item> 115200 </item>
<Item> 230400 </item>
<Item> 460800 </item>
<Item> 500000 </item>
<Item> 576000 </item>
<Item> 921600 </item>
<Item> 1000000 </item>
<Item> 1152000 </item>
<Item> 1500000 </item>
<Item> 2000000 </item>
<Item> 2500000 </item>
<Item> 3000000 </item>
<Item> 3500000 </item>
<Item> 4000000 </item>
</String-array>
<String-array name = "baudrates_value">
<Item> 50 </item>
<Item> 75 </item>
<Item> 110 </item>
<Item> 134 </item>
<Item> 150 </item>
<Item> 200 </item>
<Item> 300 </item>
<Item> 600 </item>
<Item> 1200 </item>
<Item> 1800 </item>
<Item> 2400 </item>
<Item> 4800 </item>
<Item> 9600 </item>
<Item> 19200 </item>
<Item> 38400 </item>
<Item> 57600 </item>
<Item> 115200 </item>
<Item> 230400 </item>
<Item> 460800 </item>
<Item> 500000 </item>
<Item> 576000 </item>
<Item> 921600 </item>
<Item> 1000000 </item>
<Item> 1152000 </item>
<Item> 1500000 </item>
<Item> 2000000 </item>
<Item> 2500000 </item>
<Item> 3000000 </item>
<Item> 3500000 </item>
<Item> 4000000 </item>
</String-array>

</Resources>
</Span>

7) start writing the interface: Add two edit boxes in the main. xml layout file, one for sending commands and the other for receiving commands:Copy codeThe Code is as follows: <span style = "font-size: 18px;"> <? Xml version = "1.0" encoding = "UTF-8"?>
<LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android"
Android: layout_width = "fill_parent"
Android: layout_height = "fill_parent"
Android: orientation = "vertical">

<EditText
Android: id = "@ + id/EditTextReception"
Android: layout_width = "fill_parent"
Android: layout_height = "fill_parent"
Android: layout_weight = "1"
Android: gravity = "top"
Android: hint = "suggestion"
Android: isScrollContainer = "true"
Android: scrollbarStyle = "insideOverlay">
</EditText>

<EditText
Android: id = "@ + id/EditTextEmission"
Android: layout_width = "fill_parent"
Android: layout_height = "wrap_content"
Android: hint = "Emission"
Android: lines = "1">
</EditText>

</LinearLayout>
</Span>

8) implementation of the SerialDemoActivity class:Copy codeThe Code is as follows: <span style = "font-size: 18px;"> package org. winplus. serial;

Import java. io. IOException;

Import android. OS. Bundle;
Import android. view. KeyEvent;
Import android. widget. EditText;
Import android. widget. TextView;
Import android. widget. TextView. oneditexceptionlistener;

Public class SerialDemoActivity extends SerialPortActivity {
EditText mencryption tion;

@ Override
Protected void onCreate (Bundle savedInstanceState ){
Super. onCreate (savedInstanceState );
SetContentView (R. layout. main );

// SetTitle ("Loopback test ");
Mexception = (EditText) findViewById (R. id. EditTextReception );

EditText Emission = (EditText) findViewById (R. id. EditTextEmission );
Emission. setoneditexceptionlistener (new oneditexceptionlistener (){
Public boolean oneditexception (TextView v, int actionId, KeyEvent event ){
Int I;
CharSequence t = v. getText ();
Char [] text = new char [t. length ()];
For (I = 0; I <t. length (); I ++ ){
Text [I] = t. charAt (I );
}
Try {
MOutputStream. write (new String (text). getBytes ());
MOutputStream. write ('\ n ');
} Catch (IOException e ){
E. printStackTrace ();
}
Return false;
}
});
}

@ Override
Protected void onDataReceived (final byte [] buffer, final int size ){
RunOnUiThread (new Runnable (){
Public void run (){
If (mexception! = Null ){
Mexception. append (new String (buffer, 0, size ));
}
}
});
}
}
</Span>

Here, the code is basically finished. The following figure shows how to implement the JNI layer. To implement JNI, you must first generate the header file. The header file generation method is also very simple. We compile the project and enter javah org on the terminal. winplus. serial. utils. serialPort will generate the header file: org_winplus_serial_utils_SerialPort.h. You can name this header file as needed. We will name it: SerialPort. h copy it to the newly created directory jni and create the SerialPort. c file. The Code of these two files will not be pasted out. Go directly to the uploaded code.
(4) Application of serial ports, which can realize scanning head, fingerprint recognition, and other special applications of peripheral USB-to-serial ports
It's quite tedious. The above is just a simplified version of the open-source android-serialport-api project. To learn more about this project, click here! This is the case. We are going to see Zhou Gong later!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.