mirror of
https://github.com/pelgraine/Meck.git
synced 2026-05-03 12:02:37 +02:00
initial screen based t-echo lite with card kb support build
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
|
||||
DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(nullptr), _clock(&clock),
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
identity_store(fs, "")
|
||||
identity_store(fs, ""),
|
||||
_saveFile(fs)
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
identity_store(fs, "/identity")
|
||||
#else
|
||||
@@ -21,7 +22,8 @@ DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _fsExtra
|
||||
#if defined(EXTRAFS) || defined(QSPIFLASH)
|
||||
DataStore::DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(&fsExtra), _clock(&clock),
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
identity_store(fs, "")
|
||||
identity_store(fs, ""),
|
||||
_saveFile(fs)
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
identity_store(fs, "/identity")
|
||||
#else
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
#define FIRMWARE_VER_CODE 11
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "19 April 2026"
|
||||
#define FIRMWARE_BUILD_DATE "21 April 2026"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "Meck v1.7"
|
||||
#define FIRMWARE_VERSION "Meck v1.7.1"
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <Arduino.h> // needed for PlatformIO
|
||||
#ifdef BLE_PIN_CODE
|
||||
#if defined(ESP32) && defined(BLE_PIN_CODE)
|
||||
#include <esp_bt.h> // for esp_bt_controller_mem_release (web reader WiFi)
|
||||
#endif
|
||||
#ifdef MECK_OTA_UPDATE
|
||||
#if defined(ESP32) && defined(MECK_OTA_UPDATE)
|
||||
#include <esp_ota_ops.h>
|
||||
#endif
|
||||
#include <Mesh.h>
|
||||
@@ -890,11 +890,37 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- T-Echo Lite: CardKB keyboard, GxEPD2 e-ink, no touch ---
|
||||
#if defined(LILYGO_TECHO_LITE)
|
||||
#include "ContactsScreen.h"
|
||||
#include "ChannelScreen.h"
|
||||
#include "ChannelPickerScreen.h"
|
||||
#include "SettingsScreen.h"
|
||||
#include "RepeaterAdminScreen.h"
|
||||
#include "DiscoveryScreen.h"
|
||||
#include "LastHeardScreen.h"
|
||||
#include "PathEditorScreen.h"
|
||||
|
||||
#ifdef MECK_CARDKB
|
||||
#include "CardKBKeyboard.h"
|
||||
static CardKBKeyboard cardkb;
|
||||
static unsigned long lastCardKBProbe = 0;
|
||||
#define CARDKB_PROBE_INTERVAL_MS 5000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Board-agnostic: CPU frequency scaling and AGC reset
|
||||
CPUPowerManager cpuPower;
|
||||
#define AGC_RESET_INTERVAL_MS 500
|
||||
static unsigned long lastAGCReset = 0;
|
||||
|
||||
// nRF52 RAM diagnostic
|
||||
extern "C" char *sbrk(int incr);
|
||||
static int dbg_free_ram() {
|
||||
char top;
|
||||
return &top - reinterpret_cast<char*>(sbrk(0));
|
||||
}
|
||||
|
||||
// Believe it or not, this std C function is busted on some platforms!
|
||||
static uint32_t _atoi(const char* sp) {
|
||||
uint32_t n = 0;
|
||||
@@ -2089,9 +2115,11 @@ void setup() {
|
||||
#endif
|
||||
|
||||
// Initialize CardKB external keyboard (if connected via QWIIC)
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro) && defined(MECK_CARDKB)
|
||||
#if defined(MECK_CARDKB)
|
||||
if (cardkb.begin()) {
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.setCardKBDetected(true);
|
||||
#endif
|
||||
Serial.println("setup() - CardKB detected at 0x5F");
|
||||
} else {
|
||||
Serial.println("setup() - CardKB not detected (will re-probe)");
|
||||
@@ -2317,8 +2345,10 @@ void setup() {
|
||||
the_mesh.setVoiceEnvelopeHandler(voiceEnvelopeCallback);
|
||||
#endif
|
||||
|
||||
Serial.printf("setup() complete — free heap: %d, largest block: %d\n",
|
||||
#ifdef ESP32
|
||||
Serial.printf("setup() complete - free heap: %d, largest block: %d\n",
|
||||
ESP.getFreeHeap(), ESP.getMaxAllocHeap());
|
||||
#endif
|
||||
MESH_DEBUG_PRINTLN("=== setup() - COMPLETE ===");
|
||||
}
|
||||
|
||||
@@ -2851,6 +2881,17 @@ void loop() {
|
||||
#endif
|
||||
#endif
|
||||
rtc_clock.tick();
|
||||
|
||||
// --- T-Echo Lite runtime diagnostic (remove after debugging) ---
|
||||
{
|
||||
static unsigned long lastDbg = 0;
|
||||
if (millis() - lastDbg > 2000) {
|
||||
Serial.printf("loop alive - free RAM: %d, screen: %s\n",
|
||||
dbg_free_ram(),
|
||||
ui_task.isOnHomeScreen() ? "home" : "other");
|
||||
lastDbg = millis();
|
||||
}
|
||||
}
|
||||
// Periodic AGC reset - re-assert boosted RX gain to prevent sensitivity drift
|
||||
#ifdef MECK_OTA_UPDATE
|
||||
if (!otaRadioPaused)
|
||||
@@ -3130,12 +3171,12 @@ void loop() {
|
||||
#endif // MECK_TOUCH_ENABLED
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CardKB external keyboard polling (T5S3 only, via QWIIC)
|
||||
// CardKB external keyboard polling (via QWIIC)
|
||||
// When VKB is active: typed characters feed into the VKB text buffer.
|
||||
// When VKB is not active: navigation keys route through injectKey().
|
||||
// ESC key maps to 'q' (back) when no VKB is active.
|
||||
// ---------------------------------------------------------------------------
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro) && defined(MECK_CARDKB)
|
||||
#if defined(MECK_CARDKB)
|
||||
{
|
||||
// Hot-plug detection: re-probe periodically
|
||||
if (millis() - lastCardKBProbe >= CARDKB_PROBE_INTERVAL_MS) {
|
||||
@@ -3143,7 +3184,9 @@ void loop() {
|
||||
bool wasDetected = cardkb.isDetected();
|
||||
bool nowDetected = cardkb.probe();
|
||||
if (nowDetected != wasDetected) {
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.setCardKBDetected(nowDetected);
|
||||
#endif
|
||||
Serial.printf("[CardKB] %s\n", nowDetected ? "Connected" : "Disconnected");
|
||||
}
|
||||
}
|
||||
@@ -3151,15 +3194,22 @@ void loop() {
|
||||
// Poll for keypress
|
||||
char ckb = cardkb.readKey();
|
||||
if (ckb != 0) {
|
||||
// Block input while locked (same as touch)
|
||||
// Block input while locked (T5S3 only — T-Echo Lite has no lock screen yet)
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
if (!ui_task.isLocked()) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
cpuPower.setBoost();
|
||||
ui_task.keepAlive();
|
||||
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
if (ui_task.isVKBActive()) {
|
||||
// VKB is open — feed character into VKB text buffer
|
||||
ui_task.feedCardKBChar(ckb);
|
||||
} else if (ui_task.isOnHomeScreen()) {
|
||||
} else
|
||||
#endif
|
||||
if (ui_task.isOnHomeScreen()) {
|
||||
// Home screen: ESC does nothing special, letter shortcuts open tiles
|
||||
if (ckb == 0x1B) {
|
||||
// ESC on home — no-op (already home)
|
||||
@@ -3167,8 +3217,10 @@ void loop() {
|
||||
switch (ckb) {
|
||||
case 'm': ui_task.gotoChannelPickerScreen(); break;
|
||||
case 'c': ui_task.gotoContactsScreen(); break;
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
case 'e': ui_task.gotoTextReader(); break;
|
||||
case 'n': ui_task.gotoNotesScreen(); break;
|
||||
#endif
|
||||
case 's': ui_task.gotoSettingsScreen(); break;
|
||||
case 'f': ui_task.gotoDiscoveryScreen(); break;
|
||||
case 'h': ui_task.gotoLastHeardScreen(); break;
|
||||
@@ -3187,6 +3239,7 @@ void loop() {
|
||||
|
||||
// Notes editing/renaming: route ALL keys directly (no VKB).
|
||||
// This gives: Enter=newline, arrows=cursor, printable=insert, ESC=save&exit
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
if (ui_task.isOnNotesScreen()) {
|
||||
NotesScreen* notesScr = (NotesScreen*)ui_task.getNotesScreen();
|
||||
if (notesScr && (notesScr->isEditing() || notesScr->isRenaming())) {
|
||||
@@ -3214,6 +3267,7 @@ void loop() {
|
||||
ui_task.forceRefresh();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!handled) {
|
||||
// ESC → back (same as 'q' on T-Deck Pro) for all non-notes screens
|
||||
@@ -3253,7 +3307,11 @@ void loop() {
|
||||
if (the_mesh.getContactByIdx(j, ci) && strcmp(ci.name, dmName) == 0) {
|
||||
char label[40];
|
||||
snprintf(label, sizeof(label), "DM: %s", dmName);
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.showVirtualKeyboard(VKB_DM, label, "", 137, j);
|
||||
#else
|
||||
ui_task.injectKey('\r'); // T-Echo Lite: compose via native handler
|
||||
#endif
|
||||
ui_task.clearDMUnread(j);
|
||||
break;
|
||||
}
|
||||
@@ -3266,7 +3324,11 @@ void loop() {
|
||||
if (the_mesh.getChannel(chIdx, ch)) {
|
||||
char label[40];
|
||||
snprintf(label, sizeof(label), "To: %s", ch.name);
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.showVirtualKeyboard(VKB_CHANNEL_MSG, label, "", 137, chIdx);
|
||||
#else
|
||||
ui_task.injectKey('\r'); // T-Echo Lite: compose via native handler
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if (ui_task.isOnContactsScreen()) {
|
||||
@@ -3290,7 +3352,11 @@ void loop() {
|
||||
cs->getSelectedContactName(dname, sizeof(dname));
|
||||
char label[40];
|
||||
snprintf(label, sizeof(label), "DM: %s", dname);
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.showVirtualKeyboard(VKB_DM, label, "", 137, idx);
|
||||
#else
|
||||
ui_task.injectKey('\r'); // T-Echo Lite: compose via native handler
|
||||
#endif
|
||||
}
|
||||
} else if (idx >= 0 && ctype == ADV_TYPE_REPEATER) {
|
||||
ui_task.gotoRepeaterAdmin(idx);
|
||||
@@ -3310,9 +3376,17 @@ void loop() {
|
||||
if (admin) {
|
||||
RepeaterAdminScreen::AdminState astate = admin->getState();
|
||||
if (astate == RepeaterAdminScreen::STATE_PASSWORD_ENTRY) {
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.showVirtualKeyboard(VKB_ADMIN_PASSWORD, "Admin Password", "", 32);
|
||||
#else
|
||||
ui_task.injectKey('\r');
|
||||
#endif
|
||||
} else {
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
ui_task.showVirtualKeyboard(VKB_ADMIN_CLI, "Admin Command", "", 137);
|
||||
#else
|
||||
ui_task.injectKey('\r');
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if (ui_task.isOnPathEditor()) {
|
||||
|
||||
@@ -5,19 +5,22 @@
|
||||
// Polls 0x5F on the shared I2C bus via QWIIC connector.
|
||||
// Maps CardKB special key codes to Meck key constants.
|
||||
//
|
||||
// Platform support:
|
||||
// - ESP32/ESP32-S3 (T5S3, T-Deck Pro): Wire.begin(SDA, SCL)
|
||||
// - nRF52840 (T-Echo Lite): Wire.begin() uses variant.h pins
|
||||
//
|
||||
// Usage:
|
||||
// CardKBKeyboard cardkb;
|
||||
// if (cardkb.begin()) { /* detected */ }
|
||||
// char key = cardkb.readKey(); // returns 0 if no key
|
||||
// =============================================================================
|
||||
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro) && defined(MECK_CARDKB)
|
||||
#if defined(MECK_CARDKB)
|
||||
#ifndef CARDKB_KEYBOARD_H
|
||||
#define CARDKB_KEYBOARD_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "variant.h" // For I2C_SDA, I2C_SCL (bus recovery)
|
||||
|
||||
// I2C address (defined in variant.h, fallback here)
|
||||
#ifndef CARDKB_I2C_ADDR
|
||||
@@ -75,7 +78,13 @@ public:
|
||||
_errorCount++;
|
||||
if (_errorCount >= 3) {
|
||||
// I2C bus may be stuck — re-init to recover
|
||||
#if defined(ESP32)
|
||||
// ESP32: Wire.begin() accepts explicit SDA/SCL pins
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
#else
|
||||
// nRF52: Wire.begin() uses PIN_WIRE_SDA/SCL from variant.h
|
||||
Wire.begin();
|
||||
#endif
|
||||
Wire.setClock(100000);
|
||||
_pollInterval = 500; // Back off for 500ms
|
||||
_errorCount = 0;
|
||||
@@ -119,4 +128,4 @@ private:
|
||||
};
|
||||
|
||||
#endif // CARDKB_KEYBOARD_H
|
||||
#endif // LilyGo_T5S3_EPaper_Pro && MECK_CARDKB
|
||||
#endif // MECK_CARDKB
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "UITask.h"
|
||||
#include <helpers/TxtDataHelpers.h>
|
||||
#include "../MyMesh.h"
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
#include "NotesScreen.h"
|
||||
#endif
|
||||
#include "RepeaterAdminScreen.h"
|
||||
#include "PathEditorScreen.h"
|
||||
#include "DiscoveryScreen.h"
|
||||
@@ -56,7 +58,9 @@
|
||||
#include "ChannelScreen.h"
|
||||
#include "ChannelPickerScreen.h"
|
||||
#include "ContactsScreen.h"
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
#include "TextReaderScreen.h"
|
||||
#endif
|
||||
#include "SettingsScreen.h"
|
||||
#ifdef MECK_AUDIO_VARIANT
|
||||
#include "AudiobookPlayerScreen.h"
|
||||
@@ -1300,8 +1304,13 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
((ChannelPickerScreen*)channel_picker_screen)->setChannelScreen((ChannelScreen*)channel_screen);
|
||||
contacts_screen = new ContactsScreen(this, &rtc_clock);
|
||||
((ContactsScreen*)contacts_screen)->setDMUnreadPtr(_dmUnread);
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
text_reader = new TextReaderScreen(this, node_prefs);
|
||||
notes_screen = new NotesScreen(this, node_prefs);
|
||||
#else
|
||||
text_reader = nullptr; // T-Echo Lite: excluded to save RAM (256KB nRF52)
|
||||
notes_screen = nullptr;
|
||||
#endif
|
||||
settings_screen = new SettingsScreen(this, &rtc_clock, node_prefs);
|
||||
repeater_admin = nullptr; // Lazy-initialized on first use to preserve heap for audio
|
||||
path_editor = nullptr; // Lazy-initialized on first use from contacts screen
|
||||
@@ -1678,6 +1687,7 @@ void UITask::loop() {
|
||||
c = checkDisplayOn(KEY_NEXT);
|
||||
} else {
|
||||
// Navigate back: reader reading→file list, file list→home, others→home
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
if (isOnTextReader()) {
|
||||
TextReaderScreen* reader = (TextReaderScreen*)text_reader;
|
||||
if (reader && reader->isReading()) {
|
||||
@@ -1695,7 +1705,9 @@ void UITask::loop() {
|
||||
gotoHomeScreen();
|
||||
}
|
||||
c = 0;
|
||||
} else if (isOnChannelPickerScreen()) {
|
||||
} else
|
||||
#endif
|
||||
if (isOnChannelPickerScreen()) {
|
||||
gotoHomeScreen(); // picker → home
|
||||
c = 0;
|
||||
} else if (isOnChannelScreen()) {
|
||||
@@ -2336,12 +2348,14 @@ void UITask::onVKBSubmit() {
|
||||
break;
|
||||
}
|
||||
case VKB_NOTES: {
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
NotesScreen* notes = (NotesScreen*)getNotesScreen();
|
||||
if (notes && strlen(text) > 0) {
|
||||
for (int i = 0; text[i]; i++) {
|
||||
notes->handleInput(text[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_screenBeforeVKB) setCurrScreen(_screenBeforeVKB);
|
||||
break;
|
||||
}
|
||||
@@ -2403,6 +2417,7 @@ void UITask::onVKBSubmit() {
|
||||
}
|
||||
#endif
|
||||
case VKB_TEXT_PAGE: {
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
if (strlen(text) > 0) {
|
||||
int pageNum = atoi(text);
|
||||
TextReaderScreen* reader = (TextReaderScreen*)getTextReaderScreen();
|
||||
@@ -2410,6 +2425,7 @@ void UITask::onVKBSubmit() {
|
||||
reader->gotoPage(pageNum);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_screenBeforeVKB) setCurrScreen(_screenBeforeVKB);
|
||||
break;
|
||||
}
|
||||
@@ -2622,6 +2638,8 @@ void UITask::gotoContactsScreen() {
|
||||
}
|
||||
|
||||
void UITask::gotoTextReader() {
|
||||
if (!text_reader) return; // Not available on this platform
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
TextReaderScreen* reader = (TextReaderScreen*)text_reader;
|
||||
if (_display != NULL) {
|
||||
reader->enter(*_display);
|
||||
@@ -2632,9 +2650,12 @@ void UITask::gotoTextReader() {
|
||||
}
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||
_next_refresh = 100;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::gotoNotesScreen() {
|
||||
if (!notes_screen) return; // Not available on this platform
|
||||
#if !defined(LILYGO_TECHO_LITE)
|
||||
NotesScreen* notes = (NotesScreen*)notes_screen;
|
||||
if (_display != NULL) {
|
||||
notes->enter(*_display);
|
||||
@@ -2649,6 +2670,7 @@ void UITask::gotoNotesScreen() {
|
||||
}
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||
_next_refresh = 100;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::gotoSettingsScreen() {
|
||||
|
||||
66
patch_nrf52_bsp.py
Normal file
66
patch_nrf52_bsp.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
patch_nrf52_bsp.py — Pre-build BSP patches for nRF52 Meck builds
|
||||
|
||||
Patches the Adafruit nRF52 BSP's LittleFS File class to add a default
|
||||
constructor. BSP 1.7.0 removed the default File() constructor, but the
|
||||
Meck screen headers (NotesScreen, TextReaderScreen, EpubZipReader) have
|
||||
File member variables that need default construction.
|
||||
|
||||
Runs automatically before each build via extra_scripts in platformio.ini.
|
||||
Idempotent — safe to run repeatedly.
|
||||
"""
|
||||
Import("env")
|
||||
import os
|
||||
|
||||
framework_dir = env.PioPlatform().get_package_dir("framework-arduinoadafruitnrf52")
|
||||
lfs_src = os.path.join(framework_dir, "libraries", "Adafruit_LittleFS", "src")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 1. Patch header: add File() default constructor declaration
|
||||
# -------------------------------------------------------------------------
|
||||
header_path = os.path.join(lfs_src, "Adafruit_LittleFS_File.h")
|
||||
|
||||
with open(header_path, "r") as f:
|
||||
h = f.read()
|
||||
|
||||
if "File ();" not in h and "File();" not in h:
|
||||
h = h.replace(
|
||||
"File (Adafruit_LittleFS &fs);",
|
||||
"File (Adafruit_LittleFS &fs);\n File (); // Meck nRF52 compat"
|
||||
)
|
||||
with open(header_path, "w") as f:
|
||||
f.write(h)
|
||||
print("LittleFS File patch: Added default constructor declaration")
|
||||
else:
|
||||
print("LittleFS File patch: OK - header already patched")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 2. Patch source: add File() default constructor implementation
|
||||
# Uses C++11 delegating constructor → File(InternalFS)
|
||||
# -------------------------------------------------------------------------
|
||||
source_path = os.path.join(lfs_src, "Adafruit_LittleFS_File.cpp")
|
||||
|
||||
with open(source_path, "r") as f:
|
||||
s = f.read()
|
||||
|
||||
if "File::File()" not in s:
|
||||
# Locate InternalFileSystem header (Adafruit BSP has a known typo: "Sytem")
|
||||
ifs_include = None
|
||||
for dirname in ["InternalFileSytem", "InternalFileSystem"]:
|
||||
candidate = os.path.join(framework_dir, "libraries", dirname, "src")
|
||||
if os.path.isdir(candidate):
|
||||
rel = os.path.relpath(candidate, lfs_src).replace("\\", "/")
|
||||
ifs_include = rel + "/InternalFileSystem.h"
|
||||
break
|
||||
|
||||
if ifs_include:
|
||||
s += "\n// Meck nRF52 compat: default File() constructor\n"
|
||||
s += '#include "' + ifs_include + '"\n'
|
||||
s += "File::File() : File(InternalFS) {}\n"
|
||||
with open(source_path, "w") as f:
|
||||
f.write(s)
|
||||
print("LittleFS File patch: Added default constructor implementation")
|
||||
else:
|
||||
print("LittleFS File patch: WARNING - could not find InternalFileSystem")
|
||||
else:
|
||||
print("LittleFS File patch: OK - source already patched")
|
||||
@@ -130,7 +130,7 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
|
||||
snprintf(charpin, sizeof(charpin), "%lu", (unsigned long)pin_code);
|
||||
|
||||
// If we want to control BLE LED ourselves, uncomment this:
|
||||
// Bluefruit.autoConnLed(false);
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ bool GxEPDDisplay::begin() {
|
||||
// Tell GxEPD2 to use our SPI instance
|
||||
// Using slower speed (4MHz) for reliable e-ink communication
|
||||
display.epd2.selectSPI(displaySpi, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
#elif defined(LILYGO_TECHO)
|
||||
// T-Echo Lite: display on SPI1 (pins 19/20), LoRa on SPI (pins 13/15/17)
|
||||
SPI1.begin();
|
||||
display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
#endif
|
||||
|
||||
// Initialize with:
|
||||
@@ -35,10 +39,15 @@ bool GxEPDDisplay::begin() {
|
||||
display.init(115200, true, 2, false);
|
||||
display.setRotation(DISPLAY_ROTATION);
|
||||
setTextSize(1); // Default to size 1
|
||||
#ifdef EINK_FULL_REFRESH_ONLY
|
||||
display.setFullWindow();
|
||||
display.fillScreen(GxEPD_WHITE);
|
||||
display.display(false); // Full refresh (SSD1681 doesn't support partial)
|
||||
#else
|
||||
display.setPartialWindow(0, 0, display.width(), display.height());
|
||||
|
||||
display.fillScreen(GxEPD_WHITE);
|
||||
display.display(true);
|
||||
#endif
|
||||
|
||||
#if DISP_BACKLIGHT
|
||||
digitalWrite(DISP_BACKLIGHT, LOW);
|
||||
@@ -238,7 +247,11 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
|
||||
void GxEPDDisplay::endFrame() {
|
||||
uint32_t crc = display_crc.finalize();
|
||||
if (crc != last_display_crc_value) {
|
||||
display.display(true); // Partial refresh
|
||||
#ifdef EINK_FULL_REFRESH_ONLY
|
||||
display.display(false); // Full refresh (SSD1681 doesn't support partial)
|
||||
#else
|
||||
display.display(true); // Partial refresh
|
||||
#endif
|
||||
last_display_crc_value = crc;
|
||||
}
|
||||
}
|
||||
15
variants/lilygo_techo_lite/CPUPowerManager.h
Normal file
15
variants/lilygo_techo_lite/CPUPowerManager.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
// CPUPowerManager.h — nRF52 no-op stub
|
||||
// nRF52840 runs at fixed 64 MHz; no frequency scaling available.
|
||||
// All methods are empty so main.cpp compiles without #ifdef guards.
|
||||
|
||||
class CPUPowerManager {
|
||||
public:
|
||||
void begin() {}
|
||||
void loop() {}
|
||||
void setBoost() {}
|
||||
void setIdle() {}
|
||||
void setLowPower() {}
|
||||
void clearLowPower() {}
|
||||
int getFrequencyMHz() { return 64; }
|
||||
};
|
||||
19
variants/lilygo_techo_lite/FS.h
Normal file
19
variants/lilygo_techo_lite/FS.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
// FS.h — nRF52 compatibility stub
|
||||
// ESP32 Arduino core provides this as the base filesystem abstraction.
|
||||
// On nRF52, File and filesystem types come from Adafruit_LittleFS.
|
||||
// This stub exists solely to satisfy #include <FS.h> in shared headers.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <time.h> // struct tm, gmtime — implicit on ESP32, needs explicit on nRF52
|
||||
|
||||
// ESP32 FS.h defines these mode strings; some shared code references them
|
||||
#ifndef FILE_READ
|
||||
#define FILE_READ "r"
|
||||
#endif
|
||||
#ifndef FILE_WRITE
|
||||
#define FILE_WRITE "w"
|
||||
#endif
|
||||
#ifndef FILE_APPEND
|
||||
#define FILE_APPEND "a"
|
||||
#endif
|
||||
43
variants/lilygo_techo_lite/SD.h
Normal file
43
variants/lilygo_techo_lite/SD.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
// SD.h — nRF52 compatibility stub for Meck
|
||||
// Maps Arduino SD API to Adafruit InternalFS (LittleFS on QSPI flash).
|
||||
// T-Echo Lite has no SD card slot; file operations use internal flash.
|
||||
|
||||
#include <Adafruit_LittleFS.h>
|
||||
#include <InternalFileSystem.h>
|
||||
#include <time.h> // struct tm, gmtime — implicit on ESP32, explicit on nRF52
|
||||
|
||||
// ESP32 SD uses string file modes; define them here for compile compatibility
|
||||
#ifndef FILE_READ
|
||||
#define FILE_READ "r"
|
||||
#endif
|
||||
#ifndef FILE_WRITE
|
||||
#define FILE_WRITE "w"
|
||||
#endif
|
||||
|
||||
class SDClass {
|
||||
public:
|
||||
// InternalFS is already initialised by main — begin() is a no-op
|
||||
bool begin(uint8_t cs = 0) { return true; }
|
||||
|
||||
// Accept any extra args (cs, SPI, freq) without complaint
|
||||
template<typename... Args>
|
||||
bool begin(Args...) { return true; }
|
||||
|
||||
bool exists(const char* path) { return InternalFS.exists(path); }
|
||||
bool remove(const char* path) { return InternalFS.remove(path); }
|
||||
bool mkdir(const char* path) { return InternalFS.mkdir(path); }
|
||||
|
||||
// String mode overload — matches ESP32 SD API (FILE_READ="r", FILE_WRITE="w", "r+")
|
||||
Adafruit_LittleFS_Namespace::File open(const char* path, const char* mode = "r") {
|
||||
uint8_t m = FILE_O_READ;
|
||||
if (mode) {
|
||||
if (mode[0] == 'w') m = FILE_O_WRITE;
|
||||
else if (mode[0] == 'r' && mode[1] == '+') m = FILE_O_WRITE;
|
||||
}
|
||||
return InternalFS.open(path, m);
|
||||
}
|
||||
};
|
||||
|
||||
// Static instance per translation unit — no state (just forwards to InternalFS singleton)
|
||||
static SDClass SD;
|
||||
54
variants/lilygo_techo_lite/TechoBoard.cpp
Normal file
54
variants/lilygo_techo_lite/TechoBoard.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#include "TechoBoard.h"
|
||||
|
||||
#ifdef LILYGO_TECHO
|
||||
|
||||
void TechoBoard::begin() {
|
||||
NRF52Board::begin();
|
||||
|
||||
// Configure battery measurement control BEFORE Wire.begin()
|
||||
// to ensure P0.02 is not claimed by another peripheral
|
||||
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
|
||||
Wire.begin();
|
||||
|
||||
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
uint16_t TechoBoard::getBattMilliVolts() {
|
||||
// Use LilyGo's exact ADC configuration
|
||||
analogReference(AR_INTERNAL_3_0);
|
||||
analogReadResolution(12);
|
||||
|
||||
// Enable battery voltage divider (MOSFET gate on P0.31)
|
||||
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, HIGH);
|
||||
|
||||
// Reclaim P0.02 for analog input (in case another peripheral touched it)
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
delay(10); // let divider + ADC settle
|
||||
|
||||
// Read and average (matching LilyGo's approach)
|
||||
uint32_t sum = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
sum += analogRead(PIN_VBAT_READ);
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
uint16_t adc = sum / 8;
|
||||
|
||||
// Disable divider to save power
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
|
||||
// LilyGo's exact formula: adc * (3000.0 / 4096.0) * 2.0
|
||||
// = adc * 0.73242188 * 2.0 = adc * 1.46484375
|
||||
uint16_t millivolts = (uint16_t)((float)adc * (3000.0f / 4096.0f) * 2.0f);
|
||||
|
||||
return millivolts;
|
||||
}
|
||||
#endif
|
||||
43
variants/lilygo_techo_lite/TechoBoard.h
Normal file
43
variants/lilygo_techo_lite/TechoBoard.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <MeshCore.h>
|
||||
#include <Arduino.h>
|
||||
#include <helpers/NRF52Board.h>
|
||||
|
||||
// ============================================================
|
||||
// T-Echo Lite battery pins — hardcoded from LilyGo t_echo_lite_config.h
|
||||
// NOT using any defines from variant.h for battery measurement
|
||||
// ============================================================
|
||||
#define PIN_VBAT_READ _PINNUM(0, 2) // BATTERY_ADC_DATA
|
||||
#define PIN_VBAT_MEAS_EN _PINNUM(0, 31) // BATTERY_MEASUREMENT_CONTROL
|
||||
|
||||
class TechoBoard : public NRF52BoardDCDC {
|
||||
public:
|
||||
TechoBoard() {}
|
||||
void begin();
|
||||
uint16_t getBattMilliVolts() override;
|
||||
|
||||
const char* getManufacturerName() const override {
|
||||
return "LilyGo T-Echo Lite";
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
#ifdef LED_RED
|
||||
digitalWrite(LED_RED, LOW);
|
||||
#endif
|
||||
#ifdef LED_GREEN
|
||||
digitalWrite(LED_GREEN, LOW);
|
||||
#endif
|
||||
#ifdef LED_BLUE
|
||||
digitalWrite(LED_BLUE, LOW);
|
||||
#endif
|
||||
#ifdef DISP_BACKLIGHT
|
||||
digitalWrite(DISP_BACKLIGHT, LOW);
|
||||
#endif
|
||||
#ifdef PIN_PWR_EN
|
||||
digitalWrite(PIN_PWR_EN, LOW);
|
||||
#endif
|
||||
sd_power_system_off();
|
||||
}
|
||||
};
|
||||
238
variants/lilygo_techo_lite/platformio.ini
Normal file
238
variants/lilygo_techo_lite/platformio.ini
Normal file
@@ -0,0 +1,238 @@
|
||||
; =============================================================================
|
||||
; LilyGo T-Echo Lite — Meck variant configuration
|
||||
;
|
||||
; nRF52840 + SX1262 + GxEPD2 1.22" e-ink (176×192, GDEM0122T61/SSD1681)
|
||||
; + CardKB via QWIIC (0x5F) + optional L76K GPS
|
||||
;
|
||||
; Display: GxEPD2_122_T61 — full refresh only (~2s), no fast/partial refresh.
|
||||
; UI must minimise unnecessary redraws.
|
||||
; Scale factors: 1.5×/2.0× give ~117×96 virtual coordinate space.
|
||||
;
|
||||
; Platform: nRF52 (Adafruit nRF52 Arduino)
|
||||
; Board JSON: boards/t-echo.json (nRF52840 PCA10056 compatible)
|
||||
; =============================================================================
|
||||
|
||||
; --- Base configuration for all T-Echo Lite Meck builds (with display) ---
|
||||
[lilygo_techo_lite_meck]
|
||||
extends = nrf52_base
|
||||
board = t-echo
|
||||
board_build.ldscript = boards/nrf52840_s140_v6.ld
|
||||
extra_scripts = pre:patch_nrf52_bsp.py
|
||||
build_flags = ${nrf52_base.build_flags}
|
||||
-I variants/lilygo_techo_lite
|
||||
-I src/helpers/nrf52
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
|
||||
-D LILYGO_TECHO
|
||||
-D LILYGO_TECHO_LITE
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D SX126X_POWER_EN=30
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
; nRF52 compatibility — no PSRAM, no SD card, fallback GPS baud for CLI code paths
|
||||
-D ps_calloc=calloc
|
||||
-D ps_malloc=malloc
|
||||
-D GPS_BAUDRATE=9600
|
||||
-D SDCARD_CS=-1
|
||||
-D ROW_AUTO_LOCK=255
|
||||
; Display — GxEPD2 1.22" e-ink (176×192, SSD1681)
|
||||
-D DISPLAY_CLASS=GxEPDDisplay
|
||||
-D EINK_DISPLAY_MODEL=GxEPD2_122_T61
|
||||
-D EINK_SCALE_X=1.5f
|
||||
-D EINK_SCALE_Y=2.0f
|
||||
-D EINK_X_OFFSET=0
|
||||
-D EINK_Y_OFFSET=10
|
||||
-D DISPLAY_ROTATION=4
|
||||
-D EINK_FULL_REFRESH_ONLY=1
|
||||
-D AUTO_OFF_MILLIS=0
|
||||
build_src_filter = ${nrf52_base.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<TechoBoard.cpp>
|
||||
+<helpers/sensors/EnvironmentSensorManager.cpp>
|
||||
+<helpers/ui/GxEPDDisplay.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../variants/lilygo_techo_lite>
|
||||
lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
adafruit/Adafruit BME280 Library @ ^2.3.0
|
||||
https://github.com/SoulOfNoob/GxEPD2.git
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
debug_tool = jlink
|
||||
upload_protocol = nrfutil
|
||||
|
||||
; =============================================================================
|
||||
; Build Environments
|
||||
; =============================================================================
|
||||
|
||||
; --- BLE Companion Radio (no GPS) ---
|
||||
; Pairs with MeshCore companion app over Bluetooth.
|
||||
; CardKB provides on-device text input for standalone messaging.
|
||||
; No GPS — time synced via BLE companion or serial CLI.
|
||||
[env:meck_techo_lite_ble]
|
||||
extends = lilygo_techo_lite_meck
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
board_upload.maximum_size = 712704
|
||||
build_flags =
|
||||
${lilygo_techo_lite_meck.build_flags}
|
||||
-I src/helpers/ui
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=500
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=123456
|
||||
; -D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=64
|
||||
-D MECK_CARDKB
|
||||
-D UI_RECENT_LIST_SIZE=9
|
||||
-D UI_SENSORS_PAGE=1
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3300
|
||||
build_src_filter = ${lilygo_techo_lite_meck.build_src_filter}
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${lilygo_techo_lite_meck.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
; --- BLE Companion Radio (with GPS) ---
|
||||
; Same as above + L76K GPS for location and time sync.
|
||||
; Requires external GPS module connected to UART1 (TX=P0.29, RX=P1.10).
|
||||
[env:meck_techo_lite_gps_ble]
|
||||
extends = lilygo_techo_lite_meck
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
board_upload.maximum_size = 712704
|
||||
build_flags =
|
||||
${lilygo_techo_lite_meck.build_flags}
|
||||
-I src/helpers/ui
|
||||
-I examples/companion_radio/ui-new
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
-D GPS_BAUD_RATE=9600
|
||||
-D PIN_GPS_EN=GPS_EN
|
||||
-D MAX_CONTACTS=500
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D OFFLINE_QUEUE_SIZE=64
|
||||
-D MECK_CARDKB
|
||||
-D UI_RECENT_LIST_SIZE=9
|
||||
-D UI_SENSORS_PAGE=1
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3300
|
||||
build_src_filter = ${lilygo_techo_lite_meck.build_src_filter}
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${lilygo_techo_lite_meck.lib_deps}
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
; --- Repeater ---
|
||||
; Standalone LoRa repeater node. E-ink shows status.
|
||||
; CardKB not useful here but included by base for consistency.
|
||||
[env:meck_techo_lite_repeater]
|
||||
extends = lilygo_techo_lite_meck
|
||||
build_src_filter = ${lilygo_techo_lite_meck.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
build_flags =
|
||||
${lilygo_techo_lite_meck.build_flags}
|
||||
-D ADVERT_NAME='"Meck T-Echo Lite Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
|
||||
; --- Room Server ---
|
||||
; BBS-style message board node.
|
||||
[env:meck_techo_lite_room_server]
|
||||
extends = lilygo_techo_lite_meck
|
||||
build_src_filter = ${lilygo_techo_lite_meck.build_src_filter}
|
||||
+<../examples/simple_room_server>
|
||||
build_flags =
|
||||
${lilygo_techo_lite_meck.build_flags}
|
||||
-D ADVERT_NAME='"Meck T-Echo Lite Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
|
||||
; =============================================================================
|
||||
; Headless (no display) variants
|
||||
; =============================================================================
|
||||
|
||||
; --- Headless base (no display, no GxEPD2) ---
|
||||
[lilygo_techo_lite_meck_core]
|
||||
extends = nrf52_base
|
||||
board = t-echo
|
||||
board_build.ldscript = boards/nrf52840_s140_v6.ld
|
||||
build_flags = ${nrf52_base.build_flags}
|
||||
-I variants/lilygo_techo_lite
|
||||
-I src/helpers/nrf52
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
|
||||
-D LILYGO_TECHO
|
||||
-D LILYGO_TECHO_LITE
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D SX126X_POWER_EN=30
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
; nRF52 compatibility
|
||||
-D ps_calloc=calloc
|
||||
-D ps_malloc=malloc
|
||||
-D GPS_BAUDRATE=9600
|
||||
-D SDCARD_CS=-1
|
||||
-D ROW_AUTO_LOCK=255
|
||||
-D DISABLE_DIAGNOSTIC_OUTPUT
|
||||
-D AUTO_OFF_MILLIS=0
|
||||
build_src_filter = ${nrf52_base.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<TechoBoard.cpp>
|
||||
+<helpers/sensors/EnvironmentSensorManager.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../variants/lilygo_techo_lite>
|
||||
lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
adafruit/Adafruit BME280 Library @ ^2.3.0
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
debug_tool = jlink
|
||||
upload_protocol = nrfutil
|
||||
|
||||
; --- Headless Repeater (no display — lowest power, outdoor deployment) ---
|
||||
[env:meck_techo_lite_core_repeater]
|
||||
extends = lilygo_techo_lite_meck_core
|
||||
build_src_filter = ${lilygo_techo_lite_meck_core.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
build_flags =
|
||||
${lilygo_techo_lite_meck_core.build_flags}
|
||||
-D ADVERT_NAME='"Meck T-Echo Lite Core Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
|
||||
; --- Headless BLE Companion (no display — phone-only UI) ---
|
||||
[env:meck_techo_lite_core_ble]
|
||||
extends = lilygo_techo_lite_meck_core
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
board_upload.maximum_size = 712704
|
||||
build_flags =
|
||||
${lilygo_techo_lite_meck_core.build_flags}
|
||||
-D MAX_CONTACTS=500
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=234567
|
||||
-D OFFLINE_QUEUE_SIZE=64
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3300
|
||||
build_src_filter = ${lilygo_techo_lite_meck_core.build_src_filter}
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${lilygo_techo_lite_meck_core.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
55
variants/lilygo_techo_lite/target.cpp
Normal file
55
variants/lilygo_techo_lite/target.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
#include <helpers/ArduinoHelpers.h>
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
|
||||
TechoBoard board;
|
||||
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
VolatileRTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
|
||||
#ifdef ENV_INCLUDE_GPS
|
||||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock);
|
||||
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
|
||||
#else
|
||||
EnvironmentSensorManager sensors = EnvironmentSensorManager();
|
||||
#endif
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
rtc_clock.begin(Wire);
|
||||
|
||||
return radio.std_init(&SPI);
|
||||
}
|
||||
|
||||
uint32_t radio_get_rng_seed() {
|
||||
return radio.random(0x7FFFFFFF);
|
||||
}
|
||||
|
||||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
radio.setFrequency(freq);
|
||||
radio.setSpreadingFactor(sf);
|
||||
radio.setBandwidth(bw);
|
||||
radio.setCodingRate(cr);
|
||||
}
|
||||
|
||||
void radio_set_tx_power(int8_t dbm) {
|
||||
radio.setOutputPower(dbm);
|
||||
}
|
||||
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng); // create new random identity
|
||||
}
|
||||
|
||||
void radio_reset_agc() {
|
||||
radio.setRxBoostedGainMode(true);
|
||||
}
|
||||
32
variants/lilygo_techo_lite/target.h
Normal file
32
variants/lilygo_techo_lite/target.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <TechoBoard.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/sensors/EnvironmentSensorManager.h>
|
||||
#include <helpers/sensors/LocationProvider.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/GxEPDDisplay.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern TechoBoard board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
uint32_t radio_get_rng_seed();
|
||||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
|
||||
void radio_set_tx_power(int8_t dbm);
|
||||
mesh::LocalIdentity radio_new_identity();
|
||||
void radio_reset_agc();
|
||||
39
variants/lilygo_techo_lite/variant.cpp
Normal file
39
variants/lilygo_techo_lite/variant.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "variant.h"
|
||||
#include "wiring_constants.h"
|
||||
#include "wiring_digital.h"
|
||||
|
||||
const int MISO = PIN_SPI1_MISO;
|
||||
const int MOSI = PIN_SPI1_MOSI;
|
||||
const int SCK = PIN_SPI1_SCK;
|
||||
|
||||
const uint32_t g_ADigitalPinMap[] = {
|
||||
0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47
|
||||
};
|
||||
|
||||
void initVariant() {
|
||||
pinMode(PIN_PWR_EN, OUTPUT);
|
||||
digitalWrite(PIN_PWR_EN, HIGH);
|
||||
|
||||
pinMode(PIN_BUTTON1, INPUT_PULLUP);
|
||||
pinMode(PIN_BUTTON2, INPUT_PULLUP);
|
||||
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
pinMode(LED_GREEN, OUTPUT);
|
||||
pinMode(LED_BLUE, OUTPUT);
|
||||
digitalWrite(LED_BLUE, HIGH);
|
||||
digitalWrite(LED_GREEN, HIGH);
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
|
||||
// pinMode(PIN_TXCO, OUTPUT);
|
||||
// digitalWrite(PIN_TXCO, HIGH);
|
||||
|
||||
pinMode(DISP_POWER, OUTPUT);
|
||||
digitalWrite(DISP_POWER, LOW);
|
||||
|
||||
// shutdown gps
|
||||
pinMode(GPS_EN, OUTPUT);
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
}
|
||||
159
variants/lilygo_techo_lite/variant.h
Normal file
159
variants/lilygo_techo_lite/variant.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* variant.h
|
||||
* Copyright (C) 2023 Seeed K.K.
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _PINNUM(port, pin) ((port) * 32 + (pin))
|
||||
|
||||
#include "WVariant.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Low frequency clock source
|
||||
|
||||
#define USE_LFXO // 32.768 kHz crystal oscillator
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
#define WIRE_INTERFACES_COUNT (1)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Power
|
||||
|
||||
#define PIN_PWR_EN _PINNUM(0, 30) // RT9080_EN
|
||||
|
||||
#define BATTERY_PIN _PINNUM(0, 2)
|
||||
#define ADC_MULTIPLIER (2.0F)
|
||||
|
||||
#define ADC_RESOLUTION (14)
|
||||
#define BATTERY_SENSE_RES (12)
|
||||
|
||||
#define AREF_VOLTAGE (3.0)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Number of pins
|
||||
|
||||
#define PINS_COUNT (48)
|
||||
#define NUM_DIGITAL_PINS (48)
|
||||
#define NUM_ANALOG_INPUTS (1)
|
||||
#define NUM_ANALOG_OUTPUTS (0)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// UART pin definition
|
||||
|
||||
#define PIN_SERIAL1_RX PIN_GPS_TX
|
||||
#define PIN_SERIAL1_TX PIN_GPS_RX
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// I2C pin definition
|
||||
|
||||
#define PIN_WIRE_SDA _PINNUM(1, 4) // (SDA) - per LilyGo IIC_1_SDA
|
||||
#define PIN_WIRE_SCL _PINNUM(1, 2) // (SCL) - per LilyGo IIC_1_SCL
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SPI pin definition
|
||||
|
||||
#define SPI_INTERFACES_COUNT (2)
|
||||
|
||||
#define PIN_SPI_MISO _PINNUM(0, 17) // (MISO)
|
||||
#define PIN_SPI_MOSI _PINNUM(0, 15) // (MOSI)
|
||||
#define PIN_SPI_SCK _PINNUM(0, 13) // (SCK)
|
||||
#define PIN_SPI_NSS (-1)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// QSPI FLASH
|
||||
|
||||
#define PIN_QSPI_SCK _PINNUM(0, 4)
|
||||
#define PIN_QSPI_CS _PINNUM(0, 12)
|
||||
#define PIN_QSPI_IO0 _PINNUM(0, 6)
|
||||
#define PIN_QSPI_IO1 _PINNUM(0, 8)
|
||||
#define PIN_QSPI_IO2 _PINNUM(1, 9)
|
||||
#define PIN_QSPI_IO3 _PINNUM(0, 26)
|
||||
|
||||
#define EXTERNAL_FLASH_DEVICES ZD25WQ32CEIGR
|
||||
#define EXTERNAL_FLASH_USE_QSPI
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Builtin LEDs
|
||||
|
||||
#define LED_RED _PINNUM(1, 14) // LED_3
|
||||
#define LED_BLUE _PINNUM(1, 5) // LED_2
|
||||
#define LED_GREEN _PINNUM(1, 7) // LED_1
|
||||
|
||||
//#define PIN_STATUS_LED LED_BLUE
|
||||
#define LED_BUILTIN (-1)
|
||||
#define LED_PIN LED_BUILTIN
|
||||
#define LED_STATE_ON LOW
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Builtin buttons
|
||||
|
||||
#define PIN_BUTTON1 _PINNUM(0, 24) // BOOT
|
||||
#define BUTTON_PIN PIN_BUTTON1
|
||||
#define PIN_USER_BTN BUTTON_PIN
|
||||
|
||||
#define PIN_BUTTON2 _PINNUM(0, 18)
|
||||
#define BUTTON_PIN2 PIN_BUTTON2
|
||||
|
||||
#define EXTERNAL_FLASH_DEVICES MX25R1635F
|
||||
#define EXTERNAL_FLASH_USE_QSPI
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Lora
|
||||
|
||||
#define USE_SX1262
|
||||
#define LORA_CS _PINNUM(0, 11)
|
||||
#define SX126X_POWER_EN _PINNUM(0, 30)
|
||||
#define SX126X_DIO1 _PINNUM(1, 8)
|
||||
#define SX126X_BUSY _PINNUM(0, 14)
|
||||
#define SX126X_RESET _PINNUM(0, 7)
|
||||
#define SX126X_RF_VC1 _PINNUM(0, 27)
|
||||
#define SX126X_RF_VC2 _PINNUM(0, 33)
|
||||
|
||||
#define P_LORA_DIO_1 SX126X_DIO1
|
||||
#define P_LORA_NSS LORA_CS
|
||||
#define P_LORA_RESET SX126X_RESET
|
||||
#define P_LORA_BUSY SX126X_BUSY
|
||||
#define P_LORA_SCLK PIN_SPI_SCK
|
||||
#define P_LORA_MISO PIN_SPI_MISO
|
||||
#define P_LORA_MOSI PIN_SPI_MOSI
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SPI1
|
||||
|
||||
#define PIN_SPI1_MISO (-1) // Not used for Display
|
||||
#define PIN_SPI1_MOSI _PINNUM(0, 20)
|
||||
#define PIN_SPI1_SCK _PINNUM(0, 19)
|
||||
|
||||
// GxEPD2 needs that for a panel that is not even used !
|
||||
extern const int MISO;
|
||||
extern const int MOSI;
|
||||
extern const int SCK;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Display
|
||||
|
||||
// #define DISP_MISO (-1) // Not used for Display
|
||||
#define DISP_MOSI _PINNUM(0, 20)
|
||||
#define DISP_SCLK _PINNUM(0, 19)
|
||||
#define DISP_CS _PINNUM(0, 22)
|
||||
#define DISP_DC _PINNUM(0, 21)
|
||||
#define DISP_RST _PINNUM(0, 28)
|
||||
#define DISP_BUSY _PINNUM(0, 3)
|
||||
#define DISP_POWER _PINNUM(1, 12)
|
||||
// #define DISP_BACKLIGHT (-1) // Display has no backlight
|
||||
|
||||
#define PIN_DISPLAY_CS DISP_CS
|
||||
#define PIN_DISPLAY_DC DISP_DC
|
||||
#define PIN_DISPLAY_RST DISP_RST
|
||||
#define PIN_DISPLAY_BUSY DISP_BUSY
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GPS — per LilyGo t_echo_lite_config.h
|
||||
// PIN_GPS_TX/RX named from GPS module's perspective
|
||||
|
||||
#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX → MCU RX
|
||||
#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX ← MCU TX
|
||||
#define GPS_EN _PINNUM(1, 11) // GPS RT9080 power enable
|
||||
#define PIN_GPS_STANDBY _PINNUM(1, 13) // GPS wake-up
|
||||
#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS
|
||||
Reference in New Issue
Block a user