得捷电子 Follow me 第2期任务提交汇总 全程esp-idf实现方案
[复制链接]
视频:https://training.eeworld.com.cn/video/38569
大家好,这里是日历,我参与了EEWORLD的“跟着我一起做”活动,采用了espidf方案进行全程开发。在这个项目中,我选择使用了Adafruit ESP32-S3 TFT Feather板,它搭载了强大的ESP32-S3控制器,并提供丰富的外设接口,包括液晶屏、WS2812B RGB灯珠以及i2c接口。
活动内容涉及多个领域,主要包括屏幕显示、网络通信、音乐播放、数据检测与记录等。通过这次活动,我深入学习了espidf的开发方式,同时掌握了在Adafruit ESP32-S3 TFT Feather板上使用不同外设的方法。
总算是板子9底月到货了然后我成功的拖更了一整个月(doge),这块Adafruit ESP32-S3 TFT Feather的常规写法各位的方案我看大多是CircuitPython 或者少数arduino的,但是笔者习惯了使用esp-idf方案写esp32的板子的程序的原因,这块板子也稍微扒拉了下引脚配置然后用espidf+lvgl方案完成这块板子的程序
首先说回来最麻烦的屏幕配置部分
MOSI 35
CLK 36
CS 7
DC 39
RESET 40
BLK 45
lvgl的移植部分各位可以直接使用esp官方的组件库的lvgl移植内容,但是在lvgl_esp32_drivers这个依赖的st7789.c的内容我根据arduino那边的对这块屏幕的配置更改了下具体如下
-
-
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "sdkconfig.h"
-
- #include "esp_log.h"
-
- #include "st7789.h"
-
- #include "disp_spi.h"
- #include "driver/gpio.h"
-
-
- #define TAG "st7789"
-
-
-
- typedef struct {
- uint8_t cmd;
- uint8_t data[16];
- uint8_t databytes;
- } lcd_init_cmd_t;
-
-
- static void st7789_set_orientation(uint8_t orientation);
-
- static void st7789_send_color(void *data, size_t length);
-
-
-
-
-
-
- void st7789_init(void)
- {
- lcd_init_cmd_t st7789_init_cmds[] = {
- {ST7789_SWRESET, {0}, 0x80},
- {ST7789_SLPOUT, {0}, 0x80},
- {ST7789_COLMOD, {0x55}, 1},
- {ST7789_MADCTL, {0x00}, 1},
- {ST7789_CASET, {0x00, 0x00, 0x00, 0xEF}, 4},
- {ST7789_RASET, {0x00, 0x00, 0x01, 0x3F}, 4},
- {ST7789_INVON, {0}, 0},
- {ST7789_NORON, {0}, 0x80},
- {ST7789_DISPON, {0}, 0x80},
- {0, {0}, 0xFF}
- };
-
-
- gpio_reset_pin(ST7789_DC);
- gpio_set_direction(ST7789_DC, GPIO_MODE_OUTPUT);
-
- #if !defined(ST7789_SOFT_RST)
- esp_rom_gpio_pad_select_gpio(ST7789_RST);
- gpio_set_direction(ST7789_RST, GPIO_MODE_OUTPUT);
- #endif
-
-
- #if !defined(ST7789_SOFT_RST)
- gpio_set_level(ST7789_RST, 0);
- vTaskDelay(100 / portTICK_PERIOD_MS);
- gpio_set_level(ST7789_RST, 1);
- vTaskDelay(100 / portTICK_PERIOD_MS);
- #else
- st7789_send_cmd(ST7789_SWRESET);
- #endif
-
- printf("ST7789 initialization.\n");
-
-
- uint16_t cmd = 0;
- while (st7789_init_cmds[cmd].databytes!=0xff) {
- st7789_send_cmd(st7789_init_cmds[cmd].cmd);
- st7789_send_data(st7789_init_cmds[cmd].data, st7789_init_cmds[cmd].databytes&0x1F);
- if (st7789_init_cmds[cmd].databytes & 0x80) {
- vTaskDelay(100 / portTICK_PERIOD_MS);
- }
- cmd++;
- }
-
- st7789_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION);
- }
-
-
- void st7789_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map)
- {
- uint8_t data[4] = {0};
-
- uint16_t offsetx1 = area->x1;
- uint16_t offsetx2 = area->x2;
- uint16_t offsety1 = area->y1;
- uint16_t offsety2 = area->y2;
-
- offsetx1 += 40;
- offsetx2 += 40;
- offsety1 += 53;
- offsety2 += 53;
-
-
-
- st7789_send_cmd(ST7789_CASET);
- data[0] = (offsetx1 >> 8) & 0xFF;
- data[1] = offsetx1 & 0xFF;
- data[2] = (offsetx2 >> 8) & 0xFF;
- data[3] = offsetx2 & 0xFF;
- st7789_send_data(data, 4);
-
-
- st7789_send_cmd(ST7789_RASET);
- data[0] = (offsety1 >> 8) & 0xFF;
- data[1] = offsety1 & 0xFF;
- data[2] = (offsety2 >> 8) & 0xFF;
- data[3] = offsety2 & 0xFF;
- st7789_send_data(data, 4);
-
-
- st7789_send_cmd(ST7789_RAMWR);
-
- size_t size = (size_t)lv_area_get_width(area) * (size_t)lv_area_get_height(area);
-
- st7789_send_color((void*)color_map, size * 2);
-
- }
-
-
- void st7789_send_cmd(uint8_t cmd)
- {
- disp_wait_for_pending_transactions();
- gpio_set_level(ST7789_DC, 0);
- disp_spi_send_data(&cmd, 1);
- }
-
- void st7789_send_data(void * data, uint16_t length)
- {
- disp_wait_for_pending_transactions();
- gpio_set_level(ST7789_DC, 1);
- disp_spi_send_data(data, length);
- }
-
- static void st7789_send_color(void * data, size_t length)
- {
- disp_wait_for_pending_transactions();
- gpio_set_level(ST7789_DC, 1);
- disp_spi_send_colors(data, length);
- }
- #define ST77XX_MADCTL_MY 0x80
- #define ST77XX_MADCTL_MX 0x40
- #define ST77XX_MADCTL_MV 0x20
- #define ST77XX_MADCTL_ML 0x10
- #define ST77XX_MADCTL_RGB 0x00
- static void st7789_set_orientation(uint8_t orientation)
- {
- uint8_t madctl = 0;
- ESP_LOGI(TAG, "0x36 command value: 0x%02X", madctl);
- madctl = ST77XX_MADCTL_MX | ST77XX_MADCTL_MV | ST77XX_MADCTL_RGB;
- st7789_send_cmd(ST7789_MADCTL);
- st7789_send_data((void *)&madctl, 1);
- }
-
特别注意的是屏幕的分辨率水平240,垂直135已经配置过,如果颜色出现了问题,请查看下lvgl的有一个CONFIG_LV_COLOR_16_SWAP的配置
在配置完屏幕驱动项后理应就能正常内容,这个时候就可以开始完成
任务一、控制屏幕显示中文(必做任务)
首先使用lvgl构建页面内容,笔者是使用了gui guider构建配置,相关的移植gui guider方法请各位自行查找或者看下我的程序
特别提醒中文显示的话需要去lvgl字体转换网站Online font converter - TTF or WOFF fonts to C array | LVGL
把你需要转的中文字体导入并且选定要转换的文本,最终结果输出为一个 .c文件保存
此次笔者使用了一个网上找的字体和guiguider默认案例音乐播放器显示完成的第一个任务
rt
任务二、网络功能使用(必做任务)
这个任务二其实就很简单
ap是构建热点,sta是作为子设备
那么直接照抄官方的程序就可以了
这个地方po出笔者的程序
sta模式:
-
- #include <string.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "freertos/event_groups.h"
- #include "esp_system.h"
- #include "esp_wifi.h"
- #include "esp_event.h"
- #include "esp_log.h"
- #include "nvs_flash.h"
-
- #include "lwip/err.h"
- #include "lwip/sys.h"
- #include "main.h"
-
- static const char *TAG = "wifi station";
- #define EXAMPLE_ESP_WIFI_SSID "TP-LINK_CC60"
- #define EXAMPLE_ESP_WIFI_PASS "stc15f104w"
- #define EXAMPLE_ESP_MAXIMUM_RETRY 5
- #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
-
-
-
-
- static EventGroupHandle_t s_wifi_event_group;
-
-
- #define WIFI_CONNECTED_BIT BIT0
- #define WIFI_FAIL_BIT BIT1
-
-
- 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();
- } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
- if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
- esp_wifi_connect();
- s_retry_num++;
- ESP_LOGI(TAG, "retry to connect to the AP");
- } else {
- xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
- }
- ESP_LOGI(TAG,"connect to the AP fail");
- } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_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;
- xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
- }
- }
-
- void wifi_init_sta(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());
- ret = nvs_flash_init();
- }
- ESP_ERROR_CHECK(ret);
- ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
- s_wifi_event_group = xEventGroupCreate();
-
- ESP_ERROR_CHECK(esp_netif_init());
-
- ESP_ERROR_CHECK(esp_event_loop_create_default());
- esp_netif_create_default_wifi_sta();
-
- wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
- 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,
- &event_handler,
- NULL,
- &instance_any_id));
- ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
- IP_EVENT_STA_GOT_IP,
- &event_handler,
- NULL,
- &instance_got_ip));
-
- wifi_config_t wifi_config = {
- .sta = {
- .ssid = EXAMPLE_ESP_WIFI_SSID,
- .password = EXAMPLE_ESP_WIFI_PASS,
-
-
- .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
- .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
- },
- };
- 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() );
-
- ESP_LOGI(TAG, "wifi_init_sta finished.");
-
-
-
- EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
- WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
- pdFALSE,
- pdFALSE,
- portMAX_DELAY);
-
-
-
- if (bits & WIFI_CONNECTED_BIT) {
- ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
- EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
- } else if (bits & WIFI_FAIL_BIT) {
- ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
- EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
- } else {
- ESP_LOGE(TAG, "UNEXPECTED EVENT");
- }
- xTaskNotify(xtask_lvgl,1,eSetBits);
- }
只需要在main里面调用void wifi_init_sta(void)就可以
ap:
-
- #include <string.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "esp_mac.h"
- #include "esp_wifi.h"
- #include "esp_event.h"
- #include "esp_log.h"
- #include "nvs_flash.h"
-
- #include "lwip/err.h"
- #include "lwip/sys.h"
-
-
- #define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
- #define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
- #define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL
- #define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN
-
- static const char *TAG = "wifi softAP";
-
- static void wifi_event_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data)
- {
- if (event_id == WIFI_EVENT_AP_STACONNECTED) {
- wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
- ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
- MAC2STR(event->mac), event->aid);
- } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
- wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
- ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
- MAC2STR(event->mac), event->aid);
- }
- }
-
- void wifi_init_softap(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());
- ret = nvs_flash_init();
- }
- ESP_ERROR_CHECK(ret);
-
- ESP_ERROR_CHECK(esp_netif_init());
- ESP_ERROR_CHECK(esp_event_loop_create_default());
- esp_netif_create_default_wifi_ap();
-
- wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
- ESP_ERROR_CHECK(esp_wifi_init(&cfg));
-
- ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
- ESP_EVENT_ANY_ID,
- &wifi_event_handler,
- NULL,
- NULL));
-
- wifi_config_t wifi_config = {
- .ap = {
- .ssid = EXAMPLE_ESP_WIFI_SSID,
- .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
- .channel = EXAMPLE_ESP_WIFI_CHANNEL,
- .password = EXAMPLE_ESP_WIFI_PASS,
- .max_connection = EXAMPLE_MAX_STA_CONN,
- #ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT
- .authmode = WIFI_AUTH_WPA3_PSK,
- .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
- #else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */
- .authmode = WIFI_AUTH_WPA2_PSK,
- #endif
- .pmf_cfg = {
- .required = true,
- },
- },
- };
- if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
- wifi_config.ap.authmode = WIFI_AUTH_OPEN;
- }
-
- ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
- ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
- ESP_ERROR_CHECK(esp_wifi_start());
-
- ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
- EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
- }
-
任务3:控制WS2812B(必做任务)
比较简单,说下笔者的方案
esp官方依赖封装按键绑定以色轮输出rgb值的任务
使用官方案例的rmt 驱动ws2812b
要注意提前打开ws2812b的电源控制引脚
然后色轮输出rgb值的任务向lvgl任务发送任务通知
通过位操作把rgb通道存到任务通知的uint_t32
lvgl任务显示一个lv_led组件显示实际灯的颜色
任务4 分任务2:WS2812B效果控制
这个地方偷个 懒改个灯珠数量和针脚所以如下
是不是太偷懒了()
再写个任务四吧()
分任务1:日历&时钟
注册个ntp客户端去请求ntp时间
这个地方使用的esp idf的sntp(sample ntp)案例
把重要的几行程序提一下
-
- void ntp_gettime(){
- char strftime_buf[64];
- time_t now;
- struct tm timeinfo;
- time(&now);
- localtime_r(&now, &timeinfo);
-
- if (timeinfo.tm_year < (2016 - 1900)) {
- ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
- esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
- esp_netif_sntp_init(&config);
- esp_netif_sntp_start();
-
- time(&now);
- }
- setenv("TZ", "CST-8", 1);
- tzset();
- while (1)
- {
- time(&now);
- localtime_r(&now, &timeinfo);
- strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
- ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf);
-
- xQueueSend(ntp_time,(void*)strftime_buf, 0);
- vTaskDelay(pdMS_TO_TICKS(1000));
- }
- }
把获取ntp时间封装位rtos任务,每1s请求一次时间
通过队列发送到lvgl任务
效果如图
任务5:通过网络控制WS2812B(可选任务,非必做)
写一个色盘网页从ws发送rgb到esp32s3上
esp32s3上websocket使用ws_echo_server案例改一下接受并解析rgb json文件
通过任务队列发送给ws2812的显示任务里
后记:
■ 分任务3:数据检测与记录——按一定时间间隔连续记录温度/亮度信息,保存到SD卡,并可以通过按键调用查看之前的信息,并在屏幕上绘图
建议搭配器件:Adafruit ESP32-S3 TFT Feather、光传感器、温度传感器、微型 SD卡模块
■ 分任务4:音乐播放功能——实现音乐播放器功能,可以在屏幕上显示列表信息和音乐信息,使用按键进行切换,使用扬声器进行播放
建议搭配器件:Adafruit ESP32-S3 TFT Feather、音频放大器、扬声器
■ 分任务5:AI功能应用——结合运动传感器,完成手势识别功能,至少要识别三种手势(如水平左右、前后、垂直上下、水平画圈、垂直画圈,或者更复杂手势
建议搭配器件:Adafruit ESP32-S3 TFT Feather、运动传感器
这几个任务我这现在要不就是缺能和这块板子适配的元件(i2s codec比如),要不就是这段时间稍微忙点等短时间我可能会做一下补一下
只管稍微说一下在espidf上跑这几个功能的思路:
sd卡使用sdio 1line/4line或者spisd卡都可,去案例找一下引脚配置就可
iic自行找自己使用的外设
音乐播放功能可以使用esp-adf的组件自行配置adc/codec,楼主测试过es8311和es7210都是适配没问题,使用pipline或者element方法都可以写一下这个功能的还算是比较简单的
AI功能应用的话mpu6050的数据读取不做另行说明,但是这地方使用ai相关配置我还真的不太了解,如果各位有了解请麻烦分享一下
代码包下载地址:https://download.eeworld.com.cn/detail/eew_MsNISU/629815
心得体会
这次活动让我更深入地了解了ESP32-S3的开发生态和丰富的外设支持。通过实际操作,我积累了更多的硬件开发经验,同时也感受到了espidf在实际项目中的灵活性和强大功能。希望未来的活动能够继续涵盖更多领域,激发更多创意和技术探讨。感谢EEWORLD和DigiKey提供这样一个学习与交流的平台!
|