Skip to content
Open
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
49 changes: 49 additions & 0 deletions boards/_boards_json/hosyond-35-s3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0X303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "hosyond-35-s3"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"openocd_target": "esp32s3-builtin.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Hosyond 3.5 S3",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"url": "https://www.hosyond.com",
"vendor": "Hosyond"
}
55 changes: 55 additions & 0 deletions boards/_variants/hosyond-35-s3/pins_arduino.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h

#include <stdint.h>
#include "soc/soc_caps.h"

// I2C — override standard esp32s3 defaults (SDA=8, SCL=9) for this board
static const uint8_t SDA = 38;
static const uint8_t SCL = 39;

// Grove connector aliases
#define GROVE_SDA 38
#define GROVE_SCL 39

// Serial / GPS
static const uint8_t TX = 43;
static const uint8_t RX = 44;

// No SPI bus is exposed on this board: SPI2_HOST drives the QSPI display, SD uses SDMMC,
// and the only free GPIOs are IO45/IO46. Leave the generic SPI pins and every radio/SD-SPI
// alias unassigned (-1) so Bruce's radio menus can never reconfigure the display pins.
// Bruce's precompiler_flags.h supplies -1 for the CC1101/NRF24/W5500/SDCARD macros.
#define SPI_SS_PIN -1
#define SPI_MOSI_PIN -1
#define SPI_MISO_PIN -1
#define SPI_SCK_PIN -1

static const uint8_t SS = SPI_SS_PIN;
static const uint8_t MOSI = SPI_MOSI_PIN;
static const uint8_t MISO = SPI_MISO_PIN;
static const uint8_t SCK = SPI_SCK_PIN;

// RF module defaults: the two spare GPIOs (shared with IR). Bruce's own default would be
// the I2C pair (GROVE_SDA/SCL = 38/39), which carry touch and the audio codec.
#define RF_TX_DEFAULT_PIN 45
#define RF_RX_DEFAULT_PIN 46

// Font sizes (used by Bruce display code).
// Deliberately static const (NOT #define) so that FastLED's internal
// `using FP = fl::s16x16;` type-alias is not broken by macro substitution.
static const uint8_t FP = 1;
static const uint8_t FM = 2;
static const uint8_t FG = 3;

// USB HID capable
// On-board WS2812B (3-channel, GRB) on RGB_LED
#define LED_TYPE WS2812B
#define LED_ORDER GRB
#define LED_TYPE_IS_RGBW 0
#define LED_COUNT 1
#define LED_COLOR_STEP 15

#define USB_as_HID 1

#endif /* Pins_Arduino_h */
47 changes: 47 additions & 0 deletions boards/hosyond-35-s3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Hosyond 3.5" ESP32-S3 (ST77922, QSPI)

ESP32-S3-N16R8, 320x480 ST77922 over quad SPI, ST77922 I2C touch @ 0x55.
Build env: `hosyond-35-s3`. First hardware bring-up: 2026-10-03.

## Hardware
LCDWiki "3.5inch ESP32-S3 Display", models ES3C35P (with speaker) and ES3C35P-NS (no speaker):
- Docs, schematic, datasheets and vendor Arduino demos: https://www.lcdwiki.com/3.5inch_ESP32-S3_Display
- Example listing: https://www.amazon.com/dp/B0H28X8SQ4

## Status
- Display: working (Bruce menu renders, landscape, correct colors without the invert menu).
- Touch: working (verified 2026-10-04). Report slots: byte0 bit7=valid, bits[5:0]=x_h; coordinates are portrait-native panel pixels and the existing rotation mapping in `interface.cpp` is correct.
- SD: mounts in 4-bit SDMMC mode with a card inserted (verified 2026-10-04; the `0x107` boot error only means an empty slot). File browse/write not yet exercised.
- RGB LED: working (WS2812B, 3-channel GRB, GPIO40); Bruce's LED menu appears via `HAS_RGB_LED` + `LED_TYPE/ORDER/COUNT` in the variant header.
- Audio: working (verified 2026-10-04: `tone` and `say` serial commands audible on a connected speaker). Bruce gates sound on `HAS_NS4168_SPKR` with `BCLK/WCLK/DOUT/MCLK`; the board supplies `_setup_codec_speaker()` which drives the amp enable (GPIO1, active LOW, defaults off) and programs the ES8311 (I2C 0x18, shared bus) in I2S slave mode. Chip ID 0x8311 is printed to serial on use. Microphone path (ES8311 ADC) not implemented.
- Battery: calibrated ADC (`analogReadMilliVolts`, 16-sample average, x2 for the 100k/100k divider), smoothed, mapped through a Li-ion curve; floored at 1% so Bruce (which hides the indicator at 0%) always shows it. Verified 2026-10-04 with a direct 1000 mAh cell: it reads terminal voltage, so it is load sensitive (LED/backlight cost ~100 mV of sag on a small cell) and reads ~4.1 V (high %) while the TP4054 is charging. There is no VBUS sense or charge-status GPIO, so charging can't be detected.
- Display clock is 40 MHz (vendor uses 80 MHz); 40 MHz judged good enough.
- Pins: IR TX = IO45, IR RX = IO46, RF defaults share those two pins; all radio/SD-SPI buses (CC1101, NRF24, W5500, PN532-SPI, SDCARD) and the generic SPI pins are left unassigned (-1) so no menu can reconfigure the display bus. `RF_TX_DEFAULT_PIN`/`RF_RX_DEFAULT_PIN` (configPins.h hook) keep RF off the touch/codec I2C pair. IO45/IO46 are strapping pins: wire an IR LED to ground (idles low) and the IR receiver output on IO46. Bruce stores pins in `/brucePins.conf`; delete it after changing defaults.
- Not yet done: microphone.
- Free pins: only IO45/IO46, the shared I2C pair (SDA38/SCL39) and the UART header (43/44) are exposed. SPI radios (CC1101/NRF24/W5500, ~5 pins) do not fit; I2C and UART modules and IR do.
- Serial shell: `help` over USB serial lists Bruce's commands (`tone`, `say`, `gpio`, `settings`, `led`, ...), handy for exercising hardware without touching the UI.
- Touch debugging tip: a tiny Arduino sketch that scans I2C (touch is 0x55 on SDA38/SCL39) and prints reports from register 0x0014 finds protocol bugs much faster than debugging inside Bruce.

## Display notes (the hard-won part)
- The panel needs the draw window **x start and width aligned to 4 pixels**. Arbitrary windows (text glyphs,
icons, 1px lines) corrupt the raster: interlaced/offset smear, tinted text. Aligned solid fills hide the problem.
- `Panel_ST77922` (`lgfx_st77922.h`) therefore draws into a PSRAM framebuffer (`Panel_FrameBufferBase`), and
flushes the dirty rectangle widened to a multiple of 4 pixels in x. Rotation is done in software, the panel
stays at its native portrait MADCTL.
- `Panel_ST77922HW` is a `Panel_ST77916` subclass that only provides the QSPI plumbing and the vendor init
sequence (from the vendor Arduino `Simple_test`). It is never drawn to directly.
- Pixel format is plain RGB565 (COLMOD 0x01 or 0x55 both work). MADCTL bit 3 swaps R/B, hence `TFT_RGB_ORDER=1`.
- INVON is the panel's natural state, so `Panel_ST77922::setInvert` is flipped and the ini uses `TFT_INVERTION=0`.
- The `HOSYOND_TEST_PATTERN` define in `interface.cpp` draws a corner/cross/image test card at boot (8 s hold),
useful for checking geometry after display changes.

## Build and flash (Windows)
- Build with a fresh build cache if results look stale: `PLATFORMIO_BUILD_CACHE_DIR=<new dir>`; the repo-wide
`.pio/buildcache` once returned an old firmware.bin.
- Flash with esptool directly (`pio run -t upload` crashes on cp1252 consoles):
`python -m esptool --chip esp32s3 --port COMx --baud 921600 write-flash 0x0 Bruce-hosyond-35-s3.bin`
- Bruce persists rotation and color inversion in flash; `erase-flash` when testing display defaults.
- Run builds with PlatformIO's `Scripts` dir on PATH (`export PATH=/c/Users/<you>/.platformio/penv/Scripts:$PATH`); otherwise `patch.py`'s nested `pio pkg exec` fails.
- `patch.py` swaps in a patched `libnet80211.a` guarded by `.patched`. If another PlatformIO project refreshes the
libs package, Bruce can fail to link with `cannot find -lnet80211`; re-run the patch (objcopy
`--weaken-symbol=ieee80211_raw_frame_sanity_check libnet80211.a.old libnet80211.a`, then create `.patched`).
142 changes: 142 additions & 0 deletions boards/hosyond-35-s3/hosyond-35-s3.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#################################### Hosyond 3.5" ESP32-S3 #######################################
; Hosyond integrated 3.5" QSPI display board
; Hardware: ESP32-S3-N16R8 (16MB Flash, 8MB OPI PSRAM)
; ST77922 320x480 QSPI display (Bus_SPI quad, SPI2_HOST; see lgfx_st77922.h)
; ST77922 integrated capacitive touch (I2C @ 0x55)
; WS2812 NeoPixel (GRBW) on GPIO40
; ES8311 I2S audio codec (MCK=17, BCK=18, DOUT=15, WS=21)
; SD/MMC 4-bit (CLK=5, CMD=4, D0=6, D1=7, D2=2, D3=3)
; Battery ADC: GPIO8 (ADC1_CH7, 2× divider)

[env:hosyond-35-s3]
board = hosyond-35-s3
; Use a dedicated variant dir so interface.cpp is not compiled as FrameworkArduinoVariant
board_build.variants_dir = boards/_variants
board_build.arduino.memory_type = qio_opi
board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_build.partitions = custom_16Mb.csv
build_src_filter = ${env.build_src_filter} +<../boards/hosyond-35-s3>
build_unflags =
-DARDUINO_USB_CDC_ON_BOOT=0

build_flags =
${env.build_flags}
-Iboards/hosyond-35-s3
-Os
-DARDUINO_USB_CDC_ON_BOOT=1
-DBOARD_HAS_PSRAM
-DDISABLE_ALL_LIBRARY_WARNINGS

; ----- Display: ST77922 QSPI via LovyanGFX -----
-DUSE_LOVYANGFX=1
-DLOVYAN_PANEL=Panel_ST77922
-DLOVYAN_BUS=Bus_SPI
-DLOVYAN_QSPI_BUS=1
-DLOVYAN_CUSTOM_PANEL_HEADER='"lgfx_st77922.h"'

; LovyanGFX QSPI bus pins (SPI2_HOST)
-DTFT_SPI_HOST=SPI2_HOST
-DTFT_WRITE_FREQ=40000000
-DTFT_READ_FREQ=16000000
-DTFT_SPI_MODE=0
-DTFT_USE_LOCK=true
-DTFT_CS=10
-DTFT_SCLK=12
-DTFT_QSPI_D0=11
-DTFT_QSPI_D1=13
-DTFT_QSPI_D2=14
-DTFT_QSPI_D3=9
-DTFT_RST=-1
-DTFT_BUSY_PIN=-1

; LovyanGFX panel config
-DTFT_WIDTH=320
-DTFT_HEIGHT=480
-DTFT_MEM_WIDTH=320
-DTFT_MEM_HEIGHT=480
-DTFT_OFFSET_X=0
-DTFT_OFFSET_Y=0
-DTFT_INVERTION=0
-DTFT_RGB_ORDER=1

; Backlight PWM on GPIO41
-DBACKLIGHT=41
-DTFT_BL=41
-DMINBRIGHT=10
-DTFT_BRIGHT_FREQ=5000
-DTFT_BRIGHT_Bits=8

; ----- Touch: ST77922 integrated I2C @ 0x55 -----
-DHAS_TOUCH=1
-DHAS_CAPACITIVE_TOUCH=1
-DTOUCH_ST77922_I2C=1
-DST77922_TOUCH_ADDR=0x55
-DTOUCH_SDA=38
-DTOUCH_SCL=39
-DTOUCH_RST=48
-DTOUCH_INT=47

; ----- SD/MMC 4-bit -----
-DUSE_SD_MMC=1
-DBOARD_HAS_SDMMC=1
-DSDMMC_CLK=5
-DSDMMC_CMD=4
-DSDMMC_D0=6
-DSDMMC_D1=7
-DSDMMC_D2=2
-DSDMMC_D3=3

; ----- RGB LED -----
-DHAS_RGB_LED=1
-DRGB_LED=40

; ----- Audio: ES8311 codec (I2C 0x18) + SC8002B/FM8002E amp, I2S -----
-DHAS_NS4168_SPKR=1
-DES8311_CODEC=1
-DES8311_ADDR=0x18
-DBCLK=18
-DWCLK=21
-DDOUT=15
-DMCLK=17
-DI2S_MCLK_PIN=17
-DAMP_EN_PIN=1

; ----- Battery ADC -----
-DADC_BAT_PIN=8
-DANALOG_BAT_PIN=8

; ----- Buttons -----
-DHAS_BTN=1
-DBTN_PIN=0
-DBTN_ACT=LOW
-DBTN_ALIAS='"Ok"'

; ----- Other I/O -----
-DIR_TX_PINS='{{"IO45", 45}}'
-DIR_RX_PINS='{{"IO46", 46}}'
-DRF_TX_PINS='{{"IO45", 45}, {"IO46", 46}}'
-DRF_RX_PINS='{{"IO46", 46}, {"IO45", 45}}'
-DTXLED=45
-DRXLED=46
-DLED_ON=HIGH
-DLED_OFF=LOW

; Serial/GPS
-DSERIAL_TX=43
-DSERIAL_RX=44
-DGPS_SERIAL_TX=SERIAL_TX
-DGPS_SERIAL_RX=SERIAL_RX

-DDEVICE_NAME='"Hosyond 3.5 S3"'
-DHAS_SCREEN=1
-DROTATION=1

lib_deps =
${env.lib_deps}
https://github.com/lovyan03/LovyanGFX#develop

lib_ignore =
TFT_eSPI

################################## END Hosyond 3.5" S3 ############################################
Loading