Board Support Package (BSP) for SmartRing-Plus —— SmartRing-Plus development board designed by VIEWE based on ESP32-S3-N16R8.
| Author | Ayang |
| Company | SHENZHEN VIEWE TECHNOLOGY CO.,LTD |
| Target | esp32s3 (16 MB Flash + 8 MB Octal PSRAM recommended) |
| ESP-IDF | ≥ 5.5 |
| License | Apache-2.0 |
This component wraps the board’s verified peripherals behind a stable bsp_* API so application code does not hard-code pins, init sequences, or bus wiring.
#include "bsp/smartring_plus.h"| Subsystem | Hardware | Primary API |
|---|---|---|
| Display | ST77916 QSPI, 360×360 round | bsp_display_init(), backlight / color-swap helpers |
| Touch | CST816 (I2C) | Registered as LVGL pointer; bsp_touch_get_handle() |
| Shared bus | I2C SDA=8 / SCL=9 / 400 kHz | bsp_i2c_get_bus() |
| Audio | ES8311 + I2S + PA | bsp_audio_init() / read / write / set_mute |
| Storage | SDMMC 4-bit FAT | bsp_sd_init(), mount /sdcard |
| Battery / PMIC | V1 ADC GPIO1; V2 AXP2101 (runtime probe) | bsp_battery_* / bsp_battery_get_pmic_type() |
| Power | GPIO47 soft power-off (same for V1/V2) | bsp_power_shutdown() |
| IMU | QMI8658A | bsp_imu_init() / get_accel / get_tilt / calib helpers |
| UI stack | esp_lvgl_adapter + LVGL 9 |
Ready after bsp_display_init() |
Pin numbers, I2C addresses, and mount points live in a single source of truth:
include/bsp/board_config.h.
In your project’s idf_component.yml (or main/idf_component.yml):
dependencies:
viewesmart/smartring_plus: "^1.1.2"Pull dependencies:
idf.py set-target esp32s3
idf.py reconfigureAlternatively, place this folder under your project's components/smartring_plus for a local dependency.
Several modules share the I2C bus created inside bsp_display_init(). Call display first, then optional peripherals:
#include "bsp/smartring_plus.h"
#include "esp_lv_adapter.h"
#include "esp_log.h"
void app_main(void)
{
ESP_ERROR_CHECK(bsp_display_init()); /* I2C + touch + LCD + LVGL adapter + backlight */
ESP_ERROR_CHECK(bsp_battery_init());
ESP_ERROR_CHECK(bsp_power_init());
/* Optional — fail softly if card / IMU absent */
if (bsp_sd_init() != ESP_OK) {
ESP_LOGW("app", "SD not mounted");
}
if (bsp_imu_init() != ESP_OK) {
ESP_LOGW("app", "IMU init failed");
}
ESP_ERROR_CHECK(bsp_audio_init());
ESP_ERROR_CHECK(esp_lv_adapter_lock(-1));
/* create LVGL screens here */
esp_lv_adapter_unlock();
}All public APIs are declared in include/bsp/smartring_plus.h.
| Function | Description |
|---|---|
esp_err_t bsp_display_init(void) |
Bring up shared I2C, CST816 touch, ST77916 QSPI panel, esp_lvgl_adapter, and backlight at 100%. After success, the LVGL task is running. |
void bsp_display_backlight_set(uint8_t percent) |
Backlight PWM 0–100 (GPIO46, active high). |
void bsp_display_set_swap_bytes(bool enable) |
Runtime RGB565 byte-swap (debug / panel variants); forces full redraw. |
bool bsp_display_get_swap_bytes(void) |
Current swap state. |
lv_display_t *bsp_display_get_handle(void) |
LVGL display handle. |
esp_lcd_touch_handle_t bsp_touch_get_handle(void) |
Touch handle for raw reads; NULL if not ready. |
i2c_master_bus_handle_t bsp_i2c_get_bus(void) |
Shared I2C bus for codec / IMU; NULL before display init. |
LVGL note: Always take esp_lv_adapter_lock(-1) before creating or updating UI objects, then esp_lv_adapter_unlock().
ESP_ERROR_CHECK(bsp_display_init());
bsp_display_backlight_set(80);
ESP_ERROR_CHECK(esp_lv_adapter_lock(-1));
lv_obj_t *scr = lv_obj_create(NULL);
lv_screen_load(scr);
esp_lv_adapter_unlock();Cell (same on both HW revisions): single-cell LiPo 3.7V / 600mAh, CV 4.2V. Specs live in board_config.h (BOARD_BAT_*).
bsp_battery_init() probes AXP2101 on the shared I2C bus (read 0x03, expect CHIP ID 0x4A):
| Path | When | Fuel / charge | Also |
|---|---|---|---|
| V2 | Probe OK | REG 0xA4 %; 0x01[6:5] direction; VBAT ADC 0x34/0x35 (1 mV/LSB) |
ALDO3=3.3V; ICC 300mA (0.5C); CV 4.2V; precharge 75mA; Iterm 25mA |
| V1 | No AXP / ID mismatch | GPIO1 ADC + same-cell OCV table | Charge from voltage hysteresis; IR-drop compensate while charging |
| Function | Description |
|---|---|
esp_err_t bsp_battery_init(void) |
Probe and start 1 Hz sampler (requires bsp_display_init first). |
bsp_pmic_type_t bsp_battery_get_pmic_type(void) |
BSP_PMIC_V1_ADC / BSP_PMIC_V2_AXP2101. |
void bsp_battery_get_data(bsp_battery_data_t *out) |
Thread-safe snapshot. |
float bsp_battery_get_voltage(void) |
Latest voltage (V). |
int bsp_battery_get_percent(void) |
0–100 %. |
bool bsp_battery_is_charging(void) |
Charging state. |
ESP_ERROR_CHECK(bsp_battery_init());
ESP_LOGI("bat", "PMIC=%d", (int)bsp_battery_get_pmic_type());
bsp_battery_data_t bat;
bsp_battery_get_data(&bat);
ESP_LOGI("bat", "%.2f V %d%% %s",
bat.voltage_v, bat.percent,
bat.charging ? "charging" : "discharge");| Function | Description |
|---|---|
esp_err_t bsp_power_init(void) |
Configure soft-off GPIO (default idle). |
void bsp_power_shutdown(void) |
Soft power-off: hold GPIO47 high for BOARD_PW_OFF_HOLD_MS (3.5 s). Normally does not return. |
V1 and V2 both use GPIO47; AXP2101 is not used for shutdown.
ESP_ERROR_CHECK(bsp_power_init());
/* … user confirmed … */
bsp_power_shutdown();Fixed stream format: 48 kHz / 16-bit / stereo interleaved (~192 KB/s).
bsp_audio_init() only opens hardware; it does not set gain/volume — configure via esp_codec_dev on the codec handle.
| Function | Description |
|---|---|
esp_err_t bsp_audio_init(void) |
I2S + ES8311; requires prior bsp_display_init() for I2C. Idempotent (second call skips). PA is driven by the codec pa_pin. |
esp_codec_dev_handle_t bsp_audio_get_codec(void) |
Codec handle for gain / volume / mute / streaming. |
i2s_chan_handle_t bsp_audio_get_rx(void) |
I2S RX channel for raw PCM. |
esp_err_t bsp_audio_read(void *buf, size_t len, uint32_t timeout_ms) |
Blocking record until len bytes filled. |
esp_err_t bsp_audio_write(const void *buf, size_t len) |
Blocking playback of len bytes. |
esp_err_t bsp_audio_set_mute(bool mute) |
Output mute. |
Playback sequence (important): unmute → write (can be chunked) → mute again. Skipping the final mute can cause a pop/click.
Suggested app settings (board-tuned, not hard requirements):
- Mic gain ≈ 36 dB via
esp_codec_dev_set_in_gain()(driver snaps to 6 dB steps). - Playback volume ≈ 75 via
esp_codec_dev_set_out_vol()(0–100).
ESP_ERROR_CHECK(bsp_audio_init());
esp_codec_dev_handle_t codec = bsp_audio_get_codec();
esp_codec_dev_set_out_vol(codec, 75);
esp_codec_dev_set_in_gain(codec, 36.0f);
/* playback */
ESP_ERROR_CHECK(bsp_audio_set_mute(false));
ESP_ERROR_CHECK(bsp_audio_write(pcm, pcm_bytes));
ESP_ERROR_CHECK(bsp_audio_set_mute(true));
/* record */
uint8_t buf[48 * 2 * 2 * 100]; /* ~100 ms stereo */
ESP_ERROR_CHECK(bsp_audio_read(buf, sizeof(buf), 1000));When calling
esp_codec_dev_write()directly, return value0means success (opposite ofesp_err_tconvention).
| Function | Description |
|---|---|
esp_err_t bsp_imu_init(void) |
±2 g / 250 Hz accel + 250 Hz sample task + EMA filter. Needs shared I2C (bsp_display_init() first). |
void bsp_imu_get_accel(float *ax, float *ay, float *az) |
Filtered acceleration (m/s²), board axes, thread-safe. |
void bsp_imu_get_tilt(float *gx, float *gy) |
Screen-plane tilt vector (m/s²); subtracts calib zero if calibrated. +gx = right, +gy = down. |
Level calibration (optional reference):
| Function | Description |
|---|---|
void bsp_imu_calib_start(void) |
Start / restart calibration window (30 s deadline). |
bsp_imu_calib_status_t bsp_imu_calib_poll(void) |
IDLE / RUNNING / PASSED / FAILED. Poll from UI every 100–200 ms. |
void bsp_imu_calib_get_info(float *std_g, float *tilt_deg, uint32_t *elapsed_ms) |
Debug metrics during RUNNING. |
ESP_ERROR_CHECK(bsp_imu_init());
float ax, ay, az, gx, gy;
bsp_imu_get_accel(&ax, &ay, &az);
bsp_imu_get_tilt(&gx, &gy);
bsp_imu_calib_start();
while (bsp_imu_calib_poll() == BSP_IMU_CALIB_RUNNING) {
vTaskDelay(pdMS_TO_TICKS(100));
}You may ignore calibration entirely and only use bsp_imu_get_accel().
Mount point: /sdcard (BOARD_SD_MOUNT_POINT). No card-detect pin — software cannot distinguish “no card” from “mount failure”.
| Function | Description |
|---|---|
esp_err_t bsp_sd_init(void) |
SDMMC 4-bit + FAT VFS. Does not assert/reboot on failure. Second call → ESP_ERR_INVALID_STATE. |
bool bsp_sd_is_mounted(void) |
Mount status. |
void bsp_sd_deinit(void) |
Unmount (safe if not mounted). |
const sdmmc_card_t *bsp_sd_get_card(void) |
Card info for sdmmc_card_print_info(). |
esp_err_t bsp_sd_list(const char *path, bsp_sd_entry_t **entries, size_t *count) |
Sorted directory listing (dirs first). Caller must bsp_sd_free_list(). |
void bsp_sd_free_list(bsp_sd_entry_t *entries, size_t count) |
Free list from bsp_sd_list(). |
if (bsp_sd_init() == ESP_OK) {
bsp_sd_entry_t *ents = NULL;
size_t n = 0;
if (bsp_sd_list("/sdcard", &ents, &n) == ESP_OK) {
for (size_t i = 0; i < n; i++) {
ESP_LOGI("sd", "%s %s",
ents[i].type == BSP_SD_ENTRY_DIR ? "DIR " : "FILE",
ents[i].name);
}
bsp_sd_free_list(ents, n);
}
}| Function | GPIOs / notes |
|---|---|
| I2C | SDA 8, SCL 9, 400 kHz |
| Touch CST816 | Addr 0x15, RST 40, INT 41 |
| LCD ST77916 QSPI | SCL 10, CS 11, D0–D3 12/13/15/14, RST 39, TE 38 |
| Backlight | 46 (LEDC PWM) |
| SDMMC | CLK 4, CMD 5, D0–D3 3/2/7/6 |
| Battery ADC (V1) | 1 (ADC1_CH0, ÷2) |
| AXP2101 (V2) | I2C addr 0x34 (shared bus) |
| Soft power-off | 47 (hold high ~3.5 s; same for V1/V2) |
| Audio I2S | MCLK 48, BCLK 21, WS 17, DOUT 16, DIN 18 |
| PA enable | 45 |
| Codec ES8311 | Addr 0x18 on shared I2C |
| IMU INT1 | 42 (reserved; driver currently polls) |
Reserved / do not reuse as GPIO: BOOT (0), USB (19/20), UART0 (43/44), Octal PSRAM (33–37).
Full macros: include/bsp/board_config.h.
Minimal settings that this BSP expects:
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n
CONFIG_LV_USE_CLIB_MALLOC=y
Also enable Montserrat fonts / LVGL options as required by your UI.
See the SmartRing-Plus full-device demo sdkconfig.defaults for a known-good complete set.
smartring_plus/
├── include/bsp/
│ ├── smartring_plus.h # Public API
│ └── board_config.h # Pins & board constants
├── smartring_bsp.c # Display / touch / I2C / LVGL adapter
├── bsp_audio.c
├── bsp_battery.c
├── bsp_axp2101.c # V2 AXP2101 low-level
├── bsp_power.c
├── bsp_imu.c
├── bsp_sd.c
├── lcd_init_seq.h # ST77916 init sequence (vendor B)
├── CMakeLists.txt
├── idf_component.yml
└── LICENSE
espressif/esp_lvgl_adapterlvgl/lvgl9.xespressif/esp_lcd_st77916espressif/esp_lcd_touch_cst816sespressif/esp_codec_devwaveshare/qmi8658
Apache-2.0
Copyright 2024–2026 Ayang / SHENZHEN VIEWE TECHNOLOGY CO.,LTD
Special thanks to @nianhua-entropy for the generous help that made this BSP possible.