Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions boards/hosyond-es3c28p/audio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2025 Vinicius Fortuna
# SPDX-License-Identifier: Apache-2.0

# Audio codec: ES8311 (I2C control + I2S data), shared I2C bus on GPIO15/16
# Amplifier: FM8002E, enabled via GPIO1 (active low)
# Microphone: MEMS LMA2718B381 (via ES8311 ADC)
# Speaker connector: 1.25mm 2P

i2s_audio:
- id: i2s_audio_bus
i2s_mclk_pin: GPIO4 # MCLK
i2s_bclk_pin: GPIO5 # BCLK
i2s_lrclk_pin: GPIO7 # LRCK

audio_dac:
- id: es8311_dac
platform: es8311
i2c_id: i2c_bus
use_microphone: true
bits_per_sample: 16bit
sample_rate: 48000

# TODO(fortuna): I haven't validated the microphone config yet.
microphone:
- id: main_microphone
platform: i2s_audio
i2s_audio_id: i2s_audio_bus
i2s_din_pin: GPIO6 # ASDOUT from Codec -> I2S_DI on ESP32
adc_type: external
bits_per_sample: 16bit
sample_rate: 48000

speaker:
- id: main_speaker
platform: i2s_audio
i2s_audio_id: i2s_audio_bus
i2s_dout_pin: GPIO8 # I2S_DO from ESP32 -> DSDIN on Codec
dac_type: external
audio_dac: es8311_dac
bits_per_sample: 16bit
sample_rate: 48000
channel: mono

Comment thread
fortuna marked this conversation as resolved.
switch:
# FM8002E amplifier enable — must be on (low) to hear speaker output
- id: enable_speaker_amp
platform: gpio
name: Enable Speaker
pin:
number: GPIO1
inverted: true # Active low
restore_mode: RESTORE_DEFAULT_ON
entity_category: config
disabled_by_default: true
73 changes: 73 additions & 0 deletions boards/hosyond-es3c28p/board.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2025 Vinicius Fortuna
# SPDX-License-Identifier: Apache-2.0

# Hosyond ES3C28P — 2.8" 240×320 IPS TFT ESP32-S3 display board with capacitive touch
#
# References:
# - https://hosyond.com/
# - https://www.lcdwiki.com/2.8inch_ESP32-S3_Display
# - Schematic: https://www.lcdwiki.com/res/ES3C28P/2.8inch_ESP32-S3_Display_Schematic.pdf
# - User manual: https://www.lcdwiki.com/res/ES3C28P/2.8inch_IPS_ESP32-S3_ES3C28P_ES3N28P_User_Manual.pdf

packages:
display: !include ./display.yaml
audio: !include ./audio.yaml

esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: esp-idf

psram:
mode: octal
speed: 80MHz

# SPI bus — shared by the ILI9341 display
spi:
- id: spi_bus
clk_pin: GPIO12 # LCD_CLK
mosi_pin: GPIO11 # LCD_MOSI
miso_pin: GPIO13 # LCD_MISO

# I2C bus — shared by FT6336 touch controller and ES8311 audio codec
i2c:
- id: i2c_bus
scl: GPIO15
sda: GPIO16
scan: true
frequency: 400kHz

# NOTE: ESPHome does not yet implement sd_mmc_card. Uncomment once supported:
# SD card (SDIO 4-bit): CLK=IO38, CMD=IO40, D0=IO39, D1=IO41, D2=IO48, D3=IO47
#
# sd_mmc_card:
# id: sd_card
# clk_pin: GPIO38
# cmd_pin: GPIO40
# data0_pin: GPIO39
# data1_pin: GPIO41
# data2_pin: GPIO48
# data3_pin: GPIO47
# mode_1bit: false

binary_sensor:
- id: boot_button
platform: gpio
name: Boot Button
pin:
number: GPIO0
inverted: true
mode:
input: true
pullup: true

sensor:
- id: battery_voltage
platform: adc
pin: GPIO9
name: Battery Voltage
attenuation: 12dB
update_interval: 60s
filters:
- multiply: 2.0 # Voltage divider on the ADC pin
175 changes: 175 additions & 0 deletions boards/hosyond-es3c28p/demo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Copyright 2025 Vinicius Fortuna
# SPDX-License-Identifier: Apache-2.0

packages:
board: !include ./board.yaml

esphome:
name: hosyond-es3c28p-demo
friendly_name: Hosyond ES3C28P Demo
on_boot:
priority: -100
then:
- switch.turn_on: enable_speaker_amp
- delay: 500ms
# Set a reasonable default volume
- speaker.volume_set:
id: main_speaker
volume: 0.5
- light.turn_on:
id: status_led
red: 0%
green: 100%
blue: 0%
brightness: 50%
- rtttl.play: "boot:d=1,o=4,b=60:c"

logger:

rtttl:
speaker: main_speaker
gain: 0.2

# Boot button: play a sound and toggle LED
binary_sensor:
- id: !extend boot_button
on_press:
- light.toggle: status_led
- rtttl.play: "click:d=8,o=5,b=160:c6,e6"

# Update battery label when ADC fires
sensor:
- id: !extend battery_voltage
on_value:
- lvgl.label.update:
id: battery_label
text:
format: "%.2fV"
args: [x]

lvgl:
displays: [main_display]
touchscreens: [main_touchscreen]
pages:
- id: main_page
widgets:
- label:
align: top_mid
y: 8
text: "ES3C28P Demo"
- label:
id: battery_label
align: top_right
x: -8
y: 8
text: "--V"

- label:
x: 8
y: 52
text: "R"
- slider:
id: red_slider
x: 28
y: 42
width: 190
min_value: 0
max_value: 255
on_value:
- light.turn_on:
id: status_led
red: !lambda "return lv_slider_get_value(id(red_slider)) / 255.0f;"
green: !lambda "return lv_slider_get_value(id(green_slider)) / 255.0f;"
blue: !lambda "return lv_slider_get_value(id(blue_slider)) / 255.0f;"
brightness: 1.0

- label:
x: 8
y: 102
text: "G"
- slider:
id: green_slider
x: 28
y: 92
width: 190
min_value: 0
max_value: 255
on_value:
- light.turn_on:
id: status_led
red: !lambda "return lv_slider_get_value(id(red_slider)) / 255.0f;"
green: !lambda "return lv_slider_get_value(id(green_slider)) / 255.0f;"
blue: !lambda "return lv_slider_get_value(id(blue_slider)) / 255.0f;"
brightness: 1.0

- label:
x: 8
y: 152
text: "B"
- slider:
id: blue_slider
x: 28
y: 142
width: 190
min_value: 0
max_value: 255
on_value:
- light.turn_on:
id: status_led
red: !lambda "return lv_slider_get_value(id(red_slider)) / 255.0f;"
green: !lambda "return lv_slider_get_value(id(green_slider)) / 255.0f;"
blue: !lambda "return lv_slider_get_value(id(blue_slider)) / 255.0f;"
brightness: 1.0

- label:
x: 8
y: 202
text: "BL"
- slider:
id: backlight_slider
x: 28
y: 192
width: 190
min_value: 0
max_value: 100
value: 100
on_value:
- light.turn_on:
id: display_backlight_light
brightness: !lambda "return lv_slider_get_value(id(backlight_slider)) / 100.0f;"

- label:
x: 8
y: 255
text: "Amp"
- switch:
id: amp_ui_switch
x: 44
y: 248
state:
checked: true
on_click:
- if:
condition:
lambda: 'return lv_obj_has_state(id(amp_ui_switch), LV_STATE_CHECKED);'
then:
- switch.turn_on: enable_speaker_amp
else:
- switch.turn_off: enable_speaker_amp
- button:
id: sound_btn
x: 130
y: 242
width: 100
height: 36
on_click:
- rtttl.play: "ui:d=8,o=5,b=160:e6,g6"
widgets:
- label:
align: center
text: "Sound"

- label:
align: bottom_mid
y: -8
text: "Boot btn: toggle LED + sound"
52 changes: 52 additions & 0 deletions boards/hosyond-es3c28p/display.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2025 Vinicius Fortuna
# SPDX-License-Identifier: Apache-2.0

# Display: ILI9341V, 2.8" IPS TFT, 240×320, SPI
# Touch: FT6336G capacitive, I2C
# RGB LED: WS2812B on GPIO42

output:
- id: display_backlight
platform: ledc
pin: GPIO45 # LCD_BL (active high)

light:
- id: display_backlight_light
platform: monochromatic
name: Display Backlight
icon: "mdi:television"
entity_category: config
output: display_backlight
restore_mode: RESTORE_DEFAULT_ON
default_transition_length: 0ms

- id: status_led
platform: esp32_rmt_led_strip
name: Status LED
pin: GPIO42 # WS2812B
num_leds: 1
rgb_order: GRB
Comment thread
fortuna marked this conversation as resolved.
chipset: WS2812

display:
- id: main_display
platform: ili9xxx
model: ILI9341
spi_id: spi_bus
cs_pin: GPIO10 # LCD_CS
dc_pin: GPIO46 # LCD_DC
# RST is tied to the EN (system reset) pin; no software reset available.
invert_colors: false
data_rate: 40MHz
update_interval: never
auto_clear_enabled: false

touchscreen:
- id: main_touchscreen
platform: ft63x6
i2c_id: i2c_bus
display: main_display
interrupt_pin: GPIO17 # Touch IRQ (active low)
# RST is active low. ESPHome ft63x6 drives the pin LOW then HIGH to reset,
# so inverted must NOT be set (that would leave the chip held in reset).
reset_pin: GPIO18
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading