ESP32 uart+wifi+mqtt+nvs(2)

接上一篇文章,本篇重新整理了处理逻辑。

最终实现串口输入数据后保存在nvs中,等所有数据都拿到之后在进行wifi的连接及mqtt消息的发送,主要使用了freertos中的事件通知,及信号量等知识,esp32则使用了nvs,uart,wifi等相关初始化及对应操作。

代码整体步骤如下

1.app_main函数中,创建事件组,创建二进制信号量,在需要创建两个task,一个任务用于串口数据的接收,另一个任务用于监听事件的标志位,本工程定义了两个标志位bit0与bit1,分别表示wifi数据的接收和mqtt信息的接收

2.串口接收输入字符串,根据字符串的格式,截取获得wifi的用户名密码和mqtt的地址,

3.串口获取后使用write_nvs_str操作存入nvs中,并且将事件的标志位置为1

4.当所有需要的数据都得到后开始先初始化wifi,wifi连接成功后初始化mqtt,向mqtt发送连接成功消息,中间使用信号量用来控制两个程序执行的顺序,只有等wifi连接成功才会执行mqtt相关代码,当mqtt发送完成后在将事件的标志位清除。


具体修改的代码如下

1.main函数相关代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "mqtt_client.h"
#include "simple_wifi_sta.h"

#include "esp_wifi.h"
#include "esp_event.h"
#include "mymqtt.h"
#include "mynvs.h"
#include "myuart.h"



SemaphoreHandle_t wifi_connected_semaphore;

EventGroupHandle_t xEventGroup;

extern char mqtt_address;

static const char *TAG = "main";

void task1(void *pvParameters) {

    printf("task1 begin \r\n");
    EventBits_t uxBits;
    while (1) {
        // 等待BIT_0和BIT_1事件都被设置
        printf("xEventGroupWaitBits start! \r\n");
        uxBits = xEventGroupWaitBits(xEventGroup, BIT_0_AND_1, pdFALSE, pdFALSE,portMAX_DELAY);//pdMS_TO_TICKS(1000)
        printf("xEventGroupWaitBits end! \r\n");
        printf("bits: 0x%lx\n", uxBits);
        uxBits = xEventGroupGetBits(xEventGroup);
        printf("bits get : 0x%lx\n", uxBits);
        if ((uxBits & BIT_0_AND_1) == BIT_0_AND_1) {
            
            printf("task1 start \r\n");
            char read_ssid[64];
            char read_pwd[64];
            size_t len_s = 0;
            size_t len_p = 0;


            len_s =read_nvs_str(NVS_WIFI_NAMESPACE,NVS_SSID_KEY,read_ssid,64);
            if(len_s){
                ESP_LOGI(TAG,"Read read_ssid:%s",read_ssid);
                }
            else{
                ESP_LOGI(TAG,"Read read_ssid fail,please perform nvs_erase_key and try again");
            }   

            len_p =read_nvs_str(NVS_WIFI_NAMESPACE,NVS_PWD_KEY,read_pwd,64);
            if(len_p){
                ESP_LOGI(TAG,"Read read_pwd :%s",read_pwd);
                }
            else{
                ESP_LOGI(TAG,"Read read_pwd fail,please perform nvs_erase_key and try again");
            }      

            wifi_config_params_t *params = malloc(sizeof(wifi_config_params_t)); // 分配内存
            trim_spaces(read_pwd);
            trim_spaces(read_ssid);
            params->password = read_pwd;
            params->ssid = read_ssid;

            wifi_sta_init(params);

            char read_mqtt[64];
            size_t len = 0;
            len =read_nvs_str(NVS_MQTT_NAMESPACE,NVS_ADDRESS_KEY,read_mqtt,64);
            if(len){
                ESP_LOGI(TAG,"Read mqtt SEX:%s",read_mqtt);
                }
            else{
                ESP_LOGI(TAG,"Read mqtt fail,please perform nvs_erase_key and try again");
            }  

            if (xSemaphoreTake(wifi_connected_semaphore, portMAX_DELAY) == pdTRUE) {
                mqtt_start(read_mqtt); 
            }
          
        }
     vTaskDelay(pdMS_TO_TICKS(1000));
    }
    
}



void app_main(void)
{
    wifi_connected_semaphore = xSemaphoreCreateBinary();

    xEventGroup = xEventGroupCreate();
    printf("start\r\n");
    uart_init();
    printf("end\r\n");

    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());  //用于初始化tcpip协议栈
    ESP_ERROR_CHECK(esp_event_loop_create_default());       //创建一个默认系统事件调度循环,之后可以注册回调函数来处理系统的一些事件
    esp_netif_create_default_wifi_sta();    //使用默认配置创建STA对象

    register_wifi_events();

    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        //NVS出现错误,执行擦除
        ESP_ERROR_CHECK(nvs_flash_erase());
        //重新尝试初始化
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    xTaskCreatePinnedToCore(receive_task, "uart_rx_task", 4096, NULL, configMAX_PRIORITIES - 1, NULL,1);

    xTaskCreatePinnedToCore(task1, "task1", 4096, NULL, configMAX_PRIORITIES - 2, NULL,1);
    //if (xSemaphoreTake(wifi_connected_semaphore, portMAX_DELAY) == pdTRUE) {
        //xTaskCreate(mqtt_start, "mqtt_task", 2048, NULL, 5, NULL);  
        //mqtt_start();
    //}
    
    return;
}

2.nvs操作相关代码

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_err.h"

static const char* TAG = "main";



/** 从nvs中读取字符值
 * @param namespace NVS命名空间
 * @param key 要读取的键值
 * @param value 读到的值
 * @param maxlen 外部存储数组的最大值
 * @return 读取到的字节数
*/
 size_t read_nvs_str(const char* namespace,const char* key,char* value,int maxlen)
{
    nvs_handle_t nvs_handle;
    esp_err_t ret_val = ESP_FAIL;
    size_t required_size = 0;
    ESP_ERROR_CHECK(nvs_open(namespace, NVS_READWRITE, &nvs_handle));
    ret_val = nvs_get_str(nvs_handle, key, NULL, &required_size);
    if(ret_val == ESP_OK && required_size <= maxlen)
    {
        nvs_get_str(nvs_handle,key,value,&required_size);
    }
    else
        required_size = 0;
    nvs_close(nvs_handle);
    return required_size;
}

/** 写入值到NVS中(字符数据)
 * @param namespace NVS命名空间
 * @param key NVS键值
 * @param value 需要写入的值
 * @return ESP_OK or ESP_FAIL
*/
 esp_err_t write_nvs_str(const char* namespace,const char* key,const char* value)
{
    nvs_handle_t nvs_handle;
    esp_err_t ret;
    ESP_ERROR_CHECK(nvs_open(namespace, NVS_READWRITE, &nvs_handle));
    
    ret = nvs_set_str(nvs_handle, key, value);
    nvs_commit(nvs_handle);
    nvs_close(nvs_handle);
    return ret;
}

/** 从nvs中读取字节数据(二进制)
 * @param namespace NVS命名空间
 * @param key 要读取的键值
 * @param value 读到的值
 * @param maxlen 外部存储数组的最大值
 * @return 读取到的字节数
*/
 size_t read_nvs_blob(const char* namespace,const char* key,uint8_t *value,int maxlen)
{
    nvs_handle_t nvs_handle;
    esp_err_t ret_val = ESP_FAIL;
    size_t required_size = 0;
    ESP_ERROR_CHECK(nvs_open(namespace, NVS_READWRITE, &nvs_handle));
    ret_val = nvs_get_blob(nvs_handle, key, NULL, &required_size);
    if(ret_val == ESP_OK && required_size <= maxlen)
    {
        nvs_get_blob(nvs_handle,key,value,&required_size);
    }
    else
        required_size = 0;
    nvs_close(nvs_handle);
    return required_size;
}

/** 擦除nvs区中某个键
 * @param namespace NVS命名空间
 * @param key 要读取的键值
 * @return 错误值
*/
 esp_err_t erase_nvs_key(const char* namespace,const char* key)
{
    nvs_handle_t nvs_handle;
    esp_err_t ret_val = ESP_FAIL;
    ESP_ERROR_CHECK(nvs_open(namespace, NVS_READWRITE, &nvs_handle));
    ret_val = nvs_erase_key(nvs_handle,key);
    ret_val = nvs_commit(nvs_handle);
    nvs_close(nvs_handle);
    return ret_val;
}

/** 写入值到NVS中(字节数据)
 * @param namespace NVS命名空间
 * @param key NVS键值
 * @param value 需要写入的值
 * @return ESP_OK or ESP_FAIL
*/
 esp_err_t write_nvs_blob(const char* namespace,const char* key,uint8_t* value,size_t len)
{
    nvs_handle_t nvs_handle;
    esp_err_t ret;
    ESP_ERROR_CHECK(nvs_open(namespace, NVS_READWRITE, &nvs_handle));
    ret = nvs_set_blob(nvs_handle, key, value,len);
    nvs_commit(nvs_handle);
    nvs_close(nvs_handle);
    return ret;
}

3.uart代码接收的逻辑修改如下


#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "simple_wifi_sta.h"
#include <stdio.h>
#include <ctype.h>
#include "mymqtt.h"
#include "myuart.h"
#include "mynvs.h"



static const int RX_BUF_SIZE = 1024;

#define TXD_PIN (GPIO_NUM_1)
#define RXD_PIN (GPIO_NUM_3)

#define UART_NUM      (UART_NUM_0)
#define BUF_SIZE      (1024)



char wifi_ssid[33];
char wifi_password[65];

char mqtt_address[65];




extern EventGroupHandle_t xEventGroup;


bool str_starts_with(const char *str, const char *prefix);
void trim_spaces(char *str);

void uart_init(void)
{

    printf("uart_init\r\n");
    const uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    // We won't use a buffer for sending data.
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM, RX_BUF_SIZE * 2, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
}



void receive_task(void *arg){
    
    static const char *RX_TASK_TAG = "RX_TASK";
    char *at_str = "AT+U";
    char *m_str = "AT+M";
    uint8_t *data = (uint8_t *) malloc(RX_BUF_SIZE + 1);
    ESP_LOGI(RX_TASK_TAG, "Receive task start ");
    while (1) {
        // Read data from the UART
        int len = uart_read_bytes(UART_NUM, data, (RX_BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
        
        // Write data back to the UART
        uart_write_bytes(UART_NUM, (const char *) data, len);
        if (len>0) {
            data[len] = '\0';
            ESP_LOGI(RX_TASK_TAG, "Recv str: %s", (char *) data);
            if(str_starts_with((char *) data,at_str)){
                printf("this char is%s",(char *) data);
                 if (sscanf((char *) data, "AT+U%32[^P]P%64[^\n]", wifi_ssid, wifi_password) == 2) {
                    ESP_LOGI(RX_TASK_TAG, "SSID: %s, Password: %s \r\n", wifi_ssid, wifi_password);
                    printf("ssid is %s \r\n",wifi_ssid);
                    printf("Password is %s \r\n",wifi_password);
                    wifi_config_params_t *params = malloc(sizeof(wifi_config_params_t)); // 分配内存
                    if (params == NULL) {
                        printf("Memory allocation failed\n");
                        return;
                    }
                    trim_spaces(wifi_ssid);
                    trim_spaces(wifi_password);
                    write_nvs_str(NVS_WIFI_NAMESPACE,NVS_SSID_KEY,wifi_ssid); 
                    write_nvs_str(NVS_WIFI_NAMESPACE,NVS_PWD_KEY,wifi_password);
                  
                    xEventGroupSetBits(xEventGroup, BIT_0); // 设置BIT_0事件
                    free(params);
                
                    
                } else {
                    ESP_LOGE(RX_TASK_TAG, "Failed to extract strings.");
                }
            }

            if(str_starts_with((char *) data,m_str)){
                printf("this char mqtt is%s",(char *) data);
                 if (sscanf((char *) data, "AT+M%64[^\n]", mqtt_address) == 1) {
                    ESP_LOGI(RX_TASK_TAG, "mqtt_address: %s", mqtt_address);
                    printf("mqtt_address is %s \r\n",mqtt_address);
                   
                    trim_spaces(mqtt_address);

                    //存储mqtt相关信息
                    write_nvs_str(NVS_MQTT_NAMESPACE,NVS_ADDRESS_KEY,mqtt_address); 

                    xEventGroupSetBits(xEventGroup, BIT_1); // 设置BIT_1事件
                    
                   
                    
                } else {
                    ESP_LOGE(RX_TASK_TAG, "Failed to extract strings.");
                }
            }
        }
    }
    free(data);
    vTaskDelete(NULL);
}

/**
 * 去除字符串两侧的空格
 * @param str 要处理的字符串
 */
void trim_spaces(char *str) {
    char *end, *start = str;
    // 去除字符串左侧的空格
    while (isspace((unsigned char)*start)) start++;
    
    if (*start == 0) {  // 如果字符串全是空格
        *str = 0;  // 将其设为空字符串
        return;
    }

    // 去除字符串右侧的空格
    end = start + strlen(start) - 1;
    while (end > start && isspace((unsigned char)*end)) end--;
    // 写入字符串结束符
    *(end + 1) = 0;

    // 将剔除空格后的字符串前移
    memmove(str, start, end - start + 2);
}


bool str_starts_with(const char *str, const char *prefix) {
    size_t prefix_len = strlen(prefix);
    return strncmp(str, prefix, prefix_len) == 0;
}

4.mqtt代码 (主要就是增加了一行清除事件标志位的代码,还有就是mqtt的参数化)

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "mqtt_client.h"
#include "simple_wifi_sta.h"
#include "mymqtt.h"


static const char* TAG = "main";

#define MQTT_ADDRESS    "mqtt://broker-cn.emqx.io"     //MQTT连接地址
#define MQTT_PORT       1883                        //MQTT连接端口号
#define MQTT_CLIENT     "mqttx_d11213"              //Client ID(设备唯一,大家最好自行改一下)
#define MQTT_USERNAME   "wangxiaohuang"                     //MQTT用户名
#define MQTT_PASSWORD   "11111111"                  //MQTT密码

#define MQTT_PUBLIC_TOPIC      "/test/topic1"       //测试用的,推送消息主题
#define MQTT_SUBSCRIBE_TOPIC    "/test/topic2"      //测试用的,需要订阅的主题


extern EventGroupHandle_t xEventGroup;

//MQTT客户端操作句柄
static esp_mqtt_client_handle_t     s_mqtt_client = NULL;

//MQTT连接标志
static bool   s_is_mqtt_connected = false;

/**
 * mqtt连接事件处理函数
 * @param event 事件参数
 * @return 无
 */
static void aliot_mqtt_event_handler(void* event_handler_arg,
                                        esp_event_base_t event_base,
                                        int32_t event_id,
                                        void* event_data)
{
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;

    // your_context_t *context = event->context;
    switch ((esp_mqtt_event_id_t)event_id) {
        case MQTT_EVENT_CONNECTED:  //连接成功
            ESP_LOGI(TAG, "mqtt connected");
            s_is_mqtt_connected = true;
            //连接成功后,订阅测试主题
            esp_mqtt_client_subscribe_single(s_mqtt_client,MQTT_SUBSCRIBE_TOPIC,1);
            break;
        case MQTT_EVENT_DISCONNECTED:   //连接断开
            ESP_LOGI(TAG, "mqtt disconnected");
            s_is_mqtt_connected = false;
            break;
        case MQTT_EVENT_SUBSCRIBED:     //收到订阅消息ACK
            ESP_LOGI(TAG, " mqtt subscribed ack, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_UNSUBSCRIBED:   //收到解订阅消息ACK
            break;
        case MQTT_EVENT_PUBLISHED:      //收到发布消息ACK
            ESP_LOGI(TAG, "mqtt publish ack, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_DATA:
            printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);       //收到Pub消息直接打印出来
            printf("DATA=%.*s\r\n", event->data_len, event->data);
            break;
        case MQTT_EVENT_ERROR:
            ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
            break;
        default:
            break;
    }
}


/** 启动mqtt连接
 * @param 无
 * @return 无
*/
void mqtt_start(char* address)
{
    esp_mqtt_client_config_t mqtt_cfg = {0};
    mqtt_cfg.broker.address.uri = MQTT_ADDRESS;
    mqtt_cfg.broker.address.port = MQTT_PORT;
    //Client ID
    mqtt_cfg.credentials.client_id = MQTT_CLIENT;
    //用户名
    mqtt_cfg.credentials.username = MQTT_USERNAME;
    //密码
    mqtt_cfg.credentials.authentication.password = MQTT_PASSWORD;
    ESP_LOGI(TAG,"mqtt connect->clientId:%s,username:%s,password:%s",mqtt_cfg.credentials.client_id,
    mqtt_cfg.credentials.username,mqtt_cfg.credentials.authentication.password);
    //设置mqtt配置,返回mqtt操作句柄
    s_mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
    //注册mqtt事件回调函数
    esp_mqtt_client_register_event(s_mqtt_client, ESP_EVENT_ANY_ID, aliot_mqtt_event_handler, s_mqtt_client);
    //启动mqtt连接
    esp_mqtt_client_start(s_mqtt_client);
    esp_mqtt_client_publish(s_mqtt_client, "/test/topic1", "WiFi Connected", 0, 1, 0);

    //清除标志位
    xEventGroupClearBits(xEventGroup, BIT_0 | BIT_1);
   
}


void send_topic(){

   static char mqtt_pub_buff[64];   
   int count = 0;
    while(1)
    {
        
         // 生成随机数
   
        //延时2秒发布一条消息到/test/topic1主题
        if(s_is_mqtt_connected)
        {
            snprintf(mqtt_pub_buff,64,"{\"count\":\"%d\"}",count);
            esp_mqtt_client_publish(s_mqtt_client, MQTT_PUBLIC_TOPIC,
                            mqtt_pub_buff, strlen(mqtt_pub_buff),1, 0);
            count++;
        }

        vTaskDelay(pdMS_TO_TICKS(2000));
    }

}

5.wifi相关代码跟上一篇的代码一致,删除掉mqtt_start()即可

遇到的主要问题就是事件组的用法

EventBits_t uxBits = xEventGroupWaitBits(
    xEventGroup,        // 事件组的句柄
    BIT_0,              // 等待的事件位
    pdTRUE,             // 等待所有位  clearOnExit
    pdFALSE,            // 不清除事件位
    portMAX_DELAY       // 无限期等待
);

在本文中,因为操作事件标志位在两个不同的任务中,如果clearOnExit为pdTRUE,那么在 xEventGroupWaitBits 返回后,BIT_0 将被清除。就无法进行if ((uxBits & BIT_0_AND_1) == BIT_0_AND_1)里边的操作,所以这块需要设置为false,这样串口在接收到两部分的数据后,将bit0与bit1全部置为1之后,才会执行if块操作,最后在mqtt的相关代码中,手动清楚两个事件的标志位即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值