ESP32 uart+wifi+mqtt(1)

本文记录下Esp32串口+wifi+mqtt三个小模块的整合.

本人之前干的是java,c语言刚学习,如果有语法问题,大家也可以帮忙看看,

这个程序代码主要是要完成串口助手输入获取wifi的用户名及密码,在通过代码实现wifi的连接,最后向mqttx发送wifi连接成功的消息。

开发及演示工具使用到的有vscode,mqttx,串口助手v1.1,

开发板使用esp-wroom-32,及usb-ttl串口转换

主要思路是app_main函数中完成

(1)串口初始化,nvs初始化,创建二进制信号量

(2)创建串口接收数据的任务

(3)当串口中接收到指定的数据后,通过结构体传参,完成wifi的初始化及连接操作,在wifi的回调函数中give信号量

(4)在main方法中如果take到信号量,则进行mqtt的连接并发送成功的消息

#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 "MYUART.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "mymqtt.h"


SemaphoreHandle_t wifi_connected_semaphore;

void app_main(void)
{
    wifi_connected_semaphore = xSemaphoreCreateBinary();
    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());
    }

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

1.wifi初始化相关代码

#include "simple_wifi_sta.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"

#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "myuart.h"
#include "mymqtt.h"
//需要把这两个修改成你家WIFI,测试是否连接成功
#define DEFAULT_WIFI_SSID           "HUAWEI-yellow"
#define DEFAULT_WIFI_PASSWORD       "wangxiaohuang.."

static const char *TAG = "wifi";

//事件通知回调函数
static wifi_event_cb    wifi_cb = NULL;
extern SemaphoreHandle_t wifi_connected_semaphore;

/** 事件回调函数
 * @param arg   用户传递的参数
 * @param event_base    事件类别
 * @param event_id      事件ID
 * @param event_data    事件携带的数据
 * @return 无
*/
static void event_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data)
{   
    if(event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
        case WIFI_EVENT_STA_START:      //WIFI以STA模式启动后触发此事件
            esp_wifi_connect();         //启动WIFI连接
            break;
        case WIFI_EVENT_STA_CONNECTED:  //WIFI连上路由器后,触发此事件
            ESP_LOGI(TAG, "connected to AP");
            break;
        case WIFI_EVENT_STA_DISCONNECTED:   //WIFI从路由器断开连接后触发此事件
            esp_wifi_connect();             //继续重连
            ESP_LOGI(TAG,"connect to the AP fail,retry now");
            break;
        default:
            break;
        }
    }
    if(event_base == IP_EVENT)                  //IP相关事件
    {
        switch(event_id)
        {
            case IP_EVENT_STA_GOT_IP:           //只有获取到路由器分配的IP,才认为是连上了路由器
                if(wifi_cb)
                    wifi_cb(WIFI_CONNECTED);
                ESP_LOGI(TAG,"get ip address");
                
                xSemaphoreGive(wifi_connected_semaphore);

               //mqtt_start();
                break;
        }
    }
}



// 注册事件处理器
void register_wifi_events() {
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
}
//WIFI STA初始化
esp_err_t wifi_sta_init(const wifi_config_params_t *params)//,const wifi_config_params_t *params
{   

    printf("wifi_sta_init start \r\n");
    printf("SSID: %s, Password: %s\n", params->ssid, params->password);

    //初始化WIFI
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
    //注册事件
    //ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT,ESP_EVENT_ANY_ID,&event_handler,NULL));
    //ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT,IP_EVENT_STA_GOT_IP,&event_handler,NULL));

    //WIFI配置
   wifi_config_t wifi_config;
    memset(&wifi_config, 0, sizeof(wifi_config)); // 清零结构体

    // 复制 SSID 和密码到结构体
    strncpy((char*)wifi_config.sta.ssid, params->ssid, sizeof(wifi_config.sta.ssid) - 1);
    wifi_config.sta.ssid[sizeof(wifi_config.sta.ssid) - 1] = '\0'; // 确保字符串以 NULL 结尾

    strncpy((char*)wifi_config.sta.password, params->password, sizeof(wifi_config.sta.password) - 1);
    wifi_config.sta.password[sizeof(wifi_config.sta.password) - 1] = '\0'; // 确保字符串以 NULL 结尾

    // 设置其他 WiFi 参数
    wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
    wifi_config.sta.pmf_cfg.capable = true;
    wifi_config.sta.pmf_cfg.required = false;

    printf("SSID: %s, Password: %s\n", wifi_config.sta.ssid, wifi_config.sta.password);
    //启动WIFI
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );         //设置工作模式为STA
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );   //设置wifi配置
    ESP_ERROR_CHECK(esp_wifi_start() );                         //启动WIFI
    //ESP_ERROR_CHECK(esp_wifi_connect());
    ESP_LOGI(TAG, "wifi_init_sta finished.");
    
    return ESP_OK;
}

2.串口相关代码
 

/* UART asynchronous example, that uses separate RX and TX tasks

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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>



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] = {0};
char wifi_password[65] = {0};

//定义一个事件组,用于通知main函数WIFI连接成功
#define WIFI_CONNECT_BIT     BIT0


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+";
    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);
                    params->password = wifi_password;
                    params->ssid = wifi_ssid;
                    wifi_sta_init(params); 
                    free(params);
                    //break;
                    
                } 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;
}

3串口相关代码

#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"


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"      //测试用的,需要订阅的主题


//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(void)
{
    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);
   
}


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));
    }

}

遇到的问题

1.esp_logi 或者esp_loge 无法打印日志输出到控制台,后边更改了串口号为 UART_NUM_0,就可解决打印,espidf乐鑫给的示例为UART_NUM_1

2.全局变量的使用,网上视频讲解的全局变量及局部变量的用法都在同一个类中演示完成,忽略了extern 关键字的使用,如果有跟我一样的菜鸟,可以去学习一下extren关键字的使用

3.结构体初始化的问题,下图两种都可以进行结构体的初始化,如果动态传参,我使用了注释掉的第一种。

4.串口接收问题,串口中明明发送的数据正确,并且打印的日志 wifi用户名密码也正确,就是无法连接上wifi,后面我在串口接收的后边加上了trim_spaces去除空格的代码操作,就可以正确的连接上wifi。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值