前情回顾
书接上回,我们掌握了ESP8266NodeMCU的基本功能后,终于开始学习ESP8266最强大的WiFi功能了。ESP8266的WiFi功能强大且易于使用,是物联网和智能设备项目的热门选择。
上回链接:快速攻略ESP8266NodeMCU(基于Arduino IDE)(上)
没有看上回的小伙伴也莫慌,如果只是需要ESP8266做WiFi模块使用,那么单看本文就行。
Wi-Fi
要使用ESP8266的WiFi功能需要先包含#include<ESP8266WiFi.h>
ESP8266WiFi库的函数很多,我这里只会挑一些常用的讲。
设置网络模式
WiFi.mode(WiFiMode_t mode);
设置 ESP8266 的 WiFi 模式。参数mode是一个 WiFiMode_t 类型的枚举值,用于设定ESP8266的工作模式。WiFiMode_t 枚举定义了以下几种模式:
WIFI_OFF
:关闭 Wi-Fi 功能。WIFI_STA
:设置为客户端模式,用于连接到其他接入点。WIFI_AP
:设置为接入点模式,允许其他设备连接到 ESP8266。WIFI_AP_STA
:同时设置为接入点模式和客户端模式。
WiFi.softAP(ssid, password);
WiFi.softAPConfig(ip, gateway, subnet);
这两个函数在设置 ESP8266 作为软接入点时使用,用于设置 ESP8266软接入点(通俗说就是热点)的名称和密码,以及IP 地址、网关地址和子网掩码。
初始化和配置
WiFi.begin(ssid, password);
启动与指定WiFi网络连接。和多数功能一样.begin都是启动函数,参数ssid是指定WiFi网络的名称,password则是指定网络的密码。
WiFi.disconnect();
断开当前连接的WiFi网络。这个函数没有参数和返回值,通常在你需要断开当前网络并连接另一个网络时使用。
WiFi.status();
获取当前WiFi连接的状态。这个函数会返回一个wl_status_t
类型的值,该值代表了当前WiFi的状态。wl_status_t
枚举定义了以下几种状态:
WL_NO_SHIELD
: 没有检测到 WiFi模块。WL_IDLE_STATUS
: WiFi模块处于空闲状态。WL_NO_SSID_AVAIL
: 扫描不到指定的 SSID。WL_SCAN_COMPLETED
: 扫描完成。WL_CONNECTED
: 已连接到一个接入点。WL_CONNECT_FAILED
: 连接失败。WL_CONNECTION_LOST
: 连接丢失。WL_DISCONNECTED
: 未连接到接入点。
WiFi.setAutoReconnect(boolean autoReconnect)
设置是否自动重新连接到WiFi网络。参数autoReconnect设置是否启用重新连接,可输入以下关键词:
true
:启用自动重新连接。如果ESP8266失去了与WiFi网络的连接,它将自动尝试重新连接到之前配置的网络。false
:禁用自动重新连接。如果ESP8266失去了与WiFi网络的连接,它不会自动尝试重新连接。
获取网络信息
WiFi.SSID();
WiFi.localIP();
WiFi.gatewayIP();
WiFi.subnetMask();
这几个函数都是用于返回设备当前网络信息。从上到下依次是返回当前连接网络的名称,返回设备当前的IP地址,返回网络的网关IP地址,返回网络的子网掩码。
连接网络
现在我们来写连接WiFi并返回网络状态的函数。
#include<ESP8266WiFi.h>
const char* WIFISSID ="OPPOFindX3"; //WiFi名称
const char* PASSWORO ="55555555"; //WiFi密码
uint8_t wifi_flag=0;
void setup() {
Serial.begin(115200);
//设置为客户端模式
WiFi.mode(WIFI_STA);
//连接Wifi
WiFi.begin(WIFISSID, PASSWORO);
//等待连接
while(WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
//打印WiFi信息
Serial.println("Connected to Wi-Fi");
Serial.print("Wi-Fi Name: ");
Serial.println(WiFi.SSID());
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
//启用自动重连
WiFi.setAutoReconnect(true);
}
void loop() {
//持续检查连接状态
switch(WiFi.status()){
case WL_CONNECTED://已连接网络
if(wifi_flag == 1){
Serial.println("Connected to Wi-Fi");
Serial.print("Wi-Fi Name: ");
Serial.println(WiFi.SSID());
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
wifi_flag=0;
}
break;
case WL_DISCONNECTED://未连接网络
if(wifi_flag == 0){
wifi_flag=1;
Serial.println("wifi Disconnected");
}
break;
}
}
这里我连接的是我手机开的热点,特意关了一下热点,连接和重连都是没问题的。
MQTT
既然我们已经让ESP8266连上网络了,ESP8266的学习也将步入尾声。
要想将ESP8266应用与物联网中我们还需要掌握通讯协议,MQTT是一种轻量级的消息传输协议,专为带宽有限和不稳定的网络环境设计。它广泛应用于物联网领域,用于连接远程设备,实现数据传输。
环境配置
ESP8266有相应的MQTT协议的库PubSubClient,可以在Arduino的官方网站上去下载。
下载2.8.0版本,下载完成后再去Arduino IDE中安装。
添加库之后需要等一会加载。
加载完成后我们在程序中包含PubSubClient库再编译下,测试是否装好。
初始化
#include<ESP8266WiFi.h>
#include<PubSubClient.h>
WiFiClient espClient;
PubSubClient client(espClient);
要使用PubSubClient库需要同时包含WiFi库。
首先创建一个WiFiClient 类型,再创建PubSubClient类型来使用WiFiClient 类型。
设置服务器
client.setServer(IPAddress ip, uint16_t port);
client.setServer(const char* domain, uint16_t port);
设置MQTT服务器地址和端口号。这个函数有两种形式,可以使用IP地址或者域名来访问服务器。参数ip为服务器的 IP 地址,可以是IPv4地址,domain是服务器域名,port是服务器端口号。
连接服务器
client.connect(const char* id);
连接MQTT 服务器。这个函数有几种形式,这个是无身份验证连接服务器。参数id是设置客户端唯一标识符。
client.connect(const char* client_id, const char* username, const char* password);
带用户名和密码的连接。和上个函数差不多,只是多了设置用户名和服务器密码的参数。
断开连接
client.disconnect();
断开与MQTT服务器的连接。这个函数一般用在需要重新配置连接参数是使用。
发布主题消息
client.publish(const char* topic, const char* payload);
发布消息到 MQTT 服务器上的特定主题。这个函数也有几种形式,这个是向指定主题发布消息。参数topic是要发布消息的主题(字符串),payload是要发布的内容。
client.publish(const char* topic, const char* payload, boolean retained);
发布保留消息到主题。和上个函数差不多,不过多了一个参数retained。retained是一个布尔值,用于指定消息是否为保留消息。如果设置为true
,那么新的订阅者订阅该主题时会立即收到保留消息。
如果想要取消保留消息,需要发布一个消息为空的保留消息如:client.publish("topic","",true);
订阅主题
client.subscribe(const char* topic);
订阅MQTT服务器上的主题。参数topic是要订阅的主题字符串。
订阅回调函数
client.setCallback(Callback);
客户端接收到来自订阅主题的消息时,回调函数就会被调用。
要使用这个函数需要定义一个回调函数,且必须要符合以下原型:
void callback(char* topic, byte* payload, unsigned int length);
topic
: 接收到消息的主题字符串。payload
: 接收到的消息内容,以字节数组形式。length
: 接收到的消息长度。
消息循环
在完成前面一系列配置后,我们还需要保持设备和服务器之间的联系。
我们需要定时的向服务器发送一个心跳信号,告诉服务器我们还没寄寄。这里我们可以设置这个心跳信号的时间。
client.setKeepAlive(tim);
如果没有设置间隔时间的话默认会是15秒,当超过间隔时间内没发送心跳信号,服务器就会认为设备寄了。在 PubSubClient
库中,心跳信号的管理是自动的。需要调用下函数:
client.loop();
将这个函数放在主循环中使用时,客户端在设定心跳信号时间内没有发送任何消息,PubSubClient 库会自动发送一个 PINGREQ 消息。如果在设定心跳信号时间内客户端没有从服务器接收到任何消息,它也会发送 PINGREQ。如果服务器在一定时间内没有响应 PINGRESP,客户端将断开连接。
检查连接状态
client.connected();
返回设备与服务器连接状态。可以在主循环中使用此函数来实时检查与服务器的连接状态。
MQTT发送/接收消息
好了我们已经基本掌握了PubSubClient 库的使用了,下面我们来编写代码。
#include<ESP8266WiFi.h>
#include<PubSubClient.h>
#include <Ticker.h>
// WiFi设置
const char* WIFISSID ="name";
const char* PASSWORO ="passworo";
uint8_t wifi_flag=0;
// MQTT服务器设置
const char* mqtt_server = "xx.xx.xx.xx";
const int mqtt_port = 1883;
const char* mqtt_id = "esp-01";
WiFiClient espClient;
PubSubClient client(espClient);
Ticker tim0;
void setup_wifi(){
WiFi.mode(WIFI_STA);//设置为客户端模式
//连接Wifi
WiFi.begin(WIFISSID, PASSWORO);
while(WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println("Connected to Wi-Fi");
Serial.println(WiFi.SSID());
//启用自动重连
WiFi.setAutoReconnect(true);
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(mqtt_id)) {
Serial.println("connected");
// 订阅主题
client.subscribe("topic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
void Issued(){
client.publish("test","Hello!MQTT");
}
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
client.setKeepAlive(15);
tim0.attach_ms(1000, Issued);
}
void loop() {
//持续检查连接状态
switch(WiFi.status()){
case WL_CONNECTED:
if(wifi_flag == 1){
Serial.println("Connected to Wi-Fi");
Serial.println(WiFi.SSID());
wifi_flag=0;
}
if(!client.connected()){//检查与服务器连接状态
reconnect();
}
client.loop();
break;
case WL_DISCONNECTED:
if(wifi_flag == 0){
wifi_flag=1;
Serial.println("wifi Disconnected");
}
break;
}
}
这里我写了一个向主题test定时发送消息,同时接收topic主题发送的消息并将其输出到串口的程序。
OK,这里使用MQTTX软件来验证下。软件地址:MQTTX下载
可以看到,test主题确实收到来自ESP8266发布的消息。现在我们在topic主题发送消息。
OK,ESP8266也是接收到了topic主题发布的消息。
这里我使用的MQTT服务器是我自己搭建的,后续我会出一期关于MQTT服务器的文章。
使用MQTT点灯
好了,行至此处ESP8266已经可以说通关了,此时应当不忘初心,继续向点灯大师的方向前进。下面让我们使用MQTT服务器实现远程点灯。
在点灯之前,我们还需要了解一下MQTT常用的数据格式Json。
JSON数据格式
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON 是基于 JavaScript 语言的一个子集,但它独立于语言,使用文本格式来存储和表示数据。
以下是一个简单 JSON 对象的示例:
{
"name": "Van Darkholme",
"age": 30,
"DATA": [11, 45, 14]
}
一个 JSON 对象以 {
开始,并以 }
结束,它包含键值对,键是字符串,值可以是字符串、数字、数组、布尔值、null 或者另一个 JSON 对象。
ArduinoJson库
在了解JSON格式后,我们需要使用ESP8266去解析它。
在Arduino IDE中有专门解析JSON数据的库ArduinoJson。下载地址:ArduinoJson库
添加库又是熟悉的配方,熟悉的味道ヽ( ̄▽ ̄)ノ
初始化
#include<ArduinoJson.h>
DynamicJsonDocument doc(1024);
和前面的库差不多,先添加库文件然后创建JsonDocument
对象。
解析数据
使用ArduinoJson库可以非常简单的解析JSON数据,下面举一个解析数据的例子。
String json = "{\"name\":\"Van\",\"temper\":36.6}";
//分配解析文档内存
DynamicJsonDocument doc(1024);
// 解析 JSON 字符串
DeserializationError error = deserializeJson(doc, json);
// 检查解析是否成功
if (error) {
return;
}
// 访问解析后的数据
const char* name= doc["name"];
double temper= doc["temper"];
deserializeJson函数会将JSON数据解析,然后放入我们创建的JsonDocument
对象里,之后就可以直接访问。
转换数据
掌握了解析数据后我们就可以接受数据了,需要发送数据的话还需要将数据转换为JSON格式发送。下面举个栗子。
const char* name = "dio";
double temper = 114.514;
uint8_t led = 1;
DynamicJsonDocument doc(128);
// 写入数据
doc["name"] = name;
doc["temper"] = temper;
doc["led"] = led;
//将JSON转换为字符串
String output;
serializeJson(doc, output);
将数据做成JSON格式很方便,这样就可以将数据转换成JSON格式的字符串了。
点灯
现在可以开始点灯了,下面编写代码。
#include<ESP8266WiFi.h>
#include<PubSubClient.h>
#include<ArduinoJson.h>
#include <Ticker.h>
// WiFi设置
const char* WIFISSID ="SSID";
const char* PASSWORO ="passworo";
uint8_t wifi_flag=0;
// MQTT服务器设置
const char* mqtt_server = "xx.xx.xx.xx";
const int mqtt_port = 1883;
const char* mqtt_id = "esp-01";
WiFiClient espClient;
PubSubClient client(espClient);
DynamicJsonDocument doc(1024);
Ticker tim0;
const char* name;
double temper;
uint8_t led;
void setup_wifi(){
WiFi.mode(WIFI_STA);//设置为客户端模式
//连接Wifi
WiFi.begin(WIFISSID, PASSWORO);
while(WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println("Connected to Wi-Fi");
Serial.println(WiFi.SSID());
//启用自动重连
WiFi.setAutoReconnect(true);
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(mqtt_id)) {
Serial.println("connected");
// 订阅主题
client.subscribe("topic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
DeserializationError error = deserializeJson(doc, payload);
if (error) {
return;
}
name = doc["name"];
temper = doc["temper"];
led = doc["led"];
Serial.printf("name=%s, temper=%f, led=%d\n",name,temper,led);
}
void Issued(){
doc["name"] = name;
doc["temper"] = temper;
doc["led"] = led;
String output;
serializeJson(doc, output);
client.publish("test",output.c_str());
}
void setup() {
Serial.begin(115200);
pinMode(12,OUTPUT);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
client.setKeepAlive(15);
tim0.attach_ms(1000, Issued);
}
void loop() {
//持续检查连接状态
switch(WiFi.status()){
case WL_CONNECTED:
if(wifi_flag == 1){
Serial.println("Connected to Wi-Fi");
Serial.println(WiFi.SSID());
wifi_flag=0;
}
if(!client.connected()){//检查与服务器连接状态
reconnect();
}
client.loop();
break;
case WL_DISCONNECTED:
if(wifi_flag == 0){
wifi_flag=1;
Serial.println("wifi Disconnected");
}
break;
}
if(led){ //开关灯
digitalWrite(12,1);
}else{
digitalWrite(12,0);
}
}
这段代码实现了从MQTT服务器订阅的主题中获取消息,并且解析JSON格式数据,再将客户端的信息以JSON格式定时发送到test主题。使用了一个变量来控制灯的开关,当接收到led=1的消息时点亮灯,反之则关灯。
通过MQTTX验证一下,看来是没问题。
总结
能够坚持看到这里的朋友,恭喜你通关了ESP8266。根据现在掌握的知识,相信做一些简单的物联网项目也是没什么问题了,比如物联网小车,智能寝室门锁之类的项目。
关于MQTT服务器,我使用服务器的是利用阿里云的免费体验ECS服务器搭建的。之后我会出一期关于MQTT服务器的文章(我先自己玩明白捏)。
好了,快速攻略ESP8266NodeMCU(基于Arduino IDE)系列完结了。感谢各位阅读,下个系列再见。
评论