文章目录

  • 一、前言
  • 二、系统架构
  • 2.1 系统架构
  • 2.2 组件component
  • 三、软件流程
  • 3.1 初始化流程
  • 3.2 WiFi任务回调流程
  • 3.3 MQTT订阅接收消息流程
  • 3.4 按键扫描执行流程
  • 四、button
  • 五、rgb_led
  • 六、mqtt_solo
  • 6.1 mqtt订阅
  • 6.2 mqtt发布
  • 6.3 mqtt接收处理消息
  • 6.4 mqtt启动
  • 七、Flash 烧录&运行
  • 7.1 固件文件编译&下载
  • 7.2 配置文件生成&烧写
  • 7.3 运行
  • 八、其他

一、前言

本文基于VS Code IDE进行编程、编译、下载、运行等操作
基础入门章节请查阅:ESP32-C3入门教程 基础篇①——基于VS Code构建Hello World 教程目录大纲请查阅:ESP32-C3入门教程——导读

ESP32-C3入门教程 IoT篇①——阿里云 物联网平台 EspAliYun RGB LED 实战ESP32-C3入门教程 IoT篇②——阿里云 物联网平台 EspAliYun RGB LED 实战之ESP32固件端源码解析ESP32-C3入门教程 IoT篇③——阿里云 物联网平台 EspAliYun RGB LED 实战之Android端源码解析ESP32-C3入门教程 IoT篇④——阿里云 物联网平台 EspAliYun RGB LED 实战之微信小程序端源码解析ESP32-C3入门教程 IoT篇⑤——阿里云 物联网平台 EspAliYun RGB LED 实战之设备生产流程ESP32-C3入门教程 IoT篇⑥——阿里云 物联网平台 EspAliYun RGB LED 实战之设备批量生产工具

ESP32固件端源码已经全部开源:小康师兄 / EspAliYun (gitee地址)

二、系统架构

2.1 系统架构











app_main 
         

           button 
         

           rgb_led 
         

           mqtt_solo 
         

           driver/gpio 
         

           common_components/led_strip 
         

           components/esp-aliyun 
         

           conn_mgr 
         

           iotkit-embedded 
         

           wrappers


2.2 组件component

两个外部组件component,分别通过两种方式依赖进来。

ESP32 mqtt掉线严重 esp32 iot_彩色灯

三、软件流程

3.1 初始化流程


app_main 
     
       button 
     
       rgb_led 
     
       conn_mgr 
     
    button_init() 
  
    gpio配置 
  
    创建按键轮询线程 
  
    rgb_led_init() 
  
    install ws2812 driver 
  
    Clear LED strip 
  
    conn_mgr_init() 
  
    esp_event_loop_init(...) 
  
    esp_wifi_init(&cfg) 
  
    esp_wifi_set_storage(WIFI_STORAGE_RAM) 
  
    esp_wifi_set_mode(WIFI_MODE_STA) 
  
    esp_wifi_start() 
  
    conn_mgr_register_wifi_event(wifi_event_handle) 
  
    conn_mgr_set_wifi_config_ext(...) 
  
    conn_mgr_start() 
  
      app_main 
    
      button 
    
      rgb_led 
    
      conn_mgr

3.2 WiFi任务回调流程

WiFi任务 
     
       app_main 
     
       mqtt_solo 
     
       wrappers 
     
       iotkit-embedded 
     
    SYSTEM_EVENT_STA_GOT_IP 
  
    创建mqtt线程 
  
    HAL_GetProductKey(...) 
  
    从nvs中获取productKey 
  
    HAL_GetDeviceName(...) 
  
    从nvs中获取deviceName 
  
    HAL_GetDeviceSecret(...) 
  
    从nvs中获取deviceSecret 
  
    IOT_MQTT_Construct(...) 
  
    example_subscribe(...) 
  
    example_publish(...) 
  
    while(...) 
  
      WiFi任务 
    
      app_main 
    
      mqtt_solo 
    
      wrappers 
    
      iotkit-embedded


3.3 MQTT订阅接收消息流程

MQTT任务 
     
       mqtt_solo 
     
       rgb_led 
     
    example_message_arrive 
  
    cJSON解析 
  
    rgb_led_update() 
  
    example_publish() 
  
      MQTT任务 
    
      mqtt_solo 
    
      rgb_led

3.4 按键扫描执行流程


button 
     
       按键扫描线程 
     
       rgb_led 
     
       mqtt_solo 
     
    按键扫描线程 轮询 
  
    按键短按,Led开关 
  
    rgb_led_update() 
  
    example_publish() 
  
    按键长按,Led颜色渐变 
  
    rgb_led_update() 
  
    example_publish() 
  
      button 
    
      按键扫描线程 
    
      rgb_led 
    
      mqtt_solo


四、button

  • 按键部分可以查阅之前的博文:ESP32-C3入门教程 基础篇②——GPIO口输入,按键的长按和短按
  • 主要实现的功能是
  • 按键短按的时候,开关Led
  • 按键长按的时候,彩色灯的颜色渐变,变亮
//按键扫描
void read_button()
{
    if(gpio_get_level(GPIO_INPUT_IO_0)==0){
        uint32_t tick1 = xTaskGetTickCount();
        uint32_t tick2 = xTaskGetTickCount();
        while(gpio_get_level(GPIO_INPUT_IO_0)==0){
            vTaskDelay(10 / portTICK_RATE_MS);
            if(xTaskGetTickCount()>tick1+100){
                tick1 = xTaskGetTickCount();
                ESP_LOGI(TAG, "按键长按\n");
                rgb_led.red*=1.1;
                rgb_led.green*=1.1;
                rgb_led.blue*=1.1;

                rgb_led.red=rgb_led.red>255?255:rgb_led.red;
                rgb_led.green=rgb_led.green>255?255:rgb_led.green;
                rgb_led.blue=rgb_led.blue>255?255:rgb_led.blue;

                rgb_led_update();
                example_publish();
            }
        }
        if(xTaskGetTickCount()>tick2 && xTaskGetTickCount()<tick2+100){
            ESP_LOGI(TAG, "按键短按\n");
            if(rgb_led.led_switch)
            {
                rgb_led.led_switch = 0;
            }else{
                rgb_led.led_switch = 1;
                if(rgb_led.red==0&&rgb_led.green==0&&rgb_led.blue==0){
                    rgb_led.red=100;
                    rgb_led.green=100;
                    rgb_led.blue=100;
                }
            }
            rgb_led_update();
            example_publish();
        }
    }
}

五、rgb_led

  • rgb_led_update(),更新彩色灯状态
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "esp_log.h"
#include "driver/rmt.h"
#include "led_strip.h"
#include "rgb_led.h"


#define RMT_TX_CHANNEL RMT_CHANNEL_0
#define EXAMPLE_RMT_TX_GPIO 8
#define EXAMPLE_STRIP_LED_NUMBER 1

static const char *TAG = "rgb_led";

led_strip_t *strip;
rgb_led_t rgb_led;

void rgb_led_update(void)
{
    ESP_LOGI(TAG, "rgb_led_update led_switch: %d, red: %d, green: %d, blue: %d", rgb_led.led_switch, rgb_led.red, rgb_led.green, rgb_led.blue);
    if(rgb_led.led_switch){
        strip->set_pixel(strip, 0, rgb_led.red, rgb_led.green, rgb_led.blue);
    }else{
        strip->set_pixel(strip, 0, 0, 0, 0);
    }
    strip->refresh(strip, 50);
}
  • rgb_led_init(),初始化彩色灯
void rgb_led_init(void)
{
    rmt_config_t config = RMT_DEFAULT_CONFIG_TX(EXAMPLE_RMT_TX_GPIO, RMT_TX_CHANNEL);
    // set counter clock to 40MHz
    config.clk_div = 2;

    ESP_ERROR_CHECK(rmt_config(&config));
    ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

    // install ws2812 driver
    led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(EXAMPLE_STRIP_LED_NUMBER, (led_strip_dev_t)config.channel);
    strip = led_strip_new_rmt_ws2812(&strip_config);
    if (!strip) {
        ESP_LOGE(TAG, "install WS2812 driver failed");
    }
    // Clear LED strip (turn off all LEDs)
    ESP_ERROR_CHECK(strip->clear(strip, 100));
    // Show simple rainbow chasing pattern
    ESP_LOGI(TAG, "LED Rainbow Chase Start");
}

六、mqtt_solo

6.1 mqtt订阅

int example_subscribe(void *handle)
{
    int res = 0;
    const char *fmt = "/sys/%s/%s/thing/service/property/set";
    char *topic = NULL;
    int topic_len = 0;

    topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    topic = HAL_Malloc(topic_len);
    if (topic == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(topic, 0, topic_len);
    HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, example_message_arrive, NULL);
    if (res < 0) {
        EXAMPLE_TRACE("subscribe failed");
        HAL_Free(topic);
        return -1;
    }

    HAL_Free(topic);
    return 0;
}

6.2 mqtt发布

int example_publish(void)
{
    int             res = 0;
    char           *topic = NULL;
    const char     *topic_fmt = "/sys/%s/%s/thing/event/property/post";
    int             topic_len = 0;
    char           *payload = NULL; 
    const char     *payload_fmt = "{ \
                        \"version\": \"1.0\", \
                        \"params\": { \
                            \"RGBColor\": { \
                                \"value\": %s, \
                            },\
                            \"LEDSwitch\": { \
                                \"value\": %d, \
                            }\
                        },\
                        \"method\": \"thing.event.property.post\"  \      
                    }";
    int             payload_len = 0;
    cJSON          *rgb =  cJSON_CreateObject();

    topic_len = strlen(topic_fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    topic = HAL_Malloc(topic_len);
    if (topic == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(topic, 0, topic_len);
    HAL_Snprintf(topic, topic_len, topic_fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    cJSON_AddItemToObject(rgb, "Red", cJSON_CreateNumber(rgb_led.red));
    cJSON_AddItemToObject(rgb, "Green", cJSON_CreateNumber(rgb_led.green));
    cJSON_AddItemToObject(rgb, "Blue", cJSON_CreateNumber(rgb_led.blue));    
    payload_len = strlen(payload_fmt) + strlen(cJSON_Print(rgb)) + 1 + 1;
    payload = HAL_Malloc(payload_len);
    if (payload == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(payload, 0, payload_len);
    HAL_Snprintf(payload, payload_len, payload_fmt, cJSON_Print(rgb), rgb_led.led_switch);
    EXAMPLE_TRACE("publish %s", payload);

    res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload));
    if (res < 0) {
        EXAMPLE_TRACE("publish failed, res = %d", res);
        HAL_Free(topic);
        return -1;
    }

    HAL_Free(topic);
    return 0;
}

6.3 mqtt接收处理消息

void example_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
    cJSON * root = NULL;
    cJSON * params = NULL;
    cJSON * rgb_color = NULL;

    iotx_mqtt_topic_info_t     *topic_info = (iotx_mqtt_topic_info_pt) msg->msg;

    switch (msg->event_type) {
        case IOTX_MQTT_EVENT_PUBLISH_RECEIVED:
            /* print topic name and topic message */
            EXAMPLE_TRACE("Message Arrived:");
            EXAMPLE_TRACE("Topic  : %.*s", topic_info->topic_len, topic_info->ptopic);
            EXAMPLE_TRACE("Payload: %.*s", topic_info->payload_len, topic_info->payload);
            EXAMPLE_TRACE("\n");
            root = cJSON_Parse(topic_info->payload);     
            params = cJSON_GetObjectItem(root, "params");
            rgb_color = cJSON_GetObjectItem(params, "RGBColor");
            rgb_led.led_switch = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(params, "LEDSwitch"));
            rgb_led.red = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(rgb_color, "Red"));
            rgb_led.green = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(rgb_color, "Green"));
            rgb_led.blue = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(rgb_color, "Blue"));
            rgb_led_update();
            example_publish();
            break;
        default:
            break;
    }
}

6.4 mqtt启动

int mqtt_main(void *paras)
{
    void                   *pclient = NULL;
    int                     res = 0;
    iotx_mqtt_param_t       mqtt_params;

    HAL_GetProductKey(DEMO_PRODUCT_KEY);
    HAL_GetDeviceName(DEMO_DEVICE_NAME);
    HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);

    EXAMPLE_TRACE("mqtt example");

    /* Initialize MQTT parameter */
    /*
     * Note:
     *
     * If you did NOT set value for members of mqtt_params, SDK will use their default values
     * If you wish to customize some parameter, just un-comment value assigning expressions below
     *
     **/
    memset(&mqtt_params, 0x0, sizeof(mqtt_params));

    /**
     *
     *  MQTT connect hostname string
     *
     *  MQTT server's hostname can be customized here
     *
     *  default value is ${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com
     */
    /* mqtt_params.host = "something.iot-as-mqtt.cn-shanghai.aliyuncs.com"; */

    /**
     *
     *  MQTT connect port number
     *
     *  TCP/TLS port which can be 443 or 1883 or 80 or etc, you can customize it here
     *
     *  default value is 1883 in TCP case, and 443 in TLS case
     */
    /* mqtt_params.port = 1883; */

    /**
     *
     * MQTT request timeout interval
     *
     * MQTT message request timeout for waiting ACK in MQTT Protocol
     *
     * default value is 2000ms.
     */
    /* mqtt_params.request_timeout_ms = 2000; */

    /**
     *
     * MQTT clean session flag
     *
     * If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from
     * the current Session (as identified by the Client identifier).
     *
     * If CleanSession is set to 1, the Client and Server MUST discard any previous Session and Start a new one.
     *
     * default value is 0.
     */
    /* mqtt_params.clean_session = 0; */

    /**
     *
     * MQTT keepAlive interval
     *
     * KeepAlive is the maximum time interval that is permitted to elapse between the point at which
     * the Client finishes transmitting one Control Packet and the point it starts sending the next.
     *
     * default value is 60000.
     */
    /* mqtt_params.keepalive_interval_ms = 60000; */

    /**
     *
     * MQTT write buffer size
     *
     * Write buffer is allocated to place upstream MQTT messages, MQTT client will be limitted
     * to send packet no longer than this to Cloud
     *
     * default value is 1024.
     *
     */
    /* mqtt_params.write_buf_size = 1024; */

    /**
     *
     * MQTT read buffer size
     *
     * Write buffer is allocated to place downstream MQTT messages, MQTT client will be limitted
     * to recv packet no longer than this from Cloud
     *
     * default value is 1024.
     *
     */
    /* mqtt_params.read_buf_size = 1024; */

    /**
     *
     * MQTT event callback function
     *
     * Event callback function will be called by SDK when it want to notify user what is happening inside itself
     *
     * default value is NULL, which means PUB/SUB event won't be exposed.
     *
     */
    mqtt_params.handle_event.h_fp = example_event_handle;

    pclient = IOT_MQTT_Construct(&mqtt_params);
    if (NULL == pclient) {
        EXAMPLE_TRACE("MQTT construct failed");
        return -1;
    }

    res = example_subscribe(pclient);
    if (res < 0) {
        IOT_MQTT_Destroy(&pclient);
        return -1;
    }

    example_publish();
    while (1) {
        IOT_MQTT_Yield(pclient, 1000);
    }

    return 0;
}

七、Flash 烧录&运行

7.1 固件文件编译&下载

  • 编译&下载,如有遗忘,请查阅基础入门章节:ESP32-C3入门教程 基础篇①——基于VS Code构建Hello World

7.2 配置文件生成&烧写

  • 将从阿里云物联网平台获取的ProductKeyProductSecretDeviceNameDeviceSecret(第3.3章节),按照如下格式输入,保存成csv文件
key,type,encoding,value
aliyun-key,namespace,,
DeviceName,data,string,xxx1
DeviceSecret,data,string,xxx2
ProductKey,data,string,xxx3
ProductSecret,data,string,xxx4
  • 通过nvs_partition_gen.py ,转成配置文件bin文件
python $IDF_PATH/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py generate single_mfg_config.csv single_mfg.bin 0x4000
python C:\Espressif\frameworks\esp-idf-v4.4\components\nvs_flash\nvs_partition_generator\nvs_partition_gen.py generate single_mfg_config.csv single_mfg.bin 0x4000

ESP32 mqtt掉线严重 esp32 iot_ESP32 mqtt掉线严重_02

  • 烧写配置文件到ESP32-C3
python C:\Espressif\frameworks\esp-idf-v4.4\components\esptool_py\esptool\esptool.py -p COM6 -b 460800 write_flash 0x210000 single_mfg.bin
esptool.py v3.2-dev
Serial port COM6
Connecting....
Detecting chip type... ESP32-C3
Chip is ESP32-C3 (revision 3)
Features: Wi-Fi
Crystal is 40MHz
MAC: 84:f7:03:08:4d:80
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Flash will be erased from 0x00100000 to 0x00103fff...
Compressed 16384 bytes to 326...
Wrote 16384 bytes (326 compressed) at 0x00100000 in 0.2 seconds (effective 581.6 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

ESP32 mqtt掉线严重 esp32 iot_ESP32_03

7.3 运行

ESP32 mqtt掉线严重 esp32 iot_ESP32 mqtt掉线严重_04

八、其他

文章中关于CJSON部分内容,同学们如有不了解的可以查阅:ESP32-C3入门教程 系统篇④——cJSON应用实例 | C语言中超轻量级JSON解析器