diff --git a/build-iotthinks.sh b/build-iotthinks.sh new file mode 100644 index 00000000..7c654482 --- /dev/null +++ b/build-iotthinks.sh @@ -0,0 +1,96 @@ +# sh ./build-repeaters-iotthinks.sh +export FIRMWARE_VERSION="PowerSaving15" + +############# Repeaters ############# +# Commonly-used boards +## ESP32 - 12 boards +sh build.sh build-firmware \ +Heltec_v3_repeater \ +Heltec_WSL3_repeater \ +heltec_v4_repeater \ +Station_G2_repeater \ +T_Beam_S3_Supreme_SX1262_repeater \ +Tbeam_SX1262_repeater \ +LilyGo_T3S3_sx1262_repeater \ +Xiao_S3_WIO_repeater \ +Xiao_C3_repeater \ +Xiao_C6_repeater_ \ +Heltec_E290_repeater \ +Heltec_Wireless_Tracker_repeater + +## NRF52 - 13 boards +sh build.sh build-firmware \ +RAK_4631_repeater \ +Heltec_t114_repeater \ +Xiao_nrf52_repeater \ +Heltec_mesh_solar_repeater \ +ProMicro_repeater \ +SenseCap_Solar_repeater \ +t1000e_repeater \ +LilyGo_T-Echo_repeater \ +WioTrackerL1_repeater \ +RAK_3401_repeater \ +RAK_WisMesh_Tag_repeater \ +GAT562_30S_Mesh_Kit_repeater \ +GAT562_Mesh_Tracker_Pro_repeater + +## ESP32, SX1276 - 3 boards +sh build.sh build-firmware \ +Heltec_v2_repeater \ +LilyGo_TLora_V2_1_1_6_repeater \ +Tbeam_SX1276_repeater + +## Ikoka - 3 boards +sh build.sh build-firmware \ +ikoka_nano_nrf_22dbm_repeater \ +ikoka_nano_nrf_30dbm_repeater \ +ikoka_nano_nrf_33dbm_repeater + +############# Room Server ############# +# ESP32 +sh build.sh build-firmware \ +Heltec_v3_room_server \ +heltec_v4_room_server + +# NRF52 +sh build.sh build-firmware \ +RAK_4631_room_server \ +Heltec_t114_room_server \ +Xiao_nrf52_room_server \ +t1000e_room_server \ +WioTrackerL1_room_server \ +RAK_3401_room_server + +############# Companions BLE ############# +# ESP32 +sh build.sh build-firmware \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps_femoff \ +Xiao_S3_WIO_companion_radio_ble \ +Heltec_Wireless_Paper_companion_radio_ble + +# NRF52 +sh build.sh build-firmware \ +RAK_4631_companion_radio_ble \ +Heltec_t114_companion_radio_ble \ +Xiao_nrf52_companion_radio_ble \ +t1000e_companion_radio_ble \ +LilyGo_T-Echo_companion_radio_ble \ +WioTrackerL1_companion_radio_ble \ +RAK_3401_companion_radio_ble \ +RAK_WisMesh_Tag_companion_radio_ble + +############# Companions USB ############# +sh build.sh build-firmware \ +Heltec_v3_companion_radio_usb + +############# Companions BLE PS ############# +sh build.sh build-firmware \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_3_companion_radio_ble_ps_femoff \ +Xiao_C3_companion_radio_ble_ps \ +Xiao_S3_companion_radio_ble_ps \ +Xiao_S3_WIO_companion_radio_ble_ps \ +Heltec_v2_companion_radio_ble_ps diff --git a/build.sh b/build.sh index 30b73b5f..9ef20d11 100755 --- a/build.sh +++ b/build.sh @@ -743,4 +743,3 @@ main() { } main "$@" - diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9b4723e3..c771d379 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -297,6 +297,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the LoRa FEM receive-path gain state on supported boards +**Usage:** +- `get radio.fem.rxgain` +- `set radio.fem.rxgain ` + +**Parameters:** +- `state`: `on`|`off` + +**Notes:** +- This controls the external LoRa FEM receive-path LNA where the board supports it. +- This is separate from `radio.rxgain`, which controls the radio chip receive gain mode. + +--- + ### System #### View or change this node's name diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index c7988bb3..362ba68a 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -233,6 +233,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 + file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } @@ -273,6 +274,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 + file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index e8c1914b..8a6f63c5 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -61,6 +61,8 @@ #define CMD_SEND_CHANNEL_DATA 62 #define CMD_SET_DEFAULT_FLOOD_SCOPE 63 #define CMD_GET_DEFAULT_FLOOD_SCOPE 64 +#define CMD_GET_RADIO_FEM_RXGAIN 65 +#define CMD_SET_RADIO_FEM_RXGAIN 66 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -876,6 +878,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.rx_boosted_gain = 1; // enabled by default #endif #endif + _prefs.radio_fem_rxgain = 1; } void MyMesh::begin(bool has_display) { @@ -925,6 +928,7 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours + _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -954,6 +958,7 @@ void MyMesh::begin(bool has_display) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); } @@ -1798,6 +1803,30 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } + } else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) { + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else { + out_frame[0] = RESP_CODE_OK; + uint8_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; + memcpy(&out_frame[1], &value, 1); + _serial->writeFrame(out_frame, 2); + } + } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { + uint8_t value = cmd_frame[1]; + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else if (value <= 1) { + _prefs.radio_fem_rxgain = value; + if (board.setLoRaFemLnaEnabled(value != 0)) { + savePrefs(); + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } + } else { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } } else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) { // FUTURE use: uint8_t reserved = cmd_frame[1]; uint8_t *pub_key = &cmd_frame[2]; @@ -2191,3 +2220,11 @@ bool MyMesh::advert() { return false; } } + +// To check if there is pending work +bool MyMesh::hasPendingWork() const { +#if defined(WITH_BRIDGE) + if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep +#endif + return _mgr->getOutboundTotal() > 0; +} diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index aeff591c..ad0909bc 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -165,7 +165,7 @@ protected: public: void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); } - + #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0"); @@ -177,6 +177,9 @@ public: } #endif + // To check if there is pending work + bool hasPendingWork() const; + private: void writeOKFrame(); void writeErrFrame(uint8_t err_code); diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 48c381ce..ecb117bd 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,9 +29,10 @@ struct NodePrefs { // persisted to file uint32_t gps_interval; // GPS read interval in seconds uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting 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) char default_scope_name[31]; uint8_t default_scope_key[16]; -}; \ No newline at end of file +}; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 876dc9c3..bb350fd0 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -2,6 +2,11 @@ #include #include "MyMesh.h" +#ifdef ESP32_PLATFORM +#include "esp_pm.h" +#include "esp_bt.h" +#endif + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -42,8 +47,8 @@ static uint32_t _atoi(const char* sp) { #define TCP_PORT 5000 #endif #elif defined(BLE_PIN_CODE) - #include - SerialBLEInterface serial_interface; + #include + SerialBLEInterface serial_interface; #elif defined(SERIAL_RX) #include ArduinoSerialInterface serial_interface; @@ -220,6 +225,33 @@ void setup() { #ifdef DISPLAY_CLASS ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved #endif + +#ifdef ESP32_PLATFORM + // Enable BLE sleep + esp_err_t errBLESleep = esp_bt_sleep_enable(); + if (errBLESleep == ESP_OK) { + Serial.println("Bluetooth sleep enabled successfully"); + } else { + Serial.printf("Bluetooth sleep enable failed: %s\n", esp_err_to_name(errBLESleep)); + } + +#if CONFIG_IDF_TARGET_ESP32C3 + esp_pm_config_esp32c3_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32S3 + esp_pm_config_esp32s3_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32 + esp_pm_config_esp32_t pm_config; +#endif + + // Configure Power Management + pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 40, .light_sleep_enable = true }; + esp_err_t errPM = esp_pm_configure(&pm_config); + if (errPM == ESP_OK) { + Serial.println("Power Management configured successfully"); + } else { + Serial.printf("Power Management failed to configure: %d\r\n", errPM); + } +#endif } void loop() { @@ -229,4 +261,14 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); + + if (!the_mesh.hasPendingWork()) { +#if defined(NRF52_PLATFORM) + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible +#else if defined(ESP32_PLATFORM) + if (!serial_interface.isReadBusy() && !serial_interface.isWriteBusy()) { // BLE is not busy + vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep + } +#endif + } } diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 29b126fc..3e3a16ae 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1206,6 +1206,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_boosted_gain = 1; // enabled by default; #endif #endif + _prefs.radio_fem_rxgain = 1; pending_discover_tag = 0; pending_discover_until = 0; @@ -1261,6 +1262,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index acb46325..ac6958ba 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index e37078ce..17a3c192 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -20,8 +20,7 @@ void halt() { static char command[160]; // For power saving -unsigned long lastActive = 0; // mark last active time -unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot +unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot #if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_) static unsigned long userBtnDownAt = 0; @@ -40,9 +39,6 @@ void setup() { delay(5000); #endif - // For power saving - lastActive = millis(); // mark last active time since boot - #ifdef DISPLAY_CLASS if (display.begin()) { display.startFrame(); @@ -155,16 +151,12 @@ void loop() { rtc_clock.tick(); if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { - #if defined(NRF52_PLATFORM) +#if defined(NRF52_PLATFORM) board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible - #else - if (the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep - board.sleep(1800); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet - lastActive = millis(); - nextSleepinSecs = 5; // Default: To work for 5s and sleep again - } else { - nextSleepinSecs += 5; // When there is pending work, to work another 5s +#else + if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(30); // Sleep. Wake up after some seconds or when receiving a LoRa packet } - #endif +#endif } } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 145fb0fd..bdda8194 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -652,6 +652,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; next_post_idx = 0; next_client_idx = 0; @@ -693,6 +694,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); @@ -1022,3 +1024,11 @@ void MyMesh::loop() { uptime_millis += now - last_millis; last_millis = now; } + +// To check if there is pending work +bool MyMesh::hasPendingWork() const { +#if defined(WITH_BRIDGE) + if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep +#endif + return _mgr->getOutboundTotal() > 0; +} diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 1b35ae95..410ffef0 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -222,4 +222,7 @@ public: void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); + + // To check if there is pending work + bool hasPendingWork() const; }; diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp index 42bc14d4..6445baf6 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 825fb007..55179809 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -18,6 +18,9 @@ void halt() { static char command[MAX_POST_TEXT_LEN+1]; +// For power saving +unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot + void setup() { Serial.begin(115200); delay(1000); @@ -113,4 +116,14 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); + + if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { +#if defined(NRF52_PLATFORM) + board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible +#else + if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(1800); // Sleep. Wake up after 30 minutes or when receiving a LoRa packet + } +#endif + } } diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index b8fe1e57..05b4a9dd 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -731,6 +731,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; memset(default_scope.key, 0, sizeof(default_scope.key)); } @@ -766,6 +767,7 @@ void SensorMesh::begin(FILESYSTEM* fs) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/examples/simple_sensor/UITask.cpp b/examples/simple_sensor/UITask.cpp index 0e78fee0..698b9fd2 100644 --- a/examples/simple_sensor/UITask.cpp +++ b/examples/simple_sensor/UITask.cpp @@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/src/MeshCore.h b/src/MeshCore.h index 2db1d4c3..a79bd8b0 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -52,12 +52,16 @@ public: virtual void onAfterTransmit() { } virtual void reboot() = 0; virtual void powerOff() { /* no op */ } + virtual uint32_t getIRQGpio() { return -1; } // not supported. Returns DIO1 (SX1262) and DIO0 (SX127x) virtual void sleep(uint32_t secs) { /* no op */ } virtual uint32_t getGpio() { return 0; } virtual void setGpio(uint32_t values) {} virtual uint8_t getStartupReason() const = 0; virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported + virtual bool setLoRaFemLnaEnabled(bool enable) { return false; } + virtual bool canControlLoRaFemLna() const { return false; } + virtual bool isLoRaFemLnaEnabled() const { return false; } // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } diff --git a/src/helpers/BaseSerialInterface.h b/src/helpers/BaseSerialInterface.h index e6092765..ddde4830 100644 --- a/src/helpers/BaseSerialInterface.h +++ b/src/helpers/BaseSerialInterface.h @@ -15,6 +15,7 @@ public: virtual bool isConnected() const = 0; + virtual bool isReadBusy() const = 0; virtual bool isWriteBusy() const = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; virtual size_t checkRecvFrame(uint8_t dest[]) = 0; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 9460a28f..357da210 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -3,6 +3,8 @@ #include "TxtDataHelpers.h" #include "AdvertDataHelpers.h" #include +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) #ifndef BRIDGE_MAX_BAUD #define BRIDGE_MAX_BAUD 115200 @@ -115,10 +117,24 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - file.read((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 291 + uint8_t legacy_retry_attempts_or_radio_fem_rxgain = _prefs->direct_retry_attempts; + size_t legacy_retry_attempts_read = file.read((uint8_t *)&legacy_retry_attempts_or_radio_fem_rxgain, + sizeof(legacy_retry_attempts_or_radio_fem_rxgain)); // 291 + if (legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain)) { + _prefs->direct_retry_attempts = legacy_retry_attempts_or_radio_fem_rxgain; + } file.read((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 292 file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 294 - // next: 296 + size_t radio_fem_rxgain_read = file.read((uint8_t *)&_prefs->radio_fem_rxgain, + sizeof(_prefs->radio_fem_rxgain)); // 296 + // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. + if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) + && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) + && (_prefs->direct_retry_timing_magic[0] != DIRECT_RETRY_TIMING_MAGIC_0 + || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { + _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); + } + // next: 297 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -165,6 +181,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean + _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean file.close(); } @@ -234,7 +251,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 292 uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 }; file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 294 - // next: 296 + file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 + // next: 297 file.close(); } @@ -296,7 +314,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(command, "clock", 5) == 0) { uint32_t now = getRTCClock()->getCurrentTime(); DateTime dt = DateTime(now); - sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); + sprintf(reply, "%02d:%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), dt.year()); } else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds) uint32_t secs = _atoi(&command[5]); uint32_t curr = getRTCClock()->getCurrentTime(); @@ -935,19 +953,59 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } #endif } else if (memcmp(command, "powersaving on", 14) == 0) { +#if defined(NRF52_PLATFORM) _prefs->powersaving_enabled = 1; savePrefs(); - strcpy(reply, "ok"); // TODO: to return Not supported if required + strcpy(reply, "On - Immediate effect"); +#elif defined(ESP32) && !defined(WITH_BRIDGE) + _prefs->powersaving_enabled = 1; + savePrefs(); + strcpy(reply, "On - After 2 minutes"); +#elif defined(WITH_BRIDGE) + strcpy(reply, "Bridge not supported"); +#else + strcpy(reply, "Board not supported"); +#endif } else if (memcmp(command, "powersaving off", 15) == 0) { _prefs->powersaving_enabled = 0; savePrefs(); - strcpy(reply, "ok"); + strcpy(reply, "Off"); } else if (memcmp(command, "powersaving", 11) == 0) { if (_prefs->powersaving_enabled) { - strcpy(reply, "on"); + strcpy(reply, "On"); } else { - strcpy(reply, "off"); + strcpy(reply, "Off"); } + } else if (memcmp(command, "sensor", 6) == 0) { + // I2C +#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL) + sprintf(reply, "I2C Wire1: SDA=%s,SCL=%s\r\n", STR(ENV_PIN_SDA), STR(ENV_PIN_SCL)); +#elif defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_BOARD_SDA), STR(PIN_BOARD_SCL)); +#elif defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) + sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_WIRE_SDA), STR(PIN_WIRE_SCL)); +#else + sprintf(reply, "I2C GPIOs not defined\r\n"); +#endif + + // GPS +#if defined(PIN_GPS_RX) && defined(PIN_GPS_TX) + sprintf(reply + strlen(reply), "GPS Serial: RX=%s, TX=%s", STR(PIN_GPS_RX), STR(PIN_GPS_TX)); +#ifdef ENV_INCLUDE_GPS> 0 + sprintf(reply + strlen(reply), ". Configured"); +#else + sprintf(reply + strlen(reply), ". Not configured"); +#endif +#else + sprintf(reply + strlen(reply), "GPS Serial not defined"); +#endif + } else if (memcmp(command, "powerlog", 8) == 0) { + sprintf(reply, "Last reset reason: %s", _board->getResetReasonString(_board->getResetReason())); +#if defined(NRF52_PLATFORM) + sprintf(reply + strlen(reply), "\r\nLast shutdown reason: %s", + _board->getShutdownReasonString(_board->getShutdownReason())); + sprintf(reply + strlen(reply), "\r\nLast boot voltage: %u mV", _board->getBootVoltage()); +#endif } else if (memcmp(command, "log start", 9) == 0) { _callbacks->setLoggingOn(true); strcpy(reply, " logging on"); @@ -1061,6 +1119,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep savePrefs(); _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); #endif + } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported"); + } else if (memcmp(&config[17], "on", 2) == 0) { + if (_board->setLoRaFemLnaEnabled(true)) { + _prefs->radio_fem_rxgain = 1; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain on"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else if (memcmp(&config[17], "off", 3) == 0) { + if (_board->setLoRaFemLnaEnabled(false)) { + _prefs->radio_fem_rxgain = 0; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain off"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else { + strcpy(reply, "Error: state must be on or off"); + } } else if (memcmp(config, "radio ", 6) == 0) { strcpy(tmp, &config[6]); const char *parts[4]; @@ -1271,10 +1351,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } } else { _prefs->adc_multiplier = 0.0f; - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); }; } else { - sprintf(reply, "unknown config: %s", config); + strcpy(reply, "unknown config: "); } } @@ -1318,6 +1398,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "radio.rxgain", 12) == 0) { sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off"); #endif + } else if (memcmp(config, "radio.fem.rxgain", 16) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported"); + } else { + sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); + } } else if (memcmp(config, "radio", 5) == 0) { char freq[16], bw[16]; strcpy(freq, StrHelper::ftoa(_prefs->freq)); @@ -1406,12 +1492,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "> unknown"); } #else - strcpy(reply, "ERROR: unsupported"); + strcpy(reply, "Error: unsupported"); #endif } else if (memcmp(config, "adc.multiplier", 14) == 0) { float adc_mult = _board->getAdcMultiplier(); if (adc_mult == 0.0f) { - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); } else { sprintf(reply, "> %.3f", adc_mult); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ddc92ff1..3ca19357 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -61,6 +61,7 @@ struct NodePrefs { // persisted to file float adc_multiplier; char owner_info[120]; uint8_t rx_boosted_gain; // power settings + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; uint8_t direct_retry_attempts; diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index c2d78ae0..fe986593 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -12,12 +12,14 @@ #include #include #include -#include "driver/rtc_io.h" +#include "soc/rtc.h" +#include "esp_system.h" class ESP32Board : public mesh::MainBoard { protected: uint8_t startup_reason; bool inhibit_sleep = false; + static inline portMUX_TYPE sleepMux = portMUX_INITIALIZER_UNLOCKED; public: void begin() { @@ -60,25 +62,48 @@ public: return raw / 4; } - void enterLightSleep(uint32_t secs) { -#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // Supported ESP32 variants - if (rtc_gpio_is_valid_gpio((gpio_num_t)P_LORA_DIO_1)) { // Only enter sleep mode if P_LORA_DIO_1 is RTC pin - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // To wake up when receiving a LoRa packet - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs - } - - esp_light_sleep_start(); // CPU enters light sleep - } -#endif + uint32_t getIRQGpio() { + return P_LORA_DIO_1; // default for SX1262 } void sleep(uint32_t secs) override { - if (!inhibit_sleep) { - enterLightSleep(secs); // To wake up after "secs" seconds or when receiving a LoRa packet + // Skip if not allow to sleep + if (inhibit_sleep) { + delay(1); // Give MCU to OTA to run + return; } + + // Set GPIO wakeup + gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); + + // Configure timer wakeup + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000ULL); // Wake up periodically to do scheduled jobs + } + + // Disable CPU interrupt servicing + portENTER_CRITICAL(&sleepMux); + + // Skip sleep if there is a LoRa packet + if (gpio_get_level(wakeupPin) == HIGH) { + portEXIT_CRITICAL(&sleepMux); + delay(1); + return; + } + + // Configure GPIO wakeup + esp_sleep_enable_gpio_wakeup(); + gpio_wakeup_enable((gpio_num_t)wakeupPin, GPIO_INTR_HIGH_LEVEL); // Wake up when receiving a LoRa packet + + // MCU enters light sleep + esp_light_sleep_start(); + + // Avoid ISR flood during wakeup due to HIGH LEVEL interrupt + gpio_wakeup_disable(wakeupPin); + gpio_set_intr_type(wakeupPin, GPIO_INTR_POSEDGE); + + // Enable CPU interrupt servicing + portEXIT_CRITICAL(&sleepMux); } uint8_t getStartupReason() const override { return startup_reason; } @@ -102,7 +127,7 @@ public: #endif uint16_t getBattMilliVolts() override { - #ifdef PIN_VBAT_READ + #ifdef PIN_VBAT_READ analogReadResolution(12); uint32_t raw = 0; @@ -130,31 +155,88 @@ public: void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; } + + uint32_t getResetReason() const override { + return esp_reset_reason(); + } + + // https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/system/system.html + const char *getResetReasonString(uint32_t reason) { + switch (reason) { + case ESP_RST_UNKNOWN: + return "Unknown or first boot"; + case ESP_RST_POWERON: + return "Power-on reset"; + case ESP_RST_EXT: + return "External reset"; + case ESP_RST_SW: + return "Software reset"; + case ESP_RST_PANIC: + return "Panic / exception reset"; + case ESP_RST_INT_WDT: + return "Interrupt watchdog reset"; + case ESP_RST_TASK_WDT: + return "Task watchdog reset"; + case ESP_RST_WDT: + return "Other watchdog reset"; + case ESP_RST_DEEPSLEEP: + return "Wake from deep sleep"; + case ESP_RST_BROWNOUT: + return "Brownout (low voltage)"; + case ESP_RST_SDIO: + return "SDIO reset"; + default: + static char buf[40]; + snprintf(buf, sizeof(buf), "Unknown reset reason (%d)", reason); + return buf; + } + } }; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_time; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; +#define RTC_BACKUP_MAGIC 0xAA55CC33 +#define RTC_TIME_MIN 1772323200 // 1 Mar 2026 + class ESP32RTCClock : public mesh::RTCClock { public: ESP32RTCClock() { } void begin() { esp_reset_reason_t reason = esp_reset_reason(); - if (reason == ESP_RST_POWERON) { - // start with some date/time in the recent past - struct timeval tv; - tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm - tv.tv_usec = 0; - settimeofday(&tv, NULL); + if (reason == ESP_RST_DEEPSLEEP) { + return; // ESP-IDF preserves system time across deep sleep } + // All other resets (power-on, crash, WDT, brownout) lose system time. + // Restore from RTC backup if valid, otherwise use hardcoded seed. + struct timeval tv; + if (_rtc_backup_magic == RTC_BACKUP_MAGIC && _rtc_backup_time > RTC_TIME_MIN) { + tv.tv_sec = _rtc_backup_time; + } else { + tv.tv_sec = 1772323200; // 1 Mar 2026 + } + tv.tv_usec = 0; + settimeofday(&tv, NULL); } uint32_t getCurrentTime() override { time_t _now; time(&_now); return _now; } - void setCurrentTime(uint32_t time) override { + void setCurrentTime(uint32_t time) override { struct timeval tv; tv.tv_sec = time; tv.tv_usec = 0; settimeofday(&tv, NULL); + _rtc_backup_time = time; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + } + void tick() override { + time_t now; + time(&now); + if (now > RTC_TIME_MIN && (uint32_t)now != _rtc_backup_time) { + _rtc_backup_time = (uint32_t)now; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + } } }; diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c2..d4aa63b7 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -2,6 +2,7 @@ #include #include "sensors/LocationProvider.h" +#include #define TELEM_PERM_BASE 0x01 // 'base' permission includes battery #define TELEM_PERM_LOCATION 0x02 @@ -15,6 +16,7 @@ public: double node_altitude; // altitude in meters SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } + virtual bool i2c_probe(TwoWire& wire, uint8_t addr) { return false; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index dcfa0e1e..50e1501e 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -182,6 +182,10 @@ size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { #define BLE_WRITE_MIN_INTERVAL 60 +bool SerialBLEInterface::isReadBusy() const { + return (recv_queue_len > 0); +} + bool SerialBLEInterface::isWriteBusy() const { return millis() < _last_write + BLE_WRITE_MIN_INTERVAL; // still too soon to start another write? } diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index 965e90fd..19e024b0 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -76,6 +76,7 @@ public: bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/esp32/TBeamBoard.h b/src/helpers/esp32/TBeamBoard.h index 4ff95551..98bd16bf 100644 --- a/src/helpers/esp32/TBeamBoard.h +++ b/src/helpers/esp32/TBeamBoard.h @@ -59,13 +59,13 @@ // uint32_t P_LORA_BUSY = 0; //shared, so define at run // uint32_t P_LORA_DIO_2 = 0; //SX1276 only, so define at run - #define P_LORA_DIO_0 26 - #define P_LORA_DIO_1 33 - #define P_LORA_NSS 18 - #define P_LORA_RESET 23 - #define P_LORA_SCLK 5 - #define P_LORA_MISO 19 - #define P_LORA_MOSI 27 + // #define P_LORA_DIO_0 26 + // #define P_LORA_DIO_1 33 + // #define P_LORA_NSS 18 + // #define P_LORA_RESET 23 + // #define P_LORA_SCLK 5 + // #define P_LORA_MISO 19 + // #define P_LORA_MOSI 27 // #define PIN_GPS_RX 34 // #define PIN_GPS_TX 12 diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 75a4e3b0..a846e744 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -401,6 +401,10 @@ bool SerialBLEInterface::isConnected() const { return _isDeviceConnected && Bluefruit.connected() > 0; } +bool SerialBLEInterface::isReadBusy() const { + return (recv_queue_len > 0); +} + bool SerialBLEInterface::isWriteBusy() const { return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3); } diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h index de103054..d3cc5055 100644 --- a/src/helpers/nrf52/SerialBLEInterface.h +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -66,6 +66,7 @@ public: void disable() override; bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 19472406..749ea689 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -7,8 +7,10 @@ #endif #ifdef ENV_INCLUDE_BME680 -#ifndef TELEM_BME680_ADDRESS -#define TELEM_BME680_ADDRESS 0x76 +#if defined(TELEM_BME680_ADDRESS) +uint8_t TELEM_BME680_ADDRESSES[] = { TELEM_BME680_ADDRESS }; +#else +uint8_t TELEM_BME680_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BME680 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include @@ -28,8 +30,10 @@ static Adafruit_AHTX0 AHTX0; #endif #if ENV_INCLUDE_BME280 -#ifndef TELEM_BME280_ADDRESS -#define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address +#if defined(TELEM_BME280_ADDRESS) +uint8_t TELEM_BME280_ADDRESSES[] = { TELEM_BME280_ADDRESS }; +#else +uint8_t TELEM_BME280_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BME280 #endif #define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level #include @@ -37,8 +41,10 @@ static Adafruit_BME280 BME280; #endif #if ENV_INCLUDE_BMP280 -#ifndef TELEM_BMP280_ADDRESS -#define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address +#if defined(TELEM_BMP280_ADDRESS) +uint8_t TELEM_BMP280_ADDRESSES[] = { TELEM_BMP280_ADDRESS }; +#else +uint8_t TELEM_BMP280_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BMP280 #endif #define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level #include @@ -161,6 +167,12 @@ public: static RAK12500LocationProvider RAK12500_provider; #endif +bool EnvironmentSensorManager::i2c_probe(TwoWire &wire, uint8_t addr) { + wire.beginTransmission(addr); + uint8_t error = wire.endTransmission(); + return (error == 0); // If found i2c device, the error is 0 +} + bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS #ifdef RAK_WISBLOCK_GPS @@ -182,7 +194,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_AHTX0 - if (AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { + if (i2c_probe(*TELEM_WIRE, TELEM_AHTX_ADDRESS) && AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); AHTX0_initialized = true; } else { @@ -192,46 +204,55 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BME680 - if (BME680.begin(TELEM_BME680_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); - BME680_initialized = true; - } else { - BME680_initialized = false; - MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESS); + for (size_t i = 0; i < sizeof(TELEM_BME680_ADDRESSES) / sizeof(TELEM_BME680_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BME680_ADDRESSES[i]) && BME680.begin(TELEM_BME680_ADDRESSES[i], TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESSES[i]); + BME680_initialized = true; + break; + } else { + BME680_initialized = false; + MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESSES[i]); + } } #endif #if ENV_INCLUDE_BME280 - if (BME280.begin(TELEM_BME280_ADDRESS, TELEM_WIRE)) { - MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); - MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); - // Reduce self-heating: single-shot conversions, light oversampling, long standby. - BME280.setSampling(Adafruit_BME280::MODE_FORCED, + for (size_t i = 0; i < sizeof(TELEM_BME280_ADDRESSES) / sizeof(TELEM_BME280_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BME280_ADDRESSES[i]) && BME280.begin(TELEM_BME280_ADDRESSES[i], TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESSES[i]); + MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); + // Reduce self-heating: single-shot conversions, light oversampling, long standby. + BME280.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X1, // temperature Adafruit_BME280::SAMPLING_X1, // pressure Adafruit_BME280::SAMPLING_X1, // humidity Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000); - BME280_initialized = true; - } else { - BME280_initialized = false; - MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS); + BME280_initialized = true; + break; + } else { + BME280_initialized = false; + MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESSES[i]); + } } - #endif +#endif #if ENV_INCLUDE_BMP280 - if (BMP280.begin(TELEM_BMP280_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESS); - MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); - BMP280_initialized = true; - } else { - BMP280_initialized = false; - MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESS); - } - #endif + for (size_t i = 0; i < sizeof(TELEM_BMP280_ADDRESSES) / sizeof(TELEM_BMP280_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BMP280_ADDRESSES[i]) && BMP280.begin(TELEM_BMP280_ADDRESSES[i])) { + MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESSES[i]); + MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); + BMP280_initialized = true; + break; + } else { + BMP280_initialized = false; + MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESSES[i]); + } + } +#endif #if ENV_INCLUDE_SHTC3 - if (SHTC3.begin(TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, 0x70) && SHTC3.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor: SHTC3"); SHTC3_initialized = true; } else { @@ -242,21 +263,23 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_SHT4X - SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS); - uint32_t serialNumber = 0; - int16_t sht4x_error; - sht4x_error = SHT4X.serialNumber(serialNumber); - if (sht4x_error == 0) { - MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS); - SHT4X_initialized = true; - } else { - SHT4X_initialized = false; - MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS); + if (i2c_probe(*TELEM_WIRE, TELEM_SHT4X_ADDRESS)) { + SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS); + uint32_t serialNumber = 0; + int16_t sht4x_error; + sht4x_error = SHT4X.serialNumber(serialNumber); + if (sht4x_error == 0) { + MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS); + SHT4X_initialized = true; + } else { + SHT4X_initialized = false; + MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS); + } } #endif #if ENV_INCLUDE_LPS22HB - if (LPS22HB.begin()) { + if (i2c_probe(*TELEM_WIRE, 0x5C) && LPS22HB.begin()) { MESH_DEBUG_PRINTLN("Found sensor: LPS22HB"); LPS22HB_initialized = true; } else { @@ -266,7 +289,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA3221 - if (INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA3221_ADDRESS) && INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); @@ -281,7 +304,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA219 - if (INA219.begin(TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA219_ADDRESS) && INA219.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); INA219_initialized = true; } else { @@ -291,17 +314,17 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA260 - if (INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA260_ADDRESS) && INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA260 at address: %02X", TELEM_INA260_ADDRESS); INA260_initialized = true; } else { INA260_initialized = false; - MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA260_ADDRESS); + MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA219_ADDRESS); } #endif #if ENV_INCLUDE_INA226 - if (INA226.begin()) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA226_ADDRESS) && INA226.begin()) { MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS); INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE); INA226_initialized = true; @@ -312,7 +335,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_MLX90614 - if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_MLX90614_ADDRESS) && MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS); MLX90614_initialized = true; } else { @@ -322,7 +345,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_VL53L0X - if (VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_VL53L0X_ADDRESS) && VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found VL53L0X at address: %02X", TELEM_VL53L0X_ADDRESS); VL53L0X_initialized = true; } else { @@ -334,7 +357,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_BMP085 // First argument is MODE (aka oversampling) // choose ULTRALOWPOWER - if (BMP085.begin(0, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, 0x77) && BMP085.begin(0, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor BMP085"); BMP085_initialized = true; } else { @@ -345,7 +368,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_RAK12035 RAK12035.setup(*TELEM_WIRE); - if (RAK12035.begin(TELEM_RAK12035_ADDRESS)) { + if (i2c_probe(*TELEM_WIRE, TELEM_RAK12035_ADDRESS) && RAK12035.begin(TELEM_RAK12035_ADDRESS)) { MESH_DEBUG_PRINTLN("Found sensor RAK12035 at address: %02X", TELEM_RAK12035_ADDRESS); RAK12035_initialized = true; } else { @@ -716,7 +739,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ gps_detected = true; return true; } - + pinMode(ioPin, INPUT); MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); return false; @@ -759,7 +782,7 @@ void EnvironmentSensorManager::loop() { #if ENV_INCLUDE_GPS if (gps_active) { - _location->loop(); + _location->loop(); } if (millis() > next_gps_update) { diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 32413ebc..38de5560 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -47,6 +47,7 @@ public: #else EnvironmentSensorManager(){}; #endif + bool i2c_probe(TwoWire& wire, uint8_t addr) override; bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; #if ENV_INCLUDE_GPS diff --git a/variants/gat562_30s_mesh_kit/platformio.ini b/variants/gat562_30s_mesh_kit/platformio.ini index 1467f0fa..4266d134 100644 --- a/variants/gat562_30s_mesh_kit/platformio.ini +++ b/variants/gat562_30s_mesh_kit/platformio.ini @@ -7,7 +7,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/gat562_30s_mesh_kit -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_evb_pro/platformio.ini b/variants/gat562_mesh_evb_pro/platformio.ini index cede9c97..f098237f 100644 --- a/variants/gat562_mesh_evb_pro/platformio.ini +++ b/variants/gat562_mesh_evb_pro/platformio.ini @@ -5,7 +5,7 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_evb_pro - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/gat562_mesh_tracker_pro/platformio.ini b/variants/gat562_mesh_tracker_pro/platformio.ini index 8a947bce..cf25424b 100644 --- a/variants/gat562_mesh_tracker_pro/platformio.ini +++ b/variants/gat562_mesh_tracker_pro/platformio.ini @@ -5,7 +5,7 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_tracker_pro - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_watch13/platformio.ini b/variants/gat562_mesh_watch13/platformio.ini index ef30829d..f34f0167 100644 --- a/variants/gat562_mesh_watch13/platformio.ini +++ b/variants/gat562_mesh_watch13/platformio.ini @@ -8,7 +8,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/gat562_mesh_watch13 -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index 2c50b742..a3b5c4ed 100644 --- a/variants/heltec_t096/LoRaFEMControl.h +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -12,10 +12,11 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index 55013157..54425145 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -123,4 +123,22 @@ void T096Board::powerOff() { const char* T096Board::getManufacturerName() const { return "Heltec T096"; -} \ No newline at end of file +} + +bool T096Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; +} + +bool T096Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); +} + +bool T096Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); +} diff --git a/variants/heltec_t096/T096Board.h b/variants/heltec_t096/T096Board.h index d1e3bdfd..15c7e68b 100644 --- a/variants/heltec_t096/T096Board.h +++ b/variants/heltec_t096/T096Board.h @@ -25,4 +25,7 @@ public: uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; void powerOff() override; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index 19b05f3c..00cc5d10 100644 --- a/variants/heltec_t096/platformio.ini +++ b/variants/heltec_t096/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t096 -I src/helpers/ui -D HELTEC_T096 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=21 -D P_LORA_NSS=5 -D P_LORA_RESET=16 @@ -126,6 +126,30 @@ lib_deps = ${Heltec_t096.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_t096_companion_radio_ble_femoff] +extends = Heltec_t096 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${Heltec_t096.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D ENV_INCLUDE_GPS=1 ; enable the GPS page in UI +; -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off +build_src_filter = ${Heltec_t096.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_t096.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_t096_companion_radio_usb] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index b985030f..8011641d 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -12,7 +12,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t114 -I src/helpers/ui -D HELTEC_T114 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=20 -D P_LORA_NSS=24 -D P_LORA_RESET=25 diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index e0a8f5fa..2c915526 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -99,6 +99,33 @@ lib_deps = ${Heltec_tracker_base.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_Wireless_Tracker_companion_radio_ble_ps] +extends = Heltec_tracker_base +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_tracker_base.build_flags} + -I src/helpers/ui + -I examples/companion_radio/ui-new + -D DISPLAY_ROTATION=1 + -D DISPLAY_CLASS=ST7735Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 ; HWT will use display for pin + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_base.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + +lib_deps = + ${Heltec_tracker_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_Wireless_Tracker_repeater] extends = Heltec_tracker_base build_flags = diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index aabfed79..f182c905 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -82,3 +82,21 @@ void HeltecTrackerV2Board::begin() { const char* HeltecTrackerV2Board::getManufacturerName() const { return "Heltec Tracker V2"; } + + bool HeltecTrackerV2Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecTrackerV2Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index 33c897bc..ccbecc7a 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -21,5 +21,8 @@ public: void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index 2c50b742..a3b5c4ed 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -12,10 +12,11 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index 956b1ec7..f7b87133 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -176,6 +176,33 @@ lib_deps = ${Heltec_tracker_v2.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_tracker_v2_companion_radio_ble_ps] +extends = Heltec_tracker_v2 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_tracker_v2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=ST7735Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + ; -D BLE_DEBUG_LOGGING=1 + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:heltec_tracker_v2_companion_radio_wifi] extends = Heltec_tracker_v2 build_flags = diff --git a/variants/heltec_v2/HeltecV2Board.h b/variants/heltec_v2/HeltecV2Board.h index a6221036..fe800890 100644 --- a/variants/heltec_v2/HeltecV2Board.h +++ b/variants/heltec_v2/HeltecV2Board.h @@ -17,12 +17,12 @@ public: 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) + if (wakeup_source & (1 << P_LORA_DIO_0)) { // received a LoRa packet (while in deep sleep) startup_reason = BD_STARTUP_RX_PACKET; } rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); - rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); + rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_0); } } @@ -30,15 +30,15 @@ public: esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0); rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); 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 + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn } if (secs > 0) { @@ -64,4 +64,8 @@ public: const char* getManufacturerName() const override { return "Heltec V2"; } + + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } }; diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 99f6f7e1..2ff6e9ff 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -7,10 +7,10 @@ build_flags = -D HELTEC_LORA_V2 -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper - -D P_LORA_DIO_1=26 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=35 -D P_LORA_NSS=18 - -D P_LORA_RESET=RADIOLIB_NC - -D P_LORA_BUSY=RADIOLIB_NC + -D P_LORA_RESET=14 -D P_LORA_SCLK=5 -D P_LORA_MISO=19 -D P_LORA_MOSI=27 @@ -172,6 +172,31 @@ lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v2_companion_radio_ble_ps] +extends = Heltec_lora32_v2 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_lora32_v2.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=160 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_lora32_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_v2_companion_radio_wifi] extends = Heltec_lora32_v2 build_flags = diff --git a/variants/heltec_v2/target.cpp b/variants/heltec_v2/target.cpp index 2dfb4c6e..54a2d7b2 100644 --- a/variants/heltec_v2/target.cpp +++ b/variants/heltec_v2/target.cpp @@ -5,9 +5,9 @@ HeltecV2Board board; #if defined(P_LORA_SCLK) static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); #else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1); #endif WRAPPER_CLASS radio_driver(radio, board); diff --git a/variants/heltec_v3/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h index ba22a7f2..5361d029 100644 --- a/variants/heltec_v3/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -53,6 +53,10 @@ public: } void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 803ee683..fe9cd926 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -179,6 +179,32 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v3_companion_radio_ble_ps] +extends = Heltec_lora32_v3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_lora32_v3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = @@ -320,6 +346,26 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_WSL3_companion_radio_ble_ps] +extends = Heltec_lora32_v3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_WSL3_companion_radio_usb] extends = Heltec_lora32_v3 build_flags = diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 49580d2e..87791e6b 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -33,6 +33,10 @@ void HeltecV4Board::begin() { } void HeltecV4Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep @@ -83,3 +87,25 @@ void HeltecV4Board::begin() { return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED"; #endif } + + bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) { +#if defined(RADIO_FEM_RXGAIN) && (RADIO_FEM_RXGAIN == 0) + enable = false; +#endif + + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecV4Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecV4Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 4d5ee461..f96e161c 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -17,6 +17,9 @@ public: void onAfterTransmit(void) override; void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_v4/LoRaFEMControl.h b/variants/heltec_v4/LoRaFEMControl.h index 75452965..d84ebe9c 100644 --- a/variants/heltec_v4/LoRaFEMControl.h +++ b/variants/heltec_v4/LoRaFEMControl.h @@ -18,8 +18,9 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } LoRaFEMType getFEMType(void) const { return fem_type; } private: LoRaFEMType fem_type=OTHER_FEM_TYPES; diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 6f6bf2b5..bc038c8e 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -102,6 +102,28 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 +[env:heltec_v4_expansionkit_repeater] +extends = heltec_v4_oled +build_flags = + ${heltec_v4_oled.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Heltec Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D ENV_PIN_SDA=4 + -D ENV_PIN_SCL=3 +build_src_filter = ${heltec_v4_oled.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${heltec_v4_oled.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + [env:heltec_v4_repeater_bridge_espnow] extends = heltec_v4_oled build_flags = @@ -200,6 +222,59 @@ lib_deps = ${heltec_v4_oled.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_v4_companion_radio_ble_ps] +extends = heltec_v4_oled +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${heltec_v4_oled.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${heltec_v4_oled.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${heltec_v4_oled.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_v4_3_companion_radio_ble_ps_femoff] +extends = heltec_v4_oled +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${heltec_v4_oled.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off +build_src_filter = ${heltec_v4_oled.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${heltec_v4_oled.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:heltec_v4_companion_radio_wifi] extends = heltec_v4_oled build_flags = @@ -207,7 +282,6 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 - -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SSD1306Display -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index c6fe657d..2e4c4c9d 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -64,6 +64,29 @@ lib_deps = densaugeo/base64 @ ~1.4.0 bakercp/CRC32 @ ^2.0.0 +[env:Heltec_Wireless_Paper_companion_radio_ble_ps] +extends = Heltec_Wireless_Paper_base +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_Wireless_Paper_base.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=E213Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_Wireless_Paper_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 + [env:Heltec_Wireless_Paper_companion_radio_usb] extends = Heltec_Wireless_Paper_base build_flags = diff --git a/variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h b/variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h new file mode 100644 index 00000000..7da620fd --- /dev/null +++ b/variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class LilygoT3S3SX1276Board : public ESP32Board { +public: + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } +}; \ No newline at end of file diff --git a/variants/lilygo_t3s3_sx1276/target.cpp b/variants/lilygo_t3s3_sx1276/target.cpp index e7fe07a0..8236f459 100644 --- a/variants/lilygo_t3s3_sx1276/target.cpp +++ b/variants/lilygo_t3s3_sx1276/target.cpp @@ -1,7 +1,7 @@ #include #include "target.h" -ESP32Board board; +LilygoT3S3SX1276Board board; #if defined(P_LORA_SCLK) static SPIClass spi; diff --git a/variants/lilygo_t3s3_sx1276/target.h b/variants/lilygo_t3s3_sx1276/target.h index 2df4b3ed..079d2a7e 100644 --- a/variants/lilygo_t3s3_sx1276/target.h +++ b/variants/lilygo_t3s3_sx1276/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include @@ -12,7 +12,7 @@ #include #endif -extern ESP32Board board; +extern LilygoT3S3SX1276Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 7c845307..60b3291b 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -146,6 +146,31 @@ lib_deps = ${LilyGo_TBeam_1W.lib_deps} densaugeo/base64 @ ~1.4.0 +; === LILYGO T-Beam 1W Companion Radio PS (BLE PS) === +[env:LilyGo_TBeam_1W_companion_radio_ble_ps] +extends = LilyGo_TBeam_1W +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + densaugeo/base64 @ ~1.4.0 + ; === LILYGO T-Beam 1W Companion Radio (WiFi) === [env:LilyGo_TBeam_1W_companion_radio_wifi] extends = LilyGo_TBeam_1W diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index d3bc7c99..1585dd74 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -5,6 +5,13 @@ build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_SX1262 -D TBEAM_SX1262 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h b/variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h new file mode 100644 index 00000000..afe106d0 --- /dev/null +++ b/variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class LilygoTBeamSX1276Board : public TBeamBoard { +public: + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } +}; \ No newline at end of file diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 3562c40e..7482ef7b 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -5,6 +5,13 @@ build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_SX1276 -D TBEAM_SX1276 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 -D SX127X_CURRENT_LIMIT=120 -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper diff --git a/variants/lilygo_tbeam_SX1276/target.cpp b/variants/lilygo_tbeam_SX1276/target.cpp index 495337b8..5481d672 100644 --- a/variants/lilygo_tbeam_SX1276/target.cpp +++ b/variants/lilygo_tbeam_SX1276/target.cpp @@ -1,7 +1,7 @@ #include #include "target.h" -TBeamBoard board; +LilygoTBeamSX1276Board board; #if defined(P_LORA_SCLK) static SPIClass spi; diff --git a/variants/lilygo_tbeam_SX1276/target.h b/variants/lilygo_tbeam_SX1276/target.h index ad385645..abeaef46 100644 --- a/variants/lilygo_tbeam_SX1276/target.h +++ b/variants/lilygo_tbeam_SX1276/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 //#include #include -#include +#include #include #include #include @@ -12,7 +12,7 @@ #include #endif -extern TBeamBoard board; +extern LilygoTBeamSX1276Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index ffee37a9..249e6871 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -5,6 +5,13 @@ build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_supreme_SX1262 -D TBEAM_SUPREME_SX1262 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 -D USE_SX1262 diff --git a/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h b/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h index 545219b2..f126f006 100644 --- a/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h +++ b/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h @@ -21,4 +21,8 @@ public: return (2 * raw); } + + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } }; \ No newline at end of file diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index 3d2d4a3e..e1207114 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -6,7 +6,7 @@ build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak3401 -D RAK_3401 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index ea7e49c3..a96374a8 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -7,7 +7,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/rak4631 -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_GPS_TX=PIN_SERIAL1_RX diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index aabbcf00..6aeb509b 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -10,7 +10,7 @@ build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 -D NRF52_PLATFORM=1 -D USE_SX1262 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D P_LORA_TX_LED=11 diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index c5254b46..244a39f1 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -90,6 +90,28 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Xiao_C3_companion_radio_ble_ps] +extends = Xiao_esp32_C3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_src_filter = ${Xiao_esp32_C3.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${Xiao_esp32_C3.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + ; -D BLE_DEBUG_LOGGING=1 + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_esp32_C3.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 +board_build.partitions = min_spiffs.csv ; get around 4mb flash limit + [env:Xiao_C3_companion_radio_usb] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index a0854336..f4d1b93e 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/xiao_nrf52 -UENV_INCLUDE_GPS -D NRF52_PLATFORM - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D XIAO_NRF52 -D USE_SX1262 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/xiao_s3/XiaoS3Board.h b/variants/xiao_s3/XiaoS3Board.h new file mode 100644 index 00000000..288fcf62 --- /dev/null +++ b/variants/xiao_s3/XiaoS3Board.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class XiaoS3Board : public ESP32Board { +public: + XiaoS3Board() { } + + const char* getManufacturerName() const override { + return "Xiao S3"; + } +}; diff --git a/variants/xiao_s3/platformio.ini b/variants/xiao_s3/platformio.ini new file mode 100644 index 00000000..58bcc60b --- /dev/null +++ b/variants/xiao_s3/platformio.ini @@ -0,0 +1,180 @@ +[Xiao_S3] +extends = esp32_base +board = seeed_xiao_esp32s3 +board_check = true +board_build.mcu = esp32s3 +build_flags = ${esp32_base.build_flags} + ${sensor_base.build_flags} + -I variants/xiao_s3 + -UENV_INCLUDE_GPS + -D SEEED_XIAO_S3 + -D PIN_VBAT_READ=1 ; D0 + -D P_LORA_DIO_1=2 ; D1 + -D P_LORA_NSS=5 ; D4 + -D P_LORA_RESET=3 ; D2 + -D P_LORA_BUSY=4 ; D3 + -D P_LORA_SCLK=7 ; D8 + -D P_LORA_MISO=8 ; D0 + -D P_LORA_MOSI=9 ; D10 + -D PIN_USER_BTN=-1 ; NC + -D PIN_STATUS_LED=21 ; Orange user led, LOW=On + -D PIN_BOARD_SDA=D6 ; D6=43 + -D PIN_BOARD_SCL=D7 ; D7=44 + -D SX126X_RXEN=6 ; D5 + -D SX126X_TXEN=RADIOLIB_NC + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/xiao_s3> + + +lib_deps = + ${esp32_base.lib_deps} + ${sensor_base.lib_deps} + +[env:Xiao_S3_repeater] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + +<../examples/simple_repeater/*.cpp> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"Xiao S3 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Xiao_S3_repeater_bridge_espnow] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + + + +<../examples/simple_repeater/*.cpp> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Xiao_S3_room_server] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"Xiao S3 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Xiao_S3_companion_radio_ble] +extends = Xiao_S3 +build_flags = + ${Xiao_S3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Xiao_S3_companion_radio_ble_ps] +extends = Xiao_S3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Xiao_S3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Xiao_S3_companion_radio_usb] +extends = Xiao_S3 +build_flags = + ${Xiao_S3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Xiao_S3_sensor] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + +<../examples/simple_sensor> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"Xiao S3 Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} \ No newline at end of file diff --git a/variants/xiao_s3/target.cpp b/variants/xiao_s3/target.cpp new file mode 100644 index 00000000..014b2552 --- /dev/null +++ b/variants/xiao_s3/target.cpp @@ -0,0 +1,56 @@ +#include +#include "target.h" + +XiaoS3Board board; + +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + pinMode(21, INPUT); + pinMode(48, OUTPUT); + + #if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/xiao_s3/target.h b/variants/xiao_s3/target.h new file mode 100644 index 00000000..93b27862 --- /dev/null +++ b/variants/xiao_s3/target.h @@ -0,0 +1,30 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include + #include +#endif +#include "XiaoS3Board.h" + +extern XiaoS3Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 13d40679..7fcebce2 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -173,6 +173,32 @@ lib_deps = densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SSD1306 @ ^2.5.13 +[env:Xiao_S3_WIO_companion_radio_ble_ps] +extends = Xiao_S3_WIO +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Xiao_S3_WIO.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3_WIO.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO build_flags =