iOS - WiFi近场通信(局域网通信)

硬件:乐鑫 ESP8266


模块工作原理:

首先,硬件通电,如果之前没有配过网,会进入到等待配网的模式,然后监听周围的数据包,手机通过UDP组播、广播数据包(这个数据包是SSID和密码),WIFI模块监听接收到数据包之后进行解析(每家厂商有自己的算法),解析之后自动连接上路由器;如果之前配过网,先自己连接路由器,连接上路由器后进入工作模式。
然后,在连接路由器后进入工作模式,会向外发送数据包(这个数据硬件自定义),手机通过UDP监听1112端口,就可以得到这些数据,这些数据分析一下显示出来。这是接收数据,发送数据的话是通过UDP直接往2525端口(别的端口好像也没问题,比如1112端口)发送数据包。


APP工作流程:
主要为三个页面;
一、WiFi设备列表页;
二、一键配网页;
三、设备控制页;

在WiFi设备列表页,右上角为添加设备按钮,点击按钮后进入到一键配网页,配网成功新建一个设备并返回到WiFi设备列表页显示,点击WiFi设备列表页进入 设备控制页。(注意,比如设备列表页有两个设备,两个设备分别显示各自的数据)


项目分析:

整个模块与硬件交互大致分为两个部分;一、配网部分;二、和硬件数据交互部分。

1、配网部分,乐鑫已经有了sdk(有乐鑫自己配网的方式),地址:https://github.com/EspressifApp

2、和硬件数据交互部分,为了图方便,采用了第三方Asyncudpsocket框架。下面展示原生UDP和Asyncudpsocket各自方法。



和硬件交互部分:

/

1.原生UDP部分

引用C框架

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
UDP代码

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.view.backgroundColor = [UIColor whiteColor];
    
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        [self UDP_Server];
    });

}

/*
 第一步:创建socket并配置socket
 第二步:调用bind绑定服务器本机ip及端口号
 第三步:调用recvfrom接收来自客户端的消息
 第四步:调用sendto将接收到服务器端的信息返回给客户端
 第四步:调用close关闭socket
 */
-(void)UDP_Server
{
    int serverSockerId = -1;
    ssize_t len = -1;
    socklen_t addrlen;
    char buff[1024];
    struct sockaddr_in ser_addr;
    
    // 第一步:创建socket
    // 注意,第二个参数是SOCK_DGRAM,因为udp是数据报格式的
    serverSockerId = socket(AF_INET, SOCK_DGRAM, 0);
    
    if(serverSockerId < 0) {
        NSLog(@"Create server socket fail");
        return;
    }
    
    addrlen = sizeof(struct sockaddr_in);
    bzero(&ser_addr, addrlen);
    
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    ser_addr.sin_port = htons(1112);
    
    // 第二步:绑定端口号
    if(bind(serverSockerId, (struct sockaddr *)&ser_addr, addrlen) < 0) {
        NSLog(@"server connect socket fail");
        return;
    }
    
    for (; ; ) {
        bzero(buff, sizeof(buff));
        
        // 第三步:接收客户端的消息
        len = recvfrom(serverSockerId, buff, sizeof(buff), 0, (struct sockaddr *)&ser_addr, &addrlen);
        // 显示client端的网络地址
        NSLog(@"receive from %s\n", inet_ntoa(ser_addr.sin_addr));
        // 显示客户端发来的字符串
        NSLog(@"recevce:%s", buff);
        
        // 第四步:将接收到的客户端发来的消息,发回客户端
        // 将字串返回给client端
//        sendto(serverSockerId, buff, len, 0, (struct sockaddr *)&ser_addr, addrlen);
    }
    
    
    // 第五步:关闭socket
//    close(serverSockerId);
    
}


/
2.第三方Asyncudpsocket部分
关于数据接收直接写了一个单例,用以专门处理数据。

#import "WiFiLanDataModel.h"
#import "GCDAsyncUdpSocket.h"

@interface WiFiLanDataModel ()<GCDAsyncUdpSocketDelegate>
{
    NSString *ipStr;
    NSString *portStr;
    NSString *msgStr;
    NSData   *macData;
    
    int htemp;
    int filmp;
    int wtemp;
    NSString *wifiMac;
}

@property (nonatomic, strong) GCDAsyncUdpSocket *serverSocket;

@end

@implementation WiFiLanDataModel

@synthesize serverSocket;

//单例
+ (WiFiLanDataModel *)sharedWiFiLanDataModel
{
    static WiFiLanDataModel *wifiLanDataModel = nil;
    
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        wifiLanDataModel = [[self alloc] init];
        [wifiLanDataModel initWiFi];
    });
    
    return wifiLanDataModel;
}

-(void)initWiFi
{
    if (serverSocket == nil) {
        serverSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    
    portStr = @"1112";    
    
    NSError *error = nil;
    if (![serverSocket bindToPort:[portStr intValue] error:&error])
    {
        NSLog(@"Error starting server (bind): %@", error);
        return;
    }
    if (![serverSocket beginReceiving:&error])
    {
        [serverSocket close];
        NSLog(@"Error starting server (recv): %@", error);
        return;
    }
    [serverSocket localPort];
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  //wifi模块返回数据,下面为自己项目数据处理。
    if (msg) {
        /* If you want to get a display friendly version of the IPv4 or IPv6 address, you could do this:
         
         NSString *host = nil;
         uint16_t port = 0;
         [GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
         */
        
        NSString *msgMac = [msg substringWithRange:NSMakeRange(0,12)];
        NSString *msgNum = [msg substringWithRange:NSMakeRange(12,4)];
        
        int temp; 
        if (msg.length != 18) {
            temp = [[msg substringWithRange:NSMakeRange(16,3)] intValue] / 10;
        }
        else {
            temp = [[msg substringWithRange:NSMakeRange(16,2)] intValue] / 10;
        }
        wifiMac = msgMac;
        
//        NSLog(@"msg==>%@", msg);

        if ([msgNum isEqualToString:@"open"]) {
//            NSLog(@"open ==> %d°C", temp);
        }
        else if ([msgNum isEqualToString:@"htem"]) {
//            NSLog(@"室温 ==> %d°C", temp);
            htemp = temp;
        }
        else if ([msgNum isEqualToString:@"film"]) {
//            NSLog(@"膜温 ==> %d°C", temp);
            filmp = temp;
        }
        else if ([msgNum isEqualToString:@"wtem"]) {
//            NSLog(@"设置 ==> %d°C", temp);
            wtemp = temp;
        }
        else if ([msgNum isEqualToString:@"clos"]) {
//            NSLog(@"关闭 ==> %d°C", temp);
        }
        else {
//            NSLog(@"msg==>%@", msgNum);
        }
        
        
    }
    else {
        NSLog(@"Error converting received data into UTF-8 String");
    }
    
    NSDictionary *dic = @{@"msg":msg,
                          @"wifiMac":wifiMac,
                          @"htem":[NSNumber numberWithInt:htemp],
                          @"film":[NSNumber numberWithInt:filmp],
                          @"wtem":[NSNumber numberWithInt:wtemp]};
    //温度数据发送ACK
    [[NSNotificationCenter defaultCenter] postNotificationName:kWiFiTemperatureDataTransmissionNotification object:dic];
    
    macData = address;
    NSString *msgM = [msg substringWithRange:NSMakeRange(0,12)];

    [WiFiDeviceSave DatamacAddress:msgM withWifiMacData:macData];

}

///
//设置温度
-(void)connectBtnActions:(int)temp withMac:(NSString *)wifiMacs withData:(NSData *)dataMac
{
    NSString *msg = [NSString stringWithFormat:@"%@wtem%d", wifiMacs, temp];
    NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
    [serverSocket sendData:data toAddress:dataMac withTimeout:-1 tag:0];
}

//开关
-(void)connectBtnActionsOFFON:(NSString *)off withMac:(NSString *)wifiMacs withData:(NSData *)dataMac
{
    NSString *msg = [NSString stringWithFormat:@"%@%@180", wifiMacs, off];
    NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
    [serverSocket sendData:data toAddress:dataMac withTimeout:-1 tag:0];
}

//取消配网
-(void)connectBtnActionsWlan:(NSString *)wifiMacs withData:(NSData *)dataMac
{
    // 5ccf7f93ec54wifi
    NSString *msg = [NSString stringWithFormat:@"%@wifi", wifiMacs];
    NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
    [serverSocket sendData:data toAddress:dataMac withTimeout:-1 tag:0];
}







版权声明:本文为u010980584原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。