diff --git a/application/edge_agent/boards/community/anyboard/board_devices.yaml b/application/edge_agent/boards/community/anyboard/board_devices.yaml new file mode 100644 index 000000000..969e8b5b9 --- /dev/null +++ b/application/edge_agent/boards/community/anyboard/board_devices.yaml @@ -0,0 +1,98 @@ +version: 1.0.0 +# Devices Configuration for Anyboard +devices: + - name: fake_camera # Just for invoking camera dependencies, no actual camera hardware connected + type: camera + version: default + sub_type: "dvp" + init_skip: true + peripherals: + - name: i2c_master + frequency: 100000 + + - name: camera + type: custom + version: default + dependencies: + espressif/usb_host_uvc: "2.4.2" + + # PDM Microphone (bare PDM mic, no I2C codec IC). + # Lua audio reads the already-initialized i2s_audio_in peripheral directly. + - name: audio_adc + chip: internal + type: audio_codec + version: default + config: + adc_enabled: true + dac_enabled: false + adc_max_channel: 1 + adc_channel_mask: "1" + adc_init_gain: 0 + peripherals: + - name: i2s_audio_in + + # SPI Display Device (ST7789 320x240 landscape, rotated right from portrait) + # SCLK=IO5, SDA=IO7, RST=IO6, DC=IO8, CS=IO4 + - name: display_lcd + chip: st7789 + type: display_lcd + sub_type: spi + version: default + config: + mirror_x: false + mirror_y: true + swap_xy: true + invert_color: true + x_max: 320 + y_max: 200 + io_spi_config: + cs_gpio_num: 4 + dc_gpio_num: 8 + spi_mode: 3 + pclk_hz: 80000000 + + lcd_panel_config: + reset_gpio_num: 6 + data_endian: "LCD_RGB_DATA_ENDIAN_BIG" + flags: + reset_active_high: false + vendor_config: NULL + + peripherals: + - name: spi_display + + # IMU Sensor (BMI270, I2C SCL=IO40 SDA=IO39, INT=IO18) + - name: imu_sensor + chip: bmi270 + type: custom + version: 1.0.0 + init_skip: true + config: + i2c_addr: 0x68 + frequency: 400000 + int_gpio_num: 18 + peripherals: + - name: i2c_master + + # Buttons (active low) + # enter=IO41, left=IO42, right=IO0 + - name: buttons + chip: gpio_button + type: custom + version: 1.0.0 + init_skip: true + config: + button_enter_gpio: 41 + button_left_gpio: 42 + button_right_gpio: 0 + active_level: 0 + + # Buzzer (IO46) + - name: buzzer + chip: gpio_buzzer + type: custom + version: 1.0.0 + init_skip: true + config: + gpio_num: 46 + active_level: 1 diff --git a/application/edge_agent/boards/community/anyboard/board_info.yaml b/application/edge_agent/boards/community/anyboard/board_info.yaml new file mode 100644 index 000000000..b7c9c1820 --- /dev/null +++ b/application/edge_agent/boards/community/anyboard/board_info.yaml @@ -0,0 +1,5 @@ +board: anyboard +chip: esp32s3 +version: 1.0.0 +description: "Anyboard Development Board" +manufacturer: "TEMP" diff --git a/application/edge_agent/boards/community/anyboard/board_peripherals.yaml b/application/edge_agent/boards/community/anyboard/board_peripherals.yaml new file mode 100644 index 000000000..7c6ed8019 --- /dev/null +++ b/application/edge_agent/boards/community/anyboard/board_peripherals.yaml @@ -0,0 +1,50 @@ +version: 1.0.0 +# Peripherals Configuration for Anyboard +peripherals: + # I2C Configuration (SCL=IO40, SDA=IO39) + - name: i2c_master + type: i2c + role: master + config: + port: 0 + pins: + sda: 39 + scl: 40 + + # I2S PDM RX for microphone (CLK=IO9, DIN=IO17) + - name: i2s_audio_in + type: i2s + version: default + role: master + format: pdm-in + config: + port: 0 + sample_rate_hz: 16000 + clk_src: I2S_CLK_SRC_DEFAULT + mclk_multiple: 256 + dn_sample_mode: I2S_PDM_DSR_8S + bclk_div: 8 + data_bit_width: 16 + slot_bit_width: I2S_SLOT_BIT_WIDTH_AUTO + slot_mode: I2S_SLOT_MODE_MONO + slot_mask: I2S_PDM_SLOT_LEFT + data_fmt: I2S_PDM_DATA_FMT_PCM + hp_en: true + hp_cut_off_freq_hz: 35.5 + amplify_num: 1 + pins: + clk: 9 + din: 17 + invert_flags: + clk_inv: false + + # LCD SPI Configuration for ST7789 (SCLK=IO5, MOSI=IO7) + - name: spi_display + type: spi + role: master + config: + spi_bus_config: + spi_port: SPI2_HOST + data0_io_num: 7 + sclk_io_num: 5 + max_transfer_sz: 6400 # 320 * 2 * 10 (10 lines RGB565) diff --git a/application/edge_agent/boards/community/anyboard/sdkconfig.defaults.board b/application/edge_agent/boards/community/anyboard/sdkconfig.defaults.board new file mode 100644 index 000000000..7f657378e --- /dev/null +++ b/application/edge_agent/boards/community/anyboard/sdkconfig.defaults.board @@ -0,0 +1,18 @@ +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE=y +CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=1024 +CONFIG_USB_UVC_VIDEO_DEVICE_URB_SIZE=4096 +CONFIG_APP_CLAW_LUA_MODULE_CAMERA=y +CONFIG_APP_CLAW_LUA_MODULE_VISION=y +CONFIG_APP_CLAW_LUA_MODULE_IMU=y +CONFIG_LUA_MODULE_IMU_CHIP_BMI270=y +CONFIG_ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT=y +CONFIG_APP_CLAW_LUA_MODULE_AUDIO=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y diff --git a/application/edge_agent/boards/community/anyboard/setup_device.c b/application/edge_agent/boards/community/anyboard/setup_device.c new file mode 100644 index 000000000..ba0f12d2c --- /dev/null +++ b/application/edge_agent/boards/community/anyboard/setup_device.c @@ -0,0 +1,156 @@ +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_video_init.h" +#include "esp_video_device.h" +#include "esp_board_manager_includes.h" +#include "gen_board_device_custom.h" + +static const char *TAG = "ANYBOARD_SETUP_DEVICE"; + +#define USB_UVC_DEV_NUM 1 +#define USB_HOST_TASK_PRIORITY 5 +#define USB_HOST_TASK_STACK_SIZE 4096 +#define USB_UVC_TASK_PRIORITY configMAX_PRIORITIES - 2 +#define USB_UVC_TASK_STACK_SIZE 4096 + +typedef struct { + dev_camera_handle_t handle; +} custom_usb_camera_handle_t; + +static int usb_camera_init(void *config, int cfg_size, void **device_handle) +{ + (void)config; + (void)cfg_size; + ESP_RETURN_ON_FALSE(device_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "invalid camera handle"); + +#if CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE + const esp_video_init_usb_uvc_config_t usb_uvc_config = { + .uvc = { + .uvc_dev_num = USB_UVC_DEV_NUM, + .task_stack = USB_UVC_TASK_STACK_SIZE, + .task_priority = USB_UVC_TASK_PRIORITY, + .task_affinity = 0, + }, + .usb = { + .init_usb_host_lib = true, + .task_stack = USB_HOST_TASK_STACK_SIZE, + .task_priority = USB_HOST_TASK_PRIORITY, + .task_affinity = 0, + }, + }; + const esp_video_init_config_t video_config = { + .usb_uvc = &usb_uvc_config, + }; + + esp_err_t ret = esp_video_init(&video_config); + if (ret != ESP_OK) { + return ret; + } + + custom_usb_camera_handle_t *handle = calloc(1, sizeof(*handle)); + if (handle == NULL) { + (void)esp_video_deinit(); + return ESP_ERR_NO_MEM; + } + + handle->handle.dev_path = ESP_VIDEO_USB_UVC_NAME(0); + handle->handle.meta_path = ""; + *device_handle = &handle->handle; + ESP_LOGI(TAG, "USB UVC camera initialized, dev_path: %s", handle->handle.dev_path); + return ESP_OK; +#else + ESP_LOGE(TAG, "USB UVC camera is disabled. Enable CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static int usb_camera_deinit(void *device_handle) +{ +#if CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE + free(device_handle); + ESP_RETURN_ON_ERROR(esp_video_deinit(), TAG, "failed to deinit USB UVC camera"); + ESP_LOGI(TAG, "USB UVC camera deinitialized"); + return ESP_OK; +#else + (void)device_handle; + return ESP_OK; +#endif +} + +CUSTOM_DEVICE_IMPLEMENT(camera, usb_camera_init, usb_camera_deinit); + +/* + * The AnyBoard LCD uses a cropped ST7789 glass. Without a panel gap, the + * top rows are addressed outside the visible window after landscape rotation. + */ +#define ANYBOARD_LCD_OFFSET_X 0 +#define ANYBOARD_LCD_OFFSET_Y 16 + +typedef struct { + int cmd; + const void *data; + size_t data_bytes; + unsigned int delay_ms; +} st7789_lcd_init_cmd_t; + +static const st7789_lcd_init_cmd_t vendor_specific_init[] = { + {0x11, (uint8_t []){0x00}, 1, 120}, + {0xB2, (uint8_t []){0x0C, 0x0C, 0x00, 0x33, 0x33}, 5, 0}, + {0x35, (uint8_t []){0x00}, 1, 0}, + {0x36, (uint8_t []){0x00}, 1, 0}, + {0x3A, (uint8_t []){0x05}, 1, 0}, + {0xB7, (uint8_t []){0x35}, 1, 0}, + {0xBB, (uint8_t []){0x2D}, 1, 0}, + {0xC0, (uint8_t []){0x2C}, 1, 0}, + {0xC2, (uint8_t []){0x01}, 1, 0}, + {0xC3, (uint8_t []){0x15}, 1, 0}, + {0xC4, (uint8_t []){0x20}, 1, 0}, + {0xC6, (uint8_t []){0x0F}, 1, 0}, + {0xD0, (uint8_t []){0xA4, 0xA1}, 2, 0}, + {0xD6, (uint8_t []){0xA1}, 1, 0}, + {0xE0, (uint8_t []){0x70, 0x05, 0x0A, 0x0B, 0x0A, 0x27, 0x2F, 0x44, 0x47, 0x37, 0x14, 0x14, 0x29, 0x2F}, 14, 0}, + {0xE1, (uint8_t []){0x70, 0x07, 0x0C, 0x08, 0x08, 0x04, 0x2F, 0x33, 0x46, 0x18, 0x15, 0x15, 0x2B, 0x2D}, 14, 0}, + {0x21, (uint8_t []){0x00}, 1, 0}, + {0x29, (uint8_t []){0x00}, 1, 0}, + {0x2C, (uint8_t []){0x00}, 1, 0}, +}; + +esp_err_t lcd_panel_factory_entry_t(esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) +{ + esp_err_t ret = esp_lcd_new_panel_st7789(io, panel_dev_config, ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create ST7789 panel: %s", esp_err_to_name(ret)); + return ret; + } + + ret = esp_lcd_panel_set_gap(*ret_panel, ANYBOARD_LCD_OFFSET_X, ANYBOARD_LCD_OFFSET_Y); + if (ret != ESP_OK) { + ESP_LOGW(TAG, "esp_lcd_panel_set_gap failed: %s", esp_err_to_name(ret)); + } + + for (size_t i = 0; i < sizeof(vendor_specific_init) / sizeof(vendor_specific_init[0]); i++) { + ret = esp_lcd_panel_io_tx_param(io, vendor_specific_init[i].cmd, + vendor_specific_init[i].data, + vendor_specific_init[i].data_bytes); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to send init cmd 0x%02x: %s", vendor_specific_init[i].cmd, esp_err_to_name(ret)); + return ret; + } + if (vendor_specific_init[i].delay_ms > 0) { + vTaskDelay(pdMS_TO_TICKS(vendor_specific_init[i].delay_ms)); + } + } + + esp_lcd_panel_reset(*ret_panel); + esp_lcd_panel_init(*ret_panel); + + return ESP_OK; +}