Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1117,28 +1117,44 @@ region save

---

#### View or change the channel used for bridging (ESPNow only)
#### View or change the channel used for bridging (ESP-NOW and nRF52 only)
**Usage:**
- `get bridge.channel`
- `set bridge.channel <channel>`

**Parameters:**
- `channel`: Channel number (1-14)
- `channel`:
- ESP-NOW: Wi-Fi channel number (1-14)
- nRF52: `1` = 2482 MHz (default), `2` = 2450 MHz, `3` = 2424 MHz. These sit in the gaps between Wi-Fi channels 1/6/11 and the BLE advertising channels

---

#### Set the ESP-Now secret
#### Set the ESP-NOW / nRF52 bridge secret
**Usage:**
- `get bridge.secret`
- `set bridge.secret <secret>`

**Parameters:**
- `secret`: ESP-NOW bridge secret, up to 15 characters
- `secret`: bridge secret, up to 15 characters

**Default:** Varies by board

---

#### View or change the transmit power of the bridge (nRF52 only)
**Usage:**
- `get bridge.txpower`
- `set bridge.txpower <dbm>`

**Parameters:**
- `dbm`: Transmit power in dBm (-20 to 8 on nRF52840). Values between the radio's supported steps (8, 7, 6, 5, 4, 3, 2, 0, -4, -8, -12, -16, -20) are rounded down, and the reply shows the power actually used.

**Default:** `4`

**Note:** The nRF52 bridge (`bridge.type` = `nrf52-wireless`) uses its own 2.4GHz protocol (1Mbit GFSK) that mimics ESP-NOW. nRF52 and ESP-NOW bridges can't talk to each other, because the nRF52 radio can't transmit or receive Wi-Fi frames. The nRF52 bridge needs a 2.4GHz (BLE) antenna attached and can't run while BLE is active. Starting OTA stops the bridge.

---

#### View the bootloader version (nRF52 only)
**Usage:** `get bootloader.ver`

Expand Down
5 changes: 4 additions & 1 deletion examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ uint8_t MyMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t sender
reply_data[8] |= 0x01; // is bridge, type UART
#elif WITH_ESPNOW_BRIDGE
reply_data[8] |= 0x03; // is bridge, type ESP-NOW
#elif WITH_NRF52_WIRELESS_BRIDGE
reply_data[8] |= 0x05; // is bridge, type nRF52 2.4GHz
#endif
if (_prefs.disable_fwd) { // is this repeater currently disabled
reply_data[8] |= 0x80; // is disabled
Expand Down Expand Up @@ -869,7 +871,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#if defined(WITH_RS232_BRIDGE)
, bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc)
#endif
#if defined(WITH_ESPNOW_BRIDGE)
#if defined(WITH_ESPNOW_BRIDGE) || defined(WITH_NRF52_WIRELESS_BRIDGE)
, bridge(&_prefs, _mgr, &rtc)
#endif
{
Expand Down Expand Up @@ -915,6 +917,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.bridge_pkt_src = 0; // logTx
_prefs.bridge_baud = 115200; // baud rate
_prefs.bridge_channel = 1; // channel 1
_prefs.bridge_tx_power = 4; // dBm (nRF52 2.4GHz bridge only)

StrHelper::strncpy(_prefs.bridge_secret, "LVSITANOS", sizeof(_prefs.bridge_secret));

Expand Down
7 changes: 7 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
#define WITH_BRIDGE
#endif

#ifdef WITH_NRF52_WIRELESS_BRIDGE
#include "helpers/bridges/NRF52RadioBridge.h"
#define WITH_BRIDGE
#endif

#include <helpers/AdvertDataHelpers.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/ClientACL.h>
Expand Down Expand Up @@ -114,6 +119,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
RS232Bridge bridge;
#elif defined(WITH_ESPNOW_BRIDGE)
ESPNowBridge bridge;
#elif defined(WITH_NRF52_WIRELESS_BRIDGE)
NRF52RadioBridge bridge;
#endif

void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
Expand Down
38 changes: 34 additions & 4 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include "AdvertDataHelpers.h"
#include "TxtDataHelpers.h"
#include <RTClib.h>
#ifdef WITH_NRF52_WIRELESS_BRIDGE
#include "bridges/NRF52RadioBridge.h"
#endif

#ifndef BRIDGE_MAX_BAUD
#define BRIDGE_MAX_BAUD 115200
Expand Down Expand Up @@ -218,6 +221,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
strcpy(reply, "ERR: clock cannot go backwards");
}
} else if (memcmp(command, "start ota", 9) == 0) {
#ifdef WITH_NRF52_WIRELESS_BRIDGE
_callbacks->setBridgeState(false); // release RADIO before SoftDevice takes it over
#endif
if (!_board->startOTAUpdate(_prefs->node_name, reply)) {
strcpy(reply, "Error");
}
Expand Down Expand Up @@ -613,22 +619,40 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "Error: baud rate must be between 9600-%d",BRIDGE_MAX_BAUD);
}
#endif
#ifdef WITH_ESPNOW_BRIDGE
#if defined(WITH_ESPNOW_BRIDGE) || defined(WITH_NRF52_WIRELESS_BRIDGE)
} else if (memcmp(config, "bridge.channel ", 15) == 0) {
int ch = atoi(&config[15]);
if (ch > 0 && ch < 15) {
#ifdef WITH_NRF52_WIRELESS_BRIDGE
const int max_ch = NRF52RadioBridge::NUM_CHANNELS;
#else
const int max_ch = 14;
#endif
if (ch > 0 && ch <= max_ch) {
_prefs->bridge_channel = (uint8_t)ch;
_callbacks->restartBridge();
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error: channel must be between 1-14");
sprintf(reply, "Error: channel must be between 1-%d", max_ch);
}
} else if (memcmp(config, "bridge.secret ", 14) == 0) {
StrHelper::strncpy(_prefs->bridge_secret, &config[14], sizeof(_prefs->bridge_secret));
_callbacks->restartBridge();
savePrefs();
strcpy(reply, "OK");
#endif
#ifdef WITH_NRF52_WIRELESS_BRIDGE
} else if (memcmp(config, "bridge.txpower ", 15) == 0) {
int dbm = atoi(&config[15]);
if (dbm >= NRF52RadioBridge::MIN_TX_POWER && dbm <= NRF52RadioBridge::MAX_TX_POWER) {
_prefs->bridge_tx_power = (int8_t)dbm;
_callbacks->restartBridge();
savePrefs();
sprintf(reply, "OK - %d dBm", NRF52RadioBridge::supportedTxPower(_prefs->bridge_tx_power));
} else {
sprintf(reply, "Error: tx power must be between %d and %d dBm", NRF52RadioBridge::MIN_TX_POWER,
NRF52RadioBridge::MAX_TX_POWER);
}
#endif
} else if (memcmp(config, "adc.multiplier ", 15) == 0) {
_prefs->adc_multiplier = atof(&config[15]);
Expand Down Expand Up @@ -731,6 +755,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
"rs232"
#elif WITH_ESPNOW_BRIDGE
"espnow"
#elif WITH_NRF52_WIRELESS_BRIDGE
"nrf52-wireless"
#else
"none"
#endif
Expand All @@ -747,11 +773,15 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else if (memcmp(config, "bridge.baud", 11) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_baud);
#endif
#ifdef WITH_ESPNOW_BRIDGE
#if defined(WITH_ESPNOW_BRIDGE) || defined(WITH_NRF52_WIRELESS_BRIDGE)
} else if (memcmp(config, "bridge.channel", 14) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_channel);
} else if (memcmp(config, "bridge.secret", 13) == 0) {
sprintf(reply, "> %s", _prefs->bridge_secret);
#endif
#ifdef WITH_NRF52_WIRELESS_BRIDGE
} else if (memcmp(config, "bridge.txpower", 14) == 0) {
sprintf(reply, "> %d", (int)NRF52RadioBridge::supportedTxPower(_prefs->bridge_tx_power));
#endif
} else if (memcmp(config, "bootloader.ver", 14) == 0) {
#ifdef NRF52_PLATFORM
Expand Down
4 changes: 3 additions & 1 deletion src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <helpers/CommonRadioPrefs.h>
#include <helpers/DynamicConfigSerializer.h>

#if defined(WITH_RS232_BRIDGE) || defined(WITH_ESPNOW_BRIDGE)
#if defined(WITH_RS232_BRIDGE) || defined(WITH_ESPNOW_BRIDGE) || defined(WITH_NRF52_WIRELESS_BRIDGE)
#define WITH_BRIDGE
#endif

Expand Down Expand Up @@ -56,6 +56,7 @@ class NodePrefs : public ConfigSerializer {
uint32_t bridge_baud = 0; // 9600, 19200, 38400, 57600, 115200 (default 115200)
uint8_t bridge_channel = 0; // 1-14 (ESP-NOW only)
char bridge_secret[16]; // for XOR encryption of bridge packets (ESP-NOW only)
int8_t bridge_tx_power = 0; // dBm (nRF52 2.4GHz bridge only)
// Power setting
uint8_t powersaving_enabled = 0; // boolean
// Gps settings
Expand Down Expand Up @@ -146,6 +147,7 @@ class NodePrefs : public ConfigSerializer {
def("baud", _parent->bridge_baud); // 9600, 19200, 38400, 57600, 115200 (default 115200)
def("ch", _parent->bridge_channel); // 1-14 (ESP-NOW only)
def("secret", _parent->bridge_secret, sizeof(_parent->bridge_secret)); // for XOR encryption of bridge packets (ESP-NOW only)
def("txpwr", _parent->bridge_tx_power); // dBm (nRF52 2.4GHz bridge only)
}
public:
BridgePrefs(NodePrefs* parent) : _parent(parent) { }
Expand Down
8 changes: 8 additions & 0 deletions src/helpers/bridges/BridgeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ bool BridgeBase::validateChecksum(const uint8_t *data, size_t len, uint16_t rece
return received_checksum == calculated_checksum;
}

void BridgeBase::xorCrypt(uint8_t *data, size_t len) {
size_t keyLen = strnlen(_prefs->bridge_secret, sizeof(_prefs->bridge_secret));
if (keyLen == 0) return;
for (size_t i = 0; i < len; i++) {
data[i] ^= _prefs->bridge_secret[i % keyLen];
}
}

void BridgeBase::handleReceivedPacket(mesh::Packet *packet) {
// Guard against uninitialized state
if (_initialized == false) {
Expand Down
13 changes: 13 additions & 0 deletions src/helpers/bridges/BridgeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ class BridgeBase : public AbstractBridge {
*/
bool validateChecksum(const uint8_t *data, size_t len, uint16_t received_checksum);

/**
* @brief Performs XOR encryption/decryption of data using _prefs->bridge_secret
*
* Used by radio bridges (ESP-NOW, nRF52 2.4GHz) to isolate different mesh networks.
* The same operation is used for both encryption and decryption.
* While not cryptographically secure, it provides basic network isolation.
* An empty secret leaves the data unchanged.
*
* @param data Pointer to data to encrypt/decrypt
* @param len Length of data in bytes
*/
void xorCrypt(uint8_t *data, size_t len);

/**
* @brief Common packet handling for received packets
*
Expand Down
7 changes: 0 additions & 7 deletions src/helpers/bridges/ESPNowBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,6 @@ void ESPNowBridge::loop() {
// Nothing to do here - ESP-NOW is callback based
}

void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) {
size_t keyLen = strlen(_prefs->bridge_secret);
for (size_t i = 0; i < len; i++) {
data[i] ^= _prefs->bridge_secret[i % keyLen];
}
}

void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len) {
// Ignore packets that are too small to contain header + checksum
if (len < (BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE)) {
Expand Down
13 changes: 0 additions & 13 deletions src/helpers/bridges/ESPNowBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,6 @@ class ESPNowBridge : public BridgeBase {
/** Current position in receive buffer */
size_t _rx_buffer_pos;

/**
* Performs XOR encryption/decryption of data
* Used to isolate different mesh networks
*
* Uses _prefs->bridge_secret as the key in a simple XOR operation.
* The same operation is used for both encryption and decryption.
* While not cryptographically secure, it provides basic network isolation.
*
* @param data Pointer to data to encrypt/decrypt
* @param len Length of data in bytes
*/
void xorCrypt(uint8_t *data, size_t len);

/**
* ESP-NOW receive callback
* Called by ESP-NOW when a packet is received
Expand Down
Loading
Loading