本文共 9194 字,大约阅读时间需要 30 分钟。
ESP32-C3模组是4月初发布上线的一款双模(2.4GWiFi+BLE5.0)的通信模块,博主手上的是一款外置2M Flash的型号ESP32-C3F:
把ESP-IDF中的一个空例程复制到自己工程目录中,空例程在ESP-IDF中的路径如下:
.../esp=idf/examples/get-started/sample_project
主函数文件就在该目录下的:main文件夹中的main.c
WS2812的驱动其实乐鑫的ESP-IDF中就做有相关的例程:…/examples/peripherals/rmt/led_strip,这个例程使用了RMT红外驱动,还连接了les_strip 驱动库(库路径:…/examples/common_components/led_strip)。但是那个例程无法驱动WS2812灯条,但是它的库却可以使用,所以要移植一下这个库。
在main文件夹的同级目录下创建一个名为compornents的文件夹:
mkidr compornents
#include "driver/rmt.h"#include "led_strip.h"#include "esp_system.h"#include "esp_log.h"
定义RMT发送管脚和频道及灯珠个数的宏
#define RMT_TX_NUM 3 //发送口#define RMT_TX_CHANNEL RMT_CHANNEL_0//发送频道#define LED_STRIP_NUM 24//灯珠数量
void init_led(){ rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_TX_NUM, 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(24, (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));}
缓存颜色的结构体
struct WS2812_COLOR{ uint32_t red;uint32_t green ;uint32_t blue;};struct WS2812_COLOR WS2812_RGB;
void set_rgb(uint16_t Red, uint16_t Green, uint16_t Blue){ for (int i = 0; i < LED_STRIP_NUM; i++) { strip->set_pixel(strip, i, Red, Green, Blue);//设置颜色 } WS2812_RGB.red = Red; WS2812_RGB.green = Green; WS2812_RGB.blue = Blue; strip->refresh(strip, 10);}
UART通信同样也可以使用ESP-IDF的example,参考的例程在:examples/peripherals/uart/uart_async_rxtxtasks 目录之下。这个例程使用了FreeRTOS来管理UART的发送函数和接收函数,博主也照样使用了freeRTOS ,UART驱动这里只是使用了FreeRTOS 创建任务,并没有使用太多FreeRTOS的各种功能,所以对于不太熟悉FreeRTOS 的同学也不要怕。
同样的,先要引用一下相关头文件
/*********RTOS Handle-file****************/#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "freertos/event_groups.h"/*********UART Handle-file*****************/#include "driver/gpio.h"#include "driver/uart.h"
串口初始化配置函数:
void UART_Init(void){ const uart_config_t uart_config = { .baud_rate =38400, //波特率 .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_APB,//时钟 }; // We won't use a buffer for sending data. uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0); uart_param_config(UART_NUM_1, &uart_config); uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);}
在主函数中初始化UART之后,就创建串口的接收数据的任务,提高数据接收的速度:
//开启串口接收任务xTaskCreate(uartRxTask, "uart_rx_task", 1024*3, NULL, 4, NULL);
任务函数:
static void uartRxTask(void *arg){ static const char *RX_TASK_TAG = "RX_TASK"; esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO); uint8_t *data=(uint8_t *)malloc(RX_BUF_SIZE+1); while (1) { const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_RATE_MS); if (rxBytes > 0) { data[rxBytes] = 0; ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data); memset(cJson_data, 0, sizeof(cJson_data)); uartControlLedStrip(uartDataHandle(data)); //设置灯条颜色 ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO); } } free(data);}
说明: uartControlLedStrip函数和uartDataHandle函数是博主自己写一个串口数据处理函数,目的是把串口接收的数据中提取控制RGB的指令或者数据,各位同学可通过自己的需求去写这样的函数,比如提取指令ID号,只是用一些字符串操作的函数就可以了。
WiFi的配置连接极为简单,不需要些配置函数,也不需要写初始化函数,需要连接的WiFi名称和密码都可以通过menuconfig来配置。因为ESP-IDF的例程中,已经写好了相关的配置文件,只需要简单的修改就可以配置连接WiFi。
/**********WiFI Handle-file**************/#include "esp_netif.h"#include "protocol_examples_common.h"#include "esp_wifi.h"#include "nvs_flash.h"
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()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(esp_netif_init()); /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect());
在工程文件目录下,打开CMakefile文件并加入以下内容:
set(EXTRA_COMPONENT_DIRS $ENV{ IDF_PATH}/examples/common_components/protocol_examples_common)
idf.py flash //烧录程序进ESP32C3idf.py monitor //监控串口
获取到IP地址,连接成功。
本项目是使用阿里云远程控制RGB灯条,所以需要在阿里云物联网平台创建响应的产品和设备。创建方式请参考: .
除此之外,还需要了解阿里云连接所需要的三元组与MQTT客户端和密码的关系。这个请参考:.中的一机一密接入。博主是使用创建RTOS任务的方式来配置MQTT,因为在配置的过程中需要引入MQTT事件回调函数,该函数可以负责处理各个事件所要执行的动作,比如MQTT连接成功事件:MQTT_EVENT_CONNECTED;订阅成功事件:MQTT_EVENT_SUBSCRIBED等。当然收到数据事件也在其中。
当MQTT有一个事件被触发的时候,就会跳到事件回调函数去执行相关事件下的动作。所以创建任务来管理是很有必要的。/***********MQTT Handle-file************/#include "mqtt_client.h"#include "lwip/sockets.h"#include "lwip/dns.h"#include "lwip/netdb.h"
void TaskXMqttRecieve(void *p){ //连接的配置参数#if MQTT_LINK_Aliyum sprintf(MQTT_LINK_USERNAME,"%s&%s",MQTT_DeviceName,MQTT_ProductKey);#endif esp_mqtt_client_config_t mqtt_cfg = { .host = MQTT_LINK_HOST, //连接的域名 ,请务必修改为您的 .port = MQTT_LINK_PORT, //端口,请务必修改为您的 .username = MQTT_LINK_USERNAME, //用户名,请务必修改为您的 .password = MQTT_LINK_PASS, //密码,请务必修改为您的 .client_id = MQTT_LINK_CLIENT_ID, .event_handle = MqttCloudsCallBack, //设置回调函数 .keepalive = 120, //心跳 .disable_auto_reconnect = false, //开启自动重连 .disable_clean_session = false, //开启 清除会话 }; client = esp_mqtt_client_init(&mqtt_cfg); esp_mqtt_client_start(client); vTaskDelete(NULL);}
博主自己定义的宏
#define MQTT_LINK_HOST "www.baidu.com" //域名:改成自己的域名#define MQTT_LINK_PORT 1883//端口//是否开启发布 1 为开启发布 0:关闭发布/***** 阿里云设备三元组 ************/#define MQTT_DeviceName "xxxxx"//设备名:改成你们自己的域名#define MQTT_ProductKey "xxxx"//char MQTT_LINK_USERNAME[128];#define MQTT_LINK_PASS "xxxxxxxxxxxxx" //加过hmacmd5加密后的密码#define MQTT_LINK_CLIENT_ID "改成自己的设备名|securemode=3,signmethod=hmacmd5|"//客户端ID 阿里云
域名查看方式:注意看鼠标箭头
如果通过阿里云控制设备,就必须订阅阿里云的设备Topic,
先查看以下阿里云的产品 ——>Topic类列表——>属性Topic 中的属性设置。这个Topic中,
“g6oroz0VLBN” 是产品的:ProductKey “${deviceName}” 是设备名:DeviceName 比如现有有以下设备信息:ProductKey = abcdf12;DeviceName=asdvsa。那么该Topic就是:/sys/abcdf12/asdvsa/thing/service/property/set。 订阅主题的代码:sprintf(MqttTopicSub, "/sys/%s/%s/thing/service/property/set",MQTT_ProductKey,MQTT_DeviceName); ESP_LOGI(TAG, "MqttTopicSub: %s", MqttTopicSub); //ESP_LOGI(TAG, "MqttTopicPub: %s", MqttTopicPub); ESP_ERROR_CHECK(esp_event_loop_create_default());
case MQTT_EVENT_DATA: { printf("TOPIC=%.*s \r\n", event->topic_len, event->topic); printf("DATA=%.*s \r\n\r\n", event->data_len, event->data); //发送数据到队列 struct __User_data *pTmper; memset(&user_data,0,sizeof(struct __User_data)); sprintf(user_data.allData, "%.*s",event->data_len,event->data); pTmper = &user_data; user_data.dataLen = event->data_len; //把数据发送到消息队列 xQueueSend(ParseJSONQueueHandler, (void *)&pTmper, portMAX_DELAY); break; }
void Task_ParseJSON(void *pvParameters){ printf("[SY] Task_ParseJSON_Message creat ... \n"); while (1) { struct __User_data *pMqttMsg; printf("Task_ParseJSON_Message xQueueReceive wait [%d] ... \n", esp_get_free_heap_size()); xQueueReceive(ParseJSONQueueHandler, &pMqttMsg, portMAX_DELAY); printf("Task_ParseJSON_Message xQueueReceive get [%s] ... \n", pMqttMsg->allData); 首先整体判断是否为一个json格式的数据 cJSON *pJsonRoot = cJSON_Parse(pMqttMsg->allData); //如果是否json格式数据 if (pJsonRoot == NULL) { printf("[SY] Task_ParseJSON_Message xQueueReceive not json ... \n"); goto __cJSON_Delete; } cJSON *pJsonParams = cJSON_GetObjectItem(pJsonRoot,"params"); if(pJsonParams==NULL){ printf("pJsonParams NULL\r\n"); goto __cJSON_Delete; } cJSON *pJsonGRB =cJSON_GetObjectItem(pJsonParams,"RGB"); if(pJsonGRB==NULL) goto __cJSON_Delete; cJSON *pJSON_Item_Red = cJSON_GetObjectItem(pJsonGRB, "R"); cJSON *pJSON_Item_Gree = cJSON_GetObjectItem(pJsonGRB, "G"); cJSON *pJSON_Item_Blue = cJSON_GetObjectItem(pJsonGRB, "B"); //设置RGB颜色 set_rgb(pJSON_Item_Red->valueint, pJSON_Item_Gree->valueint, pJSON_Item_Blue->valueint); //播放提示音 if(WS2812_RGB.red!=0&&WS2812_RGB.green!=0&&WS2812_RGB.blue!=0); else uart_write_bytes(UART_NUM_1,"AT+PLAY=21\r\n",13);//播放:已经打开灯了 if(WS2812_RGB.red!=254&&WS2812_RGB.green!=254&&WS2812_RGB.blue!=254)goto __cJSON_Delete; else uart_write_bytes(UART_NUM_1,"AT+PLAY=22\r\n",13);//播放:已经关灯了 __cJSON_Delete: cJSON_Delete(pJsonRoot); }}
转载地址:http://ltlwz.baihongyu.com/