#include #include #include #include #include #include #include #include #include #include "include/gpiod.h" #define TAG "SerialApp" #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) typedef struct comport_s { char devname[12]; unsigned int databit, parity, stopbit, flowctrl; long baudrate; int fd; int frag_size; }comport_t; comport_t *comport; // 将int类型的波特率转换成可以识别的波特率 static speed_t getBaudRate(int 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; } } extern "C" JNIEXPORT int JNICALL Java_com_example_serial_RS485Control_openSerialPort(JNIEnv *env, jclass clazz, jstring path, jlong baud_rate, jint data_bits, jint parity, jint stop_bits, jint flow_control) { // TODO: implement openSerialPort() speed_t speed; struct termios old_cfg, new_cfg; int old_flags; long temp; comport = (comport_t *) malloc(sizeof(comport_t)); if (comport == NULL) { LOGE("Failed to allocate memory for comport structure."); return -1; } memset(comport, 0, sizeof(comport)); // mSend_len = max_len; // 检查波特率是否合法 { speed = getBaudRate(baud_rate); if (speed == -1) { LOGE("Invalid buad rate"); return -1; } } // 打开串口设备 { jboolean iscopy; // 获取String字符串path中的UTF8编码,并将其转换成C中的字符串 const char* devname = (*env).GetStringUTFChars(path, &iscopy); LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR); strncpy(comport->devname, devname, 12); comport->baudrate = baud_rate; comport->fd = -1; comport->frag_size = 128; comport->databit = data_bits; comport->parity = parity; comport->flowctrl = flow_control; comport->stopbit = stop_bits; if( !strstr(comport->devname, "tty") ) { LOGE("Open Not a tty device \" %s\" \n", comport->devname); return -2; } comport->fd = open(comport->devname, O_RDWR); if ( comport->fd < 0 ) { if( strstr(strerror(errno), "Permission denied") != nullptr ) return 1; LOGE("open port failed:%s\n", strerror(errno)); return -3; } LOGD("Open device %s", comport->devname); // 释放JNI字符串的UTF8编码 (*env).ReleaseStringUTFChars(path, devname); if( (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK))) { if(-1 == tcflush(comport->fd, TCIOFLUSH)) { LOGE("open port failed:%s\n", strerror(errno)); return -4; } } else { LOGE("open port failed:%s\n", strerror(errno)); return -5; } if ( 0 != tcgetattr(comport->fd, &old_cfg)) { LOGE("open port failed:%s\n", strerror(errno)); return -6; } } { LOGD("starting configuring serial port"); old_cfg.c_cflag &= ~CSIZE; old_cfg.c_lflag &= ~ICANON; old_cfg.c_lflag &= ~ECHO; old_cfg.c_lflag &= ~ECHOE; old_cfg.c_lflag &= ~ECHONL; old_cfg.c_lflag &= ~ISIG; old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY); old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); old_cfg.c_oflag &= ~OPOST; old_cfg.c_oflag &= ~ONLCR; old_cfg.c_cflag |= CREAD | CLOCAL; switch (comport->databit) { case 7: old_cfg.c_cflag |= CS7; break; case 6: old_cfg.c_cflag |= CS6; break; case 5: old_cfg.c_cflag |= CS5; break; case 8: old_cfg.c_cflag |= CS8; break; } switch(comport->parity) { case 1: old_cfg.c_cflag |= (PARENB | PARODD); old_cfg.c_cflag |= (INPCK | ISTRIP); break; case 2: old_cfg.c_cflag |= PARENB; old_cfg.c_cflag &= ~PARODD; old_cfg.c_cflag |= (INPCK | ISTRIP); break; case 0: old_cfg.c_cflag &= ~PARENB; break; } if(1 != comport->stopbit) { old_cfg.c_cflag |= CSTOPB; } else { old_cfg.c_cflag &= ~CSTOPB; } // set flow control // 1: software control; 2:hardware control; 0: none switch(comport->flowctrl) { case 1: old_cfg.c_cflag &= ~(CRTSCTS); old_cfg.c_iflag |= (IXON | IXOFF); break; case 2: old_cfg.c_cflag |= CRTSCTS; old_cfg.c_iflag &= ~(IXON | IXOFF); break; case 0: old_cfg.c_cflag &= ~(CRTSCTS); break; } // set baudRate cfsetispeed(&old_cfg, speed); cfsetospeed(&old_cfg, speed); new_cfg.c_cc[VMIN] = 10; new_cfg.c_cc[VTIME] = 0; // 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入) if(tcsetattr(comport->fd, TCSANOW, &old_cfg)) { LOGE("tcsetattr() failed:%s", strerror(errno)); close(comport->fd); return -1; } LOGD("Connected device \" %s \" successfully\n", comport->devname); LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", comport->devname, comport->databit, comport->stopbit, comport->parity, comport->flowctrl); } return 0; } extern "C" JNIEXPORT int JNICALL Java_com_example_serial_RS485Control_closeSerialPort(JNIEnv *env, jclass clazz) { // TODO: implement closeSerialPort() if ( !comport ) { LOGE("%s() get invalid input arguments.\n", __FUNCTION__ ); return -1; } if (comport->fd >= 0) { close(comport->fd); LOGD("close device \" %s \" successfully\n", comport->devname); } comport->fd = -1; free(comport); comport = NULL; return 0; } extern "C" JNIEXPORT jint JNICALL Java_com_example_serial_RS485Control_sendToPort(JNIEnv *env, jclass clazz, jstring msg, jint len) { // TODO: implement sendToPort() // msg: 表示发送的数据;len: 发送的长度 int rv; char *ptr, left_bytes; int send = 0; const char *sendMsg = env->GetStringUTFChars(msg, 0); if ( sendMsg == nullptr ) { return -1; } if( !comport || !msg || len <= 0 ) { LOGE("Invalid parameter.\n"); return -1; } if (comport->fd == -1) { LOGE("Serail not connected.\n"); return -2; } left_bytes = len; ptr = (char *)sendMsg; while( left_bytes > 0 ) { /* Large data, then slice them to frag and send */ send = left_bytes>comport->frag_size ? comport->frag_size : left_bytes; rv = write(comport->fd, ptr, send); if( rv<0 ) { return -4; } left_bytes -= rv; ptr += rv; } LOGD("send %s successfully\n", sendMsg); env->ReleaseStringUTFChars(msg, sendMsg); return rv; } extern "C" JNIEXPORT jstring JNICALL Java_com_example_serial_RS485Control_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) { int rv = 0; int iRet; fd_set rdfds, exfds; struct timeval stTime; jstring result; char nativeMsg[len]; memset(nativeMsg, 0, len); if( !comport || len <= 0 ) { LOGE("Invalid parameter.\n"); return nullptr; } if (comport->fd == -1) { LOGE("Serail not connected.\n"); return nullptr; } FD_ZERO(&rdfds); FD_ZERO(&exfds); FD_SET(comport->fd, &rdfds); FD_SET(comport->fd, &exfds); if (0xFFFFFFFF != timeout) { stTime.tv_sec = (time_t) (timeout / 1000); stTime.tv_usec = (long)(1000 * (timeout % 1000)); iRet = select(comport->fd + 1, &rdfds, 0, &exfds, &stTime); if (0 == iRet) { return nullptr; } else if (0 < iRet) { if (0 != FD_ISSET(comport->fd, &exfds)) { LOGE("Error checking recv status.\n"); return nullptr; } if (0 == FD_ISSET(comport->fd, &rdfds)) { LOGE("No incoming data.\n"); return nullptr; } } else { if (EINTR == errno) { LOGE("catch interrupt signal.\n"); // rv = 0; } else { LOGE("Check recv status failure.\n"); // rv = -7; } return nullptr; } } usleep(10000); /* sleep for 10ms for data incoming */ // Get data from Com port iRet = read(comport->fd, nativeMsg, len); if (0 > iRet) { if (EINTR == errno) rv = 0; // Interrupted signal catched else rv = -3; // Failed to read comport return nullptr; } LOGD("Receive {%s} successfully\n", nativeMsg); return env->NewStringUTF(nativeMsg); } extern "C" JNIEXPORT jstring JNICALL Java_com_example_serial_RS485Control_saveToFile(JNIEnv *env, jclass clazz, jstring msg, jint len, jstring file_name) { // TODO: implement saveToFile() int mFd = -1; int rv; mFd = open("dev/log.txt", O_CREAT | O_RDWR | O_APPEND, 0666); rv = write(mFd, msg, len); if (rv < 0) { LOGE("Save data to file failed:%s\n" , strerror(errno)); return nullptr; } LOGD("save received data into file successfully!\n"); char buf[1024]; rv = read(mFd, buf, len); if (rv < 0) { LOGE("Read data failed:%s\n" , strerror(errno)); return nullptr; } strcat(reinterpret_cast(buf), "file"); close(mFd); return reinterpret_cast(buf); } // RS485是半双工通信的,由GPIO的高低电平来控制其是接收状态还是发送状态 // 0: 接收(默认);1:发送 // 切换状态成功返回0,失败返回-1 extern "C" JNIEXPORT jint JNICALL Java_com_example_serial_RS485Control_changeState(JNIEnv *env, jclass clazz, jint state) { // TODO: implement changeState() struct gpiod_chip *chip; struct gpiod_line *line; const char *chipname = "gpiochip4"; int rv = 0; int gpio = 26; chip = gpiod_chip_open_by_name(chipname); if( !chip ) { LOGE("Gpio open '%s' failed:%s\n", chipname, strerror(errno)); return -1; } line = gpiod_chip_get_line(chip, gpio); if ( !line ) { LOGE("Get gpio[%d] failed:%s\n", gpio, strerror(errno)); rv = -1; goto Cleanup; } rv = gpiod_line_is_used(line); if( rv ) { LOGE("GPIO[%d] is used\n", gpio); rv = -1; goto Cleanup; } rv = gpiod_line_request_output(line, "rs485", 0); if ( rv < 0 ) { LOGE("Set GPIO[%d] as output failed:%s\n", gpio, strerror(errno)); rv = -1; goto Cleanup; } rv = gpiod_line_set_value(line, state); if ( rv < 0 ) { LOGE("Set GPIO[%d]'s value failed:%s\n", gpio, strerror(errno)); rv = -1; goto Cleanup; } LOGD("Set GPIO[%d]'s value as %d successfully\n", gpio, state); rv = 0; Cleanup: gpiod_line_release(line); gpiod_chip_close(chip); return rv; }