必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

连接成功后获取真正的host和port必威:,广播地址

2019-09-17 13:21 来源:未知

热点直连的需求中,获取Wi-Fi列表是当时最头痛的问题,首先数次申请 Network Extension这个框架的使用权限,不知为何不停被拒始终申请不下来,到项目结束的现在也未能申请下来,到了怀疑人生的地步;而我们的需求是客户在系统设定中连接Wi-Fi模块热点直连后,还要客户选择要配网的路由进行配网,在拿不到这个权限而又要获取Wi-Fi列表,我们想了另一个途径,就是从Wi-Fi模块中去获取,直接发送at指令让模块给我返回附近的Wi-Fi列表,具体的at指令请咨询厂商或嵌入式工程师。后面就是modbus协议通讯交互的各种逻辑问题了,请自行发挥。

怎样搜得更合理,更科学更严谨,欢迎指导,我们用最屌丝最土的做法

在公司待的一年多时间里,做了几个蓝牙、Wi-Fi通讯的项目,碍于时间关系一直没有做整理工作,现在稍微闲下来,就顺便整理一下。iOS蓝牙3.0需要获得苹果MFI认证,因此项目初期和嵌入式工程师沟通iOS蓝牙只做4.0BLE,和嵌入式工程师做好沟通后,就开始进入BLE的iOS开发阶段。蓝牙、Wi-Fi项目中,实现连接、通讯很简单,项目大部分的时间是和嵌入式工程师联调通讯部分,最让人头痛的是iOS开发工程师没做过蓝牙项目同时嵌入式工程师也没做过蓝牙项目出了问题的时候不清楚到底是嵌入式工程师AT指令不对呢,还是iOS开发工程师自己的代码有问题,也花费大量时间发现、处理iphone与蓝牙模块中的异常情况,做出逻辑改进,优化用户体验。我们的项目中,iphone与蓝牙模块基于Modbus协议通讯,这以后笔者会慢慢写。本文是通过iphone手机控制外设,不涉及iphone手机之间的蓝牙连接交互,也不涉及其他设备控制iphone手机。

大概流程可以这样总结:

用户账号密码登录,返回这个账号关联的扫地机列表,如果没有扫地机,则跳转到wifi界面,让用户绑定扫地机。绑定扫地机的流程是这样的(我前面说错了):手机连到一个wifi下面,按住扫地机的绑定按钮,这个时候扫地机会处于接收广播的状态,然后手机广播手机连接的wifi名称和密码给扫地机,扫地机拿到wifi名称和密码之后,连接wifi,连接wifi成功之后,把扫地机的IP地址广播给手机。手机拿到扫地机的IP地址之后,把登录用户的用户名和密码发给扫地机,到扫地机通过刚才连接的wifi,连接到服务器,把自己关联到这个用户下面!

}

必威 1

必威 2

必威 3

四、登录成功而且有设备在线的情况下发指令操作

    1.主页的启动和暂停指令;

    2.预约的预约时间启动指令;

    3.娱乐的各种指令(顺时、逆时、向前、向后,风机);

    4.信息中的语音报时指令;

NSString *responseMessage = @"已即受到";

1.询问嵌入式使用哪个host、端口,接收该端口的广播;调用bindToPort,enableBroadcast,beginReceiving三个方法,之前没做过然后百度出了些答案,忘记了他们少了哪一个方法,然后一直接收不了设备返回的数据,请自行尝试;

使用上,直接按着ESPTouch提供的demo进行修改就行了,此处也不做过多的交接,详情请查看demo,同事,别问我demo在哪,官方自行搜索,谢谢。

必威 4CBCentralManagerStatePoweredOff的时候可添加方法通知到你当前的控制器,让它提示用户到系统->设置->蓝牙里打开蓝牙连接;CBCentralManagerStatePoweredOn的时候可添加方法通知你当前的控制器,告诉它蓝牙可用,让用户继续后面的操作。

二、在WiFi连接登录界面的情况下扫描扫地机设备

    1.首先按住扫地机的接收广播的按钮,为了可以接收手机发出的广播信息;

    2.通过第三方库ESPTouch来发布广播信息,主要广播手机连接WiFi的信息,ssid、Bssid、password;

    3.通过第三方库ESPTouch来接收扫地机返回的信息,主要是获取扫地机返回的host,用于后面绑定扫地机。

if(![socket acceptOnPort:8099 error:&err])//tcp socket的监听端口必须要与udp socket的端口一样 后面客户端才能正确与服务端建立tcp连接,因为客户端到时获取的端口是udp socket的端口

2.交互,当向Wi-Fi模块发送数据,host,port向嵌入式工程师询问,质问,不说就吊打他们就行了;

2.数据发送,请记得writeData:withTimeout:tag与readDataWithTimeout:tag都要调取,若不调取readDataWithTimeout:tag,数据发出去,然而接收不到response;

必威 5

一、连接服务器登录

    1.在固定的host和port情况下,通过TCP的GCDAsyncSocket连接,连接成功后获取真正的host和port;

    2.先把第一步的socket断开,创建新的socket,然后把第1步获取的host和port通过TCP的GCDAsyncSocket连接,连接成功获取安全码;

    3.把第2步获取的安全码通过第2步的socket(已连接)登录;

    4.登录成功后获取序列号,并且发送心跳包和获取扫地机设备数据(账号绑定的扫地机设备),有设备的情况下并发送查询和预约指令;

    5.监听心跳包是否超时断开连接了、设备的状态,账号是否在另外设备登录。

单播、多播(组播)和广播的区别 

4.以上,就完成了热点直连的整个过程,简单得不要不要的,都不知道为啥当时和我一起做项目的那个安卓搞来搞去都搞不出来,其余心跳什么乱七八糟的逻辑处理请自行发挥;

二.SmartConfig

第二步:实现通讯

三、在WiFi连接下的账户绑定扫地机设备

    1.通过UDP的AsyncUdpSocket广播(用户名、密码、扫地机返回的host、端口)给扫地机设备;

    2.扫地机获取到用户名和密码,然后用用户名和密码登陆服务器,然后此扫地机就关联到本用户账户下,下次登录服务器就可以获取到关联的扫地机设备了;

    3.通接收收据的代理可以知道返回什么数据,是绑定失败还是成功;

    4.通接收收据的代理监听是否要重新登录,如果绑定成功,且要重新登录测要跑登录流程,登录后再返回上一层界面。

NSLog(@"tcp newMessage = %@",newMessage);

3.Wi-Fi返回结果的时候,会触发GCDAsyncUdpSocketDelegate的回调,回调的数据处理自行发挥;

乐鑫官网有下载ESPTouch , SmartConfig具体传递秘钥之类的乱七八糟的原理请自行百度,只交接实现。

必威 6

2.客户端:

通过手机Wi-Fi控制设备有两种,一种是热点直连,另一种通过路由;刚好公司两个不同的项目,嵌入式那边分别用了这两个不同模式实现。笔者这两种的实现都是基于第三方库GCDAsyncSocket实现,GCDAsyncSocket只有四个文件,直接拖进项目或者pod进项目马上就能使用,而第二种的路由方案中,还要通过SmartConfig进行配网。关于SmartConfig,请自行百度。

1.搜,upd广播搜,看看能不能搜索出已经配网Wi-Fi模块,如果搜不出已配网的Wi-Fi模块则判定为没有已配网模块(总感觉很不严谨,但这个请找产品和设计,欢迎吊打他们);全域udp广播$identify,看看有没有回应的设备,同事,别问我为啥是$identify,我们用的这厂家人家就是要你搜$identify,你不喜欢可以找厂家出来吊打他们;

1.基于系统库<CoreBluetooth/CoreBluetooth.h>进行开发,因此导入头文件#import <CoreBluetooth/CoreBluetooth.h>;

if(![sockeTCP connectToHost:host onPort:port error:&err])

[self.udpSocket sendData:self.readCommandData toHost:@"10.10.176.1" port:520 withTimeout:-1 tag:100];

必威 7

9.上面调用discoverCharacteristics:forService,触发CBPeripheralDelegate回调。readValueForCharacteristic 和 setNotifyValue:forCharacteristic:请记得打开,不打开这两个进行操作的话,数据发出去了,得不到回调,然后一脸懵逼地跟嵌入式说没有数据返回,他只会跟你说已经给你返回了,人家确实是返回了;

NSLog(@"sock = %@, ReceiveData = %@, fromAddress = %@",sock,[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding],[[NSString alloc] initWithData:address encoding:NSUTF8StringEncoding]);

必威 8

必威 9

8.当上面peripheral调用discoverServices时,会触发CBPeripheralDelegate代理的回调。下面回调里面的内容忽略,根据具体情况而定,哪个服务里的哪个特征是读写的问嵌入式,使劲质问他就对了;

NSLog(@"bindError = %@",bindError);

一.热点直连

至此,基于SmartConfig方案的Wi-Fi完成,后面的都是逻辑处理问题了。

2.创建CBCentralManager实例

NSError *error = nil;

必威 10

11.调用了readValueForCharacteristic和setNotifyValue:forCharacteristic:后,按照嵌入式制定的modbus协议发送数据,触发CBPeripheralDelegate回调,忘记了是什么原因触发下图第一个回调了请自行尝试,后面按照正确的modbus协议发出数据,蓝牙模块透传给机器,机器完成操作返回给模块,模块->iphone,调起didUpdateValueForCharacteristic回调,NSData*data

characteristic.value;data就是要取回的包,回调中拼包等一系列数据处理、block回传值之类的乱七八遭的东西请自行发挥;

必威 11

至此,iphone手机连接蓝牙外设的项目已完成最主要的连接、交互两部分,剩下的功能实现,就要看各种逻辑处理的自行发挥了;

12.断开蓝牙

必威 12

整个交互过程就是这么简单,然后就是一大堆的组包、解包、拼包等逻辑题;以上,为本人在实战中自行探讨出来,如有说得不对的地方、可以更简单的地方,请帮忙指出;

NSLog(@"Adress = %@ %i",host,port);

SmartConfig和热点直连的两个项目中,我们公司SmartConfig项目使用TCP协议通讯,而热点直连使用的是UDP协议通讯。下面是SmartConfig项目:

  1. iphone手机发送指令

}

必威 13

必威 14

//开启tcp连接

2.问嵌入式工程师ip地址和port,不给直接吊打,连上socket,开启tcp通讯之旅;

value是你组好的包NSData,Characteristic是那个打开了readValueForCharacteristic和setNotifyValue:forCharacteristic的特征

NSLog(@"ReceiveData = %@, fromAddress = %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding],[[NSString alloc] initWithData:address encoding:NSUTF8StringEncoding]);

3.数据成功发出后,会触发GCDAsyncSocketDelegate的回调,回调里block返回控制器/数据处理等自行发挥;

6.搜索出你的蓝牙模块后,开始下一个动作,用iphone去连接蓝牙模块;

uint16_t port = 0;

补充:iOS12中,要获取Wi-Fi的SSID,一定要打开access wifi information

3.当创建完CBCentralManager实例后,会收到CBCentralManagerDelegate回调,执行以下代理方法(可自行把苹果弃用的方法枚举改为新的枚举,笔者较懒没改,但能用);

/////////////////////////////

必威 15

第一步:建立连接

- (IBAction)broadCast:(id)sender {

5.当CBCentralManager开始做scan这个搜索动作后,会触发CBCentralManagerDelegate的回调;此处有三坑,第一坑:蓝牙模块很久都搜不出来。针对这个坑,需要找到嵌入式工程师,让嵌入式工程师去调整蓝牙模块广播的频率,笔者的项目中,当嵌入式工程师调整完此频率后,就很容易把蓝牙模块搜索出来了;第二坑:比如我有一个蓝牙模块,两台名字不同的机器,第一台机器叫A,第二台机器叫B,当蓝牙模块插进A的时候,此时我们搜出的名字应该叫A,当蓝牙模块插进B的时候,此时我们搜出的名字应该叫B,如果此时间用peripheral.name,我们把原本插在A的蓝牙拔出,然后插进B,会发现peripheral.name还没变过来依然叫A,但嵌入式工程师广播的name,NSString* name = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];确实已经变成B了,笔者原来一直用peripheral.name,然后一直跟嵌入式工程师纠缠说他没改过来,但其实他确实已经改过来了;第三坑:iOS与安卓不同,安卓能轻松拿到蓝牙设备的mac地址,但iOS拿不到,这里嵌入式工程师和产品又会跟你说你最讨厌的一句话“安卓可以”,然后问题又推到iOS开发者身上了,笔者是不能够通过iphone直接拿到mac地址,要获取mac地址只能叫嵌入式工程师把mac地址写在广播里的advertisementData中,key是kCBAdvDataManufacturerData,要写到这个key的value里,关于kCBAdvDataManufacturerData,请自行百度搜索;RSSI为距离值;

}else

遵循代理<CBCentralManagerDelegate,CBPeripheralDelegate>

}

7.当调用上面connectPeripheral:options的方法连接成功后,会触发CBCentralManagerDelegate的回调,在回调中,需要调起服务services和特征Characteristics,这时,去找到你的嵌入式工程师,问他把读写放在services、Characteristics这两个数组的哪个地方,找出特征,通过该特征实现iphone和蓝牙设备之间的交互;

[sockeTCP readDataWithTimeout:-1 tag:0];

4.当蓝牙走到CBCentralManagerStatePoweredOn的时候,此时添加方法去告诉控制器蓝牙是可用的,此时用户可进行scan扫描周边蓝牙设备的操作,scanForPeripheralsWithServices中,传入nil表示对搜索周边所有的蓝牙设备,当然你也可以通过UUID寻找特定的蓝牙设备;

GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

必威 16

应用场景:

TAG标签:
版权声明:本文由必威发布于必威-编程,转载请注明出处:连接成功后获取真正的host和port必威:,广播地址