T-Watch Ultra support (#8171)

* feat: T-Watch Ultra support

* fix init touch controller

* add framebuffer

* update to device-ui

* trunk fmt

* update amoled driver reference

* PMU cosmetics

* power off lora

* fix NodeDB defaults

* trySetRTC when fixedPosition

* haptic touch (only BaseUI)

* init lora RF switch

* update LovyanGFX 1.2.19

* earlyInitVariant() adaptations acc. #9438

* update device-ui / touch handling

* Set NFC_CS disabled on boot

* Get t-watch-ultra working better on BaseUI

* Fix compilation

* Fix flash reads on t-watch-ultra

* Get baseui drawing to the screen correctly again on t-watch and add touch IRQ handling

* Add PMU IRQ handling

* Add IMU support

* Change define to avoid collision

* BaseUI changes to support t-watch-s3 rounded screen (#10786)

* BaseUI changes to support t-watch-s3 rounded screen

* Extend margin work to CannedMessages

* Finish merge

* Get audio working on watch-ultra

* trunk fmt

* added custom_meshtastic boilerplate

* T-Echo-Plus: disable BHI260AP while assumingly not implemented

* Drop the duplicate origBold declaration from the merge

* Inset incoming message bubbles on rounded screens

* Fix RTTTL tempo, WiFi screen margins, PMU guard and a duplicate define

* fix compile errror (the 2nd time)

* fix SDcard

* fix/workaround CO5300 pixel flush to SPI

* trunk fmt

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
This commit is contained in:
Manuel
2026-08-20 12:28:57 +00:00
committed by GitHub
co-authored by Jonathan Bennett Thomas Göttgens
parent 389559bddb
commit 0b906b4d15
35 changed files with 1060 additions and 166 deletions
+22 -19
View File
@@ -62,20 +62,17 @@ const int DURATION_1_1 = 1000; // 1/1 note
#ifdef HAS_I2S
void playTonesRTTTL(const ToneDuration *tone_durations, int size)
{
// translate ToneDuration[] to RTTTL string and play using audioThread
static std::unordered_map<int, std::string> freqToNote = {
{NOTE_C3, "c4"}, {NOTE_CS3, "c#4"}, {NOTE_D3, "d4"}, {NOTE_DS3, "d#4"}, {NOTE_E3, "e4"}, {NOTE_F3, "f4"},
{NOTE_FS3, "f#4"}, {NOTE_G3, "g4"}, {NOTE_GS3, "g#4"}, {NOTE_A3, "a4"}, {NOTE_AS3, "a#4"}, {NOTE_B3, "b4"},
{NOTE_C4, "c5"}, {NOTE_E4, "e5"}, {NOTE_G4, "g5"}, {NOTE_A4, "a5"}, {NOTE_C5, "c6"}, {NOTE_E5, "e6"},
{NOTE_G5, "g6"}, {NOTE_F5, "f6"}, {NOTE_G6, "g7"}, {NOTE_E7, "e8"}};
// translate ToneDuration[] to a single RTTTL string and play it via audioThread
static std::unordered_map<int, const char *> freqToNote = {
{NOTE_SILENT, "p"}, // rest
{NOTE_C3, "c4"}, {NOTE_CS3, "c#4"}, {NOTE_D3, "d4"}, {NOTE_DS3, "d#4"}, {NOTE_E3, "e4"}, {NOTE_F3, "f4"},
{NOTE_FS3, "f#4"}, {NOTE_G3, "g4"}, {NOTE_GS3, "g#4"}, {NOTE_A3, "a4"}, {NOTE_AS3, "a#4"}, {NOTE_B3, "b4"},
{NOTE_C4, "c5"}, {NOTE_CS4, "c#5"}, {NOTE_E4, "e5"}, {NOTE_G4, "g5"}, {NOTE_A4, "a5"}, {NOTE_B4, "b5"},
{NOTE_C5, "c6"}, {NOTE_E5, "e6"}, {NOTE_G5, "g6"}, {NOTE_F5, "f6"}, {NOTE_G6, "g7"}, {NOTE_E7, "e8"}};
char rtttl[128] = "tone:d=32,o=4,b=200:"; // default duration and octave
char rtttl[128] = "tone:d=32,o=4,b=240:"; // b=240 makes 240000/(bpm*d) match the ms durations above
for (int i = 0; i < size; i++) {
const auto &td = tone_durations[i];
std::string note = "b4";
if (freqToNote.find(td.frequency_khz) != freqToNote.end()) {
note = freqToNote[td.frequency_khz];
}
int dur = 32; // default duration
if (td.duration_ms >= 1000)
dur = 1;
@@ -90,16 +87,22 @@ void playTonesRTTTL(const ToneDuration *tone_durations, int size)
else
dur = 32;
char noteStr[64];
snprintf(noteStr, sizeof(noteStr), "%s,%d", note.c_str(), dur);
strncat(rtttl, noteStr, sizeof(rtttl) - strlen(rtttl) - 1);
auto it = freqToNote.find(td.frequency_khz);
const char *note = (it != freqToNote.end()) ? it->second : "p"; // unknown freq -> rest
audioThread->beginRttl(rtttl, strlen(rtttl));
while (audioThread->isPlaying()) {
delay(10);
}
return;
// RTTTL grammar puts duration before the note; notes are comma-separated
char noteStr[64];
snprintf(noteStr, sizeof(noteStr), "%s%d%s", i ? "," : "", dur, note);
strncat(rtttl, noteStr, sizeof(rtttl) - strlen(rtttl) - 1);
}
// trailing rest flushes the last note out of the I2S DMA buffer before teardown
strncat(rtttl, ",32p", sizeof(rtttl) - strlen(rtttl) - 1);
audioThread->beginRttl(rtttl, strlen(rtttl));
while (audioThread->isPlaying()) {
delay(10);
}
audioThread->stop(); // release I2S so the amp goes silent instead of looping the last buffer
}
#endif