rnode: harden t-deck host mode

This commit is contained in:
DeFiDude
2026-06-13 14:06:31 -06:00
parent 10e1b8037e
commit e2dad27836
7 changed files with 185 additions and 27 deletions
+14 -8
View File
@@ -290,7 +290,7 @@ char bt_devname[11];
bool bt_confirm_pin_callback(uint32_t pin) {
// Serial.printf("Confirm PIN callback: %d\n", pin);
return true;
return bt_allow_pairing;
}
void bt_update_passkey() {
@@ -301,6 +301,10 @@ char bt_devname[11];
uint32_t bt_passkey_callback() {
// Serial.println("API passkey request");
if (!bt_allow_pairing) {
SerialBT.disconnect();
return 0;
}
if (pairing_pin == 0) { bt_update_passkey(); }
return pairing_pin;
}
@@ -310,13 +314,15 @@ char bt_devname[11];
}
bool bt_security_request_callback() {
if (bt_allow_pairing) {
// Serial.println("Accepting security request");
return true;
} else {
// Serial.println("Rejecting security request");
return false;
}
if (bt_allow_pairing || bt_bond_count() > 0) {
// Accept explicit pairing, and accept bonded reconnect encryption.
// Requiring the pairing window here breaks already-bonded Android
// reconnects after the timer expires.
return true;
}
SerialBT.disconnect();
return false;
}
void bt_authentication_complete_callback(esp_ble_auth_cmpl_t auth_result) {
+5
View File
@@ -18,6 +18,10 @@
#ifndef BOARDS_H
#define BOARDS_H
#ifndef RNODE_TDECK_DIAG
#define RNODE_TDECK_DIAG 0
#endif
#define PLATFORM_AVR 0x90
#define PLATFORM_ESP32 0x80
#define PLATFORM_NRF52 0x70
@@ -574,6 +578,7 @@
#define DIO2_AS_RF_SWITCH true
#define HAS_BUSY true
#define HAS_TCXO true
#define OCP_TUNED 0x38
// Display on for the rsDeck dual-boot build: node ID, waterfall, BLE pairing PIN.
#define HAS_DISPLAY true
+7
View File
@@ -182,6 +182,8 @@
#if BOARD_MODEL == BOARD_CARDPUTER_ADV
float disp_target_fps = 3;
#elif BOARD_MODEL == BOARD_TDECK
float disp_target_fps = 4;
#else
float disp_target_fps = 7;
#endif
@@ -196,6 +198,7 @@ float epd_update_fps = 0.5;
#define CARDPUTER_ADV_DISPLAY_INTENSITY_DEFAULT 96
#elif BOARD_MODEL == BOARD_TDECK
#define DISPLAY_BLANKING_TIMEOUT 60*1000
#define TDECK_DISPLAY_INTENSITY_DEFAULT 12
#else
#define DISPLAY_BLANKING_TIMEOUT 15*1000
#endif
@@ -604,6 +607,10 @@ bool display_init() {
if (display_intensity == 0xFF) {
display_intensity = CARDPUTER_ADV_DISPLAY_INTENSITY_DEFAULT;
}
#elif BOARD_MODEL == BOARD_TDECK
if (display_intensity == 0xFF) {
display_intensity = TDECK_DISPLAY_INTENSITY_DEFAULT;
}
#endif
display_unblank_intensity = display_intensity;
+76 -8
View File
@@ -265,6 +265,8 @@ void setup() {
eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0x03);
#elif BOARD_MODEL == BOARD_CARDPUTER_ADV
eeprom_update(eeprom_addr(ADDR_CONF_DINT), CARDPUTER_ADV_DISPLAY_INTENSITY_DEFAULT);
#elif BOARD_MODEL == BOARD_TDECK
eeprom_update(eeprom_addr(ADDR_CONF_DINT), TDECK_DISPLAY_INTENSITY_DEFAULT);
#else
eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF);
#endif
@@ -291,7 +293,7 @@ void setup() {
#if HAS_BLUETOOTH || HAS_BLE == true
bt_init();
bt_init_ran = true;
#if (BOARD_MODEL == BOARD_CARDPUTER_ADV || BOARD_MODEL == BOARD_TDECK) && HAS_BLE == true
#if BOARD_MODEL == BOARD_CARDPUTER_ADV && HAS_BLE == true
if (!console_active && bt_ready && bt_enabled && bt_state != BT_STATE_OFF && bt_bond_count() == 0) {
bt_enable_pairing_on_request();
}
@@ -396,6 +398,10 @@ void ISR_VECT receive_callback(int packet_size) {
BaseType_t int_mask;
#endif
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] rx callback packet_size=%d promisc=%d\r\n", packet_size, promisc);
#endif
if (!promisc) {
// The standard operating mode allows large
// packets with a payload up to 500 bytes,
@@ -510,9 +516,22 @@ void ISR_VECT receive_callback(int packet_size) {
// unable to receive the packet.
modem_packet->len = read_len;
memcpy(modem_packet->data, pbuf, read_len); read_len = 0;
#if BOARD_MODEL == BOARD_TDECK
if (!modem_packet_queue || xQueueSend(modem_packet_queue, &modem_packet, 0) != pdPASS) {
#if RNODE_TDECK_DIAG
Serial.println("[RNODE] rx queue failed");
#endif
free(modem_packet);
} else {
#if RNODE_TDECK_DIAG
Serial.printf("[RNODE] rx queued len=%u rssi=%d snr_raw=%u\r\n", modem_packet->len, modem_packet->rssi, modem_packet->snr_raw);
#endif
}
#else
if (!modem_packet_queue || xQueueSendFromISR(modem_packet_queue, &modem_packet, NULL) != pdPASS) {
free(modem_packet);
}
#endif
#endif
}
} else {
@@ -544,6 +563,9 @@ bool startRadio() {
update_radio_lock();
if (!radio_online && !console_active) {
if (!radio_locked && hw_ready) {
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] startRadio freq=%ld bw=%ld sf=%d cr=%d txp=%d\r\n", lora_freq, lora_bw, lora_sf, lora_cr, lora_txp);
#endif
if (!LoRa->begin(lora_freq)) {
// The radio could not be started.
// Indicate this failure over both the
@@ -551,6 +573,9 @@ bool startRadio() {
radio_error = true;
kiss_indicate_error(ERROR_INITRADIO);
led_indicate_error(0);
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.println("[RNODE] startRadio failed");
#endif
return false;
} else {
radio_online = true;
@@ -571,6 +596,9 @@ bool startRadio() {
// that the radio is now on
kiss_indicate_radiostate();
led_indicate_info(3);
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.println("[RNODE] startRadio ok");
#endif
return true;
}
@@ -584,8 +612,10 @@ bool startRadio() {
return false;
}
} else {
// If radio is already on, we silently
// ignore the request.
// If radio is already on, re-arm receive. Host reconnects normally replay
// radio parameters before RADIO_STATE=ON; SX1262 reconfiguration uses
// standby, so the final ON command must put the modem back in RX.
if (radio_online && !console_active) { lora_receive(); }
kiss_indicate_radiostate();
return true;
}
@@ -600,8 +630,11 @@ void handleModemTimeout() {
kiss_indicate_error(ERROR_MODEM_TIMEOUT);
kiss_indicate_error(ERROR_TXFAILED);
led_indicate_error(5);
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.println("[RNODE] modem timeout; restarting radio");
#endif
#if BOARD_MODEL == BOARD_CARDPUTER_ADV
#if BOARD_MODEL == BOARD_CARDPUTER_ADV || BOARD_MODEL == BOARD_TDECK
stopRadio();
delay(50);
startRadio();
@@ -625,6 +658,9 @@ void flush_queue(void) {
if (!queue_flushing) {
queue_flushing = true;
led_tx_on();
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] flush_queue height=%u bytes=%u\r\n", queue_height, queued_bytes);
#endif
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
while (!fifo16_isempty(&packet_starts)) {
@@ -665,6 +701,9 @@ void flush_queue(void) {
void pop_queue() {
if (!queue_flushing) {
queue_flushing = true; led_tx_on();
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] pop_queue height=%u bytes=%u\r\n", queue_height, queued_bytes);
#endif
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
if (!fifo16_isempty(&packet_starts)) {
@@ -764,6 +803,9 @@ void update_airtime() {
void transmit(uint16_t size) {
if (radio_online) {
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] tx begin size=%u promisc=%d\r\n", size, promisc);
#endif
if (!promisc) {
uint16_t written = 0;
uint8_t header = random(256) & 0xF0;
@@ -794,6 +836,9 @@ void transmit(uint16_t size) {
}
add_airtime(written);
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] tx ok written=%u\r\n", written);
#endif
} else {
led_tx_on(); uint16_t written = 0;
@@ -824,6 +869,9 @@ void serial_callback(uint8_t sbyte) {
fifo16_push(&packet_starts, s);
fifo16_push(&packet_lengths, l);
current_packet_start = queue_cursor;
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] queued tx len=%u height=%u bytes=%u\r\n", l, queue_height, queued_bytes);
#endif
}
}
@@ -1391,9 +1439,12 @@ void serial_callback(uint8_t sbyte) {
portMUX_TYPE update_lock = portMUX_INITIALIZER_UNLOCKED;
#endif
extern bool noise_floor_sampled;
bool medium_free() {
update_modem_status();
if (avoid_interference && interference_detected) { return false; }
update_noise_floor();
if (avoid_interference && noise_floor_sampled && interference_detected) { return false; }
return !dcd;
}
@@ -1454,10 +1505,11 @@ void update_modem_status() {
#endif
#if BOARD_MODEL == BOARD_HELTEC32_V4
if (noise_floor > LNA_GD_THRSHLD) { interference_detected = !carrier_detected && (current_rssi > (noise_floor+CSMA_INFR_THRESHOLD_DB)); }
else { interference_detected = !carrier_detected && (current_rssi > LNA_GD_LIMIT); }
if (!noise_floor_sampled) { interference_detected = false; }
else if (noise_floor > LNA_GD_THRSHLD) { interference_detected = !carrier_detected && (current_rssi > (noise_floor+CSMA_INFR_THRESHOLD_DB)); }
else { interference_detected = !carrier_detected && (current_rssi > LNA_GD_LIMIT); }
#else
interference_detected = !carrier_detected && (current_rssi > (noise_floor+CSMA_INFR_THRESHOLD_DB));
interference_detected = noise_floor_sampled && !carrier_detected && (current_rssi > (noise_floor+CSMA_INFR_THRESHOLD_DB));
#endif
if (interference_detected) { if (led_id_filter < LED_ID_TRIG) { led_id_filter += 1; } }
@@ -1673,6 +1725,15 @@ void validate_status() {
void tx_queue_handler() {
if (!airtime_lock && queue_height > 0) {
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
static unsigned long last_txq_diag = 0;
if (millis() - last_txq_diag > 500) {
last_txq_diag = millis();
Serial.printf("[RNODE] txq height=%u bytes=%u dcd=%d intf=%d rssi=%d nf=%d cw=%d target=%lu passed=%lu difs=%ld\r\n",
queue_height, queued_bytes, dcd, interference_detected, current_rssi,
noise_floor, csma_cw, cw_wait_target, cw_wait_passed, difs_wait_start);
}
#endif
if (csma_cw == -1) {
csma_cw = random(cw_min, cw_max);
cw_wait_target = csma_cw * csma_slot_ms;
@@ -1706,9 +1767,16 @@ void work_while_waiting() { loop(); }
void loop() {
if (radio_online) {
#if MODEM == SX1262 && BOARD_MODEL == BOARD_TDECK
LoRa->serviceInterrupt();
#endif
#if MCU_VARIANT == MCU_ESP32
modem_packet_t *modem_packet = NULL;
if(modem_packet_queue && xQueueReceive(modem_packet_queue, &modem_packet, 0) == pdTRUE && modem_packet) {
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] host write rx len=%u rssi=%d snr_raw=%u\r\n", modem_packet->len, modem_packet->rssi, modem_packet->snr_raw);
#endif
host_write_len = modem_packet->len;
last_rssi = modem_packet->rssi;
last_snr_raw = modem_packet->snr_raw;
+19 -7
View File
@@ -10,6 +10,7 @@
void display_unblank();
void bt_enable_pairing();
void bt_disable_pairing();
int bt_bond_count();
extern uint32_t bt_pairing_started;
#define TD_PAIRING_WINDOW_MS 30000
@@ -323,18 +324,29 @@ void td_draw_pair_panel() {
return;
}
// Idle: pairing button
// Idle: paired devices can connect without opening pairing. The same panel
// remains tappable to pair an additional or re-paired host.
int bw = 98; int bh = 48;
int bx = TD_WF_X + (TD_WF_W - bw) / 2;
int by = TD_WF_Y + 72;
bool has_bond = bt_bond_count() > 0;
td_canvas->fillRoundRect(bx, by, bw, bh, 8, TD_CLR_BG_PANEL);
td_canvas->drawRoundRect(bx, by, bw, bh, 8, TD_CLR_ACCENT);
td_canvas->drawRoundRect(bx, by, bw, bh, 8, has_bond ? TD_CLR_GREEN : TD_CLR_ACCENT);
td_canvas->setTextSize(1);
td_canvas->setTextColor(TD_CLR_ACCENT);
td_canvas->setCursor(bx + 25, by + 14);
td_canvas->print("Pair via");
td_canvas->setCursor(bx + 40, by + 28);
td_canvas->print("BLE");
if (has_bond) {
td_canvas->setTextColor(TD_CLR_GREEN);
td_canvas->setCursor(bx + 23, by + 10);
td_canvas->print("BLE ready");
td_canvas->setTextColor(TD_CLR_TEXT_DIM);
td_canvas->setCursor(bx + 19, by + 25);
td_canvas->print("tap to pair");
} else {
td_canvas->setTextColor(TD_CLR_ACCENT);
td_canvas->setCursor(bx + 25, by + 14);
td_canvas->print("Pair via");
td_canvas->setCursor(bx + 40, by + 28);
td_canvas->print("BLE");
}
}
void td_draw_waterfall() {
+57 -4
View File
@@ -164,6 +164,9 @@ sx126x::sx126x() :
_fifo_rx_addr_ptr(0),
_packet({0}),
_preinit_done(false),
#if BOARD_MODEL == BOARD_TDECK
_irq_pending(false),
#endif
_onReceive(NULL)
{ setTimeout(0); }
@@ -308,6 +311,12 @@ void sx126x::readBuffer(uint8_t* buffer, size_t size) {
}
void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
// SX1262 only accepts SetModulationParams from standby. During host
// reconnects, RNode may already be in continuous RX when the host reapplies
// a radio preset; issuing this opcode from RX is silently ignored.
standby();
if (!loraMode()) { return; }
// Because there is no access to these registers on the sx1262, we have
// to set all these parameters at once or not at all.
uint8_t buf[8];
@@ -323,6 +332,12 @@ void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
}
void sx126x::setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) {
// SetPacketParams has the same standby-mode requirement as modulation
// parameters. Keep this in the low-level driver so callers cannot leave the
// modem in a stale packet format after live reconfiguration.
standby();
if (!loraMode()) { return; }
// Because there is no access to these registers on the sx1262, we have
// to set all these parameters at once or not at all.
uint8_t buf[9];
@@ -359,7 +374,11 @@ void sx126x::reset(void) {
digitalWrite(_reset, LOW);
delay(10);
digitalWrite(_reset, HIGH);
delay(10);
#if BOARD_MODEL == BOARD_TDECK
delay(100);
#else
delay(10);
#endif
}
}
@@ -406,6 +425,10 @@ int sx126x::begin(long frequency) {
// M5's Cap LoRa-1262 reference path uses LDO regulator mode.
uint8_t reg_mode = 0x00;
executeOpcode(OP_REGULATOR_MODE_6X, &reg_mode, 1);
#elif BOARD_MODEL == BOARD_TDECK
// LilyGo's integrated SX1262 path is stable in DC-DC regulator mode.
uint8_t reg_mode = 0x01;
executeOpcode(OP_REGULATOR_MODE_6X, &reg_mode, 1);
#endif
calibrate();
@@ -434,7 +457,7 @@ int sx126x::begin(long frequency) {
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
enableDio2RfSwitch();
#if BOARD_MODEL == BOARD_CARDPUTER_ADV
#if BOARD_MODEL == BOARD_CARDPUTER_ADV || BOARD_MODEL == BOARD_TDECK
// Keep the TCXO-backed oscillator as the fallback between TX/RX transitions.
uint8_t fallback = MODE_FALLBACK_STDBY_XOSC_6X;
executeOpcode(OP_RX_TX_FALLBACK_MODE_6X, &fallback, 1);
@@ -730,7 +753,7 @@ void sx126x::onReceive(void(*callback)(int)){
}
void sx126x::receive(int size) {
#if BOARD_MODEL == BOARD_CARDPUTER_ADV
#if BOARD_MODEL == BOARD_CARDPUTER_ADV || BOARD_MODEL == BOARD_TDECK
uint8_t clear[2] = {0xFF, 0xFF};
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clear, 2);
#endif
@@ -775,7 +798,7 @@ void sx126x::enableTCXO() {
#elif BOARD_MODEL == BOARD_TBEAM
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
#elif BOARD_MODEL == BOARD_TDECK
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0xA0, 0x00};
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
#elif BOARD_MODEL == BOARD_T3S3
@@ -825,6 +848,8 @@ void sx126x::setTxPower(int level, int outputPin) {
uint8_t sx126x::getTxPower() { return _txp; }
void sx126x::setFrequency(long frequency) {
standby();
calibrate_image(frequency);
_frequency = frequency;
uint8_t buf[4];
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
@@ -954,11 +979,39 @@ void ISR_VECT sx126x::handleDio0Rise() {
uint8_t rxbuf[2] = {0}; // Read packet length
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
int packetLength = rxbuf[0];
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] sx1262 irq=0x%02X%02X len=%d\r\n", buf[0], buf[1], packetLength);
#endif
if (_onReceive) { _onReceive(packetLength); }
} else {
#if BOARD_MODEL == BOARD_TDECK && RNODE_TDECK_DIAG
Serial.printf("[RNODE] sx1262 irq=0x%02X%02X crc_error\r\n", buf[0], buf[1]);
#endif
}
}
#if BOARD_MODEL == BOARD_TDECK
void sx126x::serviceInterrupt() {
bool pending = false;
noInterrupts();
if (_irq_pending) {
_irq_pending = false;
pending = true;
}
interrupts();
if (pending) {
#if RNODE_TDECK_DIAG
Serial.println("[RNODE] sx1262 irq pending");
#endif
handleDio0Rise();
}
}
void ISR_VECT sx126x::onDio0Rise() { sx126x_modem._irq_pending = true; }
#else
void ISR_VECT sx126x::onDio0Rise() { sx126x_modem.handleDio0Rise(); }
#endif
void sx126x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
void sx126x::enableCrc() { _crcMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
void sx126x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
+7
View File
@@ -53,6 +53,10 @@ public:
void onReceive(void(*callback)(int));
#if BOARD_MODEL == BOARD_TDECK
void serviceInterrupt();
#endif
void receive(int size = 0);
void standby();
void sleep();
@@ -138,6 +142,9 @@ private:
int _fifo_rx_addr_ptr;
uint8_t _packet[255];
bool _preinit_done;
#if BOARD_MODEL == BOARD_TDECK
volatile bool _irq_pending;
#endif
void (*_onReceive)(int);
};