










ESP32开发相关资料收集
《ESP32-S2 笔记》 ARDUINO 上的开发环境搭建
在Arduino IDE中安装固件ESP32,ESP8266板
如何通过 Arduino IDE 玩转乐鑫 ESP32 模块
玩转 ESP32 + Arduino (十) WIFI(AP模式) (STA模式) (smartConfig)
ESP32-CAM视频传输至公网服务器并转发视频数据流
上代码, esp32-cam端使用的Arduino c语言编写:
#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#include <vector>
const char *ssid = "xxxx"; //wifi用户名
const char *password = "xxxx"; //wifi密码
const IPAddress serverIP(xxxx); //你自己的公网服务器ip地址
uint16_t serverPort = xxxx; //服务器端口号(tcp协议)
#define maxcache 1430
WiFiClient client; //声明一个客户端对象,用于与服务器进行连接
//CAMERA_MODEL_AI_THINKER类型摄像头的引脚定义
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sscb_sda = SIOD_GPIO_NUM,
.pin_sscb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_VGA,
.jpeg_quality = 12,
.fb_count = 1,
};
void wifi_init()
{
WiFi.mode(WIFI_STA);
WiFi.setSleep(false); //关闭STA模式下wifi休眠,提高响应速度
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("WiFi Connected!");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
}
esp_err_t camera_init() {
//initialize the camera
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.println("Camera Init Failed");
return err;
}
sensor_t * s = esp_camera_sensor_get();
//initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV2640_PID) {
// s->set_vflip(s, 1);//flip it back
// s->set_brightness(s, 1);//up the blightness just a bit
// s->set_contrast(s, 1);
}
Serial.println("Camera Init OK!");
return ESP_OK;
}
void setup()
{
Serial.begin(115200);
wifi_init();
camera_init();
}
void loop()
{
Serial.println("Try To Connect TCP Server!");
if (client.connect(serverIP, serverPort)) //尝试访问目标地址
{
Serial.println("Connect Tcp Server Success!");
client.println("Frame Begin"); //46 72 61 6D 65 20 42 65 67 69 6E // 0D 0A 代表换行 //向服务器发送数据
while (1){
camera_fb_t * fb = esp_camera_fb_get();
uint8_t * temp = fb->buf; //这个是为了保存一个地址,在摄像头数据发送完毕后需要返回,否则会出现板子发送一段时间后自动重启,不断重复
if (!fb)
{
Serial.println( "Camera Capture Failed");
}
else
{
//先发送Frame Begin 表示开始发送图片 然后将图片数据分包发送 每次发送1430 余数最后发送
//完毕后发送结束标志 Frame Over 表示一张图片发送完毕
client.print("Frame Begin"); //一张图片的起始标志
// 将图片数据分段发送
int leng = fb->len;
int timess = leng/maxcache;
int extra = leng%maxcache;
for(int j = 0;j< timess;j++)
{
client.write(fb->buf, maxcache);
for(int i =0;i< maxcache;i++)
{
fb->buf++;
}
}
client.write(fb->buf, extra);
client.print("Frame Over"); // 一张图片的结束标志
Serial.print("This Frame Length:");
Serial.print(fb->len);
Serial.println(".Succes To Send Image For TCP!");
//return the frame buffer back to the driver for reuse
fb->buf = temp; //将当时保存的指针重新返还
esp_camera_fb_return(fb); //这一步在发送完毕后要执行,具体作用还未可知。
}
delay(30);//短暂延时 增加数据传输可靠性
}
/*
while (client.connected() || client.available()) //如果已连接或有收到的未读取的数据
{
if (client.available()) //如果有数据可读取
{
String line = client.readStringUntil('\n'); //读取数据到换行符
Serial.print("ReceiveData:");
Serial.println(line);
client.print("--From ESP32--:Hello Server!");
}
}
Serial.println("close connect!");
client.stop(); //关闭客户端
*/
}
else
{
Serial.println("Connect To Tcp Server Failed!After 10 Seconds Try Again!");
client.stop(); //关闭客户端
}
delay(10000);
}
客户端代码, Python编写:
import socket
import threading
import time
import numpy as np
import cv2
begin_data = b'Frame Begin'
end_data = b'Frame Over'
# 接收数据
# ESP32发送一张照片的流程
# 先发送Frame Begin 表示开始发送图片 然后将图片数据分包发送 每次发送1430 余数最后发送
# 完毕后发送结束标志 Frame Over 表示一张图片发送完毕
# 1430 来自ESP32cam发送的一个包大小为1430 接收到数据 data格式为b''
def handle_sock(sock, addr):
temp_data = b''
t1 = int(round(time.time() * 1000))
while True:
data = sock.recv(1430)
# 如果这一帧数据包的开头是 b'Frame Begin' 则是一张图片的开始
if data[0:len(begin_data)] == begin_data:
# 将这一帧数据包的开始标志信息(b'Frame Begin')清除 因为他不属于图片数据
data = data[len(begin_data):len(data)]
# 判断这一帧数据流是不是最后一个帧 最后一针数据的结尾时b'Frame Over'
while data[-len(end_data):] != end_data:
temp_data = temp_data + data # 不是结束的包 将数据添加进temp_data
data = sock.recv(1430) # 继续接受数据 直到接受的数据包包含b'Frame Over' 表示是这张图片的最后一针
# 判断为最后一个包 将数据去除 结束标志信息 b'Frame Over'
temp_data = temp_data + data[0:(len(data) - len(end_data))] # 将多余的(\r\nFrame Over)去掉 其他放入temp_data
# 显示图片
receive_data = np.frombuffer(temp_data, dtype='uint8') # 将获取到的字符流数据转换成1维数组
r_img = cv2.imdecode(receive_data, cv2.IMREAD_COLOR) # 将数组解码成图像
r_img = r_img.reshape(480, 640, 3)
t2 = int(round(time.time() * 1000))
fps = 1000 // (t2 - t1)
cv2.putText(r_img, "FPS" + str(fps), (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
cv2.imshow('server_frame', r_img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
t1 = t2
# print("接收到的数据包大小:" + str(len(temp_data))) # 显示该张照片数据大小
temp_data = b'' # 清空数据 便于下一章照片使用
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.connect(("xxxx", xxxx))
server.send('monitor'.encode("utf-8"))
# server.bind(('0.0.0.0', 9090))
# server.listen(5)
# CONNECTION_LIST = []
# 主线程循环接收客户端连接
handle_sock(server, '') # mac电脑直接在主线程运行
# while True:
# sock, addr = server.accept()
# CONNECTION_LIST.append(sock)
# print('Connect--{}'.format(addr))
# 连接成功后开一个线程用于处理客户端
# handle_sock(server, '') # mac电脑直接在主线程运行
# client_thread = threading.Thread(target=handle_sock, args=(sock, addr))
# client_thread.start()
这里做一下特别说明, 在python代码的最后主线程那里, 由于我是运行在Mac电脑中, 所以不能开新的线程, 否则会报错, 如果你是运行在Windows电脑下, 请把注释打开, 创建新线程来运行.
下面是nodejs写的服务器端代码, 用于视频数据流的透传转发:
// 创建tcp连接服务
const net = require('net')
const { size } = require('underscore')
const HOST = 'xxxx'
const PORT = xxxx
// 创建udp
// const dgram = require("dgram")
// const server = dgram.createSocket("udp4")
// 统计连接客户端的个数
var count = 0
// 创建slave_server服务
const slave_server = new net.createServer()
// slave_server.setEncoding = 'UTF-8'
// 保存监视器的socket
var s
// 获得一个连接,该链接自动关联scoket对象
var tcp_sock = null
slave_server.on('connection', sock => {
tcp_sock = sock
sock.name = ++count
console.log(`当前连接客户端数:${count}`)
// 接收client发来的信息
sock.on('data', data => {
// console.log(`客户端${sock.name}发来一个信息:${data}`)
// 判断是否为监视器发来的链接
if (data == 'monitor') {
// 则把当前的socket保存起来
s = sock
} else {
s.write(data)
}
})
// 为socket添加error事件处理函数
sock.on('error', error => { //监听客户端异常
console.log('error' + error)
sock.end()
})
// 服务器关闭时触发,如果存在连接,这个事件不会被触发,直到所有的连接关闭
sock.on('close', () => {
console.log(`客户端${sock.name}下线了`)
count -= 1
})
})
// listen函数开始监听指定端口
slave_server.listen(PORT, () => {
console.log(`服务器已启动,运行在:http://xxxx:xxxx`)
})
再次特别说明一下, 先启动服务器端代码, 然后在电脑端运行python端代码, 最后再烧录esp32-cam的代码, 第一次运行时电脑客户端可能报错直接就退出了, 没关系, 重新运行一下电脑客户端的python代码, 就能看到视频图像了. 我在服务器端的逻辑是接收到'monitor'字符串时认为是客户端监视器, 把对应的socket保存一下, 然后当接收到下一个socket连接时, 则默认认为是esp32-cam, 将之前保存的socket取出来, 把视频流数据转发出去, 此种方式只能有一个esp32-cam和一个客户端监视器, 有需要的小伙伴可以自行修改代码.
ESP32-CAM 摄像头开发板 进行 推流 已解决
项目背景,自己喜欢折腾电子。对嘉立创打板子。非常热爱。 购买了n多个电子模块 都在这儿进行一一展示,
配置参数
祖传代码
#include "esp_camera.h"
#include <WiFi.h>
//
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
// Ensure ESP32 Wrover Module or other board with PSRAM is selected
// Partial images will be transmitted if image exceeds buffer size
//
// Select camera model
//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
#include "camera_pins.h"
const char* ssid = "*****";
const char* password = "******";
void startCameraServer();
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
#if defined(CAMERA_MODEL_ESP_EYE)
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
#endif
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t * s = esp_camera_sensor_get();
// initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_vflip(s, 1); // flip it back
s->set_brightness(s, 1); // up the brightness just a bit
s->set_saturation(s, -2); // lower the saturation
}
// drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA);
#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
s->set_vflip(s, 1);
s->set_hmirror(s, 1);
#endif
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
startCameraServer();
Serial.print("Camera Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.println("' to connect");
}
void loop() {
// put your main code here, to run repeatedly:
delay(10000);
}
出现问题 三种情况
1、问题一
解决办法 、出现这个问题。 在出现 connectiing 的时候。按住按键
问题二
解决办法 设置 比特率
是由于ESP32的开发版下载速度太大出的问题,把下载速度设为460800波特率或者更低试试
问题三
串口窗口乱码问题。 设置波特率 115200
传输完毕
开机 在一定时间按一下 那个 boo键。就能正常使用
出现 wifi连接地址 进行简单配置
浏览器输入 192.168.31.9 进行 简单配置。进行简单推流
购买建议
由于自己喜欢玩,一下买了两个
右面镜头比左面镜头清晰, 建议购买 右面产品。 摄像头参数 分别
左面:AF2569C927*8 右面:DC-26 40-V3
购买截图。 没记错右面专门选的摄像头是高清的。
记录完毕。 接下里会根据 安立信芯片 画一个班子。 做一个属于自己的监控小工具。 配合小程序 可以进行 远程查看
保姆级教程手把手教你使用Arduino开发ESP32
Arduino简单,易学,好用,资料全,特别适合小白新手上路。但是最初ARDUINO使用 ATMEL的51单片机同级别低配芯片,频率限制了我的想象力!
ESP32频率高,价格低,内置蓝牙WIFI,支持语音识别LVGL等,是国产芯片中神一样的存在,大有取代STM32的趋势,再不学就晚了。但是原生的ESP_IDF确实对新手不太友好。还没开始写代码,就需要整开发环境,搞FREERTOS,头疼头疼。
ESP32+ARDUINO强强联手,价格便宜,功能强大,界面友好,创意多多。接下来自己挖坑自己填,用ESP32+ARDUINO平台,完成一些好玩的东西。边学边玩边输出,不亦乐乎!
下载开发环境
官网连接入选:
https://www.arduino.cc/en/software
完成下载后,直接解压即可运行。
也可以从百度网盘下载:
链接:https://pan.baidu.com/s/1vmKzPDiy4NKpyVISvsCSNg
提取码:4dp8
安装开发环境
发现这里没有ESP32开发板,下面进行ESP32开发环境安装。
进入首选项。
这里输入ESP32 ARDUINO的网址。
https://dl.espressif.com/dl/package_esp32_index.json
然后再次进入工具->开发板->开发板管理器
安装需要一端时间。
下载时候往往会出错。如果出错,多试几次即可。实在不行也可以用手机热点来试试。多试几次基本上都可以安装成功。
《ESP32-S2 笔记》 ARDUINO 上的开发环境搭建
ESP32-S2 是一款安全可靠的低功耗、高集成 2.4 GHz Wi-Fi 系统级芯片 (SoC),支持 Wi-Fi HT40 和多达 43 个 GPIO。ESP32-S2 搭载 Xtensa? 32-bit LX7 单核处理器,工作时钟频率高达 240 MHz。
ESP32-S2 具有行业领先的低功耗管理与射频性能、IO 功能和安全性能,是物联网、移动设备、可穿戴电子设备、智能家居等各种应用的理想选择。ESP32-S2 集成了 240 MHz Xtensa? 单核处理器,无需外接任何 MCU 即可独立满足各种互联设备的需求。
ESP32-S2 仍然沿用了乐鑫已经非常成熟的软件开发框架 ESP-IDF,可以实现性能和成本的平衡,为市场带来更高速、更安全的物联网连接解决方案。
我选用的ESP32-S2板卡为下图,它将USB和TTL下载都用Type-C接口引出,用来调试ESP32-S2的USB功能也是非常的方便:
从Github上下载 arduino-esp32 的 esp32s2 分支:【地址】,下载完成后进行解压。
进行解压后,打开文件夹,可以发现目录如下:
然后进入tools
目录,目录如下:
双击get.exe
文件会弹出命令窗口,等待上几分钟,下载完成后窗口会自动关闭。
ARDUINO
再将板卡支持包放入Arduino
之前,首先你之前安装过ESP32的开发环境, 如果没有安装过,请跟着流程走:
ESP32
的板卡包【地址】,解压后并按照下图目录放好,并且复制一份,并修改文件名为arduino-esp32-esp32s2
,其中红色框出来的为你的Arduino
软件的安装目录,如果不懂什么是安装目录,请自行搜索:D:\Arduino\hardware\espressif\arduino-esp32-esp32s2
目录,并将刚2.2
步骤的文件夹下蓝色标记出来的文件拷贝到此目录中,如果弹出来 需要替换文件 ,点击全部替换即可,如果按照以上方式来的话,应该不会弹出。我的板子上有一个2020
封装的WS2812
灯珠,根据板子原理图查到引脚为WS2812->IO18
,现在使用【FastLED
】库点亮板载灯试试,先查看IO口的宏定义,其文件在D:\Arduino\hardware\espressif\arduino-esp32-esp32s2\variants\esp32s2\pins_arduino.h
:
然后打开Arduino
软件,选择开发板:
从上表查到IO18
的定义为:
因此我们需要在程序中使用A17
或者DAC2
来作为WS2812
的输出口:
点击上传程序,等待程序上传成功,即可发现LED布灵布灵
的闪起来了呢:
到此我们的安装过程已经完成,可以进行ESP32-S2的开发了,但是现在很多库还没有对ESP32-S2进行适配,比如我尝试了下u8g2库去驱动OLED,就没有成功,因此还是有很多问题的哦,期待各开源库的完善~
在Arduino IDE中安装固件ESP32,ESP8266板
1.在Arduino IDE中 文件 —>首选项
2.在“板管理器的附加链接”中添加2行或中间加逗号
https://dl.espressif.com/dl/package_esp32_index.json
http://arduino.esp8266.com/stable/package_esp8266com_index.json
添加更多板型可看这个网址https://github.com/arduino/Arduino/wiki/Unofficial-list-of-3rd-party-boards-support-urls
3.添加Arduino控制板,通过 工具>开发板>开发板管理器... 就可以打开 开发板管理器
4.在搜索栏输入型号 如esp32 点击安装
就可以选择相应的板子工作了
如何通过 Arduino IDE 玩转乐鑫 ESP32 模块
如何实现 ESP32 固件的 OTA 在线升级更新
1、背景
在实际产品开发过程中,在线升级可以远程解决产品软件开发引入的问题,更好地满足用户需求。
2、OTA 简介
OTA(空中)更新是使用 Wi-Fi 连接而不是串行端口将固件加载到 ESP 模块的过程。
2.1、ESP32 的 OTA 升级有三种方式:
Arduino IDE:主要用于软件开发阶段,实现不接线固件烧写
Web Browser:通过 Web 浏览器手动提供应用程序更新模块
HTTP Server:自动使用http服务器 - 针对产品应用
在三种升级情况下,必须通过串行端口完成第一个固件上传。
OTA 进程没有强加的安全性,需要确保开发人员只能从合法/受信任的来源获得更新。更新完成后,模块将重新启动,并执行新的代码。开发人员应确保在模块上运行的应用程序以安全的方式关闭并重新启动。
2.2、保密性 Security
模块必须以无线方式显示,以便通过新的草图进行更新。 这使得模块被强行入侵并加载了其他代码。 为了减少被黑客入侵的可能性,请考虑使用密码保护您的上传,选择某些OTA端口等。
可以提高安全性的 ArduinoOTA 库接口:
void setPort(uint16_t port);
void setHostname(const char* hostname);
void setPassword(const char* password);
void onStart(OTA_CALLBACK(fn));
void onEnd(OTA_CALLBACK(fn));
void onProgress(OTA_CALLBACK_PROGRESS(fn));
void onError(OTA_CALLBACK_ERROR (fn));
已经内置了某些保护功能,不需要开发人员进行任何其他编码。ArduinoOTA和espota.py使用Digest-MD5来验证上传。使用MD5校验和,在ESP端验证传输数据的完整性。
2.2、OTA 升级策略 - 针对 http
ESP32 连接 HTTP 服务器,发送请求 Get 升级固件;每次读取1KB固件数据,写入Flash。
ESP32 SPI Flash 内有与升级相关的(至少)四个分区:OTA data、Factory App、OTA_0、OTA_1。其中 FactoryApp 内存有出厂时的默认固件。
首次进行 OTA 升级时,OTA Demo 向 OTA_0 分区烧录目标固件,并在烧录完成后,更新 OTA data 分区数据并重启。
系统重启时获取 OTA data 分区数据进行计算,决定此后加载 OTA_0 分区的固件执行(而不是默认的 Factory App 分区内的固件),从而实现升级。
同理,若某次升级后 ESP32 已经在执行 OTA_0 内的固件,此时再升级时 OTA Demo 就会向 OTA_1 分区写入目标固件。再次启动后,执行 OTA_1 分区实现升级。以此类推,升级的目标固件始终在 OTA_0、OTA_1 两个分区之间交互烧录,不会影响到出厂时的 Factory App 固件。
3、OTA 实例解析
3,1、Arduino IDE 方案固件更新
从 Arduino IDE 无线上传模块适用于以下典型场景:
在固件开发过程中,通过串行加载更快的替代方案 - 用于更新少量模块,只有模块在与 Arduino IDE 的计算机相同的网络上可用。
参考实例:
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "..........";
const char* password = "..........";
void setup() {
Serial.begin(115200);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// Port defaults to 3232
// ArduinoOTA.setPort(3232);
// Hostname defaults to esp3232-[MAC]
// ArduinoOTA.setHostname("myesp32");
// 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;
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()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
ArduinoOTA.handle();
}
3,2、Web Browser 方案固件更新
该方案使用场景:
直接从 Arduino IDE 加载是不方便或不可能的
用户无法从外部更新服务器公开 OTA 的模块
在设置更新服务器不可行时,将部署后的更新提供给少量模块
参考实例:
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
const char* host = "esp32";
const char* ssid = "xxx";
const char* password = "xxxx";
WebServer server(80);
/*
* Login page
*/
const char* loginIndex =
"<form name='loginForm'>"
"<table width='20%' bgcolor='A09F9F' align='center'>"
"<tr>"
"<td colspan=2>"
"<center><font size=4><b>ESP32 Login Page</b></font></center>"
"<br>"
"</td>"
"<br>"
"<br>"
"</tr>"
"<td>Username:</td>"
"<td><input type='text' size=25 name='userid'><br></td>"
"</tr>"
"<br>"
"<br>"
"<tr>"
"<td>Password:</td>"
"<td><input type='Password' size=25 name='pwd'><br></td>"
"<br>"
"<br>"
"</tr>"
"<tr>"
"<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
"</tr>"
"</table>"
"</form>"
"<script>"
"function check(form)"
"{"
"if(form.userid.value=='admin' && form.pwd.value=='admin')"
"{"
"window.open('/serverIndex')"
"}"
"else"
"{"
" alert('Error Password or Username')/*displays error message*/"
"}"
"}"
"</script>";
/*
* Server Index Page
*/
const char* serverIndex =
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
"<input type='file' name='update'>"
"<input type='submit' value='Update'>"
"</form>"
"<div id='prg'>progress: 0%</div>"
"<script>"
"$('form').submit(function(e){"
"e.preventDefault();"
"var form = $('#upload_form')[0];"
"var data = new FormData(form);"
" $.ajax({"
"url: '/update',"
"type: 'POST',"
"data: data,"
"contentType: false,"
"processData:false,"
"xhr: function() {"
"var xhr = new window.XMLHttpRequest();"
"xhr.upload.addEventListener('progress', function(evt) {"
"if (evt.lengthComputable) {"
"var per = evt.loaded / evt.total;"
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!')"
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>";
/*
* setup function
*/
void setup(void) {
Serial.begin(115200);
// Connect to WiFi network
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
/*use mdns for host name resolution*/
if (!MDNS.begin(host)) { //http://esp32.local
Serial.println("Error setting up MDNS responder!");
while (1) {
delay(1000);
}
}
Serial.println("mDNS responder started");
/*return index page which is stored in serverIndex */
server.on("/", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", loginIndex);
});
server.on("/serverIndex", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
/*handling uploading firmware file */
server.on("/update", HTTP_POST, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
ESP.restart();
}, []() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.printf("Update: %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
/* flashing firmware to ESP*/
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) { //true to set the size to the current progress
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
Update.printError(Serial);
}
}
});
server.begin();
}
void loop(void) {
server.handleClient();
delay(1);
}
3.3、HTTP 服务器实现更新
ESPhttpUpdate 类可以检查更新并从 HTTP Web 服务器下载二进制文件。可以从网络或 Internet 上的每个 IP 或域名地址下载更新,主要应用于远程服务器更新升级。
参考实例:
/**
AWS S3 OTA Update
Date: 14th June 2017
Author: Arvind Ravulavaru <https://github.com/arvindr21>
Purpose: Perform an OTA update from a bin located in Amazon S3 (HTTP Only)
Upload:
Step 1 : Download the sample bin file from the examples folder
Step 2 : Upload it to your Amazon S3 account, in a bucket of your choice
Step 3 : Once uploaded, inside S3, select the bin file >> More (button on top of the file list) >> Make Public
Step 4 : You S3 URL => http://bucket-name.s3.ap-south-1.amazonaws.com/sketch-name.ino.bin
Step 5 : Build the above URL and fire it either in your browser or curl it `curl -I -v http://bucket-name.ap-south-1.amazonaws.com/sketch-name.ino.bin` to validate the same
Step 6: Plug in your SSID, Password, S3 Host and Bin file below
Build & upload
Step 1 : Menu > Sketch > Export Compiled Library. The bin file will be saved in the sketch folder (Menu > Sketch > Show Sketch folder)
Step 2 : Upload bin to S3 and continue the above process
// Check the bottom of this sketch for sample serial monitor log, during and after successful OTA Update
*/
#include <WiFi.h>
#include <Update.h>
WiFiClient client;
// Variables to validate
// response from S3
int contentLength = 0;
bool isValidContentType = false;
// Your SSID and PSWD that the chip needs
// to connect to
const char* SSID = "YOUR-SSID";
const char* PSWD = "YOUR-SSID-PSWD";
// S3 Bucket Config
String host = "bucket-name.s3.ap-south-1.amazonaws.com"; // Host => bucket-name.s3.region.amazonaws.com
int port = 80; // Non https. For HTTPS 443. As of today, HTTPS doesn't work.
String bin = "/sketch-name.ino.bin"; // bin file name with a slash in front.
// Utility to extract header value from headers
String getHeaderValue(String header, String headerName) {
return header.substring(strlen(headerName.c_str()));
}
// OTA Logic
void execOTA() {
Serial.println("Connecting to: " + String(host));
// Connect to S3
if (client.connect(host.c_str(), port)) {
// Connection Succeed.
// Fecthing the bin
Serial.println("Fetching Bin: " + String(bin));
// Get the contents of the bin file
client.print(String("GET ") + bin + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Cache-Control: no-cache\r\n" +
"Connection: close\r\n\r\n");
// Check what is being sent
// Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" +
// "Host: " + host + "\r\n" +
// "Cache-Control: no-cache\r\n" +
// "Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println("Client Timeout !");
client.stop();
return;
}
}
// Once the response is available,
// check stuff
/*
Response Structure
HTTP/1.1 200 OK
x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0=
x-amz-request-id: 2D56B47560B764EC
Date: Wed, 14 Jun 2017 03:33:59 GMT
Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT
ETag: "d2afebbaaebc38cd669ce36727152af9"
Accept-Ranges: bytes
Content-Type: application/octet-stream
Content-Length: 357280
Server: AmazonS3
{{BIN FILE CONTENTS}}
*/
while (client.available()) {
// read line till /n
String line = client.readStringUntil('\n');
// remove space, to check if the line is end of headers
line.trim();
// if the the line is empty,
// this is end of headers
// break the while and feed the
// remaining `client` to the
// Update.writeStream();
if (!line.length()) {
//headers ended
break; // and get the OTA started
}
// Check if the HTTP Response is 200
// else break and Exit Update
if (line.startsWith("HTTP/1.1")) {
if (line.indexOf("200") < 0) {
Serial.println("Got a non 200 status code from server. Exiting OTA Update.");
break;
}
}
// extract headers here
// Start with content length
if (line.startsWith("Content-Length: ")) {
contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str());
Serial.println("Got " + String(contentLength) + " bytes from server");
}
// Next, the content type
if (line.startsWith("Content-Type: ")) {
String contentType = getHeaderValue(line, "Content-Type: ");
Serial.println("Got " + contentType + " payload.");
if (contentType == "application/octet-stream") {
isValidContentType = true;
}
}
}
} else {
// Connect to S3 failed
// May be try?
// Probably a choppy network?
Serial.println("Connection to " + String(host) + " failed. Please check your setup");
// retry??
// execOTA();
}
// Check what is the contentLength and if content type is `application/octet-stream`
Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType));
// check contentLength and content type
if (contentLength && isValidContentType) {
// Check if there is enough to OTA Update
bool canBegin = Update.begin(contentLength);
// If yes, begin
if (canBegin) {
Serial.println("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!");
// No activity would appear on the Serial monitor
// So be patient. This may take 2 - 5mins to complete
size_t written = Update.writeStream(client);
if (written == contentLength) {
Serial.println("Written : " + String(written) + " successfully");
} else {
Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?" );
// retry??
// execOTA();
}
if (Update.end()) {
Serial.println("OTA done!");
if (Update.isFinished()) {
Serial.println("Update successfully completed. Rebooting.");
ESP.restart();
} else {
Serial.println("Update not finished? Something went wrong!");
}
} else {
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
}
} else {
// not enough space to begin OTA
// Understand the partitions and
// space availability
Serial.println("Not enough space to begin OTA");
client.flush();
}
} else {
Serial.println("There was no content in the response");
client.flush();
}
}
void setup() {
//Begin Serial
Serial.begin(115200);
delay(10);
Serial.println("Connecting to " + String(SSID));
// Connect to provided SSID and PSWD
WiFi.begin(SSID, PSWD);
// Wait for connection to establish
while (WiFi.status() != WL_CONNECTED) {
Serial.print("."); // Keep the serial monitor lit!
delay(500);
}
// Connection Succeed
Serial.println("");
Serial.println("Connected to " + String(SSID));
// Execute OTA Update
execOTA();
}
void loop() {
// chill
}
/*
* Serial Monitor log for this sketch
*
* If the OTA succeeded, it would load the preference sketch, with a small modification. i.e.
* Print `OTA Update succeeded!! This is an example sketch : Preferences > StartCounter`
* And then keeps on restarting every 10 seconds, updating the preferences
*
*
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0x00
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0008,len:8
load:0x3fff0010,len:160
load:0x40078000,len:10632
load:0x40080000,len:252
entry 0x40080034
Connecting to SSID
......
Connected to SSID
Connecting to: bucket-name.s3.ap-south-1.amazonaws.com
Fetching Bin: /StartCounter.ino.bin
Got application/octet-stream payload.
Got 357280 bytes from server
contentLength : 357280, isValidContentType : 1
Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!
Written : 357280 successfully
OTA done!
Update successfully completed. Rebooting.
ets Jun 8 2016 00:22:57
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0x00
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0008,len:8
load:0x3fff0010,len:160
load:0x40078000,len:10632
load:0x40080000,len:252
entry 0x40080034
OTA Update succeeded!! This is an example sketch : Preferences > StartCounter
Current counter value: 1
Restarting in 10 seconds...
E (102534) wifi: esp_wifi_stop 802 wifi is not init
ets Jun 8 2016 00:22:57
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0x00
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0008,len:8
load:0x3fff0010,len:160
load:0x40078000,len:10632
load:0x40080000,len:252
entry 0x40080034
OTA Update succeeded!! This is an example sketch : Preferences > StartCounter
Current counter value: 2
Restarting in 10 seconds...
....
*
*/
ESP32 VS ESP8266
ESP32和ESP8266是性价比非常高的Wi-Fi模块,非常适合物联网(IoT)领域的DIY项目。
两款芯片都属于32位处理器。ESP32是双核160MHz至240MHz CPU,而ESP8266是单核处理器,运行频率为80MHz。
这些WiFi模块都带有GPIO,支持SPI,I2C,UART等各种协议。最好的部分是它们带有无线网络,这使得它们与Arduino等其他微控制器区别开来。这意味着你可以通过Wi-Fi或蓝牙(ESP32)以极低的价格轻松地远程控制和监控设备。
规格:ESP32 vs ESP8266
该ESP32是ESP8266继任者。它增加了额外的CPU内核,更快的Wi-Fi,更多的GPIO,并支持蓝牙4.2和蓝牙低功耗。此外,ESP32配有触摸感应针脚,可用于唤醒ESP32深度睡眠,内置霍尔效应传感器和内置温度传感器。
两块板都非常便宜,但ESP32的成本略高。虽然ESP32的售价大约在6美元到12美元之间,但ESP8266的售价却在4美元到6美元之间(但这取决于你获得它们的位置)。
ESP32上有更多GPIO
该ESP32有更多的GPIO比ESP8266,你可以决定哪些引脚UART,I2C,SPI -你只需要设置的代码。这是可能的,因为ESP32芯片的多路复用功能允许你为同一个引脚分配多个功能。
如果你没有在代码上设置它们,它们将位于默认定义的引脚上,如下图所示(这是ESP32 DEVKIT V1 DOIT板的示例 - 引脚位置可能因制造商而异)。
PWM,ADC等
你可以在任何GPIO中设置PWM信号,并在代码上设置可配置的频率和占空比。
对于模拟引脚,这些是静态的,但ESP32支持18个通道(模拟使能引脚)上的测量,而ESP8266只有一个10位ADC引脚。ESP32还支持两个8位DAC通道。
此外,ESP32包含10个电容感应GPIO,可检测触摸并可用于触发事件,或唤醒ESP32深度睡眠。
Arduino IDE - ESP32 vs ESP8266
ESP32和ESP8266板的编程方法有很多种。目前,可以使用Arduino IDE编程环境对两个板进行编程。
这是一件好事,特别适合那些习惯Arduino编程并熟悉Arduino编程语言的人。
使用Arduino IDE开始使用ESP32或ESP8266并让你的第一个项目运行非常简单。网上有非常多的指南操作:比如在Arduino IDE中安装ESP32板(Windows说明);在Arduino IDE中安装ESP32板(Mac和Linux说明书);如何在Arduino IDE中安装ESP8266板等。
虽然你可以使用Arduino IDE对两个板进行编程,但它们可能与相同的库和命令不兼容。有些库只与其中一个板兼容。这意味着大多数时候你的ESP8266代码与ESP32不兼容。但是,通常你只需要进行一些修改。
MicroPython Firwmare - ESP32 vs ESP8266
另一种对ESP32和ESP8266板进行编程的流行方法是使用MicroPython固件。
MicroPython是针对微控制器和嵌入式系统的Python 3的重新实现。MicroPython与常规Python非常相似。因此,如果你已经知道如何使用Python编程,那么你也知道如何在MicroPython中编程。
在MicroPython中,大多数Python脚本都与两个板兼容(与使用Arduino IDE时不同)。这意味着大多数时候你可以为ESP32和ESP8266使用相同的脚本。
所以,此时你应该问: 我应该买ESP8266还是ESP32?
这真的取决于你想做什么。两块板都有使用的价值,两者都有利有弊。
ESP8266比ESP32便宜。虽然它没有那么多的功能,但对大多数简单的DIY物联网项目来说效果都不错。此外,由于它“较旧”,因此在软件方面受到更多支持,你可能会更容易找到帮助。但是,它在GPIO映射方面有一些限制,它可能没有足够的引脚用于你打算执行的操作。如果是这种情况,你应该使用ESP32。
ESP32比ESP8266 功能更强大,包含更多具有多种功能的GPIO,更快的Wi-Fi,并且还支持蓝牙。很多人认为ESP32比ESP8266更难处理,因为它更复杂。在我们看来,ESP32编程与ESP8266一样容易,特别是如果你打算用Arduino语言或MicroPython编程它。
另一方面,ESP32也有一些缺点。首先,它比ESP8266贵。因此,如果你正在制作一个简单的物联网项目,那么ESP8266可能会以更低的价格成功。此外,由于它比ESP8266更新,并非所有软件都是为了充分利用ESP32功能而开发的,因此支持更少,错误更多。但从长远来看,这些问题将得到解决,两个开发板都将有各自应用的空间。
我的个人经历:在2019年,我几乎完全使用ESP32进行物联网项目。它功能更多,并且具有更多功能,如蓝牙,不同的唤醒源,许多外围设备等等。此外,在我看来,价格的差异并不是什么大问题。我想,一旦你移植到ESP32,你就不会想回到ESP8266了。
ESP32的几种简单固件烧录和编译环境的安装
接下来去乐鑫的github网站上(https://github.com/espressif)下载arduino_esp32的库文件
https://github.com/espressif/arduino-esp32.git
把下载的开发板资料解压到C:\Program Files (x86)\Arduino\hardware中。我自己是C:\Program Files (x86)\Arduino\hardware\espressif\esp32
记得把C:\Program Files (x86)\Arduino\hardware\espressif\esp32\libraries中的AzureIoT文件夹删除,因为这个文件夹为空,导致arduino报错。
选择例程
编译一下
用ARDUINO进行编译:
打开arduino,在文件->首选项->开发板管理网址中填入https://dl.espressif.com/dl/package_esp32_index.json,然后工具>开发板>开发板管理器中输入esp32,点击下载,下载完就可以用了
最后在工具->开发板选择正确的esp32板子型号和你的串口,一般是ESP32 Dev Module。下载程序时要按一下RST按键。
玩转 ESP32 + Arduino (十) WIFI(AP模式) (STA模式) (smartConfig)
玩转 ESP32 + Arduino (十) WIFI(AP模式) (STA模式) (smartConfig)
1. AP
AP,也就是无线接入点,是一个无线网络的创建者,是网络的中心节点。一般家庭或办公室使用的无线路由器就一个AP。
2. STA
STA是Station的简称,类似于无线终端,STA本身并不接受无线的接入,它可以连接到AP,简单来说就是和手机连接WIFI热点的工作状态相同,可以连接其它的热点。
3. AP混合STA
了解了前两个概念,AP混合STA模式就不难理解了,就是既可以连接到其它的WIFI热点,也可以让别的无线终端连接,这两个过程能同时进行。
二. 切换模式相关API
1. 设置WIFI模式
WiFi.mode(WIFI_MODE_AP);
模式定义如下:
typedef enum {
WIFI_MODE_NULL = 0, /**< null mode */
WIFI_MODE_STA, /**< WiFi station mode */
WIFI_MODE_AP, /**< WiFi soft-AP mode */
WIFI_MODE_APSTA, /**< WiFi station + soft-AP mode */
WIFI_MODE_MAX
} wifi_mode_t;
2. 获取WIFI工作模式
WiFi.getMode();
3. 使能/使能STA模式
WiFi.enableSTA(true);
4. 使能/使能AP模式
WiFi.enableAP(true);
5. 使能/使能Wifi休眠
WiFi.setSleep(true);
仅限STA模式, 默认开启休眠, 如果对实时性要求高,应关闭休眠
三. AP热点
1. WIFI AP热点建立 WiFisoftAP()
bool WiFiAPClass::softAP(const char *ssid, const char *passphrase = (const char *)__null, int channel = 1, int ssid_hidden = 0, int max_connection = 4)
功能:建立AP热点
参数:
ssid – WIFI名称SSID (max 63 char).
passphrase – WIFI密码(for WPA2 min 8 char, for open use NULL)
channel – WiFi 信道, 1 - 13.(默认1)
ssid_hidden – 是否隐藏WIFI名 (0 = broadcast SSID, 1 = hide SSID) 默认0,不隐藏
max_connection – 最大可连接数, 1 - 4. 默认4
2. 配置AP 热点 WiFi.softAPConfig();
bool WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet)
功能: 配置AP 热点
参数:
local_ip – 本地IP 192.168.4.1
gateway – 网关IP 192.168.4.1
subnet – 子网掩码 255.255.255.0
3. 断开wifi WiFi.disconnect();
bool WiFiSTAClass::disconnect(bool wifioff = false, bool eraseap = false)
功能: 断开AP热点
参数:
wifioff : 为true则还将关闭网络功能, 默认false
eraseap : 为true则清空AP热点配置信息, 默认false
返回:
返回wlan状态码
4. 一些获取AP热点信息的API
#include <Arduino.h>
#include "WiFi.h"
void setup()
{
Serial.begin(115200);
WiFi.softAP("ESP_AP", "12345678");
}
void loop()
{
Serial.print("主机名:");
Serial.println(WiFi.softAPgetHostname());
Serial.print("主机IP:");
Serial.println(WiFi.softAPIP());
Serial.print("主机IPV6:");
Serial.println(WiFi.softAPIPv6());
Serial.print("主机SSID:");
Serial.println(WiFi.SSID());
Serial.print("主机广播IP:");
Serial.println(WiFi.softAPBroadcastIP());
Serial.print("主机mac地址:");
Serial.println(WiFi.softAPmacAddress());
Serial.print("主机连接个数:");
Serial.println(WiFi.softAPgetStationNum());
Serial.print("主机网络ID:");
Serial.println(WiFi.softAPNetworkID());
Serial.print("主机状态:");
Serial.println(WiFi.status());
delay(1000);
}
5. 设置主机名称 WiFi.softAPsetHostname("ESP_xiongba");
bool WiFiAPClass::softAPsetHostname(const char *hostname)
Set the softAP interface Host name.
参数:
hostname – pointer to const string
返回:
true on success
四. STA模式
1. 开始wifi连接 WiFi.begin()
wl_status_t WiFiSTAClass::begin(const char *ssid, const char *passphrase = (const char *)__null, int32_t channel = 0, const uint8_t *bssid = (const uint8_t *)__null, bool connect = true)
还有 2 个重载
启动Wifi连接如果设置了密码短语,将自动选择最安全的支持模式
参数:
ssid – WIFI名, const char* Pointer to the SSID string.
passphrase – 密码, const char * Optional. Passphrase. Valid characters in a passphrase must be between ASCII 32-126 (decimal).
channel – (AP的通道, 可选参数)Optional. Channel of AP
bssid – BSSID是指站点的MAC地址(可选参数) uint8_t[6] Optional. BSSID / MAC of AP
connect – (是否连接此WIFI, 可选参数)Optional. call connect
WiFi.begin("anny", "20141208");
while (!WiFi.isConnected())
{
delay(500);
Serial.print(".");
}
Serial.println("WIFI连接成功!");
2. wifi配置
bool WiFiSTAClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0, IPAddress dns2 = (uint32_t)0)
Change IP configuration settings disabling the dhcp client
参数:
local_ip – 当地IP, Static ip configuration
gateway – 网关, Static gateway configuration
subnet – 子网掩码, Static Subnet mask
dns1 – DNS服务器1, Static DNS server 1
dns2 – DNS服务器2, Static DNS server 2
3. 关闭wifi连接
用法和AP的disconnect一样
bool WiFiSTAClass::disconnect(bool wifioff = false, bool eraseap = false)
Disconnect from the network
参数:
wifioff
返回:
one value of wl_status_t enum
4. 其他一些获取WIFI信息的API
#include <Arduino.h>
#include "WiFi.h"
void setup()
{
Serial.begin(115200);
// WiFi.softAP("ESP_AP", "12345678");
// WiFi.softAPsetHostname("ESP_xiongba");
WiFi.begin("anny", "20141208");
WiFi.setAutoReconnect(true);
}
void loop()
{
Serial.print("是否连接:");
Serial.println(WiFi.isConnected());
Serial.print("本地IP:");
Serial.println(WiFi.localIP());
Serial.print("本地IPv6:");
Serial.println(WiFi.localIPv6());
Serial.print("mac地址:");
Serial.println(WiFi.macAddress());
Serial.print("网络ID:");
Serial.println(WiFi.networkID());
Serial.print("休息:");
Serial.println(WiFi.getSleep());
Serial.print("获取状态吗:");
Serial.println(WiFi.getStatusBits());
Serial.print("getTxPower:");
Serial.println(WiFi.getTxPower());
Serial.print("是否自动连接:");
Serial.println(WiFi.getAutoConnect());
Serial.print("是否自动重连:");
Serial.println(WiFi.getAutoReconnect());
Serial.print("获取模式:");
Serial.println(WiFi.getMode());
Serial.print("获取主机名:");
Serial.println(WiFi.getHostname());
Serial.print("获取网关IP:");
Serial.println(WiFi.gatewayIP());
Serial.print("dnsIP:");
Serial.println(WiFi.dnsIP());
Serial.print("状态:");
Serial.println(WiFi.status());
delay(1000);
}
连接瞬间的状态变化
关于状态码, 请看下面解释:
5. 设置自动连接
bool WiFiSTAClass::setAutoReCoennect(bool autoConnect)
Setting the ESP32 station to connect to the AP (which is recorded)automatically or not when powered on. Enable auto-connect by default.
参数:
autoConnect – bool
返回:
if saved
6. 设置自动重连
设置WIFI断开后是否自动尝试重连
bool WiFiSTAClass::setAutoReconnect(bool autoReconnect)
五. 扫描WIFI
1. 扫描WIFI,返回扫描数量
这个功能默认在阻塞模式下运行,程序会扫描WIFI,期间什么事情都做不了, 可以通过参数设置改为异步模式.
int16_t WiFiScanClass::scanNetworks(bool async = false, bool show_hidden = false, bool passive = false, uint32_t max_ms_per_chan = 300U)
Start scan WiFi networks available
参数:
async – run in async mode
show_hidden – show hidden networks
返回:
Number of discovered networks
只返回了扫描到的WIFI的数量, 那结果怎么得到? 原来他已经将扫描到的WIFI存入了WIFI单例对应的数组中.
比如 SSID,都存入了 WiFi.SSID中,可以通过索引值遍历获取. 其他属性亦然.
2. 扫描连接WIFI(同步方式)
#include <Arduino.h>
#include "WiFi.h"
String ssid;
String password;
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
WiFi.disconnect(); //断开可能的连接
delay(100);
Serial.println("开始扫描WIFI:");
int n = WiFi.scanNetworks();
if (n)
{
Serial.print("扫描到");
Serial.print(n);
Serial.println("个WIFI");
for (size_t i = 0; i < n; i++)
{
Serial.print("WiFi的名称(SSID):");
Serial.println(WiFi.SSID(i));
Serial.print("WiFi的信号强度(RSSI):");
Serial.println(WiFi.RSSI(i));
Serial.print("WiFi加密与否:");
Serial.println(WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "未加密" : "加密");
if (WiFi.SSID(i) == "anny")
{
ssid = WiFi.SSID(i);
password = "20141208";
}
}
}
else
{
Serial.println("没扫描到WIFI");
}
WiFi.begin(ssid.c_str(), password.c_str());
while (!WiFi.isConnected())
{
Serial.print(".");
}
Serial.println("连接成功");
}
void loop()
{
}
3. 异步模式时,获取扫描完成信息 WiFi.scanComplete();
int16_t WiFiScanClass::scanComplete()
called to get the scan state in Async mode
返回:
scan result or status -1 if scan not fin -2 if scan not triggered
如果没用扫描完成, 则返回-1 , 如果根本没在扫描,返回-2
如果完成了扫描,返回扫描结果
4. 清除现有的扫描结果
WiFi.scanDelete();
5. 扫描连接WIFI(异步)
采用异步扫描方式, 每隔0.5秒询问一下WiFi.scanComplete();判断是否扫描完毕.
如果扫描完毕,置标志位,停定时器.
触发连接程序, 连接完成后
#include <Arduino.h>
#include "WiFi.h"
#include "Ticker.h"
String ssid = "";
String password = "";
Ticker t1;
void aysncScanHandler()
{
if (!WiFi.isConnected())
{
int wifiScanStatus = WiFi.scanComplete();
switch (wifiScanStatus)
{
case -1:
Serial.println("扫描中...");
break;
case -2:
Serial.println("未进行扫描...");
break;
default:
Serial.println("扫描完成");
for (size_t i = 0; i < wifiScanStatus; i++)
{
Serial.print("WiFi的名称(SSID):");
Serial.println(WiFi.SSID(i));
Serial.print("WiFi的信号强度(RSSI):");
Serial.println(WiFi.RSSI(i));
Serial.print("WiFi加密与否:");
Serial.println(WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "未加密" : "加密");
if (WiFi.SSID(i) == "anny")
{
ssid = WiFi.SSID(i);
password = "20141208";
};
}
WiFi.scanDelete();
t1.detach();
break;
}
}
}
void connectAnny()
{
WiFi.begin();
while (!WiFi.isConnected())
{
Serial.print(".");
}
Serial.println("连接成功");
}
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
WiFi.disconnect(); //断开可能的连接
delay(100);
Serial.println("开始扫描WIFI:");
WiFi.scanNetworks(true);
t1.attach(0.5, aysncScanHandler);
}
void loop()
{
if (!WiFi.isConnected())
{
if (ssid == "anny")
{
Serial.println(ssid);
connectAnny();
}
}
}
六. 智能配网
1. 什么是智能配网
智能配网这种快速连接方式,相对于 AP 模式连接简化操作,更加贴近于市场
1、手机连上 WiFi,打开智能硬件指定 APP 软件,进入配置界面,输入手机所在 WiFi 密码,请求配网 TOKEN
2、智能硬件开启混杂模式监听所有网络数据包
3、手机通过广播、组播循环发送 ssid/password/token
4、硬件设备通过 UDP 包(长度)获取配置信息捕捉到 ssid/password/token,报文后解码,得到正确的 SSID 和密码,然后主动连接指定 SSID 的路由完成连接。
2. 智能配网过程
进入智能配网模式后, 用手机微信扫描安信可官方二维码
输入WIFI密码
微信会广播这些信息,
ESP32获取后得知 WIFI名, 密码. 自动存储好. 自动连接WIFI
3. 示例, 智能配网过程
思路: 先自动联网20秒, 如果失败了进入智能配网阶段, 智能配网过程如上??
#include <Arduino.h>
#include "WiFi.h"
void mySmartWifiConfig()
{
WiFi.mode(WIFI_MODE_STA);
Serial.println("开启智能配网:");
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
delay(500);
if (WiFi.smartConfigDone())
{
Serial.println("配网成功");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.printf("PSW:%s", WiFi.psk().c_str());
break;
}
}
}
bool autoConfig()
{
WiFi.disconnect(true,true);
WiFi.begin();
for (size_t i = 0; i < 20; i++)
{
int wifiStatus = WiFi.status();
if (wifiStatus == WL_CONNECTED)
{
Serial.println("自动连接成功!");
return 1;
}
else
{
delay(1000);
Serial.println("等待自动配网中...");
}
}
Serial.println("无法自动配网!");
return 0;
}
void setup()
{
Serial.begin(115200);
delay(5000);
if (!autoConfig())
{
mySmartWifiConfig();
}
}
void loop()
{
if (WiFi.isConnected())
{
Serial.println("网络已连接");
delay(1000);
}
}
第一次连接时:
手机端也会同步显示:
第二次连接时:
ESP32-CAM开发板测试板WiFi+蓝牙模块
资料链接:https://pan.baidu.com/s/1dL-qZKXrnKhcRCXIZtZabA
上一篇:树莓派技术收集
下一篇:硬件及开发工具使用说明