From ce198ccbe84b22caee288741d770d58ced284210 Mon Sep 17 00:00:00 2001 From: Bo Date: Thu, 19 Feb 2026 12:10:00 +0800 Subject: [PATCH] chore: remove display and rgb source trees Signed-off-by: Bo --- assets/banner_172x320.rgb565 | Bin 110080 -> 0 bytes assets/banner_320x172.rgb565 | Bin 110080 -> 0 bytes main/CMakeLists.txt | 2 - main/display/Vernon_ST7789T/Vernon_ST7789T.c | 307 ---------- main/display/Vernon_ST7789T/Vernon_ST7789T.h | 47 -- main/display/display.c | 598 ------------------- main/display/display.h | 28 - main/display/font5x7.h | 105 ---- main/rgb/rgb.c | 39 -- main/rgb/rgb.h | 15 - 10 files changed, 1141 deletions(-) delete mode 100644 assets/banner_172x320.rgb565 delete mode 100644 assets/banner_320x172.rgb565 delete mode 100644 main/display/Vernon_ST7789T/Vernon_ST7789T.c delete mode 100644 main/display/Vernon_ST7789T/Vernon_ST7789T.h delete mode 100644 main/display/display.c delete mode 100644 main/display/display.h delete mode 100644 main/display/font5x7.h delete mode 100644 main/rgb/rgb.c delete mode 100644 main/rgb/rgb.h diff --git a/assets/banner_172x320.rgb565 b/assets/banner_172x320.rgb565 deleted file mode 100644 index c58e269c5499c176ab7d7617ce1c1c55a5ea17cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110080 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 KifI933;YX?epIUf diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 9466663..4c285ad 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -29,8 +29,6 @@ idf_component_register( "skills/skill_loader.c" INCLUDE_DIRS "." - EMBED_FILES - "../assets/banner_320x172.rgb565" REQUIRES nvs_flash esp_wifi esp_netif esp_http_client esp_http_server esp_https_ota esp_event json spiffs console vfs app_update esp-tls diff --git a/main/display/Vernon_ST7789T/Vernon_ST7789T.c b/main/display/Vernon_ST7789T/Vernon_ST7789T.c deleted file mode 100644 index f6ef3fa..0000000 --- a/main/display/Vernon_ST7789T/Vernon_ST7789T.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "sdkconfig.h" -#if CONFIG_LCD_ENABLE_DEBUG_LOG -// The local log level must be defined before including esp_log.h -// Set the maximum log level for this source file -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG -#endif -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_commands.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "esp_check.h" - -#include "display/Vernon_ST7789T/Vernon_ST7789T.h" - -static const char *TAG = "lcd_panel.st7789t"; - -static esp_err_t panel_st7789t_del(esp_lcd_panel_t *panel); -static esp_err_t panel_st7789t_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_st7789t_init(esp_lcd_panel_t *panel); -static esp_err_t panel_st7789t_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data); -static esp_err_t panel_st7789t_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); -static esp_err_t panel_st7789t_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_st7789t_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); -static esp_err_t panel_st7789t_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); -static esp_err_t panel_st7789t_disp_on_off(esp_lcd_panel_t *panel, bool off); - -typedef struct { - esp_lcd_panel_t base; - esp_lcd_panel_io_handle_t io; - int reset_gpio_num; - bool reset_level; - int x_gap; - int y_gap; - uint8_t fb_bits_per_pixel; - uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_cal; // save surrent value of LCD_CMD_COLMOD register -} st7789t_panel_t; - -esp_err_t esp_lcd_new_panel_st7789t(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_st7789t_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) -{ -#if CONFIG_LCD_ENABLE_DEBUG_LOG - esp_log_level_set(TAG, ESP_LOG_DEBUG); -#endif - esp_err_t ret = ESP_OK; - st7789t_panel_t *st7789t = NULL; - ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - st7789t = calloc(1, sizeof(st7789t_panel_t)); - ESP_GOTO_ON_FALSE(st7789t, ESP_ERR_NO_MEM, err, TAG, "no mem for st7789t panel"); - - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, - }; - ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); - } - - switch (panel_dev_config->rgb_endian) { - case LCD_RGB_ENDIAN_RGB: - st7789t->madctl_val = 0; - break; - case LCD_RGB_ENDIAN_BGR: - st7789t->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); - break; - } - - uint8_t fb_bits_per_pixel = 0; - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - st7789t->colmod_cal = 0x55; - fb_bits_per_pixel = 16; - break; - case 18: // RGB666 - st7789t->colmod_cal = 0x66; - // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - fb_bits_per_pixel = 24; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; - } - - st7789t->io = io; - st7789t->fb_bits_per_pixel = fb_bits_per_pixel; - st7789t->reset_gpio_num = panel_dev_config->reset_gpio_num; - st7789t->reset_level = panel_dev_config->flags.reset_active_high; - st7789t->base.del = panel_st7789t_del; - st7789t->base.reset = panel_st7789t_reset; - st7789t->base.init = panel_st7789t_init; - st7789t->base.draw_bitmap = panel_st7789t_draw_bitmap; - st7789t->base.invert_color = panel_st7789t_invert_color; - st7789t->base.set_gap = panel_st7789t_set_gap; - st7789t->base.mirror = panel_st7789t_mirror; - st7789t->base.swap_xy = panel_st7789t_swap_xy; - st7789t->base.disp_on_off = panel_st7789t_disp_on_off; - *ret_panel = &(st7789t->base); - ESP_LOGD(TAG, "new st7789t panel @%p", st7789t); - // printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"); - return ESP_OK; - -err: - if (st7789t) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(st7789t); - } - return ret; -} - -static esp_err_t panel_st7789t_del(esp_lcd_panel_t *panel) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - - if (st7789t->reset_gpio_num >= 0) { - gpio_reset_pin(st7789t->reset_gpio_num); - } - ESP_LOGD(TAG, "del st7789t panel @%p", st7789t); - free(st7789t); - return ESP_OK; -} - -static esp_err_t panel_st7789t_reset(esp_lcd_panel_t *panel) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - esp_lcd_panel_io_handle_t io = st7789t->io; - - // perform hardware reset - if (st7789t->reset_gpio_num >= 0) { - gpio_set_level(st7789t->reset_gpio_num, st7789t->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st7789t->reset_gpio_num, !st7789t->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - } else { // perform software reset - esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0); - vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5m before sending new command - } - - return ESP_OK; -} - -static esp_err_t panel_st7789t_init(esp_lcd_panel_t *panel) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - esp_lcd_panel_io_handle_t io = st7789t->io; - // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - // printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"); - esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0); - vTaskDelay(pdMS_TO_TICKS(100)); - // esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {st7789t->madctl_val,}, 1); - // esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {st7789t->colmod_cal,}, 1); - - /* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */ - esp_lcd_panel_io_tx_param(io, 0x36, (uint8_t []){0x00}, 1); // 0x36: 接口像素格式 X镜像,Y镜像 - /* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */ - esp_lcd_panel_io_tx_param(io, 0x3A, (uint8_t []){0x55}, 1); // 0x3A: Porch 设置 - - esp_lcd_panel_io_tx_param(io, 0xB0, (uint8_t []){0x00, 0xE8}, 2); - /* Porch Setting */ - esp_lcd_panel_io_tx_param(io, 0xB2, (uint8_t []){0x0c, 0x0c, 0x00, 0x33, 0x33}, 5); - /* Gate Control, Vgh=13.65V, Vgl=-10.43V */ - esp_lcd_panel_io_tx_param(io, 0xB7, (uint8_t []){0x75}, 1); - /* VCOM Setting, VCOM=1.175V */ - esp_lcd_panel_io_tx_param(io, 0xBB, (uint8_t []){0x1A}, 1); - /* LCM Control, XOR: BGR, MX, MH */ - esp_lcd_panel_io_tx_param(io, 0xC0, (uint8_t []){0x80}, 1); - /* VDV and VRH Command Enable, enable=1 */ - esp_lcd_panel_io_tx_param(io, 0xC2, (uint8_t []){0x01, 0xff}, 2); - /* VRH Set, Vap=4.4+... */ - esp_lcd_panel_io_tx_param(io, 0xC3, (uint8_t []){0x13}, 1); - /* VDV Set, VDV=0 */ - esp_lcd_panel_io_tx_param(io, 0xC4, (uint8_t []){0x20}, 1); - /* Frame Rate Control, 60Hz, inversion=0 */ - esp_lcd_panel_io_tx_param(io, 0xC6, (uint8_t []){0x0F}, 1); - /* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */ - esp_lcd_panel_io_tx_param(io, 0xD0, (uint8_t []){0xA4, 0xA1}, 1); - /* Positive Voltage Gamma Control */ - esp_lcd_panel_io_tx_param(io, 0xE0, (uint8_t []){0xD0, 0x0D, 0x14, 0x0D, 0x0D, 0x09, 0x38, 0x44, 0x4E, 0x3A, 0x17, 0x18, 0x2F, 0x30}, 14); - /* Negative Voltage Gamma Control */ - esp_lcd_panel_io_tx_param(io, 0xE1, (uint8_t []){0xD0, 0x09, 0x0F, 0x08, 0x07, 0x14, 0x37, 0x44, 0x4D, 0x38, 0x15, 0x16, 0x2C, 0x2E}, 14); - /* Sleep Out */ - esp_lcd_panel_io_tx_param(io, 0x21, NULL, 0); - /* Display On */ - esp_lcd_panel_io_tx_param(io, 0x29, NULL, 0); - - esp_lcd_panel_io_tx_param(io, 0x2C, NULL, 0); - - return ESP_OK; -} - -static esp_err_t panel_st7789t_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position"); - esp_lcd_panel_io_handle_t io = st7789t->io; - - x_start += st7789t->x_gap; - x_end += st7789t->x_gap; - y_start += st7789t->y_gap; - y_end += st7789t->y_gap; - - // define an area of frame memory where MCU can access - esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { - (x_start >> 8) & 0xFF, - x_start & 0xFF, - ((x_end - 1) >> 8) & 0xFF, - (x_end - 1) & 0xFF, - }, 4); - esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { - (y_start >> 8) & 0xFF, - y_start & 0xFF, - ((y_end - 1) >> 8) & 0xFF, - (y_end - 1) & 0xFF, - }, 4); - // transfer frame buffer - size_t len = (x_end - x_start) * (y_end - y_start) * st7789t->fb_bits_per_pixel / 8; - esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len); - - return ESP_OK; -} - -static esp_err_t panel_st7789t_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - esp_lcd_panel_io_handle_t io = st7789t->io; - int command = 0; - if (invert_color_data) { - command = LCD_CMD_INVON; - } else { - command = LCD_CMD_INVOFF; - } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); - return ESP_OK; -} - -static esp_err_t panel_st7789t_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - esp_lcd_panel_io_handle_t io = st7789t->io; - if (mirror_x) { - st7789t->madctl_val |= LCD_CMD_MX_BIT; - } else { - st7789t->madctl_val &= ~LCD_CMD_MX_BIT; - } - if (mirror_y) { - st7789t->madctl_val |= LCD_CMD_MY_BIT; - } else { - st7789t->madctl_val &= ~LCD_CMD_MY_BIT; - } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7789t->madctl_val - }, 1); - return ESP_OK; -} - -static esp_err_t panel_st7789t_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - esp_lcd_panel_io_handle_t io = st7789t->io; - if (swap_axes) { - st7789t->madctl_val |= LCD_CMD_MV_BIT; - } else { - st7789t->madctl_val &= ~LCD_CMD_MV_BIT; - } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7789t->madctl_val - }, 1); - return ESP_OK; -} - -static esp_err_t panel_st7789t_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - st7789t->x_gap = x_gap; - st7789t->y_gap = y_gap; - return ESP_OK; -} - -static esp_err_t panel_st7789t_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - st7789t_panel_t *st7789t = __containerof(panel, st7789t_panel_t, base); - esp_lcd_panel_io_handle_t io = st7789t->io; - int command = 0; - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); - return ESP_OK; -} diff --git a/main/display/Vernon_ST7789T/Vernon_ST7789T.h b/main/display/Vernon_ST7789T/Vernon_ST7789T.h deleted file mode 100644 index 7472f57..0000000 --- a/main/display/Vernon_ST7789T/Vernon_ST7789T.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#pragma once - -#include -#include "esp_err.h" -#include "esp_lcd_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Configuration structure for panel device - */ -typedef struct { - int reset_gpio_num; /*!< GPIO used to reset the LCD panel, set to -1 if it's not used */ - union { - lcd_color_rgb_endian_t color_space; /*!< @deprecated Set RGB color space, please use rgb_endian instead */ - lcd_color_rgb_endian_t rgb_endian; /*!< Set RGB data endian: RGB or BGR */ - }; - unsigned int bits_per_pixel; /*!< Color depth, in bpp */ - struct { - unsigned int reset_active_high: 1; /*!< Setting this if the panel reset is high level active */ - } flags; /*!< LCD panel config flags */ - void *vendor_config; /*!< vendor specific configuration, optional, left as NULL if not used */ -} esp_lcd_panel_dev_st7789t_config_t; - -/** - * @brief Create LCD panel for model ST7789T - * - * @param[in] io LCD panel IO handle - * @param[in] panel_dev_config general panel device configuration - * @param[out] ret_panel Returned LCD panel handle - * @return - * - ESP_ERR_INVALID_ARG if parameter is invalid - * - ESP_ERR_NO_MEM if out of memory - * - ESP_OK on success - */ -esp_err_t esp_lcd_new_panel_st7789t(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_st7789t_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel); - -#ifdef __cplusplus -} -#endif diff --git a/main/display/display.c b/main/display/display.c deleted file mode 100644 index 0c7f5d2..0000000 --- a/main/display/display.c +++ /dev/null @@ -1,598 +0,0 @@ -#include "display/display.h" -#include "mimi_config.h" - -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/timers.h" -#include "esp_check.h" -#include "esp_log.h" -#include "driver/ledc.h" -#include "driver/spi_master.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_ops.h" -#include "display/Vernon_ST7789T/Vernon_ST7789T.h" -#include "display/font5x7.h" -#include "qrcode.h" - -#define LCD_HOST SPI3_HOST - -#define LCD_PIXEL_CLOCK_HZ (12 * 1000 * 1000) -#define LCD_CMD_BITS 8 -#define LCD_PARAM_BITS 8 - -#define LCD_H_RES 172 -#define LCD_V_RES 320 - -#define BANNER_W 320 -#define BANNER_H 172 - -#define LCD_PIN_SCLK 40 -#define LCD_PIN_MOSI 45 -#define LCD_PIN_MISO -1 -#define LCD_PIN_DC 41 -#define LCD_PIN_RST 39 -#define LCD_PIN_CS 42 -#define LCD_PIN_BK_LIGHT 46 - -#define LCD_X_GAP 34 -#define LCD_Y_GAP 0 - -#define LEDC_TIMER LEDC_TIMER_0 -#define LEDC_MODE LEDC_LOW_SPEED_MODE -#define LEDC_CHANNEL LEDC_CHANNEL_0 -#define LEDC_DUTY_RES LEDC_TIMER_13_BIT -#define LEDC_FREQUENCY_HZ 4000 - -#define BACKLIGHT_MIN_PERCENT 10 -#define BACKLIGHT_MAX_PERCENT 100 -#define BACKLIGHT_STEP_PERCENT 10 - -static const char *TAG = "display"; - -static esp_lcd_panel_handle_t panel_handle = NULL; -static uint8_t backlight_percent = 50; -static uint16_t *framebuffer = NULL; -static SemaphoreHandle_t s_display_lock = NULL; -static TimerHandle_t s_card_hide_timer = NULL; -static uint32_t s_card_generation = 0; - -typedef enum { - SCREEN_KIND_NONE = 0, - SCREEN_KIND_BANNER, - SCREEN_KIND_CONFIG, - SCREEN_KIND_CARD, -} screen_kind_t; - -static screen_kind_t s_screen_kind = SCREEN_KIND_NONE; - -typedef struct { - int x; - int y; - int box; - uint16_t fg; -} qr_draw_ctx_t; - -static qr_draw_ctx_t s_qr_ctx; - -extern const uint8_t _binary_banner_320x172_rgb565_start[]; -extern const uint8_t _binary_banner_320x172_rgb565_end[]; - -static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) -{ - return (uint16_t)(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)); -} - -static bool display_lock_take(TickType_t wait_ticks) -{ - if (!s_display_lock) { - return false; - } - return xSemaphoreTake(s_display_lock, wait_ticks) == pdTRUE; -} - -static void display_lock_give(void) -{ - if (s_display_lock) { - xSemaphoreGive(s_display_lock); - } -} - -static void draw_framebuffer_locked(void) -{ - if (!panel_handle || !framebuffer) { - return; - } - esp_err_t err = esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, BANNER_W, BANNER_H, framebuffer); - if (err != ESP_OK) { - ESP_LOGW(TAG, "panel draw failed: %s", esp_err_to_name(err)); - } -} - -static void card_hide_timer_cb(TimerHandle_t timer) -{ - uint32_t generation = (uint32_t)(uintptr_t)pvTimerGetTimerID(timer); - bool should_hide = false; - - if (!display_lock_take(pdMS_TO_TICKS(30))) { - return; - } - should_hide = (s_screen_kind == SCREEN_KIND_CARD && s_card_generation == generation); - display_lock_give(); - - if (should_hide) { - display_show_banner(); - } -} - -static void fb_ensure(void) -{ - if (!framebuffer) { - framebuffer = (uint16_t *)calloc(BANNER_W * BANNER_H, sizeof(uint16_t)); - } -} - -static inline void fb_set_pixel(int x, int y, uint16_t color) -{ - if (x < 0 || y < 0 || x >= BANNER_W || y >= BANNER_H || !framebuffer) { - return; - } - framebuffer[y * BANNER_W + x] = color; -} - -static void fb_fill_rect(int x, int y, int w, int h, uint16_t color) -{ - if (!framebuffer) { - return; - } - for (int yy = y; yy < y + h; yy++) { - for (int xx = x; xx < x + w; xx++) { - fb_set_pixel(xx, yy, color); - } - } -} - -static void fb_fill_rect_clipped(int x, int y, int w, int h, uint16_t color, int clip_x0, int clip_x1) -{ - if (!framebuffer) { - return; - } - int x0 = x; - int x1 = x + w; - if (x0 < clip_x0) { - x0 = clip_x0; - } - if (x1 > clip_x1) { - x1 = clip_x1; - } - if (x1 <= x0) { - return; - } - for (int yy = y; yy < y + h; yy++) { - for (int xx = x0; xx < x1; xx++) { - fb_set_pixel(xx, yy, color); - } - } -} - -static void fb_draw_char_scaled_clipped(int x, int y, char c, uint16_t color, int scale, int clip_x0, int clip_x1) -{ - if (c < 32 || c > 127) { - c = '?'; - } - const uint8_t *glyph = font5x7[(uint8_t)c - 32]; - for (int col = 0; col < FONT5X7_WIDTH; col++) { - uint8_t bits = glyph[col]; - for (int row = 0; row < FONT5X7_HEIGHT; row++) { - if (bits & (1 << row)) { - int px = x + col * scale; - int py = y + row * scale; - fb_fill_rect_clipped(px, py, scale, scale, color, clip_x0, clip_x1); - } - } - } -} - -static void fb_draw_text_clipped(int x, int y, const char *text, uint16_t color, int line_height, int scale, - int clip_x0, int clip_x1) -{ - int cx = x; - int cy = y; - for (size_t i = 0; text[i] != '\0'; i++) { - if (text[i] == '\n') { - cy += line_height; - cx = x; - continue; - } - fb_draw_char_scaled_clipped(cx, cy, text[i], color, scale, clip_x0, clip_x1); - cx += (FONT5X7_WIDTH + 1) * scale; - } -} - -static void backlight_ledc_init(void) -{ - ledc_timer_config_t ledc_timer = { - .speed_mode = LEDC_MODE, - .timer_num = LEDC_TIMER, - .duty_resolution = LEDC_DUTY_RES, - .freq_hz = LEDC_FREQUENCY_HZ, - .clk_cfg = LEDC_AUTO_CLK, - }; - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); - - ledc_channel_config_t ledc_channel = { - .speed_mode = LEDC_MODE, - .channel = LEDC_CHANNEL, - .timer_sel = LEDC_TIMER, - .intr_type = LEDC_INTR_DISABLE, - .gpio_num = LCD_PIN_BK_LIGHT, - .duty = 0, - .hpoint = 0, - }; - ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); -} - -void display_set_backlight_percent(uint8_t percent) -{ - if (percent > BACKLIGHT_MAX_PERCENT) { - percent = BACKLIGHT_MAX_PERCENT; - } - backlight_percent = percent; - - uint32_t duty_max = (1U << LEDC_DUTY_RES) - 1; - uint32_t duty = (duty_max * backlight_percent) / 100; - ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty)); - ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); -} - -uint8_t display_get_backlight_percent(void) -{ - return backlight_percent; -} - -void display_cycle_backlight(void) -{ - uint8_t next = backlight_percent + BACKLIGHT_STEP_PERCENT; - if (next > BACKLIGHT_MAX_PERCENT) { - next = BACKLIGHT_MIN_PERCENT; - } - display_set_backlight_percent(next); - ESP_LOGI(TAG, "Backlight -> %u%%", next); -} - -esp_err_t display_init(void) -{ - esp_err_t ret = ESP_OK; - - if (!s_display_lock) { - s_display_lock = xSemaphoreCreateMutex(); - if (!s_display_lock) { - return ESP_ERR_NO_MEM; - } - } - - spi_bus_config_t buscfg = { - .sclk_io_num = LCD_PIN_SCLK, - .mosi_io_num = LCD_PIN_MOSI, - .miso_io_num = LCD_PIN_MISO, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = LCD_H_RES * LCD_V_RES * sizeof(uint16_t), - }; - ESP_RETURN_ON_ERROR(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO), TAG, "spi bus init failed"); - - esp_lcd_panel_io_handle_t io_handle = NULL; - esp_lcd_panel_io_spi_config_t io_config = { - .dc_gpio_num = LCD_PIN_DC, - .cs_gpio_num = LCD_PIN_CS, - .pclk_hz = LCD_PIXEL_CLOCK_HZ, - .lcd_cmd_bits = LCD_CMD_BITS, - .lcd_param_bits = LCD_PARAM_BITS, - .spi_mode = 0, - .trans_queue_depth = 10, - .on_color_trans_done = NULL, - .user_ctx = NULL, - }; - ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle), TAG, "panel io init failed"); - - esp_lcd_panel_dev_st7789t_config_t panel_config = { - .reset_gpio_num = LCD_PIN_RST, - .rgb_endian = LCD_RGB_ENDIAN_BGR, - .bits_per_pixel = 16, - }; - - ESP_RETURN_ON_ERROR(esp_lcd_new_panel_st7789t(io_handle, &panel_config, &panel_handle), TAG, "panel init failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(panel_handle), TAG, "panel reset failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_init(panel_handle), TAG, "panel init failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_mirror(panel_handle, true, true), TAG, "panel mirror failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_swap_xy(panel_handle, true), TAG, "panel swap failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_set_gap(panel_handle, LCD_Y_GAP, LCD_X_GAP), TAG, "panel gap failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_disp_on_off(panel_handle, true), TAG, "panel on failed"); - - backlight_ledc_init(); - display_set_backlight_percent(backlight_percent); - - if (!s_card_hide_timer) { - s_card_hide_timer = xTimerCreate("card_hide", - pdMS_TO_TICKS(MIMI_TG_CARD_SHOW_MS), - pdFALSE, NULL, card_hide_timer_cb); - if (!s_card_hide_timer) { - return ESP_ERR_NO_MEM; - } - } - - return ret; -} - -void display_show_banner(void) -{ - if (!display_lock_take(pdMS_TO_TICKS(200))) { - ESP_LOGW(TAG, "display lock timeout (banner)"); - return; - } - - if (!panel_handle) { - ESP_LOGW(TAG, "display not initialized"); - display_lock_give(); - return; - } - - const uint8_t *start = _binary_banner_320x172_rgb565_start; - const uint8_t *end = _binary_banner_320x172_rgb565_end; - size_t len = (size_t)(end - start); - size_t expected = (size_t)BANNER_W * (size_t)BANNER_H * 2; - if (len < expected) { - ESP_LOGW(TAG, "banner data too small (%u < %u)", (unsigned)len, (unsigned)expected); - display_lock_give(); - return; - } - - esp_err_t err = esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, BANNER_W, BANNER_H, start); - if (err != ESP_OK) { - ESP_LOGW(TAG, "banner draw failed: %s", esp_err_to_name(err)); - } else { - s_screen_kind = SCREEN_KIND_BANNER; - } - if (s_card_hide_timer) { - xTimerStop(s_card_hide_timer, 0); - } - display_lock_give(); -} - -static void qr_draw_cb(esp_qrcode_handle_t qrcode) -{ - int size = esp_qrcode_get_size(qrcode); - int quiet = 2; - int scale = s_qr_ctx.box / (size + quiet * 2); - if (scale < 1) { - scale = 1; - } - int qr_px = (size + quiet * 2) * scale; - int origin_x = s_qr_ctx.x + (s_qr_ctx.box - qr_px) / 2 + quiet * scale; - int origin_y = s_qr_ctx.y + (s_qr_ctx.box - qr_px) / 2 + quiet * scale; - - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - if (esp_qrcode_get_module(qrcode, x, y)) { - fb_fill_rect(origin_x + x * scale, origin_y + y * scale, scale, scale, s_qr_ctx.fg); - } - } - } -} - -void display_show_config_screen(const char *qr_text, const char *ip_text, - const char **lines, size_t line_count, size_t scroll, - size_t selected, int selected_offset_px) -{ - if (!qr_text || !ip_text || !lines) { - return; - } - - if (!display_lock_take(pdMS_TO_TICKS(200))) { - ESP_LOGW(TAG, "display lock timeout (config)"); - return; - } - - if (!panel_handle) { - ESP_LOGW(TAG, "display not initialized"); - display_lock_give(); - return; - } - - fb_ensure(); - if (!framebuffer) { - ESP_LOGW(TAG, "framebuffer alloc failed"); - display_lock_give(); - return; - } - - const uint16_t color_bg = rgb565(0, 0, 0); - const uint16_t color_fg = rgb565(255, 255, 255); - const uint16_t color_qr_bg = rgb565(255, 255, 255); - const uint16_t color_qr_fg = rgb565(0, 0, 0); - const uint16_t color_title = rgb565(100, 200, 255); - const uint16_t color_sel_bg = rgb565(50, 80, 120); - - fb_fill_rect(0, 0, BANNER_W, BANNER_H, color_bg); - - // QR area (left column) - const int left_pad = 6; - const int qr_box = 110; - const int qr_x = left_pad; - const int qr_y = (BANNER_H - qr_box) / 2 - 8; - - fb_fill_rect(qr_x, qr_y, qr_box, qr_box, color_qr_bg); - - s_qr_ctx.x = qr_x; - s_qr_ctx.y = qr_y; - s_qr_ctx.box = qr_box; - s_qr_ctx.fg = color_qr_fg; - - esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT(); - cfg.display_func = qr_draw_cb; - cfg.max_qrcode_version = 6; - cfg.qrcode_ecc_level = ESP_QRCODE_ECC_MED; - - esp_qrcode_generate(&cfg, qr_text); - - // IP text under QR - fb_draw_text_clipped(qr_x, qr_y + qr_box + 4, ip_text, color_fg, 10, 1, 0, BANNER_W); - - // Right column - const int right_x = qr_x + qr_box + 10; - const int right_w = BANNER_W - right_x - 6; - (void)right_w; - fb_draw_text_clipped(right_x, 4, "Configuration", color_title, 14, 2, right_x, BANNER_W); - - const int line_height = 16; - const int start_y = 24; - size_t lines_per_page = (BANNER_H - start_y - 6) / line_height; - for (size_t i = 0; i < lines_per_page; i++) { - if (line_count == 0) { - break; - } - size_t idx = (scroll + i) % line_count; - if (idx < line_count) { - int line_y = start_y + (int)i * line_height; - if (idx == selected) { - fb_fill_rect(right_x, line_y - 1, BANNER_W - right_x - 2, line_height + 2, color_sel_bg); - fb_draw_text_clipped(right_x - selected_offset_px, line_y, lines[idx], color_fg, line_height, 2, right_x, BANNER_W); - } else { - fb_fill_rect(right_x, line_y - 1, BANNER_W - right_x - 2, line_height + 2, color_bg); - fb_draw_text_clipped(right_x, line_y, lines[idx], color_fg, line_height, 2, right_x, BANNER_W); - } - } - } - - draw_framebuffer_locked(); - s_screen_kind = SCREEN_KIND_CONFIG; - if (s_card_hide_timer) { - xTimerStop(s_card_hide_timer, 0); - } - display_lock_give(); -} - -void display_show_message_card(const char *title, const char *body) -{ - if (!title || !body) { - return; - } - - if (!display_lock_take(pdMS_TO_TICKS(200))) { - ESP_LOGW(TAG, "display lock timeout (card)"); - return; - } - if (!panel_handle) { - display_lock_give(); - return; - } - - fb_ensure(); - if (!framebuffer) { - ESP_LOGW(TAG, "framebuffer alloc failed"); - display_lock_give(); - return; - } - - const uint16_t color_bg = rgb565(0, 0, 0); - const uint16_t color_title = rgb565(100, 200, 255); - const uint16_t color_fg = rgb565(255, 255, 255); - - const int body_scale = (MIMI_TG_CARD_BODY_SCALE < 1) ? 1 : MIMI_TG_CARD_BODY_SCALE; - const int title_scale = 2; - const int title_line_h = (FONT5X7_HEIGHT + 1) * title_scale; - const int body_line_h = (FONT5X7_HEIGHT + 1) * body_scale + 1; - const int body_y = 10 + title_line_h; - const int max_cols = (BANNER_W - 12) / ((FONT5X7_WIDTH + 1) * body_scale); - int max_lines = (BANNER_H - body_y - 6) / body_line_h; - if (max_lines < 1) { - max_lines = 1; - } - - fb_fill_rect(0, 0, BANNER_W, BANNER_H, color_bg); - fb_draw_text_clipped(6, 6, title, color_title, title_line_h, title_scale, 0, BANNER_W); - - char wrapped[512]; - size_t w = 0; - int cols = 0; - int lines = 1; - - for (size_t i = 0; body[i] != '\0'; i++) { - if (w >= sizeof(wrapped) - 2 || lines > max_lines) { - break; - } - - char c = body[i]; - if (c == '\r') { - continue; - } - if (c == '\n') { - wrapped[w++] = '\n'; - cols = 0; - lines++; - continue; - } - if (cols >= max_cols) { - wrapped[w++] = '\n'; - cols = 0; - lines++; - if (lines > max_lines || w >= sizeof(wrapped) - 2) { - break; - } - } - wrapped[w++] = c; - cols++; - } - - if (w == 0) { - strncpy(wrapped, "(empty)", sizeof(wrapped) - 1); - wrapped[sizeof(wrapped) - 1] = '\0'; - } else { - wrapped[w] = '\0'; - } - - fb_draw_text_clipped(6, body_y, wrapped, color_fg, body_line_h, body_scale, 0, BANNER_W); - draw_framebuffer_locked(); - - s_screen_kind = SCREEN_KIND_CARD; - s_card_generation++; - uint32_t generation = s_card_generation; - if (s_card_hide_timer) { - vTimerSetTimerID(s_card_hide_timer, (void *)(uintptr_t)generation); - xTimerStop(s_card_hide_timer, 0); - xTimerChangePeriod(s_card_hide_timer, pdMS_TO_TICKS(MIMI_TG_CARD_SHOW_MS), 0); - xTimerStart(s_card_hide_timer, 0); - } - display_lock_give(); -} - -bool display_get_banner_center_rgb(uint8_t *r, uint8_t *g, uint8_t *b) -{ - if (!r || !g || !b) { - return false; - } - - const uint8_t *start = _binary_banner_320x172_rgb565_start; - const uint8_t *end = _binary_banner_320x172_rgb565_end; - size_t len = (size_t)(end - start); - size_t expected = (size_t)BANNER_W * (size_t)BANNER_H * 2; - if (len < expected) { - return false; - } - - size_t cx = BANNER_W / 2; - size_t cy = BANNER_H / 2; - size_t idx = (cy * BANNER_W + cx) * 2; - uint16_t pixel = (uint16_t)start[idx] | ((uint16_t)start[idx + 1] << 8); - - uint8_t r5 = (pixel >> 11) & 0x1F; - uint8_t g6 = (pixel >> 5) & 0x3F; - uint8_t b5 = pixel & 0x1F; - - *r = (uint8_t)((r5 * 255) / 31); - *g = (uint8_t)((g6 * 255) / 63); - *b = (uint8_t)((b5 * 255) / 31); - return true; -} diff --git a/main/display/display.h b/main/display/display.h deleted file mode 100644 index e2c958a..0000000 --- a/main/display/display.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define DISPLAY_WIDTH 320 -#define DISPLAY_HEIGHT 172 - -esp_err_t display_init(void); -void display_show_banner(void); -void display_set_backlight_percent(uint8_t percent); -uint8_t display_get_backlight_percent(void); -void display_cycle_backlight(void); -bool display_get_banner_center_rgb(uint8_t *r, uint8_t *g, uint8_t *b); -void display_show_config_screen(const char *qr_text, const char *ip_text, - const char **lines, size_t line_count, size_t scroll, - size_t selected, int selected_offset_px); -void display_show_message_card(const char *title, const char *body); - -#ifdef __cplusplus -} -#endif diff --git a/main/display/font5x7.h b/main/display/font5x7.h deleted file mode 100644 index 3e48fcd..0000000 --- a/main/display/font5x7.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include - -#define FONT5X7_WIDTH 5 -#define FONT5X7_HEIGHT 7 - -static const uint8_t font5x7[96][5] = { - {0x00,0x00,0x00,0x00,0x00}, // ' ' - {0x00,0x00,0x5f,0x00,0x00}, // '!' - {0x00,0x07,0x00,0x07,0x00}, // '"' - {0x14,0x7f,0x14,0x7f,0x14}, // '#' - {0x24,0x2a,0x7f,0x2a,0x12}, // '$' - {0x23,0x13,0x08,0x64,0x62}, // '%' - {0x36,0x49,0x55,0x22,0x50}, // '&' - {0x00,0x05,0x03,0x00,0x00}, // '\'' - {0x00,0x1c,0x22,0x41,0x00}, // '(' - {0x00,0x41,0x22,0x1c,0x00}, // ')' - {0x14,0x08,0x3e,0x08,0x14}, // '*' - {0x08,0x08,0x3e,0x08,0x08}, // '+' - {0x00,0x50,0x30,0x00,0x00}, // ',' - {0x08,0x08,0x08,0x08,0x08}, // '-' - {0x00,0x60,0x60,0x00,0x00}, // '.' - {0x20,0x10,0x08,0x04,0x02}, // '/' - {0x3e,0x51,0x49,0x45,0x3e}, // '0' - {0x00,0x42,0x7f,0x40,0x00}, // '1' - {0x42,0x61,0x51,0x49,0x46}, // '2' - {0x21,0x41,0x45,0x4b,0x31}, // '3' - {0x18,0x14,0x12,0x7f,0x10}, // '4' - {0x27,0x45,0x45,0x45,0x39}, // '5' - {0x3c,0x4a,0x49,0x49,0x30}, // '6' - {0x01,0x71,0x09,0x05,0x03}, // '7' - {0x36,0x49,0x49,0x49,0x36}, // '8' - {0x06,0x49,0x49,0x29,0x1e}, // '9' - {0x00,0x36,0x36,0x00,0x00}, // ':' - {0x00,0x56,0x36,0x00,0x00}, // ';' - {0x08,0x14,0x22,0x41,0x00}, // '<' - {0x14,0x14,0x14,0x14,0x14}, // '=' - {0x00,0x41,0x22,0x14,0x08}, // '>' - {0x02,0x01,0x51,0x09,0x06}, // '?' - {0x32,0x49,0x79,0x41,0x3e}, // '@' - {0x7e,0x11,0x11,0x11,0x7e}, // 'A' - {0x7f,0x49,0x49,0x49,0x36}, // 'B' - {0x3e,0x41,0x41,0x41,0x22}, // 'C' - {0x7f,0x41,0x41,0x22,0x1c}, // 'D' - {0x7f,0x49,0x49,0x49,0x41}, // 'E' - {0x7f,0x09,0x09,0x09,0x01}, // 'F' - {0x3e,0x41,0x49,0x49,0x7a}, // 'G' - {0x7f,0x08,0x08,0x08,0x7f}, // 'H' - {0x00,0x41,0x7f,0x41,0x00}, // 'I' - {0x20,0x40,0x41,0x3f,0x01}, // 'J' - {0x7f,0x08,0x14,0x22,0x41}, // 'K' - {0x7f,0x40,0x40,0x40,0x40}, // 'L' - {0x7f,0x02,0x0c,0x02,0x7f}, // 'M' - {0x7f,0x04,0x08,0x10,0x7f}, // 'N' - {0x3e,0x41,0x41,0x41,0x3e}, // 'O' - {0x7f,0x09,0x09,0x09,0x06}, // 'P' - {0x3e,0x41,0x51,0x21,0x5e}, // 'Q' - {0x7f,0x09,0x19,0x29,0x46}, // 'R' - {0x46,0x49,0x49,0x49,0x31}, // 'S' - {0x01,0x01,0x7f,0x01,0x01}, // 'T' - {0x3f,0x40,0x40,0x40,0x3f}, // 'U' - {0x1f,0x20,0x40,0x20,0x1f}, // 'V' - {0x3f,0x40,0x38,0x40,0x3f}, // 'W' - {0x63,0x14,0x08,0x14,0x63}, // 'X' - {0x07,0x08,0x70,0x08,0x07}, // 'Y' - {0x61,0x51,0x49,0x45,0x43}, // 'Z' - {0x00,0x7f,0x41,0x41,0x00}, // '[' - {0x02,0x04,0x08,0x10,0x20}, // '\\' - {0x00,0x41,0x41,0x7f,0x00}, // ']' - {0x04,0x02,0x01,0x02,0x04}, // '^' - {0x40,0x40,0x40,0x40,0x40}, // '_' - {0x00,0x01,0x02,0x04,0x00}, // '`' - {0x20,0x54,0x54,0x54,0x78}, // 'a' - {0x7f,0x48,0x44,0x44,0x38}, // 'b' - {0x38,0x44,0x44,0x44,0x20}, // 'c' - {0x38,0x44,0x44,0x48,0x7f}, // 'd' - {0x38,0x54,0x54,0x54,0x18}, // 'e' - {0x08,0x7e,0x09,0x01,0x02}, // 'f' - {0x0c,0x52,0x52,0x52,0x3e}, // 'g' - {0x7f,0x08,0x04,0x04,0x78}, // 'h' - {0x00,0x44,0x7d,0x40,0x00}, // 'i' - {0x20,0x40,0x44,0x3d,0x00}, // 'j' - {0x7f,0x10,0x28,0x44,0x00}, // 'k' - {0x00,0x41,0x7f,0x40,0x00}, // 'l' - {0x7c,0x04,0x18,0x04,0x78}, // 'm' - {0x7c,0x08,0x04,0x04,0x78}, // 'n' - {0x38,0x44,0x44,0x44,0x38}, // 'o' - {0x7c,0x14,0x14,0x14,0x08}, // 'p' - {0x08,0x14,0x14,0x18,0x7c}, // 'q' - {0x7c,0x08,0x04,0x04,0x08}, // 'r' - {0x48,0x54,0x54,0x54,0x20}, // 's' - {0x04,0x3f,0x44,0x40,0x20}, // 't' - {0x3c,0x40,0x40,0x20,0x7c}, // 'u' - {0x1c,0x20,0x40,0x20,0x1c}, // 'v' - {0x3c,0x40,0x30,0x40,0x3c}, // 'w' - {0x44,0x28,0x10,0x28,0x44}, // 'x' - {0x0c,0x50,0x50,0x50,0x3c}, // 'y' - {0x44,0x64,0x54,0x4c,0x44}, // 'z' - {0x00,0x08,0x36,0x41,0x00}, // '{' - {0x00,0x00,0x7f,0x00,0x00}, // '|' - {0x00,0x41,0x36,0x08,0x00}, // '}' - {0x10,0x08,0x08,0x10,0x08}, // '~' - {0x00,0x00,0x00,0x00,0x00} // DEL -}; diff --git a/main/rgb/rgb.c b/main/rgb/rgb.c deleted file mode 100644 index 3b9f5e7..0000000 --- a/main/rgb/rgb.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "rgb/rgb.h" - -#include "esp_check.h" -#include "led_strip.h" - -#define RGB_GPIO 38 - -static led_strip_handle_t s_strip = NULL; - -esp_err_t rgb_init(void) -{ - led_strip_config_t strip_config = { - .strip_gpio_num = RGB_GPIO, - .max_leds = 1, - .led_pixel_format = LED_PIXEL_FORMAT_GRB, - .led_model = LED_MODEL_WS2812, - .flags.invert_out = false, - }; - - led_strip_rmt_config_t rmt_config = { - .clk_src = RMT_CLK_SRC_DEFAULT, - .resolution_hz = 10 * 1000 * 1000, - .flags.with_dma = false, - }; - - ESP_RETURN_ON_ERROR(led_strip_new_rmt_device(&strip_config, &rmt_config, &s_strip), "rgb", "led_strip init failed"); - led_strip_clear(s_strip); - return ESP_OK; -} - -void rgb_set(uint8_t r, uint8_t g, uint8_t b) -{ - if (!s_strip) { - return; - } - /* Swap R/G for this board's LED ordering */ - led_strip_set_pixel(s_strip, 0, g, r, b); - led_strip_refresh(s_strip); -} diff --git a/main/rgb/rgb.h b/main/rgb/rgb.h deleted file mode 100644 index fa59683..0000000 --- a/main/rgb/rgb.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -esp_err_t rgb_init(void); -void rgb_set(uint8_t r, uint8_t g, uint8_t b); - -#ifdef __cplusplus -} -#endif