16 changed files with 1412 additions and 0 deletions
-
1lib-serialport/.gitignore
-
33lib-serialport/build.gradle
-
17lib-serialport/proguard-rules.pro
-
9lib-serialport/src/main/AndroidManifest.xml
-
9lib-serialport/src/main/cpp/CMakeLists.txt
-
167lib-serialport/src/main/cpp/SerialPort.c
-
30lib-serialport/src/main/cpp/SerialPort.h
-
48lib-serialport/src/main/java/com/kongqw/serialportlibrary/Device.java
-
54lib-serialport/src/main/java/com/kongqw/serialportlibrary/Driver.java
-
47lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPort.java
-
66lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortFinder.java
-
187lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortManager.java
-
23lib-serialport/src/main/java/com/kongqw/serialportlibrary/listener/OnSerialPortDataListener.java
-
231lib-serialport/src/main/java/com/kongqw/serialportlibrary/thread/SerialPortReadThread.java
-
487lib-serialport/src/main/java/com/kongqw/serialportlibrary/utils/ByteUtils.java
-
3lib-serialport/src/main/res/values/strings.xml
@ -0,0 +1 @@ |
|||
/build |
@ -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 { |
|||
|
|||
} |
@ -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 *; |
|||
#} |
@ -0,0 +1,9 @@ |
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|||
package="com.kongqw.serialportlibrary"> |
|||
|
|||
<application android:allowBackup="true" android:label="@string/app_name" |
|||
android:supportsRtl="true"> |
|||
|
|||
</application> |
|||
|
|||
</manifest> |
@ -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) |
@ -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 <termios.h> |
|||
#include <unistd.h> |
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <fcntl.h> |
|||
#include <string.h> |
|||
#include <jni.h> |
|||
|
|||
#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, "<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; |
|||
} |
|||
|
|||
/* |
|||
* 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); |
|||
} |
|||
|
@ -0,0 +1,30 @@ |
|||
/* DO NOT EDIT THIS FILE - it is machine generated */ |
|||
#include <jni.h> |
|||
/* 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 |
@ -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; |
|||
} |
|||
} |
@ -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<File> getDevices() { |
|||
ArrayList<File> 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; |
|||
} |
|||
|
|||
} |
@ -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(); |
|||
} |
@ -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<Driver> getDrivers() throws IOException { |
|||
ArrayList<Driver> 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<Device> getDevices() { |
|||
ArrayList<Device> devices = new ArrayList<>(); |
|||
try { |
|||
ArrayList<Driver> drivers = getDrivers(); |
|||
for (Driver driver : drivers) { |
|||
String driverName = driver.getName(); |
|||
ArrayList<File> 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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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 |
|||
* <p> |
|||
* 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; |
|||
} |
|||
} |
@ -0,0 +1,3 @@ |
|||
<resources> |
|||
<string name="app_name">SerialPortLibrary</string> |
|||
</resources> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue