Compare commits

...
42 Commits
Author SHA1 Message Date
Jonathan Bennett 7de19ae1fd Change define to avoid collision 2026-06-23 16:45:21 -05:00
Jonathan Bennett 7dbf7ed32d Add IMU support 2026-06-23 15:26:05 -05:00
Jonathan Bennett da5ed9f6e9 Add PMU IRQ handling 2026-06-23 09:05:39 -05:00
Jonathan Bennett 7808c81649 Get baseui drawing to the screen correctly again on t-watch and add touch IRQ handling 2026-06-22 22:44:20 -05:00
Jonathan Bennett 352a97d81c Fix flash reads on t-watch-ultra 2026-06-22 07:51:59 -05:00
Jonathan Bennett 519bd4994d Fix compilation 2026-06-20 00:27:01 -05:00
Jonathan BennettandGitHub 7980dd2cdb Merge branch 'develop' into t-watch-ultra 2026-06-19 22:26:59 -05:00
Jonathan BennettandGitHub 2be14eb6c7 Merge branch 'develop' into t-watch-ultra 2026-06-19 18:27:37 -05:00
Jonathan BennettandGitHub 31b38630f7 Merge branch 'develop' into t-watch-ultra 2026-04-27 07:58:36 -05:00
Jonathan Bennett 2d37a58959 Merge remote-tracking branch 'origin/develop' into t-watch-ultra 2026-04-27 07:53:48 -05:00
Jonathan BennettandGitHub 293b86013f Merge branch 'develop' into t-watch-ultra 2026-04-20 00:25:23 -05:00
Jonathan Bennett 97824c6ba0 Get t-watch-ultra working better on BaseUI 2026-04-20 00:20:53 -05:00
Jonathan Bennett fe45e5fde6 Set NFC_CS disabled on boot 2026-04-17 15:43:17 -05:00
ManuelandGitHub d4514cff62 Merge branch 'develop' into t-watch-ultra 2026-04-17 16:08:41 +02:00
ManuelandGitHub db4a097303 Merge branch 'develop' into t-watch-ultra 2026-01-28 10:37:01 +01:00
ManuelandGitHub dc212b08ac Merge branch 'develop' into t-watch-ultra 2026-01-27 16:14:20 +01:00
Manuel 93f05406c4 update device-ui / touch handling 2026-01-27 16:13:46 +01:00
ManuelandGitHub 147299e44f Merge branch 'develop' into t-watch-ultra 2026-01-27 13:38:09 +01:00
Manuel bd7ebd946a earlyInitVariant() adaptations acc. #9438 2026-01-27 13:36:59 +01:00
ManuelandGitHub e4314904be Merge branch 'develop' into t-watch-ultra 2026-01-27 12:53:09 +01:00
ManuelandGitHub 42a20016d9 Merge branch 'develop' into t-watch-ultra 2026-01-23 23:53:24 +01:00
Manuel 6015b05c09 update LovyanGFX 1.2.19 2026-01-23 23:52:48 +01:00
ManuelandGitHub 083c65e42b Merge branch 'develop' into t-watch-ultra 2026-01-20 01:04:50 +01:00
Manuel b38cbf9a40 init lora RF switch 2026-01-20 00:21:19 +01:00
Manuel ad819321f7 haptic touch (only BaseUI) 2026-01-20 00:20:34 +01:00
Manuel 3529c0bce2 trySetRTC when fixedPosition 2026-01-20 00:01:19 +01:00
Manuel 8ee5407db5 fix NodeDB defaults 2026-01-20 00:00:33 +01:00
Manuel 65d4adb7c2 power off lora 2026-01-20 00:00:07 +01:00
Manuel 0cda4eb675 PMU cosmetics 2026-01-19 23:59:49 +01:00
Manuel 7763469c62 update amoled driver reference 2026-01-19 13:34:24 +01:00
Manuel f158964d5e trunk fmt 2026-01-19 01:10:35 +01:00
Manuel a8af68844c update to device-ui 2026-01-19 00:51:53 +01:00
ManuelandGitHub e8153ca6c2 Merge branch 'develop' into t-watch-ultra 2026-01-18 23:26:31 +01:00
ManuelandGitHub b905766c6a Merge branch 'develop' into t-watch-ultra 2026-01-11 11:50:06 +01:00
ManuelandGitHub 9f8311ffd5 Merge branch 'develop' into t-watch-ultra 2025-10-21 10:17:13 +02:00
ManuelandGitHub efd2ca9c06 Merge branch 'develop' into t-watch-ultra 2025-10-10 20:04:37 +02:00
Manuel 19e62b8686 add framebuffer 2025-10-04 12:36:31 +02:00
Manuel fb46938582 fix init touch controller 2025-10-04 12:35:31 +02:00
ManuelandGitHub 4776631ba8 Merge branch 'develop' into t-watch-ultra 2025-10-04 08:22:06 +02:00
ManuelandGitHub 295b8b6db5 Merge branch 'develop' into t-watch-ultra 2025-10-03 23:09:01 +02:00
ManuelandGitHub 82123b1519 Merge branch 'develop' into t-watch-ultra 2025-10-03 13:34:24 +02:00
mverch67 d5bb3db4ed feat: T-Watch Ultra support 2025-09-30 20:05:22 +02:00
31 changed files with 873 additions and 31 deletions
+40
View File
@@ -0,0 +1,40 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_qspi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"psram_type": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "t-watch-ultra"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino"],
"name": "LilyGo T-Watch Ultra",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"speed": 921600
},
"url": "https://www.lilygo.cc/en-pl/products/t-watch-ultra",
"vendor": "LilyGo"
}
+46 -2
View File
@@ -1063,8 +1063,10 @@ int32_t Power::runOnce()
// cancel action also turns the screen on and off.
if (PMU->isPekeyShortPressIrq()) {
LOG_INFO("Input: Corona Button Click");
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_CANCEL, .kbchar = 0, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event);
if (inputBroker) {
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_CANCEL, .kbchar = 0, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event);
}
}
#endif
/*
@@ -1367,6 +1369,48 @@ bool Power::axpChipInit()
PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist
PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist
PMU->disablePowerOutput(XPOWERS_VBACKUP);
} else if (HW_VENDOR == meshtastic_HardwareModel_T_WATCH_ULTRA) {
PMU->clearIrqStatus();
// Turn off the PMU charging indicator light, no physical connection
PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF); // NO LED
PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300); // SD Card
PMU->enablePowerOutput(XPOWERS_ALDO1);
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300); // Display
PMU->enablePowerOutput(XPOWERS_ALDO2);
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); // LoRa
PMU->enablePowerOutput(XPOWERS_ALDO3);
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 1800); // Sensor
PMU->enablePowerOutput(XPOWERS_ALDO4);
PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300); // GPS
PMU->enablePowerOutput(XPOWERS_BLDO1);
PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300); // Speaker
PMU->enablePowerOutput(XPOWERS_BLDO2);
PMU->setPowerChannelVoltage(XPOWERS_VBACKUP, 3300); // RTC Button battery
PMU->enablePowerOutput(XPOWERS_VBACKUP);
// PMU->enablePowerOutput(XPOWERS_DLDO1); // NFC
// UNUSED POWER CHANNEL
PMU->disablePowerOutput(XPOWERS_DCDC2);
PMU->disablePowerOutput(XPOWERS_DCDC3);
PMU->disablePowerOutput(XPOWERS_DCDC4);
PMU->disablePowerOutput(XPOWERS_DCDC5);
PMU->disablePowerOutput(XPOWERS_CPULDO);
// Enable Measure
PMU->enableBattDetection();
PMU->enableVbusVoltageMeasure();
PMU->enableBattVoltageMeasure();
PMU->enableSystemVoltageMeasure();
PMU->enableTemperatureMeasure();
}
// disable all axp chip interrupt
+3 -3
View File
@@ -37,9 +37,9 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
{
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX,
ICM20948, QMA6100P, BMM150, BMI270, ICM42607P, ISM330DHCX};
return firstOfOrNONE(12, types);
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948,
QMA6100P, BHI260AP, BMM150, BMI270, ICM42607P, ISM330DHCX};
return firstOfOrNONE(13, types);
}
ScanI2C::FoundDevice ScanI2C::firstMagnetometer() const
+2 -2
View File
@@ -1279,8 +1279,8 @@ void GPS::setPowerPMU(bool on)
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
// t-beam-s3-core GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
} else if (HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
// t-watch-s3-plus GNSS power channel
} else if (HW_VENDOR == meshtastic_HardwareModel_T_WATCH_ULTRA || HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
// t-watch-ultra / t-watch-s3-plus GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_BLDO1) : PMU->disablePowerOutput(XPOWERS_BLDO1);
}
} else if (model == XPOWERS_AXP192) {
+8 -6
View File
@@ -512,7 +512,8 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
LOG_INFO("SSD1306 init success");
}
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(CO5300_CS) || \
defined(HACKADAY_COMMUNICATOR)
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY) && !defined(USE_EINK_PARALLELDISPLAY)
@@ -586,12 +587,12 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
if (on) {
LOG_INFO("Turn on screen");
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
#ifdef T_WATCH_S3
#if defined(T_WATCH_S3) || defined(T_WATCH_ULTRA)
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
// some screens seem to need a kick in the pants to turn back on
#if defined(MUZI_BASE) || defined(M5STACK_CARDPUTER_ADV)
#if defined(MUZI_BASE) || defined(M5STACK_CARDPUTER_ADV) || defined(TFT_RESET_AFTER_SLEEP)
dispdev->init();
dispdev->setBrightness(brightness);
dispdev->flipScreenVertically();
@@ -716,7 +717,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif
#endif
#ifdef T_WATCH_S3
#if defined(T_WATCH_S3) // on T_WATCH_ULTRA, powering down this pin seems to goober the i2c bus.
PMU->disablePowerOutput(XPOWERS_ALDO2);
#endif
enabled = false;
@@ -848,7 +849,8 @@ void Screen::setup()
#else
if (!config.display.flip_screen) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(CO5300_CS) || \
defined(HACKADAY_COMMUNICATOR)
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
@@ -886,7 +888,7 @@ void Screen::setup()
touchScreenImpl1->init();
}
}
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE && !VARIANT_TOUCHSCREEN
touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init();
+3
View File
@@ -271,6 +271,9 @@ class Screen : public concurrency::OSThread
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
char ourId[5];
// if we have a step counter, this stores the number of steps.
uint32_t steps = 0;
/// Initializes the UI, turns on the display, starts showing boot screen.
//
// Not thread safe - must be called before any other methods are called.
+1 -1
View File
@@ -100,7 +100,7 @@
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)) && \
defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
+141 -5
View File
@@ -16,6 +16,92 @@
extern SX1509 gpioExtender;
#endif
#ifdef TFT_MESH_OVERRIDE
uint16_t TFT_MESH = TFT_MESH_OVERRIDE;
#else
uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
#endif
#if defined(CO5300_CS)
#include <LovyanGFX.hpp> // Graphics and font library for AMOLED driver chip
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_CO5300 _panel_instance;
lgfx::Bus_SPI _bus_instance;
public:
LGFX(void)
{
{
auto cfg = _bus_instance.config();
// configure SPI
cfg.spi_host = CO5300_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
cfg.spi_mode = SPI_MODE0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
// 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
// SPI_DMA_CH_AUTO=auto setting)
cfg.pin_sclk = CO5300_SCK; // Set SPI SCLK pin number
cfg.pin_io0 = CO5300_IO0;
cfg.pin_io1 = CO5300_IO1;
cfg.pin_io2 = CO5300_IO2;
cfg.pin_io3 = CO5300_IO3;
_bus_instance.config(cfg); // applies the set value to the bus.
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
}
{ // Set the display panel control.
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
cfg.pin_cs = CO5300_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = CO5300_RESET; // Pin number where RST is connected (-1 = disable)
cfg.panel_width = TFT_WIDTH; // actual displayable width
cfg.panel_height = TFT_HEIGHT; // actual displayable height
cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is upside down)
cfg.offset_x = TFT_OFFSET_X;
cfg.offset_y = TFT_OFFSET_Y;
cfg.dummy_read_pixel = 8; // Number of bits for dummy read before pixel readout
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
cfg.readable = true; // Set to true if data can be read
cfg.invert = false; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
// Set the following only when the display is shifted with a driver with a variable number of pixels
cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
_panel_instance.config(cfg);
}
setPanel(&_panel_instance);
}
bool init()
{
#ifdef CO5300_RESET
LOG_DEBUG("LGFX_Panel_CO5300::init()");
lgfx::pinMode(CO5300_RESET, lgfx::pin_mode_t::output);
lgfx::gpio_hi(CO5300_RESET);
delay(20);
lgfx::gpio_lo(CO5300_RESET);
delay(30);
lgfx::gpio_hi(CO5300_RESET);
delay(20);
#endif
return lgfx::LGFX_Device::init();
}
};
static LGFX *tft = nullptr;
#endif
#if defined(ST7735S)
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
@@ -1362,6 +1448,7 @@ void TFTDisplay::display(bool fromBlank)
x_LastPixelUpdate = displayWidth - 1;
}
int y_offset = 0;
// Step 3: Copy only the changed span into the pixel line buffer.
for (x = x_FirstPixelUpdate; x <= x_LastPixelUpdate; x++) {
isset = buffer[x + y_byteIndex] & y_byteMask;
@@ -1376,13 +1463,58 @@ void TFTDisplay::display(bool fromBlank)
linePixelBuffer[x] = isset ? colorTftWhite : colorTftBlack;
#endif
}
#if defined(CO5300_CS)
uint8_t lines_updated = 2;
if (y % 2 == 0) {
y_byteIndex = ((y + 1) / 8) * displayWidth;
y_byteMask = (1 << ((y + 1) & 7));
uint32_t bufferIndex = 1;
for (x = x_FirstPixelUpdate; x < x_LastPixelUpdate; x++) {
isset = buffer[x + y_byteIndex] & y_byteMask;
#if GRAPHICS_TFT_COLORING_ENABLED
if (hasColorRegions) {
linePixelBuffer[bufferIndex++ + x_LastPixelUpdate] = graphics::resolveTFTColorPixel(
static_cast<int16_t>(x), static_cast<int16_t>(y), isset, colorTftWhite, colorTftBlack);
} else {
linePixelBuffer[bufferIndex++ + x_LastPixelUpdate] = isset ? colorTftWhite : colorTftBlack;
}
#else
linePixelBuffer[bufferIndex++ + x_LastPixelUpdate] = isset ? colorTftWhite : colorTftBlack;
#endif
}
} else {
y_offset = -1;
memcpy(&linePixelBuffer[x_LastPixelUpdate + 1], &linePixelBuffer[x_FirstPixelUpdate],
2 * (x_LastPixelUpdate - x_FirstPixelUpdate + 1));
y_byteIndex = ((y - 1) / 8) * displayWidth;
y_byteMask = (1 << ((y - 1) & 7));
uint32_t bufferIndex = 0;
for (x = x_FirstPixelUpdate; x < x_LastPixelUpdate; x++) {
isset = buffer[x + y_byteIndex] & y_byteMask;
#if GRAPHICS_TFT_COLORING_ENABLED
if (hasColorRegions) {
linePixelBuffer[bufferIndex++ + x_FirstPixelUpdate] = graphics::resolveTFTColorPixel(
static_cast<int16_t>(x), static_cast<int16_t>(y), isset, colorTftWhite, colorTftBlack);
} else {
linePixelBuffer[bufferIndex++ + x_FirstPixelUpdate] = isset ? colorTftWhite : colorTftBlack;
}
#else
linePixelBuffer[bufferIndex++ + x_FirstPixelUpdate] = isset ? colorTftWhite : colorTftBlack;
#endif
}
}
#else
uint8_t lines_updated = 1;
#endif
#if defined(HACKADAY_COMMUNICATOR)
tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate],
(x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
#else
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
tft->pushImage(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
tft->pushImage(x_FirstPixelUpdate, y + y_offset, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), lines_updated,
&linePixelBuffer[x_FirstPixelUpdate]);
#endif
somethingChanged = true;
@@ -1450,7 +1582,7 @@ void TFTDisplay::sendCommand(uint8_t com)
// handle display on/off directly
switch (com) {
case DISPLAYON: {
// LOG_DEBUG("Display on");
LOG_DEBUG("Display on");
backlightEnable->set(true);
#if ARCH_PORTDUINO
display(true);
@@ -1478,7 +1610,7 @@ void TFTDisplay::sendCommand(uint8_t com)
break;
}
case DISPLAYOFF: {
// LOG_DEBUG("Display off");
LOG_DEBUG("Display off");
backlightEnable->set(false);
#if ARCH_PORTDUINO
tft->clear();
@@ -1581,8 +1713,8 @@ bool TFTDisplay::connect()
tft = new LGFX;
#endif
backlightEnable->set(true);
LOG_INFO("Power to TFT Backlight");
backlightEnable->set(true);
#ifdef UNPHONE
unphone.backlight(true); // using unPhone library
@@ -1610,7 +1742,7 @@ bool TFTDisplay::connect()
tft->setRotation(1); // T-Deck has the TFT in landscape
#elif defined(T_WATCH_S3)
tft->setRotation(2); // T-Watch S3 left-handed orientation
#elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR) || defined(T_LORA_PAGER)
#elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR) || defined(T_LORA_PAGER) || defined(T_WATCH_ULTRA)
tft->setRotation(0); // use config.yaml to set rotation
#else
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
@@ -1618,7 +1750,11 @@ bool TFTDisplay::connect()
tft->fillScreen(getThemeDefaultOffColor());
if (this->linePixelBuffer == NULL) {
#if defined(CO5300_CS)
this->linePixelBuffer = (uint16_t *)malloc(sizeof(uint16_t) * displayWidth * 2);
#else
this->linePixelBuffer = (uint16_t *)malloc(sizeof(uint16_t) * displayWidth);
#endif
if (!this->linePixelBuffer) {
LOG_ERROR("Not enough memory to create TFT line buffer\n");
+3 -3
View File
@@ -99,7 +99,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
8, imgQuestionL1);
@@ -112,7 +112,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
} else {
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
8, imgSFL1);
@@ -128,7 +128,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
// TODO: Raspberry Pi supports more than just the one screen size
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
+8 -1
View File
@@ -719,7 +719,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
if (currentResolution == ScreenResolution::High) {
@@ -1304,6 +1304,13 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
UIRenderer::drawStringWithEmotes(display, nameX, getTextPositions(display)[line++], shortName, FONT_HEIGHT_SMALL, 1,
false);
}
#ifdef SHOW_STEP_COUNTER
std::string stepsLine = "Steps: " + std::to_string(screen->steps);
textWidth = UIRenderer::measureStringWithEmotes(display, stepsLine.c_str());
nameX = (SCREEN_WIDTH - textWidth) / 2;
UIRenderer::drawStringWithEmotes(display, nameX, getTextPositions(display)[line++], stepsLine.c_str(), FONT_HEIGHT_SMALL, 1,
false);
#endif
#endif
graphics::drawCommonFooter(display, x, y);
}
+1 -1
View File
@@ -28,7 +28,7 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR) || ARCH_PORTDUINO) && \
defined(USE_ST7796) || defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
+7 -1
View File
@@ -66,8 +66,14 @@ void TouchScreenBase::init(bool hasTouch)
int32_t TouchScreenBase::runOnce()
{
uint32_t nowMs = millis();
if (nowMs - _lastRun < 20) { // suppress too fast consecutive runOnce() executions
return 20;
}
_lastRun = nowMs;
TouchEvent e;
e.touchEvent = static_cast<char>(TOUCH_ACTION_NONE);
this->setInterval(TOUCH_POLL_INTERVAL_IDLE);
const bool fastTapMode = fastTapModeEnabled();
const bool allowLongPress = longPressEnabled();
@@ -186,7 +192,7 @@ int32_t TouchScreenBase::runOnce()
void TouchScreenBase::hapticFeedback()
{
#ifdef T_WATCH_S3
#if defined(T_WATCH_S3) || defined(T_WATCH_ULTRA)
drv.setWaveform(0, 75);
drv.setWaveform(1, 0); // end waveform
drv.go();
+1
View File
@@ -53,6 +53,7 @@ class TouchScreenBase : public Observable<const InputEvent *>, public concurrenc
time_t _start; // for LONG_PRESS
uint32_t _lastTouchSeenMs; // helps suppress brief touch-controller dropouts
bool _tapped; // for DOUBLE_TAP
uint32_t _lastRun = 0; // helps suppress too fast consecutive runOnce() executions
const char *_originName;
};
+61
View File
@@ -2,7 +2,9 @@
#include "InputBroker.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "main.h"
#include "modules/ExternalNotificationModule.h"
#include "sleep.h"
#include <cstring>
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
@@ -16,6 +18,27 @@
TouchScreenImpl1 *touchScreenImpl1;
// Hardware-interrupt wake on the touch IRQ line. Scoped to T-Watch Ultra: other touch boards
// either drive this pin differently (RAK14014 already owns this interrupt in TFTDisplay) or
// route it through an IO expander, which can't be used with attachInterrupt().
#if defined(SCREEN_TOUCH_INT) && defined(T_WATCH_ULTRA)
#define TOUCHSCREEN_HAS_INTERRUPT 1
// The touch controller pulls SCREEN_TOUCH_INT low when a new touch begins. Wake the polling
// thread immediately so the touch is handled without waiting for the next idle poll. The
// periodic poll in TouchScreenBase::runOnce() remains as a fallback. Mirrors the button
// interrupt handling in ButtonThread/InputBroker.
static void touchScreenInterruptHandler()
{
if (touchScreenImpl1) {
touchScreenImpl1->setIntervalFromNow(0);
runASAP = true;
BaseType_t higherWake = 0;
concurrency::mainDelay.interruptFromISR(&higherWake);
}
}
#endif
TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *))
: TouchScreenBase("touchscreen1", width, height), _getTouch(getTouch)
{
@@ -27,6 +50,7 @@ void TouchScreenImpl1::init()
if (portduino_config.touchscreenModule) {
TouchScreenBase::init(true);
inputBroker->registerSource(this);
attachTouchInterrupt();
} else {
TouchScreenBase::init(false);
}
@@ -37,9 +61,46 @@ void TouchScreenImpl1::init()
TouchScreenBase::init(true);
if (inputBroker)
inputBroker->registerSource(this);
attachTouchInterrupt();
#endif
#if defined(TOUCHSCREEN_HAS_INTERRUPT) && defined(ARCH_ESP32)
// Detach/reattach our interrupt around light sleep, so sleep.cpp can configure the touch
// pin as a wake source without our handler interfering.
lsObserver.observe(&notifyLightSleep);
lsEndObserver.observe(&notifyLightSleepEnd);
#endif
}
// Attach the touch-controller IRQ so a new touch wakes the polling thread immediately.
// No-op on boards without a usable touch interrupt line.
void TouchScreenImpl1::attachTouchInterrupt()
{
#ifdef TOUCHSCREEN_HAS_INTERRUPT
pinMode(SCREEN_TOUCH_INT, INPUT_PULLUP);
attachInterrupt(SCREEN_TOUCH_INT, touchScreenInterruptHandler, FALLING);
LOG_INFO("TouchScreen interrupt attached on pin %d", SCREEN_TOUCH_INT);
#endif
}
#ifdef ARCH_ESP32
// Detach our interrupt before light sleep; sleep.cpp configures its own wake-on-touch.
int TouchScreenImpl1::beforeLightSleep(void *unused)
{
#ifdef TOUCHSCREEN_HAS_INTERRUPT
detachInterrupt(SCREEN_TOUCH_INT);
#endif
return 0; // Indicates success
}
// Reattach our interrupt after waking from light sleep.
int TouchScreenImpl1::afterLightSleep(esp_sleep_wakeup_cause_t cause)
{
attachTouchInterrupt();
return 0; // Indicates success
}
#endif
bool TouchScreenImpl1::getTouch(int16_t &x, int16_t &y)
{
return _getTouch(&x, &y);
+16
View File
@@ -13,7 +13,23 @@ class TouchScreenImpl1 : public TouchScreenBase
bool fastTapModeEnabled() const override;
bool longPressEnabled() const override;
// Attach/detach a hardware interrupt on the touch IRQ pin (SCREEN_TOUCH_INT) so a new touch
// wakes the polling thread immediately. No-op on boards without a usable touch interrupt line.
void attachTouchInterrupt();
bool (*_getTouch)(int16_t *, int16_t *);
#ifdef ARCH_ESP32
// Detach the touch interrupt before light sleep (so sleep.cpp can own the wake config),
// and reattach it afterwards. Mirrors ButtonThread's interrupt handling.
int beforeLightSleep(void *unused);
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
CallbackObserver<TouchScreenImpl1, void *> lsObserver =
CallbackObserver<TouchScreenImpl1, void *>(this, &TouchScreenImpl1::beforeLightSleep);
CallbackObserver<TouchScreenImpl1, esp_sleep_wakeup_cause_t> lsEndObserver =
CallbackObserver<TouchScreenImpl1, esp_sleep_wakeup_cause_t>(this, &TouchScreenImpl1::afterLightSleep);
#endif
};
extern TouchScreenImpl1 *touchScreenImpl1;
+5 -2
View File
@@ -290,6 +290,9 @@ __attribute__((weak, noinline)) bool loopCanSleep()
void lateInitVariant() __attribute__((weak));
void lateInitVariant() {}
// NOTE: earlyInitVariant() runs before consoleInit(), so the logging subsystem isn't set
// up yet. Calling a LOG_* macro here CRASHES the device -- it is not a silent no-op. Do
// not use them in earlyInitVariant(); defer any logging to lateInitVariant() or later.
void earlyInitVariant() __attribute__((weak));
void earlyInitVariant() {}
@@ -916,7 +919,7 @@ void setup()
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_SPISSD1306) || defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)
defined(USE_SPISSD1306) || defined(USE_ST7796) || defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
#elif defined(ARCH_PORTDUINO)
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
@@ -1042,7 +1045,7 @@ void setup()
// the current region name)
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || defined(USE_SPISSD1306) || defined(HACKADAY_COMMUNICATOR)
defined(USE_ST7796) || defined(USE_SPISSD1306) || defined(CO5300_CS) || defined(HACKADAY_COMMUNICATOR)
if (screen)
screen->setup();
#elif defined(ARCH_PORTDUINO)
+2 -2
View File
@@ -1006,7 +1006,7 @@ 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(ELECROW_PANEL) || defined(HELTEC_V4_TFT) || defined(T_WATCH_ULTRA) || defined(HELTEC_V4_R8_TFT)) && \
HAS_TFT
// switch BT off by default; use TFT programming mode or hotkey to enable
config.bluetooth.enabled = false;
@@ -1092,7 +1092,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.display.wake_on_tap_or_motion = true;
#endif
#if defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR)
#if defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR) || defined(T_WATCH_ULTRA)
config.display.screen_on_secs = 30;
config.display.wake_on_tap_or_motion = true;
#endif
+1 -1
View File
@@ -70,7 +70,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
if (config.position.fixed_position) {
LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true");
#ifdef T_WATCH_S3
#if defined(T_WATCH_S3) || defined(T_WATCH_ULTRA)
// Since we return early if position.fixed_position is true, set the T-Watch's RTC to the time received from the
// client device here
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
+8
View File
@@ -27,6 +27,9 @@
#ifdef HAS_STK8XXX
#include "STK8XXXSensor.h"
#endif
#ifdef HAS_BHI260AP
#include "BHI260APSensor.h"
#endif
extern ScanI2C::DeviceAddress accelerometer_found;
@@ -139,6 +142,11 @@ class AccelerometerThread : public concurrency::OSThread
case ScanI2C::DeviceType::QMA6100P:
sensor = new QMA6100PSensor(device);
break;
#endif
#ifdef HAS_BHI260AP
case ScanI2C::DeviceType::BHI260AP:
sensor = new BHI260APSensor(device);
break;
#endif
default:
disable();
+80
View File
@@ -0,0 +1,80 @@
#include "BHI260APSensor.h"
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_BHI260AP) && __has_include(<SensorBHI260AP.hpp>)
#define BOSCH_BHI260_KLIO
#define USING_DATA_HELPER
#include <BoschFirmware.h>
BHI260APSensor::BHI260APSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {}
// https://github.com/lewisxhe/SensorLib/blob/master/examples/Sensors/IMU/BHI260AP_InterruptSettings/BHI260AP_InterruptSettings.ino
bool BHI260APSensor::init()
{
LOG_WARN("Initializing BHI260AP sensor %u", deviceAddress());
sensor.setFirmware(bosch_firmware_image, bosch_firmware_size, bosch_firmware_type);
sensor.setBootFromFlash(bosch_firmware_type);
if (sensor.begin(Wire, deviceAddress())) {
sensor.setRemapAxes(SensorBHI260AP::TOP_LAYER_BOTTOM_RIGHT_CORNER);
BoschSensorInfo info = sensor.getSensorInfo();
LOG_INFO("Product ID : %02x\n", info.product_id);
LOG_INFO("Kernel version : %04u\n", info.kernel_version);
LOG_INFO("User version : %04u\n", info.user_version);
LOG_INFO("ROM version : %04u\n", info.rom_version);
LOG_INFO("Power state : %s\n", (info.host_status & BHY2_HST_POWER_STATE) ? "sleeping" : "active");
LOG_INFO("Host interface : %s\n", (info.host_status & BHY2_HST_HOST_PROTOCOL) ? "SPI" : "I2C");
LOG_INFO("Feature status : 0x%02x\n", info.feat_status);
stepCounter = new SensorStepCounter(sensor);
// stepDetector = new SensorStepDetector(sensor);
// sensor.configAccelerometer(sensor.RANGE_2G, sensor.ODR_100HZ, sensor.BW_NORMAL_AVG4, sensor.PERF_CONTINUOUS_MODE);
// sensor.enableAccelerometer();
// sensor.configInterrupt();
#ifdef BHI260AP_INT
pinMode(BHI260AP_INT, INPUT);
attachInterrupt(
BHI260AP_INT,
[] {
// Set interrupt to set irq value to true
},
RISING); // Select the interrupt mode according to the actual circuit
#endif
#ifdef T_WATCH_S3
// Need to raise the wrist function, need to set the correct axis
sensor.setRemapAxes(sensor.REMAP_TOP_LAYER_RIGHT_CORNER);
#else
// sensor.setRemapAxes(sensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
#endif
// stepDetector->enable(1.0, 0);
stepCounter->enable(1.0, 0);
LOG_DEBUG("BHI260AP init ok");
return true;
}
LOG_DEBUG("BHI260AP init failed");
return false;
}
int32_t BHI260APSensor::runOnce()
{
sensor.update();
if (stepCounter->hasUpdated()) {
steps = stepCounter->getStepCount();
LOG_WARN("Step count updated: %u", steps);
if (screen)
screen->steps = steps;
}
// LOG_WARN("Step count: %u", stepCounter->getStepCount());
// if (sensor.readIrqStatus()) {
// if (sensor.isTilt() || sensor.isDoubleTap()) {
// wakeScreen();
// return 500;
// }
//}
return 1000;
}
#endif
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#ifndef _BHI260AP_SENSOR_H_
#define _BHI260AP_SENSOR_H_
#include "MotionSensor.h"
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_BHI260AP) && __has_include(<SensorBHI260AP.hpp>)
// Sensor lib
#include <SensorBHI260AP.hpp>
#include <Wire.h>
#include <bosch/BoschSensorDataHelper.hpp>
class BHI260APSensor : public MotionSensor
{
private:
SensorBHI260AP sensor;
volatile bool BHI_IRQ = false;
SensorStepCounter *stepCounter;
SensorStepDetector *stepDetector;
uint32_t steps = 0;
public:
explicit BHI260APSensor(ScanI2C::FoundDevice foundDevice);
virtual bool init() override;
virtual int32_t runOnce() override;
};
#endif
#endif
+2
View File
@@ -206,6 +206,8 @@
#define HW_VENDOR meshtastic_HardwareModel_M5STACK_C6L
#elif defined(HELTEC_WIRELESS_TRACKER_V2)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER_V2
#elif defined(T_WATCH_ULTRA)
#define HW_VENDOR meshtastic_HardwareModel_T_WATCH_ULTRA
#elif defined(M5STACK_CARDPUTER_ADV)
#define HW_VENDOR meshtastic_HardwareModel_M5STACK_CARDPUTER_ADV
#else
@@ -0,0 +1,42 @@
// Workaround for the IDF 5.5 manual esp_flash read regression on t-watch-ultra.
//
// On this board (Winbond W25Q128JW, ef:8018), the IDF 5.5 *direct* flash read path
// (esp_flash_read / esp_partition_read) returns 0x00 for data that is physically
// correct on flash. We proved the *memory-mapped* (cache) read returns the right
// data, writes work, and it's not read-mode/HPM/timing-tuning/PSRAM. So route every
// esp_partition_read through esp_partition_mmap + memcpy, which uses the working
// cache path. Activated by `-Wl,--wrap=esp_partition_read` (t-watch-ultra only).
#if defined(T_WATCH_ULTRA)
#include "esp_partition.h"
#include <string.h>
extern esp_err_t __real_esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size);
esp_err_t __wrap_esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size)
{
if (partition == NULL || dst == NULL)
return ESP_ERR_INVALID_ARG;
if (size == 0)
return ESP_OK;
// mmap requires a 64KB-aligned start; map the containing page span and copy
// out from the requested offset.
const size_t PAGE = 0x10000;
size_t aligned = src_offset & ~(PAGE - 1);
size_t delta = src_offset - aligned;
const void *ptr = NULL;
esp_partition_mmap_handle_t handle;
esp_err_t err = esp_partition_mmap(partition, aligned, delta + size, ESP_PARTITION_MMAP_DATA, &ptr, &handle);
if (err != ESP_OK) {
// Encrypted partitions / regions mmap can't serve: fall back to the real
// read (may be wrong on this board, but better than failing the call).
return __real_esp_partition_read(partition, src_offset, dst, size);
}
memcpy(dst, (const uint8_t *)ptr + delta, size);
esp_partition_munmap(handle);
return ESP_OK;
}
#endif // T_WATCH_ULTRA
+2
View File
@@ -7,6 +7,8 @@ This directory tree is designed to solve two problems.
So we are borrowing the initVariant() ideas here (by using weak gcc references). You can now define earlyInitVariant() and lateInitVariant() if your board needs them. earlyInitVariant() runs at the beginning of setup() directly after waitUntilPowerLevelSafe(); while lateInitVariant() runs after the LoRa radio is initialized.
**Important:** earlyInitVariant() runs _before_ consoleInit(), so the logging subsystem isn't set up yet. Calling a `LOG_*` macro there **crashes the device** — it is not a silent no-op. Never use `LOG_*` in earlyInitVariant(); defer any logging to lateInitVariant() or later.
If you'd like a board specific variant to be run, add the variant.cpp file to an appropriately named
subdirectory and check for \_VARIANT_boardname in the cpp file (so that your code is only built for your board).
You'll need to define \_VARIANT_boardname in your corresponding variant.h file.
@@ -0,0 +1,83 @@
#include "configuration.h"
#ifdef T_WATCH_ULTRA
// Board-specific init lives here (rather than in variants/esp32s3/t-watch-ultra/variant.cpp)
// so that PlatformIO's library dependency finder can resolve headers such as
// input/TouchScreenImpl1.h (which transitively pulls in the ArduinoThread "Thread.h"),
// ExtensionIOXL9555.hpp and TouchDrvCSTXXX.hpp. Files compiled from outside src/ only get
// include paths for libraries they reference directly, so the transitive Thread.h include
// is not found there. See src/platform/extra_variants/README.md.
#include "TouchDrvCSTXXX.hpp"
#include "input/TouchScreenImpl1.h"
#include <ExtensionIOXL9555.hpp>
#include <Wire.h>
static ExtensionIOXL9555 io;
static TouchDrvCST92xx touchDrv;
void earlyInitVariant()
{
pinMode(LORA_CS, OUTPUT);
digitalWrite(LORA_CS, HIGH);
pinMode(DISP_CS, OUTPUT);
digitalWrite(DISP_CS, HIGH);
pinMode(SDCARD_CS, OUTPUT);
digitalWrite(SDCARD_CS, HIGH);
pinMode(NFC_CS, OUTPUT);
digitalWrite(NFC_CS, HIGH);
pinMode(I2C_SDA, INPUT_PULLUP);
pinMode(I2C_SCL, INPUT_PULLUP);
if (io.begin(Wire, XL9555_SLAVE_ADDRESS0)) {
io.pinMode(EXPANDS_DRV_EN, OUTPUT);
io.digitalWrite(EXPANDS_DRV_EN, HIGH);
delay(1);
io.pinMode(EXPANDS_DISP_EN, OUTPUT);
io.digitalWrite(EXPANDS_DISP_EN, HIGH);
delay(1);
io.pinMode(EXPANDS_TOUCH_RST, OUTPUT);
io.digitalWrite(EXPANDS_TOUCH_RST, LOW);
delay(20);
io.digitalWrite(EXPANDS_TOUCH_RST, HIGH);
delay(60);
io.pinMode(EXPANDS_LORA_RF_SW, OUTPUT);
io.digitalWrite(EXPANDS_LORA_RF_SW, HIGH); // set RF switch to built-in LoRa antenna
// io.pinMode(EXPANDS_SD_DET, INPUT);
}
// NOTE: deliberately no LOG_* on the io.begin() failure path. earlyInitVariant() runs
// before consoleInit(), where calling a LOG_* macro crashes the device (see
// extra_variants/README.md). On failure the EXPANDS_* pins stay on their defaults.
}
static bool readTouch(int16_t *x, int16_t *y)
{
int16_t x_array[1], y_array[1];
uint8_t touched = touchDrv.getPoint(x_array, y_array, 1);
if (touched > 0) {
*x = (x_array[0]);
*y = (y_array[0]);
// Check bounds
if (*x < 0 || *x >= TFT_WIDTH || *y < 0 || *y >= TFT_HEIGHT) {
return false;
}
return true; // Valid touch detected
}
return false; // No valid touch data
}
void lateInitVariant()
{
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
pinMode(SCREEN_TOUCH_INT, INPUT_PULLUP);
touchDrv.setPins(-1, SCREEN_TOUCH_INT);
if (touchDrv.begin(Wire, TOUCH_SLAVE_ADDRESS, -1, -1)) {
touchScreenImpl1 = new TouchScreenImpl1(TFT_WIDTH, TFT_HEIGHT, readTouch);
touchScreenImpl1->init();
} else {
LOG_ERROR("failed to initialize CST92xx");
}
}
}
#endif
+1 -1
View File
@@ -361,7 +361,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
// t-beam v1.2 radio power channel
PMU->disablePowerOutput(XPOWERS_ALDO2); // lora radio power channel
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE ||
HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3 || HW_VENDOR == meshtastic_HardwareModel_T_WATCH_ULTRA) {
PMU->disablePowerOutput(XPOWERS_ALDO3); // lora radio power channel
}
} else if (model == XPOWERS_AXP192) {
+1
View File
@@ -30,6 +30,7 @@
// vibration motor
#define PIN_VIBRATION 2
#define HAS_DRV2605 1
// Have SPI interface SD card slot
#define HAS_SDCARD
@@ -0,0 +1,94 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
// #ifndef digitalPinToInterrupt
// #define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1)
// #endif
#define USB_VID 0x303a
#define USB_PID 0x8227
#define USB_MANUFACTURER "LILYGO"
#define USB_PRODUCT "T-Watch-Ultra"
#define DISP_WIDTH 502
#define DISP_HEIGHT 410
// QSPI interface display
#define DISP_D0 (38)
#define DISP_D1 (39)
#define DISP_D2 (42)
#define DISP_D3 (45)
#define DISP_SCK (40)
#define DISP_CS (41)
#define DISP_RST (37)
#define DISP_TE (6)
// Interrupt IO port
#define TP_INT (12)
#define RTC_INT (1)
#define PMU_INT (7)
#define NFC_INT (5)
#define SENSOR_INT (8)
#define NFC_CS (4)
// PDM microphone
#define MIC_SCK (17)
#define MIC_DAT (18)
// MAX98357A
#define I2S_BCLK (9)
#define I2S_WCLK (10)
#define I2S_DOUT (11)
#define SD_CS (21)
// TX, RX pin connected to GPS
static const uint8_t TX = 43;
static const uint8_t RX = 44;
// BHI260,PCF85063,AXP2101,DRV2605L share I2C Bus
static const uint8_t SDA = 3;
static const uint8_t SCL = 2;
// Default sd cs pin
static const uint8_t SS = SD_CS;
static const uint8_t MOSI = 34;
static const uint8_t MISO = 33;
static const uint8_t SCK = 35;
#define GPS_TX (TX)
#define GPS_RX (RX)
#define GPS_PPS (13)
#define TP_SDA (SDA)
#define TP_SCL (SCL)
// LoRa and SD card share SPI bus -> variant.h
// #define LORA_SCK (SCK) // share spi bus
// #define LORA_MISO (MISO) // share spi bus
// #define LORA_MOSI (MOSI) // share spi bus
// #define LORA_CS (36)
// #define LORA_RST (47)
// #define LORA_BUSY (48)
// #define LORA_IRQ (14)
// External expansion chip IO definition
#define EXPANDS_DRV_EN (6)
#define EXPANDS_DISP_EN (7)
#define EXPANDS_TOUCH_RST (8)
#define EXPANDS_SD_DET (10)
#define EXPANDS_LORA_RF_SW (11)
// Peripheral definition exists
#define USING_XL9555_EXPANDS
#define USING_PCM_AMPLIFIER
#define USING_PDM_MICROPHONE
#define USING_PMU_MANAGE
#define USING_INPUT_DEV_TOUCHPAD
#define USING_ST25R3916
#define USING_BHI260_SENSOR
#define HAS_SD_CARD_SOCKET
#endif /* Pins_Arduino_h */
@@ -0,0 +1,77 @@
; LilyGo T-Watch S3
[env:t-watch-ultra]
extends = esp32s3_base
board = t-watch-ultra
board_check = true
board_build.partitions = default_16MB.csv
upload_protocol = esptool
custom_sdkconfig =
${esp32s3_base.custom_sdkconfig}
; Keep esp_littlefs buffers in internal RAM (off the shared-bus PSRAM).
CONFIG_LITTLEFS_MALLOC_STRATEGY_INTERNAL=y
CONFIG_SPI_FLASH_SHARE_SPI1_BUS=y
build_flags = ${esp32_base.build_flags} -Ivariants/esp32s3/t-watch-ultra
; Route flash reads through the cache/mmap path (esp_partition_read_mmap_wrap.c)
; to dodge the IDF 5.5 manual-read regression on this board's flash.
-Wl,--wrap=esp_partition_read
-D T_WATCH_ULTRA
-D RADIOLIB_EXCLUDE_SX128X=1
-D RADIOLIB_EXCLUDE_SX127X=1
-D RADIOLIB_EXCLUDE_LR11X0=1
-UMESHTASTIC_EXCLUDE_ACCELEROMETER
; -D HAS_SDCARD
-D SDCARD_CS=21
; -DHAS_BMA423=1
build_src_filter =
${esp32s3_base.build_src_filter}
+<../variants/esp32s3/t-watch-ultra>
lib_deps = ${esp32s3_base.lib_deps}
https://github.com/lovyan03/LovyanGFX/archive/tags/1.2.19.zip
adafruit/Adafruit DRV2605 Library@^1.2.4
earlephilhower/ESP8266Audio@^1.9.9
earlephilhower/ESP8266SAM@^1.0.1
lewisxhe/SensorLib@0.3.1
[env:t-watch-ultra-tft]
board_level = extra
extends = env:t-watch-ultra
build_flags =
${env:t-watch-ultra.build_flags}
-D CONFIG_DISABLE_HAL_LOCKS=1
-D INPUTDRIVER_BUTTON_TYPE=0
-D HAS_SCREEN=1
-D HAS_TFT=1
-D USE_I2S_BUZZER
-D RAM_SIZE=5120
-D LV_LVGL_H_INCLUDE_SIMPLE
-D LV_CONF_INCLUDE_SIMPLE
-D LV_COMP_CONF_INCLUDE_SIMPLE
-D LV_USE_SYSMON=0
-D LV_USE_PROFILER=0
-D LV_USE_PERF_MONITOR=0
-D LV_USE_MEM_MONITOR=0
-D LV_USE_LOG=0
-D USE_LOG_DEBUG
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
-D RADIOLIB_SPI_PARANOID=0
-D LGFX_SCREEN_WIDTH=410
-D LGFX_SCREEN_HEIGHT=502
-D LGFX_AMOLED_ROUNDER=1
-D LGFX_BUFSIZE=308732
-D DISPLAY_SIZE=410x502 ; portrait mode
-D DISPLAY_SET_RESOLUTION
-D LGFX_DRIVER=LGFX_TWATCH_ULTRA
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_T_WATCH_ULTRA.h\"
; -D LVGL_DRIVER=LVGL_T_WATCH_ULTRA
-D VIEW_320x240
-D USE_PACKET_API
-D MAP_FULL_REDRAW
-D CUSTOM_TOUCH_DRIVER
lib_deps =
${env:t-watch-ultra.lib_deps}
${device-ui_base.lib_deps}
+100
View File
@@ -0,0 +1,100 @@
// CO5300 TFT AMOLED
#define CO5300_CS 41
#define CO5300_SCK 40
#define CO5300_RESET 37
#define CO5300_TE 6
#define CO5300_IO0 38
#define CO5300_IO1 39
#define CO5300_IO2 42
#define CO5300_IO3 45
#define CO5300_SPI_HOST SPI2_HOST
#define SPI_FREQUENCY 75000000
#define SPI_READ_FREQUENCY 16000000 // irrelevant
#define TFT_HEIGHT 502
#define TFT_WIDTH 410
#define TFT_OFFSET_X 22
#define TFT_OFFSET_Y 0
#define TFT_OFFSET_ROTATION 0
#define SCREEN_TRANSITION_FRAMERATE 5 // fps
#define USE_TFTDISPLAY 1
#define HAS_SCREEN 1
#define TFT_RESET_AFTER_SLEEP
#define HAS_TOUCHSCREEN 1
#define VARIANT_TOUCHSCREEN 1
#define SCREEN_TOUCH_INT 12
#define TOUCH_I2C_PORT 0
#define TOUCH_SLAVE_ADDRESS 0x1A
#define WAKE_ON_TOUCH
#define BUTTON_PIN 0
#define USE_POWERSAVE
#define SLEEP_TIME 120
// External expansion chip XL9555
#define USE_XL9555
// PCF85063 RTC Module
#define PCF85063_RTC 0x51
#define HAS_RTC 1
// MAX98357A
#define HAS_I2S
#define DAC_I2S_BCK 9
#define DAC_I2S_WS 10
#define DAC_I2S_DOUT 11
#define DAC_I2S_MCLK -1 // TODO
#define HAS_AXP2101
#define PMU_IRQ 7
#define PMU_POWER_BUTTON_IS_CANCEL
#define HAS_DRV2605 1
#define HAS_BHI260AP
#define BHI260AP_INT 8
#undef MESHTASTIC_EXCLUDE_ACCELEROMETER
#define SHOW_STEP_COUNTER
#define I2C_SDA 3
#define I2C_SCL 2
#define I2C_NO_RESCAN
#define HAS_GPS 1
#define GPS_BAUDRATE 38400
#define GPS_RX_PIN 44
#define GPS_TX_PIN 43
#define PIN_GPS_PPS 13
// SPI interface SD card slot
#define SPI_MOSI MOSI
#define SPI_SCK SCK
#define SPI_MISO MISO
#define SPI_CS 21
#define SD_SPI_FREQUENCY 75000000U
#define USE_SX1262
// #define USE_SX1280
#define HW_SPI1_DEVICE
#define LORA_SCK 35
#define LORA_MISO 33
#define LORA_MOSI 34
#define LORA_CS 36
#define LORA_DIO0 -1 // a No connect on the SX1262 module
#define LORA_RESET 47
#define LORA_DIO1 14 // SX1262 IRQ
#define LORA_DIO2 48 // SX1262 BUSY
#define LORA_DIO3
#define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define USE_VIRTUAL_KEYBOARD 1
#define DISPLAY_CLOCK_FRAME 1
+3
View File
@@ -69,6 +69,9 @@
#define DAC_I2S_DIN 17
#define DAC_I2S_MCLK 10
// haptic driver
#define HAS_DRV2605 1
// gyroscope BHI260AP
#define HAS_BHI260AP