mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-09 16:55:35 +00:00
Merge branch 'PowerSaving-v17' of https://github.com/IoTThinks/MeshCore into keymindCascade
# Conflicts: # examples/companion_radio/MyMesh.cpp # examples/companion_radio/NodePrefs.h # examples/simple_repeater/MyMesh.cpp # examples/simple_room_server/MyMesh.cpp # examples/simple_sensor/SensorMesh.cpp # platformio.ini # src/MeshCore.h # src/helpers/CommonCLI.cpp # src/helpers/CommonCLI.h # src/helpers/radiolib/CustomSX1262Wrapper.h # src/helpers/radiolib/CustomSX1268Wrapper.h # src/helpers/radiolib/LR11x0Reset.h # src/helpers/radiolib/SX126xReset.h
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
github: meshcore-dev
|
||||
github: IoTThinks
|
||||
|
||||
@@ -79,6 +79,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| Radio | [`get freq`; `set freq <mhz>`](cli_commands.md#view-or-change-this-nodes-frequency) | `set` is local serial only | Yes | Yes | Yes |
|
||||
| Radio | [`get/set radio.rxgain`](cli_commands.md#view-or-change-this-nodes-rx-boosted-gain-mode-sx12xx-and-lr1110-v1141) | Supported radio | Feature | Feature | Feature |
|
||||
| Radio | [`get/set radio.fem.rxgain`](cli_commands.md#view-or-change-the-lora-fem-receive-path-gain-state-on-supported-boards) | Controllable LoRa FEM | Feature | Feature | Feature |
|
||||
| Radio | [`get/set radio.fem.txgain`](cli_commands.md#view-or-change-the-lora-fem-transmit-path-gain-state-on-supported-boards) | Controllable LoRa FEM | Feature | Feature | Feature |
|
||||
| Radio | [`get/set radio.rxps`; `get rxps.wd`](#radio-rxps) | RX power-saving support | Feature | Feature | Feature |
|
||||
| System | [`get/set name`](cli_commands.md#view-or-change-this-nodes-name) | All text CLI roles | Yes | Yes | Yes |
|
||||
| System | [`get/set lat`](cli_commands.md#view-or-change-this-nodes-latitude) | All text CLI roles | Yes | Yes | Yes |
|
||||
@@ -234,6 +235,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| Radio | [`get freq`; `set freq <mhz>`](cli_commands.md#view-or-change-this-nodes-frequency) | `set` is local serial only | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
||||
| Radio | [`get/set radio.rxgain`](cli_commands.md#view-or-change-this-nodes-rx-boosted-gain-mode-sx12xx-and-lr1110-v1141) | Supported radio | Feature | Feature | Feature | Feature | Feature | Feature | Feature |
|
||||
| Radio | [`get/set radio.fem.rxgain`](cli_commands.md#view-or-change-the-lora-fem-receive-path-gain-state-on-supported-boards) | Controllable LoRa FEM | Feature | Feature | Feature | Feature | Feature | Feature | Feature |
|
||||
| Radio | [`get/set radio.fem.txgain`](cli_commands.md#view-or-change-the-lora-fem-transmit-path-gain-state-on-supported-boards) | Controllable LoRa FEM | Feature | Feature | Feature | Feature | Feature | Feature | Feature |
|
||||
| Radio | [`get/set radio.rxps`; `get rxps.wd`](#radio-rxps) | Full parser and RX power-saving support | Feature | Feature | Feature | No | No | Feature | Feature |
|
||||
| System | [`get/set name`](cli_commands.md#view-or-change-this-nodes-name) | All full-parser text CLI roles | Yes | Yes | Yes | No | No | Yes | Yes |
|
||||
| System | [`get/set lat`](cli_commands.md#view-or-change-this-nodes-latitude) | All full-parser text CLI roles | Yes | Yes | Yes | No | No | Yes | Yes |
|
||||
|
||||
@@ -605,6 +605,22 @@ Station G2/G3 targets default to `off`.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the LoRa FEM transmit-path gain state on supported boards
|
||||
**Usage:**
|
||||
- `get radio.fem.txgain`
|
||||
- `set radio.fem.txgain <state>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on`|`off`
|
||||
|
||||
**Notes:**
|
||||
- This controls a software-selectable external LoRa FEM transmit gain where the board supports it.
|
||||
- On Station G3, remove the PA PL1 jumper to allow software control. `on` selects PA PL1 high/short and `off` selects PA PL1 low/open. The PA PL2 hardware jumper determines whether this switches between power levels 1/3 or 2/4.
|
||||
- Select an operating level and SX1262 transmit power that comply with local RF limits and the Station G3 power-supply requirements.
|
||||
- The setting is saved immediately, but on Station G3 the level is applied to the hardware at the start of the next transmit, so that the PA supply rail is never re-targeted while the PA is being driven. `get` reports the configured state, which may lead the hardware until the node next transmits.
|
||||
|
||||
---
|
||||
|
||||
### System
|
||||
|
||||
#### View or change this node's name
|
||||
|
||||
@@ -57,9 +57,13 @@ boost** switch. From the USB terminal, use:
|
||||
get radio.fem.rxgain
|
||||
set radio.fem.rxgain off
|
||||
set radio.fem.rxgain on
|
||||
get radio.fem.txgain
|
||||
set radio.fem.txgain off
|
||||
set radio.fem.txgain on
|
||||
```
|
||||
|
||||
The selected state is applied immediately and retained after reboot.
|
||||
The selected states are applied immediately and retained after reboot. FEM TX
|
||||
gain is reported as unsupported on boards without software-selectable PA gain.
|
||||
|
||||
Artifacts are written to `out/` by default.
|
||||
|
||||
|
||||
@@ -87,12 +87,14 @@ Sets the transmit air-time-factor. Deprecated - use `set dutycycle` instead.
|
||||
```
|
||||
get radio.fem.rxgain
|
||||
set radio.fem.rxgain {on|off}
|
||||
get radio.fem.txgain
|
||||
set radio.fem.txgain {on|off}
|
||||
```
|
||||
Shows or changes the external FEM receive-path LNA on supported Heltec boards.
|
||||
The change is applied immediately, saved across reboots, and recalibrates the
|
||||
radio noise floor. This is separate from the SX126x `radio.rxgain` setting.
|
||||
Boards whose FEM receive path cannot be controlled report the setting as
|
||||
unsupported.
|
||||
Shows or changes the external FEM receive-path LNA or transmit-path gain on
|
||||
supported boards. Changes are applied immediately and saved across reboots;
|
||||
changing RX gain also recalibrates the radio noise floor. These settings are
|
||||
separate from the SX126x `radio.rxgain` setting. Boards without the respective
|
||||
control report it as unsupported.
|
||||
|
||||
|
||||
```
|
||||
|
||||
@@ -320,6 +320,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
|
||||
if (file.available() >= (int)sizeof(_prefs.vibe_quiet)) {
|
||||
file.read((uint8_t *)&_prefs.vibe_quiet, sizeof(_prefs.vibe_quiet)); // 124
|
||||
}
|
||||
if (file.available() >= (int)sizeof(_prefs.radio_fem_txgain)) {
|
||||
file.read((uint8_t *)&_prefs.radio_fem_txgain, sizeof(_prefs.radio_fem_txgain)); // 125
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
@@ -369,6 +372,8 @@ bool DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
|
||||
sizeof(_prefs.radio_fem_rxgain_override)) == sizeof(_prefs.radio_fem_rxgain_override); // 123
|
||||
success = success && file.write((uint8_t *)&_prefs.vibe_quiet,
|
||||
sizeof(_prefs.vibe_quiet)) == sizeof(_prefs.vibe_quiet); // 124
|
||||
success = success && file.write((uint8_t *)&_prefs.radio_fem_txgain,
|
||||
sizeof(_prefs.radio_fem_txgain)) == sizeof(_prefs.radio_fem_txgain); // 125
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
success = file.commit(success);
|
||||
|
||||
@@ -1357,6 +1357,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
_prefs.gps_interval = 0; // No automatic GPS updates by default
|
||||
_prefs.autoadd_config = DEFAULT_AUTOADD_CONFIG;
|
||||
_prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE;
|
||||
_prefs.radio_fem_txgain = 0;
|
||||
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
|
||||
_prefs.setRepeatEn(false);
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110) \
|
||||
@@ -1436,6 +1437,7 @@ void MyMesh::begin(bool has_display) {
|
||||
_prefs.radio_fem_rxgain = DEFAULT_FEM_RX_GAIN;
|
||||
}
|
||||
_prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1);
|
||||
_prefs.radio_fem_txgain = constrain(_prefs.radio_fem_txgain, 0, 1);
|
||||
|
||||
#ifdef BLE_PIN_CODE // 123456 by default
|
||||
if (_prefs.ble_pin == 0) {
|
||||
@@ -1484,6 +1486,7 @@ void MyMesh::begin(bool has_display) {
|
||||
if (board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain) && fem_gain_changed) {
|
||||
_radio->recalibrateNoiseFloor();
|
||||
}
|
||||
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
|
||||
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
|
||||
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
|
||||
|
||||
@@ -2047,6 +2050,19 @@ void MyMesh::execCommand(char* cmd, char* reply) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (strcmp(key, "radio.fem.txgain") == 0) {
|
||||
bool enabled;
|
||||
if (!wcParseBool(value, enabled)) {
|
||||
strcpy(reply, "Error: must be on or off");
|
||||
} else if (!board.canControlLoRaFemPaGain()) {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
} else if (!applyAndSaveFemTxGain(enabled)) {
|
||||
strcpy(reply, "Error: failed to apply FEM TX gain");
|
||||
} else {
|
||||
strcpy(reply, "OK");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WITH_MQTT_BRIDGE
|
||||
bool changed = false;
|
||||
@@ -3450,6 +3466,15 @@ bool MyMesh::applyAndSaveFemRxGain(bool enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MyMesh::applyAndSaveFemTxGain(bool enabled) {
|
||||
if (!board.canControlLoRaFemPaGain()) return false;
|
||||
if (!board.setLoRaFemPaGainEnabled(enabled)) return false;
|
||||
|
||||
_prefs.radio_fem_txgain = enabled ? 1 : 0;
|
||||
savePrefs();
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_USB_INTERFACE
|
||||
void MyMesh::enterTerminalMode() {
|
||||
_terminal_mode = true;
|
||||
@@ -4236,6 +4261,13 @@ void MyMesh::handleTerminalCommand(char* command) {
|
||||
Serial.printf(" FEM RX gain: %s\r\n",
|
||||
board.isLoRaFemLnaEnabled() ? "on" : "off");
|
||||
}
|
||||
} else if (strcmp(command, "get radio.fem.txgain") == 0) {
|
||||
if (!board.canControlLoRaFemPaGain()) {
|
||||
Serial.print(" ERROR: FEM TX gain control is unsupported on this board\r\n");
|
||||
} else {
|
||||
Serial.printf(" FEM TX gain: %s\r\n",
|
||||
board.isLoRaFemPaGainEnabled() ? "on" : "off");
|
||||
}
|
||||
} else if (strncmp(command, "set ", 4) == 0) {
|
||||
const char* config = command + 4;
|
||||
if (strncmp(config, "af ", 3) == 0) {
|
||||
@@ -4276,6 +4308,20 @@ void MyMesh::handleTerminalCommand(char* command) {
|
||||
} else {
|
||||
Serial.printf(" OK - FEM RX gain %s\r\n", value);
|
||||
}
|
||||
} else if (strncmp(config, "radio.fem.txgain", 16) == 0
|
||||
&& (config[16] == 0 || config[16] == ' '
|
||||
|| config[16] == '\t')) {
|
||||
const char* value = config + 16;
|
||||
while (*value == ' ' || *value == '\t') value++;
|
||||
if (strcmp(value, "on") != 0 && strcmp(value, "off") != 0) {
|
||||
Serial.print(" ERROR: use set radio.fem.txgain <on|off>\r\n");
|
||||
} else if (!board.canControlLoRaFemPaGain()) {
|
||||
Serial.print(" ERROR: FEM TX gain control is unsupported on this board\r\n");
|
||||
} else if (!applyAndSaveFemTxGain(strcmp(value, "on") == 0)) {
|
||||
Serial.print(" ERROR: failed to apply FEM TX gain\r\n");
|
||||
} else {
|
||||
Serial.printf(" OK - FEM TX gain %s\r\n", value);
|
||||
}
|
||||
} else {
|
||||
Serial.printf(" ERROR: unknown setting: %s\r\n", config);
|
||||
}
|
||||
@@ -4287,6 +4333,8 @@ void MyMesh::handleTerminalCommand(char* command) {
|
||||
Serial.print(" set {name|lat|lon|freq|tx|af} {value}\r\n");
|
||||
Serial.print(" get radio.fem.rxgain\r\n");
|
||||
Serial.print(" set radio.fem.rxgain <on|off>\r\n");
|
||||
Serial.print(" get radio.fem.txgain\r\n");
|
||||
Serial.print(" set radio.fem.txgain <on|off>\r\n");
|
||||
Serial.print(" card\r\n");
|
||||
Serial.print(" import <meshcore://card>\r\n");
|
||||
Serial.print(" clock\r\n");
|
||||
|
||||
@@ -277,6 +277,7 @@ private:
|
||||
void checkCLIRescueCmd();
|
||||
void checkSerialInterface();
|
||||
bool applyAndSaveFemRxGain(bool enabled);
|
||||
bool applyAndSaveFemTxGain(bool enabled);
|
||||
#ifdef ENABLE_USB_INTERFACE
|
||||
ContactInfo* getTerminalRecipient();
|
||||
void printTerminalPath(const ContactInfo& recipient);
|
||||
|
||||
@@ -37,6 +37,7 @@ struct NodePrefs { // persisted to file
|
||||
uint8_t default_scope_key[16];
|
||||
uint8_t radio_fem_rxgain_override; // 1 once the user overrides the build default
|
||||
uint8_t vibe_quiet; // haptic quiet mode; appended for prefs compatibility
|
||||
uint8_t radio_fem_txgain; // LoRa FEM TX gain; appended for prefs compatibility
|
||||
|
||||
// Keep the upstream repeat API while retaining the existing binary prefs
|
||||
// layout used by this branch.
|
||||
|
||||
@@ -3030,6 +3030,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.rx_boosted_gain = mesh::radio::configuredRxBoostedGainDefault();
|
||||
#endif
|
||||
_prefs.radio_fem_rxgain = 1; // LoRa FEM RX gain on by default (FEM boards)
|
||||
_prefs.radio_fem_txgain = 0;
|
||||
|
||||
pending_discover_tag = 0;
|
||||
pending_discover_until = 0;
|
||||
@@ -3200,6 +3201,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
||||
if (board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain) && fem_gain_changed) {
|
||||
_radio->recalibrateNoiseFloor();
|
||||
}
|
||||
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
|
||||
setRxPowerSaving(_prefs.rx_powersaving_enabled, _prefs.rx_ps_rx_us, _prefs.rx_ps_sleep_us);
|
||||
|
||||
updateAdvertTimer();
|
||||
|
||||
@@ -1164,6 +1164,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.rx_boosted_gain = mesh::radio::configuredRxBoostedGainDefault();
|
||||
#endif
|
||||
_prefs.radio_fem_rxgain = 1;
|
||||
_prefs.radio_fem_txgain = 0;
|
||||
|
||||
// Observer defaults (alert.*, etc.) moved to applyMQTTDefaults() - they live
|
||||
// in /mqtt_prefs now, not NodePrefs.
|
||||
@@ -1241,6 +1242,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
||||
if (board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain) && fem_gain_changed) {
|
||||
_radio->recalibrateNoiseFloor();
|
||||
}
|
||||
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
|
||||
setRxPowerSaving(_prefs.rx_powersaving_enabled, _prefs.rx_ps_rx_us, _prefs.rx_ps_sleep_us);
|
||||
|
||||
updateAdvertTimer();
|
||||
|
||||
@@ -931,6 +931,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
||||
_prefs.gps_interval = 0;
|
||||
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
||||
_prefs.radio_fem_rxgain = 1;
|
||||
_prefs.radio_fem_txgain = 0;
|
||||
|
||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||
}
|
||||
@@ -974,6 +975,7 @@ void SensorMesh::begin(FILESYSTEM* fs) {
|
||||
if (board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain) && fem_gain_changed) {
|
||||
_radio->recalibrateNoiseFloor();
|
||||
}
|
||||
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
|
||||
setRxPowerSaving(_prefs.rx_powersaving_enabled, _prefs.rx_ps_rx_us, _prefs.rx_ps_sleep_us);
|
||||
|
||||
updateAdvertTimer();
|
||||
|
||||
+21
-11
@@ -5,8 +5,9 @@
|
||||
#include <Ed25519.h>
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
#include "helpers/NRF52Crypto.h"
|
||||
#include "nrf_cc310/include/crys_ec_edw_api.h"
|
||||
#include "nrf_cc310/include/crys_ec_mont_edw_error.h"
|
||||
#endif
|
||||
|
||||
namespace mesh {
|
||||
@@ -21,20 +22,29 @@ Identity::Identity(const char* pub_hex) {
|
||||
}
|
||||
|
||||
bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const {
|
||||
if (sig == NULL || message == NULL || msg_len < 0) return false;
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
// nRF52840 CryptoCell CC310 hardware Ed25519 verification. The software
|
||||
// implementations need ~3KB of stack (which can overflow the Adafruit core's
|
||||
// 4KB loop task stack from the advert receive path); the hardware path
|
||||
// needs much less, around 600-700bytes. The CC310 workspace is static, faster,
|
||||
// should save power at scale as well.
|
||||
static CRYS_ECEDW_TempBuff_t cc310_tmp;
|
||||
nRFCrypto.begin();
|
||||
CRYSError_t rc = CRYS_ECEDW_Verify((uint8_t*)sig, CRYS_ECEDW_SIGNATURE_BYTES,
|
||||
(uint8_t*)pub_key, CRYS_ECEDW_MOD_SIZE_IN_BYTES,
|
||||
(uint8_t*)message, (size_t)msg_len, &cc310_tmp);
|
||||
nRFCrypto.end();
|
||||
return rc == CRYS_OK;
|
||||
#elif 0
|
||||
// needs much less, around 600-700 bytes. Access is serialized because the
|
||||
// workspace and the CC310 driver are not reentrant.
|
||||
CC310CryptoSession session;
|
||||
if (session) {
|
||||
static CRYS_ECEDW_TempBuff_t cc310_tmp;
|
||||
const CRYSError_t rc = CRYS_ECEDW_Verify(
|
||||
const_cast<uint8_t*>(sig), CRYS_ECEDW_SIGNATURE_BYTES,
|
||||
const_cast<uint8_t*>(pub_key), CRYS_ECEDW_MOD_SIZE_IN_BYTES,
|
||||
const_cast<uint8_t*>(message), static_cast<size_t>(msg_len), &cc310_tmp);
|
||||
if (rc == CRYS_OK) return true;
|
||||
// A normal signature mismatch is a conclusive hardware result. Other
|
||||
// CC310 errors fall through to the correctness-preserving software path.
|
||||
if (rc == CRYS_ECEDW_SIGN_VERIFY_FAILED_ERROR) return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// NOTE: memory corruption bug was found in this function!!
|
||||
return ed25519_verify(sig, message, msg_len, pub_key);
|
||||
#else
|
||||
|
||||
@@ -93,6 +93,10 @@ public:
|
||||
virtual bool setLoRaFemLnaEnabled(bool enable) { return false; }
|
||||
virtual bool canControlLoRaFemLna() const { return false; }
|
||||
virtual bool isLoRaFemLnaEnabled() const { return false; }
|
||||
// Software-selectable external FEM transmit gain. This is not a PA power switch.
|
||||
virtual bool setLoRaFemPaGainEnabled(bool enable) { return false; }
|
||||
virtual bool canControlLoRaFemPaGain() const { return false; }
|
||||
virtual bool isLoRaFemPaGainEnabled() const { return false; }
|
||||
#if defined(ENABLE_OTA)
|
||||
// 4-byte build-target discriminator for OTA-over-LoRa (docs/ota_protocol.md Section 9). Default is the
|
||||
// MOTA_TARGET_ID build flag injected by build.sh; 0 when unset (e.g. a bare IDE build).
|
||||
|
||||
+199
-120
@@ -3,7 +3,7 @@
|
||||
#include <SHA256.h>
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
#include "helpers/NRF52Crypto.h"
|
||||
#include "nrf_cc310/include/crys_hash.h"
|
||||
#include "nrf_cc310/include/crys_hmac.h"
|
||||
#include "nrf_cc310/include/ssi_aes.h"
|
||||
@@ -13,6 +13,142 @@
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void sha256Software(uint8_t* hash, size_t hash_len, const uint8_t* msg, int msg_len) {
|
||||
SHA256 sha;
|
||||
sha.update(msg, msg_len);
|
||||
sha.finalize(hash, hash_len);
|
||||
}
|
||||
|
||||
void sha256Software(uint8_t* hash, size_t hash_len,
|
||||
const uint8_t* frag1, int frag1_len,
|
||||
const uint8_t* frag2, int frag2_len) {
|
||||
SHA256 sha;
|
||||
sha.update(frag1, frag1_len);
|
||||
sha.update(frag2, frag2_len);
|
||||
sha.finalize(hash, hash_len);
|
||||
}
|
||||
|
||||
int decryptSoftware(const uint8_t* shared_secret, uint8_t* dest,
|
||||
const uint8_t* src, int src_len) {
|
||||
AES128 aes;
|
||||
uint8_t* dp = dest;
|
||||
const uint8_t* sp = src;
|
||||
|
||||
aes.setKey(shared_secret, CIPHER_KEY_SIZE);
|
||||
while (sp - src < src_len) {
|
||||
aes.decryptBlock(dp, sp);
|
||||
dp += CIPHER_BLOCK_SIZE;
|
||||
sp += CIPHER_BLOCK_SIZE;
|
||||
}
|
||||
return static_cast<int>(sp - src);
|
||||
}
|
||||
|
||||
int encryptSoftware(const uint8_t* shared_secret, uint8_t* dest,
|
||||
const uint8_t* src, int src_len) {
|
||||
AES128 aes;
|
||||
uint8_t* dp = dest;
|
||||
|
||||
aes.setKey(shared_secret, CIPHER_KEY_SIZE);
|
||||
while (src_len >= CIPHER_BLOCK_SIZE) {
|
||||
aes.encryptBlock(dp, src);
|
||||
dp += CIPHER_BLOCK_SIZE;
|
||||
src += CIPHER_BLOCK_SIZE;
|
||||
src_len -= CIPHER_BLOCK_SIZE;
|
||||
}
|
||||
if (src_len > 0) {
|
||||
uint8_t tmp[CIPHER_BLOCK_SIZE] = {};
|
||||
memcpy(tmp, src, src_len);
|
||||
aes.encryptBlock(dp, tmp);
|
||||
dp += CIPHER_BLOCK_SIZE;
|
||||
}
|
||||
return static_cast<int>(dp - dest);
|
||||
}
|
||||
|
||||
void hmacSoftware(const uint8_t* shared_secret, uint8_t* dest,
|
||||
const uint8_t* src, int src_len) {
|
||||
SHA256 sha;
|
||||
sha.resetHMAC(shared_secret, PUB_KEY_SIZE);
|
||||
sha.update(src, src_len);
|
||||
sha.finalizeHMAC(shared_secret, PUB_KEY_SIZE, dest, CIPHER_MAC_SIZE);
|
||||
}
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
|
||||
bool rangesOverlap(const uint8_t* first, size_t first_len,
|
||||
const uint8_t* second, size_t second_len) {
|
||||
const uintptr_t first_addr = reinterpret_cast<uintptr_t>(first);
|
||||
const uintptr_t second_addr = reinterpret_cast<uintptr_t>(second);
|
||||
if (first_addr <= second_addr) return second_addr - first_addr < first_len;
|
||||
return first_addr - second_addr < second_len;
|
||||
}
|
||||
|
||||
// Returns -1 when the hardware path is unavailable or any CC310 call fails.
|
||||
// Callers retain the original input and can recompute the complete result in
|
||||
// software.
|
||||
int aesHardware(bool encrypting, const uint8_t* shared_secret, uint8_t* dest,
|
||||
const uint8_t* src, int src_len) {
|
||||
mesh::CC310CryptoSession session;
|
||||
if (!session) return -1;
|
||||
|
||||
static SaSiAesUserContext_t ctx;
|
||||
SaSiAesUserKeyData_t key_data = {
|
||||
const_cast<uint8_t*>(shared_secret), CIPHER_KEY_SIZE
|
||||
};
|
||||
SaSiError_t rc = SaSi_AesInit(
|
||||
&ctx, encrypting ? SASI_AES_ENCRYPT : SASI_AES_DECRYPT,
|
||||
SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE);
|
||||
const bool initialized = rc == SASI_OK;
|
||||
if (rc == SASI_OK) {
|
||||
rc = SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &key_data, sizeof(key_data));
|
||||
}
|
||||
|
||||
uint8_t* dp = dest;
|
||||
const uint8_t* sp = src;
|
||||
int remaining = src_len;
|
||||
while (rc == SASI_OK && remaining >= CIPHER_BLOCK_SIZE) {
|
||||
rc = SaSi_AesBlock(&ctx, const_cast<uint8_t*>(sp), CIPHER_BLOCK_SIZE, dp);
|
||||
if (rc == SASI_OK) {
|
||||
dp += CIPHER_BLOCK_SIZE;
|
||||
sp += CIPHER_BLOCK_SIZE;
|
||||
remaining -= CIPHER_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
if (rc == SASI_OK && encrypting && remaining > 0) {
|
||||
uint8_t padded[CIPHER_BLOCK_SIZE] = {};
|
||||
memcpy(padded, sp, remaining);
|
||||
rc = SaSi_AesBlock(&ctx, padded, CIPHER_BLOCK_SIZE, dp);
|
||||
if (rc == SASI_OK) dp += CIPHER_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
size_t final_size = 0;
|
||||
if (rc == SASI_OK) {
|
||||
rc = SaSi_AesFinish(&ctx, 0, NULL, 0, NULL, &final_size);
|
||||
}
|
||||
const SaSiError_t free_rc = initialized ? SaSi_AesFree(&ctx) : SASI_OK;
|
||||
if (rc != SASI_OK || free_rc != SASI_OK) return -1;
|
||||
return static_cast<int>(dp - dest);
|
||||
}
|
||||
|
||||
bool hmacHardware(const uint8_t* shared_secret, uint8_t* dest,
|
||||
const uint8_t* src, int src_len) {
|
||||
mesh::CC310CryptoSession session;
|
||||
if (!session) return false;
|
||||
|
||||
static CRYS_HASH_Result_t result;
|
||||
const CRYSError_t rc = CRYS_HMAC(
|
||||
CRYS_HASH_SHA256_mode, const_cast<uint8_t*>(shared_secret), PUB_KEY_SIZE,
|
||||
const_cast<uint8_t*>(src), static_cast<size_t>(src_len), result);
|
||||
if (rc != CRYS_OK) return false;
|
||||
memcpy(dest, result, CIPHER_MAC_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mesh {
|
||||
|
||||
uint32_t RNG::nextInt(uint32_t _min, uint32_t _max) {
|
||||
@@ -23,35 +159,53 @@ uint32_t RNG::nextInt(uint32_t _min, uint32_t _max) {
|
||||
|
||||
void Utils::sha256(uint8_t *hash, size_t hash_len, const uint8_t* msg, int msg_len) {
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
static CRYS_HASH_Result_t result;
|
||||
nRFCrypto.begin();
|
||||
CRYS_HASH(CRYS_HASH_SHA256_mode, (uint8_t*)msg, (size_t)msg_len, result);
|
||||
nRFCrypto.end();
|
||||
memcpy(hash, result, hash_len);
|
||||
#else
|
||||
SHA256 sha;
|
||||
sha.update(msg, msg_len);
|
||||
sha.finalize(hash, hash_len);
|
||||
if (hash_len <= SHA256::HASH_SIZE && msg_len >= 0) {
|
||||
CC310CryptoSession session;
|
||||
if (session) {
|
||||
static CRYS_HASH_Result_t result;
|
||||
const CRYSError_t rc = CRYS_HASH(
|
||||
CRYS_HASH_SHA256_mode, const_cast<uint8_t*>(msg),
|
||||
static_cast<size_t>(msg_len), result);
|
||||
if (rc == CRYS_OK) {
|
||||
memcpy(hash, result, hash_len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sha256Software(hash, hash_len, msg, msg_len);
|
||||
}
|
||||
|
||||
void Utils::sha256(uint8_t *hash, size_t hash_len, const uint8_t* frag1, int frag1_len, const uint8_t* frag2, int frag2_len) {
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
static CRYS_HASHUserContext_t ctx;
|
||||
static CRYS_HASH_Result_t result;
|
||||
nRFCrypto.begin();
|
||||
CRYS_HASH_Init(&ctx, CRYS_HASH_SHA256_mode);
|
||||
CRYS_HASH_Update(&ctx, (uint8_t*)frag1, (size_t)frag1_len);
|
||||
CRYS_HASH_Update(&ctx, (uint8_t*)frag2, (size_t)frag2_len);
|
||||
CRYS_HASH_Finish(&ctx, result);
|
||||
nRFCrypto.end();
|
||||
memcpy(hash, result, hash_len);
|
||||
#else
|
||||
SHA256 sha;
|
||||
sha.update(frag1, frag1_len);
|
||||
sha.update(frag2, frag2_len);
|
||||
sha.finalize(hash, hash_len);
|
||||
if (hash_len <= SHA256::HASH_SIZE && frag1_len > 0 && frag2_len > 0) {
|
||||
CC310CryptoSession session;
|
||||
if (session) {
|
||||
static CRYS_HASHUserContext_t ctx;
|
||||
static CRYS_HASH_Result_t result;
|
||||
CRYSError_t rc = CRYS_HASH_Init(&ctx, CRYS_HASH_SHA256_mode);
|
||||
const bool initialized = rc == CRYS_OK;
|
||||
if (rc == CRYS_OK) {
|
||||
rc = CRYS_HASH_Update(&ctx, const_cast<uint8_t*>(frag1),
|
||||
static_cast<size_t>(frag1_len));
|
||||
}
|
||||
if (rc == CRYS_OK) {
|
||||
rc = CRYS_HASH_Update(&ctx, const_cast<uint8_t*>(frag2),
|
||||
static_cast<size_t>(frag2_len));
|
||||
}
|
||||
if (rc == CRYS_OK) rc = CRYS_HASH_Finish(&ctx, result);
|
||||
if (rc == CRYS_OK) {
|
||||
memcpy(hash, result, hash_len);
|
||||
return;
|
||||
}
|
||||
if (initialized) {
|
||||
const bool context_freed = CRYS_HASH_Free(&ctx) == CRYS_OK;
|
||||
(void) context_freed;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sha256Software(hash, hash_len, frag1, frag1_len, frag2, frag2_len);
|
||||
}
|
||||
|
||||
int Utils::decrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* src, int src_len) {
|
||||
@@ -61,100 +215,38 @@ int Utils::decrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* s
|
||||
}
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
static SaSiAesUserContext_t ctx;
|
||||
SaSiAesUserKeyData_t keyData = { (uint8_t*)shared_secret, CIPHER_KEY_SIZE };
|
||||
uint8_t* dp = dest;
|
||||
const uint8_t* sp = src;
|
||||
size_t dummy_out = 0;
|
||||
|
||||
nRFCrypto.begin();
|
||||
SaSi_AesInit(&ctx, SASI_AES_DECRYPT, SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE);
|
||||
SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &keyData, sizeof(keyData));
|
||||
while (sp - src < src_len) {
|
||||
SaSi_AesBlock(&ctx, (uint8_t*)sp, 16, dp);
|
||||
dp += 16; sp += 16;
|
||||
if (!rangesOverlap(dest, static_cast<size_t>(src_len),
|
||||
src, static_cast<size_t>(src_len))) {
|
||||
const int hardware_len = aesHardware(false, shared_secret, dest, src, src_len);
|
||||
if (hardware_len >= 0) return hardware_len;
|
||||
}
|
||||
SaSi_AesFinish(&ctx, 0, NULL, 0, NULL, &dummy_out);
|
||||
SaSi_AesFree(&ctx);
|
||||
nRFCrypto.end();
|
||||
return sp - src;
|
||||
#else
|
||||
AES128 aes;
|
||||
uint8_t* dp = dest;
|
||||
const uint8_t* sp = src;
|
||||
|
||||
aes.setKey(shared_secret, CIPHER_KEY_SIZE);
|
||||
while (sp - src < src_len) {
|
||||
aes.decryptBlock(dp, sp);
|
||||
dp += 16; sp += 16;
|
||||
}
|
||||
|
||||
return sp - src; // will always be multiple of 16
|
||||
#endif
|
||||
return decryptSoftware(shared_secret, dest, src, src_len);
|
||||
}
|
||||
|
||||
int Utils::encrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* src, int src_len) {
|
||||
if (shared_secret == NULL || dest == NULL || src == NULL || src_len <= 0) return 0;
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
static SaSiAesUserContext_t ctx;
|
||||
SaSiAesUserKeyData_t keyData = { (uint8_t*)shared_secret, CIPHER_KEY_SIZE };
|
||||
uint8_t* dp = dest;
|
||||
size_t dummy_out = 0;
|
||||
|
||||
nRFCrypto.begin();
|
||||
SaSi_AesInit(&ctx, SASI_AES_ENCRYPT, SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE);
|
||||
SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &keyData, sizeof(keyData));
|
||||
while (src_len >= 16) {
|
||||
SaSi_AesBlock(&ctx, (uint8_t*)src, 16, dp);
|
||||
dp += 16; src += 16; src_len -= 16;
|
||||
const size_t output_len = static_cast<size_t>(
|
||||
(src_len + CIPHER_BLOCK_SIZE - 1) / CIPHER_BLOCK_SIZE * CIPHER_BLOCK_SIZE);
|
||||
if (!rangesOverlap(dest, output_len, src, static_cast<size_t>(src_len))) {
|
||||
const int hardware_len = aesHardware(true, shared_secret, dest, src, src_len);
|
||||
if (hardware_len >= 0) return hardware_len;
|
||||
}
|
||||
if (src_len > 0) { // remaining partial block — zero-pad to 16 bytes
|
||||
uint8_t tmp[16] = {};
|
||||
memcpy(tmp, src, src_len);
|
||||
SaSi_AesBlock(&ctx, tmp, 16, dp);
|
||||
dp += 16;
|
||||
}
|
||||
SaSi_AesFinish(&ctx, 0, NULL, 0, NULL, &dummy_out);
|
||||
SaSi_AesFree(&ctx);
|
||||
nRFCrypto.end();
|
||||
return dp - dest;
|
||||
#else
|
||||
AES128 aes;
|
||||
uint8_t* dp = dest;
|
||||
|
||||
aes.setKey(shared_secret, CIPHER_KEY_SIZE);
|
||||
while (src_len >= 16) {
|
||||
aes.encryptBlock(dp, src);
|
||||
dp += 16; src += 16; src_len -= 16;
|
||||
}
|
||||
if (src_len > 0) { // remaining partial block
|
||||
uint8_t tmp[16];
|
||||
memset(tmp, 0, 16);
|
||||
memcpy(tmp, src, src_len);
|
||||
aes.encryptBlock(dp, tmp);
|
||||
dp += 16;
|
||||
}
|
||||
return dp - dest; // will always be multiple of 16
|
||||
#endif
|
||||
return encryptSoftware(shared_secret, dest, src, src_len);
|
||||
}
|
||||
|
||||
int Utils::encryptThenMAC(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* src, int src_len) {
|
||||
int enc_len = encrypt(shared_secret, dest + CIPHER_MAC_SIZE, src, src_len);
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
static CRYS_HMACUserContext_t hmac_ctx;
|
||||
static CRYS_HASH_Result_t hmac_result;
|
||||
nRFCrypto.begin();
|
||||
CRYS_HMAC_Init(&hmac_ctx, CRYS_HASH_SHA256_mode, (uint8_t*)shared_secret, PUB_KEY_SIZE);
|
||||
CRYS_HMAC_Update(&hmac_ctx, dest + CIPHER_MAC_SIZE, enc_len);
|
||||
CRYS_HMAC_Finish(&hmac_ctx, hmac_result);
|
||||
nRFCrypto.end();
|
||||
memcpy(dest, hmac_result, CIPHER_MAC_SIZE);
|
||||
#else
|
||||
SHA256 sha;
|
||||
sha.resetHMAC(shared_secret, PUB_KEY_SIZE);
|
||||
sha.update(dest + CIPHER_MAC_SIZE, enc_len);
|
||||
sha.finalizeHMAC(shared_secret, PUB_KEY_SIZE, dest, CIPHER_MAC_SIZE);
|
||||
if (!hmacHardware(shared_secret, dest, dest + CIPHER_MAC_SIZE, enc_len))
|
||||
#endif
|
||||
{
|
||||
hmacSoftware(shared_secret, dest, dest + CIPHER_MAC_SIZE, enc_len);
|
||||
}
|
||||
|
||||
return CIPHER_MAC_SIZE + enc_len;
|
||||
}
|
||||
@@ -167,24 +259,11 @@ int Utils::MACThenDecrypt(const uint8_t* shared_secret, uint8_t* dest, const uin
|
||||
|
||||
uint8_t hmac[CIPHER_MAC_SIZE];
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
{
|
||||
static CRYS_HMACUserContext_t hmac_ctx;
|
||||
static CRYS_HASH_Result_t hmac_result;
|
||||
nRFCrypto.begin();
|
||||
CRYS_HMAC_Init(&hmac_ctx, CRYS_HASH_SHA256_mode, (uint8_t*)shared_secret, PUB_KEY_SIZE);
|
||||
CRYS_HMAC_Update(&hmac_ctx, (uint8_t*)(src + CIPHER_MAC_SIZE), src_len - CIPHER_MAC_SIZE);
|
||||
CRYS_HMAC_Finish(&hmac_ctx, hmac_result);
|
||||
nRFCrypto.end();
|
||||
memcpy(hmac, hmac_result, CIPHER_MAC_SIZE);
|
||||
}
|
||||
#else
|
||||
{
|
||||
SHA256 sha;
|
||||
sha.resetHMAC(shared_secret, PUB_KEY_SIZE);
|
||||
sha.update(src + CIPHER_MAC_SIZE, enc_len);
|
||||
sha.finalizeHMAC(shared_secret, PUB_KEY_SIZE, hmac, CIPHER_MAC_SIZE);
|
||||
}
|
||||
if (!hmacHardware(shared_secret, hmac, src + CIPHER_MAC_SIZE, enc_len))
|
||||
#endif
|
||||
{
|
||||
hmacSoftware(shared_secret, hmac, src + CIPHER_MAC_SIZE, enc_len);
|
||||
}
|
||||
if (memcmp(hmac, src, CIPHER_MAC_SIZE) == 0) {
|
||||
return decrypt(shared_secret, dest, src + CIPHER_MAC_SIZE, enc_len);
|
||||
}
|
||||
|
||||
@@ -909,6 +909,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
// Defaults for the trailing fields that older/shorter files may not contain.
|
||||
// Build-profile defaults - overwritten below when the saved field is present.
|
||||
_prefs->radio_fem_rxgain = 1;
|
||||
_prefs->radio_fem_txgain = 0;
|
||||
_prefs->cad_enabled = DEFAULT_CAD_ENABLED;
|
||||
memset(_prefs->extra_sf, 0, sizeof(_prefs->extra_sf));
|
||||
_prefs->rx_powersaving_enabled = 0;
|
||||
@@ -1193,6 +1194,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
}
|
||||
if (file.available() >= (int)sizeof(_prefs->extra_sf)) {
|
||||
file.read((uint8_t *)_prefs->extra_sf, sizeof(_prefs->extra_sf));
|
||||
if (file.available() >= (int)sizeof(_prefs->radio_fem_txgain)) {
|
||||
file.read((uint8_t *)&_prefs->radio_fem_txgain,
|
||||
sizeof(_prefs->radio_fem_txgain));
|
||||
}
|
||||
} else if (file.available() > 0) {
|
||||
// Never accept a torn append as a partial detector list.
|
||||
_com_prefs_needs_upgrade = true;
|
||||
@@ -1261,6 +1266,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
|
||||
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
|
||||
_prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean
|
||||
_prefs->radio_fem_txgain = constrain(_prefs->radio_fem_txgain, 0, 1); // boolean
|
||||
_prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean
|
||||
if (!directRetryPrefsValid(_prefs)) {
|
||||
setDefaultDirectRetryPrefs(_prefs);
|
||||
@@ -1467,6 +1473,7 @@ static bool writeCommonPrefsImage(Writer& writer, NodePrefs* prefs) {
|
||||
WRITE_COMMON_PREFS(&prefs->rx_watchdog_enabled); // 854
|
||||
WRITE_COMMON_PREFS(&prefs->system_watchdog_enabled); // 855
|
||||
WRITE_COMMON_PREFS(&prefs->extra_sf); // 856
|
||||
WRITE_COMMON_PREFS(&prefs->radio_fem_txgain); // 860
|
||||
|
||||
#undef WRITE_COMMON_PREFS_BYTES
|
||||
#undef WRITE_COMMON_PREFS
|
||||
@@ -1617,7 +1624,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs, PrefsSaveRouting::Scope scope) {
|
||||
file.write((uint8_t *)&_prefs->system_watchdog_enabled,
|
||||
sizeof(_prefs->system_watchdog_enabled)); // 855
|
||||
file.write((uint8_t *)_prefs->extra_sf, sizeof(_prefs->extra_sf)); // 856
|
||||
// next: 860
|
||||
file.write((uint8_t *)&_prefs->radio_fem_txgain, sizeof(_prefs->radio_fem_txgain)); // 860
|
||||
// next: 861
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
if (!file.commit()) {
|
||||
@@ -3208,6 +3216,23 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
|
||||
}
|
||||
}
|
||||
} else if (memcmp(config, "radio.fem.txgain ", 17) == 0) {
|
||||
const char* value = &config[17];
|
||||
if (!_board->canControlLoRaFemPaGain()) {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
} else if (strcmp(value, "on") != 0 && strcmp(value, "off") != 0) {
|
||||
strcpy(reply, "Error: state must be on or off");
|
||||
} else {
|
||||
const bool enabled = strcmp(value, "on") == 0;
|
||||
if (_board->setLoRaFemPaGainEnabled(enabled)) {
|
||||
_prefs->radio_fem_txgain = enabled ? 1 : 0;
|
||||
savePrefs();
|
||||
strcpy(reply, enabled
|
||||
? "OK - LoRa FEM TX gain on" : "OK - LoRa FEM TX gain off");
|
||||
} else {
|
||||
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
|
||||
}
|
||||
}
|
||||
} else if (memcmp(config, "radio ", 6) == 0) {
|
||||
strcpy(tmp, &config[6]);
|
||||
const char* parts[4];
|
||||
@@ -3508,6 +3533,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
} else {
|
||||
strcpy(reply, "Error: state must be on or off");
|
||||
}
|
||||
} else if (memcmp(config, "radio.fem.txgain ", 17) == 0) {
|
||||
if (!_board->canControlLoRaFemPaGain()) {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
} else if (memcmp(&config[17], "on", 2) == 0) {
|
||||
if (_board->setLoRaFemPaGainEnabled(true)) {
|
||||
_prefs->radio_fem_txgain = 1;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK - LoRa FEM TX gain on");
|
||||
} else {
|
||||
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
|
||||
}
|
||||
} else if (memcmp(&config[17], "off", 3) == 0) {
|
||||
if (_board->setLoRaFemPaGainEnabled(false)) {
|
||||
_prefs->radio_fem_txgain = 0;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK - LoRa FEM TX gain off");
|
||||
} else {
|
||||
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Error: state must be on or off");
|
||||
}
|
||||
} else if (memcmp(config, "radio.rxps ", 11) == 0) { // RX PowerSaving
|
||||
const char* value = &config[11];
|
||||
uint8_t enable = _prefs->rx_powersaving_enabled;
|
||||
@@ -4249,6 +4296,13 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
sprintf(reply, "> %s",
|
||||
_board->isLoRaFemLnaEnabled() ? "on" : "off");
|
||||
}
|
||||
} else if (strcmp(config, "radio.fem.txgain") == 0) {
|
||||
if (!_board->canControlLoRaFemPaGain()) {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
} else {
|
||||
sprintf(reply, "> %s",
|
||||
_board->isLoRaFemPaGainEnabled() ? "on" : "off");
|
||||
}
|
||||
} else if (strcmp(config, "radio") == 0) {
|
||||
char freq[16], bw[16];
|
||||
strcpy(freq, StrHelper::ftoa(_prefs->freq));
|
||||
@@ -4359,6 +4413,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
} else {
|
||||
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off");
|
||||
}
|
||||
} else if (memcmp(config, "radio.fem.txgain", 16) == 0) {
|
||||
if (!_board->canControlLoRaFemPaGain()) {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
} else {
|
||||
sprintf(reply, "> %s", _board->isLoRaFemPaGainEnabled() ? "on" : "off");
|
||||
}
|
||||
} else if (memcmp(config, "tempradioat", 11) == 0 && (config[11] == 0 || config[11] == ' ')) {
|
||||
_callbacks->formatScheduledRadioParams(true, skipSpacesConst(&config[11]), reply);
|
||||
} else if (memcmp(config, "radioat", 7) == 0 && (config[7] == 0 || config[7] == ' ')) {
|
||||
|
||||
@@ -160,6 +160,7 @@ struct NodePrefs { // persisted to file
|
||||
// LR2021 side-detector SFs are appended at /com_prefs offset 856. Their
|
||||
// in-memory placement here does not shift the established binary layout.
|
||||
uint8_t extra_sf[4] = {};
|
||||
uint8_t radio_fem_txgain; // LoRa FEM TX gain; persisted at /com_prefs offset 860
|
||||
uint8_t retry_preset;
|
||||
uint8_t direct_retry_attempts;
|
||||
uint16_t direct_retry_base_ms;
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "NRF52Crypto.h"
|
||||
|
||||
#if defined(USE_CC310_HW_CRYPTO)
|
||||
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace {
|
||||
|
||||
enum CC310State : uint8_t {
|
||||
CC310_UNINITIALIZED,
|
||||
CC310_AVAILABLE,
|
||||
CC310_UNAVAILABLE
|
||||
};
|
||||
|
||||
StaticSemaphore_t cc310_mutex_storage;
|
||||
SemaphoreHandle_t cc310_mutex = NULL;
|
||||
CC310State cc310_state = CC310_UNINITIALIZED;
|
||||
|
||||
SemaphoreHandle_t getCC310Mutex() {
|
||||
if (cc310_mutex == NULL) {
|
||||
// nRF52840 is single-core. The critical section makes the lazy static
|
||||
// mutex construction safe if two tasks make their first crypto call at
|
||||
// the same time.
|
||||
taskENTER_CRITICAL();
|
||||
if (cc310_mutex == NULL) {
|
||||
cc310_mutex = xSemaphoreCreateMutexStatic(&cc310_mutex_storage);
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
return cc310_mutex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mesh {
|
||||
|
||||
CC310CryptoSession::CC310CryptoSession()
|
||||
: _locked(false), _available(false) {
|
||||
// FreeRTOS mutex APIs and the CC310 driver are not ISR-safe.
|
||||
if (isInISR()) return;
|
||||
|
||||
SemaphoreHandle_t mutex = getCC310Mutex();
|
||||
if (mutex == NULL || xSemaphoreTake(mutex, portMAX_DELAY) != pdTRUE) return;
|
||||
_locked = true;
|
||||
|
||||
if (cc310_state == CC310_UNINITIALIZED) {
|
||||
// Adafruit_nRFCrypto::begin() also initializes its DRBG. Keep it alive for
|
||||
// the process lifetime: end()/begin() would repeatedly reset that state.
|
||||
cc310_state = nRFCrypto.begin() ? CC310_AVAILABLE : CC310_UNAVAILABLE;
|
||||
}
|
||||
_available = cc310_state == CC310_AVAILABLE;
|
||||
}
|
||||
|
||||
CC310CryptoSession::~CC310CryptoSession() {
|
||||
if (_locked) xSemaphoreGive(cc310_mutex);
|
||||
}
|
||||
|
||||
bool initializeCC310Crypto() {
|
||||
CC310CryptoSession session;
|
||||
return static_cast<bool>(session);
|
||||
}
|
||||
|
||||
void mixCC310Random(uint8_t* dest, size_t size) {
|
||||
if (dest == NULL || size == 0) return;
|
||||
|
||||
CC310CryptoSession session;
|
||||
if (!session) return;
|
||||
|
||||
uint8_t hardware_random[32];
|
||||
size_t offset = 0;
|
||||
while (offset < size) {
|
||||
const size_t remaining = size - offset;
|
||||
const uint16_t chunk = static_cast<uint16_t>(
|
||||
remaining < sizeof(hardware_random) ? remaining : sizeof(hardware_random));
|
||||
if (!nRFCrypto.Random.generate(hardware_random, chunk)) break;
|
||||
for (uint16_t i = 0; i < chunk; ++i) {
|
||||
dest[offset + i] ^= hardware_random[i];
|
||||
}
|
||||
offset += chunk;
|
||||
}
|
||||
memset(hardware_random, 0, sizeof(hardware_random));
|
||||
}
|
||||
|
||||
} // namespace mesh
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_CC310_HW_CRYPTO)
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mesh {
|
||||
|
||||
// Serializes access to the CC310 and performs its process-lifetime
|
||||
// initialization. A session is unavailable in interrupt context or when the
|
||||
// one-time hardware initialization failed; callers must then use software.
|
||||
class CC310CryptoSession {
|
||||
public:
|
||||
CC310CryptoSession();
|
||||
~CC310CryptoSession();
|
||||
|
||||
explicit operator bool() const { return _available; }
|
||||
|
||||
CC310CryptoSession(const CC310CryptoSession&) = delete;
|
||||
CC310CryptoSession& operator=(const CC310CryptoSession&) = delete;
|
||||
|
||||
private:
|
||||
bool _locked;
|
||||
bool _available;
|
||||
};
|
||||
|
||||
// Initialize the CC310 once during normal system startup. Sessions also call
|
||||
// this lazily so early crypto users remain safe.
|
||||
bool initializeCC310Crypto();
|
||||
|
||||
// XOR CC310 random output into bytes already populated by another entropy
|
||||
// source. A hardware failure leaves those existing bytes usable.
|
||||
void mixCC310Random(uint8_t* dest, size_t size);
|
||||
|
||||
} // namespace mesh
|
||||
|
||||
#endif
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomLLCC68 *)_radio)->spreadingFactor; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
|
||||
|
||||
protected:
|
||||
bool radioDeepInit() override {
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
return ((CustomLR1110 *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE;
|
||||
}
|
||||
|
||||
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); }
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomLR1110 *)_radio)->isReceiving();
|
||||
}
|
||||
@@ -133,4 +132,6 @@ public:
|
||||
bool getRxBoostedGainMode() const override {
|
||||
return ((CustomLR1110 *)_radio)->getRxBoostedGainMode();
|
||||
}
|
||||
|
||||
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz(), getRxBoostedGainMode()); }
|
||||
};
|
||||
|
||||
@@ -35,5 +35,5 @@ public:
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomSTM32WLx *)_radio)->spreadingFactor; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
|
||||
};
|
||||
|
||||
@@ -99,8 +99,6 @@ protected:
|
||||
}
|
||||
bool supportsRadioDeepInit() const override { return true; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
|
||||
protected:
|
||||
bool applyRxBoostedGainMode(bool en) override {
|
||||
return ((CustomSX1262 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
|
||||
@@ -109,4 +107,6 @@ public:
|
||||
bool getRxBoostedGainMode() const override {
|
||||
return ((CustomSX1262 *)_radio)->getRxBoostedGainMode();
|
||||
}
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
|
||||
};
|
||||
|
||||
@@ -46,8 +46,6 @@ public:
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomSX1268 *)_radio)->spreadingFactor; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
|
||||
protected:
|
||||
bool radioDeepInit() override {
|
||||
return ((CustomSX1268 *)_radio)->std_init();
|
||||
@@ -62,4 +60,6 @@ public:
|
||||
bool getRxBoostedGainMode() const override {
|
||||
return ((CustomSX1268 *)_radio)->getRxBoostedGainMode();
|
||||
}
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// 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) {
|
||||
inline void lr11x0ResetAGC(LR11x0* radio, float freqMHz, bool rx_boost_gain) {
|
||||
radio->sleep(true, 0);
|
||||
radio->standby(RADIOLIB_LR11X0_STANDBY_RC, true);
|
||||
|
||||
@@ -16,8 +16,6 @@ inline void lr11x0ResetAGC(LR11x0* radio, float freqMHz) {
|
||||
radio->calibrateImageRejection(freqMHz - 4.0f, freqMHz + 4.0f);
|
||||
|
||||
#ifdef RX_BOOSTED_GAIN
|
||||
// Fallback for callers without a runtime override. RadioLibWrapper::resetAGC()
|
||||
// reapplies its cached radio.rxgain selection before receive mode resumes.
|
||||
radio->setRxBoostedGainMode(RX_BOOSTED_GAIN);
|
||||
radio->setRxBoostedGainMode(rx_boost_gain);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ void setFlag(void) {
|
||||
}
|
||||
|
||||
void RadioLibWrapper::begin() {
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
// Initialize CryptoCell once from normal task context. The session helper
|
||||
// still initializes lazily if an earlier crypto operation runs first.
|
||||
(void) mesh::initializeCC310Crypto();
|
||||
#endif
|
||||
_radio->setPacketReceivedAction(setFlag); // this is also SentComplete interrupt
|
||||
_preamble_sf = getSpreadingFactor();
|
||||
_radio->setPreambleLength(preambleLengthForSF(_preamble_sf)); // longer preamble for lower SF improves reliability
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define RX_PS_FALLBACK_SLEEP_US 60000UL
|
||||
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
#include "../NRF52Crypto.h"
|
||||
#endif
|
||||
struct PacketMillis {
|
||||
uint32_t preambleMillis; // preamble-detect -> header-valid deadline
|
||||
@@ -217,15 +217,14 @@ public:
|
||||
RadioNoiseListener(PhysicalLayer& radio): _radio(&radio) { }
|
||||
|
||||
void random(uint8_t* dest, size_t sz) override {
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
// CC310 TRNG is higher quality and environment-independent vs radio RSSI noise.
|
||||
nRFCrypto.begin();
|
||||
nRFCrypto.Random.generate(dest, (uint16_t)sz);
|
||||
nRFCrypto.end();
|
||||
#else
|
||||
for (int i = 0; i < sz; i++) {
|
||||
// Preserve the existing radio/PRNG entropy on every platform. On nRF52840,
|
||||
// independently generated CC310 bytes are mixed in without becoming the
|
||||
// sole source of randomness.
|
||||
for (size_t i = 0; i < sz; i++) {
|
||||
dest[i] = _radio->randomByte() ^ (::random(0, 256) & 0xFF);
|
||||
}
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
mesh::mixCC310Random(dest, sz);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// 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) {
|
||||
inline void sx126xResetAGC(SX126x* radio, bool rx_boost_gain) {
|
||||
radio->sleep(true);
|
||||
radio->standby(RADIOLIB_SX126X_STANDBY_RC, true);
|
||||
|
||||
@@ -26,9 +26,7 @@ inline void sx126xResetAGC(SX126x* radio) {
|
||||
radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
|
||||
#endif
|
||||
#ifdef SX126X_RX_BOOSTED_GAIN
|
||||
// Fallback for callers without a runtime override. RadioLibWrapper::resetAGC()
|
||||
// reapplies its cached radio.rxgain selection before receive mode resumes.
|
||||
radio->setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
|
||||
radio->setRxBoostedGainMode(rx_boost_gain);
|
||||
#endif
|
||||
#ifdef SX126X_REGISTER_PATCH
|
||||
uint8_t r_data = 0;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "LoRaFEMControl.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
void LoRaFEMControl::init() {
|
||||
#ifdef P_PA1_EN
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_PA1_EN);
|
||||
pinMode(P_PA1_EN, OUTPUT);
|
||||
applyPAGain();
|
||||
#endif
|
||||
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_PRIMARY_LNA_EN);
|
||||
pinMode(P_PRIMARY_LNA_EN, OUTPUT);
|
||||
setRxModeEnable();
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setSleepModeEnable() {
|
||||
#ifdef P_PA1_EN
|
||||
// PA PL1 low/open selects the lower of the two hardware-jumper-selected levels.
|
||||
digitalWrite(P_PA1_EN, !P_PA1_EN_ACTIVE);
|
||||
#endif
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
// Preserve the existing Station G3 power-off state.
|
||||
digitalWrite(P_PRIMARY_LNA_EN, P_PRIMARY_LNA_EN_ACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setTxModeEnable() {
|
||||
// Latch the requested PA level here, before the SX1262 starts driving the PA. PA PL1
|
||||
// retargets the PA's DC-DC rail, so moving it mid-transmit collapses the supply while
|
||||
// the PA is still driven at full input power.
|
||||
applyPAGain();
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
digitalWrite(P_PRIMARY_LNA_EN, !P_PRIMARY_LNA_EN_ACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setRxModeEnable() {
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
digitalWrite(P_PRIMARY_LNA_EN, lna_enabled ? P_PRIMARY_LNA_EN_ACTIVE : !P_PRIMARY_LNA_EN_ACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setLNAEnable(bool enabled) {
|
||||
lna_enabled = enabled;
|
||||
setRxModeEnable();
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setPAGainEnable(bool enabled) {
|
||||
// Recorded only -- the pin is driven from setTxModeEnable(). The PA level only matters
|
||||
// while transmitting, so deferring costs nothing and keeps the rail change out of an
|
||||
// in-flight transmit (the CLI runs on every main-loop pass, including mid-TX).
|
||||
pa_gain_enabled = enabled;
|
||||
}
|
||||
|
||||
void LoRaFEMControl::applyPAGain() {
|
||||
#ifdef P_PA1_EN
|
||||
digitalWrite(P_PA1_EN, pa_gain_enabled ? P_PA1_EN_ACTIVE : !P_PA1_EN_ACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LoRaFEMControl::canControlLNA() const {
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LoRaFEMControl::canControlPAGain() const {
|
||||
#ifdef P_PA1_EN
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
class LoRaFEMControl {
|
||||
public:
|
||||
void init();
|
||||
void setSleepModeEnable();
|
||||
void setTxModeEnable();
|
||||
void setRxModeEnable();
|
||||
void setLNAEnable(bool enabled);
|
||||
void setPAGainEnable(bool enabled);
|
||||
|
||||
bool canControlLNA() const;
|
||||
bool canControlPAGain() const;
|
||||
bool isLNAEnabled() const { return lna_enabled; }
|
||||
bool isPAGainEnabled() const { return pa_gain_enabled; }
|
||||
|
||||
private:
|
||||
void applyPAGain();
|
||||
|
||||
bool lna_enabled = true;
|
||||
bool pa_gain_enabled = false;
|
||||
};
|
||||
@@ -1,15 +1,46 @@
|
||||
#include "StationG3Board.h"
|
||||
|
||||
void StationG3Board::powerOff() {
|
||||
loRaFEMControl.setSleepModeEnable();
|
||||
#ifdef P_PA1_EN
|
||||
setPAModeHigh(false);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_PA1_EN);
|
||||
#endif
|
||||
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
setPrimaryLNAControl(true);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN);
|
||||
#endif
|
||||
|
||||
ESP32Board::powerOff();
|
||||
}
|
||||
|
||||
bool StationG3Board::setLoRaFemLnaEnabled(bool enable) {
|
||||
if (!loRaFEMControl.canControlLNA()) {
|
||||
return false;
|
||||
}
|
||||
loRaFEMControl.setLNAEnable(enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StationG3Board::canControlLoRaFemLna() const {
|
||||
return loRaFEMControl.canControlLNA();
|
||||
}
|
||||
|
||||
bool StationG3Board::isLoRaFemLnaEnabled() const {
|
||||
return loRaFEMControl.isLNAEnabled();
|
||||
}
|
||||
|
||||
bool StationG3Board::setLoRaFemPaGainEnabled(bool enable) {
|
||||
if (!loRaFEMControl.canControlPAGain()) {
|
||||
return false;
|
||||
}
|
||||
loRaFEMControl.setPAGainEnable(enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StationG3Board::canControlLoRaFemPaGain() const {
|
||||
return loRaFEMControl.canControlPAGain();
|
||||
}
|
||||
|
||||
bool StationG3Board::isLoRaFemPaGainEnabled() const {
|
||||
return loRaFEMControl.isPAGainEnabled();
|
||||
}
|
||||
|
||||
@@ -3,45 +3,15 @@
|
||||
#include <Arduino.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
#ifndef P_PRIMARY_LNA_EN_ACTIVE
|
||||
#define P_PRIMARY_LNA_EN_ACTIVE LOW
|
||||
#endif
|
||||
|
||||
#ifndef P_PA1_EN_ACTIVE
|
||||
#define P_PA1_EN_ACTIVE HIGH
|
||||
#endif
|
||||
#include "LoRaFEMControl.h"
|
||||
|
||||
class StationG3Board : public ESP32Board {
|
||||
void setPAModeHigh(bool enabled) {
|
||||
#ifdef P_PA1_EN
|
||||
// Station G3 PA PL1 mode: LOW/open is PA low, HIGH/short is PA high.
|
||||
digitalWrite(P_PA1_EN, enabled ? P_PA1_EN_ACTIVE : !P_PA1_EN_ACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setPrimaryLNAControl(bool enabled) {
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
// Station G3 primary LNA mode is active-low: LOW/open is LNA on, HIGH/short is LNA off.
|
||||
digitalWrite(P_PRIMARY_LNA_EN, enabled ? P_PRIMARY_LNA_EN_ACTIVE : !P_PRIMARY_LNA_EN_ACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
LoRaFEMControl loRaFEMControl;
|
||||
|
||||
void begin() {
|
||||
ESP32Board::begin();
|
||||
|
||||
#ifdef P_PA1_EN
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_PA1_EN);
|
||||
pinMode(P_PA1_EN, OUTPUT);
|
||||
setPAModeHigh(false);
|
||||
#endif
|
||||
|
||||
#ifdef P_PRIMARY_LNA_EN
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_PRIMARY_LNA_EN);
|
||||
pinMode(P_PRIMARY_LNA_EN, OUTPUT);
|
||||
setPrimaryLNAControl(true);
|
||||
#endif
|
||||
loRaFEMControl.init();
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
@@ -56,23 +26,30 @@ public:
|
||||
}
|
||||
|
||||
void setPrimaryLNAEnable(bool enabled) {
|
||||
setPrimaryLNAControl(enabled);
|
||||
loRaFEMControl.setLNAEnable(enabled);
|
||||
}
|
||||
|
||||
void setPrimaryPAHighPower(bool enabled) {
|
||||
setPAModeHigh(enabled);
|
||||
loRaFEMControl.setPAGainEnable(enabled);
|
||||
}
|
||||
|
||||
void onBeforeTransmit() override {
|
||||
ESP32Board::onBeforeTransmit();
|
||||
setPrimaryLNAControl(false);
|
||||
loRaFEMControl.setTxModeEnable();
|
||||
}
|
||||
|
||||
void onAfterTransmit() override {
|
||||
ESP32Board::onAfterTransmit();
|
||||
setPrimaryLNAControl(true);
|
||||
loRaFEMControl.setRxModeEnable();
|
||||
}
|
||||
|
||||
bool setLoRaFemLnaEnabled(bool enable) override;
|
||||
bool canControlLoRaFemLna() const override;
|
||||
bool isLoRaFemLnaEnabled() const override;
|
||||
bool setLoRaFemPaGainEnabled(bool enable) override;
|
||||
bool canControlLoRaFemPaGain() const override;
|
||||
bool isLoRaFemPaGainEnabled() const override;
|
||||
|
||||
void powerOff() override;
|
||||
|
||||
uint16_t getBattMilliVolts() override {
|
||||
|
||||
@@ -18,11 +18,11 @@ build_flags =
|
||||
-D P_LORA_SCLK=12
|
||||
-D P_LORA_MISO=14
|
||||
-D P_LORA_MOSI=13
|
||||
-D P_PA1_EN=9 ; PA PL1 Mode: LOW/open is PA low, HIGH/short is PA high.
|
||||
-D P_PA1_EN=9 ; PA PL1 Mode: LOW/open selects low level, HIGH/short selects high level.
|
||||
-D P_PA1_EN_ACTIVE=HIGH
|
||||
-D P_PRIMARY_LNA_EN=10 ; Primary Slot LNA Mode: LOW/open is LNA on, HIGH/short is LNA off.
|
||||
-D P_PRIMARY_LNA_EN_ACTIVE=LOW
|
||||
-D LORA_TX_POWER=7 ; configured as 7dbm, because the final output will be ~27dbm (~0.5w) if the PA is enabled.
|
||||
-D LORA_TX_POWER=7 ; SX1262 input power to the Station G3 PA; final output depends on PA PL1/PL2 level.
|
||||
-D MAX_LORA_TX_POWER=22
|
||||
; -D P_LORA_TX_LED=35
|
||||
-D PIN_BOARD_SDA=5
|
||||
|
||||
Reference in New Issue
Block a user