MeshPager X2 initial commit

This commit is contained in:
Manuel committed 2026-07-27 13:55:41 +02:00
1 parent 1e982fa78c
commit 0730aaa861
7 files changed
+596 -1

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
{
"build": {
"arduino": {
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0X303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3box"
},
"connectivity": [
"wifi",
"bluetooth"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "SenseCAP MeshPager X2 (16 MB FLASH, 8 MB PSRAM)",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"url": "https://www.seeedstudio.com",
"vendor": "Seeed Studio"
}
+2 -1
View File
@@ -1007,7 +1007,8 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \
defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT) || defined(RAK_WISMESH_TAP_V2)) && \
defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT) || defined(RAK_WISMESH_TAP_V2) || \
defined(SEEED_MESHPAGER_X2)) && \
HAS_TFT
// switch BT off by default; use TFT programming mode or hotkey to enable
config.bluetooth.enabled = false;
@@ -0,0 +1,34 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t TX = 43;
static const uint8_t RX = 44;
static const uint8_t SDA = 17;
static const uint8_t SCL = 18;
static const uint8_t SS = -1;
static const uint8_t MOSI = 14;
static const uint8_t MISO = 16;
static const uint8_t SCK = 15;
static const uint8_t A8 = 9;
static const uint8_t A9 = 10;
static const uint8_t A10 = 11;
static const uint8_t A11 = 12;
static const uint8_t A12 = 13;
static const uint8_t A13 = 14;
static const uint8_t T9 = 9;
static const uint8_t T10 = 10;
static const uint8_t T11 = 11;
static const uint8_t T12 = 12;
static const uint8_t T13 = 13;
static const uint8_t T14 = 14;
#endif /* Pins_Arduino_h */
@@ -0,0 +1,105 @@
[env:seeed_meshpager_x2]
custom_meshtastic_hw_model = 100 ; TODO: assign a unique number for this hardware model
custom_meshtastic_hw_model_slug = SEEED_MESHPAGER_X2
custom_meshtastic_architecture = esp32s3
custom_meshtastic_actively_supported = true
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = Seeed MeshPager X2
custom_meshtastic_images = meshpager_x2.svg
custom_meshtastic_tags = Seeed
custom_meshtastic_requires_dfu = true
custom_meshtastic_partition_scheme = 16MB
board = seeed_meshpager_x2
board_level = pr
extends = esp32s3_base
board_check = true
upload_protocol = esptool
board_build.partitions = default_16MB.csv
; Build flags for MeshPager X2
build_flags = ${esp32s3_base.build_flags}
-I variants/esp32s3/seeed_mesh_pager_x2
-D SEEED_MESHPAGER_X2
-D CONFIG_ARDUHAL_ESP_LOG
-D CONFIG_ARDUHAL_LOG_COLORS=1
-D CONFIG_DISABLE_HAL_LOCKS=1
-D MESHTASTIC_EXCLUDE_WEBSERVER=1
-D LV_LVGL_H_INCLUDE_SIMPLE
-D LV_CONF_INCLUDE_SIMPLE
-D LV_COMP_CONF_INCLUDE_SIMPLE
-D INPUTDRIVER_ENCODER_TYPE=4
-D INPUTDRIVER_ENCODER_UP=0x40
-D INPUTDRIVER_ENCODER_DOWN=0x41
-D INPUTDRIVER_ENCODER_LEFT=0x42
-D INPUTDRIVER_ENCODER_RIGHT=0x43
-D INPUTDRIVER_ENCODER_ENT=0x44
-D INPUTDRIVER_ENCODER_ESC=0x45
-D INPUTDRIVER_BUTTON_TYPE=9
-D LV_USE_LOG=0
-D LV_BUILD_TEST=0
-D USE_LOG_DEBUG
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
-D RADIOLIB_SPI_PARANOID=0
; Display Configuration
-D HAS_SCREEN=1
-D HAS_TFT=1
-D USE_I2S_BUZZER
; SD Card - 1-bit SDIO mode
-D HAS_SDCARD
-D HAS_SD_MMC
-D SD_SCLK_PIN=15
-D SD_MOSI_PIN=14
-D SD_MISO_PIN=16
-D SDCARD_CS=-1 ; control via IO Expander pin 13
-D SD_SPI_FREQUENCY=40000000
; Memory Configuration
-D RAM_SIZE=5120
-D LGFX_BUFSIZE=153600
-D LV_CACHE_DEF_SIZE=2097152
; Graphics/UI Configuration
-D LGFX_DRIVER_TEMPLATE
-D DISPLAY_SIZE=320x240
-D LGFX_DRIVER=LGFX_GENERIC
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_GENERIC.h\"
-D LGFX_PANEL=ST7789P3
-D LGFX_SCREEN_WIDTH=240
-D LGFX_SCREEN_HEIGHT=320
-D LGFX_INVERT_COLOR=true
-D LGFX_RGB_ORDER=true
-D LGFX_CFG_HOST=SPI2_HOST
-D LGFX_PIN_SCK=12
-D LGFX_PIN_MOSI=11
-D LGFX_PIN_MISO=-1
-D LGFX_PIN_DC=13
-D LGFX_PIN_CS=10
-D LGFX_PIN_RST=-1
-D LGFX_PIN_BL=46
-D LGFX_ROTATION=1
-D SPI_FREQUENCY=80000000
-D VIEW_320x240
-D MAP_FULL_REDRAW
-D USE_PACKET_API
build_src_filter = ${esp32s3_base.build_src_filter}
+<../variants/esp32s3/seeed_mesh_pager_x2>
lib_deps =
${esp32s3_base.lib_deps}
${device-ui_base.lib_deps}
; Audio processing and synthesis
# renovate: datasource=git-refs depName=ESP8266Audio packageName=https://github.com/meshtastic/ESP8266Audio gitBranch=meshtastic-2.0.0-dacfix
https://github.com/meshtastic/ESP8266Audio/archive/343024632ee78d6216907b2353fc943a62422d80.zip
# renovate: datasource=pio depName=ESP8266SAM packageName=earlephilhower/library/ESP8266SAM
earlephilhower/ESP8266SAM@1.1.0
# renovate: datasource=github-tags depName=pschatzmann_arduino-audio-driver packageName=pschatzmann/arduino-audio-driver
https://github.com/pschatzmann/arduino-audio-driver/archive/v0.2.1.zip
; IO Expander (TCA6424)
# renovate: datasource=pio depName=TCA9555 packageName=RobTillaart/library/TCA9555
RobTillaart/TCA9555@0.4.4
; Display driver (ST7789)
# renovate: datasource=pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@1.2.26
; Sensors: ADS1115 (if used), BMM350, SHT4x, LSM6DSO, etc.
# renovate: datasource=github-tags depName=Adafruit_ADS1X15 packageName=adafruit/Adafruit_ADS1X15
https://github.com/adafruit/Adafruit_ADS1X15/archive/refs/tags/2.6.2.zip
@@ -0,0 +1,24 @@
#include "RadioLib.h"
// MeshPager X2 RF switch matrix for the Semtech LR2021 chip (Wio-LR2021 module).
// Derived from SenseCAP BSP components/lr20xx/src/ral_lr20xx_bsp.c:
// DIO5: active during RX_LF and TX_HF
// DIO6: active during all RF states (RX_LF, TX_LF, RX_HF, TX_HF)
static const uint32_t lr20x0_rfswitch_dio_pins[] = {
RADIOLIB_LR2021_DIO5,
RADIOLIB_LR2021_DIO6,
RADIOLIB_NC,
RADIOLIB_NC,
RADIOLIB_NC,
};
static const Module::RfSwitchMode_t lr20x0_rfswitch_table[] = {
// mode DIO5 DIO6
{LR2021::MODE_STBY, {LOW, LOW}}, // standby: both off
{LR2021::MODE_RX, {HIGH, HIGH}}, // RX sub-GHz (LF): DIO5 on, DIO6 on
{LR2021::MODE_TX, {LOW, HIGH}}, // TX sub-GHz (LF): DIO5 off, DIO6 on
{LR2021::MODE_RX_HF, {LOW, HIGH}}, // RX 2.4 GHz (HF): DIO5 off, DIO6 on
{LR2021::MODE_TX_HF, {HIGH, HIGH}}, // TX 2.4 GHz (HF): DIO5 on, DIO6 on
END_OF_MODE_TABLE,
};
@@ -0,0 +1,225 @@
#include "variant.h"
#include "Arduino.h"
#include <Wire.h>
#include "AudioBoard.h"
namespace {
// TCA6424 register map
constexpr uint8_t TCA6424_INPUT_PORT0 = 0x00;
constexpr uint8_t TCA6424_OUTPUT_PORT0 = 0x04;
constexpr uint8_t TCA6424_CONFIG_PORT0 = 0x0C;
constexpr uint8_t EXP_PIN_BUTTON_UP = 0;
constexpr uint8_t EXP_PIN_BUTTON_DOWN = 1;
constexpr uint8_t EXP_PIN_BUTTON_LEFT = 2;
constexpr uint8_t EXP_PIN_BUTTON_RIGHT = 3;
constexpr uint8_t EXP_PIN_BUTTON_CONFIRM = 4;
constexpr uint8_t EXP_PIN_BUTTON_RETURN = 5;
constexpr uint8_t EXP_PIN_BUTTON_PWR = 6;
constexpr uint8_t EXP_PIN_SD_DETECT = 7;
constexpr uint8_t EXP_PIN_GNSS_RTC_INT = 8;
constexpr uint8_t EXP_PIN_GNSS_SLEEP_INT = 9;
constexpr uint8_t EXP_PIN_IMU_INT1 = 10;
constexpr uint8_t EXP_PIN_SEN_EN = 12;
constexpr uint8_t EXP_PIN_SD_PWR_EN = 13;
constexpr uint8_t EXP_PIN_LCD_PWR_EN = 14;
constexpr uint8_t EXP_PIN_GNSS_VRTC_EN = 15;
constexpr uint8_t EXP_PIN_GNSS_PWR_EN = 16;
constexpr uint8_t EXP_PIN_BAT_ADC_EN = 17;
constexpr uint8_t EXP_PIN_PA_PWR_EN = 18;
constexpr uint8_t EXP_PIN_PWR_HOLD = 20;
constexpr uint8_t EXP_PIN_LCD_RST = 21;
constexpr uint8_t EXP_PIN_LORA_RESET = 22;
constexpr uint8_t EXP_PIN_GNSS_RST = 23;
class Tca6424 {
public:
explicit Tca6424(uint8_t address) : address(address) {}
bool begin(TwoWire &wire)
{
this->wire = &wire;
// Default all pins as inputs and all outputs low in our local shadow.
config[0] = 0xFF;
config[1] = 0xFF;
config[2] = 0xFF;
out[0] = 0x00;
out[1] = 0x00;
out[2] = 0x00;
return writeReg(TCA6424_OUTPUT_PORT0 + 0, out[0]) && writeReg(TCA6424_OUTPUT_PORT0 + 1, out[1]) &&
writeReg(TCA6424_OUTPUT_PORT0 + 2, out[2]) && writeReg(TCA6424_CONFIG_PORT0 + 0, config[0]) &&
writeReg(TCA6424_CONFIG_PORT0 + 1, config[1]) && writeReg(TCA6424_CONFIG_PORT0 + 2, config[2]);
}
bool pinMode(uint8_t pin, bool output)
{
const uint8_t port = pin / 8;
const uint8_t bit = pin % 8;
if (port > 2) {
return false;
}
if (output) {
config[port] &= ~(1U << bit); // 0 = output
} else {
config[port] |= (1U << bit); // 1 = input
}
return writeReg(TCA6424_CONFIG_PORT0 + port, config[port]);
}
bool digitalWrite(uint8_t pin, bool high)
{
const uint8_t port = pin / 8;
const uint8_t bit = pin % 8;
if (port > 2) {
return false;
}
if (high) {
out[port] |= (1U << bit);
} else {
out[port] &= ~(1U << bit);
}
return writeReg(TCA6424_OUTPUT_PORT0 + port, out[port]);
}
private:
bool writeReg(uint8_t reg, uint8_t value)
{
wire->beginTransmission(address);
wire->write(reg);
wire->write(value);
return wire->endTransmission() == 0;
}
uint8_t address;
TwoWire *wire = nullptr;
uint8_t config[3] = {0xFF, 0xFF, 0xFF};
uint8_t out[3] = {0x00, 0x00, 0x00};
};
Tca6424 ioExpander(IO_EXPANDER_I2C_ADDR);
DriverPins pinsAudioBoardES8311;
AudioBoard board(AudioDriverES8311, pinsAudioBoardES8311);
bool expanderReady = false;
bool earlyInitFailed = false;
bool configureInput(uint8_t pin)
{
return ioExpander.pinMode(pin, false);
}
bool configureOutput(uint8_t pin, bool level)
{
return ioExpander.pinMode(pin, true) && ioExpander.digitalWrite(pin, level);
}
} // namespace
void earlyInitVariant()
{
// Use explicit pins to avoid accidental macro drift during early startup.
Wire.begin(17, 18);
if (!ioExpander.begin(Wire)) {
earlyInitFailed = true;
return;
}
// Buttons + SD detect + IMU interrupt are inputs.
bool ok = true;
ok &= configureInput(EXP_PIN_BUTTON_UP);
ok &= configureInput(EXP_PIN_BUTTON_DOWN);
ok &= configureInput(EXP_PIN_BUTTON_LEFT);
ok &= configureInput(EXP_PIN_BUTTON_RIGHT);
ok &= configureInput(EXP_PIN_BUTTON_CONFIRM);
ok &= configureInput(EXP_PIN_BUTTON_RETURN);
ok &= configureInput(EXP_PIN_BUTTON_PWR);
ok &= configureInput(EXP_PIN_SD_DETECT);
ok &= configureInput(EXP_PIN_IMU_INT1);
// Keep board alive and power domains in known state.
ok &= configureOutput(EXP_PIN_PWR_HOLD, true);
ok &= configureOutput(EXP_PIN_BAT_ADC_EN, false);
ok &= configureOutput(EXP_PIN_SEN_EN, true);
ok &= configureOutput(EXP_PIN_SD_PWR_EN, true);
ok &= configureOutput(EXP_PIN_PA_PWR_EN, true);
ok &= configureOutput(EXP_PIN_LORA_RESET, true);
// ST7789P3 power-on and reset sequence (mirrors bsp_lcd_hard_reset in BSP demo).
ok &= configureOutput(EXP_PIN_LCD_PWR_EN, true);
delay(20);
ok &= configureOutput(EXP_PIN_LCD_RST, false); // assert reset (active low)
delay(20);
ok &= ioExpander.digitalWrite(EXP_PIN_LCD_RST, true); // release reset
delay(120); // allow ST7789P3 to complete internal initialization
// Match demo LR20xx reset sequence on expander pin 22.
ok &= ioExpander.digitalWrite(EXP_PIN_LORA_RESET, false);
delay(10);
ok &= ioExpander.digitalWrite(EXP_PIN_LORA_RESET, true);
delay(10);
// GNSS sequence from demo firmware.
ok &= configureOutput(EXP_PIN_GNSS_PWR_EN, true);
delay(10);
ok &= configureOutput(EXP_PIN_GNSS_VRTC_EN, true);
delay(10);
ok &= configureOutput(EXP_PIN_GNSS_RST, true);
delay(10);
ok &= ioExpander.digitalWrite(EXP_PIN_GNSS_RST, false);
delay(10);
ok &= configureOutput(EXP_PIN_GNSS_SLEEP_INT, true);
delay(10);
ok &= configureOutput(EXP_PIN_GNSS_RTC_INT, false);
expanderReady = ok;
earlyInitFailed = !ok;
}
void lateInitVariant()
{
if (!expanderReady) {
if (earlyInitFailed) {
//LOG_ERROR("Skipping ES8311 init because TCA6424 init/config failed");
} else {
//LOG_ERROR("Skipping ES8311 init because TCA6424 is not ready");
}
return;
}
Wire1.begin(I2C_SDA1, I2C_SCL1);
// I2C: function, bus
pinsAudioBoardES8311.addI2C(PinFunction::CODEC, Wire1);
// I2S: function, mclk, bck, ws, data_out, data_in
pinsAudioBoardES8311.addI2S(PinFunction::CODEC, DAC_I2S_MCLK, DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_DIN);
CodecConfig cfg;
cfg.input_device = ADC_INPUT_LINE1;
cfg.output_device = DAC_OUTPUT_ALL;
cfg.i2s.bits = BIT_LENGTH_16BITS;
cfg.i2s.rate = RATE_44K;
board.begin(cfg);
board.setVolume(75);
//LOG_INFO("ES8311 audio board initialized on I2C1");
}
extern "C" void meshtastic_variant_pre_radio_reset(void)
{
if (!expanderReady) {
return;
}
// Match SenseCAP demo: pulse LR20xx reset through expander right before radio init.
ioExpander.digitalWrite(EXP_PIN_LORA_RESET, false);
delay(10);
ioExpander.digitalWrite(EXP_PIN_LORA_RESET, true);
delay(10);
}
@@ -0,0 +1,161 @@
#pragma once
/**
* SenseCAP MeshPager X2 - ESP32-S3 LoRa/GNSS/Audio Pager
*
* Target: ESP32-S3
* Display: ST7789P3 LCD 240×320 RGB565 (SPI)
* Radio: LR20xx wideband (LR1120/LR1121)
* Audio: ES8311 DAC + ES7243E ADC
* GNSS: L76K UART
* Storage: SD card (1-bit SDIO mode)
* IO Expander: TCA6424
*/
// Buttons - via IO Expander (TCA6424)
#define BUTTON_UP GPIO_NUM_NC // EXP pin 0, active low
#define BUTTON_DOWN GPIO_NUM_NC // EXP pin 1, active low
#define BUTTON_LEFT GPIO_NUM_NC // EXP pin 2, active low
#define BUTTON_RIGHT GPIO_NUM_NC // EXP pin 3, active low
#define BUTTON_CONFIRM GPIO_NUM_NC // EXP pin 4, active low
#define BUTTON_RETURN GPIO_NUM_NC // EXP pin 5, active low
#define BUTTON_PIN 9 // Power/wake button (GPIO 9), active high
// I2C Buses
#define I2C_SDA 17 // I2C0: SDA (IO Expander, RTC)
#define I2C_SCL 18 // I2C0: SCL
#define I2C_SDA1 47 // I2C1: SDA (Sensors, Audio codecs)
#define I2C_SCL1 48 // I2C1: SCL
// I2C0 devices: TCA6424 IO expander, YSN8900E RTC
// I2C1 devices: ES8311, ES7243E, SHT4X, LSM6DSO, BMM350
// IO Expander (TCA6424)
#define HAS_IO_EXPANDER
#define IO_EXPANDER_I2C_PORT 0
#define IO_EXPANDER_I2C_ADDR 0x22
#define IO_EXPANDER_INT 2 // GPIO 2 interrupt from expander
// Battery & Power
#define BATTERY_PIN 1 // GPIO 1, ADC for VBAT with 2:1 divider
#define ADC_CHANNEL ADC_CHANNEL_0 // ADC1_GPIO1_CHANNEL
#define ADC_MULTIPLIER 2.0 * 1.045 // 2.0 divider + 4.5% correction
#define ADC_ATTEN ADC_ATTEN_DB_12
#define USE_POWERSAVE
#define SLEEP_TIME 120
#if 0
#define TFT_CS 10 // Chip select (GPIO 10, SPI2)
#define TFT_SDA 11 // MOSI (GPIO 11)
#define TFT_SCK 12 // Clock (GPIO 12)
#define TFT_MISO -1 // Not used for display
#define TFT_A0 13 // DC / Data-Command (GPIO 13)
#define TFT_BL 46 // Backlight PWM (GPIO 46, LEDC)
#define TFT_RESET -1 // Reset via expander (EXP pin 21), managed separately
// TFT uses SPI bus (SPI2_HOST on ESP32-S3)
#define ST7789_CS TFT_CS
#define ST7789_RS TFT_A0
#define ST7789_SDA TFT_SDA
#define ST7789_SCK TFT_SCK
#define ST7789_MISO TFT_MISO
#define ST7789_RESET TFT_RESET
#define ST7789_BUSY -1
#define ST7789_BL TFT_BL
#define ST7789_SPI_HOST SPI2_HOST
//#define SPI_FREQUENCY 80000000
#define SPI_READ_FREQUENCY 16000000
#define TFT_HEIGHT 320
#define TFT_WIDTH 240
#define TFT_OFFSET_X 0
#define TFT_OFFSET_Y 0
#define TFT_OFFSET_ROTATION 0
#define BRIGHTNESS_DEFAULT 130 // Medium-low brightness (0-255)
#define SCREEN_TRANSITION_FRAMERATE 5
#endif
// LoRa Radio - Semtech LR2021 (Wio-LR2021 module)
#define USE_LR2021 // Wideband radio supporting sub-GHz and 2.4 GHz
// LoRa uses SPI1 bus
#define HW_SPI1_DEVICE
#define LORA_SCK 3 // GPIO 3
#define LORA_MOSI 4 // GPIO 4
#define LORA_MISO 5 // GPIO 5
#define LORA_CS 6 // GPIO 6
#define LORA_DIO0 -1 // Not connected
#define LORA_DIO1 7 // GPIO 7 (IRQ)
#define LORA_DIO2 8 // GPIO 8 (BUSY)
#define LORA_DIO3 -1 // Not connected (managed via IO expander pin 22 for reset)
#define LORA_RESET -1 // Reset via IO Expander (TCA6424 pin 22)
#define LR2021_IRQ_PIN LORA_DIO1
#define LR2021_NRESET_PIN LORA_RESET
#define LR2021_BUSY_PIN LORA_DIO2
#define LR2021_SPI_NSS_PIN LORA_CS
#define LR2021_SPI_SCK_PIN LORA_SCK
#define LR2021_SPI_MOSI_PIN LORA_MOSI
#define LR2021_SPI_MISO_PIN LORA_MISO
// LR2021 IRQ is on chip DIO8 (not the default DIO5).
#define LR2021_IRQ_DIO_NUM 8
// LR2021 TCXO supply voltage on DIO3 (1.8 V).
#define LR2021_DIO3_TCXO_VOLTAGE 1.8
#define LR2021_DIO_AS_RF_SWITCH
// GPS/GNSS - L76K (UART)
#define HAS_GPS 1
#define GPS_BAUDRATE 115200
#define GPS_TX_PIN 43 // UART1 TX
#define GPS_RX_PIN 44 // UART1 RX
#define GPS_THREAD_INTERVAL 50 // 50 ms update interval
// Audio - I2S with ES8311 DAC + ES7243E ADC
#define HAS_I2S
#define DAC_I2S_BCK 39 // SCLK (GPIO 39)
#define DAC_I2S_WS 40 // LRLK (GPIO 40)
#define DAC_I2S_DOUT 41 // SDOUT/Speaker (GPIO 41)
#define DAC_I2S_DIN -1 // Not used for DAC
#define DAC_I2S_MCLK 38 // MCLK (GPIO 38)
#define ADC_I2S_BCK 39 // SCLK (GPIO 39, shared with DAC)
#define ADC_I2S_WS 40 // LRLK (GPIO 40, shared with DAC)
#define ADC_I2S_DOUT -1 // Not used for ADC
#define ADC_I2S_DIN 42 // SDIN/Microphone (GPIO 42)
#define ADC_I2S_MCLK 38 // MCLK (GPIO 38, shared with DAC)
// Audio Codec Addresses
#define HAS_ES8311
#define ES8311_I2C_ADDR 0x18 // I2C1 address for ES8311
#define HAS_ES7243E
#define ES7243E_I2C_ADDR 0x14 /* I2C1 address for ES7243E ADC
Note: May conflict with BMM350 sensor in some configs */
// Sensors - I2C1
#define HAS_BMM350 /* Magnetometer on I2C1 */
#define HAS_SHT4X /* Temperature/Humidity on I2C1 */
#define HAS_LSM6DSO /* IMU (Accel/Gyro) on I2C1 */
// RTC - via I2C0
#define RTC_INT 45 // GPIO 45 (RTC interrupt input)
// Debug UART
#define SERIAL_TX_PIN 43 // Shared with GPS TX
#define SERIAL_RX_PIN 44 // Shared with GPS RX
// Display backlight control via LEDC
#define LEDC_TIMER LEDC_TIMER_0
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_OUTPUT_IO TFT_BL
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT
#define LEDC_FREQUENCY 5000 // 5 kHz PWM