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/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. 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