Compare commits

...
28 Commits
Author SHA1 Message Date
ripplebizandGitHub a4ab7f0e59 Merge pull request #3137 from agessaman/feat/station-g3-fem-prefs
Station G3: Expose, persist, and apply FEM gain preferences
2026-08-12 16:11:23 +10:00
ripplebizandGitHub 1cb308b694 Merge pull request #3182 from oltaco/t096-tx-workaround
Workaround for Heltec T096 transmit issue
2026-08-12 14:49:32 +10:00
taco 82de18fb0e T096 PA_CTX pin direction workaround 2026-08-12 14:07:31 +10:00
taco 9c60f9696e t096 compiler layout workaround
see https://github.com/meshcore-dev/MeshCore/issues/3151
2026-08-12 14:04:50 +10:00
ripplebizandGitHub e027263d1e Merge pull request #3154 from fdlamotte/nrf52_hwcrypto_init
nrf52: call nRFCrypto.begin()/end() only once
2026-08-12 12:14:21 +10:00
Huw DuddyandGitHub 34ad059afb Merge pull request #3170 from recrof/promicro-pin-tidy-up
[promicro] make pinmap more tidy, move the LoRa pins to variant.h
2026-08-11 18:43:20 +10:00
Rastislav Vysoky 5cce5cfe97 removed misleading 'GPIO' from pin numbers 2026-08-11 10:37:20 +02:00
ripplebizandGitHub fbef2ff0dc Merge pull request #3164 from weebl2000/fix-tbeam-s3-display
Fix display issues on T-beam Supreme S3
2026-08-11 14:39:39 +10:00
agessaman e2aa7b98f9 feat(companion_radio): add external FEM gain preferences for RX and TX for companions
Introduced consistent preferences for external LoRa FEM RX and TX gain settings in NodePrefs. Updated companion MyMesh to apply these settings during initialization and transmission. Added unit tests to verify the round-trip serialization of these new preferences.
2026-08-10 11:59:26 -07:00
Rastislav Vysoky a82dc1b9da make promicro pinmap more tidy, move the LoRa pins to variant.h 2026-08-10 20:13:59 +02:00
Liam CottleandGitHub 9d8894e166 Merge pull request #3166 from recrof/debug-noise-floor-flag
move RadioLibWrapper noise_floor level message under special debug flag
2026-08-11 03:07:31 +12:00
Rastislav Vysoky 3138ad99ba move RadioLibWrapper noise_floor level message under special debug flag 2026-08-10 16:56:56 +02:00
Wessel Nieboer 32bd6d48d6 Fix T-beam Supreme S3 display 2026-08-10 13:39:16 +02:00
Huw DuddyandGitHub f6c25e6a25 Merge pull request #3158 from mepholic/bug/fix-rxgain-setting-post-agc-reset
Fix RX Boosted Gain setting after AGC reset is triggered
2026-08-10 17:31:38 +10:00
ripplebizandGitHub 9941669fd5 Merge pull request #3142 from oltaco/buildfix-userbtn
Build fix for R1 Neo and Minewsemi repeater
2026-08-10 13:30:24 +10:00
ripplebizandGitHub 0d60de6fa6 Merge pull request #3146 from oltaco/lr2021-preamble-and-irq-timeout
LR2021: add PREAMBLE_DETECTED bit and IRQ timeout logic
2026-08-10 13:29:12 +10:00
Dan Theisen 6f491f3033 RX Boosted Gain was set back to the compiled in value rather than the current/flash value set by the user when the ACG reset is triggered. This commit fixes this. 2026-08-09 18:11:35 -07:00
Florent ecb8c94560 nrf52: call nRFCrypto.begin()/end() only once 2026-08-09 14:46:23 -04:00
Huw DuddyandGitHub a1c50e15dd Merge pull request #3148 from recrof/add-missing-kiss-radio
add missing kiss radio roles, add kiss radio build to build.sh
2026-08-09 22:22:08 +10:00
Rastislav Vysoky c2d57f08c8 add missing kiss radio roles, add kiss radio build to build.sh 2026-08-09 14:17:18 +02:00
Huw DuddyandGitHub f310afb5e5 Merge pull request #3140 from entr0p1/fix/techo-lite-pins
Lilygo T-Echo Lite Pin Fixes
2026-08-09 22:06:21 +10:00
taco ce62c8b5d7 LR2021: add PREAMBLE_DETECTED bit and IRQ timeout logic 2026-08-09 21:41:22 +10:00
taco 39934d6999 minewsemi: build fix and platformio.ini tidy up
fixes minewsemi repeater failing build due to user_btn missing.
2026-08-09 18:33:27 +10:00
taco e8bbcd4580 R1 neo: remove stray lib_deps entry 2026-08-09 16:51:45 +10:00
taco b75a548d7f build fix for R1 Neo repeater
added missing declarations for user_btn and NullDisplayDriver
2026-08-09 16:50:44 +10:00
entr0p1 5732b2edd8 Lilygo T-Echo Lite Pin Fixes
- Aligned to Lilygo's updated schematic and pin map as per https://github.com/Xinyuan-LilyGO/T-Echo-Lite/issues/14
2026-08-09 14:09:35 +10:00
Adam Gessaman c58c9b2f31 fix(station-g3): apply FEM PA level at TX start
PA PL1 re-targets the PA's DC-DC supply rail rather than selecting a
logic-level gain, and the serial CLI is serviced on every main-loop pass
regardless of whether a transmit is in flight. A `set radio.fem.txgain`
write could therefore move the rail mid-transmit, while the SX1262 was
still driving the PA at full input power.

Record the requested level in setPAGainEnable() and drive the pin from
setTxModeEnable(), which runs from onBeforeTransmit() ahead of
startTransmit(). The level only matters while transmitting, so deferring
costs nothing.

Document that the pref is saved immediately but applied at the next
transmit, so `get radio.fem.txgain` can lead the hardware until then.
2026-08-08 14:53:57 -07:00
agessaman 23066573e5 fix(station-g3): expose FEM gain preferences 2026-08-06 15:29:14 -07:00
48 changed files with 563 additions and 150 deletions
+16
View File
@@ -34,6 +34,9 @@ $ sh build.sh build-repeater-firmwares
Build all chat room server firmwares
$ sh build.sh build-room-server-firmwares
Build all kiss radio firmwares
$ sh build.sh build-kiss-radio-firmwares
Environment Variables:
DISABLE_DEBUG=1: Disables all debug logging flags (MESH_DEBUG, MESH_PACKET_LOGGING, etc.)
If not set, debug flags from variant platformio.ini files are used.
@@ -242,6 +245,17 @@ build_room_server_firmwares() {
}
build_kiss_modem_firmwares() {
# # build specific kiss radio firmwares
# build_firmware "Heltec_v3_kiss_modem"
# build_firmware "RAK_4631_kiss_modem"
# build all room server firmwares
build_all_firmwares_by_suffix "_kiss_modem"
}
build_firmwares() {
build_companion_firmwares
build_repeater_firmwares
@@ -278,6 +292,8 @@ elif [[ $1 == "build-repeater-firmwares" ]]; then
build_repeater_firmwares
elif [[ $1 == "build-room-server-firmwares" ]]; then
build_room_server_firmwares
elif [[ $1 == "build-kiss-radio-firmwares" ]]; then
build_kiss_modem_firmwares
elif [[ $1 == "get-companion-firmwares-to-build" ]]; then
get_pio_envs_ending_with_string "_companion_radio_usb"
get_pio_envs_ending_with_string "_companion_radio_ble"
+16
View File
@@ -291,6 +291,22 @@ This document provides an overview of CLI commands that can be sent to MeshCore
---
#### 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
+4
View File
@@ -885,6 +885,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.gps_enabled = 0; // GPS disabled by default
_prefs.gps_interval = 0; // No automatic GPS updates by default
_prefs.radio_fem_rxgain = 1;
_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)
@@ -974,6 +976,8 @@ void MyMesh::begin(bool has_display) {
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_driver.setTxPower(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
}
+5 -2
View File
@@ -33,6 +33,8 @@ public:
uint32_t gps_interval = 0; // GPS read interval in seconds
uint8_t autoadd_config = 0; // bitmask for auto-add contacts config
uint8_t rx_boosted_gain = 0; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
uint8_t radio_fem_rxgain = 0; // external LoRa FEM RX gain (LNA)
uint8_t radio_fem_txgain = 0; // external LoRa FEM TX gain (low by default)
uint8_t _client_repeat = 0; // DEPRECATED -> use repeat.disable_fwd
uint8_t path_hash_mode = 0; // which path mode to use when sending
uint8_t autoadd_max_hops = 0; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
@@ -51,7 +53,8 @@ private:
//def("cad", _parent->cad_enabled);
//def("int_thr", _parent->interference_threshold);
def("rxgain", _parent->rx_boosted_gain);
def("fem_rxgain", _parent->rx_boosted_gain);
def("fem_rxgain", _parent->radio_fem_rxgain);
def("fem_txgain", _parent->radio_fem_txgain);
def("tx", _parent->tx_power_dbm);
def("af", _parent->airtime_factor);
def("rxdelay", _parent->rx_delay_base);
@@ -135,4 +138,4 @@ public:
// new accessor methods
bool isRepeatEn() const { return repeat.disable_fwd == 0; }
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
};
};
+2
View File
@@ -913,6 +913,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#endif
#endif
_prefs.radio_fem_rxgain = 1;
_prefs.radio_fem_txgain = 0;
pending_discover_tag = 0;
pending_discover_until = 0;
@@ -962,6 +963,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
updateAdvertTimer();
updateFloodAdvertTimer();
+2
View File
@@ -683,6 +683,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#endif
#endif
_prefs.radio_fem_rxgain = 1;
_prefs.radio_fem_txgain = 0;
next_post_idx = 0;
next_client_idx = 0;
@@ -726,6 +727,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
radio_driver.setTxPower(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
updateAdvertTimer();
updateFloodAdvertTimer();
+2
View File
@@ -735,6 +735,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));
}
@@ -771,6 +772,7 @@ void SensorMesh::begin(FILESYSTEM* fs) {
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_driver.setTxPower(_prefs.tx_power_dbm);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
updateAdvertTimer();
updateFloodAdvertTimer();
-2
View File
@@ -27,11 +27,9 @@ bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) c
// 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
// NOTE: memory corruption bug was found in this function!!
+4
View File
@@ -67,6 +67,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; }
// Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; }
-12
View File
@@ -24,9 +24,7 @@ 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;
@@ -39,12 +37,10 @@ void Utils::sha256(uint8_t *hash, size_t hash_len, const uint8_t* frag1, int fra
#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;
@@ -62,7 +58,6 @@ int Utils::decrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* s
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) {
@@ -71,7 +66,6 @@ int Utils::decrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* s
}
SaSi_AesFinish(&ctx, 0, NULL, 0, NULL, &dummy_out);
SaSi_AesFree(&ctx);
nRFCrypto.end();
return sp - src;
#else
AES128 aes;
@@ -95,7 +89,6 @@ int Utils::encrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* s
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) {
@@ -110,7 +103,6 @@ int Utils::encrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* s
}
SaSi_AesFinish(&ctx, 0, NULL, 0, NULL, &dummy_out);
SaSi_AesFree(&ctx);
nRFCrypto.end();
return dp - dest;
#else
AES128 aes;
@@ -138,11 +130,9 @@ int Utils::encryptThenMAC(const uint8_t* shared_secret, uint8_t* dest, const uin
#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;
@@ -162,11 +152,9 @@ int Utils::MACThenDecrypt(const uint8_t* shared_secret, uint8_t* dest, const uin
{
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
+29
View File
@@ -133,6 +133,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // Legacy
// 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
_prefs->radio_fem_txgain = constrain(_prefs->radio_fem_txgain, 0, 1); // boolean
_prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean
file.close();
@@ -562,6 +563,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 ", 6) == 0) {
strcpy(tmp, &config[6]);
const char *parts[4];
@@ -827,6 +850,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, "radio", 5) == 0) {
char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(_prefs->freq));
+5 -2
View File
@@ -65,6 +65,7 @@ public:
char owner_info[120];
uint8_t rx_boosted_gain = 0; // power settings
uint8_t radio_fem_rxgain = 0; // LoRa FEM RX gain setting
uint8_t radio_fem_txgain = 0; // LoRa FEM TX gain setting
uint8_t path_hash_mode = 0; // which path mode to use when sending
uint8_t loop_detect = 0;
uint8_t cad_enabled = 0; // hardware Channel Activity Detection before TX (boolean)
@@ -79,10 +80,12 @@ private:
def("bw", _parent->bw);
def("sf", _parent->sf);
def("cr", _parent->cr);
def("cad", _parent->cad_enabled);
static const char radio_cad_key[] = {'c', 'a', 'd', '\0'}; // workaround for T096, don't touch without testing T096 repeater still transmit
def(radio_cad_key, _parent->cad_enabled);
def("int_thr", _parent->interference_threshold);
def("rxgain", _parent->rx_boosted_gain);
def("fem_rxgain", _parent->rx_boosted_gain);
def("fem_rxgain", _parent->radio_fem_rxgain);
def("fem_txgain", _parent->radio_fem_txgain);
def("tx", _parent->tx_power_dbm);
def("af", _parent->airtime_factor);
def("rxdelay", _parent->rx_delay_base);
+13
View File
@@ -5,6 +5,10 @@
#include <bluefruit.h>
#include <nrf_soc.h>
#ifdef USE_CC310_HW_CRYPTO
#include <Adafruit_nRFCrypto.h>
#endif
static BLEDfu bledfu;
static void connect_callback(uint16_t conn_handle) {
@@ -21,6 +25,11 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
void NRF52Board::begin() {
startup_reason = BD_STARTUP_NORMAL;
#ifdef USE_CC310_HW_CRYPTO
// CC310 TRNG is higher quality and environment-independent vs radio RSSI noise.
nRFCrypto.begin();
#endif
}
#ifdef NRF52_POWER_MANAGEMENT
@@ -352,6 +361,10 @@ void NRF52Board::shutdownPeripherals() {
sensors.getLocationProvider()->stop();
}
#ifdef USE_CC310_HW_CRYPTO
nRFCrypto.end();
#endif
// Flush serial buffers
Serial.flush();
delay(100);
+2 -1
View File
@@ -19,7 +19,6 @@ public:
((CustomLR1110 *)_radio)->setMaxPayloadMillis(pm.payloadMillis);
}
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); }
bool isReceivingPacket() override {
return ((CustomLR1110 *)_radio)->isReceiving();
}
@@ -50,4 +49,6 @@ public:
bool getRxBoostedGainMode() const override {
return ((CustomLR1110 *)_radio)->getRxBoostedGainMode();
}
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz(), getRxBoostedGainMode()); }
};
+57 -2
View File
@@ -4,6 +4,10 @@
#include "MeshCore.h"
class CustomLR2021 : public LR2021 {
uint32_t _preambleMillis = 66;
uint32_t _maxPayloadMillis = 3934;
uint32_t _activityAt = 0;
bool _headerSeen = false;
bool _rx_boosted = false;
public:
@@ -66,11 +70,62 @@ class CustomLR2021 : public LR2021 {
bool getRxBoostedGainMode() const { return _rx_boosted; }
int16_t startReceive() override {
// include the PREAMBLE_DETECTED irq bit in reported flags
return LR2021::startReceive(RADIOLIB_LR2021_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | (1UL << RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED), RADIOLIB_IRQ_RX_DEFAULT_MASK, 0);
}
bool isReceiving() {
uint32_t irq = getIrqStatus();
bool detected = ((irq & RADIOLIB_LR2021_IRQ_SYNCWORD_VALID) || (irq & RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED));
return detected;
bool preamble = irq & RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED; // bit 5
bool header = irq & RADIOLIB_LR2021_IRQ_LORA_HEADER_VALID; // bit 6
bool hdrErr = irq & RADIOLIB_LR2021_IRQ_LORA_HDR_CRC_ERROR; // bit 9
uint32_t now = millis();
if (hdrErr) {
clearIrqFlags(RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED | RADIOLIB_LR2021_IRQ_LORA_HEADER_VALID | RADIOLIB_LR2021_IRQ_LORA_HDR_CRC_ERROR);
_activityAt = 0;
_headerSeen = false;
return false;
}
if (!header && _headerSeen) {
// something cleared the header flag, reset our state.
_activityAt = 0; _headerSeen = false;
return false;
}
if (header) {
if (!_headerSeen) { _headerSeen = true; _activityAt = now; };
if (now - _activityAt > _maxPayloadMillis) {
MESH_DEBUG_PRINTLN("Clearing header IRQ after %ums", _maxPayloadMillis);
clearIrqFlags(RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED | RADIOLIB_LR2021_IRQ_LORA_HEADER_VALID | RADIOLIB_LR2021_IRQ_LORA_HDR_CRC_ERROR);
_activityAt = 0; _headerSeen = false;
return false;
}
return true;
}
if (preamble) {
if (_activityAt == 0) _activityAt = now;
if (now - _activityAt > _preambleMillis) {
clearIrqFlags(RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED);
_activityAt = 0;
MESH_DEBUG_PRINTLN("Clearing preamble IRQ after %ums", _preambleMillis);
return false;
}
return true;
}
_activityAt = 0; _headerSeen = false;
return false;
}
void setPreambleMillis(uint32_t preambleMillis) {
_preambleMillis = preambleMillis;
MESH_DEBUG_PRINTLN("Set _preambleMillis=%u", _preambleMillis);
}
void setMaxPayloadMillis(uint32_t payloadMillis) {
_maxPayloadMillis = payloadMillis;
MESH_DEBUG_PRINTLN("Set _maxPayloadMillis=%u", _maxPayloadMillis);
}
uint8_t getSpreadingFactor() const { return spreadingFactor; }
};
@@ -22,6 +22,10 @@ public:
((CustomLR2021 *)_radio)->setCodingRate(cr);
updatePreamble(sf);
applySideDetectorConfig();
PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf));
((CustomLR2021 *)_radio)->setPreambleMillis(pm.preambleMillis);
((CustomLR2021 *)_radio)->setMaxPayloadMillis(pm.payloadMillis);
}
bool configSideDetectors(const uint8_t* sideDetSFs, uint8_t num, float bw) override {
+1 -1
View File
@@ -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()); }
};
+2 -2
View File
@@ -41,12 +41,12 @@ public:
((CustomSX1262 *)_radio)->sleep(false);
}
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
bool setRxBoostedGainMode(bool en) override {
return ((CustomSX1262 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
}
bool getRxBoostedGainMode() const override {
return ((CustomSX1262 *)_radio)->getRxBoostedGainMode();
}
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
};
+2 -2
View File
@@ -38,12 +38,12 @@ public:
}
uint8_t getSpreadingFactor() const override { return ((CustomSX1268 *)_radio)->spreadingFactor; }
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
bool setRxBoostedGainMode(bool en) override {
return ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
}
bool getRxBoostedGainMode() const override {
return ((CustomSX1268 *)_radio)->getRxBoostedGainMode();
}
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
};
+2 -2
View File
@@ -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,6 +16,6 @@ inline void lr11x0ResetAGC(LR11x0* radio, float freqMHz) {
radio->calibrateImageRejection(freqMHz - 4.0f, freqMHz + 4.0f);
#ifdef RX_BOOSTED_GAIN
radio->setRxBoostedGainMode(RX_BOOSTED_GAIN);
radio->setRxBoostedGainMode(rx_boost_gain);
#endif
}
+6 -4
View File
@@ -15,7 +15,7 @@ static volatile uint8_t state = STATE_IDLE;
// this function is called when a complete packet
// is transmitted by the module
static
static
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
@@ -100,7 +100,9 @@ void RadioLibWrapper::loop() {
}
_floor_sample_sum = 0;
#ifdef MESH_DEBUG_NOISE_FLOOR
MESH_DEBUG_PRINTLN("RadioLibWrapper: noise_floor = %d", (int)_noise_floor);
#endif
}
}
@@ -224,10 +226,10 @@ static float snr_threshold[] = {
-17.5,// SF11 needs at least -17.5 dB SNR
-20 // SF12 needs at least -20 dB SNR
};
float RadioLibWrapper::packetScoreInt(float snr, int sf, int packet_len) {
if (sf < 7) return 0.0f;
if (snr < snr_threshold[sf - 7]) return 0.0f; // Below threshold, no chance of success
auto success_rate_based_on_snr = (snr - snr_threshold[sf - 7]) / 10.0;
@@ -243,7 +245,7 @@ PacketMillis RadioLibWrapper::calcMaxPacketMillis(uint8_t sf, float bw, uint8_t
// preamble + syncword + sfd + header
uint32_t preamble_us = (((preambleSymbols + 8) * 4 + sfCoeff1_x4) * tsym_us) / 4;
// airtime for max packet at current radio settings
uint32_t total_us = _radio->getTimeOnAir(MAX_TRANS_UNIT);
// airtime for payload only (no preamble, header or SOF)
-2
View File
@@ -93,9 +93,7 @@ public:
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++) {
dest[i] = _radio->randomByte() ^ (::random(0, 256) & 0xFF);
+2 -2
View File
@@ -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,7 +26,7 @@ inline void sx126xResetAGC(SX126x* radio) {
radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio->setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
radio->setRxBoostedGainMode(rx_boost_gain);
#endif
#ifdef SX126X_REGISTER_PATCH
uint8_t r_data = 0;
+15 -2
View File
@@ -24,8 +24,21 @@ bool SH1106Display::begin()
{
// Wire must already be initialised by board.begin() before this is called.
// Boards with non-standard SH1106 addresses should define DISPLAY_ADDRESS
// in their variant/platformio configuration.
return i2c_probe(Wire, DISPLAY_ADDRESS) && display.begin(DISPLAY_ADDRESS, true);
// in their variant/platformio configuration. The SA0 strap selects 0x3C or
// 0x3D and differs between revisions of the same board (e.g. T-Beam
// Supreme), so fall back to the other address of the pair.
uint8_t addr = 0;
if (i2c_probe(Wire, DISPLAY_ADDRESS)) {
addr = DISPLAY_ADDRESS;
} else if (i2c_probe(Wire, DISPLAY_ADDRESS ^ 1)) {
addr = DISPLAY_ADDRESS ^ 1;
}
// Run the Adafruit init even when no panel answered: it is what allocates
// the frame buffer and the I2C device. Skipping it leaves i2c_dev and
// spi_dev NULL, and UITask::begin() calls turnOn() regardless of our
// return value, which then dereferences the null spi_dev.
bool ok = display.begin(addr ? addr : DISPLAY_ADDRESS, true);
return addr != 0 && ok;
}
void SH1106Display::turnOn()
@@ -0,0 +1,80 @@
#include <gtest/gtest.h>
#include <cstdio>
#include <cstring>
#include <string>
#include "../../examples/companion_radio/NodePrefs.h"
class ReplayStream : public Stream {
const char* _text;
int _pos = 0;
int _len;
public:
explicit ReplayStream(const char* text) : _text(text), _len(strlen(text)) { }
int available() override { return _len - _pos; }
int read() override { return _pos < _len ? _text[_pos++] : -1; }
int peek() override { return _pos < _len ? _text[_pos] : -1; }
};
class CaptureStream : public Stream {
std::string _text;
size_t emit(long long value) {
char text[24];
int length = snprintf(text, sizeof(text), "%lld", value);
return write(reinterpret_cast<const uint8_t*>(text), length);
}
public:
size_t write(uint8_t value) override {
_text.push_back(static_cast<char>(value));
return 1;
}
size_t write(const uint8_t* buffer, size_t size) override {
_text.append(reinterpret_cast<const char*>(buffer), size);
return size;
}
size_t print(unsigned char value, int = DEC) override { return emit(value); }
size_t print(int value, int = DEC) override { return emit(value); }
size_t print(unsigned int value, int = DEC) override { return emit(value); }
size_t print(long value, int = DEC) override { return emit(value); }
size_t print(unsigned long value, int = DEC) override { return emit(value); }
size_t print(long long value, int = DEC) override { return emit(value); }
size_t print(unsigned long long value, int = DEC) override { return emit(value); }
const std::string& text() const { return _text; }
};
TEST(CompanionNodePrefs, RxGainSettingsRoundTripIndependently) {
NodePrefs saved;
saved.rx_boosted_gain = 0;
saved.radio_fem_rxgain = 1;
saved.radio_fem_txgain = 0;
CaptureStream output;
ASSERT_TRUE(saved.saveSerial(output));
EXPECT_NE(std::string::npos, output.text().find("rxgain:0"));
EXPECT_NE(std::string::npos, output.text().find("fem_rxgain:1"));
EXPECT_NE(std::string::npos, output.text().find("fem_txgain:0"));
ReplayStream input("{radio:{rxgain:1,fem_rxgain:0,fem_txgain:1}}");
NodePrefs loaded;
loaded.rx_boosted_gain = 0;
loaded.radio_fem_rxgain = 1;
loaded.radio_fem_txgain = 0;
ASSERT_TRUE(loaded.loadSerial(input));
EXPECT_EQ(1, loaded.rx_boosted_gain);
EXPECT_EQ(0, loaded.radio_fem_rxgain);
EXPECT_EQ(1, loaded.radio_fem_txgain);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -1,6 +1,14 @@
#include <gtest/gtest.h>
#include "helpers/ConfigSerializer.h"
class NativeFileSystem {
public:
void mkdir(const char*) { }
};
#define FILESYSTEM NativeFileSystem
#include "helpers/CommonCLI.h"
#undef FILESYSTEM
#define TEST_INT_S "56"
#define TEST_INT 56
#define TEST_FLOAT_S "-6.123"
@@ -21,6 +29,19 @@ public:
class MockPrintStream : public Stream {
int len = 0;
uint8_t _buf[1024];
size_t printSigned(long long value) {
char text[24];
snprintf(text, sizeof(text), "%lld", value);
return Print::print(text);
}
size_t printUnsigned(unsigned long long value) {
char text[24];
snprintf(text, sizeof(text), "%llu", value);
return Print::print(text);
}
public:
size_t write(uint8_t b) override {
if (len < sizeof(_buf)) {
@@ -30,17 +51,17 @@ public:
return 0;
}
size_t print(unsigned char b, int r) override { if (b == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(unsigned int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(unsigned long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(unsigned long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
size_t print(double v, int p = 2) override {
if (p == 6) return Print::print(TEST_DOUBLE_S);
if (p == 4) return Print::print(TEST_FLOAT_S);
return 0;
size_t print(unsigned char v, int r) override { return printUnsigned(v); }
size_t print(int v, int r) override { return printSigned(v); }
size_t print(unsigned int v, int r) override { return printUnsigned(v); }
size_t print(long v, int r) override { return printSigned(v); }
size_t print(unsigned long v, int r) override { return printUnsigned(v); }
size_t print(long long v, int r) override { return printSigned(v); }
size_t print(unsigned long long v, int r) override { return printUnsigned(v); }
size_t print(double v, int p = 2) override {
char text[32];
snprintf(text, sizeof(text), "%.*f", p, v);
return Print::print(text);
}
int getLength() const { return len; }
@@ -171,6 +192,28 @@ TEST(ConfigSerializer, LoadSerial_IgnoreUnknowns) {
EXPECT_TRUE(match);
}
TEST(NodePrefs, FemGainSettingsRoundTrip) {
NodePrefs saved;
saved.radio_fem_rxgain = 0;
saved.radio_fem_txgain = 1;
MockPrintStream output;
ASSERT_TRUE(saved.saveSerial(output));
std::string serialised(reinterpret_cast<const char*>(output.getBytes()), output.getLength());
EXPECT_NE(std::string::npos, serialised.find("fem_rxgain:0"));
EXPECT_NE(std::string::npos, serialised.find("fem_txgain:1"));
MockInputStream input(serialised.c_str());
NodePrefs loaded;
loaded.radio_fem_rxgain = 1;
loaded.radio_fem_txgain = 0;
ASSERT_TRUE(loaded.loadSerial(input));
EXPECT_EQ(0, loaded.radio_fem_rxgain);
EXPECT_EQ(1, loaded.radio_fem_txgain);
}
// ── main ───────────────────────────────────────────────────────
+1
View File
@@ -21,6 +21,7 @@ void LoRaFEMControl::setSleepModeEnable(void)
void LoRaFEMControl::setTxModeEnable(void)
{
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT); // force pinMode before transmit as temporary workaround for https://github.com/meshcore-dev/MeshCore/issues/3151
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
}
@@ -119,3 +119,8 @@ lib_deps =
${LilyGo_T-Echo_Card.lib_deps}
end2endzone/NonBlockingRTTTL@^1.3.0
densaugeo/base64 @ ~1.4.0
[env:LilyGo_T-Echo_Card_kiss_modem]
extends = LilyGo_T-Echo_Card
build_src_filter = ${LilyGo_T-Echo_Card.build_src_filter}
+<../examples/kiss_modem/>
+3 -3
View File
@@ -12,10 +12,9 @@ build_flags = ${nrf52_base.build_flags}
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_POWER_EN=30
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_DIO3_TCXO_VOLTAGE=3.0
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D SX126X_USE_REGULATOR_LDO=1
-D P_LORA_TX_LED=LED_GREEN
-D DISABLE_DIAGNOSTIC_OUTPUT
-D ENV_INCLUDE_GPS=1
@@ -117,7 +116,7 @@ build_flags =
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_POWER_EN=30
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_DIO3_TCXO_VOLTAGE=3.0
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D P_LORA_TX_LED=LED_GREEN
@@ -162,6 +161,7 @@ build_flags =
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_POWER_EN=30
-D SX126X_DIO3_TCXO_VOLTAGE=3.0
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D P_LORA_TX_LED=LED_GREEN
+2
View File
@@ -105,12 +105,14 @@
#define LORA_CS _PINNUM(0, 11)
#define SX126X_POWER_EN _PINNUM(0, 30)
#define SX126X_DIO1 _PINNUM(1, 8)
#define SX126X_DIO2 _PINNUM(0, 5)
#define SX126X_BUSY _PINNUM(0, 14)
#define SX126X_RESET _PINNUM(0, 7)
#define SX126X_RXEN _PINNUM(1, 1)
#define SX126X_TXEN _PINNUM(0, 27)
#define P_LORA_DIO_1 SX126X_DIO1
#define P_LORA_DIO_2 SX126X_DIO2
#define P_LORA_NSS LORA_CS
#define P_LORA_RESET SX126X_RESET
#define P_LORA_BUSY SX126X_BUSY
@@ -98,3 +98,8 @@ build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
lib_deps =
${LilyGo_TETH_Elite_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TETH_Elite_sx1262_kiss_modem]
extends = LilyGo_TETH_Elite_sx1262
build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
+<../examples/kiss_modem/>
+5
View File
@@ -130,3 +130,8 @@ lib_deps = ${MeshTracker_X1.lib_deps}
stevemarple/MicroNMEA @ ^2.0.6
end2endzone/NonBlockingRTTTL@^1.3.0
adafruit/Adafruit DRV2605 Library @ ^1.2.4
[env:MeshTracker_X1_kiss_modem]
extends = MeshTracker_X1
build_src_filter = ${MeshTracker_X1.build_src_filter}
+<../examples/kiss_modem/>
+1 -17
View File
@@ -21,6 +21,7 @@ build_flags = ${nrf52_base.build_flags}
-D ENV_INCLUDE_INA219=1
build_src_filter = ${nrf52_base.build_src_filter}
+<helpers/*.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
+<../variants/minewsemi_me25ls01>
+<helpers/sensors>
debug_tool = jlink
@@ -55,7 +56,6 @@ build_flags = ${me25ls01.build_flags}
;-D PIN_BUZZER=25
;-D PIN_BUZZER_EN=37
build_src_filter = ${me25ls01.build_src_filter}
+<helpers/ui/NullDisplayDriver.cpp>
+<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-orig/*.cpp>
@@ -63,10 +63,6 @@ build_src_filter = ${me25ls01.build_src_filter}
[env:Minewsemi_me25ls01_repeater]
extends = me25ls01
build_flags = ${me25ls01.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256
@@ -85,10 +81,6 @@ build_src_filter = ${me25ls01.build_src_filter}
[env:Minewsemi_me25ls01_room_server]
extends = me25ls01
build_flags = ${me25ls01.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256
@@ -103,15 +95,10 @@ build_flags = ${me25ls01.build_flags}
-D DISPLAY_CLASS=NullDisplayDriver
build_src_filter = ${me25ls01.build_src_filter}
+<../examples/simple_room_server>
+<helpers/ui/NullDisplayDriver.cpp>
[env:Minewsemi_me25ls01_terminal_chat]
extends = me25ls01
build_flags = ${me25ls01.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256
@@ -126,7 +113,6 @@ build_flags = ${me25ls01.build_flags}
-D DISPLAY_CLASS=NullDisplayDriver
build_src_filter = ${me25ls01.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
[env:Minewsemi_me25ls01_companion_radio_usb]
extends = me25ls01
@@ -137,7 +123,6 @@ build_flags = ${me25ls01.build_flags}
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
;-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256
@@ -147,7 +132,6 @@ build_flags = ${me25ls01.build_flags}
-D ENABLE_USB_INTERFACE
build_src_filter = ${me25ls01.build_src_filter}
+<helpers/nrf52/*.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-orig/*.cpp>
+2 -2
View File
@@ -4,7 +4,6 @@
MinewsemiME25LS01Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock rtc_clock;
@@ -18,7 +17,8 @@ extern EnvironmentSensorManager sensors;
#endif
#ifdef DISPLAY_CLASS
NullDisplayDriver display;
DISPLAY_CLASS display;
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
#endif
#ifndef LORA_CR
+5 -4
View File
@@ -11,16 +11,17 @@
#include <helpers/sensors/EnvironmentSensorManager.h>
#ifdef DISPLAY_CLASS
#include <helpers/ui/NullDisplayDriver.h>
#endif
#ifdef DISPLAY_CLASS
extern NullDisplayDriver display;
#include <helpers/ui/MomentaryButton.h>
#endif
extern MinewsemiME25LS01Board board;
extern WRAPPER_CLASS radio_driver;
extern VolatileRTCClock rtc_clock;
extern EnvironmentSensorManager sensors;
#ifdef DISPLAY_CLASS
extern DISPLAY_CLASS display;
extern MomentaryButton user_btn;
#endif
bool radio_init();
mesh::LocalIdentity radio_new_identity();
+1 -1
View File
@@ -10,6 +10,7 @@ build_flags = ${nrf52_base.build_flags}
-D NRF52_POWER_MANAGEMENT
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D DISPLAY_CLASS=NullDisplayDriver
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
@@ -100,7 +101,6 @@ build_src_filter = ${R1Neo.build_src_filter}
+<../examples/companion_radio/ui-orig/*.cpp>
lib_deps =
${R1Neo.lib_deps}
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0
end2endzone/NonBlockingRTTTL@^1.3.0
+8 -3
View File
@@ -4,12 +4,17 @@
R1NeoBoard board;
DISPLAY_CLASS display;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
#ifdef DISPLAY_CLASS
NullDisplayDriver display;
#endif
#ifdef PIN_USER_BTN
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
#endif
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
-13
View File
@@ -4,19 +4,6 @@
#include <Arduino.h>
#include <helpers/NRF52Board.h>
#define P_LORA_NSS 13 //P1.13 45
#define P_LORA_DIO_1 11 //P0.10 10
#define P_LORA_RESET 10 //P0.09 9
#define P_LORA_BUSY 16 //P0.29 29
#define P_LORA_MISO 15 //P0.02 2
#define P_LORA_SCLK 12 //P1.11 43
#define P_LORA_MOSI 14 //P1.15 47
#define SX126X_POWER_EN 21 //P0.13 13
#define SX126X_RXEN 2 //P0.17
#define SX126X_TXEN RADIOLIB_NC
#define SX126X_DIO2_AS_RF_SWITCH true
#define SX126X_DIO3_TCXO_VOLTAGE (1.8f)
#define PIN_VBAT_READ 17
#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking
+24 -8
View File
@@ -3,13 +3,29 @@
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
8, 6, 17, 20, 22, 24, 32, 11, 36, 38,
9, 10, 43, 45, 47, 2, 29, 31,
33, 34, 37,
13, 15
8, // P0.08 = 0
6, // P0.06 = 1
17, // P0.17 = 2
20, // P0.20 = 3
22, // P0.22 = 4
24, // P0.24 = 5
32, // P1.00 = 6
11, // P0.11 = 7
36, // P1.04 = 8
38, // P1.06 = 9
9, // P0.09 = 10
10, // P0.10 = 11
43, // P1.11 = 12
45, // P1.13 = 13
47, // P1.15 = 14
2, // P0.02 = 15
29, // P0.29 = 16
31, // P0.31 = 17
33, // P1.01 = 18
34, // P1.02 = 19
37, // P1.05 = 20
13, // P0.13 = 21
15 // P0.15 = 22
};
void initVariant()
{
}
void initVariant() {}
+16 -2
View File
@@ -7,9 +7,9 @@
#pragma once
#include "WVariant.h"
////////////////////////////////////////////////////////////////////////////////
// Low frequency clock source
// Low frequency clock source
#define VARIANT_MCK (64000000ul)
@@ -79,4 +79,18 @@
#define PIN_BUTTON1 (6)
#define BUTTON_PIN PIN_BUTTON1
//////////////////////////////////////////////////////////////////////////////
// LoRa
#define P_LORA_NSS (13)
#define P_LORA_DIO_1 (11)
#define P_LORA_RESET (10)
#define P_LORA_BUSY (16)
#define P_LORA_MISO (15)
#define P_LORA_SCLK (12)
#define P_LORA_MOSI (14)
#define SX126X_POWER_EN (21)
#define SX126X_RXEN (2)
#define SX126X_TXEN (-1)
#define SX126X_DIO2_AS_RF_SWITCH true
#define SX126X_DIO3_TCXO_VOLTAGE (1.8f)
@@ -33,7 +33,7 @@ lib_deps=${esp32_base.lib_deps}
lovyan03/LovyanGFX @ ^1.2.7
[env:SenseCapIndicator-ESPNow_comp_radio_usb]
extends =SenseCapIndicator-ESPNow
extends = SenseCapIndicator-ESPNow
build_flags =
${SenseCapIndicator-ESPNow.build_flags}
-I examples/companion_radio/ui-new
@@ -47,4 +47,4 @@ build_src_filter = ${SenseCapIndicator-ESPNow.build_src_filter}
+<../examples/companion_radio/*.cpp>
lib_deps =
${SenseCapIndicator-ESPNow.lib_deps}
densaugeo/base64 @ ~1.4.0
densaugeo/base64 @ ~1.4.0
+5
View File
@@ -101,3 +101,8 @@ build_src_filter = ${SenseCap_Solar.build_src_filter}
lib_deps =
${SenseCap_Solar.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:SenseCap_Solar_kiss_modem]
extends = SenseCap_Solar
build_src_filter = ${SenseCap_Solar.build_src_filter}
+<../examples/kiss_modem/>
+2 -4
View File
@@ -229,7 +229,7 @@ build_flags =
-D WIFI_DEBUG_LOGGING=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D OFFLINE_QUEUE_SIZE=256
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Station_G2.build_src_filter}
@@ -242,10 +242,8 @@ lib_deps =
[env:Station_G2_kiss_modem]
extends = Station_G2
build_unflags =
-DARDUINO_USB_MODE=0
build_flags =
${Station_G2.build_flags}
-DARDUINO_USB_MODE=1
-D ARDUINO_USB_MODE=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/kiss_modem/>
@@ -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;
};
+33 -2
View File
@@ -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();
}
+15 -38
View File
@@ -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 {
+2 -2
View File
@@ -17,11 +17,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