diff --git a/lib-serialport/.gitignore b/lib-serialport/.gitignore new file mode 100644 index 0000000..3543521 --- /dev/null +++ b/lib-serialport/.gitignore @@ -0,0 +1 @@ +/build diff --git a/lib-serialport/build.gradle b/lib-serialport/build.gradle new file mode 100644 index 0000000..b6c20a2 --- /dev/null +++ b/lib-serialport/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 32 + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 32 + versionCode 1 + versionName "1.0" + externalNativeBuild { + cmake { + arguments '-DANDROID_TOOLCHAIN=clang' + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + } + } +} + +dependencies { + +} diff --git a/lib-serialport/proguard-rules.pro b/lib-serialport/proguard-rules.pro new file mode 100644 index 0000000..d11d05f --- /dev/null +++ b/lib-serialport/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\Android\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/lib-serialport/src/main/AndroidManifest.xml b/lib-serialport/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0d3854a --- /dev/null +++ b/lib-serialport/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/lib-serialport/src/main/cpp/CMakeLists.txt b/lib-serialport/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..61ee169 --- /dev/null +++ b/lib-serialport/src/main/cpp/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_library(SerialPort SHARED + SerialPort.c) + +# Include libraries needed for libserial_port lib +target_link_libraries(SerialPort + android + log) \ No newline at end of file diff --git a/lib-serialport/src/main/cpp/SerialPort.c b/lib-serialport/src/main/cpp/SerialPort.c new file mode 100644 index 0000000..c595be5 --- /dev/null +++ b/lib-serialport/src/main/cpp/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.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: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_com_kongqw_serialportlibrary_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + 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("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, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_kongqw_serialportlibrary_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + 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, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/lib-serialport/src/main/cpp/SerialPort.h b/lib-serialport/src/main/cpp/SerialPort.h new file mode 100644 index 0000000..3f38ef0 --- /dev/null +++ b/lib-serialport/src/main/cpp/SerialPort.h @@ -0,0 +1,30 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_qingwei_kong_serialportlibrary_SerialPort +#define _Included_qingwei_kong_serialportlibrary_SerialPort +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_com_kongqw_serialportlibrary_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_kongqw_serialportlibrary_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/Device.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/Device.java new file mode 100644 index 0000000..800c012 --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/Device.java @@ -0,0 +1,48 @@ +package com.kongqw.serialportlibrary; + +import java.io.File; +import java.io.Serializable; + +/** + * Created by Kongqw on 2017/11/13. + * Device + */ + +public class Device implements Serializable{ + + private static final String TAG = Device.class.getSimpleName(); + + private String name; + private String root; + private File file; + + public Device(String name, String root, File file) { + this.name = name; + this.root = root; + this.file = file; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRoot() { + return root; + } + + public void setRoot(String root) { + this.root = root; + } + + public File getFile() { + return file; + } + + public void setFile(File path) { + this.file = file; + } +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/Driver.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/Driver.java new file mode 100644 index 0000000..445b828 --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/Driver.java @@ -0,0 +1,54 @@ +package com.kongqw.serialportlibrary; + +import android.util.Log; + +import java.io.File; +import java.util.ArrayList; + +/** + * Created by Kongqw on 2017/11/13. + * Driver + */ + +public class Driver { + + private static final String TAG = Driver.class.getSimpleName(); + + private String mDriverName; + private String mDeviceRoot; + + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + public ArrayList getDevices() { + ArrayList devices = new ArrayList<>(); + File dev = new File("/dev"); + + if (!dev.exists()) { + Log.i(TAG, "getDevices: " + dev.getAbsolutePath() + " 不存在"); + return devices; + } + if (!dev.canRead()) { + Log.i(TAG, "getDevices: " + dev.getAbsolutePath() + " 没有读取权限"); + return devices; + } + + 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]); + devices.add(files[i]); + } + } + return devices; + } + + public String getName() { + return mDriverName; + } + +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPort.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPort.java new file mode 100644 index 0000000..28e332a --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPort.java @@ -0,0 +1,47 @@ +package com.kongqw.serialportlibrary; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; + +public class SerialPort { + + static { + System.loadLibrary("SerialPort"); + } + + private static final String TAG = SerialPort.class.getSimpleName(); + + /** + * 文件设置最高权限 777 可读 可写 可执行 + * + * @param file 文件 + * @return 权限修改是否成功 + */ + boolean chmod777(File file) { + if (null == file || !file.exists()) { + // 文件不存在 + return false; + } + try { + // 获取ROOT权限 + Process su = Runtime.getRuntime().exec("/system/bin/su"); + // 修改文件属性为 [可读 可写 可执行] + String cmd = "chmod 777 " + file.getAbsolutePath() + "\n" + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if (0 == su.waitFor() && file.canRead() && file.canWrite() && file.canExecute()) { + return true; + } + } catch (IOException | InterruptedException e) { + // 没有ROOT权限 + e.printStackTrace(); + } + return false; + } + + // 打开串口 + protected native FileDescriptor open(String path, int baudRate, int flags); + + // 关闭串口 + protected native void close(); +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortFinder.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortFinder.java new file mode 100644 index 0000000..598b19e --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortFinder.java @@ -0,0 +1,66 @@ +package com.kongqw.serialportlibrary; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.ArrayList; + +public class SerialPortFinder { + + private static final String TAG = SerialPortFinder.class.getSimpleName(); + private static final String DRIVERS_PATH = "/proc/tty/drivers"; + private static final String SERIAL_FIELD = "serial"; + + public SerialPortFinder() { + File file = new File(DRIVERS_PATH); + boolean b = file.canRead(); + Log.i(TAG, "SerialPortFinder: file.canRead() = " + b); + } + + /** + * 获取 Drivers + * + * @return Drivers + * @throws IOException IOException + */ + private ArrayList getDrivers() throws IOException { + ArrayList drivers = new ArrayList<>(); + LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(DRIVERS_PATH)); + String readLine; + while ((readLine = lineNumberReader.readLine()) != null) { + String driverName = readLine.substring(0, 0x15).trim(); + String[] fields = readLine.split(" +"); + if ((fields.length >= 5) && (fields[fields.length - 1].equals(SERIAL_FIELD))) { + Log.d(TAG, "Found new driver " + driverName + " on " + fields[fields.length - 4]); + drivers.add(new Driver(driverName, fields[fields.length - 4])); + } + } + return drivers; + } + + /** + * 获取串口 + * + * @return 串口 + */ + public ArrayList getDevices() { + ArrayList devices = new ArrayList<>(); + try { + ArrayList drivers = getDrivers(); + for (Driver driver : drivers) { + String driverName = driver.getName(); + ArrayList driverDevices = driver.getDevices(); + for (File file : driverDevices) { + String devicesName = file.getName(); + devices.add(new Device(devicesName, driverName, file)); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices; + } +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortManager.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortManager.java new file mode 100644 index 0000000..dcd9c01 --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortManager.java @@ -0,0 +1,187 @@ +package com.kongqw.serialportlibrary; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.util.Log; + +import com.kongqw.serialportlibrary.listener.OnSerialPortDataListener; +import com.kongqw.serialportlibrary.thread.SerialPortReadThread; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Created by Kongqw on 2017/11/13. + * SerialPortManager + */ + +public class SerialPortManager extends SerialPort { + private static final String TAG = SerialPortManager.class.getSimpleName(); + private FileInputStream mFileInputStream; + private FileOutputStream mFileOutputStream; + private FileDescriptor mFd; + private OnSerialPortDataListener mOnSerialPortDataListener; + + private HandlerThread mSendingHandlerThread; + private Handler mSendingHandler; + private SerialPortReadThread mSerialPortReadThread; + + /** + * 打开串口 + * + * @param device 串口设备 + * @param baudRate 波特率 + * @return 打开是否成功 + */ + public boolean openSerialPort(File device, int baudRate) { + Log.i(TAG, "openSerialPort: " + String.format("打开串口 %s 波特率 %s", device.getPath(), baudRate)); + // 校验串口权限 + if (!device.canRead() || !device.canWrite()) { + boolean chmod777 = chmod777(device); + if (!chmod777) { + Log.i(TAG, "openSerialPort: 没有读写权限"); + return false; + } + } + + try { + mFd = open(device.getAbsolutePath(), baudRate, 0); + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + Log.i(TAG, "openSerialPort: 串口已经打开 " + mFd); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + /** + * 关闭串口 + */ + public void closeSerialPort() { + if (null != mFd) { + close(); + mFd = null; + } + // 停止发送消息的线程 + stopSendThread(); + // 停止接收消息的线程 + stopReadThread(); + + if (null != mFileInputStream) { + try { + mFileInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + mFileInputStream = null; + } + + if (null != mFileOutputStream) { + try { + mFileOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + mFileOutputStream = null; + } + + mOnSerialPortDataListener = null; + } + + /** + * 添加数据通信监听 + * + * @param listener listener + * @return SerialPortManager + */ + public SerialPortManager setOnSerialPortDataListener(OnSerialPortDataListener listener) { + mOnSerialPortDataListener = listener; + return this; + } + + /** + * 开启发送消息的线程 + */ + public void startSendThread() { + // 开启发送消息的线程 + mSendingHandlerThread = new HandlerThread("mSendingHandlerThread"); + mSendingHandlerThread.start(); + // Handler + mSendingHandler = new Handler(mSendingHandlerThread.getLooper()) { + @Override + public void handleMessage(Message msg) { + byte[] sendBytes = (byte[]) msg.obj; + + if (null != mFileOutputStream && null != sendBytes && 0 < sendBytes.length) { + try { + mFileOutputStream.write(sendBytes); + if (null != mOnSerialPortDataListener) { + mOnSerialPortDataListener.onDataSent(sendBytes); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }; + } + + /** + * 停止发送消息线程 + */ + public void stopSendThread() { + mSendingHandler = null; + if (null != mSendingHandlerThread) { + mSendingHandlerThread.interrupt(); + mSendingHandlerThread.quit(); + mSendingHandlerThread = null; + } + } + + /** + * 开启接收消息的线程 + */ + public void startReadThread(int type) { + mSerialPortReadThread = new SerialPortReadThread(mFileInputStream, type) { + @Override + public void onDataReceived(byte[] bytes) { + if (null != mOnSerialPortDataListener) { + mOnSerialPortDataListener.onDataReceived(bytes); + } + } + }; + mSerialPortReadThread.startRead(); + } + + /** + * 停止接收消息的线程 + */ + public void stopReadThread() { + if (null != mSerialPortReadThread) { + mSerialPortReadThread.stopRead(); + } + } + + /** + * 发送数据 + * + * @param sendBytes 发送数据 + * @return 发送是否成功 + */ + public boolean sendBytes(byte[] sendBytes) { + if (null != mFd && null != mFileInputStream && null != mFileOutputStream) { + if (null != mSendingHandler) { + Message message = Message.obtain(); + message.obj = sendBytes; + return mSendingHandler.sendMessage(message); + } + } + return false; + } +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/listener/OnSerialPortDataListener.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/listener/OnSerialPortDataListener.java new file mode 100644 index 0000000..02c7bee --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/listener/OnSerialPortDataListener.java @@ -0,0 +1,23 @@ +package com.kongqw.serialportlibrary.listener; + +/** + * Created by Kongqw on 2017/11/14. + * 串口消息监听 + */ + +public interface OnSerialPortDataListener { + + /** + * 数据接收 + * + * @param bytes 接收到的数据 + */ + void onDataReceived(byte[] bytes); + + /** + * 数据发送 + * + * @param bytes 发送的数据 + */ + void onDataSent(byte[] bytes); +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/thread/SerialPortReadThread.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/thread/SerialPortReadThread.java new file mode 100644 index 0000000..4228b14 --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/thread/SerialPortReadThread.java @@ -0,0 +1,231 @@ +package com.kongqw.serialportlibrary.thread; + +import android.util.Log; + +import com.kongqw.serialportlibrary.utils.ByteUtils; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by Kongqw on 2017/11/14. + * 串口消息读取线程 + */ + +public abstract class SerialPortReadThread extends Thread { + public abstract void onDataReceived(byte[] bytes); + + private InputStream mInputStream; + private final byte[] mReadBuffer; + public boolean isStopThread = false; + private int type = 0; + + public SerialPortReadThread(InputStream inputStream, int type) { + mInputStream = inputStream; + mReadBuffer = new byte[1024]; + this.type = type; + } + + @Override + public void run() { + super.run(); + if (type == 485) { + Log.e("ReadThread", "type 485"); + while (!isStopThread) { + try { + if (null == mInputStream) { + return; + } + + Log.e("ReadThread", "type 1111"); + int size = mInputStream.read(mReadBuffer); + Log.e("ReadThread size", size + ""); + if (size <= 0) { + return; + } + Log.e("ReadThread", "type 2222"); + byte[] readBytes = new byte[size]; + System.arraycopy(mReadBuffer, 0, readBytes, 0, size); + onDataReceived(readBytes); + Log.e("ReadThread", "onDataReceived"); + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + } else { + int rLen = 0; + int dLen = 0; + byte[] crc = new byte[]{0}; + byte[] tmp1 = new byte[]{0}; + byte[] tmp2 = new byte[]{0}; + + while (!isStopThread) { + try { + if (mInputStream == null) { + return; + } + if (!isHead()) { + continue; + } + + crc[0] = 0x55; + crc[0] ^= 0xAA; + //接收 cmd + rLen = recvBuffer(tmp1, 1, 600); + if (rLen != 1) { + continue; + } + crc[0] ^= tmp1[0]; + if (tmp1[0] == 0x30) { + //接收 数据长度 + rLen = recvBuffer(tmp1, 1, 600); + if (rLen != 1) { + continue; + } + //接收 数据长度 + rLen = recvBuffer(tmp2, 1, 600); + if (rLen != 1) { + continue; + } + crc[0] ^= tmp1[0]; + crc[0] ^= tmp2[0]; + dLen = tmp1[0] & 0xff; + dLen |= ((tmp2[0] << 8) & 0xffff); + //dLen = ((uint)(tmp1[0])) | (tmp2[0] << 8); + byte[] data = new byte[dLen]; + if (dLen > 0) { + rLen = recvBuffer(data, dLen, 600); + if (rLen != dLen) { + continue; + } + } + //接收 crc + rLen = recvBuffer(tmp1, 1, 600); + if (rLen != 1) { + continue; + } + for (int i = 0; i < dLen; i++) { + crc[0] ^= data[i]; + } + //crc校验 + if (!ByteUtils.bytesToHexString(tmp1).equals(ByteUtils.bytesToHexString(crc))) { + continue; + } + onDataReceived(data); + } else { + Log.e("idhead", "no 0x30"); + } + } catch (Exception e) { + e.printStackTrace(); + return; + } + } + } + } + + public void startRead() { + isStopThread = false; + start(); + } + + + public void stopRead() { + isStopThread = true; + } + + + /** + * 关闭线程 释放资源 + */ + public void release() { + stopRead(); + + if (null != mInputStream) { + try { + mInputStream.close(); + mInputStream = null; + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private int recvBuffer(byte[] buffer, int size, int msTimeout) { + long stime = System.currentTimeMillis(); + int rLen = 0, offset = 0; + byte[] rBuffer = new byte[1024]; + byte[] cache = new byte[1024]; + try { + do { + if (mInputStream.available() <= 0) { + Thread.sleep(10); + continue; + } + rLen = mInputStream.read(rBuffer, 0, Math.min(size, rBuffer.length)); + if (rLen <= 0) { + Thread.sleep(10); + continue; + } + stime = System.currentTimeMillis(); + + if (offset + rLen > cache.length) { + byte[] tmp = new byte[offset]; + System.arraycopy(cache, 0, tmp, 0, offset); + cache = new byte[(offset + rLen) * 2]; + System.arraycopy(tmp, 0, cache, 0, offset); + } + System.arraycopy(rBuffer, 0, cache, offset, rLen); + offset += rLen; + if (offset >= size) { + System.arraycopy(cache, 0, buffer, 0, size); + break; + } + } while (msTimeout > 0 && System.currentTimeMillis() - stime < msTimeout); + } catch (Exception e) { + return offset; + } + return offset; + } + + private boolean isHead() { + byte[] head1 = new byte[]{0}; + byte[] head2 = new byte[]{0}; + + while (true) { + if (head1[0] != (byte) 0x55) { + int rLen = recvBuffer(head1, 1, 500); + if (rLen != 1) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + return false; + } + continue; + } + } + + int rLen = recvBuffer(head2, 1, 500); + if (rLen != 1) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + return false; + } + continue; + } + + if (head1[0] != (byte) 0x55) { + head1[0] = head2[0]; + continue; + } + if (head2[0] != (byte) 0xAA) { + head1[0] = head2[0]; + continue; + } + return true; + } + } +} diff --git a/lib-serialport/src/main/java/com/kongqw/serialportlibrary/utils/ByteUtils.java b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/utils/ByteUtils.java new file mode 100644 index 0000000..fbcc0a8 --- /dev/null +++ b/lib-serialport/src/main/java/com/kongqw/serialportlibrary/utils/ByteUtils.java @@ -0,0 +1,487 @@ +package com.kongqw.serialportlibrary.utils; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * @author wangsir + *

+ * 2017年9月21日 + */ +public class ByteUtils { + /** + * 将int转为高端字节序排列的byte数组(Java内存存放顺序) + * + * @param n + * @return + */ + public static byte[] int2ByteArray(int n) { + byte[] byteArray = null; + try { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + DataOutputStream dataOut = new DataOutputStream(byteOut); + dataOut.writeInt(n); + byteArray = byteOut.toByteArray(); + Arrays.toString(byteArray); + } catch (IOException e) { + e.printStackTrace(); + } + return byteArray; + } + + /** + * 将int转为高字节在前,低字节在后的byte数组 + * + * @param n int + * @return byte[] + */ + public static byte[] int2Hbytes(int n) { + byte[] b = new byte[4]; + b[3] = (byte) (n & 0xff); + b[2] = (byte) (n >> 8 & 0xff); + b[1] = (byte) (n >> 16 & 0xff); + b[0] = (byte) (n >> 24 & 0xff); + + return b; + } + + /** + * 将int转为高字节在前,低字节在后的byte数组,一字节 + * + * @param n int + * @return byte[] + */ + public static byte[] int2Hbytes1byte(int n) { + byte[] b = new byte[1]; + b[0] = (byte) (n & 0xff); + return b; + } + + /** + * 将short转为高字节在前,低字节在后的byte数组 + * + * @param n short + * @return byte[] + */ + public static byte[] short2Hbytes(short n) { + byte[] b = new byte[2]; + b[1] = (byte) (n & 0xff); + b[0] = (byte) (n >> 8 & 0xff); + return b; + } + + /** + * 以下 是整型数 和 网络字节序的 byte[] 数组之间的转换 + * + * @param n + * @return + */ + public static byte[] long2Hbytes(long n) { + byte[] b = new byte[8]; + b[7] = (byte) (n & 0xff); + b[6] = (byte) (n >> 8 & 0xff); + b[5] = (byte) (n >> 16 & 0xff); + b[4] = (byte) (n >> 24 & 0xff); + b[3] = (byte) (n >> 32 & 0xff); + b[2] = (byte) (n >> 40 & 0xff); + b[1] = (byte) (n >> 48 & 0xff); + b[0] = (byte) (n >> 56 & 0xff); + return b; + } + + public static byte[] long2H6bytes(long n) { + byte[] b = new byte[6]; + b[5] = (byte) (n & 0xff); + b[4] = (byte) (n >> 8 & 0xff); + b[3] = (byte) (n >> 16 & 0xff); + b[2] = (byte) (n >> 24 & 0xff); + b[1] = (byte) (n >> 32 & 0xff); + b[0] = (byte) (n >> 40 & 0xff); + return b; + } + + public static byte[] long2H4bytes(long n) { + byte[] b = new byte[4]; + b[3] = (byte) (n & 0xff); + b[2] = (byte) (n >> 8 & 0xff); + b[1] = (byte) (n >> 16 & 0xff); + b[0] = (byte) (n >> 24 & 0xff); + return b; + } + + public static byte[] unlong2H4bytes(long n) { + byte[] b = new byte[4]; + b[0] = (byte) (n & 0xff); + b[1] = (byte) (n >> 8 & 0xff); + b[2] = (byte) (n >> 16 & 0xff); + b[3] = (byte) (n >> 24 & 0xff); + return b; + } + + + /** + * 合并数组 + * + * @param first + * @param rest + * @return + */ + public static byte[] concatBytes(byte[] first, byte[]... rest) { + int totalLength = first.length; + for (byte[] array : rest) { + if (null != array) { + totalLength += array.length; + } + } + byte[] result = Arrays.copyOf(first, totalLength); + int offset = first.length; + for (byte[] array : rest) { + if (null != array) { + System.arraycopy(array, 0, result, offset, array.length); + offset += array.length; + } + } + return result; + } + + /** + * byte数组转为十六进制字符串 + * + * @param bytes + * @return + */ + public static String byte2Hex(byte[] bytes) { + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xff & bytes[i]); + + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + /** + * Convert hex string to byte[] + * + * @param hexString the hex string + * @return byte[] + */ + public static byte[] hex2Byte(String hexString) { + if (hexString == null || hexString.equals("")) { + return null; + } + hexString = hexString.toUpperCase().replace(" ", ""); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) { + int pos = i * 2; + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + /** + * Convert char to byte + * + * @param c char + * @return byte + */ + private static byte charToByte(char c) { + return (byte) "0123456789ABCDEF".indexOf(c); + } + + /** + * 输入流转为字节数组 + */ + public static byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + } + return output.toByteArray(); + } + + + /** + * bytes字符串转换为Byte值 + * + * @param src src Byte字符串,每个Byte之间没有分隔符 + * @return byte[] + */ + public static byte[] hexStr2Bytes(String src) { + int m = 0, n = 0; + int l = src.length() / 2; + byte[] ret = new byte[l]; + for (int i = 0; i < l; i++) { + m = i * 2 + 1; + n = m + 1; + ret[i] = Byte.decode("0x" + src.substring(i * 2, m) + src.substring(m, n)); + } + return ret; + } + +// /** +// * 字节数组转换成16进制字符串 +// * @param bytes 字节数组 +// * @return 16进制字符串 +// */ +// public static String hexEncode(byte[] bytes) { +// if (bytes == null || bytes.length <= 0) { +// return null; +// } +// return new String(Hex.encodeHex(bytes)); //Hex.encodeHex(bytes, false) +// } +// +// /** +// * 16进制字符串转换成字节数组 +// * @param hexStr 16进制字符串 +// * @return 字节数组 +// */ +// public static byte[] hexDecode(String hexStr) { +// if (hexStr == null || "".equals(hexStr)) { +// return null; +// } +// try { +// char[] cs = hexStr.toCharArray(); +// return Hex.decodeHex(cs); +// } catch (DecoderException e) { +// e.printStackTrace(); +// } +// return null; +// } + + + /** + * 字符串转换成十六进制字符串 + * + * @param s s为待转换的ASCII字符串 + */ + public static String str2HexStr(String s) { + + String str = ""; + for (int i = 0; i < s.length(); i++) { + int ch = (int) s.charAt(i); + String s4 = Integer.toHexString(ch); + str = str + s4; + } + return str; + } + + public static String int2HexStr(Integer n) { + + String str = Integer.toHexString(n); + while (str.length() < 18) { + str = "0" + str; + } + return str.toUpperCase(); + } + + public static String binaryToHex(String bin) { + String str = Long.toHexString(Long.parseLong(bin, 2)); + while (str.length() < 4) { + str = "0" + str; + } + return str.toUpperCase(); + } + + /** + * 16进制的字符串转byte数组 + * + * @param src + * @return + */ + public static byte[] str16ToBytes(String src) { + int w = 0; + byte[] bytes_2 = new byte[src.length() / 2]; + for (int i = 0; i < src.length(); i++) { + String zz = src.substring(i, i + 2); + byte aaa = (byte) Integer.parseInt(zz, 16); + bytes_2[w] = aaa; + i++; + + w = w + 1; + } + + return bytes_2; + } + + /** + * 16进制转10进制数字 + * + * @param src + * @return + */ + public static int str16ToInt10(String src) { + return Integer.parseInt(src, 16); + } + + /** + * 16进制转10进制数字 + * + * @param src + * @return + */ + public static long str16ToLong10(String src) { + return Long.parseLong(src, 16); + } + + public static String convertStringToHex(String str) { + char[] chars = str.toCharArray(); + StringBuffer hex = new StringBuffer(); + for (int i = 0; i < chars.length; i++) { + hex.append(Integer.toHexString((int) chars[i])); + } + return hex.toString(); + } + + public static String convertHexToString(String hex) { + hex = hex.replace(" ", ""); + StringBuilder sb = new StringBuilder(); + StringBuilder temp = new StringBuilder(); + + //49204c6f7665204a617661 split into two characters 49, 20, 4c... + for (int i = 0; i < hex.length() - 1; i += 2) { + + //grab the hex in pairs + String output = hex.substring(i, (i + 2)); + //convert hex to decimal + int decimal = Integer.parseInt(output, 16); + //convert the decimal to character + sb.append((char) decimal); + + temp.append(decimal); + } + + return sb.toString(); + } + + /** + * @param + * @ClassName + * @Description : 功能说明 + * @Return : + * @Author : li + * @Date : 2021/3/25 13:48 + */ + public static byte[] hexStringToByteArray(String hexString) { + hexString = hexString.replaceAll(" ", ""); + int len = hexString.length(); + byte[] bytes = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节 + bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character + .digit(hexString.charAt(i + 1), 16)); + } + return bytes; + } + + /** + * @param + * @ClassName + * @Description : 功能说明 + * @Return : + * @Author : li + * @Date : 2021/3/25 13:48 + */ + public static String bytesToHexString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xFF & bytes[i]); + if (hex.length() == 1) { + sb.append('0'); + } + sb.append(hex); + } + return sb.toString(); + } + + /** + * 从16进制字符串中获取2进制为1的序列号 + * + * @param src + * @return + */ + public static ArrayList Str16Get1(String src) { + byte[] b_p = ByteUtils.str16ToBytes(src); + ArrayList list = new ArrayList<>(); + int w = 0; + for (int i = 0; i < src.length() / 2; i++) { + for (int j = 7; j >= 0; j--) { + if ((b_p[i] & (1 << j)) != 0) { + int pp = i * 8 + 8 - j; + System.out.println("pppp=======" + pp); + list.add(w, pp); + w = w + 1; + } else { + + } + } + + } + return list; + } + + /** + * 异或校验 + * + * @param bytes + * @return + */ + public static byte[] bytesXorCrc(byte[] bytes) { + byte[] crc = new byte[1];// 异或校验 + crc[0] = bytes[0]; + for (int i = 1; i < bytes.length; i++) { + crc[0] ^= bytes[i]; + } + return crc; + } + + + /** + * 和校验 + * + * @param data + * @return + */ + public static String makeChecksum(String data) { + data = data.replace(" ", ""); + if (data == null || data.equals("")) { + return ""; + } + int total = 0; + int len = data.length(); + int num = 0; + while (num < len) { + String s = data.substring(num, num + 2); + total += Integer.parseInt(s, 16); + num = num + 2; + } + /** + * 用256求余最大是255,即16进制的FF + */ + int mod = total % 256; + String hex = Integer.toHexString(mod); + len = hex.length(); + // 如果不够校验位的长度,补0,这里用的是两位校验 + if (len < 2) { + hex = "0" + hex; + } + hex = hex.toUpperCase(); + if (hex.equals("7E")) { + hex = "7F 01"; + } else if (hex.equals("7F")) { + hex = "7F 02"; + } + return hex; + } +} diff --git a/lib-serialport/src/main/res/values/strings.xml b/lib-serialport/src/main/res/values/strings.xml new file mode 100644 index 0000000..986c664 --- /dev/null +++ b/lib-serialport/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + SerialPortLibrary +