diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index 319d7cc7c..c7cab5e5a 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -1115,6 +1115,7 @@ jobs: python3 -B test/test_ssd1306_power.py -v python3 -B test/test_oled_display_probe.py -v python3 -B test/test_oled_controller_settings.py -v + python3 -B test/test_upstream_auto_display_merge.py -v python3 -B test/test_companion_inbox.py python3 -B test/test_heltec_r8_external_power.py python3 -B test/test_bluetooth_mac_contract.py diff --git a/src/helpers/CommonCLI_Observer.cpp b/src/helpers/CommonCLI_Observer.cpp index 811943c4f..e4418af2b 100644 --- a/src/helpers/CommonCLI_Observer.cpp +++ b/src/helpers/CommonCLI_Observer.cpp @@ -30,7 +30,7 @@ #include "esp32/WiFiRadioPolicy.h" #endif #if defined(ESP32_PLATFORM) && defined(ENABLE_OTA) && \ - (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID)) + (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) #include "esp32/WiFiOtaSeeder.h" #endif #ifdef WITH_MQTT_BRIDGE @@ -52,7 +52,7 @@ static uint32_t _atoi(const char* sp) { #ifdef ESP_PLATFORM static bool observerBluetoothWiFiCoexistenceRequired() { -#if defined(BLE_PIN_CODE) && defined(WIFI_SSID) +#if defined(BLE_PIN_CODE) && (defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) return true; #else return false; @@ -1129,7 +1129,7 @@ bool CommonCLI::handleObserverGetCmd(uint32_t sender_timestamp, const char* conf #endif } #if defined(ESP32_PLATFORM) && defined(ENABLE_OTA) && \ - (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID)) + (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) mesh::ota::WiFiOtaSeeder::appendStatus(reply, 160); #endif } else if (memcmp(config, "wifi.powersave", 14) == 0) { diff --git a/src/helpers/WiFiSetupPortal.cpp b/src/helpers/WiFiSetupPortal.cpp index 2cd0108b7..ce4bdb034 100644 --- a/src/helpers/WiFiSetupPortal.cpp +++ b/src/helpers/WiFiSetupPortal.cpp @@ -1,6 +1,6 @@ #include "WiFiSetupPortal.h" -#if defined(ESP32_PLATFORM) && defined(WIFI_SSID) +#if defined(ESP32_PLATFORM) && (defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) #include #include diff --git a/src/helpers/WiFiSetupPortal.h b/src/helpers/WiFiSetupPortal.h index 9c27dcb97..d809ce6e3 100644 --- a/src/helpers/WiFiSetupPortal.h +++ b/src/helpers/WiFiSetupPortal.h @@ -1,6 +1,6 @@ #pragma once -#if defined(ESP32_PLATFORM) && defined(WIFI_SSID) +#if defined(ESP32_PLATFORM) && (defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) #include #include diff --git a/src/helpers/esp32/WebConfigServer.cpp b/src/helpers/esp32/WebConfigServer.cpp index 82cface24..7951ca817 100644 --- a/src/helpers/esp32/WebConfigServer.cpp +++ b/src/helpers/esp32/WebConfigServer.cpp @@ -21,7 +21,7 @@ static_assert(sizeof(WEBCONFIG_AP_PREFIX) <= 28, #include #if defined(ESP32_PLATFORM) && defined(ENABLE_OTA) && \ - (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID)) + (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) #include #endif @@ -177,7 +177,7 @@ static bool bluetoothWiFiCoexistenceRequired() { // This server is never constructed on the exclusive profile's BLE boot. // Any running WebConfig instance therefore has no active Bluetooth stack. return false; -#elif defined(BLE_PIN_CODE) && defined(WIFI_SSID) +#elif defined(BLE_PIN_CODE) && (defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) return mesh::wifi::activeCompanionMode() != mesh::wifi::CompanionMode::WiFiOnly; #else @@ -307,7 +307,7 @@ static bool fixedTimeEquals(const char* a, const char* b, size_t max_len) { static void appendOtaSeederStatus(char* reply, size_t reply_len) { #if defined(ESP32_PLATFORM) && defined(ENABLE_OTA) && \ - (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID)) + (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) mesh::ota::WiFiOtaSeeder::appendStatus(reply, reply_len); #else (void)reply; diff --git a/src/helpers/esp32/WiFiOtaSeeder.cpp b/src/helpers/esp32/WiFiOtaSeeder.cpp index ee930ab45..78bdac254 100644 --- a/src/helpers/esp32/WiFiOtaSeeder.cpp +++ b/src/helpers/esp32/WiFiOtaSeeder.cpp @@ -1,7 +1,7 @@ #include "WiFiOtaSeeder.h" #if defined(ESP32_PLATFORM) && defined(ENABLE_OTA) && \ - (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID)) + (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) #include #include diff --git a/src/helpers/esp32/WiFiOtaSeeder.h b/src/helpers/esp32/WiFiOtaSeeder.h index 60b2df93b..94812332c 100644 --- a/src/helpers/esp32/WiFiOtaSeeder.h +++ b/src/helpers/esp32/WiFiOtaSeeder.h @@ -1,7 +1,7 @@ #pragma once #if defined(ESP32_PLATFORM) && defined(ENABLE_OTA) && \ - (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID)) + (defined(WIFI_OTA_SEEDER) || defined(WIFI_SSID) || defined(ENABLE_WIFI_INTERFACE)) #include #include diff --git a/src/helpers/ui/AutoDetectDisplay.cpp b/src/helpers/ui/AutoDetectDisplay.cpp new file mode 100644 index 000000000..ec8f0f989 --- /dev/null +++ b/src/helpers/ui/AutoDetectDisplay.cpp @@ -0,0 +1 @@ +#include "AutoDetectDisplay.h" diff --git a/src/helpers/ui/AutoDetectDisplay.h b/src/helpers/ui/AutoDetectDisplay.h new file mode 100644 index 000000000..f0f82cfc9 --- /dev/null +++ b/src/helpers/ui/AutoDetectDisplay.h @@ -0,0 +1,8 @@ +#pragma once + +#include "SSD1306Display.h" + +// Compatibility with upstream's driver name. OLED_AUTO_DETECT selects the +// shared backend, including saved controller overrides, rotation and one +// framebuffer. RAK profiles retain SSD1306Display for existing size accounting. +using AutoDetectDisplay = SSD1306Display; diff --git a/src/helpers/ui/SH1106Display.cpp b/src/helpers/ui/SH1106Display.cpp index 6e8b0647b..79511c59d 100644 --- a/src/helpers/ui/SH1106Display.cpp +++ b/src/helpers/ui/SH1106Display.cpp @@ -21,15 +21,15 @@ bool SH1106Display::i2c_probe(TwoWire &wire, uint8_t addr) } // Color scheme -ColorVal UIColor::window_bkg = SH110X_BLACK; -ColorVal UIColor::title_bkg = SH110X_BLACK; -ColorVal UIColor::title_txt = SH110X_WHITE; -ColorVal UIColor::primary_txt = SH110X_WHITE; -ColorVal UIColor::secondary_txt = SH110X_WHITE; -ColorVal UIColor::warning_txt = SH110X_WHITE; -ColorVal UIColor::popup_bkg = SH110X_BLACK; -ColorVal UIColor::popup_txt = SH110X_WHITE; -ColorVal UIColor::corp_blue = SH110X_WHITE; +__attribute__((weak)) ColorVal UIColor::window_bkg = SH110X_BLACK; +__attribute__((weak)) ColorVal UIColor::title_bkg = SH110X_BLACK; +__attribute__((weak)) ColorVal UIColor::title_txt = SH110X_WHITE; +__attribute__((weak)) ColorVal UIColor::primary_txt = SH110X_WHITE; +__attribute__((weak)) ColorVal UIColor::secondary_txt = SH110X_WHITE; +__attribute__((weak)) ColorVal UIColor::warning_txt = SH110X_WHITE; +__attribute__((weak)) ColorVal UIColor::popup_bkg = SH110X_BLACK; +__attribute__((weak)) ColorVal UIColor::popup_txt = SH110X_WHITE; +__attribute__((weak)) ColorVal UIColor::corp_blue = SH110X_WHITE; bool SH1106Display::begin() { diff --git a/test/fixtures/native_displays/recording.cpp b/test/fixtures/native_displays/recording.cpp index 04241347d..9f3d8eeee 100644 --- a/test/fixtures/native_displays/recording.cpp +++ b/test/fixtures/native_displays/recording.cpp @@ -82,6 +82,13 @@ struct Canvas { void reset() { std::fill(pixels.begin(),pixels.end(),0); } }; Canvas* Canvas::active=nullptr; +// This fixture exercises direct panel geometry; the full buffered T-Deck +// renderer and its PSRAM canvas run in test_tdeck_display_cooperation.py. +class TDeckCanvas : public Canvas { +public: + int16_t getCursorX() const { return x; } + int16_t getCursorY() const { return y; } +}; struct ST7789Spi : Canvas { template ST7789Spi(T...) {} }; struct Adafruit_ST7789 : Canvas { template Adafruit_ST7789(T...) {} }; struct GxEPD2_150_BN { static const int WIDTH=200,HEIGHT=200; template GxEPD2_150_BN(T...) {} }; diff --git a/test/test_native_displays.py b/test/test_native_displays.py index 211263d39..fb740f518 100644 --- a/test/test_native_displays.py +++ b/test/test_native_displays.py @@ -50,12 +50,17 @@ class NativeDisplaysTest(unittest.TestCase): source += driver[a:b] source += method(driver, cls, "drawChar") else: - source += f"bool {cls}::begin() {{ return true; }}\n" + awake = "_isOn=true; " if cls == "ST7789LCDDisplay" else "" + source += f"bool {cls}::begin() {{ {awake}return true; }}\n" for name in ("turnOn", "turnOff", "clear", "endFrame"): source += f"void {cls}::{name}() {{}}\n" source += f"void {cls}::startFrame(ColorVal) {{}}\n" if cls == "ST7789LCDDisplay": source += f"void {cls}::setFlipped(bool) {{}}\n" + if "LILYGO_TDECK" in defines: + source += f"{cls}::~{cls}() {{}}\n" + source += method(driver, cls, "isBufferedRendering") + source += f"void {cls}::finishCanvasPrimitive(int,int,int,int) {{ assert(false); }}\n" names = ["setTextSize", "setColor", "setCursor", "print", "fillRect", "drawRect", "drawXbm", "getTextWidth"] if cls == "ST7789LCDDisplay": names += ["setRotationDegrees"] @@ -66,6 +71,9 @@ class NativeDisplaysTest(unittest.TestCase): source += r''' int main() { Driver d; d.begin(); d.setColor(1); d.setTextSize(1); +#ifdef LILYGO_TDECK + assert(!d.isBufferedRendering()); +#endif auto& c=*Canvas::active; assert(d.width()==EXPECTED_WIDTH && d.height()==EXPECTED_HEIGHT); assert(!d.useSmallMessageFont()); diff --git a/test/test_nrf52_variant_contracts.py b/test/test_nrf52_variant_contracts.py index ac7f69c30..41c664a47 100644 --- a/test/test_nrf52_variant_contracts.py +++ b/test/test_nrf52_variant_contracts.py @@ -440,7 +440,8 @@ class Nrf52VariantContractsTest(unittest.TestCase): self.assertIn("bool isBridgeRunning() const override", header) self.assertIn("if (isRs232BridgeRunning()) reply_data[8] |= 0x01", implementation) - self.assertIn("if (isBridgeRunning()) reply_data[8] |= 0x03", implementation) + self.assertIn("if (isEspNowBridgeRunning()) reply_data[8] |= 0x03", implementation) + self.assertNotIn("if (isBridgeRunning()) reply_data[8] |= 0x03", implementation) boot_failure = implementation[ implementation.index("if (!bridge || !beginRS232Bridge())"): implementation.index("#else", implementation.index( diff --git a/test/test_upstream_20261015_wifi_boot.py b/test/test_upstream_20261015_wifi_boot.py index aa11ba23f..94fc159e4 100644 --- a/test/test_upstream_20261015_wifi_boot.py +++ b/test/test_upstream_20261015_wifi_boot.py @@ -5,12 +5,143 @@ import subprocess import tempfile import unittest +from test_t114_autodisplay_recipes import defines +from test_upstream_observer_recipes import Recipes +from test_replay_reset_integration import extract_braced + ROOT = Path(__file__).resolve().parents[1] COMPANION = ROOT / "examples/companion_radio" class DynamicWiFiBootContractTest(unittest.TestCase): + def test_new_ble_wifi_recipes_keep_dynamic_setup_and_queue_capacity(self): + recipes = Recipes() + for name in ( + "heltec_v4_tft_companion_radio_ble_femon", + "heltec_v4_3_tft_companion_radio_ble_femoff", + "heltec_v4_expansionkit_tft_companion_radio_ble_femon", + "heltec_v4_3_expansionkit_tft_companion_radio_ble_femoff", + "T_Beam_S3_Supreme_SX1262_companion_radio_ble", + "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps", + ): + with self.subTest(target=name): + section = "env:" + name + flags = defines(recipes.value(section, "build_flags")) + self.assertEqual(flags["ENABLE_WIFI_INTERFACE"], "1") + self.assertEqual(flags["BLE_PIN_CODE"], "123456") + self.assertEqual(flags["OFFLINE_QUEUE_SIZE"], "512") + self.assertNotIn("WIFI_SSID", flags) + self.assertNotIn("WIFI_PWD", flags) + for source in ("helpers/wifi/SerialWifiInterface.cpp", + "helpers/WiFiSetupPortal.cpp", + "helpers/esp32/WiFiOtaSeeder.cpp"): + self.assertTrue(recipes.includes(section, source), source) + + def test_dynamic_wifi_helpers_link_without_companion_header_normalization(self): + portal = (ROOT / "src/helpers/WiFiSetupPortal.cpp").read_text() + seeder = (ROOT / "src/helpers/esp32/WiFiOtaSeeder.cpp").read_text() + + def guard(source): + lines = iter(source.splitlines()) + line = next(line for line in lines if line.startswith("#if ")) + result = line + while line.endswith("\\"): + line = next(lines) + result += "\n" + line + return result + + helper_source = ''' +#include +#include +#include +#include +#include +@PORTAL_GUARD@ +@PORTAL_METHODS@ +#endif +@SEEDER_GUARD@ +namespace mesh { namespace ota { +static bool listener_active = true, tcp_folder_attached = false; +@SEEDER_METHODS@ +} } +#endif +'''.replace("@PORTAL_GUARD@", guard(portal)).replace("@SEEDER_GUARD@", guard(seeder)) + helper_source = helper_source.replace("@PORTAL_METHODS@", "\n".join( + extract_braced(portal, signature) for signature in ( + "bool WiFiSetupPortal::loadStoredCredentials(", + "bool WiFiSetupPortal::isPlaceholderSSID("))) + helper_source = helper_source.replace("@SEEDER_METHODS@", "\n".join( + extract_braced(seeder, signature) for signature in ( + "bool WiFiOtaSeeder::isListening(", "bool WiFiOtaSeeder::isAttached("))) + caller = ''' +#include +#include +#include +#include +#if @PORTAL@ +static void portalTest() { + assert(WiFiSetupPortal::isPlaceholderSSID("")); + assert(!WiFiSetupPortal::isPlaceholderSSID("mesh")); + char ssid[32] = {}, password[65] = {}; + assert(!WiFiSetupPortal::loadStoredCredentials(ssid, sizeof(ssid), password, sizeof(password))); + nvs_values["ssid"] = {PT_STR, "mesh", 0}; + nvs_values["password"] = {PT_STR, "password", 0}; + assert(WiFiSetupPortal::loadStoredCredentials(ssid, sizeof(ssid), password, sizeof(password))); +} +#else +class WiFiSetupPortal {}; // disabled declarations must stay absent +#endif +#if @SEEDER@ +static void seederTest() { + assert(mesh::ota::WiFiOtaSeeder::isListening()); + assert(!mesh::ota::WiFiOtaSeeder::isAttached()); +} +#else +namespace mesh { namespace ota { class WiFiOtaSeeder {}; } } +#endif +int main() { +#if @PORTAL@ + portalTest(); +#endif +#if @SEEDER@ + seederTest(); +#endif +} +''' + cases = ( + (("ESP32_PLATFORM", "ENABLE_WIFI_INTERFACE", "ENABLE_OTA=1"), 1, 1), + (("ESP32_PLATFORM", 'WIFI_SSID="legacy"', "ENABLE_OTA=1"), 1, 1), + (("ESP32_PLATFORM", "ENABLE_OTA=1"), 0, 0), + (("ESP32_PLATFORM", "ENABLE_WIFI_INTERFACE"), 1, 0), + (("ESP32_PLATFORM", "WIFI_OTA_SEEDER", "ENABLE_OTA=1"), 0, 1), + (("NRF52_PLATFORM", "ENABLE_WIFI_INTERFACE", "ENABLE_OTA=1"), 0, 0), + ) + with tempfile.TemporaryDirectory(prefix="dynamic-wifi-helpers-") as temporary: + folder = Path(temporary) + (folder / "Arduino.h").write_text("#pragma once\n#include \n#include \n") + (folder / "IPAddress.h").write_text( + "#pragma once\nstruct IPAddress { IPAddress(int,int,int,int) {} };\n") + (folder / "Preferences.h").write_text( + (ROOT / "test/fixtures/wifi_credentials_preferences.h").read_text()) + helper = folder / "helpers.cpp" + helper.write_text(helper_source) + for index, (flags, has_portal, has_seeder) in enumerate(cases): + with self.subTest(defines=flags): + main = folder / "main.cpp" + main.write_text(caller.replace("@PORTAL@", str(has_portal)) + .replace("@SEEDER@", str(has_seeder))) + binary = folder / str(index) + built = subprocess.run([ + "c++", "-std=c++17", "-Wall", "-Wextra", "-Werror", + "-I", str(folder), "-I", str(ROOT / "src"), + *["-D" + flag for flag in flags], str(helper), str(main), + "-o", str(binary), + ], text=True, capture_output=True, timeout=30) + self.assertEqual(built.returncode, 0, built.stderr) + ran = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10) + self.assertEqual(ran.returncode, 0, ran.stderr) + def test_real_include_order_normalizes_before_feature_selection(self): mesh = (COMPANION / "MyMesh.h").read_text() abstract = (COMPANION / "AbstractUITask.h").read_text() diff --git a/test/test_upstream_auto_display_merge.py b/test/test_upstream_auto_display_merge.py new file mode 100644 index 000000000..1c45efd2c --- /dev/null +++ b/test/test_upstream_auto_display_merge.py @@ -0,0 +1,142 @@ +"""Keep upstream's display name on the existing single-buffer OLED backend.""" +from pathlib import Path +import tempfile +import unittest + +import test_oled_display_probe as probe +from test_upstream_power_policy_merge import Recipes + +DRAWING = r''' + uint8_t rotation = 0; + void setRotation(uint8_t value) { rotation = last_rotation = value; } + int16_t width() const { return rotation & 1 ? HEIGHT : WIDTH; } + int16_t height() const { return rotation & 1 ? WIDTH : HEIGHT; } + void setTextColor(uint16_t) {} + void setTextSize(int) {} + void setTextWrap(bool) {} + void cp437(bool) {} + void setCursor(int, int) {} + void print(const char*) {} + void fillRect(int, int, int, int, uint16_t) {} + void drawRect(int, int, int, int, uint16_t) {} + void drawBitmap(int, int, const uint8_t*, int, int, uint16_t) {} + void getTextBounds(const char* text, int, int, int16_t* x, int16_t* y, + uint16_t* width, uint16_t* height) { + *x = *y = 0; *width = std::strlen(text) * 6; *height = 8; + } +''' + +HARNESS = r''' +#include +#include +#include +#include +#include +static unsigned allocations = 0, active = 0; +void* test_malloc(size_t size) { + assert(size == 1024); ++allocations; ++active; return std::malloc(size); +} +void test_free(void* p) { if (p) { assert(active == 1); --active; std::free(p); } } +#define malloc test_malloc +#include +#include +#undef malloc +static_assert(std::is_same::value, + "compatibility must preserve the single driver and framebuffer"); +namespace mesh { namespace ui { +static OLEDControllerMode saved = OLEDControllerMode::Auto; +OLEDControllerMode oledControllerMode() { return saved; } +}} +using mesh::ui::OLEDControllerMode; +int main() { + for (auto mode : {OLEDControllerMode::Auto, OLEDControllerMode::SSD1306, + OLEDControllerMode::SH1106}) { + for (int status : {0, 8}) { + Wire = TwoWire{}; Wire.status[0x3C] = {status, status}; + mesh::ui::saved = mode; + const unsigned before = allocations; + { + AutoDetectDisplay panel; + DisplayDriver* ui = &panel; + assert(ui->supportsRotation() && panel.begin() && ui->isOn()); + assert(allocations == before + 1 && active == 1); + const bool sh = mode == OLEDControllerMode::SH1106 || + (mode == OLEDControllerMode::Auto && status == 8); + // SSD1306 init has no payload in this vendor double. SH1106 performs + // its real init and eight page writes through the actual backend. + bool saw_page = false; + for (const auto& tx : Wire.transactions) + if (tx.data.size() == 4 && tx.data[1] == 0xB0) saw_page = true; + assert(saw_page == sh); + for (unsigned degrees : {0, 90, 180, 270}) { + assert(ui->setRotationDegrees(degrees)); + assert(ui->width() == (degrees % 180 ? 64 : 128)); + assert(ui->height() == (degrees % 180 ? 128 : 64)); + ui->setFlipped(true); assert(last_rotation == ((degrees / 90 + 2) & 3)); + ui->setFlipped(false); assert(last_rotation == degrees / 90); + } + assert(!ui->setRotationDegrees(45)); + ui->setTextSize(2); assert(ui->textLineHeight() == 16); + ui->startFrame(); assert(ui->textLineHeight() == 8); + ui->turnOff(); assert(!ui->isOn()); ui->turnOn(); assert(ui->isOn()); + Wire.requests.clear(); + assert(panel.begin() && allocations == before + 1); + } + assert(active == 0); + } + } + Wire = TwoWire{}; Wire.present[0x3C] = false; + mesh::ui::saved = OLEDControllerMode::Auto; + AutoDetectDisplay absent; + const unsigned before = allocations; + assert(!absent.begin() && !absent.isOn()); + absent.turnOn(); assert(!absent.isOn() && allocations == before); +} +''' + + +class UpstreamAutoDisplayMergeTests(unittest.TestCase): + def test_real_compatibility_driver_detection_settings_rotation_and_memory(self): + vendor = probe.VENDOR.replace("class Adafruit_SSD1306 {", + "inline unsigned last_rotation = 0;\nclass Adafruit_SSD1306 {") + vendor = vendor.replace("public:\n", "public:\n" + DRAWING) + vendor += "\n#define SSD1306_BLACK 0\n#define SSD1306_WHITE 1\n" + vendor += "#define SSD1306_DISPLAYON 0xAF\n#define SSD1306_DISPLAYOFF 0xAE\n" + mocks = { + "Arduino.h": probe.ARDUINO + "\ninline int digitalRead(unsigned) { return 0; }\n", + "Wire.h": probe.WIRE, "Adafruit_SSD1306.h": vendor, "Adafruit_GFX.h": "", + "helpers/IdentityStore.h": "#pragma once\nstruct FakeFS;\n#define FILESYSTEM FakeFS\n", + } + # Existing helper writes only direct children; provide the one nested + # header through a separate include directory in the same test scope. + identity = mocks.pop("helpers/IdentityStore.h") + with tempfile.TemporaryDirectory() as directory: + include = Path(directory) + (include / "helpers").mkdir() + (include / "helpers/IdentityStore.h").write_text(identity) + probe.OledDisplayProbeTests.compile_run( + self, HARNESS, ("-DOLED_AUTO_DETECT=1", "-DPIN_OLED_RESET=-1", + "-Wno-unused-parameter", "-I", str(include)), mocks) + + def test_every_rak_display_role_keeps_existing_detection_and_source_budget(self): + recipes = Recipes() + count = 0 + for section in recipes.config.sections(): + if not section.startswith("env:RAK_4631_"): + continue + flags = recipes.value(section, "build_flags") + if "DISPLAY_CLASS=" not in flags: + continue + with self.subTest(role=section): + self.assertIn("OLED_AUTO_DETECT=1", flags) + self.assertIn("DISPLAY_CLASS=SSD1306Display", flags) + sources = recipes.value(section, "build_src_filter") + self.assertIn("helpers/ui/SSD1306Display.cpp", sources) + self.assertNotIn("helpers/ui/SH1106Display.cpp", sources) + self.assertNotIn("Adafruit SH110X", recipes.value(section, "lib_deps")) + count += 1 + self.assertGreater(count, 15) + + +if __name__ == "__main__": + unittest.main() diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 4fa7314c7..5298f08b2 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -743,12 +743,14 @@ build_flags = -D AUTO_SHUTDOWN_MILLIVOLTS=3400 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=512 + -D ENABLE_WIFI_INTERFACE ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${heltec_v4_tft.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> lib_deps = diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 5845c57a4..78f56635f 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -211,11 +211,13 @@ build_flags = -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=512 + -D ENABLE_WIFI_INTERFACE ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=8 ; -D MESH_DEBUG=1 build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} + + + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp>