android 系统下很多终端设备并不具备直接的串口通讯,通过use转RS232/485转换设备实现串口通讯,PL2303HXD USB to RS232(集成的RS232-USB接口转换器)可实现android的串口通信,但Qt的QSerialPort并不适用,可通过调用PL2303HXD提供的java库实现。
备注:关于qt on android及java交互知识请参考《Qt on Android核心编程》和“extendsQtWithJava” demo。
1)下载:http://www.prolific.com.tw,本文样例的下载版本“PL2303HXD_Android-SDK_v10015_20170512.zip“
2)解压后,在SampleCode给出了单个端口和多个端口的样例,由于本人项目需要,直接研究使用多端口的样例如何结合到QT中实现usb转串口的通讯。
3)建立一个qt工程,在工程文件中添加android的配置要求,以下是我项目(pro)的部分配置:
android-g++ { QT += androidextras LIBS += -lgnustl_shared ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android HEADERS += \ javaSrc/qDebug2Logcat.h \ javaSrc/simpleCustomEvent.h \ javaSrc/javanative.h \ SOURCES += \ javaSrc/javanative.cpp \ javaSrc/qDebug2Logcat.cpp \ javaSrc/simpleCustomEvent.cpp \
DISTFILES += \ #android的xml配置 android/AndroidManifest.xml \ #来自SampleCode/Multi port示例的libs android/libs/android-support-v4.jar \ #来自SampleCode/Multi port示例的libs android/libs/pl2303multilib.jar \ android/res/xml/device_filter.xml \ #java到qt的交互函数定义,qt中实现 android/src/an/qt5/javagather/PL2303HXDNative.java \ #多端口串口通信 android/src/an/qt5/javagather/PL2303HXDSerialPort.java \ #开机自启动需要 android/src/an/qt5/javagather/GatherBootUpReceiver.java \ #串口采集参配及规约解析脚本 android/assets/modbus.js \ android/assets/tcp.js \ android/assets/tcpPlay.js \ android/assets/gather.xml \ android/assets/gmap.xml }4)(PL2303HXDSerialPort.java)PL2303HXD的串口采集的java代码:
package an.qt5.javagather; import java.io.IOException;
import tw.com.prolific.pl2303multilib.PL2303MultiLib;
import android.os.Bundle;
import android.os.Handler;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbManager;
import java.io.UnsupportedEncodingException;
//min and max
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import java.util.List;
import java.util.ArrayList;
class UARTSettingInfo {
public int iPortIndex = 0;
public PL2303MultiLib.BaudRate mBaudrate = PL2303MultiLib.BaudRate.B9600;
public PL2303MultiLib.DataBits mDataBits = PL2303MultiLib.DataBits.D8;
public PL2303MultiLib.Parity mParity = PL2303MultiLib.Parity.NONE;
public PL2303MultiLib.StopBits mStopBits = PL2303MultiLib.StopBits.S1;
public PL2303MultiLib.FlowControl mFlowControl = PL2303MultiLib.FlowControl.OFF;
}//class UARTSettingInfo
public class PL2303HXDSerialPort extends org.qtproject.qt5.android.bindings.QtActivity {
//public class PL2303HXDSerialPort extends Activity{
private static PL2303MultiLib mSerialMulti;
private static final int MAX_DEVICE_COUNT = 4;
private static final String ACTION_USB_PERMISSION = "an.qt5.javagather.USB_PERMISSION";
private static UARTSettingInfo gUARTInfoList[];
private static boolean gThreadStop[] = new boolean[MAX_DEVICE_COUNT];
private static boolean gRunningReadThread[] = new boolean[MAX_DEVICE_COUNT];
private static boolean bDeviceOpened[] = new boolean[MAX_DEVICE_COUNT];
private static final int ReadDataBufferSize = 256;
private static boolean registerflag = false;
private static final int DeviceIndex1 = 0;
private static final int DeviceIndex2 = 1;
private static final int DeviceIndex3 = 2;
private static final int DeviceIndex4 = 3;
private static final int readSleep = 100;
// private static String TAG = "PL2303HXD_Gather_Log";
private static PL2303HXDSerialPort m_instance;
public PL2303HXDSerialPort(){
m_instance = this;
}
//min
public static void mini()
{
m_instance.moveTaskToBack(true);
}
//max
public static void moveTaskToFrount(){
if(!m_instance.isForeground(m_instance.getPackageName()))
{
ActivityManager am = (ActivityManager)m_instance.getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(m_instance.getTaskId(),0);
}
}
private boolean isForeground(String packageName){
ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null)
return false;
for (RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName)
&& appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
}
private static int PLCInit(){
PL2303HXDNative.OnNotify_LOG("pl2303 get service");
try{
// get service
mSerialMulti = new PL2303MultiLib((UsbManager) m_instance.getSystemService(Context.USB_SERVICE),
m_instance, ACTION_USB_PERMISSION);
// if you don't want to use Software Queue, below constructor to be used
// mSerialMulti = new PL2303MultiLib((UsbManager) getSystemService(Context.USB_SERVICE),
// this, ACTION_USB_PERMISSION,false);
}catch (Exception e) {
e.printStackTrace();
PL2303HXDNative.OnNotify_LOG("pl2303 get service Exception:"+e.toString());
mSerialMulti = null;
return 0;
}
try{
gUARTInfoList = new UARTSettingInfo[MAX_DEVICE_COUNT];
for(int i=0;i<MAX_DEVICE_COUNT;i++) {
gUARTInfoList[i] = new UARTSettingInfo();
gUARTInfoList[i].iPortIndex = i;
gThreadStop[i] = false;
gRunningReadThread[i] = false;
bDeviceOpened[i] = false;
}
}catch (Exception e) {
e.printStackTrace();
PL2303HXDNative.OnNotify_LOG("pl2303 init UARTINfo Exception:"+e.toString());
mSerialMulti = null;
return 0;
}
m_instance.registerSerialMulti();
PL2303HXDNative.OnNotify_LOG("Leave PLCInit ");
return 1;
}
private static int OpenUARTDevice(int index,int baudrate) {
PL2303HXDNative.OnNotify_LOG("Enter OpenUARTDevice: "+String.valueOf(index));
if(mSerialMulti==null) {
if(0==PLCInit()){
PL2303HXDNative.OnNotify_LOG("Error: mSerialMulti==null");
return 2;
}
}
if(!mSerialMulti.PL2303IsDeviceConnectedByIndex(index)) {
PL2303HXDNative.OnNotify_LOG("Error: !AP_mSerialMulti.PL2303IsDeviceConnectedByIndex "+String.valueOf(index));
return 3;
}
boolean res;
UARTSettingInfo info = gUARTInfoList[index];
switch(baudrate){
case 1200:
info.mBaudrate = PL2303MultiLib.BaudRate.B1200;
break;
case 2400:
info.mBaudrate = PL2303MultiLib.BaudRate.B2400;
break;
case 4800:
info.mBaudrate = PL2303MultiLib.BaudRate.B4800;
break;
case 9600:
info.mBaudrate = PL2303MultiLib.BaudRate.B9600;
break;
case 19200:
info.mBaudrate = PL2303MultiLib.BaudRate.B19200;
break;
case 38400:
info.mBaudrate = PL2303MultiLib.BaudRate.B38400;
break;
case 57600:
info.mBaudrate = PL2303MultiLib.BaudRate.B57600;
break;
case 115200:
info.mBaudrate = PL2303MultiLib.BaudRate.B115200;
break;
default:
PL2303HXDNative.OnNotify_LOG("Error: baudrate is not standard ,fail to PL2303OpenDevByUARTSetting");
return 5;
}
PL2303HXDNative.OnNotify_LOG("UARTSettingInfo: index:"+String.valueOf(info.iPortIndex));
res = mSerialMulti.PL2303OpenDevByUARTSetting(index, info.mBaudrate, info.mDataBits, info.mStopBits,
info.mParity, info.mFlowControl);
if( !res ) {
PL2303HXDNative.OnNotify_LOG("Error: fail to PL2303OpenDevByUARTSetting");
return 4;
}
bDeviceOpened[index] = true;
if(!gRunningReadThread[index]) {
UpdateDisplay(index);
}
PL2303HXDNative.OnNotify_LOG("Open ["+ mSerialMulti.PL2303getDevicePathByIndex(index) +"] successfully!");
return 1;
}//private void OpenUARTDevice(int index)
private static void UpdateDisplay(int index) {
gThreadStop[index] = false;
gRunningReadThread[index] = true;
if( DeviceIndex1==index ) {
new Thread(ReadLoop1).start();
} else if( DeviceIndex2==index ) {
new Thread(ReadLoop2).start();
} else if( DeviceIndex3==index ) {
new Thread(ReadLoop3).start();
}else if( DeviceIndex4==index ){
new Thread(ReadLoop4).start();
}else{
PL2303HXDNative.OnNotify_LOG("closeUARTDevicel:"+String.valueOf(index));
}
}
public static void closeUARTDevicel(int index){
PL2303HXDNative.OnNotify_LOG("index is out [0,3]");
if(null!=mSerialMulti){
gThreadStop[index] = true;
}
}
/**
* 十六进制串转化为byte数组
*
* @return the array of byte
*/
public static final byte[] hex2byte(String hex)
throws IllegalArgumentException {
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException();
}
char[] arr = hex.toCharArray();
byte[] b = new byte[hex.length() / 2];
for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
String swap = "" + arr[i++] + arr[i];
int byteint = Integer.parseInt(swap, 16) & 0xFF;
b[j] = new Integer(byteint).byteValue();
}
return b;
}
/**
* 字节数组转换为十六进制字符串
*
* @param b
* byte[] 需要转换的字节数组
* @return String 十六进制字符串
*/
public static final String byte2hex(byte b[],int realLength) {
if (b == null) {
throw new IllegalArgumentException(
"Argument b ( byte array ) is null! ");
}
String stmp = "";
StringBuilder sbHex=new StringBuilder();
for (int n = 0; n < realLength; n++) {
stmp = Integer.toHexString(b[n] & 0x000000FF);
if (stmp.length() == 1) {
sbHex.append("0");
sbHex.append(stmp);
} else if(stmp.length() == 2) {
sbHex.append(stmp);
}else{
PL2303HXDNative.OnNotify_LOG("byte2hex : " + stmp.length());
}
}
return sbHex.toString().toUpperCase();
}
private static void WriteToUARTDevice(int index,String strWrite) {
PL2303HXDNative.OnNotify_LOG(String.valueOf(index)+" Enter WriteToUARTDevice");
if(mSerialMulti==null)
{
PL2303HXDNative.OnNotify_LOG(String.valueOf(index)+" WriteToUARTDevice fail,mSerialMulti=null");
return;
}
if(!mSerialMulti.PL2303IsDeviceConnectedByIndex(index)){
PL2303HXDNative.OnNotify_LOG(String.valueOf(index)+" WriteToUARTDevice fail,mSerialMulti is unconect");
return;
}
if( strWrite==null || "".equals(strWrite.trim()) ) { //str is empty
PL2303HXDNative.OnNotify_LOG(String.valueOf(index)+" WriteToUARTDevice: no data to write");
return;
}
PL2303HXDNative.OnNotify_LOG(String.valueOf(index)+" PL2303Multi Write(" + strWrite.length() + "):" + strWrite);
int res = 0;
try{
res = mSerialMulti.PL2303Write(index, hex2byte(strWrite.trim()));
}catch(Exception e){
PL2303HXDNative.OnNotify_LOG("UnsupportedEncodingException");
}
if( res<0 ) {
PL2303HXDNative.OnNotify_LOG(index+"w: fail to write: "+ res);
return;
}
PL2303HXDNative.OnNotify_LOG(String.valueOf(index)+" Leave WriteToUARTDevice");
} //private void WriteToUARTDevice(int index)
@Override
protected void onStart() {
super.onStart();
registerSerialMulti();
}
@Override
protected void onRestart() {
super.onRestart();
}
@Override
protected void onResume() {
super.onResume();
registerSerialMulti();
}//public void onResume()
@Override
protected void onPause() {
super.onStart();
registerSerialMulti();
}
@Override
protected void onStop() {
super.onStop();
registerSerialMulti();
}
@Override
protected void onDestroy() {
unRegisterSerialMulti();
super.onDestroy();
}
private void registerSerialMulti(){
if(registerflag)
return;
if(mSerialMulti!=null) {
int iDeviceCount = mSerialMulti.PL2303Enumerate();
PL2303HXDNative.OnNotify_LOG("onResume for mSerialMulti iDeviceCount="+String.valueOf(iDeviceCount));
if(0!=iDeviceCount){
//register receiver for PL2303Multi_USB notification
IntentFilter filter = new IntentFilter();
filter.addAction(mSerialMulti.PLUART_MESSAGE);
registerReceiver(PLMultiLibReceiver, filter);
registerflag = true;
}
}
}
private void unRegisterSerialMulti(){
if(mSerialMulti!=null) {
for(int i=0;i<MAX_DEVICE_COUNT;i++) {
gThreadStop[i] = true;
}//First to stop app view-thread
if(mSerialMulti.PL2303Enumerate()>0){
unregisterReceiver(PLMultiLibReceiver);
registerflag = false;
}
mSerialMulti.PL2303Release();
mSerialMulti = null;
}
}
private final BroadcastReceiver PLMultiLibReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(mSerialMulti.PLUART_MESSAGE)){
Bundle extras = intent.getExtras();
if(extras!=null) {
String str = (String)extras.get(mSerialMulti.PLUART_DETACHED);
// DumpMsg("receive data:"+str);
int index = Integer.valueOf(str);
if(DeviceIndex1==index) {
bDeviceOpened[DeviceIndex1] = false;
} else if(DeviceIndex2==index) {
bDeviceOpened[DeviceIndex2] = false;
} else if(DeviceIndex3==index) {
bDeviceOpened[DeviceIndex3] = false;
}else if(DeviceIndex4==index){
bDeviceOpened[DeviceIndex4] = false;
}else{
}
}
}
}//onReceive
};
private static void DelayTime(int dwTimeMS) {
//Thread.yield();
long StartTime, CheckTime;
if(0==dwTimeMS) {
Thread.yield();
return;
}
//Returns milliseconds running in the current thread
StartTime = System.currentTimeMillis();
do {
CheckTime=System.currentTimeMillis();
Thread.yield();
} while( (CheckTime-StartTime)<=dwTimeMS);
}
private static int ReadLen1;
private static byte[] ReadBuf1 = new byte[ReadDataBufferSize];
private static Handler mHandler1 = new Handler();
private static Runnable ReadLoop1 = new Runnable() {
public void run() {
for (;;) {
ReadLen1 = mSerialMulti.PL2303Read(DeviceIndex1, ReadBuf1);
if (ReadLen1 > 0) {
//ReadBuf1[ReadLen1] = 0;
PL2303HXDNative.OnNotify_LOG("Read 1 Length : " + ReadLen1);
mHandler1.post(new Runnable() {
public void run() {
String readBuf = byte2hex(ReadBuf1,ReadLen1);
PL2303HXDNative.OnNotify_MSG(DeviceIndex1,readBuf);
}//run
});//Handler.post
}//if (len > 0)
// PL2303HXDNative.OnNotify_LOG("Read Index(1) readSleep: "+readSleep);
DelayTime(readSleep);
if (gThreadStop[DeviceIndex1]) {
gRunningReadThread[DeviceIndex1] = false;
return;
}//if
}//for(...)
}//run
};//Runnable
private static int ReadLen2;
private static byte[] ReadBuf2 = new byte[ReadDataBufferSize];
private static Handler mHandler2 = new Handler();
private static Runnable ReadLoop2 = new Runnable() {
public void run() {
for (;;) {
ReadLen2 = mSerialMulti.PL2303Read(DeviceIndex2, ReadBuf2);
if (ReadLen2 > 0) {
//ReadBuf2[ReadLen2] = 0;
PL2303HXDNative.OnNotify_LOG("Read 2 Length : " + ReadLen2);
mHandler2.post(new Runnable() {
public void run() {
String readBuf = byte2hex(ReadBuf2,ReadLen2);
PL2303HXDNative.OnNotify_MSG(DeviceIndex1,readBuf);
}//run
});//Handler.post
}//if (len > 0)
DelayTime(readSleep);
if (gThreadStop[DeviceIndex2]) {
gRunningReadThread[DeviceIndex2] = false;
return;
}//if
}//for(...)
}//run
};//Runnable
private static int ReadLen3;
private static byte[] ReadBuf3 = new byte[ReadDataBufferSize];
private static Handler mHandler3 = new Handler();
private static Runnable ReadLoop3 = new Runnable() {
public void run() {
for (;;) {
ReadLen3 = mSerialMulti.PL2303Read(DeviceIndex3, ReadBuf3);
if (ReadLen3 > 0) {
//ReadBuf3[ReadLen3] = 0;
PL2303HXDNative.OnNotify_LOG("Read 3 Length : " + ReadLen3);
// String readBuf = byte2hex(ReadBuf3,ReadLen3);
// PL2303HXDNative.OnNotify_MSG(1,readBuf);
mHandler3.post(new Runnable() {
public void run() {
String readBuf = byte2hex(ReadBuf3,ReadLen3);
PL2303HXDNative.OnNotify_MSG(DeviceIndex1,readBuf);
}//run
});//Handler.post
}//if (len > 0)
DelayTime(readSleep);
if (gThreadStop[DeviceIndex3]) {
gRunningReadThread[DeviceIndex3] = false;
return;
}//if
}//for(...)
}//run
};//Runnable
private static int ReadLen4;
private static byte[] ReadBuf4 = new byte[ReadDataBufferSize];
private static Handler mHandler4 = new Handler();
private static Runnable ReadLoop4 = new Runnable() {
public void run() {
for (;;) {
ReadLen4 = mSerialMulti.PL2303Read(DeviceIndex4, ReadBuf4);
if (ReadLen4 > 0) {
//ReadBuf4[ReadLen4] = 0;
PL2303HXDNative.OnNotify_LOG("Read 4 Length : " + ReadLen4);
mHandler4.post(new Runnable() {
public void run() {
String readBuf = byte2hex(ReadBuf4,ReadLen4);
PL2303HXDNative.OnNotify_MSG(DeviceIndex1,readBuf);
}//run
});//Handler.post
}//if (len > 0)
DelayTime(readSleep);
if (gThreadStop[DeviceIndex4]) {
gRunningReadThread[DeviceIndex4] = false;
return;
}//if
}//for(...)
}//run
};//Runnable
}5)(PL2303HXDNative.java)定义java到Qt的调用函数:
package an.qt5.javagather;
import java.lang.String;
public class PL2303HXDNative
{
public static native void OnNotify_MSG(int index,String msg);
public static native void OnNotify_LOG(String log);
}
6)(javanative.h/cpp)在QT中实现java到Qt的调用函数及注册函数:
#ifndef JAVANATIVE_H
#define JAVANATIVE_H
bool registerNativeMethods();
#endif // JAVANATIVE_H
#include "javanative.h"
#include "simpleCustomEvent.h"
#include <QApplication>
#include <QDebug>
#ifdef ANDROID
#include <QAndroidJniEnvironment>
#include <QAndroidJniObject>
#include <jni.h>
#endif
namespace GVAL_Gather {
extern QObject *g_listener;
}
static void OnNotify_MSG(JNIEnv *env, jobject /*thiz*/,int index,jstring msg)
{
try{
QString qmsg;
const char *nativeString = env->GetStringUTFChars(msg, 0);
qmsg = QString("%1").arg(nativeString);
env->ReleaseStringUTFChars(msg, nativeString);
qDebug() << "msg=" << qmsg;
QCoreApplication::postEvent(GVAL_Gather::g_listener, new SimpleCustomEvent(1, qmsg,index));
}catch(...){
qDebug() << "OnNotify_MSG Exception";
}
}
static void OnNotify_LOG(JNIEnv *env, jobject /*thiz*/,jstring log)
{
try{
QString qlog;
const char *nativeString = env->GetStringUTFChars(log, 0);
qlog = nativeString;
env->ReleaseStringUTFChars(log, nativeString);
qDebug() << "log=" << qlog;
QCoreApplication::postEvent(GVAL_Gather::g_listener, new SimpleCustomEvent(2, qlog));
}catch(...){
qDebug() << "OnNotify_LOG Exception";
}
}
bool registerNativeMethods()
{
try{
JNINativeMethod methods[] {
{"OnNotify_MSG", "(ILjava/lang/String;)V", (void*)OnNotify_MSG},
{"OnNotify_LOG", "(Ljava/lang/String;)V", (void*)OnNotify_LOG}
};
const char *classname = "an/qt5/javagather/PL2303HXDNative";
jclass clazz;
QAndroidJniEnvironment env;
QAndroidJniObject javaClass(classname);
clazz = env->GetObjectClass(javaClass.object<jobject>());
qDebug() << "find PL2303HXDNative - " << clazz;
bool result = false;
if(clazz)
{
jint ret = env->RegisterNatives(clazz,
methods,
sizeof(methods) / sizeof(methods[0]));
env->DeleteLocalRef(clazz);
qDebug() << "RegisterNatives return - " << ret;
result = ret >= 0;
}
if(env->ExceptionCheck())
env->ExceptionClear();
return result;
}catch(...){
qDebug() << "registerNativeMethods Exception";
return false;
}
}
7)(simpleCustomEvent.h/cpp)java交互的
QEvent事件
#ifndef SIMPLECUSTOMEVENT_H
#define SIMPLECUSTOMEVENT_H
#include <QEvent>
#include <QString>
class SimpleCustomEvent : public QEvent
{
public:
SimpleCustomEvent(int arg1 = 0, const QString &arg2 = QString(),int arg3 = 0);
~SimpleCustomEvent();
static Type eventType();
int m_arg1;
QString m_arg2;
int m_arg3;
private:
static Type m_evType;
};
#endif // SIMPLECUSTOMEVENT_H
#include "simpleCustomEvent.h"
QEvent::Type SimpleCustomEvent::m_evType = (QEvent::Type)QEvent::None;
SimpleCustomEvent::SimpleCustomEvent(int arg1, const QString &arg2, int arg3)
: QEvent(eventType()), m_arg1(arg1), m_arg2(arg2), m_arg3(arg3)
{}
SimpleCustomEvent::~SimpleCustomEvent()
{
}
QEvent::Type SimpleCustomEvent::eventType()
{
if(m_evType == QEvent::None)
{
m_evType = (QEvent::Type)registerEventType();
}
return m_evType;
}
8)
通过QEvent事件实现及注册绑定
bool GatherMgr::event(QEvent *et)
{
#ifdef ANDROID
if(et->type() == SimpleCustomEvent::eventType())
{
SimpleCustomEvent *sce = (SimpleCustomEvent*)et;
if(sce->m_arg1 == 1)//msg
{
displayMsg(QString("port[%1] ReadData(%2):%3")
.arg(sce->m_arg3).arg(sce->m_arg2.length()).arg(sce->m_arg2));
emit notify_MsgAndroid(sce->m_arg2,sce->m_arg3);
}
else if(sce->m_arg1 == 2)//log
{
displayMsg(sce->m_arg2);
}
else
{
displayMsg("unkown_info");
}
return true;
}
#endif
return QWidget::event(et);
}
#ifdef ANDROID
SimpleCustomEvent::eventType();
registerNativeMethods();
#endif
GatherMgr gatherMgr;
#ifdef ANDROID
GVAL_Gather::g_listener = qobject_cast<QObject*>(&gatherMgr);
#endif
9)Qt到java函数的调用实现,项目涉及的业务、通信解析、跨平台相关的处理就不展示了:
#ifdef ANDROID
#include <QtAndroidExtras/QAndroidJniObject>
#include <QAndroidJniEnvironment>
#include<unistd.h>
#endif
void SerialPort::openSerialPort()
{
#ifndef ANDROID
...
#else
jint javaIndex = serialconfig.name.toInt();
jint javaBaudRate = (int)serialconfig.baudRate;
jint ret = QAndroidJniObject::callStaticMethod<jint>("an/qt5/javagather/PL2303HXDSerialPort",
"OpenUARTDevice",
"(II)I",
javaIndex,
javaBaudRate);
QAndroidJniEnvironment env;
if(env->ExceptionCheck()){
qDebug()<<"openSerialPort Exception!";
ret = 3;
env->ExceptionClear();
}
if(1!=ret){
qDebug() << (QObject::tr("Open error,ret(%1),Index(%2)").arg(ret).arg(serialconfig.name));
serialState = false;
}else{
serialState = true;
}
#endif
}
void SerialPort::closeSerialPort()
{
#ifndef ANDROID
...
#else
if(serialState){
jint javaIndex = serialconfig.name.toInt();
QAndroidJniObject::callStaticMethod<void>("an/qt5/javagather/PL2303HXDSerialPort",
"closeUARTDevicel"
,"(I)V"
,javaIndex);
serialState = false;
QAndroidJniEnvironment env;
if(env->ExceptionCheck()){
qDebug()<<"closeSerialPort Exception!";
env->ExceptionClear();
}
}
#endif
}
void SerialPort::sendMsg(QString cmd,bool u16f)
{
// qDebug() << "cmd="<<cmd;
// emit logNotify("16bit="+cmd);
try{
#ifndef ANDROID
...
#else
// qDebug() << QString("get cmd! cmd.size=%1").arg(cmd.size());
if(!serialState)
{
openSerialPort();
if (!serialState){
qDebug() << "send cmd fail,serial isn't open!";
m_PortState.m_bExit=true;
return;
}
}
if(serialState){
m_PortState.m_bExit=false;
// qDebug() << QString("send cmd for serial write! cmd.size=%1").arg(cmd.size());
emit logNotify(QString("gather(%1) send cmd for serial write! cmd.size=%2,cmd=%3")
.arg(gatherID).arg(cmd.size()).arg(cmd));
jint javaIndex = serialconfig.name.toInt();
QAndroidJniObject javaMsg = QAndroidJniObject::fromString(cmd);
QAndroidJniObject::callStaticMethod<void>("an/qt5/javagather/PL2303HXDSerialPort",
"WriteToUARTDevice"
,"(ILjava/lang/String;)V"
,javaIndex
, javaMsg.object<jstring>());
QAndroidJniEnvironment env;
if(env->ExceptionCheck()){
qDebug()<<"SerialPort writeData Exception!";
env->ExceptionClear();
m_PortState.m_wf=false;
}else{
oldDownCmd = cmd;
m_PortState.m_wf=true;
}
}
#endif
}catch(...){
qDebug()<<QString("SerialPort::sendMsg(%1,%2) Exception!").arg(cmd).arg(u16f);
}
}
10)程序的开机自启动及通知相关:
package an.qt5.javagather;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class GatherBootUpReceiver extends BroadcastReceiver
{
// 系统启动完成
static final String ACTION = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent)
{
// 当收听到的事件是“BOOT_COMPLETED”时,就创建并启动相应的Activity和Service
if (intent.getAction().equals(ACTION)) {
// 开机启动的Activity
Intent boot = new Intent(context, PL2303HXDSerialPort.class);
boot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(boot);
}
}
}
</activity>
<receiver android:enabled="true" android:name="an.qt5.javagather.GatherBootUpReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
11)android的usb授权:
<application>
<activity>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
</application>
<uses-feature android:name="android.hardware.usb.host"/>
关于PL2303 usb 的串口通讯,实际项目使用中根据实际需要需要进行通讯报文采集等待、报文分帧等细节处理,本文项目使用主要参考了PL2303的demo和《Qt on Android核心编程》的extendsQtWithJava样例迎合自身项目要求进行实现,在项目测试中表现得稳定性、通讯效率与QSerialPort的串口通讯并无多大差别(更严格的压力测试没深化)。