mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-09-26 20:18:04 +00:00
touch: P4 antenna — internal on every boot, confirm before going external
Reworks the P4 antenna setting so the failure mode it protects against is
impossible by default. Transmitting into an external connector with nothing
fitted is what destroys a PA, so the state you get for free after any power
cycle, crash or OTA has to be the on-board antenna.
- The on-board antenna is forced at EVERY boot, in two places: the park in
Xl9535::powerOnSequence() (before the radio object even exists) and a
re-assert in UITask::begin() in case anything touched IO1 in between.
- The choice is now session-only and is never written to flash. That is the
whole point: if "external" could survive a reboot, the safety property is
gone. touchPrefsGet/SetP4Antenna are removed; the p4_antenna byte stays in
the struct as reserved so TOUCH_CFG_VER does not have to rewind.
- Selecting External raises a confirmation naming the MMCX socket and the
risk. showConfirm has no cancel hook, so the dropdown reverts immediately
and is only re-selected from the confirm handler — a dismissed dialog
therefore leaves both the UI and the hardware on internal.
- The dropdown reads the live xl9535.antennaMode() rather than a stored pref,
so it can never claim an antenna the hardware is not actually on.
Also renames the modes from the opaque Auto/Pinned A/Pinned B to Internal /
External / Auto (legacy). The reasoning for treating IO1 as an antenna select
rather than a TX/RX path switch is written up on Xl9535.h: a per-transmit
switch must settle within microseconds of the PA ramping, and an I2C expander
write on a bus shared with touch, the RTC and the fuel gauge cannot do that,
while the SX1262 has DIO2 for exactly that job. Which LEVEL is which antenna is
still unconfirmed against LilyGo's schematic, but the SAFE level is known from
the field: the legacy mode sits LOW to receive and every P4 receives fine at
+12 dB, so LOW demonstrably radiates into something connected, while HIGH was
only ever used for transmit and is the suspect. One named constant
(INTERNAL_LEVEL) flips the mapping if the schematic ever says otherwise.
Builds clean on the P4 IDF target plus the V4 and T-Deck S3 envs (the shared
TouchPrefsStore change); compile-verified only, the P4 is off the USB bus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
fe87ff02c5
commit
b066c379c0
@@ -38,7 +38,7 @@ static bool s_begun = false;
|
||||
// short read (→ treat as absent → defaults); `ver` lets later builds add fields.
|
||||
static const char* KEY_CFG = "cfg";
|
||||
static const uint16_t TOUCH_CFG_MAGIC = 0x5743; // 'WC' (WadaCfg)
|
||||
static const uint8_t TOUCH_CFG_VER = 41; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys; v17 notify_new_contact; v18 kbd_nav OFF by default (reverses v15; T-Deck/V4 only, Tanmatsu stays on); v19 show_sensors_tab; v20 map_show_links; v21 map_style (0=OSM default, 1=OpenTopoMap); v22 tb_nav; v23 scope_direct (opt-in: scope direct/login floods to the region); v24 tb_nav default OFF (experimental); v25 fem_lna (Heltec V4.3 high-gain FEM LNA, opt-in); v26 msg_flash (flash keyboard backlight + wake screen on a new message, opt-in); v27 flood_adv_hrs + local_adv_min (periodic self-advert intervals, the standard MeshCore flood/local advert on a timer); v28 beta_updates (opt-in to test/beta firmware on the OTA update check + install); v29 ui_scale default -> Large/150% (Tanmatsu; bumps the old 100% default, leaves an explicit Large/Huge choice); v30 boot_advert (opt-in one-shot flood self-advert ~6s after boot, all boards, #76); v31 compact_chat (opt-in IRC-style dense chat rows instead of bubbles); v32 clock_floor (highest epoch handed out — monotonic send-timestamp floor across reboots, #89); v33 rx_queue (buffered LoRa receive: drain task + packet ring, experimental, default OFF); v34 web_mirror (web control panel: mirror the live UI to a phone browser + inject taps, opt-in, default OFF); v35 remote_mode (render the UI off-screen at a web resolution instead of the panel; boot mode, default OFF); v36 remote_landscape (remote mode orientation: landscape 800x480 vs portrait 480x800); v37 remote_landscape now defaults ON (remote mode = landscape/desktop by default; one-time flip of existing installs, portrait stays a toggle); v38 web_terminal (web mesh CLI terminal served on the device IP; runtime toggle, mutually exclusive with VNC, default OFF); v40 hist_sync_after (chat-history flush: consecutive off-thread write failures before the blocking loop-task fallback, 0 = never); v41 p4_antenna (T-Display P4: pin the XL9535 RF/antenna switch instead of toggling it per TX; 0=auto keeps the legacy behaviour)
|
||||
static const uint8_t TOUCH_CFG_VER = 41; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys; v17 notify_new_contact; v18 kbd_nav OFF by default (reverses v15; T-Deck/V4 only, Tanmatsu stays on); v19 show_sensors_tab; v20 map_show_links; v21 map_style (0=OSM default, 1=OpenTopoMap); v22 tb_nav; v23 scope_direct (opt-in: scope direct/login floods to the region); v24 tb_nav default OFF (experimental); v25 fem_lna (Heltec V4.3 high-gain FEM LNA, opt-in); v26 msg_flash (flash keyboard backlight + wake screen on a new message, opt-in); v27 flood_adv_hrs + local_adv_min (periodic self-advert intervals, the standard MeshCore flood/local advert on a timer); v28 beta_updates (opt-in to test/beta firmware on the OTA update check + install); v29 ui_scale default -> Large/150% (Tanmatsu; bumps the old 100% default, leaves an explicit Large/Huge choice); v30 boot_advert (opt-in one-shot flood self-advert ~6s after boot, all boards, #76); v31 compact_chat (opt-in IRC-style dense chat rows instead of bubbles); v32 clock_floor (highest epoch handed out — monotonic send-timestamp floor across reboots, #89); v33 rx_queue (buffered LoRa receive: drain task + packet ring, experimental, default OFF); v34 web_mirror (web control panel: mirror the live UI to a phone browser + inject taps, opt-in, default OFF); v35 remote_mode (render the UI off-screen at a web resolution instead of the panel; boot mode, default OFF); v36 remote_landscape (remote mode orientation: landscape 800x480 vs portrait 480x800); v37 remote_landscape now defaults ON (remote mode = landscape/desktop by default; one-time flip of existing installs, portrait stays a toggle); v38 web_terminal (web mesh CLI terminal served on the device IP; runtime toggle, mutually exclusive with VNC, default OFF); v40 hist_sync_after (chat-history flush: consecutive off-thread write failures before the blocking loop-task fallback, 0 = never); v41 p4_antenna (T-Display P4 antenna select; now RESERVED/unused - the choice is session-only so every boot comes up on the on-board antenna)
|
||||
|
||||
// Defaults (kept identical to the historical per-key defaults).
|
||||
static const uint16_t DEFAULT_SCREEN_TIMEOUT_S = 20;
|
||||
@@ -112,7 +112,7 @@ struct __attribute__((packed)) TouchCfg {
|
||||
uint8_t web_terminal; // web mesh-CLI terminal served on the device IP (runtime; exclusive with VNC) — v38 (trailing)
|
||||
uint8_t map_tile_debug; // show the map tile-pipeline diagnostic overlay (bool, 0=off) — v39 (trailing)
|
||||
uint8_t hist_sync_after; // chat-history flush: consecutive off-thread write failures before falling back to the blocking loop-task write; 0 = never — v40 (trailing)
|
||||
uint8_t p4_antenna; // T-Display P4 LoRa antenna/RF-switch (XL9535 IO1): 0=auto (legacy per-TX toggle), 1=pin LOW, 2=pin HIGH — v41 (trailing)
|
||||
uint8_t p4_antenna; // RESERVED (was: T-Display P4 antenna select). The antenna choice is session-only by design and is never stored — see the note further down — v41 (trailing)
|
||||
};
|
||||
|
||||
static TouchCfg s_cfg;
|
||||
@@ -159,7 +159,7 @@ static void cfgSetDefaults(TouchCfg& c) {
|
||||
c.lock_color = DEFAULT_LOCK_COLOR;
|
||||
c.accent = DEFAULT_ACCENT;
|
||||
c.gps_baud = 0; // 0 sentinel -> getter returns caller fallback
|
||||
c.p4_antenna = 0; // 0 = auto: legacy per-TX toggle (unchanged behaviour)
|
||||
c.p4_antenna = 0; // reserved, unused: the P4 antenna choice is never persisted
|
||||
c.sig_probe_en = DEFAULT_SIG_PROBE_EN;
|
||||
c.sig_poll_min = DEFAULT_SIG_POLL_MIN;
|
||||
c.tz_zone = 0; // 0 = Europe (CET/CEST) — preserves prior behaviour
|
||||
@@ -410,26 +410,15 @@ bool touchPrefsSetScreenTimeoutSecs(uint16_t seconds) {
|
||||
static const uint16_t SIG_POLL_MIN_MINS = 1; // 1 min = 60 s (the old fixed cadence)
|
||||
static const uint16_t SIG_POLL_MAX_MINS = 1440;
|
||||
|
||||
// --- T-Display P4 LoRa antenna / RF-switch mode ------------------------------
|
||||
// XL9535 IO1 drives the board's SKY13453. Our variant assumes it is a TX/RX path switch and
|
||||
// toggles it around every transmit (target.h onBeforeTransmit/onAfterTransmit), but that was
|
||||
// never validated on hardware ("polarity TBD on-device") and the expander has NO separate
|
||||
// internal/external antenna line -- while other P4 firmware DOES offer an internal/external
|
||||
// choice. If IO1 is in fact the ANTENNA select, toggling it per TX means transmitting on one
|
||||
// antenna and receiving on the other, which matches a field report of -10 dB at the repeater
|
||||
// against +12 dB inbound on a unit with an external antenna fitted (symmetric on the same
|
||||
// hardware under other firmware). 0 keeps the legacy toggle so nothing changes by default;
|
||||
// 1/2 pin the line so both directions use one antenna and the user can find out which is which.
|
||||
uint8_t touchPrefsGetP4Antenna() {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
return s_cfg.p4_antenna > 2 ? 0 : s_cfg.p4_antenna;
|
||||
}
|
||||
bool touchPrefsSetP4Antenna(uint8_t mode) {
|
||||
if (mode > 2) mode = 0;
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
s_cfg.p4_antenna = mode;
|
||||
return cfgFlush();
|
||||
}
|
||||
// --- T-Display P4 LoRa antenna select — deliberately NOT persisted -----------
|
||||
// XL9535 IO1 drives the board's SKY13453 antenna switch (full reasoning in Xl9535.h). The
|
||||
// getter/setter that used to live here are gone on purpose: the choice is session-only, so
|
||||
// there is nothing to store. Every boot forces the on-board antenna, in two places — the park
|
||||
// in Xl9535::powerOnSequence() and the re-assert in UITask::begin() — because the external
|
||||
// MMCX may have no antenna fitted, and keying a PA into an open connector damages it. That
|
||||
// safety property only holds if a power cycle cannot restore "external", which means the
|
||||
// choice must never reach flash. p4_antenna stays as a reserved trailing byte: dropping it
|
||||
// would rewind TOUCH_CFG_VER on devices already carrying a v41 blob, for no gain.
|
||||
|
||||
bool touchPrefsGetSigProbeEnabled() {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
|
||||
@@ -553,10 +553,8 @@ bool touchPrefsGetSigProbeEnabled();
|
||||
bool touchPrefsSetSigProbeEnabled(bool on);
|
||||
uint16_t touchPrefsGetSigPollMins();
|
||||
|
||||
/** T-Display P4 LoRa antenna / RF-switch mode: 0 = auto (legacy per-TX toggle of XL9535 IO1),
|
||||
* 1 = pin the line LOW, 2 = pin it HIGH. See the notes in the .cpp. */
|
||||
uint8_t touchPrefsGetP4Antenna();
|
||||
bool touchPrefsSetP4Antenna(uint8_t mode);
|
||||
/* NB: there is deliberately no touchPrefsGet/SetP4Antenna(). The T-Display P4 antenna choice is
|
||||
* session-only so that every boot comes up on the on-board antenna — see the note in the .cpp. */
|
||||
bool touchPrefsSetSigPollMins(uint16_t mins);
|
||||
|
||||
#endif
|
||||
|
||||
+44
-17
@@ -9550,12 +9550,35 @@ static void radioScopeDirectToggleCb(lv_event_t* e) {
|
||||
the_mesh.setScopeDirectFloods(on);
|
||||
}
|
||||
#if defined(HAS_TDISPLAY_P4)
|
||||
// T-Display P4 LoRa antenna / RF-switch mode. Persists + applies live (no reboot).
|
||||
// T-Display P4 LoRa antenna select. Applies live (no reboot) but is deliberately SESSION-ONLY:
|
||||
// every boot re-forces the on-board antenna (see the boot-apply in begin() and the park in
|
||||
// Xl9535::powerOnSequence). Transmitting into an external connector with no antenna fitted is
|
||||
// what kills a PA, so the safe state has to be the one you get for free after a power cycle,
|
||||
// and picking the external antenna has to be a fresh, explicit decision each time.
|
||||
static lv_obj_t* s_p4_ant_dd = nullptr; // showConfirm takes a bare callback, so stash the widget
|
||||
|
||||
static void radioP4AntennaExternalApply() {
|
||||
xl9535.setAntennaMode(Xl9535::ANT_EXTERNAL); // takes effect on the very next transmit
|
||||
if (s_p4_ant_dd) lv_dropdown_set_selected(s_p4_ant_dd, Xl9535::ANT_EXTERNAL);
|
||||
if (g_lv.task) g_lv.task->showAlert(TR("External antenna selected"), 1200);
|
||||
}
|
||||
|
||||
static void radioP4AntennaSelectCb(lv_event_t* e) {
|
||||
if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
|
||||
const uint8_t m = (uint8_t)lv_dropdown_get_selected(lv_event_get_target(e));
|
||||
touchPrefsSetP4Antenna(m);
|
||||
xl9535.setAntennaMode(m); // takes effect on the very next transmit
|
||||
lv_obj_t* dd = lv_event_get_target(e);
|
||||
const uint8_t m = (uint8_t)lv_dropdown_get_selected(dd);
|
||||
if (m == Xl9535::ANT_EXTERNAL) {
|
||||
// Revert the widget FIRST and only re-select it from the confirm handler: showConfirm has no
|
||||
// cancel callback, so a dismissed dialog must leave both the UI and the hardware on internal.
|
||||
s_p4_ant_dd = dd;
|
||||
lv_dropdown_set_selected(dd, xl9535.antennaMode());
|
||||
showConfirm(TR("Switch to the external antenna?\n\nMake sure an antenna is actually connected "
|
||||
"to the MMCX socket first. Transmitting with nothing attached can damage the "
|
||||
"radio.\n\nResets to the on-board antenna on every reboot."),
|
||||
TR("Switch"), radioP4AntennaExternalApply);
|
||||
return;
|
||||
}
|
||||
xl9535.setAntennaMode(m);
|
||||
}
|
||||
#endif
|
||||
#if defined(HELTEC_LORA_V4_TFT)
|
||||
@@ -9836,19 +9859,18 @@ static void buildRadioSettings() {
|
||||
mk_section("SIGNAL");
|
||||
|
||||
#if defined(HAS_TDISPLAY_P4)
|
||||
// T-Display P4: the SKY13453 on XL9535 IO1. The firmware assumes it is a TX/RX path switch and
|
||||
// flips it around every transmit, but that was never confirmed on hardware and this board has no
|
||||
// other antenna-select line — while other P4 firmware offers an internal/external choice. If IO1
|
||||
// IS the antenna select, toggling it per transmit means sending on one antenna and listening on
|
||||
// the other, which matches reports of a ~20 dB outbound deficit with a healthy inbound signal.
|
||||
// Pinning the line makes both directions use ONE antenna. Try each and keep whichever gives
|
||||
// roughly equal SNR in both directions (Trace SNR to a nearby repeater).
|
||||
// T-Display P4 antenna select: the SKY13453 on XL9535 IO1 (see the long note on Xl9535.h for why
|
||||
// this is an antenna switch and not the TX/RX path). Internal is the default and is re-forced at
|
||||
// every boot; external is session-only and gated behind a confirmation, because keying the PA
|
||||
// into an empty MMCX is how you destroy one. "Auto" keeps the old per-transmit toggle purely so
|
||||
// the two can be compared — Trace SNR to a nearby repeater should read roughly equal in both
|
||||
// directions on the correct setting, and lopsided on the wrong one.
|
||||
mk_label("LoRa antenna (P4)");
|
||||
{
|
||||
lv_obj_t* dd = lv_dropdown_create(body);
|
||||
lv_obj_set_size(dd, lv_pct(100), SC(34));
|
||||
lv_obj_set_pos(dd, 2, y);
|
||||
lv_dropdown_set_options(dd, TR("Auto (switch per transmit)\nPinned A\nPinned B"));
|
||||
lv_dropdown_set_options(dd, TR("Internal (on-board)\nExternal (MMCX)\nAuto (legacy, per transmit)"));
|
||||
lv_obj_set_style_text_font(dd, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(dd, lv_color_hex(COLOR_PANEL), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(dd, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
@@ -9857,12 +9879,13 @@ static void buildRadioSettings() {
|
||||
lv_obj_set_style_bg_color(antlist, lv_color_hex(COLOR_PANEL), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(antlist, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_font(antlist, &g_font_12, LV_PART_MAIN);
|
||||
lv_dropdown_set_selected(dd, touchPrefsGetP4Antenna());
|
||||
lv_dropdown_set_selected(dd, xl9535.antennaMode()); // live state, not a stored pref
|
||||
lv_obj_add_event_cb(dd, radioP4AntennaSelectCb, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
lv_obj_add_event_cb(dd, clampDropdownListCb, LV_EVENT_CLICKED, nullptr);
|
||||
y += SC(44);
|
||||
y += settingsRowLabel(body, y, 0,
|
||||
TR("If sending is much weaker than receiving, try Pinned A / B and compare Trace SNR both ways."),
|
||||
TR("Always starts on the on-board antenna after a reboot. Only pick External "
|
||||
"with an antenna fitted to the MMCX socket."),
|
||||
COLOR_SUB, &g_font_12, 0) + 2;
|
||||
}
|
||||
#endif
|
||||
@@ -44419,9 +44442,13 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
if (board.femLnaControllable()) board.setFemLnaEnable(touchPrefsGetFemLna());
|
||||
#endif
|
||||
#if defined(HAS_TDISPLAY_P4)
|
||||
// T-Display P4 LoRa antenna / RF-switch: apply the saved mode at boot. Default 0 = auto,
|
||||
// which is the original per-transmit toggle, so an untouched device behaves exactly as before.
|
||||
xl9535.setAntennaMode(touchPrefsGetP4Antenna());
|
||||
// T-Display P4 antenna select: force the on-board antenna on EVERY boot, deliberately
|
||||
// ignoring whatever was chosen last session. The external MMCX may have nothing screwed
|
||||
// onto it, and transmitting into an open connector is what damages the PA — so the state
|
||||
// you get for free after any power cycle, crash or OTA has to be the safe one. Choosing
|
||||
// external is a per-session, confirmed action (radioP4AntennaSelectCb). This re-asserts
|
||||
// what powerOnSequence() already parked, in case anything touched IO1 in between.
|
||||
xl9535.setAntennaMode(Xl9535::ANT_INTERNAL);
|
||||
#endif
|
||||
|
||||
// Buffered LoRa receive (experimental, default OFF): apply the saved opt-in.
|
||||
|
||||
@@ -99,7 +99,10 @@ void Xl9535::powerOnSequence() {
|
||||
// OFF and made every mount fail (0x107 OCR timeout / 0x109 CRC). Drive it LOW, always.
|
||||
write(IO_SD_EN, false); // SD slot power ON (active-low)
|
||||
write(IO_SX1262_RST, true); // park high; radio_init pulses it
|
||||
write(IO_RF_SWITCH, false); // RX path
|
||||
// Antenna select: park on the on-board antenna before the radio exists, so the first transmit
|
||||
// after any boot cannot key the PA into a possibly-empty external MMCX. Choosing the external
|
||||
// antenna is a deliberate, confirmed, session-only action from Settings -> Radio.
|
||||
write(IO_RF_SWITCH, INTERNAL_LEVEL);
|
||||
write(IO_TOUCH_RST, true); // release touch (HI8561 driver probes it)
|
||||
// NB: SCREEN_RST is intentionally NOT touched here — RM69A10Display::begin() sequences it with
|
||||
// the DSI-PHY LDO (HIGH->LOW->HIGH, 200 ms each), exactly as the working Meck-P4 app_main does.
|
||||
|
||||
@@ -53,28 +53,38 @@ public:
|
||||
// Convenience for the SX1262 glue (RESET + DIO1 live here).
|
||||
void sx1262Reset(); // active-low pulse on IO_SX1262_RST
|
||||
bool sx1262Dio1() { return read(IO_SX1262_DIO1); }
|
||||
// IO1 drives the board's SKY13453. We ASSUME it is a TX/RX path switch and flip it around every
|
||||
// transmit, but that was never validated on hardware, and this expander has NO separate
|
||||
// internal/external antenna line — while other P4 firmware DOES offer an internal/external
|
||||
// choice. If IO1 is really the ANTENNA select, toggling it per TX means transmitting on one
|
||||
// antenna and receiving on the other, which is exactly what a field report of -10 dB outbound
|
||||
// against +12 dB inbound looks like on a unit with an external antenna fitted (and symmetric on
|
||||
// that same hardware under other firmware). Until it is confirmed on a device, let the user PIN
|
||||
// the line instead of toggling it:
|
||||
// 0 = auto -> legacy behaviour, toggle per TX (default; nothing changes)
|
||||
// 1 = LOW -> hold low for both TX and RX
|
||||
// 2 = HIGH -> hold high for both TX and RX
|
||||
// Whichever pinned setting gives symmetric SNR in both directions is the right antenna — and
|
||||
// that answer also tells us what IO1 actually is.
|
||||
// IO1 drives the board's SKY13453 SP2T. It is almost certainly the ANTENNA select (on-board vs
|
||||
// the external MMCX), NOT a TX/RX path switch: a per-transmit switch has to settle within
|
||||
// microseconds of the PA ramping, and an I2C expander write takes hundreds of microseconds on a
|
||||
// bus shared with the touch panel, the RTC and the fuel gauge — it physically cannot keep up.
|
||||
// The SX1262 has DIO2 for exactly that job. So the original per-TX toggle transmitted on one
|
||||
// antenna while listening on the other, which is what a field report of -10 dB outbound against
|
||||
// +12 dB inbound looks like (and symmetric on the same hardware under other firmware).
|
||||
//
|
||||
// Which LEVEL is which antenna is still not confirmed against LilyGo's schematic. But the SAFE
|
||||
// level IS known, from the field rather than the datasheet: in the legacy per-TX mode the line
|
||||
// sits LOW to receive, and every P4 receives perfectly well at +12 dB, so LOW is demonstrably a
|
||||
// path that radiates into something connected. HIGH was only ever used for transmit and is the
|
||||
// suspect one. That fits LOW = the on-board antenna (permanently attached, always safe to
|
||||
// transmit into) and HIGH = the external MMCX (an open circuit when nothing is fitted, which is
|
||||
// what you must never key a PA into). If the schematic ever says otherwise, flip this ONE line.
|
||||
static constexpr bool INTERNAL_LEVEL = false;
|
||||
|
||||
enum AntMode : uint8_t {
|
||||
ANT_INTERNAL = 0, // pinned to the on-board antenna — the safe default, re-forced at every boot
|
||||
ANT_EXTERNAL = 1, // pinned to the external MMCX — needs an antenna fitted, never persisted
|
||||
ANT_AUTO = 2, // legacy per-TX toggle — diagnostic only, kept for A/B comparison
|
||||
};
|
||||
|
||||
void setAntennaMode(uint8_t mode) {
|
||||
_ant_mode = (mode > 2) ? 0 : mode;
|
||||
if (_ant_mode) write(IO_RF_SWITCH, _ant_mode == 2); // pinned: set once, never toggled again
|
||||
else write(IO_RF_SWITCH, false); // auto: back to the idle state
|
||||
_ant_mode = (mode > ANT_AUTO) ? ANT_INTERNAL : mode;
|
||||
if (_ant_mode == ANT_EXTERNAL) write(IO_RF_SWITCH, !INTERNAL_LEVEL);
|
||||
else write(IO_RF_SWITCH, INTERNAL_LEVEL); // internal, and auto's idle/RX state
|
||||
}
|
||||
uint8_t antennaMode() const { return _ant_mode; }
|
||||
void rfSwitchTx(bool tx) {
|
||||
if (_ant_mode) return; // pinned — the path must NOT differ between TX and RX
|
||||
write(IO_RF_SWITCH, tx); // auto (legacy): assumes a TX/RX switch; polarity unverified
|
||||
if (_ant_mode != ANT_AUTO) return; // pinned — the path must NOT differ between TX and RX
|
||||
write(IO_RF_SWITCH, tx); // legacy only: assumes a TX/RX switch, polarity unverified
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user