iOS實(shí)現(xiàn)SmartConfig技術(shù)(TI)

要做物聯(lián)網(wǎng),首先我們要把硬件連接到WiFi,如何連接有很多種方案,比如在硬件上安裝一個(gè)熱點(diǎn),手機(jī)連接這個(gè)熱點(diǎn)把WiFi名字和密碼傳給硬件,也可以通過聲波等等......僅說一下在最近項(xiàng)目中用到的:WiFi快連技術(shù).SmartConfig其實(shí)是最早攻克這項(xiàng)技術(shù)的廠商TI的一種叫法.

原理大致是:
手機(jī)APP端發(fā)送包含WiFi用戶名WiFi密碼的UDP廣播包或者組播包,智能終端的WiFi芯片可以接收到該UDP包,只要知道UDP的組織形式,就可以通過接收到的UDP包解密出WiFi用戶名密碼,然后智能硬件配置收到的WiFi用戶名密碼到指定的WiFiAP上统刮。

首先你要有一個(gè)可以支持SmartConfig技術(shù)的硬件(可以直接在TI購買http://www.ti.com.cn/tool/cn/smartconfig);
大致是這樣:

Ti本身就有app,功能簡(jiǎn)單,就是把WiFi名字和密碼傳給硬件,讓硬件連上WiFi;
我們要做一個(gè)連接界面放到我們的app中,我找了好幾天找到了這個(gè)比較好的源碼(https://github.com/ray-x/Wifi-TI3200)
他的效果是這樣子的:

我最終做出來是這個(gè)樣子的:

打開上面的工程后看一下文件

紅色箭頭是必須導(dǎo)入的庫,藍(lán)色箭頭是主要模塊控制器的代碼,下面的是核心靜態(tài)庫

但是這個(gè)源碼有個(gè)問題,點(diǎn)擊了"Start"之后,控制臺(tái)會(huì)瘋狂打印app發(fā)送的UDP數(shù)據(jù)包大小Packet size
我目前也不知道如何取消打印,定義NSLog宏和printf宏都不好使,我覺得應(yīng)該是靜態(tài)庫中的問題.雖然不影響使用,但若有人知道如何處理恭請(qǐng)?jiān)u論賜教哇

控制器.h中什么都沒有,下面是我的部分.m中代碼:

#import "JDWifiVC.h"
#import "JDWifiView.h"
#import <SystemConfiguration/CaptiveNetwork.h>//顯示W(wǎng)iFi名稱
#import "MBProgressHUD.h"
//#import "Reachability.h"http://可連接性檢查模塊,監(jiān)測(cè)iOS設(shè)備網(wǎng)絡(luò)環(huán)境
#import "SmartConfigGlobalConfig.h"
#import "SmartConfigDiscoverMDNS.h"
#import "FirstTimeConfig.h"

@interface JDWifiVC ()

@property (nonatomic,weak)JDWifiView *wifiView;
@property (nonatomic, assign) BOOL canceled;//取消彈窗
//@property (nonatomic, strong) Reachability *wifiReachability;
@property (atomic,strong) SmartConfigGlobalConfig *globalConfig;
@property (nonatomic) SmartConfigDiscoverMDNS *mdnsService;
@property (nonatomic) FirstTimeConfig *firstTimeConfig;
@property (atomic, copy) NSString *passwordKey;
@property (nonatomic,weak) NSTimer *mdnsTimer;
@property (nonatomic,assign) BOOL discoveryInProgress;

@end
int const MDNSRestartTime = 15;

@implementation JDWifiVC

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    //設(shè)置UI
    [self setupUI];
    //監(jiān)測(cè)wifi狀態(tài),我覺得沒必要用Reachability,連不上用戶自己就去檢查路由器了嘛
   // [self detectWifi];
    self.discoveryInProgress = NO;
    // Do any additional setup after loading the view, typically from a nib.
    //都是庫中的方法,直接調(diào)用
    self.globalConfig = [SmartConfigGlobalConfig getInstance];
    // init mdns service
    self.mdnsService = [SmartConfigDiscoverMDNS getInstance];
    // add notification for discovered device
    //自動(dòng)發(fā)現(xiàn)設(shè)備
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(deviceAdded:)
                                                 name:@"deviceFound"
                                               object:nil];
}
-(void)dealloc
{
    //停止發(fā)送udp
    [self stopDiscovery];
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}
//跳轉(zhuǎn)設(shè)置界面
-(void)pushSetting
{    
    //需要在plist文件中加prefs
    NSURL *url = [NSURL URLWithString:@"prefs:root=WIFI"];
    if ([UIDevice currentDevice].systemVersion.floatValue <= 10.0)
    {
        [[UIApplication sharedApplication] openURL:url];
    }else{
        // iOS10 之后,只能跳轉(zhuǎn)到應(yīng)用設(shè)置界面,UIApplicationOpenSettingsURLString這個(gè)只支持iOS8之后.
        NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
        [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
    }
}
//顯示當(dāng)前連接WiFi名稱
-(void)getCurrentWifiName
{
    //WiFi名字
    NSString *ssid = @"Not Found";
    //mac地址
    NSString *macIp = @"Not Found";
    CFArrayRef myArray = CNCopySupportedInterfaces();
    if (myArray != nil)
    {
        CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
        if (myDict != nil)
        {
            NSDictionary *dict = (NSDictionary*)CFBridgingRelease(myDict);
            ssid = [dict valueForKey:@"SSID"];
            macIp = [dict valueForKey:@"BSSID"];
        }
    }
    [self.wifiView.wifiNameBtn setTitle:ssid forState:UIControlStateNormal];
}
//點(diǎn)擊開始連接按鈕
-(void)didClickNextBtn
{
    //先檢查一下
    NSString *wifiName = self.wifiView.wifiNameBtn.titleLabel.text;
    NSString *wifiPassword = self.wifiView.wifiPasswordTF.text;
    if (
        ([wifiName isEqualToString:@"wifi名字"])||
        (!wifiPassword)||
        wifiPassword.length == 0
        )
    {
        [self alertWithMessage:@"請(qǐng)檢查WiFi和密碼是否正確"];
    }
    //開始連接
    else
    {
        //我用的MBHUD,給一個(gè)圓環(huán)狀進(jìn)度條,還可以取消連接
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
        // Set the determinate mode to show task progress.
        hud.mode = MBProgressHUDModeDeterminate;
        hud.label.text = NSLocalizedString(@"連接中...", @"HUD loading title");
        
        // Configure the button.
        [hud.button setTitle:NSLocalizedString(@"取消", @"HUD cancel button title") forState:UIControlStateNormal];
        [hud.button addTarget:self action:@selector(cancelWork) forControlEvents:UIControlEventTouchUpInside];
        
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
            // 更新進(jìn)度
            [self doSomeWorkWithProgress];
            dispatch_async(dispatch_get_main_queue(), ^{
                [hud hideAnimated:YES];
                [self stopDiscovery];
            });
        });
    }
}
//圓環(huán)進(jìn)度條
- (void)doSomeWorkWithProgress
{
    //開始
    [self continueStartAction];
    self.canceled = NO;
    // This just increases the progress indicator in a loop.
    float progress = 0.0f;
    while (progress < 1.0f) {
        if (self.canceled) break;
        progress += 0.01f;
        
        dispatch_async(dispatch_get_main_queue(), ^{
            // Instead we could have also passed a reference to the HUD
            // to the HUD to myProgressTask as a method parameter.
            [MBProgressHUD HUDForView:self.navigationController.view].progress = progress;
        });
        //發(fā)送udp包秒數(shù)30秒
        usleep(300000);
    }
}
//取消連接
- (void)cancelWork
{
    [self stopDiscovery];
    self.canceled = YES;
}
//停止發(fā)送udp
-(void) stopDiscovery {
    [self.mdnsTimer invalidate];
    self.mdnsTimer = nil;
    self.discoveryInProgress = NO;
    [self.firstTimeConfig stopTransmitting];
    [self mDnsDiscoverStop];
}
- (void) continueStartAction
{
    self.discoveryInProgress = YES;
    [self startTransmitting];
}
/*
 This method start the transmitting the data to connected
 AP. Nerwork validation is also done here. All exceptions from
 library is handled.
 這個(gè)方法開始傳輸數(shù)據(jù)連接Ap茸塞。網(wǎng)上驗(yàn)證也是在這里完成并淋。所有的異常庫處理抖剿。
 */
- (void)startTransmitting{
    //假如...就...最后..語句
    @try {
        [self connectLibrary];
        if (self.firstTimeConfig == nil)
        {
            return;
        }
        [self sendAction];
    }
    //出錯(cuò)的處理
    @catch (NSException *exception) {
        //NSLog(@"%s exception == %@",__FUNCTION__,[exception description]);
        [self performSelectorOnMainThread:@selector(alertWithMessage:) withObject:[exception description] waitUntilDone:NO];
        
    }
    @finally {
    }
}
// check for internet and initiate the libary object for further transmit.
//檢查網(wǎng)絡(luò)和啟動(dòng)庫進(jìn)一步傳播池户。
-(void) connectLibrary
{
    NSString *wifiName = self.wifiView.wifiNameBtn.titleLabel.text;
    NSString *wifiPassword = self.wifiView.wifiPasswordTF.text;
    @try {
        //斷開庫
        [self disconnectFromLibrary];
        self.passwordKey = [self.wifiView.wifiPasswordTF.text length] ? self.wifiView.wifiPasswordTF.text : nil;
        NSString *ipAddress = [FirstTimeConfig getGatewayAddress];
        
        self.firstTimeConfig = [[FirstTimeConfig alloc] initWithData:ipAddress withSSID:wifiName withKey:self.passwordKey withFreeData:nil withEncryptionKey:nil numberOfSetups:4 numberOfSyncs:10 syncLength1:3 syncLength2:23 delayInMicroSeconds:1000];
        
        [self mDnsDiscoverStart];
        // set timer to fire mDNS after 15 seconds
        self.mdnsTimer = [NSTimer scheduledTimerWithTimeInterval:MDNSRestartTime target:self selector:@selector(mDnsDiscoverStart) userInfo:nil repeats:NO];
        
    }
    @catch (NSException *exception) {
        //NSLog(@"%s exception == %@",__FUNCTION__,[exception description]);
        [self performSelectorOnMainThread:@selector(alertWithMessage:) withObject:[exception description] waitUntilDone:NO];
    }
}
/**
 MDNS Discovery
 */
- (void) mDnsDiscoverStart 
{
    [self.mdnsService startMDNSDiscovery:nil];
}

- (void) mDnsDiscoverStop
 {
    [self.mdnsService stopMDNSDiscovery];
  }

/*
 This method begins configuration transmit
 In case of a failure the method throws an OSFailureException.
 這個(gè)方法開始配置傳輸對(duì)于失敗的方法拋出一個(gè)OSFailureException
 */
-(void) sendAction{
    @try {
        //NSLog(@"%s begin", __PRETTY_FUNCTION__);
        [self.firstTimeConfig transmitSettings];
        //NSLog(@"%s end", __PRETTY_FUNCTION__);
    }
    @catch (NSException *exception) {
        //NSLog(@"exception === %@",[exception description]);
        [self performSelectorOnMainThread:@selector(alertWithMessage:) withObject:[exception description] waitUntilDone:NO];
    }
    @finally {
        
    }
}
//彈窗警告
-(void)alertWithMessage:(NSString *)message
{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"哎呀,出錯(cuò)啦" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"再試一次" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    }];
    [alert addAction:action1];
    [self presentViewController:alert animated:YES completion:nil];
}

// disconnect libary method involves to release the existing object and assign nil.
//斷開庫方法需要釋放現(xiàn)有對(duì)象和分配nil片吊。
-(void) disconnectFromLibrary
{
    self.firstTimeConfig = nil;
}

-(void)deviceAdded:(id)sender
{
    if(self.discoveryInProgress == YES)
    {
        [self stopDiscovery];
        [self alertWithMessage:@"新設(shè)備被發(fā)現(xiàn)"];
    }
}
這個(gè)技術(shù)有一個(gè)問題,就是連接比較慢.我對(duì)iPhone做過一定的測(cè)試:
iPhone5S以發(fā)送udp包1分鐘為一次,10次連接成功在4次左右,官方app稍高一點(diǎn),7次左右,但并不是百分之百;
以發(fā)送udp包2分鐘為一次,10次連接成功5次左右,是可以稍微提高的;
iPhone6與iPhone5S情況差不多
iPhone6S則不論在哪種情況下10次連接成功都在1次左右,官方app就沒連接成功過(醉了...??)
iPhone7在哪種情況下都沒成功過

打電話給售后給我個(gè)郵箱,發(fā)送了郵件也不知道猴年馬月能回復(fù)...不過搜索了一下6S普遍有這個(gè)問題,不知如何解決

最后在技術(shù)論壇上找到了答案:應(yīng)該是硬件兼容性問題.
解決辦法的話,TI適配6S以上的硬件明年才會(huì)量產(chǎn),所以換個(gè)廠家的硬件吧??比如 鼎甲微聯(lián)http://www.djlink.cn/

希望能幫助到剛做這方面的同僚,節(jié)約一點(diǎn)時(shí)間;
若有知道如何解決上述問題的,評(píng)論留言不勝感激;
若有不妥之處也希望能夠在評(píng)論指正,大家一起進(jìn)步

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市把还,隨后出現(xiàn)的幾起案子实蓬,更是在濱河造成了極大的恐慌,老刑警劉巖吊履,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件安皱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡艇炎,警方通過查閱死者的電腦和手機(jī)酌伊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缀踪,“玉大人居砖,你說我怎么就攤上這事虹脯。” “怎么了奏候?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵循集,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鼻由,道長(zhǎng)暇榴,這世上最難降的妖魔是什么厚棵? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任蕉世,我火速辦了婚禮,結(jié)果婚禮上婆硬,老公的妹妹穿的比我還像新娘狠轻。我一直安慰自己,他們只是感情好彬犯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布向楼。 她就那樣靜靜地躺著,像睡著了一般谐区。 火紅的嫁衣襯著肌膚如雪湖蜕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天宋列,我揣著相機(jī)與錄音昭抒,去河邊找鬼。 笑死炼杖,一個(gè)胖子當(dāng)著我的面吹牛灭返,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坤邪,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼熙含,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了艇纺?” 一聲冷哼從身側(cè)響起怎静,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黔衡,沒想到半個(gè)月后蚓聘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡员帮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年或粮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捞高。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氯材,死狀恐怖渣锦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情氢哮,我是刑警寧澤袋毙,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站冗尤,受9級(jí)特大地震影響听盖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裂七,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一皆看、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧背零,春花似錦腰吟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侦镇,卻和暖如春灵疮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壳繁。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工震捣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氮趋。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓伍派,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親剩胁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诉植,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容