iOS实现SmartConfig技术(TI)

要做物联网,首先我们要把硬件连接到WiFi,如何连接有很多种方案,比如在硬件上安装一个热点,手机连接这个热点把WiFi名字和密码传给硬件,也可以通过声波等等......仅说一下在最近项目中用到的:WiFi快连技术.SmartConfig其实是最早攻克这项技术的厂商TI的一种叫法.

原理大致是:
手机APP端发送包含WiFi用户名WiFi密码的UDP广播包或者组播包,智能终端的WiFi芯片可以接收到该UDP包,只要知道UDP的组织形式,就可以通过接收到的UDP包解密出WiFi用户名密码,然后智能硬件配置收到的WiFi用户名密码到指定的WiFiAP上。

首先你要有一个可以支持SmartConfig技术的硬件(可以直接在TI购买http://www.ti.com.cn/tool/cn/smartconfig);
大致是这样:

Ti本身就有app,功能简单,就是把WiFi名字和密码传给硬件,让硬件连上WiFi;
我们要做一个连接界面放到我们的app中,我找了好几天找到了这个比较好的源码(https://github.com/ray-x/Wifi-TI3200)
他的效果是这样子的:

我最终做出来是这个样子的:

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

红色箭头是必须导入的库,蓝色箭头是主要模块控制器的代码,下面的是核心静态库

但是这个源码有个问题,点击了"Start"之后,控制台会疯狂打印app发送的UDP数据包大小Packet size
我目前也不知道如何取消打印,定义NSLog宏和printf宏都不好使,我觉得应该是静态库中的问题.虽然不影响使用,但若有人知道如何处理恭请评论赐教哇

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

#import "JDWifiVC.h"
#import "JDWifiView.h"
#import <SystemConfiguration/CaptiveNetwork.h>//显示WiFi名称
#import "MBProgressHUD.h"
//#import "Reachability.h"//可连接性检查模块,监测iOS设备网络环境
#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];
    //设置UI
    [self setupUI];
    //监测wifi状态,我觉得没必要用Reachability,连不上用户自己就去检查路由器了嘛
   // [self detectWifi];
    self.discoveryInProgress = NO;
    // Do any additional setup after loading the view, typically from a nib.
    //都是库中的方法,直接调用
    self.globalConfig = [SmartConfigGlobalConfig getInstance];
    // init mdns service
    self.mdnsService = [SmartConfigDiscoverMDNS getInstance];
    // add notification for discovered device
    //自动发现设备
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(deviceAdded:)
                                                 name:@"deviceFound"
                                               object:nil];
}
-(void)dealloc
{
    //停止发送udp
    [self stopDiscovery];
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}
//跳转设置界面
-(void)pushSetting
{    
    //需要在plist文件中加prefs
    NSURL *url = [NSURL URLWithString:@"prefs:root=WIFI"];
    if ([UIDevice currentDevice].systemVersion.floatValue <= 10.0)
    {
        [[UIApplication sharedApplication] openURL:url];
    }else{
        // iOS10 之后,只能跳转到应用设置界面,UIApplicationOpenSettingsURLString这个只支持iOS8之后.
        NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
        [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
    }
}
//显示当前连接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];
}
//点击开始连接按钮
-(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:@"请检查WiFi和密码是否正确"];
    }
    //开始连接
    else
    {
        //我用的MBHUD,给一个圆环状进度条,还可以取消连接
        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), ^{
            // 更新进度
            [self doSomeWorkWithProgress];
            dispatch_async(dispatch_get_main_queue(), ^{
                [hud hideAnimated:YES];
                [self stopDiscovery];
            });
        });
    }
}
//圆环进度条
- (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;
        });
        //发送udp包秒数30秒
        usleep(300000);
    }
}
//取消连接
- (void)cancelWork
{
    [self stopDiscovery];
    self.canceled = YES;
}
//停止发送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.
 这个方法开始传输数据连接Ap。网上验证也是在这里完成。所有的异常库处理。
 */
- (void)startTransmitting{
    //假如...就...最后..语句
    @try {
        [self connectLibrary];
        if (self.firstTimeConfig == nil)
        {
            return;
        }
        [self sendAction];
    }
    //出错的处理
    @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.
//检查网络和启动库进一步传播。
-(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.
 这个方法开始配置传输对于失败的方法抛出一个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:@"哎呀,出错啦" 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.
//断开库方法需要释放现有对象和分配nil。
-(void) disconnectFromLibrary
{
    self.firstTimeConfig = nil;
}

-(void)deviceAdded:(id)sender
{
    if(self.discoveryInProgress == YES)
    {
        [self stopDiscovery];
        [self alertWithMessage:@"新设备被发现"];
    }
}
这个技术有一个问题,就是连接比较慢.我对iPhone做过一定的测试:
iPhone5S以发送udp包1分钟为一次,10次连接成功在4次左右,官方app稍高一点,7次左右,但并不是百分之百;
以发送udp包2分钟为一次,10次连接成功5次左右,是可以稍微提高的;
iPhone6与iPhone5S情况差不多
iPhone6S则不论在哪种情况下10次连接成功都在1次左右,官方app就没连接成功过(醉了...😂)
iPhone7在哪种情况下都没成功过

打电话给售后给我个邮箱,发送了邮件也不知道猴年马月能回复...不过搜索了一下6S普遍有这个问题,不知如何解决

最后在技术论坛上找到了答案:应该是硬件兼容性问题.
解决办法的话,TI适配6S以上的硬件明年才会量产,所以换个厂家的硬件吧😂比如 鼎甲微联http://www.djlink.cn/

希望能帮助到刚做这方面的同僚,节约一点时间;
若有知道如何解决上述问题的,评论留言不胜感激;
若有不妥之处也希望能够在评论指正,大家一起进步

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容