ESP32-C3通过串口配网,led指示灯显示网络状态

一、前言

本文章基于VS Code IDE进行编程,基于Ubuntu进行编译、下载、运行等操作
串口助手:UartAssist.exe
代码结合官方例程中的station_example_main.c 和 uart_echo_example_main.c,基于uart_echo_example_main.c上进行修改

直接配网介绍

直接配网,就是通过UART串口、SPI口、SDIO口、I2C等主机接口,按照一定的通信协议,将SSID和密码,直接传递给WIFI模块。WIFI模块在收到SSID和密码后去连接热点或路由器,并将连接的结果从主机接口返回

二、硬件连接

首先要准备的材料:ESP32C3,USB串口转换器,杜邦线若干,LED灯,USB数据线
背面
注:这里用GPIO7作为输出引脚,GPIO5为RX,GPIO4为TX。

三、命令

打开Ubuntu,先cd进入esp-idf,然后

. ./export.sh

idf.py set-target esp32c3  		    		//芯片类型

idf.py build        					    //编译
idf.py -b 1500000 -p /dev/ttyS4 flash		//烧录:1500000烧录的波特率 /dev/ttyS4:烧录的串口
idf.py -p /dev/ttyS4 monitor				//运行

idf.py menuconfig 							//更改配置

四、完整代码及注释分析

/* UART Echo Example

   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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include <cJSON.h>

#define GPIO_OUTPUT_IO_0    7
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0))

#define EXAMPLE_ESP_WIFI_SSID      "WT"
#define EXAMPLE_ESP_WIFI_PASS      "0987654321"

#define ECHO_TEST_TXD (CONFIG_EXAMPLE_UART_TXD)//发送数据
#define ECHO_TEST_RXD (CONFIG_EXAMPLE_UART_RXD)//接收数据
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)//请求发送
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)//清除发送

#define ECHO_UART_PORT_NUM      (CONFIG_EXAMPLE_UART_PORT_NUM)//端口号
#define ECHO_UART_BAUD_RATE     (CONFIG_EXAMPLE_UART_BAUD_RATE)
#define ECHO_TASK_STACK_SIZE    (CONFIG_EXAMPLE_TASK_STACK_SIZE)

static const char *TAG = "UART TEST";//串口测试

#define BUF_SIZE (1024)//缓冲区大小

static int s_retry_num = 0;/*重试次数*/

static void event_handler(void* arg, esp_event_base_t event_base,/*在调度事件时调用的处理程序函数*/
                                int32_t event_id, void* event_data)/*将事件发布到队列时调用的函数*/
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();/*wifi连接*/
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {/*站与AP断开连接*/
        if (s_retry_num < 5) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");/*重新尝试连接AP*/
        } else {
            //xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");/*AP连接失败*/
        gpio_set_level(GPIO_OUTPUT_IO_0, 0);//连接失败,led灯不亮
        
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {/*工作站从连接的AP获取IP*/
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        gpio_set_level(GPIO_OUTPUT_IO_0, 1);//连接成功后led被点亮
        //xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(char* ssid,char* pwd)
{
     ESP_ERROR_CHECK(esp_netif_init());/*ESP_ERROR_CHECK  宏,用于检查错误代码,并在代码未ESP_OK时终止程序,将错误代码、错误位置和失败语句打印到串行输出*/
/*esp_netif_init  初始化基础 TCP/IP 堆栈*/

    ESP_ERROR_CHECK(esp_event_loop_create_default());/*创建默认事件循环*/
    esp_netif_create_default_wifi_sta();/*创建默认的 WIFI STA。如果发生任何初始化错误,此 API 将中止*/

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();/*传递给esp_wifi_init调用的 WiFi 堆栈配置参数*/
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
    /*将事件处理程序的实例注册到默认循环*/                 ESP_EVENT_ANY_ID,/*任何事件 ID 的注册处理程序*/
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,/*工作站从连接的AP获取IP*/
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {0};
    memcpy(wifi_config.sta.ssid,ssid,strlen(ssid));
    memcpy(wifi_config.sta.password,pwd,strlen(pwd));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );//模式
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );//配置
    ESP_ERROR_CHECK(esp_wifi_start() );//根据当前配置启动wifi
}


static void echo_task(void *arg)
{
    /*配置UART驱动器的参数,通信引脚并安装驱动程序*/
    uart_config_t uart_config = {/*参数配置*/
        .baud_rate = ECHO_UART_BAUD_RATE,/*波特率*/
        .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,/*源时钟默认为APB*/
        /*APB1操作速度限于36MHz,APB2操作于全速(最高72MHz)。
        APB2负责ADC,串口1, SPI1,高级TIMI/O,EXTI和AFIO。
        APB1负责DAC,USB,I2C,CAN,串口2345,SPI23,普通TIM*/
    };
    int intr_alloc_flags = 0;/*用于分配中断的标志*/

#if CONFIG_UART_ISR_IN_IRAM
    intr_alloc_flags = ESP_INTR_FLAG_IRAM;//如果禁用了缓存,则可以调用ISR
#endif

    ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
    ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));/*设置UART配置参数 */
    ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));//将UART外设的信号分配给GPIO引脚


    //为传入数据配置临时缓冲区
    uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
    char ssid[32] = {0};
    char pwd [64] = {0};
    // int  i =0;
    while (1) {
        //从UART读取数据
        int len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
        //将数据写回UART
        uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) data, len);
        if (len) {
            data[len] = '\0';
            //以下三种方式任选其一,具体效果看本文章底部扩展部分
            /*char *message =(char *) data;

            cJSON* cjson_test = NULL;
            cJSON* cjson_ssid = NULL;
            cJSON* cjson_password = NULL;

            cjson_test = cJSON_Parse(message);
            if(cjson_test != NULL)
            {
            cjson_ssid = cJSON_GetObjectItem(cjson_test, "ssid");
            cjson_password = cJSON_GetObjectItem(cjson_test, "password");

            printf("ssid:%s\n", cjson_ssid->valuestring);
            printf("password:%s\n", cjson_password->valuestring);
            }else{
                printf("parse fail.\n");
            }
            memcpy(ssid,(char *)cjson_ssid->valuestring,strlen((char *)cjson_ssid->valuestring));
            memcpy(pwd,(char *)cjson_password->valuestring,strlen((char *)cjson_password->valuestring));
            break;*///用json格式发送账号密码

            /*char *p = strtok((char *)data,".");
            memcpy(ssid,p,strlen(p));
            printf("ssid:%s\r\n",ssid);
            p = strtok(NULL,".");
            memcpy(pwd,p,strlen(p));
            printf("password:%s\r\n",pwd);
            break;*///一次性发完账号密码

            /*if(i == 0){
                memcpy(ssid,(char *)data,strlen((char *)data));
               printf("===%s====\r\n",ssid);
               i++;
          }else{
              memcpy(pwd,(char *)data,strlen((char *)data));
                printf("===%s====\r\n",pwd);
                break;
            } *///分两次发送账号密码
        }
    }
    wifi_init_sta(ssid,pwd);
    vTaskDelete(NULL);
}

void app_main(void)
{
     esp_err_t ret = nvs_flash_init();

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());//erase擦除
      ret = nvs_flash_init();//ret 子程序的返回指令
    }
    ESP_ERROR_CHECK(ret);
    xTaskCreate(echo_task, "uart_echo_task", 4096, NULL, 10, NULL);//任务创建

    //因为用到led指示灯所以要进行一些初始化配置
    gpio_config_t io_conf = {};//零初始化配置结构
    io_conf.intr_type = GPIO_INTR_DISABLE;//禁用中断
    io_conf.mode = GPIO_MODE_OUTPUT;//设置为输出模式
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;//要设置的管脚的位掩码,例如GPIO18/19
    io_conf.pull_down_en = 0;//禁用下拉模式    
    io_conf.pull_up_en = 0;//禁用上拉模式
    gpio_config(&io_conf);//使用给定设置配置GPIO
}

扩展:账号密码多样发送方式

  • 分别发送账号,密码(发两次)
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);//为传入数据配置临时缓冲区
char ssid[32] = {0};
char pwd [64] = {0};
int  i =0;
while (1) {
        int len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);// 从UART读取数据
        uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) data, len);// 将数据写回UART
        if (len) {
            data[len] = '\0';
            if(i == 0){
               memcpy(ssid,(char *)data,strlen((char *)data));
               printf("===%s====\r\n",ssid);
               i++;
          }else{
              memcpy(pwd,(char *)data,strlen((char *)data));
              printf("===%s====\r\n",pwd);
              break;
            }
        }
     }
    wifi_init_sta(ssid,pwd);
    vTaskDelete(NULL);

串口工具发送页面:
在这里插入图片描述

  • 同时发送账号密码(只发一次)
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);//为传入数据配置临时缓冲区
char ssid[32] = {0};
char pwd [64] = {0};
while (1) {
        int len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);// 从UART读取数据
        uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) data, len);// 将数据写回UART
        if (len) {
            data[len] = '\0';
            char *p = strtok((char *)data,".");
            memcpy(ssid,p,strlen(p));
            printf("ssid:%s\r\n",ssid);
            p = strtok(NULL,".");
            memcpy(pwd,p,strlen(p));
            printf("password:%s\r\n",pwd);
            break;
            }
        }
     }
    wifi_init_sta(ssid,pwd);
    vTaskDelete(NULL);

运行效果:
发送:WiFi账号.密码
收到数据:
接收到账号密码
进行连接,连接成功获取到IP:
进行连接

  • 用Json格式发送
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);//为传入数据配置临时缓冲区
char ssid[32] = {0};
char pwd [64] = {0};
while (1) {
        int len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);// 从UART读取数据
        uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) data, len);// 将数据写回UART
        if (len) {
            data[len] = '\0';
            char *message =(char *) data;
            cJSON* cjson_test = NULL;
            cJSON* cjson_ssid = NULL;
            cJSON* cjson_password = NULL;

            cjson_test = cJSON_Parse(message);
            if(cjson_test != NULL)
            {
            cjson_ssid = cJSON_GetObjectItem(cjson_test, "ssid");
            cjson_password = cJSON_GetObjectItem(cjson_test, "password");

            printf("ssid:%s\n", cjson_ssid->valuestring);
            printf("password:%s\n", cjson_password->valuestring);
            }else{
                printf("parse fail.\n");
            }
            memcpy(ssid,(char *)cjson_ssid->valuestring,strlen((char *)cjson_ssid->valuestring));
            memcpy(pwd,(char *)cjson_password->valuestring,strlen((char *)cjson_password->valuestring));
            break;
            }
        }
     }
    wifi_init_sta(ssid,pwd);
    vTaskDelete(NULL);

串口发送json格式的账号密码:
在这里插入图片描述
成功的话就能获取到IP

小结

因为涉及到的GPIO内容不多,就没有进行详细的解释

#define GPIO_OUTPUT_IO_0    7  //这里使用7号引脚进行led灯的输出
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0))

gpio_set_level(GPIO_OUTPUT_IO_0, 0);//熄灭led
gpio_set_level(GPIO_OUTPUT_IO_0, 1);//点亮led
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值