ESP8266 RTOS开发之路(5)— ESP8266连接到MQTT服务器

ESP8266 RTOS开发之路(5)— ESP8266连接到MQTT服务器

本次开发是在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();         
}

然后在app_main()函数里调用即可
在这里插入图片描述

二、连接到MQTT服务器

首先,要定义一个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客户端结构体指针

    // 获取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接受主题和主题消息:
在这里插入图片描述

三、控制LED

接下来我们通过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);
}

  • 4
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值