iOS实现SmartConfig技术(TI)

作者: oldSix_Zhu | 来源:发表于2016-12-21 16:30 被阅读1386次

    要做物联网,首先我们要把硬件连接到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/

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

    相关文章

      网友评论

      • 把你的天空染成淡蓝色:请问楼主 我最近想研究下这方面的东西 请问有推荐的芯片设备的吗 对这个不是很懂 简单的说 就是哪里可以买到 :joy:
        把你的天空染成淡蓝色:@oldSix_Zhu 好的 谢谢啦 我去看看
        oldSix_Zhu:@把你的天空染成淡蓝色 淘宝搜索TI,我就是用的这个公司的,文中也给了官网链接哦
      • oldSix_Zhu:我是做智能穿戴设备的。
        NB-LTE和NB-IoT有了解一点,但没做过:grin:
        WiFi快连技术适合短时间与硬件通信的,
        NB-LTE和NB-IoT一般是长连接

      本文标题:iOS实现SmartConfig技术(TI)

      本文链接:https://www.haomeiwen.com/subject/lllqvttx.html