Qt智能家居应用开发:硬件设备实现智能网络配置时候,如何使用第三方smart config 库
背景
阅读前预备知识:
学习安晓辉老师的qt for android 核心编程(了解怎么在Qt 里边c++ 调用java,java库怎么加进qt工程中)
qt android wifi 开发篇
现在做智能硬件设备,都是使用模块化的流行wifi芯片来接入云端,实现实时通讯和数据交互的工作.以前使用wifi芯片我们经常会遇到一个问题:在wifi设备初始化过程中,我们要手动从ap模式配置连接路由器,这个操作流程对于我们开发人员来说看起来很简单,当换路由器的时候,按复位键然后手动配置就好了.甚至现在我看到许多这样的产品比如网络摄像头,一些远程控制设备都是这个模式.甚至我在学校工作室的时候老师买了一套智能家居设备,我们要看着一本说明书按着流程来手动配置网络.也就是现在大部分智能设备还是存在这个问题(其实已经有解决方案了,也许开发商还没有留意到现在流行的智能网络配置功能,这个有没缺点漏洞我不知道,毕竟我也是新手,只用这个技术做过简单的智能家居系统).
wifi设备实现网络配置方式之间差异
比起传统的ap模式网页配置wif芯片网络或者串口配置网络,smartconfig是一种新型的智能网络配置功能,操作起来更智能方便,时物联网芯片产品的一大趋向。
区别:
ap模式下的网页形式配置
ap (所谓ap模式就是把wifi芯片启动成像路由器一样,可以让其他wifi终端连接到这个wifi芯片)模式下的网页形式是通过wifi终端手动搜索wifi芯片开的ap服务,然后连接该wifi芯片。接着在wifi终端打开浏览器,访问wifi芯片的地址(比如: http://192.168.0.1 ),由于wifi芯片本来支持ap模式,它已经是一个简单的网站服务,通过这网页去获取权限设置wifi芯片提供的设置项服务,比如重启连接到路由器等等。
串口配置
大部分wifi芯片都支持串口命令控制wifi芯片的所有功能。这时候就需要通过串口命令区控制wifi芯片了。
smartconfig
smartconfig 是一个面向软件开发者的智能网络配置功能。硬件开发商无需关系自己的wifi芯片怎么连接到路由器,也无需知道动态的路由怎么被切换。因为smartconfig时通过终端软件去让wifi芯片连接到路由器的。具体实现原理参考:智能硬件接入云平台解决方案
微信硬件平台
微信硬件平台里边有一个叫做airkiss 技术,下面的两个产品都支持这个技术,对于实现智能网络配置功能,它是一个相对完美的平台和方案.详细的学习请自行进平台官网看文档.
hi-fly系列wifi芯片(笔者的项目就是用这个芯片)
esp8266 wifi 芯片
hi-fly和esp系列芯片看起来大同小异,知识esp8266芯片实现智能网络配置比较麻烦,它里边只有实现源码,并没有封装成java库来使用,hi-fly产品就产商提供了一个java库,后边着重介绍怎么在qt里边使用这个库.
wifi芯片学习:
如果有官方评估版可以完全参考HF-LPT220wifi模块评估版使用指南学习搭建完整的调试环境,如果没有的话请参考HF-LPT220wifi模块用户手册这份文档做一个调试板子。
这个板子是自己做的,使用的时HF-LPT220芯片。电路设计按情况,留出串口和复位键,就可以当作官方评估版使用了。
前期硬件开发要了解wifi芯片的操作:
监听wifi芯片串口传来的数据
了解透传模式
了解串口数据传输和wifi串口控制命令。
软件实现smartconfig
就拿我的项目来作分析:
我们在创建qt android项目的时候要把hi-flying 提供的hiflying-iots-android-smartlink7.0.2.jar 拷贝到android/libs目录下
然后写给c++调用的java接口类:
SmartLinkManager.java
package com.tommego; //hifly smart link libs import com.hiflying.smartlink.ISmartLinker; import com.hiflying.smartlink.OnSmartLinkListener; import com.hiflying.smartlink.SmartLinkedModule; import com.hiflying.smartlink.v3.SnifferSmartLinker; import com.hiflying.smartlink.v7.MulticastSmartLinker; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnDismissListener; import android.content.Intent; import android.content.IntentFilter; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; //屏幕像素密度 import android.util.DisplayMetrics; public class SmartLinkManager extends org.qtproject.qt5.android.bindings.QtActivity implements OnSmartLinkListener{ private static SmartLinkManager m_instance; //smart link protected static ISmartLinker mSnifferSmartLinker; private static boolean mIsConncting = false; protected static Handler mViewHandler = new Handler(); // protected static ProgressDialog mWaitingDialog; private static BroadcastReceiver mWifiChangedReceiver; private static String mSSID; public SmartLinkManager(){ m_instance = this; //smart linker initialization mSnifferSmartLinker = MulticastSmartLinker.getInstance(); } public static void startSmartLink(String ssid,String pwd){ // TODO Auto-generated method stub if(!mIsConncting){ //设置要配置的ssid 和pswd try { mViewHandler.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Toast.makeText(m_instance.getApplicationContext(), "正在后台配置wifi,请稍后", Toast.LENGTH_LONG).show(); } }); mSnifferSmartLinker.setOnSmartLinkListener(m_instance); // showDialog(); //开始 smartLink mSnifferSmartLinker.start(m_instance.getApplicationContext(), pwd.trim(), ssid.trim()); mIsConncting = true; // mWaitingDialog.show(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void stopSmartLink(){ mSnifferSmartLinker.stop(); mIsConncting = false; // closeDialog(); } public static String getSSID(){ WifiManager conMan = (WifiManager) m_instance.getSystemService(Context.WIFI_SERVICE); return conMan.getConnectionInfo().getSSID(); } @Override public void onLinked(final SmartLinkedModule module) { // TODO Auto-generated method stub // Log.w(TAG, "onLinked"); mViewHandler.post(new Runnable() { @Override public void run() { Toast.makeText(m_instance.getApplicationContext(), "发现新wifi模块"+ "\n mac:"+module.getMac()+ "\n ip:"+module.getModuleIP(), Toast.LENGTH_SHORT).show(); } }); } @Override public void onCompleted() { // Log.w(TAG, "onCompleted"); mViewHandler.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Toast.makeText(m_instance.getApplicationContext(), "智能配置完成!", Toast.LENGTH_SHORT).show(); // mWaitingDialog.dismiss(); mIsConncting = false; // closeDialog(); } }); } @Override public void onTimeOut() { // Log.w(TAG, "onTimeOut"); mViewHandler.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Toast.makeText(m_instance.getApplicationContext(), "配置超时!", Toast.LENGTH_SHORT).show(); // mWaitingDialog.dismiss(); mIsConncting = false; } }); } //获取屏幕像素密度 public static double getDentisy(){ DisplayMetrics metrics=new DisplayMetrics(); m_instance.getWindowManager().getDefaultDisplay().getMetrics(metrics); return metrics.density; } // public static native void showDialog(); // public static native void closeDialog(); }
然后c++ 就可以调度这个库了:
wifidevicemanager.h
#ifndef WIFIDEVICEMANAGER_H #define WIFIDEVICEMANAGER_H #include <QObject> #include <QUdpSocket> #include <QHostAddress> #include <QList> #include <QAndroidJniObject> #include <jni.h> #include <QDebug> #include <QByteArray> #include <QTcpSocket> struct WifiDevice{ QString ip,mac; }; class WifiDeviceManager : public QObject { Q_OBJECT public: explicit WifiDeviceManager(QObject *parent = 0); //device list Q_PROPERTY(QString deviceList READ deviceList NOTIFY deviceListChanged) QString deviceList(); Q_PROPERTY(QString revDatas READ revDatas NOTIFY revDatasChanged) QString revDatas(); Q_PROPERTY(QString revTcpDatas READ revTcpDatas NOTIFY revTcpDatasChanged) QString revTcpDatas(); //tcp socket properties Q_PROPERTY(QString tcpState READ tcpState NOTIFY tcpStateChanged) QString tcpState(); //tcp con Q_INVOKABLE void connectTcp(QString ip); Q_INVOKABLE void sendDatasTcp(QString data); //smart config Q_INVOKABLE void startSmartLink(QString SSID,QString pwd); Q_PROPERTY(QString ssID READ ssID NOTIFY ssIDChanged) QString ssID(){ return mssid; } //send udp datas Q_INVOKABLE void searchDevice(); //send datas Q_INVOKABLE void sendDatas(QString datas); Q_INVOKABLE void sendDatas(QString datas,int port); Q_INVOKABLE void setDAddress(QString ad){ this->dAddress=ad; } signals: void deviceListChanged(); void ssIDChanged(); void revDatasChanged(); void tcpStateChanged(); void revTcpDatasChanged(); public slots: void readDatas(); void readTcpDatas(); private: QString myAddress; QList<WifiDevice> mdeviceList; QUdpSocket *socket; QTcpSocket *tcpSocket; int port; QString mssid; QString mrevDatas; QString mRevTcpDatas; QString dAddress; QString m_tcpState; }; #endif // WIFIDEVICEMANAGER_H
wifidevicemanager.cpp
#include "wifidevicemanager.h" WifiDeviceManager::WifiDeviceManager(QObject *parent) : QObject(parent) { //init socket port=48899; socket=new QUdpSocket(this); socket->bind(QHostAddress::Broadcast,port); tcpSocket=new QTcpSocket(); tcpSocket->bind (8899); connect(socket,SIGNAL(readyRead()),this,SLOT(readDatas())); connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readTcpDatas())); this->m_tcpState="unConnected"; tcpStateChanged(); //init ssid QAndroidJniObject str=QAndroidJniObject::callStaticObjectMethod("com/tommego/SmartLinkManager", "getSSID", "()Ljava/lang/String;"); this->mssid=str.toString(); ssIDChanged(); //init address QHostAddress address; myAddress=address.toString(); } QString WifiDeviceManager::deviceList(){ return ""; } void WifiDeviceManager::readDatas(){ socket->waitForReadyRead(100); // qDebug()<<"&&&&&&&&&&&&&&&&&&&&&datas:"<<socket->readAll(); this->mrevDatas=socket->readAll(); while (socket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(socket->pendingDatagramSize()); QHostAddress sender; quint16 senderPort; // currentDatas="aa"; // mdataChanged(); socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); mrevDatas=datagram.data(); revDatasChanged(); // mdataChanged(); } revDatasChanged(); } void WifiDeviceManager::startSmartLink(QString SSID, QString pwd){ QAndroidJniObject cssid=QAndroidJniObject::fromString(SSID); QAndroidJniObject cpwd=QAndroidJniObject::fromString(pwd); QAndroidJniObject::callStaticMethod<void>("com/tommego/SmartLinkManager", "startSmartLink", "(Ljava/lang/String;Ljava/lang/String;)V", cssid.object<jstring>(), cpwd.object<jstring>()); } void WifiDeviceManager::searchDevice(){ QByteArray cmd; cmd.append("HF-A11ASSISTHREAD"); QString address="192.168.0.255"; QHostAddress ad(address); socket->writeDatagram(cmd,ad,port); // socket->writeDatagram(cmd,"192.168.1.255",port); } void WifiDeviceManager::sendDatas(QString datas){ QByteArray cmd; cmd.append(datas); QString address="192.168.0.255"; QHostAddress ad(address); socket->writeDatagram(cmd,ad,port); // socket->writeDatagram(cmd,"192.168.1.255",port); } QString WifiDeviceManager::revDatas(){ return this->mrevDatas; } void WifiDeviceManager::sendDatas(QString datas, int mport){ QByteArray cmd; cmd.append(datas); QString address="192.168.0.255"; QHostAddress ad(address); socket->writeDatagram(cmd,ad,mport); } QString WifiDeviceManager::tcpState (){ return this->m_tcpState; } void WifiDeviceManager::connectTcp (QString ip){ if(tcpSocket->state()==QTcpSocket::ConnectedState) return ; QHostAddress address(ip); tcpSocket->connectToHost(address,8899); if(tcpSocket->state()==QTcpSocket::ConnectedState) this->m_tcpState="connected"; else this->m_tcpState="unConnected"; tcpStateChanged(); } void WifiDeviceManager::sendDatasTcp (QString data){ if(tcpSocket->state()==QTcpSocket::ConnectedState){ QByteArray mdata; mdata.append(data); this->tcpSocket->write (mdata); } } void WifiDeviceManager::readTcpDatas(){ tcpSocket->waitForReadyRead(500); mRevTcpDatas=tcpSocket->readAll(); revTcpDatasChanged(); } QString WifiDeviceManager::revTcpDatas(){ return this->mRevTcpDatas; }
这里是调用smartlink库核心实现部分,在实现这个功能过程,遇到了很多java接口的坑.不过还好,整个源码实现完美在qt中跑起来了.这时候我们的app,就可以通过smartlink实现对为初始化的wifi设备进行一键配置网络了!(就算是有几十台wifi设备,都能直接在app中一键配置并且搜到wifi设备哈哈!)
功能截图: