Android SerialPort serial communication implementation, including cpp

What is mentioned here is that android calls the so library written by cpp through JNI to complete the serial communication function.

1: The java layer can directly encapsulate several native methods for calling, such as opening the serial port, setting the properties of the serial port, sending content, receiving content, closing the serial port, etc. In this way, basic communication can be carried out through the serial port.

Something like this:

public native long openPort(String name, long lBaudRate);

public native boolean setParams(long fd, int baudRate, int byteSize, int stopBits, int parity, boolean setRTS, boolean setDTR, int flags);

public native byte[] readBytes(long fd, int bytecount);

public native boolean writeBytes(long fd, byte[] bytes);

public native int[] getBuffersBytesCount(long fd);

public native boolean closePort(long id);

This is a basic serial call. Next you need to write cpp code, you can create .h and .cpp files. h file declares methods and variables. cpp file to implement related content.

Here I will not explain how to implement it in detail, but mainly explain the general idea:

Open the serial port through the following implementation:

jlong hComm = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

Setting the baud rate is achieved by:

cfmakeraw(settings);
cfsetspeed(settings, baudRateValue);

In addition, other properties of the serial port need to be set:

termios *settings = new termios();
tcgetattr(hComm, settings);
settings->c_cflag = CS8 | CLOCAL | CREAD;
settings->c_iflag = 0;
settings->c_oflag = 0;
settings->c_lflag = 0;
settings->c_cc[VMIN] = 1;
settings->c_cc[VTIME] = 0;
tcsetattr(hComm, TCSANOW, settings);

For the explanation of specific attributes, such as cflag is the control flow attribute, iflag is the input flow attribute, oflag is the output flow attribute, etc., you can find the relevant explanation of termios.

For a detailed explanation of termios, you can also refer to the following article:

Detailed explanation of termios_mr_raptor’s blog-CSDN blog

Close the serial port through the following implementation:

close(portHandle)

Reading data is achieved through the following:

int result = read(portHandle, lpBuffer + byteCount, byteCount);

Among them, lpBuffer is a pointer to the content to be read, and is ready to store the read content:

jbyte *lpBuffer = new jbyte[byteCount];

Note that it is best to read the content size of the input buffer before reading, otherwise the direct reading may get stuck if you don’t know the byteCount.

The read input buffer size is:

jint ibuf = -1;
ioctl(mPortHandle, FIONREAD, &ibuf);

Similarly, the read output buffer size is:

jint obuf = -1;

ioctl(portHandle, TIOCOUTQ, &obuf);

Writing data is achieved through the following:

write(portHandle, buffer, bufferSize)

Here is the calling code for reading and writing. The file name is unistd.h, and the path is in the sysroot\usr\include\bits\fortify directory.

__BIONIC_FORTIFY_INLINE
ssize_t read(int fd, void* const __pass_object_size0 buf, size_t count)
        __overloadable
        __error_if_overflows_ssizet(count, read)
        __error_if_overflows_objectsize(count, __bos0(buf), read) {
#if __ANDROID_API__ >= __ANDROID_API_L__
    size_t bos = __bos0(buf);

    if (!__bos_trivially_ge_no_overflow(bos, count)) {
        return __read_chk(fd, buf, count, bos);
    }
#endif /* __ANDROID_API__ >= __ANDROID_API_L__ */
    return __call_bypassing_fortify(read)(fd, buf, count);
}

__BIONIC_FORTIFY_INLINE
ssize_t write(int fd, const void* const __pass_object_size0 buf, size_t count)
        __overloadable
        __error_if_overflows_ssizet(count, write)
        __error_if_overflows_objectsize(count, __bos0(buf), write) {
#if __ANDROID_API__ >= __ANDROID_API_N__
    size_t bos = __bos0(buf);

    if (!__bos_trivially_ge_no_overflow(bos, count)) {
        return __write_chk(fd, buf, count, bos);
    }
#endif /* __ANDROID_API__ >= __ANDROID_API_N__ */
    return __call_bypassing_fortify(write)(fd, buf, count);
}

The files where these related functions are located are:

In the androidstudio\sdk\
dk\21.1.6352462\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\bits\fortify directory, you can refer to it.

Then the basic call sequence is to open the serial port, set the serial port parameters, then write data into the serial port, then read the size of the input buffer, read the data after getting the size, and close the serial port after completing the serial port operation.

2: Here is a sample code of serial port operation in other links for reference, the link is: C ioctl(fd, TIOCEXCL);

There are many similar codes for reference under this link, and you can browse other examples under this link by the way. The implementation of the serial port operation here is similar:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <time.h>
#include <sys/ioctl.h>

#define BUFSIZE (65536 + 100)
unsigned char readbuf[BUFSIZE];

static struct termios term;
static struct termios gOriginalTTYAttrs;

void sendCmd(int fd, void *buf, size_t size);
void sendStrCmd(int fd, char *buf);
int readResp(int fd);
int initConn(int speed);
void closeConn(int fd);
void sendAt(int fd);
void at(int fd);

void sendCmd(int fd, void *buf, size_t size) {
    if(write(fd, buf, size) == -1) {
        fprintf(stderr, "sendCmd error. %s\
", strerror(errno));
        exit(1);
    }
}

void sendStrCmd(int fd, char *buf) {
    sendCmd(fd, buf, strlen(buf));
}

int readResp(int fd) {
    int len = 0;
    struct timeval timeout;
    int nfds = fd + 1;
    fd_set readfds;
    int select_ret;
    
    FD_ZERO( &readfds);
    FD_SET(fd, &readfds);
    
    // Wait a second
    timeout.tv_sec = 1;
    timeout.tv_usec = 500000;
    
    while ((select_ret = select(nfds, & amp; readfds, NULL, NULL, & amp; timeout)) > 0) {
        len + = read(fd, readbuf + len, BUFSIZE - len);
        FD_ZERO( &readfds);
        FD_SET(fd, &readfds);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
    }
    if (len > 0) {
    }
    readbuf[len] = 0;
    fprintf(stderr,"%s", readbuf);
    return len;
}

int initConn(int speed) {
    int fd = open("/dev/dlci.spi-baseband.extra_0", O_RDWR | O_NOCTTY);
    
    if(fd == -1) {
        fprintf(stderr, "%i(%s)\
", errno, strerror(errno));
        exit(1);
    }
    
    ioctl(fd, TIOCEXCL);
    fcntl(fd, F_SETFL, 0);
    
    tcgetattr(fd, & term);
    gOriginalTTYAttrs = term;
    
    cfmakeraw( & term);
    cfsetspeed( & term, speed);
    term.c_cflag = CS8 | CLOCAL | CREAD;
    term.c_iflag = 0;
    term.c_oflag = 0;
    term.c_lflag = 0;
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 0;
    tcsetattr(fd, TCSANOW, & term);
    
    return fd;
}

void closeConn(int fd) {
    tcdrain(fd);
    tcsetattr(fd, TCSANOW, &gOriginalTTYAttrs);
    close(fd);
}

void sendAt(int fd) {
    char cmd[5];
    sprintf(cmd,"AT\r");
    sendCmd(fd, cmd, strlen(cmd));
}

void at(int fd) {
    sendAt(fd);
    for (;;) {
        if(readResp(fd) != 0) {
            if(strstr((const char *)readbuf,"OK") != NULL) {
                break;
            }
        }
        sendAt(fd);
    }
}

int main(int argc, char **argv) {
    int fd;
    char cmd[1024];
    if(argc < 2) {
        fprintf(stderr,"usage: %s <pdu data>\
",argv[0]);
        exit(1);
    }
    fd = initConn(115200);
    at(fd);
    sendStrCmd(fd, "AT + CMGF=0\r");
    readResp(fd);
    sprintf(cmd, "AT + CMGS=%ld\r", strlen(argv[1])/2 - 1);
    sendStrCmd(fd, cmd);
    readResp(fd);
    sprintf(cmd,"%s\032", argv[1]);
    sendStrCmd(fd, cmd);
    readResp(fd);
    closeConn(fd);
    return 0;
}