diff --git a/vendor/rnode_firmware/Bluetooth.h b/vendor/rnode_firmware/Bluetooth.h index acac49e..4e2f356 100644 --- a/vendor/rnode_firmware/Bluetooth.h +++ b/vendor/rnode_firmware/Bluetooth.h @@ -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) { diff --git a/vendor/rnode_firmware/Boards.h b/vendor/rnode_firmware/Boards.h index 04d1cd8..f71bf0e 100644 --- a/vendor/rnode_firmware/Boards.h +++ b/vendor/rnode_firmware/Boards.h @@ -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 diff --git a/vendor/rnode_firmware/Display.h b/vendor/rnode_firmware/Display.h index b3e7172..ffc77d0 100644 --- a/vendor/rnode_firmware/Display.h +++ b/vendor/rnode_firmware/Display.h @@ -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; diff --git a/vendor/rnode_firmware/RNode_Firmware.ino b/vendor/rnode_firmware/RNode_Firmware.ino index 4c61c28..b3a5089 100644 --- a/vendor/rnode_firmware/RNode_Firmware.ino +++ b/vendor/rnode_firmware/RNode_Firmware.ino @@ -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; diff --git a/vendor/rnode_firmware/TDeckUI.h b/vendor/rnode_firmware/TDeckUI.h index 45c9976..caa0325 100644 --- a/vendor/rnode_firmware/TDeckUI.h +++ b/vendor/rnode_firmware/TDeckUI.h @@ -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() { diff --git a/vendor/rnode_firmware/sx126x.cpp b/vendor/rnode_firmware/sx126x.cpp index 5be7b64..4ed3974 100644 --- a/vendor/rnode_firmware/sx126x.cpp +++ b/vendor/rnode_firmware/sx126x.cpp @@ -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, ®_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, ®_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); } diff --git a/vendor/rnode_firmware/sx126x.h b/vendor/rnode_firmware/sx126x.h index d8e2b00..be605f6 100644 --- a/vendor/rnode_firmware/sx126x.h +++ b/vendor/rnode_firmware/sx126x.h @@ -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); };