本次开发是在Ubuntu下的,使用的模块是ESP12F,32Mbit的flash。程序基于ESP8266_RTOS_SDK-3.x的工程 。
从上个工程:ESP8266 RTOS开发之路(4)— 连接到WiFi,将其中的代码封装成一个wifi_connect_init()函数,新建app_wifi.c文件,在app_main.c代码的基础上,将app_main()函数修改为wifi_connect_init()函数:
void wifi_connect_init(void)
{
/* 定义一个gpio配置结构体 */
gpio_config_t gpio_config_structure;
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_NUM);/* 选择gpio2 */
gpio_config_structure.mode = GPIO_MODE_OUTPUT; /* 输出模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_INTR_DISABLE; /* 禁止中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
/* 初始化非易失性存储库 (NVS) */
ESP_ERROR_CHECK( nvs_flash_init() );
printf("ESP_WIFI_MODE_STA \n");
wifi_init_sta();
}
首先,要定义一个MQTT客户端配置结构体,填入MQTT服务器的url即可即MQTT事件处理函数
// 1、定义一个MQTT客户端配置结构体,输入MQTT的url和MQTT事件处理函数
esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtt://39.96.35.207",
.event_handle = mqtt_event_handler,
};
然后通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体
// 2、通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
接下来就可以开启MQTT了
// 3、开启MQTT
esp_mqtt_client_start(client);
编译,烧录,运行,串口终端打印如下
可以看到,已经向MQTT服务器发送了连接报文,然后打开我们的EMQ控制台,在连接选项可以看到:
查阅ESP-IDF 编程指南可以知道,在我们对MQTT客户端配置结构体最小配置时,默认的客户端ID是ESP32_+十六进制格式的MAC地址的最后3个字节
,可以看到,该连接的客户端ID符合,另外一位我们没有配置用户名,使用用户名处为未定义,然后默认的clean_session为true,即清楚会话为true,mqtt keepalive的默认值为120秒,和连接界面显示的心跳时间相同;
我们在MQTT客户端配置结构体中设置用户名为ESP8266_LED
,客户端ID设置为ESP8266_MQTT
,
编译,烧录,运行,可以看到,连接界面的用户名和客户端ID也相应的改变了
我们在MQTT事件处理函数处理有关MQTT的事件,首先,获取MQTT客户端结构体指针
// 获取MQTT客户端结构体指针
esp_mqtt_client_handle_t client = event->client;
然后通过事件ID来分别处理对应的事件,首先,连接成功事件,连接成功后,我们订阅一个主题,发布一个主题,
// 通过事件ID来分别处理对应的事件
switch (event->event_id)
{
// 建立连接成功
case MQTT_EVENT_CONNECTED:
printf("MQTT_client cnnnect to EMQ ok. \n");
// 发布主题,主题消息为“I am ESP32.”,自动计算有效载荷,qos=1
esp_mqtt_client_publish(client, "ESP8266_Publish", "I am ESP8266.", 0, 1, 0);
// 订阅主题,qos=0
esp_mqtt_client_subscribe(client, "ESP8266_Subscribe", 0);
break;
}
然后还有其他事件的处理也添加进去
// 客户端断开连接
case MQTT_EVENT_DISCONNECTED:
printf("MQTT_client have disconnected. \n");
break;
// 主题订阅成功
case MQTT_EVENT_SUBSCRIBED:
printf("mqtt subscribe ok. msg_id = %d \n",event->msg_id);
break;
// 取消订阅
case MQTT_EVENT_UNSUBSCRIBED:
printf("mqtt unsubscribe ok. msg_id = %d \n",event->msg_id);
break;
// 主题发布成功
case MQTT_EVENT_PUBLISHED:
printf("mqtt published ok. msg_id = %d \n",event->msg_id);
break;
// 已收到订阅的主题消息
case MQTT_EVENT_DATA:
printf("mqtt received topic: %.*s \n",event->topic_len, event->topic);
printf("topic data: %.*s\r\n", event->data_len, event->data);
break;
// 客户端遇到错误
case MQTT_EVENT_ERROR:
printf("MQTT_EVENT_ERROR \n");
break;
编译,烧录,运行,可以看到,连接成功,主题发布成功,主题订阅成功
我们打开MQTTfx可以收到主题的消息:
然后通过在MQTTfx发布一个主题消息,可以看到,ESP8266接受主题和主题消息:
接下来我们通过MQTT来控制开发板上RGB灯的亮灭,首先,查阅原理图,得到RGB灯的连接引脚
然后我们定义引脚编号:
#define GPIO_LED_R_NUM 15
#define GPIO_LED_G_NUM 12
#define GPIO_LED_B_NUM 13
然后编写RGB初始化函数:
void RGB_LED_init(void)
{
/* 定义一个gpio配置结构体 */
gpio_config_t gpio_config_structure;
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_R_NUM); /* 选择gpio15 */
gpio_config_structure.mode = GPIO_MODE_OUTPUT; /* 输出模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_INTR_DISABLE; /* 禁止中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_G_NUM);/* 选择gpio12 */
gpio_config(&gpio_config_structure);
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_B_NUM);/* 选择gpio13 */
gpio_config(&gpio_config_structure);
}
还有控制函数:
#define LED_ON 1
#define LED_OFF 0
void RGB_LED_Ctrl(int LED_Ctrl)
{
gpio_set_level(GPIO_LED_R_NUM, LED_Ctrl);
gpio_set_level(GPIO_LED_G_NUM, LED_Ctrl);
gpio_set_level(GPIO_LED_B_NUM, LED_Ctrl);
}
然后通过收到的主题消息来控制LED,
sprintf(mag_data,"%.*s",event->data_len, event->data);
if(strcmp(mag_data,"LED_ON") == 0)
{
RGB_LED_Ctrl(LED_ON);
}
else if(strcmp(mag_data,"LED_OFF") == 0)
{
RGB_LED_Ctrl(LED_OFF);
}
通过MQTTfx向ESP8266_Subscribe
主题发送LED_ON
,可以看到,LED点亮,串口打印信息
通过MQTTfx向ESP8266_Subscribe
主题发送LED_OFF
,可以看到,LED熄灭,串口打印信息
另外添加一个查询LED状态的功能;
首先添加一个LED状态记录变量,然后在LED控制函数中修改其值:
static int LED_STATE = LED_OFF;
void RGB_LED_Ctrl(int LED_Ctrl)
{
LED_STATE = LED_Ctrl;
gpio_set_level(GPIO_LED_R_NUM, LED_Ctrl);
gpio_set_level(GPIO_LED_G_NUM, LED_Ctrl);
gpio_set_level(GPIO_LED_B_NUM, LED_Ctrl);
}
接收到ESP8266_Subscribe
主题消息为LED_IS
时,将LED的状态在主题ESP8266_Publish
发送出去:
else if (strcmp(mag_data,"LED_IS") == 0)
{
if(LED_STATE == LED_ON)
esp_mqtt_client_publish(client, "ESP8266_Publish", "LED_STATE_ON", 0, 1, 0);
else
esp_mqtt_client_publish(client, "ESP8266_Publish", "LED_STATE_OFF", 0, 1, 0);
}
break;
贴上app_main.c的完整代码
#include "string.h"
#include "stdlib.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "tcpip_adapter.h"
#include "esp_smartconfig.h"
#include "smartconfig_ack.h"
#include "driver/gpio.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "mqtt_client.h"
#define GPIO_LED_NUM 2
#define GPIO_LED_R_NUM 15
#define GPIO_LED_G_NUM 12
#define GPIO_LED_B_NUM 13
#define LED_ON 1
#define LED_OFF 0
static int LED_STATE = LED_OFF;
void RGB_LED_Ctrl(int LED_Ctrl)
{
LED_STATE = LED_Ctrl;
gpio_set_level(GPIO_LED_R_NUM, LED_Ctrl);
gpio_set_level(GPIO_LED_G_NUM, LED_Ctrl);
gpio_set_level(GPIO_LED_B_NUM, LED_Ctrl);
}
void RGB_LED_init(void);
void wifi_connect_init(void);
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event);
void app_main(void)
{
/* 打印Hello world! */
printf("Hello world!\n");
RGB_LED_init();
/* 连接WiFi */
wifi_connect_init();
// 1、定义一个MQTT客户端配置结构体,输入MQTT的url和MQTT事件处理函数
esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtt://39.96.35.207",
.client_id = "ESP8266_MQTT",
.username = "ESP8266_LED",
.event_handle = mqtt_event_handler,
};
// 2、通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
// 3、开启MQTT
esp_mqtt_client_start(client);
while(1)
{
gpio_set_level(GPIO_LED_NUM, 0); /* 熄灭 */
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500ms*/
gpio_set_level(GPIO_LED_NUM, 1); /* 点亮 */
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500ms*/
}
}
/* MQTT事件处理函数 */
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
char mag_data[100];
// 获取MQTT客户端结构体指针
esp_mqtt_client_handle_t client = event->client;
// 通过事件ID来分别处理对应的事件
switch (event->event_id)
{
// 建立连接成功
case MQTT_EVENT_CONNECTED:
printf("MQTT_client cnnnect to EMQ ok. \n");
// 发布主题,主题消息为“I am ESP32.”,自动计算有效载荷,qos=1
esp_mqtt_client_publish(client, "ESP8266_Publish", "I am ESP8266.", 0, 1, 0);
// 订阅主题,qos=0
esp_mqtt_client_subscribe(client, "ESP8266_Subscribe", 0);
break;
// 客户端断开连接
case MQTT_EVENT_DISCONNECTED:
printf("MQTT_client have disconnected. \n");
break;
// 主题订阅成功
case MQTT_EVENT_SUBSCRIBED:
printf("mqtt subscribe ok. msg_id = %d \n",event->msg_id);
break;
// 取消订阅
case MQTT_EVENT_UNSUBSCRIBED:
printf("mqtt unsubscribe ok. msg_id = %d \n",event->msg_id);
break;
// 主题发布成功
case MQTT_EVENT_PUBLISHED:
printf("mqtt published ok. msg_id = %d \n",event->msg_id);
break;
// 已收到订阅的主题消息
case MQTT_EVENT_DATA:
printf("mqtt received topic: %.*s \n",event->topic_len, event->topic);
printf("topic data: [%.*s]\r\n", event->data_len, event->data);
sprintf(mag_data,"%.*s",event->data_len, event->data);
if(strcmp(mag_data,"LED_ON") == 0)
{
RGB_LED_Ctrl(LED_ON);
}
else if(strcmp(mag_data,"LED_OFF") == 0)
{
RGB_LED_Ctrl(LED_OFF);
}
else if (strcmp(mag_data,"LED_IS") == 0)
{
if(LED_STATE == LED_ON)
esp_mqtt_client_publish(client, "ESP8266_Publish", "LED_STATE_ON", 0, 1, 0);
else
esp_mqtt_client_publish(client, "ESP8266_Publish", "LED_STATE_OFF", 0, 1, 0);
}
break;
// 客户端遇到错误
case MQTT_EVENT_ERROR:
printf("MQTT_EVENT_ERROR \n");
break;
}
return ESP_OK;
}
void RGB_LED_init(void)
{
/* 定义一个gpio配置结构体 */
gpio_config_t gpio_config_structure;
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_R_NUM); /* 选择gpio15 */
gpio_config_structure.mode = GPIO_MODE_OUTPUT; /* 输出模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_INTR_DISABLE; /* 禁止中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_G_NUM);/* 选择gpio12 */
gpio_config(&gpio_config_structure);
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_B_NUM);/* 选择gpio13 */
gpio_config(&gpio_config_structure);
}