From 4b078495a34b25dfec3cdc7ee202a83ec8445b52 Mon Sep 17 00:00:00 2001 From: ajax80 Date: Fri, 10 Jul 2026 16:57:08 -0500 Subject: [PATCH 1/2] Add Freenove FNK0104B (2.8" ILI9341 ESP32-S3) board The Freenove FNK0104B is the 2.8" ILI9341 + FT6336 capacitive-touch member of Freenove's ESP32-S3 Display family. It is the same reference design as the existing ES3C28P board (identical display and touch pins), so this board is derived from ES3C28P with three corrections for the Freenove variant: - WS2812 RGB LED on GPIO40 (ES3C28P: 42) - Battery ADC on GPIO8 (ES3C28P: 9) - TFT_RGB_ORDER = TFT_BGR (per Freenove's official TFT_eSPI setup, so red/blue are not swapped) Internal ES3C28P_* constants renamed to FNK0104AB_* and the board identity macro set accordingly. Added to platformio.ini's board list and the parallel build matrix. Tested on hardware (Freenove FNK0104B): builds, boots, 240x320 display and FT6336 capacitive touch confirmed working. RGB LED and battery ADC pins are taken from Freenove's own source, not bench-verified. Covers the 2.8" "B" variant only (the 3.5"/4.0" siblings use ST7796/ST7789). Co-Authored-By: Claude Opus 4.8 --- .github/workflows/buil_parallel.yml | 1 + .../Freenove-FNK0104AB/Freenove-FNK0104AB.ini | 51 ++ boards/Freenove-FNK0104AB/interface.cpp | 443 ++++++++++++++++++ boards/Freenove-FNK0104AB/partitions_16Mb.csv | 5 + boards/Freenove-FNK0104AB/pins_arduino.h | 221 +++++++++ boards/_boards_json/Freenove-FNK0104AB.json | 50 ++ platformio.ini | 1 + 7 files changed, 772 insertions(+) create mode 100644 boards/Freenove-FNK0104AB/Freenove-FNK0104AB.ini create mode 100644 boards/Freenove-FNK0104AB/interface.cpp create mode 100644 boards/Freenove-FNK0104AB/partitions_16Mb.csv create mode 100644 boards/Freenove-FNK0104AB/pins_arduino.h create mode 100644 boards/_boards_json/Freenove-FNK0104AB.json diff --git a/.github/workflows/buil_parallel.yml b/.github/workflows/buil_parallel.yml index 53ef146857..e023ee017e 100644 --- a/.github/workflows/buil_parallel.yml +++ b/.github/workflows/buil_parallel.yml @@ -46,6 +46,7 @@ jobs: - { env: "m5stack-cores3", family: "ESP32-S3",} - { env: "m5stack-dinmeter", family: "ESP32-S3",} - { env: "esp32-s3-devkitc-1", family: "ESP32-S3",} + - { env: "Freenove-FNK0104AB", family: "ESP32-S3",} - { env: "esp32-c5", family: "ESP32-C5",} - { env: "esp32-c5-tft", family: "ESP32-C5",} - { env: "nm-cyd-c5", family: "ESP32-C5",} diff --git a/boards/Freenove-FNK0104AB/Freenove-FNK0104AB.ini b/boards/Freenove-FNK0104AB/Freenove-FNK0104AB.ini new file mode 100644 index 0000000000..4cb9d2ff5b --- /dev/null +++ b/boards/Freenove-FNK0104AB/Freenove-FNK0104AB.ini @@ -0,0 +1,51 @@ +; PlatformIO Project Configuration File +; +; Freenove-FNK0104AB - 2.8" IPS ESP32-S3 Display Module with Capacitive Touch +; Manufacturer: Freenove (FNK0104B) — ILI9341 2.8" variant of the ES3C28P design +; +; Hardware: +; - ESP32-S3R8 (8MB OPI PSRAM, 16MB QSPI Flash) +; - ILI9341V 240x320 IPS TFT (4-wire SPI) +; - FT6336G Capacitive Touch (I2C) +; - ES8311 Audio Codec + FM8002E Amplifier +; - MicroSD Card (SDIO mode) +; - WS2812 RGB LED +; - Battery ADC with voltage divider +; - BOOT button (GPIO0) + +[env:Freenove-FNK0104AB] +board = Freenove-FNK0104AB +board_build.partitions = boards/Freenove-FNK0104AB/partitions_16Mb.csv +build_src_filter = ${env.build_src_filter} +<../boards/Freenove-FNK0104AB> +build_flags = + ${env.build_flags} + -Iboards/Freenove-FNK0104AB + -Os + -DCORE_DEBUG_LEVEL=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DDISABLE_ALL_LIBRARY_WARNINGS + + ; TFT SPI port for ESP32-S3 + -DUSE_HSPI_PORT=1 + + ; SD Card via SDIO (not SPI) + -DUSE_SD_MMC=1 + -I$PROJECT_PACKAGES_DIR/framework-arduinoespressif32-libs/esp32s3/include/fatfs/src + -I$PROJECT_PACKAGES_DIR/framework-arduinoespressif32-libs/esp32s3/include/fatfs/vfs + -I$PROJECT_PACKAGES_DIR/framework-arduinoespressif32-libs/esp32s3/include/fatfs/diskio + -I$PROJECT_PACKAGES_DIR/framework-arduinoespressif32-libs/esp32s3/include/fatfs/** + + ; Touch screen + -DHAS_TOUCH=1 + + ; Allow all GPIOs for IR/RF (user wires external modules) + -DALLOW_ALL_GPIO_FOR_IR_RF=1 + + ; Redraw delay to improve menu navigation on this display + -DREDRAW_DELAY=1 + +lib_deps = + ${env.lib_deps} + +lib_ignore = + SD diff --git a/boards/Freenove-FNK0104AB/interface.cpp b/boards/Freenove-FNK0104AB/interface.cpp new file mode 100644 index 0000000000..5501200fa9 --- /dev/null +++ b/boards/Freenove-FNK0104AB/interface.cpp @@ -0,0 +1,443 @@ +/* + * Freenove FNK0104B (2.8" ILI9341 ESP32-S3) - based on the ES3C28P design + * Board interface implementation for Bruce firmware + * + * Hardware: + * - ESP32-S3R8 (8MB OPI PSRAM, 16MB QSPI Flash) + * - ILI9341V 240x320 IPS TFT (SPI) + * - FT6336G Capacitive Touch (I2C @ 0x38) + * - ES8311 Audio Codec (I2C @ 0x18) + FM8002E Amplifier + * - MicroSD Card (SDIO mode) + * - WS2812 RGB LED (GPIO42) + * - Battery ADC (GPIO9, x2 divider) + * - BOOT button (GPIO0) + */ + +#include "core/powerSave.h" +#include "core/utils.h" +#include +#include +#include +#include + +// ============================================= +// SD Card SDIO pins (defined locally for USE_SD_MMC) +// ============================================= +#ifdef USE_SD_MMC +#define PIN_SD_CLK 38 +#define PIN_SD_CMD 40 +#define PIN_SD_D0 39 +#endif + +// ============================================= +// Touch Screen local defines +// ============================================= +#define FNK0104AB_TOUCH_SDA 16 +#define FNK0104AB_TOUCH_SCL 15 +#define FNK0104AB_TOUCH_RST 18 +#define FNK0104AB_TOUCH_INT 17 +#define FNK0104AB_TOUCH_ADDR 0x38 + +// ============================================= +// Audio & GPIO local defines +// ============================================= +#define FNK0104AB_AMP_EN 1 // FM8002E amplifier enable (active LOW) +#define FNK0104AB_BTN_PIN 0 // BOOT button +#define FNK0104AB_BTN_ACT LOW + +// ============================================= +// Touch Screen (FT6336G via I2C) +// ============================================= +// The FT6336G uses the same register protocol as CST816S/FT6236 +// Register 0x02: number of touch points +// Register 0x03-0x06: touch point 1 X/Y coordinates +#define FT6336_REG_NUM_TOUCHES 0x02 +#define FT6336_REG_TOUCH_DATA 0x03 + +static bool touchInitialized = false; + +static uint8_t ft6336_read_reg(uint8_t reg) { + Wire.beginTransmission(FNK0104AB_TOUCH_ADDR); + Wire.write(reg); + Wire.endTransmission(false); + Wire.requestFrom(FNK0104AB_TOUCH_ADDR, 1); + if (Wire.available()) return Wire.read(); + return 0; +} + +static bool ft6336_read_touch(int16_t &x, int16_t &y) { + uint8_t touches = ft6336_read_reg(FT6336_REG_NUM_TOUCHES); + if (touches == 0 || touches > 2) return false; + + uint8_t data[4]; + Wire.beginTransmission(FNK0104AB_TOUCH_ADDR); + Wire.write(FT6336_REG_TOUCH_DATA); + Wire.endTransmission(false); + Wire.requestFrom(FNK0104AB_TOUCH_ADDR, 4); + for (int i = 0; i < 4; i++) { data[i] = Wire.read(); } + + x = ((data[0] & 0x0F) << 8) | data[1]; + y = ((data[2] & 0x0F) << 8) | data[3]; + return true; +} + +/*************************************************************************************** +** Function name: _setup_gpio() +** Description: initial setup for the device +***************************************************************************************/ +void _setup_gpio() { +#ifdef USE_SD_MMC + // ---- SD Card (SDIO mode, 1-bit) ---- + SD.setPins(PIN_SD_CLK, PIN_SD_CMD, PIN_SD_D0); +#endif + + // ---- Touch Screen Init (FT6336G) ---- + // Reset touch controller + pinMode(FNK0104AB_TOUCH_RST, OUTPUT); + digitalWrite(FNK0104AB_TOUCH_RST, LOW); + delay(10); + digitalWrite(FNK0104AB_TOUCH_RST, HIGH); + delay(300); + + // Touch interrupt pin + pinMode(FNK0104AB_TOUCH_INT, INPUT); + + // Initialize I2C bus (shared with audio codec ES8311) + Wire.begin(FNK0104AB_TOUCH_SDA, FNK0104AB_TOUCH_SCL); + touchInitialized = true; + + // ---- Amplifier: disable by default (active LOW, so HIGH = disabled) ---- + pinMode(FNK0104AB_AMP_EN, OUTPUT); + digitalWrite(FNK0104AB_AMP_EN, HIGH); // FM8002E: HIGH = disabled + + // ---- Start with default module configs ---- + bruceConfigPins.rfModule = CC1101_SPI_MODULE; + bruceConfigPins.rfidModule = PN532_I2C_MODULE; + bruceConfigPins.irRx = RXLED; + bruceConfigPins.irTx = TXLED; + + Serial.begin(115200); +} + +/*************************************************************************************** +** Function name: _post_setup_gpio() +** Description: second stage gpio setup after TFT is initialized +***************************************************************************************/ +void _post_setup_gpio() { + // Backlight control via PWM (must be after TFT init) + pinMode(TFT_BL, OUTPUT); + analogWrite(TFT_BL, 255); // Full brightness initially +} + +/*************************************************************************************** +** Function name: getBattery() +** Description: Delivers the battery value from 0-100 +** Uses GPIO9 (ADC1_CH8) with x2 voltage divider +** Battery range: 2500mV (0%) to 4200mV (100%) +***************************************************************************************/ +int getBattery() { + static bool adcInitialized = false; + if (!adcInitialized) { + pinMode(ANALOG_BAT_PIN, INPUT); + analogSetAttenuation(ADC_11db); // Full range for 0-3.3V input + adcInitialized = true; + } + + // Read ADC and convert to actual battery voltage (with x2 divider) + uint32_t adcReading = analogReadMilliVolts(ANALOG_BAT_PIN); + float actualVoltage = (float)adcReading * 2.0f; // x2 voltage divider + + // Battery voltage range per ES3C28P specs: + // Min: 2500mV (cutoff/empty), Max: 4200mV (fully charged) + const float MIN_VOLTAGE = 2500.0f; + const float MAX_VOLTAGE = 4200.0f; + + int percent = (int)(((actualVoltage - MIN_VOLTAGE) / (MAX_VOLTAGE - MIN_VOLTAGE)) * 100.0f); + + if (percent < 0) percent = 0; + if (percent > 100) percent = 100; + + return percent; +} + +/********************************************************************* +** Function: setBrightness +** set brightness value (0-100) +**********************************************************************/ +void _setBrightness(uint8_t brightval) { + if (brightval == 0) { + analogWrite(TFT_BL, 0); + } else { + int bl = MINBRIGHT + round(((255 - MINBRIGHT) * brightval / 100)); + analogWrite(TFT_BL, bl); + } +} + +/********************************************************************* +** Function: InputHandler +** Handles touch and button inputs +** Maps to PrevPress, NextPress, SelPress, AnyKeyPress, EscPress +**********************************************************************/ +void InputHandler(void) { + static long tm = 0; + + if (millis() - tm > 200 || LongPress) { + // ---- Touch Screen Input ---- + if (touchInitialized) { + int16_t raw_x, raw_y; + if (ft6336_read_touch(raw_x, raw_y)) { + tm = millis(); + + // Apply rotation transformation + int16_t t_x = raw_x; + int16_t t_y = raw_y; + + if (bruceConfigPins.rotation == 1) { + // Landscape: swap and mirror + t_x = raw_y; + t_y = (TFT_WIDTH - 1) - raw_x; + } else if (bruceConfigPins.rotation == 2) { + // Portrait inverted + t_x = (TFT_WIDTH - 1) - raw_x; + t_y = (TFT_HEIGHT - 1) - raw_y; + } else if (bruceConfigPins.rotation == 3) { + // Landscape inverted + t_x = (TFT_HEIGHT - 1) - raw_y; + t_y = raw_x; + } + // rotation == 0: portrait default, no transform needed + + if (!wakeUpScreen()) AnyKeyPress = true; + else return; + + // Set global touch point + touchPoint.x = t_x; + touchPoint.y = t_y; + touchPoint.pressed = true; + touchHeatMap(touchPoint); + } + } + + // ---- BOOT Button Input ---- + if (digitalRead(FNK0104AB_BTN_PIN) == FNK0104AB_BTN_ACT) { + if (!wakeUpScreen()) { + AnyKeyPress = true; + SelPress = true; + } + // Debounce + while (digitalRead(FNK0104AB_BTN_PIN) == FNK0104AB_BTN_ACT) delay(10); + } + } +} + +/********************************************************************* +** Function: powerOff +** Turns off the device (deep sleep with BOOT button wakeup) +**********************************************************************/ +void powerOff() { + // Turn off backlight + analogWrite(TFT_BL, 0); + // Turn off amplifier + digitalWrite(FNK0104AB_AMP_EN, HIGH); + // Send display to sleep + tft.writecommand(0x10); // SLPIN + // Wake on BOOT button press + esp_sleep_enable_ext0_wakeup((gpio_num_t)FNK0104AB_BTN_PIN, FNK0104AB_BTN_ACT); + esp_deep_sleep_start(); +} + +/********************************************************************* +** Function: goToDeepSleep +** Puts the device into DeepSleep +**********************************************************************/ +void goToDeepSleep() { powerOff(); } + +/********************************************************************* +** Function: checkReboot +** Button logic to turn off the device +**********************************************************************/ +void checkReboot() { + // Long press BOOT button to power off + int c = 0; + while (digitalRead(FNK0104AB_BTN_PIN) == FNK0104AB_BTN_ACT) { + delay(100); + c++; + if (c > 20) { // 2 second long press + powerOff(); + } + } +} + +/*************************************************************************************** +** Function name: isCharging() +** Description: Determines if the device is charging (not available on ES3C28P) +***************************************************************************************/ +bool isCharging() { return false; } + +// ============================================= +// ES8311 Audio Codec Setup (via I2C register writes) +// ============================================= +#ifdef ES8311_CODEC +#include "core/utils.h" + +// ES8311 Register Addresses +#define ES8311_REG00_RESET 0x00 +#define ES8311_REG01_CLK_MGR 0x01 +#define ES8311_REG02_CLK_MGR 0x02 +#define ES8311_REG03_CLK_MGR 0x03 +#define ES8311_REG04_CLK_MGR 0x04 +#define ES8311_REG05_CLK_MGR 0x05 +#define ES8311_REG06_CLK_MGR 0x06 +#define ES8311_REG07_CLK_MGR 0x07 +#define ES8311_REG08_CLK_MGR 0x08 +#define ES8311_REG09_SDPIN 0x09 +#define ES8311_REG0A_SDPOUT 0x0A +#define ES8311_REG0B_SYSTEM 0x0B +#define ES8311_REG0C_SYSTEM 0x0C +#define ES8311_REG0D_SYSTEM 0x0D +#define ES8311_REG0E_SYSTEM 0x0E +#define ES8311_REG0F_SYSTEM 0x0F +#define ES8311_REG10_SYSTEM 0x10 +#define ES8311_REG11_SYSTEM 0x11 +#define ES8311_REG12_SYSTEM 0x12 +#define ES8311_REG13_SYSTEM 0x13 +#define ES8311_REG14_ADC 0x14 +#define ES8311_REG15_ADC 0x15 +#define ES8311_REG16_ADC 0x16 +#define ES8311_REG17_ADC 0x17 +#define ES8311_REG18_ADC 0x18 +#define ES8311_REG19_ADC 0x19 +#define ES8311_REG1A_ADC 0x1A +#define ES8311_REG1B_ADC 0x1B +#define ES8311_REG1C_ADC 0x1C +#define ES8311_REG32_DAC 0x32 +#define ES8311_REG37_DAC 0x37 +#define ES8311_REG44_GPIO 0x44 +#define ES8311_REG45_GPIO 0x45 + +static void es8311_write_reg(uint8_t reg, uint8_t val) { + Wire.beginTransmission(ES8311_ADDR); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +static uint8_t es8311_read_reg(uint8_t reg) { + Wire.beginTransmission(ES8311_ADDR); + Wire.write(reg); + Wire.endTransmission(false); + Wire.requestFrom((uint8_t)ES8311_ADDR, (uint8_t)1); + if (Wire.available()) return Wire.read(); + return 0; +} + +/********************************************************************* +** Function: _setup_codec_speaker +** Handles audio CODEC to enable/disable speaker +** Configures ES8311 for I2S slave mode, 16-bit, with MCLK from ESP32 +**********************************************************************/ +void _setup_codec_speaker(bool enable) { + // Enable/disable amplifier (FM8002E is active LOW) + digitalWrite(FNK0104AB_AMP_EN, enable ? LOW : HIGH); + + if (enable) { + // Reset codec (exact sequence from LCD Wiki es8311_init) + es8311_write_reg(ES8311_REG00_RESET, 0x1F); + delay(20); + es8311_write_reg(ES8311_REG00_RESET, 0x00); // Clear reset + es8311_write_reg(ES8311_REG00_RESET, 0x80); // Power on + + // Clock config: MCLK from MCLK pin, enable all clocks + es8311_write_reg(ES8311_REG01_CLK_MGR, 0x3F); + + // Clock dividers for 44100Hz sample rate with MCLK from I2S master + // The ESP8266Audio library generates MCLK automatically + // Use coefficients matching the I2S MCLK output + // pre_div=1, pre_multi=0, adc_div=1, dac_div=1, fs=0, lrck=0x00FF, bclk_div=4 + es8311_write_reg(ES8311_REG02_CLK_MGR, 0x00); // pre_div=1, pre_multi=1x + es8311_write_reg(ES8311_REG03_CLK_MGR, 0x10); // ADC OSR = 0x10 + es8311_write_reg(ES8311_REG04_CLK_MGR, 0x10); // DAC OSR = 0x10 + es8311_write_reg(ES8311_REG05_CLK_MGR, 0x00); // ADC/DAC clk div = 1 + es8311_write_reg(ES8311_REG06_CLK_MGR, 0x03); // BCLK divider + es8311_write_reg(ES8311_REG07_CLK_MGR, 0x00); // LRCK divider high + es8311_write_reg(ES8311_REG08_CLK_MGR, 0xFF); // LRCK divider low + + // SDP format: slave mode, 16-bit I2S (bits[3:2] = 11 = 16-bit) + es8311_write_reg(ES8311_REG09_SDPIN, 0x0C); // SDP in: 16-bit + es8311_write_reg(ES8311_REG0A_SDPOUT, 0x0C); // SDP out: 16-bit + + // Power up analog and DAC (from LCD Wiki es8311_init) + es8311_write_reg(ES8311_REG0D_SYSTEM, 0x01); // Power up analog circuitry + es8311_write_reg(ES8311_REG0E_SYSTEM, 0x02); // Enable analog PGA, ADC modulator + es8311_write_reg(ES8311_REG12_SYSTEM, 0x00); // Power up DAC + es8311_write_reg(ES8311_REG13_SYSTEM, 0x10); // Enable output to HP drive + es8311_write_reg(ES8311_REG1C_ADC, 0x6A); // ADC EQ bypass, cancel DC offset + es8311_write_reg(ES8311_REG37_DAC, 0x08); // Bypass DAC equalizer + + // Set volume (85% like LCD Wiki example) + es8311_write_reg(ES8311_REG32_DAC, 0xD8); // ~85% volume + + Serial.println("ES8311 speaker enabled"); + } else { + // Power down codec + es8311_write_reg(ES8311_REG0D_SYSTEM, 0xFC); // Power down analog + es8311_write_reg(ES8311_REG00_RESET, 0x00); // Power down + Serial.println("ES8311 speaker disabled"); + } +} + +/********************************************************************* +** Function: _setup_codec_mic +** Handles audio CODEC to enable/disable microphone +**********************************************************************/ +void _setup_codec_mic(bool enable) { + // Set microphone I2S pin for Bruce's mic module + extern gpio_num_t mic_bclk_pin; + mic_bclk_pin = (gpio_num_t)BCLK; + + if (enable) { + // Reset codec (exact sequence from LCD Wiki) + es8311_write_reg(ES8311_REG00_RESET, 0x1F); + delay(20); + es8311_write_reg(ES8311_REG00_RESET, 0x00); + es8311_write_reg(ES8311_REG00_RESET, 0x80); + + // Clock config: MCLK derived from BCLK (bit7=1) since Bruce's mic + // I2S driver doesn't output MCLK on a GPIO pin. + // BCLK = 48000 * 16 * 2 = 1,536,000 Hz + // pre_multi=8x (0x03) to get internal 12.288 MHz from 1.536 MHz BCLK + // Matches coefficient table: {1536000, 48000, 0x01, 0x03, ...} + es8311_write_reg(ES8311_REG01_CLK_MGR, 0xBF); // bit7=1: MCLK from BCLK + es8311_write_reg(ES8311_REG02_CLK_MGR, 0x18); // pre_div=1, pre_multi=8x + es8311_write_reg(ES8311_REG03_CLK_MGR, 0x10); // ADC OSR + es8311_write_reg(ES8311_REG04_CLK_MGR, 0x10); // DAC OSR + es8311_write_reg(ES8311_REG05_CLK_MGR, 0x00); // ADC/DAC clk div = 1 + es8311_write_reg(ES8311_REG06_CLK_MGR, 0x03); // BCLK divider + es8311_write_reg(ES8311_REG07_CLK_MGR, 0x00); // LRCK divider high + es8311_write_reg(ES8311_REG08_CLK_MGR, 0xFF); // LRCK divider low + + // I2S format: 16-bit + es8311_write_reg(ES8311_REG09_SDPIN, 0x0C); + es8311_write_reg(ES8311_REG0A_SDPOUT, 0x0C); + + // Power up analog and enable ADC + es8311_write_reg(ES8311_REG0D_SYSTEM, 0x01); // Power up analog circuitry + es8311_write_reg(ES8311_REG0E_SYSTEM, 0x02); // Enable analog PGA, ADC modulator + es8311_write_reg(ES8311_REG12_SYSTEM, 0x00); // Power up DAC too + es8311_write_reg(ES8311_REG13_SYSTEM, 0x10); // Enable HP driver + + // Microphone config (from LCD Wiki es8311_microphone_config) + es8311_write_reg(ES8311_REG14_ADC, 0x1A); // Enable analog MIC, max PGA gain + es8311_write_reg(ES8311_REG17_ADC, 0xC8); // ADC gain + es8311_write_reg(ES8311_REG1C_ADC, 0x6A); // ADC EQ bypass, DC offset cancel + + Serial.println("ES8311 microphone enabled"); + } else { + es8311_write_reg(ES8311_REG0D_SYSTEM, 0xFC); + es8311_write_reg(ES8311_REG0E_SYSTEM, 0x6A); + es8311_write_reg(ES8311_REG00_RESET, 0x00); + Serial.println("ES8311 microphone disabled"); + } +} + +#endif // ES8311_CODEC diff --git a/boards/Freenove-FNK0104AB/partitions_16Mb.csv b/boards/Freenove-FNK0104AB/partitions_16Mb.csv new file mode 100644 index 0000000000..78b60900b8 --- /dev/null +++ b/boards/Freenove-FNK0104AB/partitions_16Mb.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +app0, app, factory, 0x10000, 0x470000, +spiffs, data, spiffs, 0x480000, 0xB70000, +coredump, data, coredump, 0xFF0000, 0x10000, diff --git a/boards/Freenove-FNK0104AB/pins_arduino.h b/boards/Freenove-FNK0104AB/pins_arduino.h new file mode 100644 index 0000000000..3b39673e42 --- /dev/null +++ b/boards/Freenove-FNK0104AB/pins_arduino.h @@ -0,0 +1,221 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "soc/soc_caps.h" +#include + +#ifndef DEVICE_NAME +#define DEVICE_NAME "Freenove-FNK0104AB" +#endif + +// ============================================= +// USB +// ============================================= +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// ============================================= +// UART0 +// ============================================= +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +// ============================================= +// I2C Bus (shared between touch, audio codec, expansion) +// Touch FT6336G @ 0x38, Audio ES8311 @ 0x18 +// ============================================= +#define GROVE_SDA 16 +#define GROVE_SCL 15 +static const uint8_t SDA = GROVE_SDA; +static const uint8_t SCL = GROVE_SCL; + +// ============================================= +// Main SPI Bus (exposed on expansion pins) +// Used for CC1101, NRF24, W5500 external modules +// ============================================= +#define SPI_SCK_PIN 14 +#define SPI_MOSI_PIN 3 +#define SPI_MISO_PIN 2 +#define SPI_SS_PIN 21 + +static const uint8_t SS = SPI_SS_PIN; +static const uint8_t MOSI = SPI_MOSI_PIN; +static const uint8_t SCK = SPI_SCK_PIN; +static const uint8_t MISO = SPI_MISO_PIN; + +// ============================================= +// SD Card - Uses SDIO mode (not SPI) +// SD_CLK=38, SD_CMD=40, SD_D0=39, SD_D1=41, SD_D2=48, SD_D3=47 +// ============================================= +#define SDCARD_CS -1 +#define SDCARD_SCK -1 +#define SDCARD_MISO -1 +#define SDCARD_MOSI -1 + +// ============================================= +// CC1101 Sub-GHz Radio (external module via expansion pins) +// ============================================= +#define USE_CC1101_VIA_SPI +#define CC1101_GDO0_PIN 2 +#define CC1101_SS_PIN 21 +#define CC1101_MOSI_PIN SPI_MOSI_PIN +#define CC1101_SCK_PIN SPI_SCK_PIN +#define CC1101_MISO_PIN SPI_MISO_PIN + +// ============================================= +// NRF24L01 2.4GHz Radio (external module via expansion pins) +// ============================================= +#define USE_NRF24_VIA_SPI +#define NRF24_CE_PIN 3 +#define NRF24_SS_PIN 21 +#define NRF24_MOSI_PIN SPI_MOSI_PIN +#define NRF24_SCK_PIN SPI_SCK_PIN +#define NRF24_MISO_PIN SPI_MISO_PIN + +// ============================================= +// W5500 Ethernet (external module via expansion pins) +// ============================================= +#define USE_W5500_VIA_SPI +#define W5500_SS_PIN -1 +#define W5500_MOSI_PIN SPI_MOSI_PIN +#define W5500_SCK_PIN SPI_SCK_PIN +#define W5500_MISO_PIN SPI_MISO_PIN +#define W5500_INT_PIN -1 + +// ============================================= +// TFT Display (ILI9341V via SPI) +// ============================================= +#define USER_SETUP_LOADED +#define ILI9341_2_DRIVER 1 +#define TFT_INVERSION_ON 1 // Fix inverted colors +#define TFT_RGB_ORDER TFT_BGR // Freenove FNK0104B panel is BGR (per Freenove's official TFT_eSPI setup) +#define TFT_WIDTH 240 +#define TFT_HEIGHT 320 +#define TFT_MISO 13 +#define TFT_MOSI 11 +#define TFT_SCLK 12 +#define TFT_CS 10 +#define TFT_DC 46 +#define TFT_RST -1 // Connected to CHIP_PU (shared with board reset) +#define TFT_BL 45 // Backlight control (HIGH = on) +#define TFT_BACKLIGHT_ON HIGH +#define SMOOTH_FONT 1 +#define TOUCH_CS -1 // No SPI touch, using I2C capacitive touch +#define SPI_FREQUENCY 40000000 +#define SPI_READ_FREQUENCY 20000000 + +// ============================================= +// Display Setup +// ============================================= +#define HAS_SCREEN 1 +#define ROTATION 1 // Landscape mode (320x240) +#define MINBRIGHT 1 +#define BACKLIGHT 45 + +// ============================================= +// Touch Screen (FT6336G via I2C @ 0x38) +// ============================================= +#define HAS_TOUCH 1 +#define HAS_CAPACITIVE_TOUCH 1 +#define TOUCH_FT6336_I2C 1 +#define FT6336_I2C_ADDR 0x38 +#define FT6336_I2C_CONFIG_SDA 16 +#define FT6336_I2C_CONFIG_SCL 15 +#define FT6336_TOUCH_CONFIG_RST 18 +#define FT6336_TOUCH_CONFIG_INT 17 + +// ============================================= +// Font Sizes +// ============================================= +#define FP 1 +#define FM 2 +#define FG 3 + +// ============================================= +// RGB LED (WS2812 NeoPixel) +// ============================================= +#define HAS_RGB_LED 1 +#define RGB_LED 40 // Freenove FNK0104B WS2812 (ES3C28P uses 42) +#define LED_TYPE WS2812B +#define LED_ORDER GRB +#define LED_TYPE_IS_RGBW 0 +#define LED_COUNT 1 +#define LED_COLOR_STEP 5 + +// ============================================= +// Buttons +// ============================================= +#define HAS_BTN 1 +#define BTN_ALIAS "\"Boot\"" +#define BTN_PIN 0 // BOOT button +#define BTN_ACT LOW +#define SEL_BTN 0 // BOOT button used as Select + +// ============================================= +// Audio System (ES8311 codec + I2S + FM8002E amplifier) +// ES8311 I2C: SDA=16, SCL=15 (shared with touch) +// I2S: MCLK=4, BCLK=5, LRC=7, DOUT=8, DIN=6 +// Per LCD Wiki example: GPIO8=DOUT (ESP32→ES8311), GPIO6=DIN (ES8311→ESP32) +// ============================================= +#define HAS_NS4168_SPKR 1 // Compatible I2S speaker interface +#define ES8311_CODEC 1 +#define ES8311_ADDR 0x18 +#define I2S_MCLK_PIN 4 // Master clock +#define BCLK 5 // Bit clock +#define WCLK 7 // Word clock (LRC) +#define DOUT 8 // Data out (ESP32 → ES8311 SDIN for playback) +#define MCLK 4 // Alias for I2S_MCLK_PIN +#define AMP_EN_PIN 1 // FM8002E amplifier enable (active LOW) + +// Microphone (via ES8311 ADC) +#define MIC_SPM1423 1 +#define PIN_CLK 7 // I2S WS/LRC for mic (used as ws pin in std I2S mode) +#define I2S_SCLK_PIN 5 +#define I2S_DATA_PIN 6 // I2S DIN (ES8311 SDOUT → ESP32 for recording) +#define PIN_DATA 6 + +// ============================================= +// Battery ADC +// ============================================= +#define ANALOG_BAT_PIN 8 // Freenove FNK0104B battery ADC (ES3C28P uses 9) +#define ANALOG_BAT_MULTIPLIER 2.0f // Voltage divider: multiply ADC by 2 + +// ============================================= +// Infrared (external, via expansion pins) +// ============================================= +#define TXLED 2 // IR TX default (expansion GPIO2) +#define RXLED 3 // IR RX default (expansion GPIO3) +#define LED_ON HIGH +#define LED_OFF LOW + +#define IR_TX_PINS '{{"GPIO2", 2}, {"GPIO3", 3}, {"GPIO14", 14}, {"GPIO21", 21}}' +#define IR_RX_PINS '{{"GPIO2", 2}, {"GPIO3", 3}, {"GPIO14", 14}, {"GPIO21", 21}}' + +// ============================================= +// RF (external, via expansion pins) +// ============================================= +#define RF_TX_PINS '{{"GPIO2", 2}, {"GPIO3", 3}, {"GPIO14", 14}, {"GPIO21", 21}}' +#define RF_RX_PINS '{{"GPIO2", 2}, {"GPIO3", 3}, {"GPIO14", 14}, {"GPIO21", 21}}' + +// ============================================= +// Serial (GPS) dedicated pins +// ============================================= +#define SERIAL_TX 43 +#define SERIAL_RX 44 +#define GPS_SERIAL_TX SERIAL_TX +#define GPS_SERIAL_RX SERIAL_RX + +// ============================================= +// BadUSB (USB HID) +// ============================================= +#define USB_as_HID 1 +#define BAD_TX GROVE_SDA +#define BAD_RX GROVE_SCL + +// ============================================= +// Deep Sleep +// ============================================= +#define DEEPSLEEP_WAKEUP_PIN 0 // BOOT button +#define DEEPSLEEP_PIN_ACT LOW + +#endif /* Pins_Arduino_h */ diff --git a/boards/_boards_json/Freenove-FNK0104AB.json b/boards/_boards_json/Freenove-FNK0104AB.json new file mode 100644 index 0000000000..eb04b49d94 --- /dev/null +++ b/boards/_boards_json/Freenove-FNK0104AB.json @@ -0,0 +1,50 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_16MB.csv", + "memory_type": "dio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DFREENOVE_FNK0104AB", + "-DBOARD_HAS_PSRAM", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1", + "-DARDUINO_USB_CDC_ON_BOOT", + "-DARDUINO_USB_MODE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "hwids": [ + [ + "0X303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "pinouts" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "openocd_target": "esp32s3-builtin.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Freenove FNK0104B 2.8\" ESP32-S3 Display", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.lcdwiki.com/2.8inch_ESP32-S3_Display", + "vendor": "ShenZhen QDtech (LCDWIKI)" +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index fa639618e2..9c45916211 100644 --- a/platformio.ini +++ b/platformio.ini @@ -68,6 +68,7 @@ default_envs = ;esp32-c5-tft ;esp32-c5 ;ES3C28P + ;Freenove-FNK0104AB ;nm-cyd-c5 ;elecrow-24B ;elecrow-28B From fccf2c9d7882aa9f870f3cb2a8e4a01dc94574b3 Mon Sep 17 00:00:00 2001 From: ajax80 Date: Fri, 10 Jul 2026 17:39:56 -0500 Subject: [PATCH 2/2] Add connections.md wiring guide for Freenove FNK0104B Documents the board's broken-out headers (Expanded IO GPIO 2/3/14/21, UART 43/44, IIC 16/15), on-board peripherals, and ready-to-use CC1101 / nRF24 wiring on the shared SPI bus (including the GDO0-vs-MISO pin collision fix and the 6-free-GPIO budget for running both radios). Co-Authored-By: Claude Opus 4.8 --- boards/Freenove-FNK0104AB/connections.md | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 boards/Freenove-FNK0104AB/connections.md diff --git a/boards/Freenove-FNK0104AB/connections.md b/boards/Freenove-FNK0104AB/connections.md new file mode 100644 index 0000000000..cf46825686 --- /dev/null +++ b/boards/Freenove-FNK0104AB/connections.md @@ -0,0 +1,49 @@ +# Freenove FNK0104B — Bruce wiring / pinouts + +Freenove FNK0104B (2.8" ILI9341 ESP32-S3, FT6336 capacitive touch) — the same +hardware design as the ES3C28P. Display, touch, SD card, RGB LED and the BOOT +button are on-board and already configured; you only wire the optional add-on +radios/modules. + +## On-board (already configured — for reference) + +| Function | Pins | +| --- | --- | +| Display (ILI9341, SPI) | SCLK 12, MOSI 11, MISO 13, CS 10, DC 46, RST board-reset, BL 45 | +| Touch (FT6336, I2C @0x38) | SDA 16, SCL 15, INT 17, RST 18 | +| SD card | on-board slot (SDMMC) — just insert a microSD | +| RGB LED (WS2812) | 40 | +| Button (Select) | 0 (BOOT) | +| Battery ADC | 8 (x2 divider) | + +## Free GPIOs (broken-out headers) + +| Header | Free GPIOs | Notes | +| --- | --- | --- | +| Expanded IO | 2, 3, 14, 21 | + 3V3 + GND | +| UART | 43 (TX0), 44 (RX0) | free if the serial console is unused | +| IIC | 16 (SDA), 15 (SCL) | shared touch/audio bus — for I2C add-ons | + +Power modules from the header **3V3** pin — CC1101 and nRF24 are 3.3V, not 5V-tolerant. + +## Sub-GHz / 2.4 GHz radios (SPI, shared bus) + +| Device | SCK | MOSI | MISO | CS | GDO0/CE | +| --- | :---: | :---: | :---: | :---: | :---: | +| CC1101 | 14 | 3 | 2 | 21 | 43 (GDO0) | +| nRF24 | 14 | 3 | 2 | 44 | 3V3 * (CE) | + +- CC1101 and nRF24 share SCK/MOSI/MISO. Set pins in Bruce → Settings → Hardware/Pins. +- **CC1101 GDO0** default collides with MISO (GPIO 2) — set GDO0 to **43** in the UI. +- Both radios at once need 7 GPIOs but only 6 are free (4 Expanded IO + 2 UART). + Either tie nRF24 **CE to 3V3** ( * ) so Bruce drives it over SPI only, or use one + radio at a time and swap CS in the UI. +- nRF24: add a decoupling cap across the module VCC/GND (10 uF; 100 uF for PA+LNA). + +## I2C add-ons (no extra pins) + +PN532 (NFC), DS3231 (RTC) and other I2C devices join the IIC header +(SDA 16 / SCL 15) at their own addresses. + +> Match wiring by **GPIO number to the header silkscreen** (IO2/IO3/IO14/IO21, +> TX/RX), not by connector position.