ESP8266
发表于:2021-07-25 |
字数统计: 29.1k | 阅读时长: 144分钟 | 阅读量:

1、esp8266系列模块

​ ESP8266 系列模组是深圳市安信可科技有限公司开发的一系列基于乐鑫ESP8266EX的低功耗UART-WiFi芯片模组,可以方便地进行二次开发,接入云端服务,实现手机3/4G全球随时随地的控制,加速产品原型设计。

  模块核心处理器 ESP8266 在较小尺寸封装中集成了业界领先的 Tensilica L106 超低功耗 32 位微型 MCU,带有 16 位精简模式,主频支持 80 MHz 和 160 MHz,支持 RTOS,集成 Wi-Fi MAC/ BB/RF/PA/LNA,板载天线。支持标准的 IEEE802.11 b/g/n 协议,完整的 TCP/IP 协议栈。用户可以使用该模块为现有的设备添加联网功能,也可以构建独立的网络控制器。

 **特点:**
  • 802.11 b/g/n
  • 内置Tensilica L106 超低功耗 32 位微型 MCU,主频支持 80 MHz 和160 MHz,支持 RTOS
  • 内置10 bit高精度ADC
  • 内置TCP/IP协议栈
  • 内置TR 开关、balun、LNA、功率放大器和匹配网络
  • 内置PLL、稳压器和电源管理组件,802.11b 模式下+18 dBm的输出功率
  • A-MPDU 、 A-MSDU 的聚合和 0.4 s的保护间隔
  • Wi-Fi @ 2.4 GHz,支持 WPA/WPA2 安全模式
  • 支持AT本地升级及云端OTA升级
  • 支持 STA/AP/STA+AP 工作模式
  • 支持 Smart Config 功能(包括 Android 和 IOS 设备)
  • HSPI 、UART、I2C、I2S、IR Remote Control、PWM、GPIO
  • 深度睡眠保持电流为 20 uA,关断电流小于 5 uA
  • 2 ms 之内唤醒、连接并传递数据包
  • 待机状态消耗功率小于1.0 mW (DTIM3)
  • 工作温度范围:详情请见具体型号规格书

fCFrdK.png

2、固件下载错误进行擦除

​ ESPtool.py是一个python开发的针对ESP8266的小工具,可以实现底层的操作,弥补ESP8266官方工具的不足。flash的小工具,可以弥补ESP8266官方工具的不足。它也是一个开源项目,项目在github上进行托管:https://github.com/themadinventor/esptool

​ 虽然可以直接从github上下载使用,但是更好的方法是通过网络的方式进行安装,这样不会缺少依赖模块,减少运行中的故障。下面就介绍它的安装方法。

  1. 因为esptool.py需要使用python2,所以我们先需要安装python2,并将python加入系统路径(path)。

  2. 安装python的包管理器pip,通常是使用get-pip.py进行安装。在 https://pip.pypa.io/en/latest/installing/ 可以找到安装的说明和需要下载的文件,按照说明可以很容易安装pip。(如果同时安装了python2和python3,pip可能默认是pip3,需要用pip2来代替下面的pip,在Linux上需要用sudo权限安装)。

  3. 用pip安装esptool
    pip install esptool

  4. 因为esptool需要使用串口,所以还需要安装pyserial。

pip install pyserial

  1. 安装后,在Linux下,通常就可以直接运行esptool.py,在Windwos下,esptool一般安装在python2\Scripts\目录下,需要输入完整目录才能运行,如:

c:\Python27\Scripts\esptool.py

​ 如果不清楚esptool.py的用法,可以输入-h查看帮助,如

esptool.py -h

​ 甚至可以查看某个用法的帮助:

esptool.py read_flash -h

  1. 擦除flash。
    首先要确认一下8266所连接的串口号,要以串口号作为指令的参数,如我的设备是在COM4,我运行的指令就是esptool.py –port COM4 erase_flash
    img
    此处需要注意,执行擦除的指令前,需要像烧录固件一样,让8266进入升级模式,即按住板上的flash键不放,按下rst键,等待两秒,松开rst键,再松开flash键。否则会出现如下的错误提示:img 这样flash的擦除工作就完成了,重新再烧录固件之后即可解决固件运行异常的问题。

3、环境准备(Arduino开发)

1、硬件准备

GPIO0决定板子处于什么模式(上电低电平为进入下载模式)

1、ESP-01系列

WhEJnU.md.pngfCZ14f.png

2、用的最多的12F

Whud4e.md.png
SMD-22封装,GPIO0-GPIO16共17个通用IO口,一个单通道ADC,GPIO6-GPIO11用于连接外部flash,不可用,支持SPI总线通信:GPIO12-GPIO15,支持I2C总线:GPIO4-GPIO5,串口通信:GPIO1-GPIO3。

  • 12F一般D0-D8(除D3口即GPIO0下载用);

  • D0:INPUT(输入)、OUTPUT(输出)、INPUT_PULLDOWN(输入,默认下拉,低电平);

  • 其余IO口:INPUT(输入)、OTPUT(输出)、INPUT_PULLUP(输入,默认上拉,高电平);

WhEdhR.md.png

注意:烧录模式GPIO0接地,正常模式GPIO悬空。

3、NodeMcu(ESP-12F开发板)

WhKk5D.md.png
WhKla8.md.png

4、8266-12E

fCJX6I.jpg

2、软件准备

1、Arduino安装+8266包安装

fCmCY6.png

2、芯片检测程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 测试ESP8266 demo,打印ESP8266模块信息
* 1.打印Arduino Core For ESP8266 版本
* 2.打印Flash的唯一性芯片id
* 3.打印Flash实际大小
* 4.打印IDE配置的使用Flash大小
* 5.打印IDE配置的Flash连接通信的频率
* 6.打印Flash连接模式:QIO QOUT DIO DOUT,可以理解为Flash传输速率
*/
void setup() {
Serial.begin(115200);
//使能软件看门狗的触发间隔
//规定时间(5S)内不喂狗,系统复位
ESP.wdtEnable(5000);
}
void loop() {
//喂狗
ESP.wdtFeed();
FlashMode_t ideMode = ESP.getFlashChipMode();
String coreVersion = ESP.getCoreVersion();
Serial.print(F("Arduino Core For ESP8266 Version: "));
Serial.println(coreVersion);
Serial.printf("Flash real id(唯一标识符): %08X\n", ESP.getFlashChipId());
Serial.printf("Flash 实际大小: %u KBytes\n", ESP.getFlashChipRealSize()/1024);
Serial.printf("IDE配置Flash大小: %u KBytes,往往小于实际大小\n", ESP.getFlashChipSize()/1024);
Serial.printf("IDE配置Flash频率 : %u MHz\n", ESP.getFlashChipSpeed()/1000000);
Serial.printf("Flash ide mode: %s\n\n", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" : ideMode == FM_DIO ? "DIO" : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN"));
delay(1000);
}

WhVRqU.md.png

4、相关外设使用

1、计时和延时

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 计时和延时:
* delay(ms);
* delayMicroseconds(us);延时中不能做其他事
* millis(); //返回重启后所经过的毫秒数
* micros(); //返回重启后所经过的微秒数
**/
long debouncdDelay = 60;//延时间隔
long lastDebounceTime = 0; //最近记录的一次时间
// 判断时间间隔是否大于设定的时间间隔。
if(millis()-lastDebounceTime>debouncdDelay){
lastDebounceTime = millis();
}

2、IO口(Blink)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* LED灯闪烁实验
* 12E模块,LED在GPIO2口,即NodeMCU的D4
*/
void setup() {
pinMode(D4, OUTPUT); // 初始化D1引脚为输出引脚
}
void loop() {
digitalWrite(D4, LOW); // 亮灯
delay(1000); // 延时1s
digitalWrite(D4, HIGH);// 灭灯
delay(1000); // 延时1s
}

3、中断

(除了DO/GPIO16,中断可以绑到任意GPIO脚)

相关函数:

  1. attachInterrupt(pin,function,mode); 在指定引脚设置为响应中断。pin:要设置的中断号,function:中断时执行的函数,不带任何参数,无返回,Interrupt type/mode:中断触发条件(CHANGE:改变沿;RISING:上升沿;FALLING:下降沿)
  2. detachInterrupt(pin); 禁用指定GPIO引脚上的中断。pin:要禁用的中断的GPIO引脚,无返回值。
  3. digitalPinToInterrupt(pin); 获取指定GPIO引脚的中断号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 功能描述:ESP8266中断演示
* D2口接下拉电阻到地,同时也通过一个按键开关接到VCC
* 当开关按下,D2接到上升沿,开启中断,进入中断函数
*/
void setup() {
Serial.begin(115200);//设置串口波特率
attachInterrupt(digitalPinToInterrupt(D2), InterruptFunc, RISING);//设置中断号、响应函数、触发方式
}
void loop() {
}
/**
* 中断响应函数
*/
ICACHE_RAM_ATTR void InterruptFunc(){
Serial.println("Hello ESP8266");
}

4、模拟输入(ADC)

esp8266只用一个10位ADC通道(和芯片供电电压复用:即可设置为测量系统电压或者外部电压)

1、测量外部电压

  • 方法:analogRead(A0);
  • 0-1.0V
  • 测量精度:10位ADC:0~$2^{10}$​​​
  • 注意:开发板上做了电阻分压器,使其能够测量0 ~ 3.3V(220K与100K电阻分压)
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 功能描述:ESP8266 ADC 读取外部电压
* 在串口调试器查看效果
*/
void setup() {
Serial.begin(115200);//配置波特率
}
void loop() {
Serial.print("ADC Value: ");
Serial.println(analogRead(A0));//输出0-1023 对应 外部输入电压 0-1.0v
//延时1s
delay(1000);
}

2、测量系统外部电压

  • 方法: ESP.getVcc()

  • 单位: mv

  • ADC引脚要悬空。读取前要更改ADC模式(在#include行后面)ADC_TOUT (对外部电压),ADC_VCC(对系统电压),默认读取外部

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 功能描述:ESP8266 ADC 读取系统电压
    * 在串口调试器查看效果
    */
    ADC_MODE(ADC_VCC);//设置ADC模式为读取系统电压
    void setup() {
    Serial.begin(115200);
    }
    void loop() {
    Serial.print("ESP8266当前系统电压(mV): ");
    Serial.println(ESP.getVcc());
    delay(1000);
    }

5、模拟输出(PWM)

  • analogWrite(pin,val) 在指定引脚上启用PWM;pin:GPIO; val:一般0 ~ PWMRANGE,默认PWMRANGE=1023;无返回值;analogWrite(pin,0)相当于禁用指定引脚上的PWM;

  • analogWriteRange(new_range) 改变PWMRANGE数值;new_range:新的PWMRANGE数值;无返回值;(可以调节PWM精度);

  • analogWriteFreq(new_frequency) 改变PWM频率;默认1KHz

    • Arduino For ESP8266的PWM频率范围为100Hz ~40KHz:

      源码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      static uint16_t analogFreq = 1000;
      extern void __analogWriteFreq(uint32_t freq) {
      if (freq < 100) {
      analogFreq = 100;
      } else if (freq > 40000) {
      analogFreq = 40000;
      } else {
      analogFreq = freq;
      }
      }

例程(呼吸灯)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 功能描述:ESP8266 PWM演示例程
*/
#define PIN_LED D6
void setup() {
pinMode(PIN_LED,OUTPUT);
analogWrite(PIN_LED,0);
}
void loop() {
for(int val=0;val<1024;val++){
//占空比不断增大 亮度渐亮
analogWrite(PIN_LED,val);
delay(2);
}
for(int val=1023;val>=0;val--){
//占空比不断变小 亮度渐暗
analogWrite(PIN_LED,1023);
delay(2);
}
}

6、串口通信

与传统Arduino设备完全一样。除硬件FIFO(128字节用于TX和RX)之外,硬件串口还有额外的256字节的TX和RX缓存。发送和接受全部由中断驱动。当FIFO/缓存满时,Write函数会阻塞工程代码执行,等待空闲空间。当FIFO/缓存空时,read函数也会阻塞工程代码的执行,等待串口数据进来。

  • NodeMcu上有两组串口,Serial和Serial1(都是硬件串口)。

    • Serial使用UART0,默认GPIO1(TX)和GPIO3(RX);Serial.begin执行之后,调用Serial.swap()可以将Serial重新映射到GPIO15(TX)和GPIO13(RX)。可来回调用。一般默认

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      /**
      * 功能描述:ESP8266 Serial映射例程
      */
      void setup() {
      Serial.begin(115200);
      Serial.println("GPIO1(TX),GPIO3(RX)");
      //调用映射方法
      Serial.swap();
      Serial.println("GPIO15(TX),GPIO13(RX)");
      //重新映射回来
      Serial.swap();
      Serial.println("GPIO1(TX),GPIO3(RX)");
      }
      void loop() {
      }
    • Serial1使用UART1,默认对应GPIO2(TX)。Serial1不能接收数据(RX被flash芯片占用),Serial1.begin(baudrate)

      1
      2
      3
      4
      5
      6
      7
      8
      void setup() {
      Serial.begin(115200);
      Serial.println("Hello Serial");
      Serial1.begin(115200);
      Serial1.println("Hello Serial1");
      }
      void loop() {
      }
  • 若不用Serial1且不映射串口,可将UART0的TX映射到GPIO2:在Serial.begin()之后调用Serial.set_tx(2)或者直接调用Serial.begin(baud,config,mode,2) ;

  • 默认当调用Serial.begin后,将禁用WIFI库的诊断输出,再次启动:Serial.setDebugOutput(true)若将调试输出映射到Serial1时:Serial1.setDebugOutput(true) ;

  • Serial.setRxBufferSize(size_t size)定义接收缓冲区大小,默认256;

  • Serial和Serial1对象都支持5,6,7,8个数据位,奇数(O),偶数(E)和无(N)奇偶校验,1或2个停止位:Serial.begin(baudrate,SERIAL_8N1)

  • 获取当前波特率设置:Serial.baudRate()Serial1.baudRate()

  • ESP8266软件串口功能

  • 检测进入Serial的未知波特率的数据:Serial.detectBaudrate(time_t timeoutMillis) : 尝试在timeoutMillis ms的时间内检测波特率,检测成功返回波特率,失败返回0。detectBaudrate()方法在Serial.begin()之前调用。可使用Serial.begin(detectedBaudrate)

示例(WIFI连接):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* statin模式下,创建一个连接到可接入点(wifi热点),并且打印IP地址
*/
#include <ESP8266WiFi.h>

#define AP_SSID "xxxxx" //这里改成你的wifi名字
#define AP_PSW "xxxxx"//这里改成你的wifi密码
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup(){
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时2s 为了演示效果
delay(2000);
DebugPrintln("Setup start");
//启动STA模式,并连接到wifi网络
WiFi.begin(AP_SSID, AP_PSW);

DebugPrint(String("Connecting to ")+AP_SSID);
//判断网络状态是否连接上,没连接上就延时500ms,并且打出一个点,模拟连接过程
//笔者扩展:加入网络一直都连不上 是否可以做个判断,由你们自己实现
while (WiFi.status() != WL_CONNECTED){
delay(500);
DebugPrint(".");
}
DebugPrintln("");

DebugPrint("Connected, IP address: ");
//输出station IP地址,这里的IP地址由DHCP分配
DebugPrintln(WiFi.localIP());
DebugPrintln("Setup End");
}

void loop() {
}

7、ESP8266与EEPROM

1、写数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 功能描述:该代码向EEPROM写入100字节数据
*/
#include <EEPROM.h>
int addr = 0; //EEPROM数据地址
void setup()
{
Serial.begin(9600);
Serial.println("");
Serial.println("Start write");

EEPROM.begin(100); //申请size大小的内存大小
for(addr = 0; addr<100; addr++)
{
int data = addr;
EEPROM.write(addr, data); //写数据
}
EEPROM.end(); //写入flash并释放内存空间 保存更改的数据

Serial.println("End write");
}
void loop()
{
}

2、读数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
* 功能描述:该代码从EEPROM读取100字节数据
*/
#include <EEPROM.h>

int addr = 0;

void setup()
{
Serial.begin(9600);
Serial.println("");
Serial.println("Start read");

EEPROM.begin(100);
for(addr = 0; addr<100; addr++)
{
int data = EEPROM.read(addr); //读数据
Serial.print(data);
Serial.print(" ");
delay(2);
}
//释放内存
EEPROM.end();
Serial.println("End read");
}
void loop()
{
}

3、清除数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
EEPROM Clear
Sets all of the bytes of the EEPROM to 0.
This example code is in the public domain.
*/
#include <EEPROM.h>
void setup() {
EEPROM.begin(100);
// write a 0 to all 100 bytes of the EEPROM
for (int i = 0; i < 100; i++) {
EEPROM.write(i, 0);
}
//释放内存
EEPROM.end();
}
void loop() {
}

4、结构体操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
* 功能描述:eeprom结构体操作
*/
#include <EEPROM.h>

#define DEFAULT_STASSID "danpianjicainiao"
#define DEFAULT_STAPSW "boge"

struct config_type
{
char stassid[32];
char stapsw[64];
};

config_type config;

/*
* 保存参数到EEPROM
*/
void saveConfig()
{
Serial.println("Save config!");
Serial.print("stassid:");
Serial.println(config.stassid);
Serial.print("stapsw:");
Serial.println(config.stapsw);

EEPROM.begin(1024);
uint8_t *p = (uint8_t*)(&config);
for (int i = 0; i < sizeof(config); i++)
{
EEPROM.write(i, *(p + i));
}
EEPROM.commit();
}

/*
* 从EEPROM加载参数
*/
void loadConfig()
{
EEPROM.begin(1024);
uint8_t *p = (uint8_t*)(&config);
for (int i = 0; i < sizeof(config); i++)
{
*(p + i) = EEPROM.read(i);
}
EEPROM.commit();
Serial.println("-----Read config-----");
Serial.print("stassid:");
Serial.println(config.stassid);
Serial.print("stapsw:");
Serial.println(config.stapsw);
}

/*
*初始化
*/
void setup() {
ESP.wdtEnable(5000);
strcpy(config.stassid, DEFAULT_STASSID);
strcpy(config.stapsw, DEFAULT_STAPSW);
saveConfig();
}
/*
*主循环
*/
void loop() {
ESP.wdtFeed();
loadConfig();
}

8、SPI通信

1、概述

​ 串行外设接口(Serial Peripheral Interface),高速、全双工、同步通信总线(同一时刻只有一主一从进行通信),四线(MISO:主入从出;MOSI:主出从入;SCK:同步时钟信号;SS或CS:片选使能)。(Quad SPI=2Dual SPI=4标准SPI)。

2、ESP8266 SPI类库成员函数

SPI.h头文件中,该类库只提供主设备API

  1. SPI.begin() 初始化SPI通信;无参,无返回值;
  2. SPI.end()关闭SPI通信;无参,无返回值;
  3. SPI.setBitOrder(order)设置数据传输顺序;
    • 参数(order):
      • ~ LSBFIRST,低位在前;
      • ~ MSBFIRST,高位在前;
    • 无返回值;
  4. SPI.setClockDivider(divider)设置通信时钟(由系统时钟分频)
    • 参数(divider):
      • ~ SPI_CLOCK_DIV2, 2分频;
      • ~ SPI_CLOCK_DIV4, 4分频;
      • ~ SPI_CLOCK_DIV8, 8分频;
      • ~ SPI_CLOCK_DIV16, 16分频;
      • ~ SPI_CLOCK_DIV32, 32分频;
      • ~ SPI_CLOCK_DIV64, 64分频;
      • ~ SPI_CLOCK_DIV128, 128分频;
    • 无返回值;
  5. SPI.setDataMode(mode)设置数据模式
    • 参数(mode):
      • ~ SPI_MODE0; 即:CPOL=0,CPHA=0
      • ~ SPI_MODE1; 即:CPOL=0,CPHA=1
      • ~ SPI_MODE2; 即:CPOL=1,CPHA=0
      • ~ SPI_MODE3; 即:CPOL=1,CPHA=1
    • 无返回值
    • 补充:四种模式,即SPI相位(CPHA)和极性(CPOL)分别为0或1;
      • CPOL:即SPI空闲时,SCLK的电平(1高,0低);
      • CPHA:即SPI在SCLK的第几个边沿开始采样(0第一个,1第二个);
  6. SPI.transfer(val)传输1B的数据。全双工,发1B收1B数据
  7. SPI.transfer16(val)传输2B数据,从机返回2B作为返回值;
  8. SPI.transferBuf(buf,count)传输一个缓冲区数据,参数为发送的缓冲区buf(uint8_t*数据),count位缓冲区大小,无返回值(但从机传输来的数据会替换掉buf缓冲区的数据)
  9. SPI.pins(sck,miso,mosi,ss)切换SPI引脚映射,需在SPI.begin()前调用

3、SPI寄存器

所有SPI设置都是由Arduino SPI控制寄存器(SPCR)决定。该寄存器就是MCU内存的一个字节,可读写。寄存器提供服务:控制、数据、状态。

  1. 控制寄存器(SPCR):

    8位,(如单片机的中断允许控制寄存器IE、中断优先级控制寄存器IP、定时器/计数器控制寄存器)

    fFaWEn.png

  2. 数据寄存器(SPDR):

    (如串行口锁存器SBUF,仅hold住一个字节)

  3. 状态寄存器(SPSR):

    ​ 根据多种微控制器的条件改变其状态

10、Ticker —ESP8266定时库

1、概述

  • 用于规定时间后调用函数;

fAfIxg.png

  • 一个方法的示例(为了说明三个参数时的意义,两个参数时好理解)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 每隔xx毫秒周期性执行
* @param seconds 秒数
* @param callback 回调函数
*/
void attach_ms(float seconds, callback_function_t callback);

/**
* 每隔xx毫秒周期性执行
* @param seconds 秒数
* @param callback 回调函数
* @param arg 回调函数的参数
*/
void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)
  • 不建议使用Ticker回调函数来阻塞IO操作(网络、串口、文件);可以在Ticker回调函数中设置一个标记,在loop函数中检测这个标记;
  • 对于arg,必须时char, short, int, float, void* , char* 之一;

2、示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 代码功能:板载LED开始0.3秒闪,闪20次开始以0.1秒快闪,总共闪120次后最后常亮
*/
#include <Ticker.h>
Ticker flipper;
int count = 0;
void flip() {
int state = digitalRead(LED_BUILTIN); // get the current state of GPIO1 pin
digitalWrite(LED_BUILTIN, !state); // set pin to the opposite state
++count;
// 当翻转次数达到20次的时候,切换led的闪烁频率,每隔0.1s翻转一次
if (count == 20) {
flipper.attach(0.1, flip);
}
// 当次数达到120次的时候关闭ticker
else if (count == 120) {
flipper.detach();
}
}
void setup() {
//LED_BUILTIN 对应板载LED的IO口
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
//每隔0.3s 翻转一下led状态
flipper.attach(0.3, flip);
}
void loop() {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
*板载LED每隔25ms灭,每隔26ms亮
*/
#include <Ticker.h>
Ticker tickerSetHigh;
Ticker tickerSetLow;
void setPin(int state) {
digitalWrite(LED_BUILTIN, state);
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// 每隔25ms调用一次 setPin(0)
tickerSetLow.attach_ms(25, setPin, 0);
// 每隔26ms调用一次 setPin(1)
tickerSetHigh.attach_ms(26, setPin, 1);
}
void loop() {
}

11、Arduino Core For ESP8266

fECKd1.png

12、ESP8266工作模式与ESP8266WIFI库

1、ESP8266工作模式

  1. Station模式(STA):

    ESP8266模块连接WIFI网络(通过接入点(Access Point))

    特点

    • 连接丢失,ESP8266会自动重连最近使用的接入点(会出问题,连不上);
    • 模块重启也会这样;
    • ESP8266将最后使用的接入点认证信息(ssid , psw)保存到Flash(非易失性)存储器中;
    • 若在Arduino IDE修改代码,但不更改WIFI工作模式或接入点认证信息,ESP8266使用保存在Flash上的数据重新连接。
  2. AP模式(soft-AP):

    (Access Point) ESP8266作为接入点建立WIFI网络,供Station模式下的模块连接

    特点

    • AP模式可用作STA模式的模块之间交换中转站(让模块处于同一WIFI网络下);
    • 可先在AP模式下发出WIFI信号,手机连接,高速该模块家里的WIFI认证信息,模块转为STA模式,连接目标WIFI。
  3. AP兼STA模式:

    以上两种模式的整合(应该是做软路由那个意思)

2、ESP8266WIFI库

概述:

fEDZ5Q.png

相关知识

  1. 名字里带Secure、SSL、TLS的,和安全校验有关(https);

  2. 带Client,和tcp客户端(发送端)有关;

  3. 带Server,和tcp服务端(接收端)有关;

  4. 带8266,针对ESP8266的代码封装;

  5. 带Scan,和WIFI扫描有关;

  6. 带STA,和ESP8266 Station模式有关;

  7. 带AP,和ESP8266 AP模式(AP热点)有关;

  8. ESP8266WiFiGeneric(8266模块通用库)包括:处理程序来管理WIFI事件,如连接、断开或获得ip,WIFI模式的变化,管理模块睡眠模式的功能,以ip地址解析hostName等;

  9. ESP8266WiFiGType.h文件,主要用来定义各种配置选项,如WIFI工作模式(WiFiMode),WiFi睡眠模式(WiFiSleep Type),wifi物理模式(WiFiPhyMode),wifi事件(WiFiEvent),wifi断开原因等;

  10. ESP8266WiFi库不仅仅局限于ESP8266WIFi.h和ESP8266WiFi.cpp这两个文件,但他们是最核心的统一入口;

  11. WiFiUdp库,在ESP8266WiFi功能基础上包装了UDP广播协议,适用于UDP通信,需另加头文件

    引入#include<ESP8266WiFi.h>一步到位

详解

1、WIFI通用功能库(ESP8266WiFiGeneric):

fErOte.png

2、STA库(ESP8266WiFiSTA):

  1. 配置连接:

    注意

    1
    2
    3
    4
    5
    WiFi.mode(WIFI_STA);//最好人为加上STA设置(虽然默认是STA模式)
    WiFi.disconnect();//最好调用一下断连
    /*如果之前处于AP模式,再调begin()可能会进入STA+softAp模式
    *可检测当前模式(WiFi.getMode())
    */
    • 切换到STA模式(begin()

    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 切换工作模式到STA模式,并自动连接到最近接入的wifi热点
    * @param void
    * @return void
    * @note 调用这个方法就会切换到STA模式,并且连接到最近使用的接入点(会从flash中读取之前存储的配置信息)
    * 如果没有配置信息,那么这个方法基本上没有什么用。
    */
    wl_status_t begin()

    应用:

    1
    WiFi.begin();
    • 切换到STA模式(begin(ssid,psw)

    应用:

    1
    WiFi.begin(AP_SSID, AP_PSW);
    • 切换到STA模式(begin(ssid,psw,channel,bssid,connect)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 切换工作模式到STA模式,并根据connect属性来判断是否连接wifi
    * @param ssid wifi热点名字
    * @param password wifi热点密码
    * @param channel wifi热点的通道号,用特定通信通信,可选参数
    * @param bssid wifi热点的mac地址,可选参数
    * @param connect boolean参数,默认等于true,当设置为false,不会去连接wifi热点,会建立module保存上面参数
    * @return wl_status_t wifi状态
    * @note 调用这个方法就会切换到STA模式。
    * 如果connect等于true,会连接到ssid的wifi热点。
    * 如果connect等于false,不会连接到ssid的wifi热点,会建立module保存上面参数。
    */
    wl_status_t begin(char* ssid, char *passphrase = NULL, int32_t channel =0, const uint8_t* bssid = NULL, bool connect = true

    • 配置IP信息(config(local_ip,gateway,subnet,dns1,dns2)

    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 禁止DHCP client,设置station 模式下的IP配置
    * @param local_ip station固定的ip地址(连接快)
    * @param gateway 网关
    * @param subnet 子网掩码(前3个参数都设置为0.0.0.0,则重启DHCP)
    * @param dns1,dns2 可选参数定义域名服务器(dns)的ip地址,这些域名服务器
    * 维护一个域名目录(如www.google.co.uk),并将它们翻译成ip地址
    * @return boolean值,如果配置成功,返回true;
    * 如果配置没成功(模块没处于station或者station+soft AP模式),返回false;
    */
    bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000)
  2. 管理连接:

    • 重新连接网络(reconnect()

    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 断开连接并且重新连接station到同一个AP
    * @param void
    * @return false or true
    * 返回false,意味着ESP8266不处于STA模式或者说Station在此之前没有连接到一个可接入点。
    * 返回true,意味着已经成功重新启动连接,但是用户仍应该去检测网络连接状态指导WL_CONNECTED。
    */
    bool reconnect()

    应用:

    1
    2
    3
    4
    5
    6
    WiFi.reconnect();
    while (WiFi.status() != WL_CONNECTED)
    {
    delay(500);
    Serial.print(".");
    }
    • 断开网络连接(disconnect()

    源码:

    1
    2
    3
    4
    5
    6
    /**
    * 断开wifi连接,设置当前配置SSID和pwd为null
    * @param wifioff 可选参数,设置为true,那么就会关闭Station模式
    * @return false or true 返回wl_status_t状态
    */
    bool disconnect(bool wifioff = false);
    • 是否连接网络(isConnected()

    源码:

    1
    2
    3
    4
    5
    /**
    * 判断STA模式下是否连接上AP
    * @return 如果STA连接上AP,那么就返回true
    */
    bool isConnected();
    • 设置是否自动连接到最近接入点(setAutoConnect(bool autoReconnect)

    源码:

    1
    2
    3
    4
    5
    6
    /**
    * 当电源启动后,设置ESP8266在STA模式下是否自动连接flash中存储的AP
    * @param autoConnect bool 默认是自动连接
    * @return 返回保存状态 true or false
    */
    bool setAutoConnect(bool autoConnect);
    • 判断是否自动设置自动连接(getAutoConnect()

    源码:

    1
    2
    3
    4
    5
    /**
    * 检测ESP8266 station模式下是否启动自动连接
    * @return 返回自动连接状态 true or false
    */
    bool getAutoConnect();
    • 设置是否自动重连到最近接入点(setAutoReconnect(bool autoReconnect)

    源码:

    1
    2
    3
    4
    5
    6
    7
    /**
    * 设置当断开连接的时候是否自动重连
    * 网络断开后再设置无效
    * @param autoConnect bool
    * @return 返回保存状态 true or false
    */
    bool setAutoReconnect(bool autoReconnect);
    • 判断网络连接状态(waitForConnectResult()

    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 等待直到ESP8266连接AP返回结果
    * @return uint8_t 连接结果
    * 1.WL_CONNECTED 成功连接
    * 2.WL_NO_SSID_AVAIL 匹配SSID失败(账号错误)
    * 3.WL_CONNECT_FAILED psw错误
    * 4.WL_IDLE_STATUS 当wi-fi正在不同的状态中变化
    * 5.WL_DISCONNECTED 这个模块没有配置STA模式
    */
    uint8_t waitForConnectResult();
  3. station信息:

    • 获取mac地址
      • macAddress(macAddr)
      • macAddress()

    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 获取ESP station下的Mac地址
    * @param mac uint8_t数组的指针,数组长度为Mac地址的长度,这里为6
    * @return 返回uint8_t数组的指针
    */
    uint8_t * macAddress(uint8_t* mac);
    /**
    * 获取ESP station下的Mac地址
    * @return 返回String的Mac地址
    */
    String macAddress();

    应用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //实例代码1 这只是部分代码 不能直接使用
    if (WiFi.status() == WL_CONNECTED)
    {
    uint8_t macAddr[6];
    WiFi.macAddress(macAddr);
    Serial.printf("Connected, mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
    //Connected, mac address: 5C:CF:7F:08:11:17
    }

    //实例代码2 这只是部分代码 不能直接使用
    if (WiFi.status() == WL_CONNECTED)
    {
    Serial.printf("Connected, mac address: %s\n", WiFi.macAddress().c_str());
    Connected, mac address: 5C:CF:7F:08:11:17
    }
    • 获取ip地址(localIP()

    源码:

    1
    2
    3
    4
    5
    /**
    * 返回ESP8266 STA模式下的IP地址
    * @return IP地址
    */
    IPAddress localIP();

    应用:

    1
    2
    3
    4
    5
    6
    if (WiFi.status() == WL_CONNECTED)
    {
    Serial.print("Connected, IP address: ");
    Serial.println(WiFi.localIP());
    //Connected, IP address: 192.168.1.10
    }
    • 获取子网掩码(subnetMask()

    源码:

    1
    2
    3
    4
    5
    /**
    * 获取子网掩码的地址
    * @return 返回子网掩码的IP地址
    */
    IPAddress subnetMask();

    应用:

    1
    2
    3
    Serial.print("Subnet mask: ");
    Serial.println(WiFi.subnetMask());
    //Subnet mask: 255.255.255.0
    • 获取网关地址(getwayIP()

    源码:

    1
    2
    3
    4
    5
    /**
    * 获取网关IP地址
    * @return 返回网关IP地址
    */
    IPAddress gatewayIP();

    应用:

    1
    2
    3
    //实例代码 这只是部分代码 不能直接使用
    Serial.printf("Gataway IP: %s\n", WiFi.gatewayIP().toString().c_str());
    //Gataway IP: 192.168.1.9
    • 获取dns地址(dnsIP()

    源码:

    1
    2
    3
    4
    5
    6
    /**
    * 获取DNS ip地址
    * @param dns_no dns序列号
    * @return 返回DNS服务的IP地址
    */
    IPAddress dnsIP(uint8_t dns_no = 0);

    应用:

    1
    2
    3
    4
    5
    6
    Serial.print("DNS #1, #2 IP: ");
    WiFi.dnsIP().printTo(Serial);
    Serial.print(", ");
    WiFi.dnsIP(1).printTo(Serial);
    Serial.println();
    //DNS #1, #2 IP: 62.179.1.60, 62.179.1.61
    • 获取host名字(hostname()

    源码:

    1
    2
    3
    4
    5
    /**
    * 获取ESP8266 station DHCP的主机名
    * @return 主机名
    */
    String hostname();
    • 设置host名字(hostname(hostname))(3种)

    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 设置ESP8266 station DHCP的主机名
    * @param aHostname 最大长度:32
    * @return ok
    */
    bool hostname(char* aHostname);
    bool hostname(const char* aHostname);
    bool hostname(String aHostname);

    应用:

    1
    2
    3
    4
    5
    Serial.printf("Default hostname: %s\n", WiFi.hostname().c_str());
    WiFi.hostname("Station_Tester_02");
    Serial.printf("New hostname: %s\n", WiFi.hostname().c_str());
    //Default hostname: ESP_081117
    //New hostname: Station_Tester_02
    • 获取当前wifi连接状态(status()

    源码:

    1
    2
    3
    4
    5
    /**
    * 返回wifi的连接状态
    * @return 返回wl_status_t中定义的其中一值,wl_status_t在 wl_definitions.h中定义
    */
    wl_status_t status();
    • 获取wifi网络名字(SSID()

    源码:

    1
    2
    3
    4
    5
    /**
    * 返回当前通信网络的SSID
    * @return SSID
    */
    String SSID() const;

    应用:

    1
    2
    3
    //实例代码 这只是部分代码 不能直接使用
    Serial.printf("SSID: %s\n", WiFi.SSID().c_str());
    //SSID: sensor-net
    • 获取wifi网络密码(psk()

    源码:

    1
    2
    3
    4
    5
    /**
    * 返回当前通信网络的密码
    * @return psk
    */
    String psk() const;
    • 获取wifi网络macaddress(BSSID()

    源码:

    1
    2
    3
    4
    5
    6
    /**
    * 返回当前通信网络的mac地址
    * @return bssid uint8_t *
    */
    uint8_t * BSSID();
    String BSSIDstr();

    应用:

    1
    2
    Serial.printf("BSSID: %s\n", WiFi.BSSIDstr().c_str());
    //BSSID: 00:1A:70E:C1:68
    • 获取wifi网络的信号强度(RSSI()

    源码:

    1
    2
    3
    4
    5
    /**
    * Return the current network RSSI.返回当前通信网络的信号强度,单位是dBm
    * @return RSSI value
    */
    int32_t RSSI();

    应用:

    1
    2
    Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
    //RSSI: -68 dBm
  4. 智能配置:

    • 进入智能配置功能(beginSmartConfig()
    • 查询智能配置状态(smartConfigDone()
    • 停止智能配置功能(stopSmartConfig()
    • WiFi.beginWPSConfig()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    bool beginWPSConfig(void);
    /**
    * 启动 SmartConfig
    */
    bool beginSmartConfig();
    /**
    * 停止 SmartConfig
    */
    bool stopSmartConfig();
    /**
    * 查找SmartConfig状态来决定是否停止配置
    * @return smartConfig Done
    */
    bool smartConfigDone();

3、AP库(ESP8266WIFIAP)

  1. 配置soft-AP:
    • 启动开放式wifi网络(softAP(ssid)
    • 启动校验式wifi网络(softAP(ssid,password,channel,hidden)
    • 配置ap网络信息(softAPConfig(local_ip,gateway,subnet)
  2. 管理网络:
    • 获取连接到AP上的station的数目(softAPgetStationNum()
    • 关闭AP模式(softAPdisconnect(bool wifi off)
  3. 网络信息:
    • 获取ap的IP地址(softAPIP()
    • 获取ap的mac地址(softAPmacAddress()

4、WiFi扫描库(ESP8266WiFiScan):

一般使用异步扫描(不影响代码运行)

  1. 扫描操作:

    • 同步扫描周边有效wifi网络(scanNetworks()

      源码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      /**
      * Start scan WiFi networks available
      * @param async run in async mode(是否启动异步扫描)
      * @param show_hidden show hidden networks(是否扫描隐藏网络)
      * @param channel scan only this channel (0 for all channels)(是否扫描特定通道)
      * @param ssid* scan for only this ssid (NULL for all ssid's)(是否扫描特定的SSID)
      * @return Number of discovered networks
      */
      int8_t scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL);

      应用:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      //实例代码 这只是部分代码 不能直接使用
      //同步扫描
      int n = WiFi.scanNetworks();//不需要填任何参数
      Serial.println("scan done");
      if (n == 0) {
      Serial.println("no networks found");
      } else {
      Serial.println(" networks found");
      }
    • 异步扫描周边有效wifi网络,包括隐藏网络(scanNetworks(bool async,bool show_hiden)

      应用:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      //实例代码 这只是部分代码 不能直接使用
      //异步扫描
      WiFi.scanNetworks(true);
      // print out Wi-Fi network scan result uppon completion
      int n = WiFi.scanComplete();
      if(n >= 0){
      Serial.printf("%d network(s) found\n", n);
      for (int i = 0; i < n; i++){
      Serial.printf("%d: %s, Ch:%d (%ddBm) %s\n", i+1, WiFi.SSID(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), WiFi.encryptionType(i) == ENC_TYPE_NONE ? "open" : "");
      }
      //打印一次结果之后把缓存中的数据清掉
      WiFi.scanDelete();
      }
    • 检测异步扫描的结果(scanComplete()

      源码:

      1
      2
      3
      4
      5
      6
      7
      /**
      * called to get the scan state in Async mode(异步扫描的结果函数)
      * @return scan result or status
      * -1 if scan not find
      * -2 if scan not triggered
      */
      int8_t scanComplete();
    • 从内存中删除最近扫描结果(scanDelete()

      如果不删除,则会叠加上次扫描结果;

      1
      2
      3
      4
      /**
      * delete last scan result from RAM(从内存中删除最近的扫描结果)
      */
      void scanDelete();
    • 异步扫描周边wifi网络,并回调结果(scanNetworkAsync(onComplete,show_hidden)

      源码:

      1
      2
      3
      4
      5
      6
      /**
      * Starts scanning WiFi networks available in async mode
      * @param onComplete the event handler executed when the scan is done
      * @param show_hidden show hidden networks
      */
      void scanNetworksAsync(std::function<void(int)> onComplete, bool show_hidden = false);

      应用:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      //实例代码
      #include "ESP8266WiFi.h"
      void prinScanResult(int networksFound)
      {
      Serial.printf("%d network(s) found\n", networksFound);
      for (int i = 0; i < networksFound; i++)
      {
      Serial.printf("%d: %s, Ch:%d (%ddBm) %s\n", i + 1, WiFi.SSID(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), WiFi.encryptionType(i) == ENC_TYPE_NONE ? "open" : "");
      }
      }
      void setup()
      {
      Serial.begin(115200);
      Serial.println();

      WiFi.mode(WIFI_STA);
      WiFi.disconnect();
      delay(100);

      WiFi.scanNetworksAsync(prinScanResult);
      }
      void loop() {}
      //应该会打印如下类似的显示
      //5 network(s) found
      //1: Tech_D005107, Ch:6 (-72dBm)
      //2: HP-Print-A2-Photosmart 7520, Ch:6 (-79dBm)
      //3: ESP_0B09E3, Ch:9 (-89dBm) open
      //4: Hack-4-fun-net, Ch:9 (-91dBm)
      //5: UPC Wi-Free, Ch:11 (-79dBm)
  2. 扫描结果:

    • 获取wifi网络名字(SSID(int networkltem)

      1
      2
      3
      4
      5
      6
      /**
      * Return the SSID discovered during the network scan.
      * @param i specify from which network item want to get the information
      * @return ssid string of the specified item on the networks scanned list
      */
      String SSID(uint8_t networkItem);
    • 获取wifi网络信号强度(RSS(int networkltem)

      1
      2
      3
      4
      5
      6
      /**
      * Return the RSSI of the networks discovered during the scanNetworks(信号强度)
      * @param i specify from which network item want to get the information
      * @return signed value of RSSI of the specified item on the networks scanned list
      */
      int32_t RSSI(uint8_t networkItem);
    • 获取wifi网络加密方式(encryption Type(int networkltem)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      /**
      * Return the encryption type of the networks discovered during the scanNetworks(加密方式)
      * @param i specify from which network item want to get the information
      * @return encryption type (enum wl_enc_type) of the specified item on the networks scanned list
      * ............ Values map to 802.11 encryption suites.....................
      * AUTH_OPEN ----> ENC_TYPE_WEP = 5,
      * AUTH_WEP ----> ENC_TYPE_TKIP = 2,
      * AUTH_WPA_PSK ----> ENC_TYPE_CCMP = 4,
      * ........... except these two, 7 and 8 are reserved in 802.11-2007.......
      * AUTH_WPA2_PSK ----> ENC_TYPE_NONE = 7,
      * AUTH_WPA_WPA2_PSK ----> ENC_TYPE_AUTO = 8
      */
      uint8_t encryptionType(uint8_t networkItem);
    • 获取wifi网络mac地址(BSSID(int networkltem)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      /**
      * return MAC / BSSID of scanned wifi (物理地址)
      * @param i specify from which network item want to get the information
      * @return uint8_t * MAC / BSSID of scanned wifi
      */
      uint8_t * BSSID(uint8_t networkItem);

      /**
      * return MAC / BSSID of scanned wifi (物理地址)
      * @param i specify from which network item want to get the information
      * @return uint8_t * MAC / BSSID of scanned wifi
      */
      String BSSIDstr(uint8_t networkItem);
    • 获取整体网络信息,名字,信号强度等(getNetworkInfo

      入参前面多数加了&,意味着调完函数后外面获取到的详细洗信息;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      /**
      * loads all infos from a scanned wifi in to the ptr parameters
      * @param networkItem uint8_t
      * @param ssid const char**
      * @param encryptionType uint8_t *
      * @param RSSI int32_t *
      * @param BSSID uint8_t **
      * @param channel int32_t *
      * @param isHidden bool *
      * @return (true if ok)
      */
      bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel, bool &isHidden);
    • 获取wifi网络通道号

      1
      2
      3
      4
      /**
      * return channel of scanned wifi(通道号)
      */
      int32_t channel(uint8_t networkItem);
    • 判断wifi网络是否是隐藏网络

      1
      2
      3
      4
      5
      6
      /**
      * return if the scanned wifi is Hidden (no SSID)(判断扫描到的wifi是否是隐藏wifi)
      * @param networkItem specify from which network item want to get the information
      * @return bool (true == hidden)
      */
      bool isHidden(uint8_t networkItem);

5、TCP客户端(WiFiClient):

TCP与HTTP关系:

TCP是底层通讯协议,定义的是数据传输和连接方式的规范;

HTTP是应用层协议,定义的是传输数据的内容规范;

HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP就支持TCP。

  1. 连接操作:
    • 启动tcp连接(connect(host,port)
    • 判断client是否还在连接(connected()
    • 停止tcp连接(stop()
  2. 发送http请求操作:
    • print()
    • println()
    • write()
  3. 响应操作:
    • 判断是否有响应数据(available()
    • 查找响应数据某个字符串(find()
    • 读取响应数据直到某个字符串,会清除掉(readStringUntil
    • 读取响应数据中的一个字符,不清除(peek()
    • 读取固定大小的响应数据,不清除(peekBytes(buf,size)
    • 读取响应数据中的一个字符,清掉(read()
    • 读取固定大小的响应数据,清掉(read(buf,size)
    • 清掉缓冲区(flush()
  4. 普通设置:setNoDelay()

6、TCP客户端(WiFiClientSecure https):

7、TCP服务端(WiFiServer):

  1. 管理server:
    1. 设置tcp server:
      • 新增tcp server(WiFiServer server(port)
      • 启动tcp server(begin()
      • 关闭小包合并发送功能(setNoDelay(true)
    2. 停止server:
      • close()
      • stop()(内部实现直接调用close())
    3. server状态 : server.status()
  2. 等待WiFiClient访问:
    • 判断是否有新的client连接进来(available()
    • 判断是否有client连接(has Client()
  3. 读取WiFiClient的请求:参考WiFiClient里面的响应方法

8、TCP服务端(WiFiServerSecure https)

9、ESP8266WiFi库自带方法:

  • 调用调试信息(printDiag(serial)

总结:fedfFe.png](https://imgtu.com/i/fedfFe)

3、示例

1、AP模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* AP模式下,演示AP 函数方法的使用(最多连接4个)
*/
#include <ESP8266WiFi.h>

#define AP_SSID "AP_Test" //这里改成你的AP名字
#define AP_PSW "12345678" //这里改成你的AP密码 8位以上
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

IPAddress local_IP(192,168,4,1);
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);

void setup(){
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时2s 为了演示效果
delay(2000);
DebugPrint("Setting soft-AP configuration ... ");
//配置AP信息
WiFi.mode(WIFI_AP);
DebugPrintln(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Ready" : "Failed!");
//启动AP模式,并设置账号和密码
DebugPrint("Setting soft-AP ... ");
boolean result = WiFi.softAP(AP_SSID, AP_PSW);
if(result){
DebugPrintln("Ready");
//输出 soft-ap ip地址
DebugPrintln(String("Soft-AP IP address = ") + WiFi.softAPIP().toString());
//输出 soft-ap mac地址
DebugPrintln(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
}else{
DebugPrintln("Failed!");
}
DebugPrintln("Setup End");
}

void loop() {
//不断打印当前的station个数
DebugPrintln(String("Stations connected =") + WiFi.softAPgetStationNum());
delay(3000);
}

2、STA模式

1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Demo1:
* statin模式下,创建一个连接到可接入点(wifi热点),并且打印IP地址
*/
#include <ESP8266WiFi.h>

#define AP_SSID "XU-ChinaNet" //这里改成你的wifi名字
#define AP_PSW "15358228063"//这里改成你的wifi密码
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup(){
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时2s 为了演示效果
delay(2000);
DebugPrintln("Setup start");
//启动STA模式,并连接到wifi网络
WiFi.mode(WIFI_STA);//最好人为加上STA设置(虽然默认是STA模式)
WiFi.disconnect();//最好调用一下断连
WiFi.begin(AP_SSID, AP_PSW);
DebugPrint(String("Connecting to ")+AP_SSID);
//判断网络状态是否连接上,没连接上就延时500ms,并且打出一个点,模拟连接过程
//笔者扩展:加入网络一直都连不上 是否可以做个判断,由你们自己实现
while (WiFi.status() != WL_CONNECTED){
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected, IP address: ");
//输出station IP地址,这里的IP地址由DHCP分配
DebugPrintln(WiFi.localIP());
DebugPrintln("Setup End");
}
void loop() {
}

2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* Demo2:固定IP
* statin模式下,配置IP地址,网关地址,子网掩码,并且打印IP地址
*/
#include <ESP8266WiFi.h>

#define AP_SSID "XU-ChinaNet" //这里改成你的wifi名字
#define AP_PSW "15358228063"//这里改成你的wifi密码
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

IPAddress staticIP(192,168,1,22);//固定IP地址
IPAddress gateway(192,168,1,9);//网关地址
IPAddress subnet(255,255,255,0);//子网掩码地址

void setup(){
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时2s 为了演示效果
delay(2000);
DebugPrintln("Setup start");
//启动STA模式,并连接到wifi网络
WiFi.begin(AP_SSID, AP_PSW);
DebugPrint(String("Connecting to ")+AP_SSID);
//配置网络
WiFi.config(staticIP,gateway,subnet);
//判断网络状态是否连接上,没连接上就延时500ms,并且打出一个点,模拟连接过程
//笔者扩展:加入网络一直都连不上 是否可以做个判断,由你们自己实现
while (WiFi.status() != WL_CONNECTED){
delay(500);
DebugPrint(".");
}
DebugPrintln("");

DebugPrint("Connected, IP address: ");
//输出station IP地址,这里的IP地址理论上就是上面配置的
DebugPrintln(WiFi.localIP());
DebugPrintln("Setup End");
}

void loop() {
}

3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* Demo3:
* statin模式下,创建一个连接到可接入点(wifi热点),并且打印station信息
*/
#include <ESP8266WiFi.h>

#define AP_SSID "XU-ChinaNet" //这里改成你的wifi名字
#define AP_PSW "15358228063"//这里改成你的wifi密码
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup(){
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时2s 为了演示效果
delay(2000);
DebugPrintln("Setup start");
//启动STA模式,并连接到wifi网络
WiFi.begin(AP_SSID, AP_PSW);
//设置自动连接
WiFi.setAutoConnect(true);
//设置自动重连
WiFi.setAutoReconnect(true);
DebugPrint(String("Connecting to ")+AP_SSID);
//判断网络状态是否连接上,没连接上就延时500ms,并且打出一个点,模拟连接过程
//笔者扩展:加入网络一直都连不上 是否可以做个判断,由你们自己实现
while (WiFi.status() != WL_CONNECTED){
delay(500);
DebugPrint(".");
}
DebugPrintln("");

DebugPrintln("rint Network Info:");
if (WiFi.status() == WL_CONNECTED){
//输出mac地址
DebugPrintln(String("Connected, mac address: ")+WiFi.macAddress().c_str());

//输出station IP地址,这里的IP地址由DHCP分配
DebugPrintln(String("Connected, IP address: ")+WiFi.localIP().toString());

//输出子网掩码地址
DebugPrintln(String("Subnet mask: ")+WiFi.subnetMask().toString());

//输出网关 IP地址
DebugPrintln(String("Gataway IP: ")+WiFi.gatewayIP().toString());

//输出hostname
DebugPrintln(String("Default hostname: ")+WiFi.hostname());
//设置新的hostname
WiFi.hostname("Station_host_123");
DebugPrintln(String("New hostname: ")+WiFi.hostname());

//输出SSID
DebugPrintln(String("SSID: ")+WiFi.SSID());

//输出psk
DebugPrintln(String("psk: ")+WiFi.psk());

//输出BSSID
DebugPrintln(String("BSSID: ")+WiFi.BSSIDstr());

//输出RSSI
DebugPrintln(String("RSSI: ") + WiFi.RSSI() + " dBm");
}

DebugPrintln("Setup End");
}

void loop() {
}

3、扫描:

1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* Demo:
* STA模式下,演示同步扫描Scan wifi功能
*/
#include <ESP8266WiFi.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup() {
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时5s 为了演示效果
delay(5000);
// 我不想别人连接我,只想做个站点
WiFi.mode(WIFI_STA);
//断开连接
WiFi.disconnect();
delay(100);
DebugPrintln("Setup done");
}

void loop() {
DebugPrintln("scan start");
// 同步扫描,等待返回结果
int n = WiFi.scanNetworks();
DebugPrintln("scan done");
if (n == 0){
DebugPrintln("no networks found");
}else{
DebugPrint(n);
DebugPrintln(" networks found");
for (int i = 0; i < n; ++i){
DebugPrint(i + 1);
DebugPrint(": ");
//打印wifi账号
DebugPrint(WiFi.SSID(i));
DebugPrint(",");
DebugPrint(String("Ch:")+WiFi.channel(i));
DebugPrint(",");
DebugPrint(WiFi.isHidden(i)?"hide":"show");
DebugPrint(" (");
//打印wifi信号强度
DebugPrint(WiFi.RSSI(i));
DebugPrint("dBm");
DebugPrint(")");
//打印wifi加密方式
DebugPrintln((WiFi.encryptionType(i) == ENC_TYPE_NONE)?"open":"*");
delay(10);
}
}
DebugPrintln("");
// 延时5s之后再次扫描
delay(5000);
}

2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* Demo:
* STA模式下,演示异步扫描Scan wifi功能
*/
#include <ESP8266WiFi.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
//定义一个扫描时间间隔
#define SCAN_PERIOD 5000
long lastScanMillis;

void setup() {
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时5s 为了演示效果
delay(5000);
// 我不想别人连接我,只想做个站点
WiFi.mode(WIFI_STA);
//断开连接
WiFi.disconnect();
delay(100);
DebugPrintln("Setup done");
}

void loop() {

long currentMillis = millis();
//触发扫描
if (currentMillis - lastScanMillis > SCAN_PERIOD){
WiFi.scanNetworks(true);
Serial.print("\nScan start ... ");
lastScanMillis = currentMillis;
}

// 判断是否有扫描结果
int n = WiFi.scanComplete();
if(n >= 0){
Serial.printf("%d network(s) found\n", n);
for (int i = 0; i < n; i++){
Serial.printf("%d: %s, Ch:%d (%ddBm) %s\n", i+1, WiFi.SSID(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), WiFi.encryptionType(i) == ENC_TYPE_NONE ? "open" : "");
}
//打印完一次扫描结果之后 删除内存保存结果
WiFi.scanDelete();
}
}

3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* Demo:
* STA模式下,演示异步扫描Scan wifi功能
*/
#include <ESP8266WiFi.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

/**
* 打印扫描结果
* @param networksFound 结果个数
*/
void prinScanResult(int networksFound){
Serial.printf("%d network(s) found\n", networksFound);
for (int i = 0; i < networksFound; i++)
{
Serial.printf("%d: %s, Ch:%d (%ddBm) %s\n", i + 1, WiFi.SSID(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), WiFi.encryptionType(i) == ENC_TYPE_NONE ? "open" : "");
}
}

void setup() {
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时5s 为了演示效果
delay(5000);
// 我不想别人连接我,只想做个站点
WiFi.mode(WIFI_STA);
//断开连接
WiFi.disconnect();
delay(100);
DebugPrintln("Setup done");
Serial.print("\nScan start ... ");
WiFi.scanNetworksAsync(prinScanResult);
}

void loop() {
}

4、通用库处理事件:

  1. 官方示例:(AP模式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
This sketch shows how to use WiFi event handlers.

In this example, ESP8266 works in AP mode.
Three event handlers are demonstrated:
- station connects to the ESP8266 AP
- station disconnects from the ESP8266 AP
- ESP8266 AP receives a probe request from a station
*/

#include <ESP8266WiFi.h>
#include <stdio.h>

const char* ssid = "ap-ssid";
const char* password = "ap-password";

WiFiEventHandler stationConnectedHandler;
WiFiEventHandler stationDisconnectedHandler;
WiFiEventHandler probeRequestPrintHandler;
WiFiEventHandler probeRequestBlinkHandler;

bool blinkFlag;

void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);

// 不保存任何wifi配置到flash
WiFi.persistent(false);

// 建立一个AP
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);

// 注册事件处理器
// 回调函数会在事件发生时被调用
// onStationConnected函数会在每一次有station连接时调用
stationConnectedHandler = WiFi.onSoftAPModeStationConnected(&onStationConnected);
// onStationDisconnected函数会在每一次有station断开时调用
stationDisconnectedHandler = WiFi.onSoftAPModeStationDisconnected(&onStationDisconnected);
// onProbeRequestPrint和onProbeRequestBlink函数会在每一次收到探针请求时调用
// onProbeRequestPrint会打印station的mac地址和信号强度到串口监视器
// onProbeRequestBlink会闪烁LED
probeRequestPrintHandler = WiFi.onSoftAPModeProbeRequestReceived(&onProbeRequestPrint);
probeRequestBlinkHandler = WiFi.onSoftAPModeProbeRequestReceived(&onProbeRequestBlink);
}

void onStationConnected(const WiFiEventSoftAPModeStationConnected& evt) {
Serial.print("Station connected: ");
Serial.println(macToString(evt.mac));
}

void onStationDisconnected(const WiFiEventSoftAPModeStationDisconnected& evt) {
Serial.print("Station disconnected: ");
Serial.println(macToString(evt.mac));
}

void onProbeRequestPrint(const WiFiEventSoftAPModeProbeRequestReceived& evt) {
Serial.print("Probe request from: ");
Serial.print(macToString(evt.mac));
Serial.print(" RSSI: ");
Serial.println(evt.rssi);
}

void onProbeRequestBlink(const WiFiEventSoftAPModeProbeRequestReceived&) {
// 我们不能在事件处理函数中调用延时函数或者其他阻塞函数
// 因此这里设置一个标志位
blinkFlag = true;
}

void loop() {
if (millis() > 10000 && probeRequestPrintHandler) {
// 10s之后,禁止 onProbeRequestPrint
Serial.println("Not printing probe requests any more (LED should still blink)");
probeRequestPrintHandler = WiFiEventHandler();
}
if (blinkFlag) {
blinkFlag = false;
digitalWrite(LED_BUILTIN, LOW);
delay(100);
digitalWrite(LED_BUILTIN, HIGH);
}
delay(10);
}

String macToString(const unsigned char* mac) {
char buf[20];
snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(buf);
}
  1. station模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ESP8266WiFi.h>

const char *ssid = "********";
const char *password = "********";

WiFiEventHandler STAConnected;
WiFiEventHandler STADisconnected;
WiFiEventHandler STAGotIP;

void ConnectedHandler(const WiFiEventStationModeConnected &event)
{
Serial.println(WiFi.status());
Serial.println("模块连接到网络");
}

void DisconnectedHandler(const WiFiEventStationModeDisconnected &event)
{
Serial.println(WiFi.status());
Serial.println("模块从网络断开");
}

void setup()
{
Serial.begin(115200);
Serial.println();

STAConnected = WiFi.onStationModeConnected(ConnectedHandler);
STADisconnected = WiFi.onStationModeDisconnected(DisconnectedHandler);
STAGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) {
Serial.println(WiFi.status());
Serial.println("模块获得IP");
});

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println(WiFi.status());
}

void loop()
{
delay(5000); //等待5秒
WiFi.disconnect(); //断开当前网络连接
}

5、TCP客户端(client)

  1. 演视WiFiClient与TCP server之间通信功能(使用TCP调试助手(TCP/UDP Socket调试工具))在TCP调试助手上建立一个TCP server, IP地址是192.168.1.102,端口号是8234。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* Demo:
* STA模式下,演示WiFiClient与TCP server之间的通信功能
* 本实验需要跟TCP调试助手一起使用。
*/
#include <ESP8266WiFi.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

#define AP_SSID "XU-ChinaNet" //这里改成你的wifi名字
#define AP_PSW "15358228063"//这里改成你的wifi密码

const uint16_t port = 8234;
const char * host = "192.168.1.8"; // ip or dns
WiFiClient client;//创建一个tcp client连接

void setup() {
//设置串口波特率,以便打印信息
DebugBegin(115200);
//延时5s 为了演示效果
delay(5000);
// 我不想别人连接我,只想做个站点
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID,AP_PSW);

DebugPrint("Wait for WiFi... ");
//等待wifi连接成功
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}

DebugPrintln("");
DebugPrintln("WiFi connected");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());

delay(500);
}

void loop() {

DebugPrint("connecting to ");
DebugPrintln(host);

if (!client.connect(host, port)) {
DebugPrintln("connection failed");
DebugPrintln("wait 5 sec...");
delay(5000);
return;
}

// 发送数据到Tcp server
DebugPrintln("Send this data to server");
client.println(String("Send this data to server"));

//读取从server返回到响应数据
String line = client.readStringUntil('\r');
DebugPrintln(line);

DebugPrintln("closing connection");
client.stop();

DebugPrintln("wait 5 sec...");
delay(5000);
}

  1. 通过TCP client包装Http请求协议去调用天气接口获取天气信息

    (使用ArduinoJson库,尽量使用5.X版本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/**
* Demo:
* 演示Http请求天气接口信息
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* ssid = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* password = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* host = "api.seniverse.com";
const char* APIKEY = "wcmquevztdy1jpca"; //API KEY
const char* city = "guangzhou";
const char* language = "zh-Hans";//zh-Hans 简体中文 会显示乱码

const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server
const size_t MAX_CONTENT_SIZE = 1000; // max size of the HTTP response

// 我们要从此网页中提取的数据的类型
struct WeatherData {
char city[16];//城市名称
char weather[32];//天气介绍(多云...)
char temp[16];//温度
char udate[32];//更新时间
};

WiFiClient client;
char response[MAX_CONTENT_SIZE];
char endOfHeaders[] = "\r\n\r\n";

void setup() {
// put your setup code here, to run once:
WiFi.mode(WIFI_STA); //设置esp8266 工作模式
DebugBegin(BAUD_RATE);
DebugPrint("Connecting to ");//写几句提示,哈哈
DebugPrintln(ssid);
WiFi.begin(ssid, password); //连接wifi
WiFi.setAutoConnect(true);
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");
delay(500);
DebugPrintln("IP address: ");
DebugPrintln(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
client.setTimeout(HTTP_TIMEOUT);
}

void loop() {
// put your main code here, to run repeatedly:
//判断tcp client是否处于连接状态,不是就建立连接
while (!client.connected()){
if (!client.connect(host, 80)){
DebugPrintln("connection....");
delay(500);
}
}
//发送http请求 并且跳过响应头 直接获取响应body
if (sendRequest(host, city, APIKEY) && skipResponseHeaders()) {
//清除缓冲
clrEsp8266ResponseBuffer();
//读取响应数据
readReponseContent(response, sizeof(response));
WeatherData weatherData;
if (parseUserData(response, &weatherData)) {
printUserData(&weatherData);
}
}
delay(5000);//每5s调用一次
}

/**
* @发送http请求指令
*/
bool sendRequest(const char* host, const char* cityid, const char* apiKey) {
// We now create a URI for the request
//心知天气 发送http请求
String GetUrl = "/v3/weather/now.json?key=";
GetUrl += apiKey;
GetUrl += "&location=";
GetUrl += city;
GetUrl += "&language=";
GetUrl += language;
// This will send the request to the server
client.print(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
DebugPrintln("create a request:");
DebugPrintln(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n");
delay(1000);
return true;
}

/**
* @Desc 跳过 HTTP 头,使我们在响应正文的开头
*/
bool skipResponseHeaders() {
// HTTP headers end with an empty line
bool ok = client.find(endOfHeaders);
if (!ok) {
DebugPrintln("No response or invalid response!");
}
return ok;
}

/**
* @Desc 从HTTP服务器响应中读取正文
*/
void readReponseContent(char* content, size_t maxSize) {
size_t length = client.readBytes(content, maxSize);
delay(100);
DebugPrintln("Get the data from Internet!");
content[length] = 0;
DebugPrintln(content);
DebugPrintln("Read data Over!");
client.flush();//清除一下缓冲
}

/**
* @Desc 解析数据 Json解析
* 数据格式如下:
* {
* "results": [
* {
* "location": {
* "id": "WX4FBXXFKE4F",
* "name": "北京",
* "country": "CN",
* "path": "北京,北京,中国",
* "timezone": "Asia/Shanghai",
* "timezone_offset": "+08:00"
* },
* "now": {
* "text": "多云",
* "code": "4",
* "temperature": "23"
* },
* "last_update": "2017-09-13T09:51:00+08:00"
* }
* ]
*}
*/
bool parseUserData(char* content, struct WeatherData* weatherData) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;

JsonObject& root = jsonBuffer.parseObject(content);

if (!root.success()) {
DebugPrintln("JSON parsing failed!");
return false;
}

//复制我们感兴趣的字符串
strcpy(weatherData->city, root["results"][0]["location"]["name"]);
strcpy(weatherData->weather, root["results"][0]["now"]["text"]);
strcpy(weatherData->temp, root["results"][0]["now"]["temperature"]);
strcpy(weatherData->udate, root["results"][0]["last_update"]);
// -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
// 当你读取字符串时它仍在内存中
return true;
}

// 打印从JSON中提取的数据
void printUserData(const struct WeatherData* weatherData) {
DebugPrintln("Print parsed data :");
DebugPrint("City : ");
DebugPrint(weatherData->city);
DebugPrint(", \t");
DebugPrint("Weather : ");
DebugPrint(weatherData->weather);
DebugPrint(",\t");
DebugPrint("Temp : ");
DebugPrint(weatherData->temp);
DebugPrint(" C");
DebugPrint(",\t");
DebugPrint("Last Updata : ");
DebugPrint(weatherData->udate);
DebugPrintln("\r\n");
}

// 关闭与HTTP服务器连接
void stopConnect() {
DebugPrintln("Disconnect");
client.stop();
}

void clrEsp8266ResponseBuffer(void){
memset(response, 0, MAX_CONTENT_SIZE); //清空
}

6、TCP服务端(server)

  1. 8266作为WiFiServer端,打开TCP调试助手,模拟TCP Client的请求;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* Demo:
* 演示WiFiServer功能
* 打开TCP调试助手 模拟TCP client请求
*/
#include <ESP8266WiFi.h>

//定义最多多少个client可以连接本server(一般不要超过4个)
#define MAX_SRV_CLIENTS 1
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* ssid = "TP-LINK_5344";
const char* password = "6206908you11011010";

//创建server 端口号是23
WiFiServer server(23);
//管理clients
WiFiClient serverClients[MAX_SRV_CLIENTS];

void setup() {
DebugBegin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
DebugPrint("\nConnecting to ");
DebugPrintln(ssid);
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) {
delay(500);
}
if (i == 21) {
DebugPrint("Could not connect to");
DebugPrintln(ssid);
while (1) {
delay(500);
}
}
//启动server
server.begin();
//关闭小包合并包功能,不会延时发送数据
server.setNoDelay(true);

DebugPrint("Ready! Use 'telnet ");
DebugPrint(WiFi.localIP());
DebugPrintln(" 23' to connect");
}

void loop() {
uint8_t i;
//检测是否有新的client请求进来
if (server.hasClient()) {
for (i = 0; i < MAX_SRV_CLIENTS; i++) {
//释放旧无效或者断开的client
if (!serverClients[i] || !serverClients[i].connected()) {
if (serverClients[i]) {
serverClients[i].stop();
}
//分配最新的client
serverClients[i] = server.available();
DebugPrint("New client: ");
DebugPrint(i);
break;
}
}
//当达到最大连接数 无法释放无效的client,需要拒绝连接
if (i == MAX_SRV_CLIENTS) {
WiFiClient serverClient = server.available();
serverClient.stop();
DebugPrintln("Connection rejected ");
}
}
//检测client发过来的数据
for (i = 0; i < MAX_SRV_CLIENTS; i++) {
if (serverClients[i] && serverClients[i].connected()) {
if (serverClients[i].available()) {
//get data from the telnet client and push it to the UART
while (serverClients[i].available()) {
//发送到串口调试器
Serial.write(serverClients[i].read());
}
}
}
}

if (Serial.available()) {
//把串口调试器发过来的数据 发送给client
size_t len = Serial.available();
uint8_t sbuf[len];
Serial.readBytes(sbuf, len);
//push UART data to all connected telnet clients
for (i = 0; i < MAX_SRV_CLIENTS; i++) {
if (serverClients[i] && serverClients[i].connected()) {
serverClients[i].write(sbuf, len);
delay(1);
}
}
}
}
  1. 8266作为web server端,打开PC浏览器输入IP网址,请求web server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* Demo:
* 演示web Server功能
* 打开PC浏览器 输入IP地址。请求web server
*/
#include <ESP8266WiFi.h>

const char* ssid = "TP-LINK_5344";//wifi账号 这里需要修改
const char* password = "xxxx";//wifi密码 这里需要修改

//创建 tcp server 端口号是80
WiFiServer server(80);

void setup(){
Serial.begin(115200);
Serial.println();

Serial.printf("Connecting to %s ", ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println(" connected");
//启动TCP 连接
server.begin();
//打印TCP server IP地址
Serial.printf("Web server started, open %s in a web browser\n", WiFi.localIP().toString().c_str());
}

/**
* 模拟web server 返回http web响应内容
* 这里是手动拼接HTTP响应内容
* 后面楼主会继续讲解另外两个专用于http请求的库
*/
String prepareHtmlPage(){
String htmlPage =
String("HTTP/1.1 200 OK\r\n") +
"Content-Type: text/html\r\n" +
"Connection: close\r\n" + // the connection will be closed after completion of the response
"Refresh: 5\r\n" + // refresh the page automatically every 5 sec
"\r\n" +
"<!DOCTYPE HTML>" +
"<html>" +
"Analog input: " + String(analogRead(A0)) +
"</html>" +
"\r\n";
return htmlPage;
}


void loop(){
WiFiClient client = server.available();
// wait for a client (web browser) to connect
if (client){
Serial.println("\n[Client connected]");
while (client.connected()){
// 不断读取请求内容
if (client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
// wait for end of client's request, that is marked with an empty line
if (line.length() == 1 && line[0] == '\n'){
//返回响应内容
client.println(prepareHtmlPage());
break;
}
}
//由于我们设置了 Connection: close 当我们响应数据之后就会自动断开连接
}
delay(100); // give the web browser time to receive the data

// close the connection:
client.stop();
Serial.println("[Client disonnected]");
}
}

  1. 8266作为WiFiServer端,演示简单的web server功能,web server 会根据请求来做不同的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
* Demo:
* 演示简单web Server功能
* web server会根据请求来做不同的操作
* http://server_ip/gpio/0 打印 /gpio0
* http://server_ip/gpio/1 打印 /gpio1
* server_ip就是ESP8266的Ip地址
*/

#include <ESP8266WiFi.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* ssid = "TP-LINK_5344";//wifi账号 这里需要修改
const char* password = "xxxx";//wifi密码 这里需要修改

// 创建tcp server
WiFiServer server(80);

void setup() {
DebugBegin(115200);
delay(10);

// Connect to WiFi network
DebugPrintln("");
DebugPrintln(String("Connecting to ") + ssid);
//我只想做个安静的美男子 STA
WiFi.mode(WIFI_STA);
//我想连接路由wifi
WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");

// 启动server
server.begin();
DebugPrintln("Server started");

// 打印IP地址
DebugPrintln(WiFi.localIP().toString());
}

void loop() {
// 等待有效的tcp连接
WiFiClient client = server.available();
if (!client) {
return;
}

DebugPrintln("new client");
//等待client数据过来
while (!client.available()) {
delay(1);
}

// 读取请求的第一行 会包括一个url,这里只处理url
String req = client.readStringUntil('\r');
DebugPrintln(req);
//清掉缓冲区数据 据说这个方法没什么用 可以换种实现方式
client.flush();

// 开始匹配
int val;
if (req.indexOf("/gpio/0") != -1) {
DebugPrintln("/gpio0");
val = 0;
} else if (req.indexOf("/gpio/1") != -1) {
DebugPrintln("/gpio1");
val = 1;
} else {
DebugPrintln("invalid request");
//关闭这个client请求
client.stop();
return;
}
//清掉缓冲区数据
client.flush();

// 准备响应数据
String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nGPIO is now ";
s += (val) ? "high" : "low";
s += "</html>\n";

// 发送响应数据给client
client.print(s);
delay(1);
DebugPrintln("Client disonnected");

// The client will actually be disconnected
// when the function returns and 'client' object is detroyed
}

7、Smartconfig智能配网:

收集APP端发送包含WIFI用户名和密码的UDP广播包,智能终端的WIFI芯片接收到UDP包,解密配置连接。

手机软件esptouch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <ESP8266WiFi.h>

void smartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig");
delay(2000);
// 等待配网
WiFi.beginSmartConfig();

while (1)
{
Serial.print(".");
delay(500);
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
}

Serial.println("");
Serial.println("WiFi connected");

}

void setup()
{
Serial.begin(115200);
smartConfig();
}

void loop()
{
delay(1000);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}

13、HttpClient–ESP8266HTTPClient库

1、HTTP

1、概述

HTTP协议(Hyper Text Transfer Protocol)超文本传输协议,用于从WWW服务器传输文本到本地浏览器的传送协议。基于TCP/IP通信协议:浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。WEB服务器根据接收到的请求后,向客户端发送响应信息;

HTTP协议作为TCP/IP模型中应用层的协议,承载与TCP协议上,有时也承载与TLS或者SSL协议层之上,这时就是HTTPS

HTTP由请求响应构成,是一个标准的客户端服务器模型,默认端口号80,HTTPS是443;

浏览网页时HTTP主要应用(不只是这个功能,只是一种协议,只要通信双方都支持这个协议,HTTP就能用)

特点:HTTP0.9与1.0使用非持续连接;1.1使用持续连接。

无状态,无记忆能力

2、工作流程

一次HTTP操作称位一个事务,可分4步:

  1. client与server建立连接;

  2. 连接后,client发送一个请求给server,请求方法格式:统一资源标示符(URL)、HTTP协议版本号、请求头、请求内容等;

  3. server接收到请求后,给予响应,格式:状态行(包括协议版本、成功或者失败代码、服务器信息、实体信息等)

  4. client接收到server返回信息,通过浏览器显示在用户显示屏上,然后client与server断开连接

3、HTTP请求

Get请求:

  • 请求行(request line):说明请求类型,要访问的资源及HTTP版本
  • 请求头部(header):说明服务器要使用的附加信息(第二行起位请求头部,HOST指出目的地;User-Agent是client与server脚本都能访问,是浏览器检测逻辑的重要基础)(该信息由浏览器定义,并在每个请求中自动发送)
  • 空行(empty line):请求头部后空行是必须的(即使第四部分请求数据为空,也必须有空行)
  • 请求数据(request body):主体,可添加任意其他数据(下面例子中请求数据为空)

请求例子(Gharles抓取的request)

1
2
3
4
5
6
7
GET /562f25980001b1b106000338.jpg HTTP/1.1
Host img.mukewang.com
User-Agent Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Accept image/webp,image/*,*/*;q=0.8
Referer http://www.imooc.com/
Accept-Encoding gzip, deflate, sdch
Accept-Language zh-CN,zh;q=0.8

POST请求:

请求例子(Gharles抓取的request)

1
2
3
4
5
6
7
8
POST / HTTP1.1
Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive

name=Professional%20Ajax&publisher=Wiley

4、HTTP Response响应信息

  • 状态行
  • 消息报头
  • 空行
  • 响应正文

huf9qH.jpg

HTTP状态码

5、ESP8266HTTPClient库

f3MpdO.png

6、示例

  1. 获取天气请求(心知天气)

setup中配置好url,串口参数和httpclient并设置client请求头。loop中每1秒请求一次get服务,把获取回来的天气信息通过json库转成具体对应的数值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* Demo:
* 演示Http请求天气接口信息
* https://api.seniverse.com/v3/weather/now.json?key=Sv6J6HVHp8V2Cc34a&location=beijing&language=zh-Hans&unit=c
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h> //不属于ESP8266WiFi库的一部分

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* HOST = "http://api.seniverse.com";
const char* APIKEY = ""; //API KEY
const char* CITY = "yancheng";
const char* LANGUAGE = "zh-Hans";//zh-Hans 简体中文 会显示乱码

const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server

// 我们要从此网页中提取的数据的类型
struct WeatherData {
char city[16];//城市名称
char weather[32];//天气介绍(多云...)
char temp[16];//温度
char udate[32];//更新时间
};
WiFiClient client;
HTTPClient http;
String GetUrl;
String response;
WeatherData weatherData;

void setup() {
// put your setup code here, to run once:
WiFi.mode(WIFI_STA); //设置esp8266 工作模式
DebugBegin(BAUD_RATE);
DebugPrint("Connecting to ");//写几句提示,哈哈
DebugPrintln(AP_SSID);
WiFi.begin(AP_SSID, AP_PSK); //连接wifi
WiFi.setAutoConnect(true);
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());

//拼接get请求url 博哥后面考虑看看是否可以封装一个方法来用用 不需要自己一个个拼装这个url
GetUrl = String(HOST) + "/v3/weather/now.json?key=";
GetUrl += APIKEY;
GetUrl += "&location=";
GetUrl += CITY;
GetUrl += "&language=";
GetUrl += LANGUAGE;
//设置超时
http.setTimeout(HTTP_TIMEOUT);
//设置请求url
//http.begin(GetUrl);
http.begin(client,GetUrl);
//以下为设置一些头 其实没什么用 最重要是后端服务器支持
http.setUserAgent("esp8266");//用户代理版本
http.setAuthorization("esp8266","boge");//用户校验信息
}

void loop() {
//心知天气 发送http get请求
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
//判断请求是否成功
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
response = http.getString();
DebugPrintln("Get the data from Internet!");
DebugPrintln(response);
//解析响应内容
if (parseUserData(response, &weatherData)) {
//打印响应内容
printUserData(&weatherData);
}
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
delay(1000);//每1s调用一次
}

/**
* @Desc 解析数据 Json解析
* 数据格式如下:
* {
* "results": [
* {
* "location": {
* "id": "WX4FBXXFKE4F",
* "name": "北京",
* "country": "CN",
* "path": "北京,北京,中国",
* "timezone": "Asia/Shanghai",
* "timezone_offset": "+08:00"
* },
* "now": {
* "text": "多云",
* "code": "4",
* "temperature": "23"
* },
* "last_update": "2017-09-13T09:51:00+08:00"
* }
* ]
*}
*/
bool parseUserData(String content, struct WeatherData* weatherData) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;

JsonObject& root = jsonBuffer.parseObject(content);

if (!root.success()) {
DebugPrintln("JSON parsing failed!");
return false;
}

//复制我们感兴趣的字符串
strcpy(weatherData->city, root["results"][0]["location"]["name"]);
strcpy(weatherData->weather, root["results"][0]["now"]["text"]);
strcpy(weatherData->temp, root["results"][0]["now"]["temperature"]);
strcpy(weatherData->udate, root["results"][0]["last_update"]);
// -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
// 当你读取字符串时它仍在内存中
return true;
}

// 打印从JSON中提取的数据
void printUserData(const struct WeatherData* weatherData) {
DebugPrintln("Print parsed data :");
DebugPrint("City : ");
DebugPrint(weatherData->city);
DebugPrint(", \t");
DebugPrint("Weather : ");
DebugPrint(weatherData->weather);
DebugPrint(",\t");
DebugPrint("Temp : ");
DebugPrint(weatherData->temp);
DebugPrint(" C");
DebugPrint(",\t");
DebugPrint("Last Updata : ");
DebugPrint(weatherData->udate);
DebugPrintln("\r\n");
}
  1. 演视响应头获取信息

    设置了获取四个请求头的信息,然后通过header方法获取它们的数值(接口限制,无法发挥作用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* Demo:
* 演示Http请求天气接口信息,演示响应头操作
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* HOST = "http://api.seniverse.com";
const char* APIKEY = ""; //API KEY
const char* CITY = "yancheng";
const char* LANGUAGE = "zh-Hans";//zh-Hans 简体中文 会显示乱码
const char *keys[] = {"Content-Length","Content-Type","Connection","Date"};//需要收集的响应头的信息

const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server;

WiFiClient client;
HTTPClient http;
String GetUrl;
String response;

void setup() {
// put your setup code here, to run once:
WiFi.mode(WIFI_STA); //设置esp8266 工作模式
DebugBegin(BAUD_RATE);
DebugPrint("Connecting to ");//写几句提示,哈哈
DebugPrintln(AP_SSID);
WiFi.begin(AP_SSID, AP_PSK); //连接wifi
WiFi.setAutoConnect(true);
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
//拼接get请求url 博哥后面考虑看看是否可以封装一个方法来用用 不需要自己一个个拼装这个url
GetUrl = String(HOST) + "/v3/weather/now.json?key=";
GetUrl += APIKEY;
GetUrl += "&location=";
GetUrl += CITY;
GetUrl += "&language=";
GetUrl += LANGUAGE;
//设置超时
http.setTimeout(HTTP_TIMEOUT);
//设置请求url
http.begin(client,GetUrl);
//以下为设置一些头 其实没什么用 最重要是后端服务器支持
http.setUserAgent("esp8266");//用户代理版本
http.setAuthorization("esp8266","boge");//用户校验信息
http.addHeader("myname","cainiaobo");

//设置获取响应头的信息
http.collectHeaders(keys,4);
}

void loop() {
//心知天气 发送http get请求
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
//判断请求是否成功
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
response = http.getString();
DebugPrintln("Get the data from Internet!");
DebugPrintln(response);
DebugPrintln(String("Content-Length:")+ http.header("Content-Length"));
DebugPrintln(String("Content-Type:")+ http.header("Content-Type"));
DebugPrintln(String("Connection:")+ http.header("Connection"));
DebugPrintln(String("Date:")+ http.header("Date"));
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
delay(1000);//每1s调用一次
}

14、UDP服务

1、UDP

UDP(User Datagram Protocol)一种无连接、不可靠的协议(可能会丢包),

相对于TCP而言:

  • UDP面向无连接的,不需建立连接(TCP是面向连接的)

  • UDP尽力做到可靠,但不绝对可靠(TCP无差错、不丢失、不重复且按序到达)

  • UDP较好实时性,效率比TCP高

  • UDP支持一对一,一对多,多对一和多对多的交互通信(TCP点到点)

  • UDP对系统资源要求较少(TCP多)

2、库和示例

f31s6s.png

  1. 通过UDP收发数据

ESP8266作为UDP服务端,把电脑UDP客户端发过来的数据打印到串口调试器,并回复应答消息(使用Packet Sender软件发送UDP数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "XU-ChinaNet";//wifi账号
const char* password = "15358228063";//wifi密码

WiFiUDP Udp;
unsigned int localUdpPort = 4210; // 本地监听端口
char incomingPacket[255]; // 存储Udp客户端发过来的数据
char replyPacket[] = "Hi there! Got the message :-)"; // 应答信息


void setup()
{
Serial.begin(115200);
Serial.println();

Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");

//启动Udp监听服务
Udp.begin(localUdpPort);
//打印本地ip地址,udp client端会使用到
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
}


void loop()
{
//解析Udp数据包
int packetSize = Udp.parsePacket();
if (packetSize)
{
// 收到Udp数据包
Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
// 读取Udp数据包
int len = Udp.read(incomingPacket, 255);
if (len > 0)
{
incomingPacket[len] = 0;
}
//向D串口调试器打印信息
Serial.printf("UP packet contents: %s\n", incomingPacket);

//往udp 远端发送应答信息
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(replyPacket);
Udp.endPacket();
}
}
  1. 通过UDP控制LED
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char *ssid = "XU-ChinaNet"; //wifi名称
const char *password = "15358228063"; //wifi密码

WiFiUDP Udp;
unsigned int localUdpPort = 4210; // 本地端口号
char incomingPacket[255]; // 接收缓冲区

void setup()
{
//以下为基本功能初始化,初始化串口和网络和LED
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.printf("Connecting to %s ", ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");

//以下开启UDP监听并打印输出信息
Udp.begin(localUdpPort);
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
}

void loop()
{
int packetSize = Udp.parsePacket(); //获取当前队首数据包长度
if (packetSize) // 有数据可用
{
Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
int len = Udp.read(incomingPacket, 255); // 读取数据到incomingPacket
if (len > 0) // 如果正确读取
{
incomingPacket[len] = 0; //末尾补0结束字符串
Serial.printf("UDP packet contents: %s\n", incomingPacket);

if (strcmp(incomingPacket, "LED_OFF") == 0) // 命令LED_OFF
{
digitalWrite(LED_BUILTIN, HIGH); // 熄灭LED
sendCallBack("LED has been turn off");
}
else if (strcmp(incomingPacket, "LED_ON") == 0) // 如果收到LED_ON
{
digitalWrite(LED_BUILTIN, LOW); // 点亮LED
sendCallBack("LED has been turn on");
}
else // 如果非指定消息
{
sendCallBack("Command Error!");
}
}
}
}

/**
* 发送响应信息
*/
void sendCallBack(const char *buffer){
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(buffer); //回复内容
Udp.endPacket();
}

15、WebServer–ESP8266WebServer库

f8kYYq.png

示例:

  1. 演示webserver基础功能,wifi模块连接上热点后,在PC浏览器上输入serverip+url访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* Demo:
* 演示webserver基础功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed

//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);

ESP8266WebServer server(80);//创建一个webserver

/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
server.send(200, "text/plain", "hello from esp8266!");
}

/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}

void setup(void) {
initBasic();
initWifi();
initWebServer();
}

/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}

/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);

server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});

server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}

void loop(void) {
server.handleClient();
}

  1. 演示webserver返回html功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* Demo:
* 演示webserver html功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问)
*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed

//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);

ESP8266WebServer server(80);

/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;

snprintf(temp, 400,

"<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>ESP8266 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP8266!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",

hr, min % 60, sec % 60
);
server.send(200, "text/html", temp);
}

/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}

void setup(void) {
initBasic();
initWifi();
initWebServer();
}

void loop(void) {
server.handleClient();
}

/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}

/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);

server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.on("/test.svg", drawGraph);
server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}

/**
* 画图
*/
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";

server.send(200, "image/svg+xml", out);
}

  1. 演示webserver校验账号密码功能,Autherticate请求头
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* Demo:
* 演示webserver auth校验功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问,不过需要带校验请求头)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed
const char* www_username = "admin";
const char* www_password = "esp8266";

//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);

ESP8266WebServer server(80);//创建webserver

void setup() {
initBasic();
initWifi();
initWebServer();
}

void loop() {
server.handleClient();
}

/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}

/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", []() {
//校验帐号和密码
if (!server.authenticate(www_username, www_password)) {
return server.requestAuthentication();
}
server.send(200, "text/plain", "Login OK");
});
server.begin();

DebugPrint("Open http://");
DebugPrint(WiFi.localIP());
DebugPrintln("/ in your browser to see it working");
}

  1. 演示webserver登录功能,html登录页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* Demo:
* 演示webserver auth校验功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问,不过需要带校验请求头)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed

//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);

ESP8266WebServer server(80);

/**
* 校验是否存在cookie头并且cookie头的值是正确的
*/
bool is_authentified() {
DebugPrintln("Enter is_authentified");
//是否存在cookie头
if (server.hasHeader("Cookie")) {
DebugPrint("Found cookie: ");
//获取cookie头的信息
String cookie = server.header("Cookie");
DebugPrintln(cookie);
if (cookie.indexOf("ESPSESSIONID=1") != -1) {
DebugPrintln("Authentification Successful");
return true;
}
}
DebugPrintln("Authentification Failed");
return false;
}

/**
* 处理登陆uri
*/
void handleLogin() {
String msg;
//判断是否存在cookie头
if (server.hasHeader("Cookie")) {
DebugPrint("Found cookie: ");
String cookie = server.header("Cookie");
DebugPrint(cookie);
}
//判断是否存在DISCONNECT参数
if (server.hasArg("DISCONNECT")) {
DebugPrintln("Disconnection");
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
server.send(301);
return;
}
//判断是否存在USERNAME和PASSWORD参数
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") {
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
server.send(301);
DebugPrintln("Log in Successful");
return;
}
msg = "Wrong username/password! try again.";
DebugPrintln("Log in Failed");
}
//返回html 填写账号密码页面
String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin<br>";
content += "User:<input type='text' name='USERNAME' placeholder='user name'><br>";
content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>";
content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>";
content += "You also can go <a href='/inline'>here</a></body></html>";
server.send(200, "text/html", content);
}

/**
* 根目录处理器
*/
//root page can be accessed only if authentification is ok
void handleRoot() {
DebugPrintln("Enter handleRoot");
String header;
if (!is_authentified()) {
//校验不通过
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.send(301);
return;
}
String content = "<html><body><H2>hello, you successfully connected to esp8266!</H2><br>";
if (server.hasHeader("User-Agent")) {
content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
}
content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
server.send(200, "text/html", content);
}

/**
* 无效uri处理器
*/
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}

void setup(void) {
initBasic();
initWifi();
initWebServer();
}

void loop(void) {
server.handleClient();
}

/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}

/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler

server.on("/", handleRoot);
server.on("/login", handleLogin);
server.on("/inline", []() {
server.send(200, "text/plain", "this works without need of authentification");
});

server.onNotFound(handleNotFound);
//设置需要收集的请求头
const char * headerkeys[] = {"User-Agent", "Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
//收集头信息
server.collectHeaders(headerkeys, headerkeyssize);
server.begin();
DebugPrintln("HTTP server started");
}

16、域名服务–ESP8266mDNS库

1、DNS

​ DNS(Domain Name System)域名系统(因特网上作为域名和IP地址相互映射的一个分布式数据库),DNS协议运行在UDP协议上,端口号53,mDNS(Multicast DNS)组播dns,主要实现了在没有传统DNS服务器的情况下使用局域网内的主机实现本地发现和域名访问,端口号5353,遵从dns协议(基于UDP协议,即运用了UDP广播)。

2、库和示例

f88DBR.png

  1. 演示ESP8266 mDNS responder功能

电脑端输入http://esp8266.local/以域名方式访问webserver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
* Demo:
* 演示ESP8266 mDNS responder功能
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200;// serial connection speed

//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
void initmDNS(void);

ESP8266WebServer server(80);

/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
DebugPrintln("handleRoot");
server.send(200, "text/html", "Hello From ESP8266 mDNS demo");
}

/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
DebugPrintln("handleNotFound");
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}

void setup(void) {
initBasic();
initWifi();
initWebServer();
initmDNS();
}

void loop(void) {
MDNS.update(); //少这行
server.handleClient();
}

/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}

/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);
server.on("/inline", []() {
DebugPrintln("handleInline");
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}

/**
* 初始化mDNS
*/
void initmDNS(){
if (!MDNS.begin("esp8266")) {
DebugPrintln("Error setting up MDNS responder!");
while (1) {
delay(1000);
}
}
DebugPrintln("mDNS responder started,please input http://esp8266.local/ in your browser after install Bonjour");
}

2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
演示ESP8266 mDNS 发现服务功能

注意:
- 输入你的 WiFi SSID 和 password.
- 烧写到两块 ESP8266 板子
*/

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>

const char* ssid = "...";
const char* password = "...";
char hostString[16] = {0};

void setup() {
Serial.begin(115200);
delay(100);
Serial.println("\r\nsetup()");

sprintf(hostString, "ESP_%06X", ESP.getChipId());
Serial.print("Hostname: ");
Serial.println(hostString);
WiFi.hostname(hostString);

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());

if (!MDNS.begin(hostString)) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
//往mDNS里面注册服务
MDNS.addService("esp", "tcp", 8080);

Serial.println("Sending mDNS query");
//查找服务
int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services
Serial.println("mDNS query done");
if (n == 0) {
Serial.println("no services found");
} else {
Serial.print(n);
Serial.println(" service(s) found");
for (int i = 0; i < n; ++i) {
// 打印查找到的服务具体信息
Serial.print(i + 1);
Serial.print(": ");
Serial.print(MDNS.hostname(i));
Serial.print(" (");
Serial.print(MDNS.IP(i));
Serial.print(":");
Serial.print(MDNS.port(i));
Serial.println(")");
}
}
Serial.println();
Serial.println("loop() next");
}

void loop() {
// put your main code here, to run repeatedly:

17、SPIFFS–ESP8266 SPIFFS文件系统

1、概述:

​ Arduino环境下的esp8266的flash存储分配:

  1. 代码区:程序存储区(包含当前代码区current Sketch、更新代码区OTA update)
  2. 文件系统:SPIFFS闪存文件系统(SPI Flash File System)可通过IDE配置,一般NodeMcu配置成3M```#include<FS.h>``

不支持目录 文件名32字符限制 建议保持短文件名

  1. EEPROM
  2. WIFI Config:设置WiFi模块配置时存储的数据

2、库和使用

fGpEUP.png

  1. 文件操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* 功能描述:spiffs文件操作常见方法使用,包括文件查找、创建、打开、关闭、删除
*/
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

#define myFileName "mydemo.txt"

void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
//判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}

File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open(myFileName,"w+");
//关闭文件
myFile.close();
//再次判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
//删除文件
DebugPrintln("mydemo.txt removing...");
SPIFFS.remove(myFileName);
//再次判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
}

void loop(){
}
  1. 查看spiffs文件系统列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 功能描述:查看spiffs文件系统列表
*/
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");

File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.txt","w+");
//关闭文件
myFile.close();

//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.jpg","w+");
//关闭文件
myFile.close();

//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.html","w+");
//关闭文件
myFile.close();

Dir dir = SPIFFS.openDir("/");
while(dir.next()){
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
Serial.printf("FS File:%s,size:%d\n",fileName.c_str(),fileSize);
}
DebugPrintln("Setup Done!");
}

void loop(){
}
  1. 往文件myDemo.txt中写入一句话并读取出来显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 功能描述:演示文件读写功能
*/
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");

File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("myDemo.txt","w+");
if(myFile){
DebugPrintln("Writing something to myDemo.txt...");
myFile.println("单片机菜鸟博哥666");
myFile.close();
DebugPrintln("Writing Done.");
}else{
DebugPrintln("Open File Failed.");
}

//打开文件 可读
myFile = SPIFFS.open("myDemo.txt","r");
if(myFile){
DebugPrintln("Reading myDemo.txt...");
while(myFile.available()){
//读取文件输出
Serial.write(myFile.read());
}
myFile.close();
}else{
DebugPrintln("Open File Failed.");
}

DebugPrintln("Setup Done!");
}

void loop(){
}
  1. 烧写文件,将要存入SOIFFS区域的文件,提前放在代码目录的”data”目录中,使用ESP8266FS工具烧写(集成在ArduinoIDE中,但要安装这个工具)(提示没有找到这个工具的话,一般都是版本问题,换个版本就好了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 功能描述:演示上传文件并读取文件内容
* 前提:需要先往SPIFFS里面上传config.txt文件
*/
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");

File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/config.txt","r");
if(myFile){
//打印文件大小
int size = myFile.size();
Serial.printf("Size=%d\r\n", size);
//读取文件内容
DebugPrintln(myFile.readString());
myFile.close();
DebugPrintln("Reading Done.");
}else{
DebugPrintln("Open File Failed.");
}
}

void loop(){
}

18、web配网

自定义AP配网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
* 功能:AP配网(web配网)
* 作者:单片机菜鸟哥
* 时间:2020-02-15
* 描述:
* 1.设置固定IP 192.168.4.1
* 2.设置webserver监听web请求
* 3.处理web请求,获取ssid和pwd
* 4.连接网络
*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

#define DEBUG

#ifdef DEBUG
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#else
//以下三个定义为调试定义
#define DebugBegin(baud_rate)
#define DebugPrintln(message)
#define DebugPrint(message)
#endif

const char* ap_ssid = "esp_webconfig";
const char* ap_password = "";//开放式网络

char sta_ssid[32] = {0};
char sta_password[64] = {0};

const char* webpage_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<title>Document</title>\r\n\
</head>\r\n\
<body>\r\n\
<form name='input' action='/' method='POST'>\r\n\
wifi名称: <br>\r\n\
<input type='text' name='ssid'><br>\r\n\
wifi密码:<br>\r\n\
<input type='text' name='password'><br>\r\n\
<input type='submit' value='保存'>\r\n\
</form>\r\n\
</body>\r\n\
</html>\r\n\
";

IPAddress local_IP(192,168,4,1);
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);

void initApConfig();
void initWebServer();
void connectToWifi();
void handleRootPost();
void handleRoot();
void handleNotFound();

ESP8266WebServer server(80);

void setup(void) {
DebugBegin(115200);
DebugPrintln("");
DebugPrint("connect ap: ");
DebugPrintln(ap_ssid);

initApConfig();

DebugPrint("IP address: ");
DebugPrintln(WiFi.softAPIP());

initWebServer();

DebugPrintln("HTTP server started");

Serial.printf("Ready! Open http://%s in your browser\n",
WiFi.softAPIP().toString().c_str());
}

void loop(void) {
server.handleClient();
}

/**
* 初始化AP配置
*/
void initApConfig(){
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ap_ssid, ap_password);
}

/**
* 初始化webserver配置
*/
void initWebServer(){
server.on("/", HTTP_GET, handleRoot);
server.on("/", HTTP_POST, handleRootPost);
server.onNotFound(handleNotFound);

server.begin();
}

/**
* 连接到WiFi
*/
void connectToWifi(){
DebugPrintln("connectToWifi");
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.begin(sta_ssid, sta_password);

int cnt = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
cnt++;
Serial.print(".");
if(cnt>=40){
cnt = 0;
//重启系统
DebugPrintln("\r\nRestart now!");
ESP.restart();
}
}
DebugPrintln("connectToWifi Success!");
}

/**
* 处理web post请求
*/
void handleRootPost() {
DebugPrintln("handleRootPost");
if (server.hasArg("ssid")) {
DebugPrint("got ssid:");
strcpy(sta_ssid, server.arg("ssid").c_str());
DebugPrintln(sta_ssid);
} else {
DebugPrintln("error, not found ssid");
server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid");
return;
}

if (server.hasArg("password")) {
DebugPrint("got password:");
strcpy(sta_password, server.arg("password").c_str());
DebugPrintln(sta_password);
} else {
DebugPrintln("error, not found password");
server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
return;
}

server.send(200, "text/html", "<meta charset='UTF-8'>保存成功");
delay(2000);
//连接wifi
connectToWifi();
}

/**
* 处理web get请求
*/
void handleRoot() {
DebugPrintln("handleRoot");
server.send(200, "text/html", webpage_html);
}

void handleNotFound() {
String message = "File Not Found\n\n";
server.send(404, "text/plain", message);
}

19、NTP–时间服务

​ NTP(Network Time Protocol)网络时间协议,基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC,再配合各个时区的偏移调整就能实现精准同步对时功能。

NTP报文协议

实现:

  1. 拼接协议
  2. 使用现成NTP第三方库(NTPClient)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

const char *ssid = "XU-ChinaNet";
const char *password = "15358228063";

WiFiUDP ntpUDP;

// You can specify the time server pool and the offset (in seconds, can be
// changed later with setTimeOffset() ). Additionaly you can specify the
// update interval (in milliseconds, can be changed using setUpdateInterval() ).
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 60*60*8, 30*60*1000);

void setup(){
Serial.begin(115200);

WiFi.begin(ssid, password);

while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}

timeClient.begin();
}

void loop() {
timeClient.update();

Serial.println(timeClient.getFormattedTime());
int hours = timeClient.getHours();
int minu = timeClient.getMinutes();
int sece = timeClient.getSeconds();
Serial.printf("hour:%d minu:%d sece:%d\n", hours,minu,sece);
delay(1000);
}

20、DNSServer–真正的域名服务

​ 建立DNS服务,使用时模块必须处于AP模式下;真正意义上的精简版DNS服务器;DNSServer运行于UDP服务;这里DNS服务器唯一的作用是把域名转成对应映射地址(只支持一个)

fGI9sS.png

1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* 功能描述:在手机浏览器访问 "www.danpianji.com"会显示“Hello World”
*/

#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>

//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )

const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);

void setup() {

DebugBegin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer example");

// modify TTL associated with the domain name (in seconds)
// default is 60 seconds
dnsServer.setTTL(300);
// set which return code will be used for all other domains (e.g. sending
// ServerFailure instead of NonExistentDomain will reduce number of queries
// sent by clients)
// default is DNSReplyCode::NonExistentDomain
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);

// 启动DNS server,映射主机名为 www.danpianji.com
bool status = dnsServer.start(DNS_PORT, "www.danpianji.com", apIP);

if(status){
DebugPrintln("start dnsserver success.");
}else{
DebugPrintln("start dnsserver failed.");
}

// simple HTTP server to see that DNS server is working
webServer.onNotFound([]() {
String message = "Hello World!\n\n";
message += "URI: ";
message += webServer.uri();

webServer.send(200, "text/plain", message);
});
webServer.begin();
}

void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}

  1. Portal认证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 功能描述:portal认证
*/

#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>

//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )

const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);

String responseHTML = ""
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
"be redirected here.</p></body></html>";

void setup() {
DebugBegin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer CaptivePortal example");


// 所有请求都映射到一个具体地址
dnsServer.start(DNS_PORT, "*", apIP);

// replay to all requests with same HTML
webServer.onNotFound([]() {
DebugPrintln("webServer handle.");
webServer.send(200, "text/html", responseHTML);
});
webServer.begin();
}

void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}

21、无线更新–OTA固件更新

1、ArduinoOTA

  • 连接WIFI;
  • 配置ArduinoOTA对象的事件函数;
  • 启动ArduinoOTA服务ArduinoOTA.begin();
  • 在loop()函数将处理权交由ArduinoOTA.handle()。

为了区分正常工作模式以及更新模式,可以设置标志位来区分(标志位可通过其他手段修改,如按键、软件控制)

1
2
3
4
5
6
7
void loop() {
if (flag ==0 ) {
// 正常工作状态的代码
} else {
ArduinoOTA.handle();
}
}

fGXaO1.png

python2.7环境

旧代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 功能描述:OTA之Arduino IDE更新 V1.0版本代码
*
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )

#define CodeVersion "CodeVersion V1.0"

const char* ssid = "xxxx";//填上wifi账号
const char* password = "xxxxx";//填上wifi密码

void setup() {
DebugBegin(115200);
DebugPrintln("Booting Sketch....");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
DebugPrintln("Connection Failed! Rebooting...");
delay(5000);
//重启ESP8266模块
ESP.restart();
}

// Port defaults to 8266
// ArduinoOTA.setPort(8266);

// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");

// No authentication by default
// ArduinoOTA.setPassword("admin");

// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

ArduinoOTA.onStart([]() {
String type;
//判断一下OTA内容
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}

// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
DebugPrintln("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
DebugPrintln("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
DebugPrintF("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
DebugPrintF("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
DebugPrintln("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
DebugPrintln("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
DebugPrintln("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
DebugPrintln("Receive Failed");
} else if (error == OTA_END_ERROR) {
DebugPrintln("End Failed");
}
});
ArduinoOTA.begin();
DebugPrintln("Ready");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

void loop() {
ArduinoOTA.handle();
}

烧写成功后,重启IDE(IDE与8266建立无线连接)端口项出现网络端口

新代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* 功能描述:OTA之Arduino IDE更新 V1.1版本代码
*
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )

#define CodeVersion "CodeVersion V1.1"

const char* ssid = "xxxx";//填上wifi账号
const char* password = "xxxx";//填上wifi密码

void setup() {
DebugBegin(115200);
DebugPrintln("Booting Sketch....");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
DebugPrintln("Connection Failed! Rebooting...");
delay(5000);
//重启ESP8266模块
ESP.restart();
}

// Port defaults to 8266
// ArduinoOTA.setPort(8266);

// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");

// No authentication by default
// ArduinoOTA.setPassword("admin");

// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

ArduinoOTA.onStart([]() {
String type;
//判断一下OTA内容
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}

// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
DebugPrintln("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
DebugPrintln("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
DebugPrintF("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
DebugPrintF("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
DebugPrintln("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
DebugPrintln("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
DebugPrintln("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
DebugPrintln("Receive Failed");
} else if (error == OTA_END_ERROR) {
DebugPrintln("End Failed");
}
});
ArduinoOTA.begin();
DebugPrintln("Ready");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}

void loop() {
ArduinoOTA.handle();
}

2、WebUpdateOTA

fJ9d3T.png

  1. 系统自带OTA之WEB更新(通过建立 webserver来上传新固件以达到更新目的)

V1.1代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
* 功能描述:OTA之web更新 V1.0版本代码
*/

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )

#define CodeVersion "CodeVersion V1.0"

const char* host = "esp8266-webupdate";
const char* ssid = "xxx";//填上wifi账号
const char* password = "xxx";//填上wifi密码

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

void setup(void) {
DebugBegin(115200);
DebugPrintln("Booting Sketch...");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);

while (WiFi.waitForConnectResult() != WL_CONNECTED) {
WiFi.begin(ssid, password);
DebugPrintln("WiFi failed, retrying.");
}
//启动mdns服务
MDNS.begin(host);
//配置webserver为更新server
httpUpdater.setup(&httpServer);
httpServer.begin();

MDNS.addService("http", "tcp", 80);
DebugPrintF("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
}

void loop(void) {
httpServer.handleClient();
MDNS.update();
}

同一WIFI下,打开调试器中提示的网站

编译V1.1代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
* 功能描述:OTA之web更新 V1.1版本代码
*/

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )

#define CodeVersion "CodeVersion V1.1"

const char* host = "esp8266-webupdate";
const char* ssid = "xxx";//填上wifi账号
const char* password = "xxx";//填上wifi密码

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

void setup(void) {
DebugBegin(115200);
DebugPrintln("Booting Sketch...");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);

while (WiFi.waitForConnectResult() != WL_CONNECTED) {
WiFi.begin(ssid, password);
DebugPrintln("WiFi failed, retrying.");
}
//启动mdns服务
MDNS.begin(host);
//配置webserver为更新server
httpUpdater.setup(&httpServer);
httpServer.begin();

MDNS.addService("http", "tcp", 80);
DebugPrintF("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
}

void loop(void) {
httpServer.handleClient();
MDNS.update();
}

选择bin文件,通过WEB网页上传

  1. 自定义OTA之WEB更新(个性化页面)

3.SerialUpdateOTA—OTA之服务器更新

ftmccj.png

放在服务器上,无感知更新

22、WebSocket Client–全双工通信

问题:HTTP:一个请求-响应应用层协议,客户端没有主动发请求,服务器不能主动给客户端发数据

方法:

  1. 轮询(浪费带宽资源)
  2. 应用层协议解决MQTT协议
  3. WebSocket协议:让客户端与服务器之间建立无限制的全双工通信,任何一方都可主动发消息给对方(HTML5定义的协议,更好的节省服务器资源和带宽,并实时进行通讯)

两阶段:

  1. 通过HTTP请求确认WebSocket握手协议阶段;

  2. 通过WebSocket交互数据阶段。

23、例程

1、局域网应用–炫酷RGB

相关技术点:

  • ArduinoJson库
  • TCP Server服务
  • STA模式
  • 一键配网

内容:

手机、ESP8266均连接在同一个路由器wifi,8266作为服务端,手机作为客户端,手机再通过wifi给8266发送数据,8266接收到数据再把数据发给Arduino(串口通信),Arduino解析数据达到控制效果

实现:

APP源码地址

8266端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* 功能:wifi RGB 8266端
* 加入SmartConfig功能
**/
#include <ESP8266WiFi.h>

#define MAX_SRV_CLIENTS 3 //最大同时联接数,即你想要接入的设备数量,8266tcpserver只能接入五个
#define LED 2

WiFiServer server(8266); //你要的端口号,随意修改,范围0-65535
WiFiClient serverClients[MAX_SRV_CLIENTS];
int flag = HIGH;//默认当前灭灯

void setup() {
Serial.begin(115200);
pinMode(LED,OUTPUT);
digitalWrite(LED, HIGH);

if(!autoConfig()){ //连接WIFI,先是自动连接,超过20秒自动连接失败,开始smartConfig()
smartConfig();
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
}

delay(1000);
digitalWrite(LED, LOW);
Serial.println("IP address: ");
Serial.println(WiFi.localIP()); //WiFi.localIP()返回8266获得的ip地址
server.begin();
server.setNoDelay(true); //加上后才正常些
//使能软件看门狗的触发间隔
ESP.wdtEnable(5000);
}

void loop() {
uint8_t index;
if (server.hasClient()){
for (index = 0; index < MAX_SRV_CLIENTS; index++){
if (!serverClients[index] || !serverClients[index].connected()){
if (serverClients[index]) serverClients[index].stop();//未联接,就释放
serverClients[index] = server.available();//分配新的
continue;
}
}
//8266tcpserver只能接入五个 超出的需要释放
WiFiClient serverClient = server.available();
if (serverClient){
serverClient.stop();
}
}

for (index = 0; index < MAX_SRV_CLIENTS; index++){
if (serverClients[index] && serverClients[index].connected()){
//处理客户端发过来的数据
if (serverClients[index].available()){
while (serverClients[index].available())
//把数据发送给mega
Serial.write(serverClients[index].read());
}
}
}

if(Serial.available()>0){
char ch = Serial.read();
//收到ardunio发过来的进入smartconfig模式的命令
if(ch == '1'){
smartConfig();
delay(1000);
digitalWrite(LED, LOW);
Serial.println("IP address: ");
Serial.println(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
}
}

//喂狗
ESP.wdtFeed();
}

/**
* 自动连接20s 超过之后自动进入SmartConfig模式
*/
bool autoConfig(){
WiFi.mode(WIFI_AP_STA); //设置esp8266 工作模式
WiFi.begin();
delay(2000); //刚启动模块的话 延时稳定一下
Serial.println("AutoConfiging ......");
for(int index=0;index<20;index++){
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED){
Serial.println("AutoConfig Success");
Serial.print("SSID:");
Serial.println(WiFi.SSID().c_str());
Serial.print("PSW:");
Serial.println(WiFi.psk().c_str());
return true;
}else{
Serial.print(".");
delay(1000);
flag = !flag;
digitalWrite(LED, flag);
}
}
Serial.println("AutoConfig Faild!");
return false;
}

/**
* 开启SmartConfig功能
*/
void smartConfig()
{
WiFi.mode(WIFI_STA);
delay(2000);
Serial.println("Wait for Smartconfig");// 等待配网
WiFi.beginSmartConfig();
while (1){
Serial.print(".");
delay(500);
flag = !flag;
digitalWrite(LED, flag);

if (WiFi.smartConfigDone()){
//smartconfig配置完毕
Serial.println("SmartConfig Success");
Serial.print("SSID:");
Serial.println(WiFi.SSID().c_str());
Serial.print("PSW:");
Serial.println(WiFi.psk().c_str());
WiFi.mode(WIFI_AP_STA); //设置esp8266 工作模式
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
}
}

Arduino端代码:(接收8266信息,JSON解析)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/**
* 功能:wifi lamp arduino端
**/
#include <SoftwareSerial.h>
#include <ArduinoJson.h>

const unsigned long BAUD_RATE = 115200; // serial connection speed
const size_t MAX_CONTENT_SIZE = 50;
const size_t t_bright=1,t_color=2,t_frequency=3,t_switch=4;

//#define UNO //uncomment this line when you use it with UNO board
#define MEGA //uncomment this line when you use it with MEGA board

#ifdef UNO
SoftwareSerial mySerial(10,11);
#endif

#ifdef UNO
#define WifiSerial Serial
#define MyDebugSerial mySerial
#endif

#ifdef MEGA
#define WifiSerial Serial1
#define MyDebugSerial Serial
#endif

//该条语句用于使能DEBUG输出信息,屏蔽掉就不会输出debug调试信息
#define DEBUG
//该条语句用于使能是共阴RGB 屏蔽掉就是共阳RGB
//#define COMMON_GND

#ifdef DEBUG
#define DBGLN(message) MyDebugSerial.println(message)
#else
#define DBGLN(message)
#endif

#ifdef UNO
#define PIN_RED 3 //red 引脚
#define PIN_GREEN 5 //green 引脚
#define PIN_BLUE 6 //blue 引脚
#define PIN_ENABLE 9 //使能引脚 pwm控制亮度
#define PIN_KEY 7// 按键
#else
#define PIN_RED 2
#define PIN_GREEN 3
#define PIN_BLUE 4
#define PIN_ENABLE 5
#define PIN_KEY 6
#endif

int red = 0,green = 0,blue = 0;
int type = 4;//当前模式 1亮度 2颜色 3呼吸 4开关
int frequency = 1;//频率
int switch_status = 1;//关闭 or 开启
int bright = 1;//亮度

char response[MAX_CONTENT_SIZE];
int fadeValue = 0;//当前亮度
bool isAdd = true;//是否是从暗到亮

// 定义记录按键当前状态的变量
int state_btn;
// 定义记录按键最近一次状态变化的变量,并初始化状态为LOW。
int lastButtonState = LOW;
// 定义记录最近一次抖动的时间变量,并初始化时间为0毫秒。
long lastDebounceTime = 0;
// 定义延迟抖动的时间变量
long debouncdDelay = 60;

/**
* @Desc 初始化操作
*/
void setup() {
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
pinMode(PIN_BLUE, OUTPUT);
pinMode(PIN_ENABLE, OUTPUT);
pinMode(PIN_KEY,INPUT);

WifiSerial.begin(BAUD_RATE);
#ifdef DEBUG
#ifdef UNO
MyDebugSerial.begin(9600);//软串口9600稳定
#else
MyDebugSerial.begin(BAUD_RATE);
#endif
#endif
DBGLN("Arduino Init End");
}

/**
* @Desc 主函数
*/
void loop() {

if(WifiSerial.available()>0){
clrEsp8266ResponseBuffer();
int data_size = ReceiveMessage(response, sizeof(response));
if(data_size>0){
//开始解析数据
parseData(response);
}
}

if(type == t_frequency){
//呼吸灯效果
breatheRGB(frequency);
}
checkButton();
}

/**
* 读取串口缓冲区里面的数据
*/
int ReceiveMessage(char* content, size_t maxSize){
//不用 readBytes 因为比较耗时
size_t length = WifiSerial.readBytesUntil('}',content, maxSize);
content[length] = '}';
content[++length] = 0;
DBGLN(content);
return length;
}

/**
* @Desc 解析json
* 有三种
* 1.亮度控制页面(0 暗 1正常 2亮)
* {
* "t": 1,
* "bb": 2
* }
* 2.颜色控制页面
* {
* "t": 2,
* "cr": 154,
* "cg": 147,
* "cb": 255
* }
* 3.呼吸灯控制页面(0 慢呼吸 1正常 2快)
* {
* "t": 3,
* "gf": 1
* }
* 4.开关控制(0关闭 1开启)
* {
* "t": 4,
* "ss": 1
* }
**/
bool parseData(char* content) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;

JsonObject& root = jsonBuffer.parseObject(content);

if (!root.success()) {
Serial.println("JSON parsing failed!");
return false;
}

type = root["t"];
switch(type){
case t_bright:
bright = root["bb"];
brightRGB(bright);
break;
case t_color:
red = root["cr"];
green = root["cg"];
blue = root["cb"];
colorRGB(red,green,blue);
break;
case t_frequency:
frequency = root["gf"];
break;
case t_switch:
switch_status = root["ss"];
bool enable = switch_status == 1;
switchRGB(enable);
break;
}
return true;
}

/**
* 控制灯亮度
*/
void brightRGB(int bright){
int level = bright%3;
int bright_level;
switch(level){
case 0://暗 50
bright_level = 50;
break;
case 1://正常 100
bright_level = 100;
break;
case 2://亮 200
bright_level = 200;
break;
}
#ifdef COMMON_GND
//共地
analogWrite(PIN_ENABLE,bright_level);
#else
analogWrite(PIN_ENABLE,255-bright_level);
#endif
}

/**
* 控制RGB颜色
*/
void colorRGB(int red, int green, int blue){
#ifdef COMMON_GND
analogWrite(PIN_RED,constrain(red,0,255));
analogWrite(PIN_GREEN,constrain(green,0,255));
analogWrite(PIN_BLUE,constrain(blue,0,255));
#else
analogWrite(PIN_RED,constrain(255-red,0,255));
analogWrite(PIN_GREEN,constrain(255-green,0,255));
analogWrite(PIN_BLUE,constrain(255-blue,0,255));
#endif
}

/**
* 控制亮灭
*/
void switchRGB(bool enable){
if(enable){
//打开
#ifdef COMMON_GND
//共地
analogWrite(PIN_ENABLE,255);
#else
analogWrite(PIN_ENABLE,0);
#endif
}else{
//关闭
#ifdef COMMON_GND
//共地
analogWrite(PIN_ENABLE,0);
#else
analogWrite(PIN_ENABLE,255);
#endif
}
}

/**
* 呼吸灯
*/
void breatheRGB(int frequency){
int level = frequency%3;
int f_level;
switch(level){
case 0://慢 50
f_level = 3;
break;
case 1://正常 100
f_level = 10;
break;
case 2://快 200
f_level = 20;
break;
}
if(isAdd){
//递增方向
fadeValue +=f_level;
if(fadeValue>=255){
fadeValue = 255;
isAdd =false;
}
}else{
//递减方向
fadeValue -=f_level;
if(fadeValue<=0){
fadeValue = 0;
isAdd =true;
}
}
analogWrite(PIN_ENABLE,fadeValue);
delay(20);
}

/**
* 检查按键功能
*/
void checkButton(){
int buttonState = digitalRead(PIN_KEY);//读取当前按键状态
if(buttonState != lastButtonState){
//如果按键发生了变化 则重新设置最近一次抖动的时间
//方法millis()可以获取当前时间,单位统一为毫秒。
lastDebounceTime = millis();
}

// 判断按键按下状态时间间隔是否大于延迟抖动的时间长度。
if(millis()-lastDebounceTime>debouncdDelay){
// 判断当前的按键状态是否和之前有所变化
if(buttonState != state_btn){
// 如果发生了变化,
// 则更新按键状态变量。
state_btn = buttonState;
if(state_btn == HIGH){
//再次确认是否真的按下了按键
DBGLN("smartconfig");
WifiSerial.write('1');
}
}
}
// 更新按键最近一次状态变化的变量
lastButtonState = buttonState;
}

void clrEsp8266ResponseBuffer(void){
memset(response, 0, MAX_CONTENT_SIZE); //清空
}

2、OLED显示天气屏

相关技术点:

  • ArduinoJson V5库
  • TCP Client
  • STA模式
  • 一键配网
  • U8G2 OLED库

内容:

向心知天气请求当地城市天气情况,并在OLED上显示天气图标、温度值以及城市名称

心知天气参考wiki

天气编码wiki

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/**
* 功能:OLED显示天气屏
**/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#define LED D4
#define DEBUG //是否开启debug功能

#ifdef DEBUG
#define DebugPrintln(message) Serial.println(message)
#else
#define DebugPrintln(message)
#endif

#ifdef DEBUG
#define DebugPrint(message) Serial.print(message)
#else
#define DebugPrint(message)
#endif

#define WEATHER_CODE_DAY_SUN "0" //晴(国内城市白天晴)
#define WEATHER_CODE_NIGHT_SUN "1" //晴(国内城市夜晚晴)
#define WEATHER_CODE_DAY_SUN1 "2" //晴(国外城市白天晴)
#define WEATHER_CODE_NIGHT_SUN2 "3" //晴(国外城市夜晚晴)
#define WEATHER_CODE_CLOUDY "4" //多云
#define WEATHER_CODE_DAY_PARTLY_CLOUDY "5" //白天晴间多云
#define WEATHER_CODE_NIGHT_PARTLY_CLOUDY "6" //夜晚晴间多云
#define WEATHER_CODE_DAY_MOSTLY_CLOUDY "7" //白天大部多云
#define WEATHER_CODE_NIGHT_MOSTLY_CLOUDY "8" //夜晚大部多云
#define WEATHER_CODE_OVERCAST "9" //阴
#define WEATHER_CODE_SHOWER "10" //阵雨
#define WEATHER_CODE_THUNDERSHOWER "11" //雷阵雨
#define WEATHER_CODE_THUNDERSHOWER_WITH_HAIL "12" //雷阵雨伴有冰雹
#define WEATHER_CODE_LIGHT_RAIN "13" //小雨
#define WEATHER_CODE_MODERATE_RAIN "14" //中雨
#define WEATHER_CODE_HEAVY_RAIN "15" //大雨
#define WEATHER_CODE_STORM "16" //暴雨
#define WEATHER_CODE_HEAVY_STORM "17" //大暴雨
#define WEATHER_CODE_SEVERE_STORM "18" //特大暴雨
#define WEATHER_CODE_ICE_RAIN "19" //冻雨
#define WEATHER_CODE_SLEET "20" //雨夹雪
#define WEATHER_CODE_SNOW_FLURRY "21" //阵雪
#define WEATHER_CODE_LIGHT_SNOW "22" //小雪
#define WEATHER_CODE_MODERATE_SNOW "23" //中雪
#define WEATHER_CODE_HEAVY_SNOW "24" //大雪
#define WEATHER_CODE_SNOW_STORM "25" //暴雪

#define SUN_DAY 0
#define SUN_NIGHT 1
#define SUN_CLOUD 2
#define CLOUD 3
#define RAIN 4
#define THUNDER 5

//声明方法
bool autoConfig();
void smartConfig();
bool sendRequest(const char* host, const char* cityid, const char* apiKey);
bool skipResponseHeaders();
void readReponseContent(char* content, size_t maxSize);
void stopConnect();
void clrEsp8266ResponseBuffer(void);
bool parseUserData(char* content, struct UserData* userData);
void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol);
void drawWeather(uint8_t symbol, int degree);

const unsigned long BAUD_RATE = 115200;// serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server
const size_t MAX_CONTENT_SIZE = 500; // max size of the HTTP response
const char* host = "api.seniverse.com";
const char* APIKEY = "wcmquevztdy1jpca"; //API KEY
const char* city = "guangzhou";
const char* language = "zh-Hans";//zh-Hans 简体中文 会显示乱码

int flag = HIGH;//默认当前灭灯
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

WiFiClient client;
char response[MAX_CONTENT_SIZE];
char endOfHeaders[] = "\r\n\r\n";

long lastTime = 0;
// 请求服务间隔
long Delay = 20000;

// 我们要从此网页中提取的数据的类型
struct UserData {
char city[16];//城市名称
char weather_code[4];//天气现象code(多云...)
char temp[5];//温度
};

/**
* @Desc 初始化操作
*/
void setup() {
Serial.begin(BAUD_RATE);
pinMode(LED,OUTPUT);
digitalWrite(LED, HIGH);

WiFi.disconnect();
if(!autoConfig()){
smartConfig();
DebugPrint("Connecting to WiFi");//写几句提示,哈哈
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
}

delay(1000);
digitalWrite(LED, LOW);
DebugPrintln("IP address: ");
DebugPrintln(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
lastTime = millis();
u8g2.begin();
u8g2.enableUTF8Print();
//使能软件看门狗的触发间隔
ESP.wdtEnable(5000);
}

/**
* @Desc 主函数
*/
void loop() {
while (!client.connected()){
if (!client.connect(host, 80)){
flag = !flag;
digitalWrite(LED, flag);
delay(500);
//喂狗
ESP.wdtFeed();
}
}

if(millis()-lastTime>=Delay){
//每间隔20s左右调用一次
lastTime = millis();
if (sendRequest(host, city, APIKEY) && skipResponseHeaders()) {
clrEsp8266ResponseBuffer();
readReponseContent(response, sizeof(response));
UserData userData;
if (parseUserData(response, &userData)) {
showWeather(&userData);
}
}
}

//喂狗
ESP.wdtFeed();
}

/**
* 自动连接20s 超过之后自动进入SmartConfig模式
*/
bool autoConfig(){
WiFi.mode(WIFI_AP_STA); //设置esp8266 工作模式
WiFi.begin();
delay(2000);//刚启动模块的话 延时稳定一下
DebugPrintln("AutoConfiging ......");
for(int index=0;index<10;index++){
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED){
DebugPrintln("AutoConfig Success");
DebugPrint("SSID:");
DebugPrintln(WiFi.SSID().c_str());
DebugPrint("PSW:");
DebugPrintln(WiFi.psk().c_str());
return true;
}else{
DebugPrint(".");
delay(500);
flag = !flag;
digitalWrite(LED, flag);
}
}
DebugPrintln("AutoConfig Faild!");
return false;
}

/**
* 开启SmartConfig功能
*/
void smartConfig()
{
WiFi.mode(WIFI_STA);
delay(1000);
DebugPrintln("Wait for Smartconfig");
// 等待配网
WiFi.beginSmartConfig();
while (1){
DebugPrint(".");
delay(200);
flag = !flag;
digitalWrite(LED, flag);

if (WiFi.smartConfigDone()){
//smartconfig配置完毕
DebugPrintln("SmartConfig Success");
DebugPrint("SSID:");
DebugPrintln(WiFi.SSID().c_str());
DebugPrint("PSW:");
DebugPrintln(WiFi.psk().c_str());
WiFi.mode(WIFI_AP_STA); //设置esp8266 工作模式
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
}
}

/**
* @发送请求指令
*/
bool sendRequest(const char* host, const char* cityid, const char* apiKey) {
// We now create a URI for the request
//心知天气
String GetUrl = "/v3/weather/now.json?key=";
GetUrl += apiKey;
GetUrl += "&location=";
GetUrl += city;
GetUrl += "&language=";
GetUrl += language;
// This will send the request to the server
client.print(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
DebugPrintln("create a request:");
DebugPrintln(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n");
delay(1000);
return true;
}

/**
* @Desc 跳过 HTTP 头,使我们在响应正文的开头
*/
bool skipResponseHeaders() {
// HTTP headers end with an empty line
bool ok = client.find(endOfHeaders);
if (!ok) {
DebugPrintln("No response or invalid response!");
}
return ok;
}

/**
* @Desc 从HTTP服务器响应中读取正文
*/
void readReponseContent(char* content, size_t maxSize) {
size_t length = client.readBytes(content, maxSize);
delay(100);
DebugPrintln("Get the data from Internet!");
content[length] = 0;
DebugPrintln(content);
DebugPrintln("Read data Over!");
client.flush();//这句代码需要加上 不然会发现每隔一次client.find会失败
}

// 关闭与HTTP服务器连接
void stopConnect() {
client.stop();
}

void clrEsp8266ResponseBuffer(void){
memset(response, 0, MAX_CONTENT_SIZE); //清空
}

bool parseUserData(char* content, struct UserData* userData) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;

JsonObject& root = jsonBuffer.parseObject(content);

if (!root.success()) {
Serial.println("JSON parsing failed!");
return false;
}

//复制我们感兴趣的字符串
strcpy(userData->city, root["results"][0]["location"]["name"]);
strcpy(userData->weather_code, root["results"][0]["now"]["code"]);
strcpy(userData->temp, root["results"][0]["now"]["temperature"]);
// -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
// 当你读取字符串时它仍在内存中
return true;
}

/**
* 根据天气接口返回的数据判断显示
*/
void showWeather(struct UserData* userData){
if(strcmp(userData->weather_code,WEATHER_CODE_DAY_SUN) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_DAY_SUN1) == 0){
drawWeather(SUN_DAY,userData->temp,userData->city);
}else if(strcmp(userData->weather_code,WEATHER_CODE_NIGHT_SUN) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_NIGHT_SUN2) == 0 ){
drawWeather(SUN_NIGHT,userData->temp,userData->city);
}else if(strcmp(userData->weather_code,WEATHER_CODE_DAY_PARTLY_CLOUDY) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_NIGHT_PARTLY_CLOUDY) == 0 ){
drawWeather(SUN_CLOUD,userData->temp,userData->city);
}else if(strcmp(userData->weather_code,WEATHER_CODE_CLOUDY) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_DAY_MOSTLY_CLOUDY) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_NIGHT_MOSTLY_CLOUDY) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_OVERCAST) == 0){
drawWeather(CLOUD,userData->temp,userData->city);
}else if(strcmp(userData->weather_code,WEATHER_CODE_SHOWER) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_LIGHT_RAIN) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_MODERATE_RAIN) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_HEAVY_RAIN) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_STORM) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_HEAVY_STORM) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_SEVERE_STORM) == 0){
drawWeather(RAIN,userData->temp,userData->city);
}else if(strcmp(userData->weather_code,WEATHER_CODE_THUNDERSHOWER) == 0
|| strcmp(userData->weather_code,WEATHER_CODE_THUNDERSHOWER_WITH_HAIL) == 0){
drawWeather(THUNDER,userData->temp,userData->city);
}else{
drawWeather(CLOUD,userData->temp,userData->city);
}
}

void drawWeather(uint8_t symbol, char* degree,char* city)
{
DebugPrintln(city);
u8g2.clearBuffer(); // clear the internal memory
//绘制天气符号
drawWeatherSymbol(0, 48, symbol);
//绘制温度
u8g2.setFont(u8g2_font_logisoso32_tf);
u8g2.setCursor(48+3, 42);
u8g2.print(degree);
u8g2.print("°C"); // requires enableUTF8Print()
u8g2.setFont(u8g2_font_unifont_t_chinese3);

u8g2_uint_t strWidth = u8g2.getUTF8Width(city);
u8g2_uint_t displayWidth = u8g2.getDisplayWidth();

u8g2.setCursor(displayWidth - strWidth - 5, 60);
u8g2.print(city);
u8g2.sendBuffer(); // transfer internal memory to the display
}

/**
* 绘制天气符号
*/
void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol)
{
// fonts used:
// u8g2_font_open_iconic_embedded_6x_t
// u8g2_font_open_iconic_weather_6x_t
// encoding values, see: https://github.com/olikraus/u8g2/wiki/fntgrpiconic
switch(symbol)
{
case SUN_DAY://太阳
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 69);
break;
case SUN_NIGHT://太阳
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 66);
break;
case SUN_CLOUD://晴间多云
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 65);
break;
case CLOUD://多云
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 64);
break;
case RAIN://下雨
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 67);
break;
case THUNDER://打雷
u8g2.setFont(u8g2_font_open_iconic_embedded_6x_t);
u8g2.drawGlyph(x, y, 67);
break;
}
}

3、WIFI小车

相关技术点:

WebSocket Client 全双工通信

WebSocket Server 全双工通信

内容:

  • NodeMcu作为AP。手机控制端作为STA,连接AP
  • NodeMcu作为WebSocketServer端,手机作为WebSocketClient端
  • 两者建立WS通信,手机往NodeMcu发送控制命令
  • NodeMcu控制电机

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
*功能:wifi小车 ESP8266
*/

#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>

#define AP_SSID "WiFiCar" //这里改成你的AP名字
#define AP_PSW "12345678" //这里改成你的AP密码 8位以上

#define IN1 D1 // 7 6 右轮
#define IN2 D2
#define IN3 D3 // 5 4 左轮
#define IN4 D4

#define LEFT "3" //左转编码
#define RIGHT "4"//右转编码
#define GO "1"//前进编码
#define BACK "2"//后退编码
#define STOP "0"//停止编码

#define USE_SERIAL Serial

IPAddress local_IP(192,168,4,25);
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);

WebSocketsServer webSocket = WebSocketsServer(81);

void initSys();
void initAP();
void initWS();
void initCar();
void stopCar();
void turnLeft();
void turnRight();
void go();
void back();
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length);

void setup() {
// put your setup code here, to run once:
initSys();
initAP();
initWS();
initCar();
}

void loop() {
// put your main code here, to run repeatedly:
webSocket.loop();
//喂狗
ESP.wdtFeed();
}

void initSys(){
USE_SERIAL.begin(115200);
USE_SERIAL.println();
}

void initAP(){
//配置AP信息
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(local_IP, gateway, subnet);
boolean result = WiFi.softAP(AP_SSID, AP_PSW);
if(!result){
ESP.restart();
}
}

void initWS(){
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}

void initCar(){
pinMode(IN1,OUTPUT);
pinMode(IN2,OUTPUT);
pinMode(IN3,OUTPUT);
pinMode(IN4,OUTPUT);
}

void stopCar(){
//默认全是低电平 停止状态
digitalWrite(IN1,LOW);
digitalWrite(IN2,LOW);
digitalWrite(IN3,LOW);
digitalWrite(IN4,LOW);
}

/**
* 左转
*/
void turnLeft(){
digitalWrite(IN1,HIGH);
digitalWrite(IN2,LOW); //右轮前进
digitalWrite(IN3,LOW);
digitalWrite(IN4,LOW); //左轮不动
}

/**
* 右转
*/
void turnRight(){
digitalWrite(IN1,LOW);
digitalWrite(IN2,LOW); //右轮不动
digitalWrite(IN3,HIGH);
digitalWrite(IN4,LOW); //左轮前进
}

/**
* 前进
*/
void go(){
digitalWrite(IN1,HIGH);
digitalWrite(IN2,LOW); //右轮前进
digitalWrite(IN3,HIGH);
digitalWrite(IN4,LOW); //左轮前进
}

/**
* 倒车
*/
void back(){
digitalWrite(IN1,LOW);
digitalWrite(IN2,HIGH); //右轮后退
digitalWrite(IN3,LOW);
digitalWrite(IN4,HIGH); //左轮后退
}

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);

// 缺少控制逻辑

break;
}

}
上一篇:
OpenHD-树莓派数字图传
下一篇:
墨水屏