diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index fba64e8c..d9ebacb4 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -229,6 +229,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85 file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86 file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87 + file.read((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88 file.close(); } @@ -265,6 +266,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85 file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86 file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87 + file.write((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index c96f7e01..1f71a9bc 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -318,6 +318,10 @@ bool MyMesh::shouldOverwriteWhenFull() const { return (_prefs.autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) != 0; } +uint8_t MyMesh::getAutoAddMaxHops() const { + return _prefs.autoadd_max_hops; +} + void MyMesh::onContactOverwrite(const uint8_t* pub_key) { _store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage if (_serial->isConnected()) { @@ -1785,12 +1789,16 @@ void MyMesh::handleCmdFrame(size_t len) { } } else if (cmd_frame[0] == CMD_SET_AUTOADD_CONFIG) { _prefs.autoadd_config = cmd_frame[1]; + if (len >= 3) { + _prefs.autoadd_max_hops = min(cmd_frame[2], (uint8_t)64); + } savePrefs(); - writeOKFrame(); + writeOKFrame(); } else if (cmd_frame[0] == CMD_GET_AUTOADD_CONFIG) { int i = 0; out_frame[i++] = RESP_CODE_AUTOADD_CONFIG; out_frame[i++] = _prefs.autoadd_config; + out_frame[i++] = _prefs.autoadd_max_hops; _serial->writeFrame(out_frame, i); } else if (cmd_frame[0] == CMD_GET_ALLOWED_REPEAT_FREQ) { int i = 0; diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 87e6cf33..fe2c19bf 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -119,6 +119,7 @@ protected: bool isAutoAddEnabled() const override; bool shouldAutoAddContactType(uint8_t type) const override; bool shouldOverwriteWhenFull() const override; + uint8_t getAutoAddMaxHops() const override; void onContactsFull() override; void onContactOverwrite(const uint8_t* pub_key) override; bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index ec60c94a..090209c1 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -30,4 +30,5 @@ struct NodePrefs { // persisted to file uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending + uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) }; \ No newline at end of file diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 5ec678c7..33d7edbe 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -141,6 +141,15 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, return; } + // check hop limit for new contacts (0 = no limit, 1 = direct (0 hops), N = up to N-1 hops) + uint8_t max_hops = getAutoAddMaxHops(); + if (max_hops > 0 && packet->getPathHashCount() >= max_hops) { + ContactInfo ci; + populateContactFromAdvert(ci, id, parser, timestamp); + onDiscoveredContact(ci, true, packet->path_len, packet->path); // let UI know + return; + } + from = allocateContactSlot(); if (from == NULL) { ContactInfo ci; diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index fd391b98..ab90d581 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -98,6 +98,7 @@ protected: virtual bool shouldAutoAddContactType(uint8_t type) const { return true; } virtual void onContactsFull() {}; virtual bool shouldOverwriteWhenFull() const { return false; } + virtual uint8_t getAutoAddMaxHops() const { return 0; } // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops virtual void onContactOverwrite(const uint8_t* pub_key) {}; virtual void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0; virtual ContactInfo* processAck(const uint8_t *data) = 0; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index f2f961b9..fd631273 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -717,6 +717,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch LocationProvider * l = _sensors->getLocationProvider(); if (l != NULL) { l->syncTime(); + strcpy(reply, "ok"); + } else { + strcpy(reply, "gps provider not found"); } } else if (memcmp(command, "gps setloc", 10) == 0) { _prefs->node_lat = _sensors->node_lat; @@ -746,7 +749,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->advert_loc_policy = ADVERT_LOC_SHARE; savePrefs(); strcpy(reply, "ok"); - } else if (memcmp(command+11, "prefs", 4) == 0) { + } else if (memcmp(command+11, "prefs", 5) == 0) { _prefs->advert_loc_policy = ADVERT_LOC_PREFS; savePrefs(); strcpy(reply, "ok"); diff --git a/src/helpers/RefCountedDigitalPin.h b/src/helpers/RefCountedDigitalPin.h index 753f6c30..f30c4c58 100644 --- a/src/helpers/RefCountedDigitalPin.h +++ b/src/helpers/RefCountedDigitalPin.h @@ -20,7 +20,10 @@ public: digitalWrite(_pin, _active); } } + void release() { + if (_claims == 0) return; // avoid negative _claims + _claims--; if (_claims == 0) { digitalWrite(_pin, !_active); diff --git a/src/helpers/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index f7dd7a9f..9e783a95 100644 --- a/src/helpers/radiolib/CustomLLCC68Wrapper.h +++ b/src/helpers/radiolib/CustomLLCC68Wrapper.h @@ -2,6 +2,7 @@ #include "CustomLLCC68.h" #include "RadioLibWrappers.h" +#include "SX126xReset.h" class CustomLLCC68Wrapper : public RadioLibWrapper { public: @@ -19,4 +20,6 @@ public: int sf = ((CustomLLCC68 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + + void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } }; diff --git a/src/helpers/radiolib/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h index e4332013..b1f68080 100644 --- a/src/helpers/radiolib/CustomLR1110.h +++ b/src/helpers/radiolib/CustomLR1110.h @@ -20,6 +20,8 @@ class CustomLR1110 : public LR1110 { return len; } + float getFreqMHz() const { return freqMHz; } + bool isReceiving() { uint16_t irq = getIrqStatus(); bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED)); diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index 947bb51d..a1e0a493 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -2,11 +2,13 @@ #include "CustomLR1110.h" #include "RadioLibWrappers.h" +#include "LR11x0Reset.h" class CustomLR1110Wrapper : public RadioLibWrapper { public: CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - bool isReceivingPacket() override { + void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); } + bool isReceivingPacket() override { return ((CustomLR1110 *)_radio)->isReceiving(); } float getCurrentRSSI() override { diff --git a/src/helpers/radiolib/CustomSTM32WLxWrapper.h b/src/helpers/radiolib/CustomSTM32WLxWrapper.h index 9e2d0441..e3e52029 100644 --- a/src/helpers/radiolib/CustomSTM32WLxWrapper.h +++ b/src/helpers/radiolib/CustomSTM32WLxWrapper.h @@ -2,6 +2,7 @@ #include "CustomSTM32WLx.h" #include "RadioLibWrappers.h" +#include "SX126xReset.h" #include class CustomSTM32WLxWrapper : public RadioLibWrapper { @@ -20,4 +21,6 @@ public: int sf = ((CustomSTM32WLx *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + + void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } }; diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index 1afee5e8..5856720b 100644 --- a/src/helpers/radiolib/CustomSX1262Wrapper.h +++ b/src/helpers/radiolib/CustomSX1262Wrapper.h @@ -2,6 +2,7 @@ #include "CustomSX1262.h" #include "RadioLibWrappers.h" +#include "SX126xReset.h" class CustomSX1262Wrapper : public RadioLibWrapper { public: @@ -22,4 +23,6 @@ public: virtual void powerOff() override { ((CustomSX1262 *)_radio)->sleep(false); } + + void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } }; diff --git a/src/helpers/radiolib/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h index 5d7106b4..5149fc43 100644 --- a/src/helpers/radiolib/CustomSX1268Wrapper.h +++ b/src/helpers/radiolib/CustomSX1268Wrapper.h @@ -2,6 +2,7 @@ #include "CustomSX1268.h" #include "RadioLibWrappers.h" +#include "SX126xReset.h" class CustomSX1268Wrapper : public RadioLibWrapper { public: @@ -19,4 +20,6 @@ public: int sf = ((CustomSX1268 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + + void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } }; diff --git a/src/helpers/radiolib/LR11x0Reset.h b/src/helpers/radiolib/LR11x0Reset.h new file mode 100644 index 00000000..d06ffc53 --- /dev/null +++ b/src/helpers/radiolib/LR11x0Reset.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +// Full receiver reset for LR11x0-family chips (LR1110, LR1120, LR1121). +// Warm sleep powers down analog, calibrate(0x3F) refreshes all calibration blocks, +// then re-applies RX settings that calibration may reset. +inline void lr11x0ResetAGC(LR11x0* radio, float freqMHz) { + radio->sleep(true, 0); + radio->standby(RADIOLIB_LR11X0_STANDBY_RC, true); + + radio->calibrate(RADIOLIB_LR11X0_CALIBRATE_ALL); + + // calibrate(0x3F) defaults image calibration to 902-928MHz band. + // Re-calibrate for the actual operating frequency (band=4MHz matches RadioLib default). + radio->calibrateImageRejection(freqMHz - 4.0f, freqMHz + 4.0f); + +#ifdef RX_BOOSTED_GAIN + radio->setRxBoostedGainMode(RX_BOOSTED_GAIN); +#endif +} diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index cf3e1266..2216ca8f 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -53,13 +53,24 @@ void RadioLibWrapper::triggerNoiseFloorCalibrate(int threshold) { } } +void RadioLibWrapper::doResetAGC() { + _radio->sleep(); // warm sleep to reset analog frontend +} + void RadioLibWrapper::resetAGC() { // make sure we're not mid-receive of packet! if ((state & STATE_INT_READY) != 0 || isReceivingPacket()) return; - // NOTE: according to higher powers, just issuing RadioLib's startReceive() will reset the AGC. - // revisit this if a better impl is discovered. + doResetAGC(); state = STATE_IDLE; // trigger a startReceive() + + // Reset noise floor sampling so it reconverges from scratch. + // Without this, a stuck _noise_floor of -120 makes the sampling threshold + // too low (-106) to accept normal samples (~-105), self-reinforcing the + // stuck value even after the receiver has recovered. + _noise_floor = 0; + _num_floor_samples = 0; + _floor_sample_sum = 0; } void RadioLibWrapper::loop() { diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 9ac1bbae..b338b03a 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -16,6 +16,7 @@ protected: void startRecv(); float packetScoreInt(float snr, int sf, int packet_len); virtual bool isReceivingPacket() =0; + virtual void doResetAGC(); public: RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; } diff --git a/src/helpers/radiolib/SX126xReset.h b/src/helpers/radiolib/SX126xReset.h new file mode 100644 index 00000000..39ddb73e --- /dev/null +++ b/src/helpers/radiolib/SX126xReset.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +// Full receiver reset for all SX126x-family chips (SX1262, SX1268, LLCC68, STM32WLx). +// Warm sleep powers down analog, Calibrate(0x7F) refreshes ADC/PLL/image calibration, +// then re-applies RX settings that calibration may reset. +inline void sx126xResetAGC(SX126x* radio) { + radio->sleep(true); + radio->standby(RADIOLIB_SX126X_STANDBY_RC, true); + + uint8_t calData = RADIOLIB_SX126X_CALIBRATE_ALL; + radio->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, &calData, 1, true, false); + radio->mod->hal->delay(5); + uint32_t start = millis(); + while (radio->mod->hal->digitalRead(radio->mod->getGpio())) { + if (millis() - start > 50) break; + radio->mod->hal->yield(); + } + + // Calibrate(0x7F) defaults image calibration to 902-928MHz band. + // Re-calibrate for the actual operating frequency. + radio->calibrateImage(radio->freqMHz); + +#ifdef SX126X_DIO2_AS_RF_SWITCH + radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); +#endif +#ifdef SX126X_RX_BOOSTED_GAIN + radio->setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); +#endif +#ifdef SX126X_REGISTER_PATCH + uint8_t r_data = 0; + radio->readRegister(0x8B5, &r_data, 1); + r_data |= 0x01; + radio->writeRegister(0x8B5, &r_data, 1); +#endif +} diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index a75d378c..f7b08508 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -707,7 +707,9 @@ void EnvironmentSensorManager::loop() { static long next_gps_update = 0; #if ENV_INCLUDE_GPS - _location->loop(); + if (gps_active) { + _location->loop(); + } if (millis() > next_gps_update) { if(gps_active){ diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index 574570a3..1de75327 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -79,7 +79,10 @@ public : if (_pin_en != -1) { digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE); } - if (_peripher_power) _peripher_power->release(); + if (_pin_reset != -1) { + digitalWrite(_pin_reset, GPS_RESET_FORCE); + } + if (_peripher_power) _peripher_power->release(); } bool isEnabled() override { diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index dba05dcf..1dbda126 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -32,6 +32,11 @@ build_flags = -D PIN_TFT_LEDA_CTL=21 ; LEDK (switches on/off via mosfet to create the ground) -D PIN_GPS_RX=33 -D PIN_GPS_TX=34 + -D PIN_GPS_EN=35 ; N-ch MOSFET Q2 drives P-ch high-side switch → active HIGH (default) + -D PIN_GPS_RESET=36 + -D PIN_GPS_RESET_ACTIVE=LOW + -D GPS_BAUD_RATE=115200 + -D ENV_INCLUDE_GPS=1 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/heltec_tracker/target.cpp b/variants/heltec_tracker/target.cpp index 25c2634b..f801bacb 100644 --- a/variants/heltec_tracker/target.cpp +++ b/variants/heltec_tracker/target.cpp @@ -16,7 +16,8 @@ WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +// GPS_EN (GPIO35) drives N-ch MOSFET → P-ch high-side switch; GPS_RESET (GPIO36) active LOW +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); HWTSensorManager sensors = HWTSensorManager(nmea); #ifdef DISPLAY_CLASS @@ -58,18 +59,16 @@ mesh::LocalIdentity radio_new_identity() { void HWTSensorManager::start_gps() { if (!gps_active) { - board.periph_power.claim(); - + _location->begin(); // Claims periph_power via RefCountedDigitalPin gps_active = true; - Serial1.println("$CFGSYS,h35155*68"); + Serial1.println("$CFGSYS,h35155*68"); // Configure GPS for all constellations } } void HWTSensorManager::stop_gps() { if (gps_active) { gps_active = false; - - board.periph_power.release(); + _location->stop(); // Releases periph_power via RefCountedDigitalPin } } diff --git a/variants/heltec_tracker/target.h b/variants/heltec_tracker/target.h index 5296fb2c..29099f46 100644 --- a/variants/heltec_tracker/target.h +++ b/variants/heltec_tracker/target.h @@ -28,6 +28,7 @@ public: const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; + LocationProvider* getLocationProvider() override { return _location; } }; extern HeltecV3Board board; diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index 4975d5cd..bd7f680e 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -6,18 +6,26 @@ void HeltecTrackerV2Board::begin() { pinMode(PIN_ADC_CTRL, OUTPUT); digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive + // Set up digital GPIO registers before releasing RTC hold. The hold latches + // the pad state including function select, so register writes accumulate + // without affecting the pad. On hold release, all changes apply atomically + // (IO MUX switches to digital GPIO with output already HIGH — no glitch). pinMode(P_LORA_PA_POWER, OUTPUT); digitalWrite(P_LORA_PA_POWER,HIGH); + rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER); - rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN); pinMode(P_LORA_PA_EN, OUTPUT); digitalWrite(P_LORA_PA_EN,HIGH); + rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN); pinMode(P_LORA_PA_TX_EN, OUTPUT); digitalWrite(P_LORA_PA_TX_EN,LOW); - periph_power.begin(); - esp_reset_reason_t reason = esp_reset_reason(); + if (reason != ESP_RST_DEEPSLEEP) { + delay(1); // GC1109 startup time after cold power-on + } + + periph_power.begin(); if (reason == ESP_RST_DEEPSLEEP) { long wakeup_source = esp_sleep_get_ext1_wakeup_status(); if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) @@ -48,7 +56,9 @@ void HeltecTrackerV2Board::begin() { rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); //It also needs to be enabled in receive mode + // Hold GC1109 FEM pins during sleep to keep LNA active for RX wake + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); if (pin_wake_btn < 0) { esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index 25d16f2f..af41b4f5 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -17,11 +17,11 @@ build_flags = -D P_LORA_SCLK=9 -D P_LORA_MISO=11 -D P_LORA_MOSI=10 - -D P_LORA_PA_POWER=7 ;power en - -D P_LORA_PA_EN=4 - -D P_LORA_PA_TX_EN=46 ;enable tx - -D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm. - -D MAX_LORA_TX_POWER=22 ;Max SX1262 output + -D P_LORA_PA_POWER=7 ; VFEM_Ctrl - GC1109 LDO power enable + -D P_LORA_PA_EN=4 ; CSD - GC1109 chip enable (HIGH=on) + -D P_LORA_PA_TX_EN=46 ; CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) + -D LORA_TX_POWER=10 ; 10dBm + ~11dB GC1109 gain = ~21dBm output + -D MAX_LORA_TX_POWER=22 ; Max SX1262 output -> ~28dBm at antenna -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 6cad79ab..ce27cfe4 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -10,8 +10,6 @@ void HeltecV4Board::begin() { loRaFEMControl.init(); periph_power.begin(); - - esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_DEEPSLEEP) { long wakeup_source = esp_sleep_get_ext1_wakeup_status(); if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 3bec19f5..68000657 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -24,7 +24,7 @@ build_flags = -D P_LORA_KCT8103L_PA_CTX=5 -D PIN_USER_BTN=0 -D PIN_VEXT_EN=36 - -D PIN_VEXT_EN_ACTIVE=LOW + -D PIN_VEXT_EN_ACTIVE=HIGH -D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm. -D MAX_LORA_TX_POWER=22 ; Max SX1262 output -D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX @@ -56,8 +56,6 @@ build_flags = -D PIN_BOARD_SDA=17 -D PIN_BOARD_SCL=18 -D PIN_OLED_RESET=21 - -D ENV_PIN_SDA=4 - -D ENV_PIN_SCL=3 build_src_filter= ${Heltec_lora32_v4.build_src_filter} lib_deps = ${Heltec_lora32_v4.lib_deps} diff --git a/variants/heltec_v4/target.cpp b/variants/heltec_v4/target.cpp index f971cc60..54fc05e8 100644 --- a/variants/heltec_v4/target.cpp +++ b/variants/heltec_v4/target.cpp @@ -24,7 +24,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #endif #ifdef DISPLAY_CLASS - DISPLAY_CLASS display(&(board.periph_power)); + DISPLAY_CLASS display(NULL); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif diff --git a/variants/rak3401/RAK3401Board.cpp b/variants/rak3401/RAK3401Board.cpp index b9431c92..33e1de42 100644 --- a/variants/rak3401/RAK3401Board.cpp +++ b/variants/rak3401/RAK3401Board.cpp @@ -20,13 +20,29 @@ void RAK3401Board::begin() { Wire.begin(); + // PIN_3V3_EN (WB_IO2, P0.34) controls the 3V3_S switched peripheral rail + // AND the 5V boost regulator (U5) on the RAK13302 that powers the SKY66122 PA. + // Must stay HIGH during radio operation — do not toggle for power saving. pinMode(PIN_3V3_EN, OUTPUT); digitalWrite(PIN_3V3_EN, HIGH); -#ifdef P_LORA_PA_EN - // Initialize RAK13302 1W LoRa transceiver module PA control pin - pinMode(P_LORA_PA_EN, OUTPUT); - digitalWrite(P_LORA_PA_EN, LOW); // Start with PA disabled - delay(10); // Allow PA module to initialize + // Enable SKY66122-11 FEM on the RAK13302 module. + // CSD and CPS are tied together on the RAK13302 PCB, routed to IO3 (P0.21). + // HIGH = FEM active (LNA for RX, PA path available for TX). + // TX/RX switching (CTX) is handled by SX1262 DIO2 via SetDIO2AsRfSwitchCtrl. + pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, HIGH); + delay(1); // SKY66122 turn-on settling time (tON = 3us typ) +} + +#ifdef NRF52_POWER_MANAGEMENT +void RAK3401Board::initiateShutdown(uint8_t reason) { + // Disable SKY66122 FEM (CSD+CPS LOW = shutdown, <1 uA) + digitalWrite(SX126X_POWER_EN, LOW); + + // Disable 3V3 switched peripherals and 5V boost + digitalWrite(PIN_3V3_EN, LOW); + + enterSystemOff(reason); +} #endif -} \ No newline at end of file diff --git a/variants/rak3401/RAK3401Board.h b/variants/rak3401/RAK3401Board.h index 20edf906..3a080d5e 100644 --- a/variants/rak3401/RAK3401Board.h +++ b/variants/rak3401/RAK3401Board.h @@ -38,13 +38,6 @@ public: return "RAK 3401"; } -#ifdef P_LORA_PA_EN - void onBeforeTransmit() override { - digitalWrite(P_LORA_PA_EN, HIGH); // Enable PA before transmission - } - - void onAfterTransmit() override { - digitalWrite(P_LORA_PA_EN, LOW); // Disable PA after transmission to save power - } -#endif + // TX/RX switching is handled by SX1262 DIO2 -> SKY66122 CTX (hardware-timed). + // No onBeforeTransmit/onAfterTransmit overrides needed. }; diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index 7467ceb9..ecea0317 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -11,6 +11,7 @@ build_flags = ${nrf52_base.build_flags} -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX with SKY66122 FEM build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak3401> + diff --git a/variants/rak3401/variant.h b/variants/rak3401/variant.h index 56fe0816..268aec53 100644 --- a/variants/rak3401/variant.h +++ b/variants/rak3401/variant.h @@ -147,8 +147,15 @@ static const uint8_t AREF = PIN_AREF; #define SX126X_BUSY (9) #define SX126X_RESET (4) -#define SX126X_POWER_EN (21) -// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +// SKY66122-11 FEM control on the RAK13302 module: +// CSD + CPS are tied together on the PCB, routed to WisBlock IO3 (P0.21). +// Setting IO3 HIGH enables the FEM (LNA for RX, PA path for TX). +// CTX is connected to SX1262 DIO2 — the radio handles TX/RX switching +// in hardware via SetDIO2AsRfSwitchCtrl (microsecond-accurate, no GPIO needed). +// The 5V boost for the PA is enabled by WB_IO2 (P0.34 = PIN_3V3_EN). +#define SX126X_POWER_EN (21) // P0.21 = IO3 -> SKY66122 CSD+CPS (FEM enable) + +// CTX is driven by SX1262 DIO2, not a GPIO #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 @@ -159,7 +166,6 @@ static const uint8_t AREF = PIN_AREF; #define P_LORA_DIO_1 SX126X_DIO1 #define P_LORA_BUSY SX126X_BUSY #define P_LORA_RESET SX126X_RESET -#define P_LORA_PA_EN 31 // enables 3.3V periphery like GPS or IO Module // Do not toggle this for GPS power savings