背景

阅读前预备知识:

  • 学习安晓辉老师的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模块用户手册这份文档做一个调试板子。

基于qt智能家居系统代码 基于qt的智能家居系统_android

这个板子是自己做的,使用的时HF-LPT220芯片。电路设计按情况,留出串口和复位键,就可以当作官方评估版使用了。

前期硬件开发要了解wifi芯片的操作:
  • 监听wifi芯片串口传来的数据
  • 了解透传模式
  • 了解串口数据传输和wifi串口控制命令。

软件实现smartconfig

就拿我的项目来作分析:

基于qt智能家居系统代码 基于qt的智能家居系统_android_02

我们在创建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设备哈哈!)
功能截图:

基于qt智能家居系统代码 基于qt的智能家居系统_移动开发_03

基于qt智能家居系统代码 基于qt的智能家居系统_android_04