硬件:乐鑫 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版权协议,转载请附上原文出处链接和本声明。