微风小杨 发表于 2015-11-7 15:07:46

esp8266串口wifi简介,通过http网页实现控制引脚

本帖最后由 微风森林 于 2015-11-9 13:34 编辑

esp8266串口wifi,估计很多朋友都有。废话不多说,直接切入正题吧


esp-01,就是某宝上最常见的那款,通过uart接口,可以由arduino发送AT命令控制其功能。8266有两种工作模式,分别是station模式以及ap模式,简单来说,station模式是连你家无线路由器的,ap模式是8266作为热点由手机去连接它(或者无线路由器)。如无意外,在家里使用都会用station模式。当然,这两种模式可以同时进行,但是据我观察,我手头有好几块8266,有的可以正常工作,有的不能同时工作在两个模式。所以,以下教程是以station模式为例

某宝上直接买的8266,固件通常都比较老旧,查看固件版本的命令是AT+GMR,0.22版本以下的固件,http无法正常工作,因此如果你固件低于0.22,需要帮8266刷机。下面是0.50刷机教程。如果你的版本较高,可以跳过,直接运行代码
====================================================
刷机接线可参考http://jingyan.baidu.com/article/0964eca21d33ad8285f53632.html

另外,如果只有uno,而没有usb转串口板,可以像我一样,直接使用uno来进行串口刷机
方法是,gnd--gnd vcc--3.3v   rxd--RX txd--TX chpd--3.3vio0--gnd,另外,uno板子上的RESET请接gnd,防止arduino占用串口

________________
|   _   _    _    __   |
|||_||_||_|      |
||----------------      |
|    ____       ___   |
|   |       |    |   |    |
|   |____|   -----   |
|                            |
|   ■    ○    ○    ○   |
|   ○    ○    ○    ○   |
=============
   gnd   io2io0rxd
   txd   chpd rstvcc

懒得画图了,凑合看吧

正常连接以后,应该可以ide里面打开串口,设置波特率115200,选both NL&CR,输入AT然后回车,应该能看到串口回应OK

打开附件的工具,按如下参数设置添加文件,都在附件里面,注意COM PORT改成自己的





之后,点击START,拔插一下vcc就会开始刷机。如果提示失败,检查一下是否将串口窗口关闭了

刷机完成后,拔掉io0的线,输入AT,看看是否有反应,如果没反应,拔插一下vcc再试试。如果有提示OK,则刷机完成,输入AT+GMR回车,应该提示版本是0.50了。正常烧录程序后让arduino控制esp8266时,记得让RX--TXDTX-RXD,交叉接线。
====================================================
esp-01 常用AT命令,主要是这么几个:

AT+CWMODE=x       设置模式为x,1:station模式2:ap模式3:同时
AT+RST                   复位
AT+CWDHCP=x,y   开启dhcp,y=0关闭,1开启,x为0时是ap,1是station, 2是二者同时
AT+CWJAP="xxx","yyy"    当作为station模式时,加入热点xxx,xxx是热点SSID,yyy是热点密码
AT+CIPMUX=x         开启mux多路连接,如果要设置为服务器时,必须开启
AT+CIPSERVER=x,y   开始服务器,x为1时开启,0关闭,y为开启的端口,http协议所用的端口为80
AT+CIFSR                查看获得的ip
AT+GMR                  查看固件版本
AT+CIPSEND=x,y   发送tcp信息,x为连接的IPD值,y为信息长度
AT+CIPCLOSE=x      关闭某个tcp连接

HTTP是一个基于TCP的文本传输协议,我们在地址栏里面输入http://a.b.c.d/的时候,实际上是向a.b.c.d这个ip发送一个HTTP GET信息,这个信息如下(chrome浏览器)
GET / HTTP/1.1
Host: 192.168.1.107
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8

后面的不用管,是一系列附加信息。主要在于第一行,GET / 是发送的实际起作用的信息。这里我们地址栏什么都没输入,所以是空的。假如不是空的呢?比如,http://a.b.c.d/ABCD ,发送的信息如下
GET /ABCD HTTP/1.1
Host: 192.168.1.107
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8

看到没有,GET后面有了ABCD这个信息。因为esp8266只管tcp协议,因此http信息他原封不动的送给我们。我们要做的就是检测这一段GET信息,来实现不同的功能

除此之外,我们也可以给浏览器返回一个页面。页面实际上就是一段html代码,当我们接收到IPD的连接信息时,我们就用AT+IPSEND来发送这一段html代码给他,对方的浏览器就会显示这段html页面了。我们希望,这个页面里面,有两个按钮,一个是ON,一个是OFF,分别控制uno 13引脚的led

8266的接法是正常接法,vcc-3.3 gnd-gnd tx-rxd rx-txd chpd-3.3
下面是代码时间。

char mypage[] ="<html><body><a href=\"/ON\"><input type=\"Button\" value=\"ON\" style=\"position:absolute;top:50%;left:40%\"></input></a><a href=\"/OFF\"><input type=\"Button\" value=\"OFF\" style=\"position:absolute;top:50%;left:60%\"></input></a></body></html>";
int pagelength;
#define SSID "mySSID"
#define PSWD "myPSWD"
#define BUFFERSIZE 32
#define PORT 80          //http port 80
char SearchBuffer;
char ok[] ="OK";
char ipd[] = "+IPD,";
char rdy[] = "ready";
char GET[] = "GET /";
char on[] = "ON";
char off[] = "OFF";
void setup() {
pinMode(13,OUTPUT);
// put your setup code here, to run once:
memset(SearchBuffer,0,BUFFERSIZE);
Serial.begin(115200);
Serial.println(F("AT+RST"));
waitForStr(ok,5000);
Serial.println(F("ATE0"));         //disable echo
waitForStr(ok,1000);
Serial.println(F("AT+CWMODE=1"));    //station, AP+STATION is 3
waitForStr(ok, 1000);
Serial.println(F("AT+CWDHCP=1,1"));    //AP+STATION DHCP is 2 1
waitForStr(ok, 2000);
//Serial.println(F("AT+RST"));         //RESET
//waitForStr(ok,5000);
//waitForStr(rdy,5000);
Serial.println(F("AT+CWJAP?"));
bool ret= waitForStr(SSID,1000);
if(!ret){
    Serial.print(F("AT+CWJAP=\""));      //link
    Serial.print(SSID);
    Serial.print(F("\",\""));
    Serial.print(PSWD);
    Serial.println(F("\""));
    waitForStr(ok,10000);
}
//if(!stringCompare(Search+(n-2)%BUFFERSIZE,ok))
    //while(1);   //error
Serial.println(F("AT+CIPMUX=1"));    //mux
waitForStr(ok,1000);
Serial.print(F("AT+CIPSERVER=1,"));
Serial.println(PORT);
waitForStr(ok,1000);

pagelength=getStrLength(mypage);
}

void loop() {
// put your main code here, to run repeatedly:
static char mux;
waitForStr(ipd);
mux=Serial.read();
waitForStr(GET);
readTillSpace(SearchBuffer,10000);
if(stringCompare(SearchBuffer,on)==true)
    digitalWrite(13,HIGH);
else if(stringCompare(SearchBuffer,off)==true)
    digitalWrite(13,LOW);
delay(10);
emptyRxToBuffer(SearchBuffer, BUFFERSIZE);
Serial.print(F("AT+CIPSEND="));
Serial.print(mux);
Serial.print(",");
Serial.println(pagelength);
waitForStr(">");
Serial.println(mypage);
waitForStr(ok,10000);
Serial.print(F("AT+CIPCLOSE="));
Serial.println(mux);
waitForStr(ok,1000);
}
void waitForStr(char* str){
waitForStr(str,-1);
}
bool waitForStr(char* str, long timeout){
long intime=millis();
int i=0,j=0,cur=0,index=0;
int len=getStrLength(str);
bool ret=true;
char* temp=new char(len);
while(str!='\0'){
    while(j-cur<len){
      if(timeout>0)
      if(millis()-intime>timeout){
          ret=false;
          goto OUT;
      }
      if(Serial.available()){
      index=j%len;
      temp=Serial.read();
      j++;
      }
    }
    index=(cur+i)%len;
    if(str==temp)
      i++;
    else
      i=0,cur++;
}
OUT:
delete temp;
return ret;
}

int emptyRxToBuffer(char* buf, int bufsize) {
int i=0,ret=-1;
if(Serial.available()){
    while(Serial.available()){
      i=i+1;
      buf=Serial.read();
    }
    ret=i;
}
return ret;
}

bool stringCompare(char* a, char* b){
int i=0;
//Serial.println(a);
//Serial.println(b);
if(getStrLength(a)!=getStrLength(b))
    return false;
while(b!='\0'){
    if(a!=b){
      //Serial.println("false");
      return false;
    }
    i++;
}
//Serial.println("true");
return true;
}

int getStrLength(char* str){
int i=0;
while(str!='\0'){
    i++;
}
return i;
}

void readTillSpace(char* buf,long timeout){
long intime=millis();
int i=0;
while(1){
    if(Serial.available()){
      if((buf=Serial.read())!=' ')
      i++;
      else break;
    }
    if(millis()-intime>timeout)
      break;
}
buf='\0';
//Serial.println(buf);
}



程序本身很简单,先初始化一下8266的模式,接着开始服务器接收,如果有收到连进来的IPD号码,记住这个号码,然后向其返回一个html页面,此页面由mypage数组保存。并且,对连接进来的GET进行分析,如果是ON则开启led,如果是OFF则关闭

请将SSID和PSWD替换为自己路由器的SSID和密码。烧录程序后上电,过一会儿等它连接好,打开路由器,看看esp8266被分配了哪个ip,之后开启浏览器输入 http://*这个ip*/,成功后,就可以看到一个页面,上面有ON OFF两个按钮,控制13脚的led

至此,我们完成了一个最简单的8266 http服务器,通过浏览器页面来控制arduino的行为。大家如果脑洞够大,应该可以想到更复杂一些的页面,当然需要一定网页设计知识

wing 发表于 2015-11-8 20:09:18

有指导意义

マイナス37度 发表于 2015-11-8 20:15:09

楼主我试了下你的代码,发现不能用,我把模式改成3,DHCP也开起来可是esp没连上路由

脸上模块,浏览器里输192.168.4.1:80也打不开,但是用tcp工具能脸上服务器,说明服务器已经开启了怎么回事:'(:'(:'(

マイナス37度 发表于 2015-11-8 20:31:28

本帖最后由 マイナス37度 于 2015-11-8 21:23 编辑

又尝试了多次,终于看到on和off了,但是不稳定,点亮了之后关不掉了,或者关掉之后点不亮。。。

用的192。168.4.1:80路由还是连不上

wetnt 发表于 2015-11-9 11:07:18

收藏,很不错!收藏,很不错!收藏,很不错!

微风小杨 发表于 2015-11-9 13:15:54

be

本帖最后由 微风森林 于 2015-11-9 13:32 编辑

マイナス37度 发表于 2015-11-8 20:31 static/image/common/back.gif
又尝试了多次,终于看到on和off了,但是不稳定,点亮了之后关不掉了,或者关掉之后点不亮。。。

用的192。 ...

在我这边,升完0.50固件以后就蛮稳定了,我把它用在插座上控制,不过8266运行了一天还是会挂,需要重新初始化。

你的问题的话,需要确定问题先,是8266已经挂了,还是没挂,只是arduino处理串口信息未跟上?一般8266都有个蓝灯,当你初始化正确并且程序正常运行后,如果8266在服务器状态,那么在地址栏输入8266的ip,8266接收到http消息的时候会闪蓝灯,你先看看有没有闪?如果有闪的话,说明8266没问题,是arduino处理消息的问题,你可以打开串口,看看发生卡顿时,AT命令走到哪一步了。如果没闪,那就说明8266要么没连上路由,要么服务器没工作,我有一块8266就是当station+ap同时的时候无法接收外来http请求,试着排查一下,比如手动按照我程序里的命令去给8266初始化,然后看能不能接http请求。另外请告知一下固件版本。老固件版本一般都有很多问题,建议升级

マイナス37度 发表于 2015-11-9 15:14:41

我晚上换新款的esp试试,老款的新固件烧不了

マイナス37度 发表于 2015-11-9 15:31:39

本帖最后由 マイナス37度 于 2015-11-9 16:26 编辑

微风森林 发表于 2015-11-9 13:15 static/image/common/back.gif
在我这边,升完0.50固件以后就蛮稳定了,我把它用在插座上控制,不过8266运行了一天还是会挂,需要重新 ...

能讲下http么,好想知道怎么处理点击信号的

微风小杨 发表于 2015-11-9 16:35:18

本帖最后由 微风森林 于 2015-11-9 16:36 编辑

マイナス37度 发表于 2015-11-9 15:31 static/image/common/back.gif
能讲下http么,好想知道怎么处理点击信号的

这个其实很简单啊....我千言万语,都不如你自己手动操作试试看:
将8266接在usb转串口或者用我的方法接uno上面,然后按照我程序的步骤设置模式,dhcp,加入热点,最后开启服务器和80端口。然后你再电脑上用浏览器访问8266的ip,看看串口会打印什么。其实点击事件就是转到了另一个URL 比如 http://192.168.1.104/ON,这个ON字符串会传给8266,arduino解析ON还是OFF就可以了

マイナス37度 发表于 2015-11-9 16:50:46

微风森林 发表于 2015-11-9 16:35
这个其实很简单啊....我千言万语,都不如你自己手动操作试试看:
将8266接在usb转串口或者用我的方法接 ...

我现在的情况是:点击on或者off会跳转,再点就不跳转了,复位uno后再点又可以跳转一下

微风小杨 发表于 2015-11-9 18:57:15

本帖最后由 微风森林 于 2015-11-9 18:58 编辑

マイナス37度 发表于 2015-11-9 16:50 static/image/common/back.gif
我现在的情况是:点击on或者off会跳转,再点就不跳转了,复位uno后再点又可以跳转一下

这事我也碰到过,就是AT+CIPCLOSE的时候,再来连接AT+CIPSEND会提示busy,这就是0.22以下固件的问题,这是个bug,官网有说。你假如没法升级固件的话,可以把AT+CIPCLOSE那行以及下面的waitforok这几句注释掉,这样就不会进到这个漏洞,不过浏览器会一直是加载状态,有些浏览器未加载完便不会显示页面,不过chrome可以

マイナス37度 发表于 2015-11-9 19:04:32

本帖最后由 マイナス37度 于 2015-11-9 19:11 编辑

微风森林 发表于 2015-11-9 18:57
这事我也碰到过,就是AT+CIPCLOSE的时候,再来连接AT+CIPSEND会提示busy,这就是0.22以下固件的问题,这 ...

这漏洞我知道,也就是说在跳转页面时连接会断开咯?可是为什么一定要断开呢,哎,实在是不懂http

微风小杨 发表于 2015-11-9 20:29:00

マイナス37度 发表于 2015-11-9 19:04 static/image/common/back.gif
这漏洞我知道,也就是说在跳转页面时连接会断开咯?可是为什么一定要断开呢,哎,实在是不懂http

http流程就是这样,请求一定是客户端发起,客户端发出请求,服务器给出响应,响应完以后,这次会话就结束了,服务器必须断开tcp连接

マイナス37度 发表于 2015-11-9 21:28:10

本帖最后由 マイナス37度 于 2015-11-9 22:04 编辑

微风森林 发表于 2015-11-9 20:29 static/image/common/back.gif
http流程就是这样,请求一定是客户端发起,客户端发出请求,服务器给出响应,响应完以后,这次会话就结束 ...

下了1.0的固件,终于可以了,HTTP还是晕呼呼的,比如我在网页里输入192。168.4.1:80实际上像esp发送了什么,uno又像浏览器发送了什么

GET /ABCD HTTP/1.1
Host: 192.168.1.107
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8

看到没有,GET后面有了ABCD这个信息。因为esp8266只管tcp协议,因此http信息他原封不动的送给我们。我们要做的就是检测这一段GET信息,来实现不同的功能

除此之外,我们也可以给浏览器返回一个页面。页面实际上就是一段html代码,当我们接收到IPD的连接信息时,我们就用AT+IPSEND来发送这一段html代码给他,对方的浏览器就会显示这段html页面了。我们希望,这个页面里面,有两个按钮,一个是ON,一个是OFF,分别控制uno 13引脚的led

上面这段话的意思是我只要处理get/后面的on和off就行了是吧,但是我不理解uno到底像浏览器发送了什么

我把
<html><body><a href=\"/ON\"><input type=\"Button\" value=\"ON\" style=\"position:absolute;top:50%;left:40%\"></input></a><a href=\"/OFF\"><input type=\"Button\" value=\"OFF\" style=\"position:absolute;top:50%;left:60%\"></input></a></body></html>
这段保存成HTML,打开后不太一样

マイナス37度 发表于 2015-11-9 21:48:55

还有个问题,我手机输 ip:80打不开,我输ip/on或者off可以控制,但网页还是打不开,是不是要用HTML5协议啊?
页: [1] 2
查看完整版本: esp8266串口wifi简介,通过http网页实现控制引脚