Merge upstream companion-v1.17.1 into power-saving

Pulls in real fixes: scoped-reply routing, RX boosted-gain restored
correctly after AGC reset, T-Echo Lite/Card SPI pin corrections
(unused pins now map to NRFX_SPIM_PIN_NOT_USED via 0 instead of -1)
and TCXO voltage fix, Heltec T096/T1 and MeshPocket pin fixes,
T-beam Supreme S3 display fix, LR2021 preamble/IRQ timeout handling.

None of the touched variants overlap with our active Heltec V3/V4,
Cardputer ADV, GAT562, or WioTracker builds.

# Conflicts:
#	examples/companion_radio/MyMesh.cpp
#	examples/companion_radio/MyMesh.h
#	examples/companion_radio/NodePrefs.h
#	src/helpers/radiolib/CustomSX1262Wrapper.h
#	src/helpers/radiolib/RadioLibWrappers.cpp
#	src/helpers/ui/SH1106Display.cpp
#	variants/lilygo_techo_lite/platformio.ini
#	variants/lilygo_techo_lite/variant.h
This commit is contained in:
MarekZegare4
2026-08-14 15:58:23 +02:00
57 changed files with 820 additions and 193 deletions
+16
View File
@@ -35,6 +35,9 @@ $ sh build.sh build-repeater-firmwares
Build all chat room server firmwares Build all chat room server firmwares
$ sh build.sh build-room-server-firmwares $ sh build.sh build-room-server-firmwares
Build all kiss radio firmwares
$ sh build.sh build-kiss-radio-firmwares
Environment Variables: Environment Variables:
DISABLE_DEBUG=1: Disables all debug logging flags (MESH_DEBUG, MESH_PACKET_LOGGING, etc.) 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. If not set, debug flags from variant platformio.ini files are used.
@@ -256,6 +259,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_firmwares() {
build_companion_firmwares build_companion_firmwares
build_repeater_firmwares build_repeater_firmwares
@@ -296,6 +310,8 @@ elif [[ $1 == "build-solo-firmwares" ]]; then
build_solo_firmwares build_solo_firmwares
elif [[ $1 == "build-room-server-firmwares" ]]; then elif [[ $1 == "build-room-server-firmwares" ]]; then
build_room_server_firmwares build_room_server_firmwares
elif [[ $1 == "build-kiss-radio-firmwares" ]]; then
build_kiss_modem_firmwares
elif [[ $1 == "get-companion-firmwares-to-build" ]]; then 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_usb"
get_pio_envs_ending_with_string "_companion_radio_ble" 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 ### System
#### View or change this node's name #### View or change this node's name
+4
View File
@@ -1608,6 +1608,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
seedDefaultRepeaterProfile(_prefs); seedDefaultRepeaterProfile(_prefs);
_prefs.gps_enabled = 0; // GPS disabled by default _prefs.gps_enabled = 0; // GPS disabled by default
_prefs.gps_interval = 0; // No automatic GPS updates by default _prefs.gps_interval = 0; // No automatic GPS updates by default
_prefs.radio_fem_rxgain = 1;
_prefs.radio_fem_txgain = 0;
_prefs.display_brightness = 2; // medium brightness by default _prefs.display_brightness = 2; // medium brightness by default
_prefs.buzzer_volume = 4; // max volume by default _prefs.buzzer_volume = 4; // max volume by default
_prefs.ringtone_bpm_idx = 2; // 120 bpm default _prefs.ringtone_bpm_idx = 2; // 120 bpm default
@@ -1729,6 +1731,8 @@ void MyMesh::begin(bool has_display) {
applyApc(); // sets TX power to the ceiling and arms APC if enabled applyApc(); // sets TX power to the ceiling and arms APC if enabled
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
radio_driver.setPowerSaving(_prefs.rx_powersave && !_prefs.client_repeat); // duty-cycle RX off while repeating (must hear all traffic) radio_driver.setPowerSaving(_prefs.rx_powersave && !_prefs.client_repeat); // duty-cycle RX off while repeating (must hear all traffic)
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
} }
+1 -1
View File
@@ -12,7 +12,7 @@ class UITask;
#define FIRMWARE_VER_CODE 13 #define FIRMWARE_VER_CODE 13
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "12 Aug 2026" #define FIRMWARE_BUILD_DATE "14 Aug 2026"
#endif #endif
// Fallback only -- every real build (local or CI) goes through build.sh, which // Fallback only -- every real build (local or CI) goes through build.sh, which
+8
View File
@@ -65,6 +65,14 @@ struct NodePrefs { // persisted to file
uint32_t gps_interval; // GPS duty-cycle sleep window in seconds (0 = disabled, GPS stays continuous) uint32_t gps_interval; // GPS duty-cycle sleep window in seconds (0 = disabled, GPS stays continuous)
uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t autoadd_config; // bitmask for auto-add contacts config
uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
// External LoRa FEM gain (LNA/PA), from upstream companion-v1.17.1 —
// board-level only right now (BaseCustomBoard::setLoRaFemLnaEnabled/
// PaGainEnabled, both default no-op false), no companion CLI/UI to set
// these yet, so — matching upstream's own decision — NOT persisted here:
// always reset to the constructor default (MyMesh.cpp) on boot. Not part
// of the DataStore save/load tripwire below.
uint8_t radio_fem_rxgain;
uint8_t radio_fem_txgain;
uint8_t client_repeat; uint8_t client_repeat;
uint8_t path_hash_mode; // which path mode to use when sending uint8_t path_hash_mode; // which path mode to use when sending
uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
+38 -17
View File
@@ -411,24 +411,31 @@ bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[])
} }
void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) { void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) {
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // if _request_ packet scope is known, send reply with same scope TransportKey req_scope;
TransportKey scope; bool is_wildcard = recv_pkt_region != NULL && recv_pkt_region->isWildcard();
if (region_map.getTransportKeysFor(*recv_pkt_region, &scope, 1) > 0) { bool req_scope_known = recv_pkt_region != NULL && !is_wildcard
sendFloodScoped(scope, packet, delay_millis, path_hash_size); && region_map.getTransportKeysFor(*recv_pkt_region, &req_scope, 1) > 0;
} else {
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped switch (mesh::chooseReplyScope(req_scope_known, is_wildcard, !default_scope.isNull())) {
} case mesh::REPLY_SCOPE_REQUEST:
} else { sendFloodScoped(req_scope, packet, delay_millis, path_hash_size); // reply with same scope as request
break;
case mesh::REPLY_SCOPE_DEFAULT:
// requester's scope is unknown: DIRECT request (no transport codes), or code matched no Region.
// un-scoped would be dropped at hop 0 by repeaters running flood.max.unscoped=0
sendFloodScoped(default_scope, packet, delay_millis, path_hash_size);
break;
case mesh::REPLY_SCOPE_NONE:
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
break;
} }
} }
bool MyMesh::allowPacketForward(const mesh::Packet *packet) { bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false; if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood()) { if (packet->isRouteFlood()
if (packet->getPathHashCount() >= _prefs.flood_max) return false; && mesh::isFloodHopLimitExceeded(packet, _prefs.flood_max, _prefs.flood_max_unscoped, _prefs.flood_max_advert)) {
if (packet->getRouteType() == ROUTE_TYPE_FLOOD && packet->getPathHashCount() >= _prefs.flood_max_unscoped) return false; return false;
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->getPathHashCount() >= _prefs.flood_max_advert) return false;
} }
if (packet->isRouteFlood() && recv_pkt_region == NULL) { if (packet->isRouteFlood() && recv_pkt_region == NULL) {
MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet"); MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet");
@@ -586,17 +593,29 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
if (reply_len == 0) return; // invalid request if (reply_len == 0) return; // invalid request
if (packet->isRouteFlood()) { // a DIRECT login can reply via the stored out_path, as onPeerDataRecv() does for REQ
ClientInfo* client = acl.getClient(sender.pub_key, PUB_KEY_SIZE);
bool have_out_path = client != NULL && client->out_path_len != OUT_PATH_UNKNOWN;
auto route = mesh::chooseReplyRoute(packet->isRouteFlood(), reply_path_len != 0xFF, have_out_path);
if (route == mesh::REPLY_ROUTE_PATH_RETURN) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len, mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
} else if (reply_path_len == 0xFF) { return;
}
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len); mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
if (reply) sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); if (reply == NULL) return;
if (route == mesh::REPLY_ROUTE_DIRECT_SUPPLIED) {
sendDirect(reply, reply_path, reply_path_len, SERVER_RESPONSE_DELAY);
} else if (route == mesh::REPLY_ROUTE_DIRECT_OUT_PATH) {
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
} else { } else {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len); sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
if (reply) sendDirect(reply, reply_path, reply_path_len, SERVER_RESPONSE_DELAY);
} }
} }
} }
@@ -913,6 +932,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#endif #endif
#endif #endif
_prefs.radio_fem_rxgain = 1; _prefs.radio_fem_rxgain = 1;
_prefs.radio_fem_txgain = 0;
pending_discover_tag = 0; pending_discover_tag = 0;
pending_discover_until = 0; pending_discover_until = 0;
@@ -962,6 +982,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
updateAdvertTimer(); updateAdvertTimer();
updateFloodAdvertTimer(); updateFloodAdvertTimer();
+3 -2
View File
@@ -34,6 +34,7 @@
#include <helpers/StatsFormatHelper.h> #include <helpers/StatsFormatHelper.h>
#include <helpers/TxtDataHelpers.h> #include <helpers/TxtDataHelpers.h>
#include <helpers/RegionMap.h> #include <helpers/RegionMap.h>
#include <helpers/RoutingPolicy.h>
#include "RateLimiter.h" #include "RateLimiter.h"
#ifdef WITH_BRIDGE #ifdef WITH_BRIDGE
@@ -70,11 +71,11 @@ struct NeighbourInfo {
}; };
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "9 Aug 2026" #define FIRMWARE_BUILD_DATE "14 Aug 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.17.0" #define FIRMWARE_VERSION "v1.17.1"
#endif #endif
#define FIRMWARE_ROLE "repeater" #define FIRMWARE_ROLE "repeater"
+21 -12
View File
@@ -299,10 +299,9 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
bool MyMesh::allowPacketForward(const mesh::Packet *packet) { bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false; if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood()) { if (packet->isRouteFlood()
if (packet->getPathHashCount() >= _prefs.flood_max) return false; && mesh::isFloodHopLimitExceeded(packet, _prefs.flood_max, _prefs.flood_max_unscoped, _prefs.flood_max_advert)) {
if (packet->getRouteType() == ROUTE_TYPE_FLOOD && packet->getPathHashCount() >= _prefs.flood_max_unscoped) return false; return false;
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->getPathHashCount() >= _prefs.flood_max_advert) return false;
} }
return true; return true;
} }
@@ -683,6 +682,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#endif #endif
#endif #endif
_prefs.radio_fem_rxgain = 1; _prefs.radio_fem_rxgain = 1;
_prefs.radio_fem_txgain = 0;
next_post_idx = 0; next_post_idx = 0;
next_client_idx = 0; next_client_idx = 0;
@@ -726,6 +726,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setTxPower(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
updateAdvertTimer(); updateAdvertTimer();
updateFloodAdvertTimer(); updateFloodAdvertTimer();
@@ -749,15 +750,23 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3
} }
void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) { void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) {
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // if _request_ packet scope is known, send reply with same scope TransportKey req_scope;
TransportKey scope; bool is_wildcard = recv_pkt_region != NULL && recv_pkt_region->isWildcard();
if (region_map.getTransportKeysFor(*recv_pkt_region, &scope, 1) > 0) { bool req_scope_known = recv_pkt_region != NULL && !is_wildcard
sendFloodScoped(scope, packet, delay_millis, path_hash_size); && region_map.getTransportKeysFor(*recv_pkt_region, &req_scope, 1) > 0;
} else {
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped switch (mesh::chooseReplyScope(req_scope_known, is_wildcard, !default_scope.isNull())) {
} case mesh::REPLY_SCOPE_REQUEST:
} else { sendFloodScoped(req_scope, packet, delay_millis, path_hash_size); // reply with same scope as request
break;
case mesh::REPLY_SCOPE_DEFAULT:
// requester's scope is unknown: DIRECT request (no transport codes), or code matched no Region.
// un-scoped would be dropped at hop 0 by repeaters running flood.max.unscoped=0
sendFloodScoped(default_scope, packet, delay_millis, path_hash_size);
break;
case mesh::REPLY_SCOPE_NONE:
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
break;
} }
} }
+3 -2
View File
@@ -21,17 +21,18 @@
#include <helpers/StatsFormatHelper.h> #include <helpers/StatsFormatHelper.h>
#include <helpers/ClientACL.h> #include <helpers/ClientACL.h>
#include <helpers/RegionMap.h> #include <helpers/RegionMap.h>
#include <helpers/RoutingPolicy.h>
#include <RTClib.h> #include <RTClib.h>
#include <target.h> #include <target.h>
/* ------------------------------ Config -------------------------------- */ /* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "9 Aug 2026" #define FIRMWARE_BUILD_DATE "14 Aug 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.17.0" #define FIRMWARE_VERSION "v1.17.1"
#endif #endif
#ifndef LORA_FREQ #ifndef LORA_FREQ
+2
View File
@@ -735,6 +735,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.gps_interval = 0; _prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS; _prefs.advert_loc_policy = ADVERT_LOC_PREFS;
_prefs.radio_fem_rxgain = 1; _prefs.radio_fem_rxgain = 1;
_prefs.radio_fem_txgain = 0;
memset(default_scope.key, 0, sizeof(default_scope.key)); 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.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setTxPower(_prefs.tx_power_dbm);
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
updateAdvertTimer(); updateAdvertTimer();
updateFloodAdvertTimer(); updateFloodAdvertTimer();
+2 -2
View File
@@ -34,11 +34,11 @@
#define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts #define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts
#ifndef FIRMWARE_BUILD_DATE #ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "9 Aug 2026" #define FIRMWARE_BUILD_DATE "14 Aug 2026"
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.17.0" #define FIRMWARE_VERSION "v1.17.1"
#endif #endif
#define FIRMWARE_ROLE "sensor" #define FIRMWARE_ROLE "sensor"
-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, // needs much less, around 600-700bytes. The CC310 workspace is static, faster,
// should save power at scale as well. // should save power at scale as well.
static CRYS_ECEDW_TempBuff_t cc310_tmp; static CRYS_ECEDW_TempBuff_t cc310_tmp;
nRFCrypto.begin();
CRYSError_t rc = CRYS_ECEDW_Verify((uint8_t*)sig, CRYS_ECEDW_SIGNATURE_BYTES, 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*)pub_key, CRYS_ECEDW_MOD_SIZE_IN_BYTES,
(uint8_t*)message, (size_t)msg_len, &cc310_tmp); (uint8_t*)message, (size_t)msg_len, &cc310_tmp);
nRFCrypto.end();
return rc == CRYS_OK; return rc == CRYS_OK;
#elif 0 #elif 0
// NOTE: memory corruption bug was found in this function!! // 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 setLoRaFemLnaEnabled(bool enable) { return false; }
virtual bool canControlLoRaFemLna() const { return false; } virtual bool canControlLoRaFemLna() const { return false; }
virtual bool isLoRaFemLnaEnabled() 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) // Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; } 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) { void Utils::sha256(uint8_t *hash, size_t hash_len, const uint8_t* msg, int msg_len) {
#ifdef USE_CC310_HW_CRYPTO #ifdef USE_CC310_HW_CRYPTO
static CRYS_HASH_Result_t result; static CRYS_HASH_Result_t result;
nRFCrypto.begin();
CRYS_HASH(CRYS_HASH_SHA256_mode, (uint8_t*)msg, (size_t)msg_len, result); CRYS_HASH(CRYS_HASH_SHA256_mode, (uint8_t*)msg, (size_t)msg_len, result);
nRFCrypto.end();
memcpy(hash, result, hash_len); memcpy(hash, result, hash_len);
#else #else
SHA256 sha; 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 #ifdef USE_CC310_HW_CRYPTO
static CRYS_HASHUserContext_t ctx; static CRYS_HASHUserContext_t ctx;
static CRYS_HASH_Result_t result; static CRYS_HASH_Result_t result;
nRFCrypto.begin();
CRYS_HASH_Init(&ctx, CRYS_HASH_SHA256_mode); 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*)frag1, (size_t)frag1_len);
CRYS_HASH_Update(&ctx, (uint8_t*)frag2, (size_t)frag2_len); CRYS_HASH_Update(&ctx, (uint8_t*)frag2, (size_t)frag2_len);
CRYS_HASH_Finish(&ctx, result); CRYS_HASH_Finish(&ctx, result);
nRFCrypto.end();
memcpy(hash, result, hash_len); memcpy(hash, result, hash_len);
#else #else
SHA256 sha; 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; const uint8_t* sp = src;
size_t dummy_out = 0; size_t dummy_out = 0;
nRFCrypto.begin();
SaSi_AesInit(&ctx, SASI_AES_DECRYPT, SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE); SaSi_AesInit(&ctx, SASI_AES_DECRYPT, SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE);
SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &keyData, sizeof(keyData)); SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &keyData, sizeof(keyData));
while (sp - src < src_len) { 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_AesFinish(&ctx, 0, NULL, 0, NULL, &dummy_out);
SaSi_AesFree(&ctx); SaSi_AesFree(&ctx);
nRFCrypto.end();
return sp - src; return sp - src;
#else #else
AES128 aes; 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; uint8_t* dp = dest;
size_t dummy_out = 0; size_t dummy_out = 0;
nRFCrypto.begin();
SaSi_AesInit(&ctx, SASI_AES_ENCRYPT, SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE); SaSi_AesInit(&ctx, SASI_AES_ENCRYPT, SASI_AES_MODE_ECB, SASI_AES_PADDING_NONE);
SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &keyData, sizeof(keyData)); SaSi_AesSetKey(&ctx, SASI_AES_USER_KEY, &keyData, sizeof(keyData));
while (src_len >= 16) { 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_AesFinish(&ctx, 0, NULL, 0, NULL, &dummy_out);
SaSi_AesFree(&ctx); SaSi_AesFree(&ctx);
nRFCrypto.end();
return dp - dest; return dp - dest;
#else #else
AES128 aes; AES128 aes;
@@ -138,11 +130,9 @@ int Utils::encryptThenMAC(const uint8_t* shared_secret, uint8_t* dest, const uin
#ifdef USE_CC310_HW_CRYPTO #ifdef USE_CC310_HW_CRYPTO
static CRYS_HMACUserContext_t hmac_ctx; static CRYS_HMACUserContext_t hmac_ctx;
static CRYS_HASH_Result_t hmac_result; 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_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_Update(&hmac_ctx, dest + CIPHER_MAC_SIZE, enc_len);
CRYS_HMAC_Finish(&hmac_ctx, hmac_result); CRYS_HMAC_Finish(&hmac_ctx, hmac_result);
nRFCrypto.end();
memcpy(dest, hmac_result, CIPHER_MAC_SIZE); memcpy(dest, hmac_result, CIPHER_MAC_SIZE);
#else #else
SHA256 sha; 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_HMACUserContext_t hmac_ctx;
static CRYS_HASH_Result_t hmac_result; 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_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_Update(&hmac_ctx, (uint8_t*)(src + CIPHER_MAC_SIZE), src_len - CIPHER_MAC_SIZE);
CRYS_HMAC_Finish(&hmac_ctx, hmac_result); CRYS_HMAC_Finish(&hmac_ctx, hmac_result);
nRFCrypto.end();
memcpy(hmac, hmac_result, CIPHER_MAC_SIZE); memcpy(hmac, hmac_result, CIPHER_MAC_SIZE);
} }
#else #else
+29
View File
@@ -133,6 +133,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // Legacy
// sanitise settings // sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _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_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 _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean
file.close(); file.close();
@@ -562,6 +563,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else { } else {
strcpy(reply, "Error: state must be on or off"); 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) { } else if (memcmp(config, "radio ", 6) == 0) {
strcpy(tmp, &config[6]); strcpy(tmp, &config[6]);
const char *parts[4]; const char *parts[4];
@@ -827,6 +850,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else { } else {
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); 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) { } else if (memcmp(config, "radio", 5) == 0) {
char freq[16], bw[16]; char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(_prefs->freq)); strcpy(freq, StrHelper::ftoa(_prefs->freq));
+3 -1
View File
@@ -65,6 +65,7 @@ public:
char owner_info[120]; char owner_info[120];
uint8_t rx_boosted_gain = 0; // power settings uint8_t rx_boosted_gain = 0; // power settings
uint8_t radio_fem_rxgain = 0; // LoRa FEM RX gain setting 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 path_hash_mode = 0; // which path mode to use when sending
uint8_t loop_detect = 0; uint8_t loop_detect = 0;
uint8_t cad_enabled = 0; // hardware Channel Activity Detection before TX (boolean) uint8_t cad_enabled = 0; // hardware Channel Activity Detection before TX (boolean)
@@ -82,7 +83,8 @@ private:
def("cad", _parent->cad_enabled); def("cad", _parent->cad_enabled);
def("int_thr", _parent->interference_threshold); def("int_thr", _parent->interference_threshold);
def("rxgain", _parent->rx_boosted_gain); 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("tx", _parent->tx_power_dbm);
def("af", _parent->airtime_factor); def("af", _parent->airtime_factor);
def("rxdelay", _parent->rx_delay_base); def("rxdelay", _parent->rx_delay_base);
+13
View File
@@ -5,6 +5,10 @@
#include <bluefruit.h> #include <bluefruit.h>
#include <nrf_soc.h> #include <nrf_soc.h>
#ifdef USE_CC310_HW_CRYPTO
#include <Adafruit_nRFCrypto.h>
#endif
static BLEDfu bledfu; static BLEDfu bledfu;
static void connect_callback(uint16_t conn_handle) { 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() { void NRF52Board::begin() {
startup_reason = BD_STARTUP_NORMAL; 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 #ifdef NRF52_POWER_MANAGEMENT
@@ -352,6 +361,10 @@ void NRF52Board::shutdownPeripherals() {
sensors.getLocationProvider()->stop(); sensors.getLocationProvider()->stop();
} }
#ifdef USE_CC310_HW_CRYPTO
nRFCrypto.end();
#endif
// Flush serial buffers // Flush serial buffers
Serial.flush(); Serial.flush();
delay(100); delay(100);
+68
View File
@@ -0,0 +1,68 @@
#pragma once
#include <Packet.h>
namespace mesh {
/**
* \brief Test a flood packet against the configured hop limits.
* \param packet inbound flood packet (caller has already checked isRouteFlood())
* \param flood_max max hops for any flood packet
* \param flood_max_unscoped max hops for ROUTE_TYPE_FLOOD (ie. un-scoped) packets
* \param flood_max_advert max hops for ADVERT packets
* \returns true if the packet has exceeded a limit, and must not be forwarded
*/
inline bool isFloodHopLimitExceeded(const Packet* packet, uint8_t flood_max,
uint8_t flood_max_unscoped, uint8_t flood_max_advert) {
uint8_t hops = packet->getPathHashCount();
if (hops >= flood_max) return true;
if (packet->getRouteType() == ROUTE_TYPE_FLOOD && hops >= flood_max_unscoped) return true;
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && hops >= flood_max_advert) return true;
return false;
}
/**
* \brief How a server routes a reply back to the requesting client.
*/
enum ReplyRoute : uint8_t {
REPLY_ROUTE_PATH_RETURN, // request arrived by flood: reply with a PATH return, flooded back
REPLY_ROUTE_DIRECT_SUPPLIED, // reply DIRECT, along the return path supplied in the request
REPLY_ROUTE_DIRECT_OUT_PATH, // reply DIRECT, along the out_path already stored for this client
REPLY_ROUTE_FLOOD, // no return path known: flood the reply
};
/**
* \param inbound_is_flood the request arrived as a flood packet
* \param have_supplied_path the request payload carried an explicit reply path
* \param have_out_path this server already has a stored out_path for the client
*/
inline ReplyRoute chooseReplyRoute(bool inbound_is_flood, bool have_supplied_path, bool have_out_path) {
if (inbound_is_flood) return REPLY_ROUTE_PATH_RETURN;
if (have_supplied_path) return REPLY_ROUTE_DIRECT_SUPPLIED;
if (have_out_path) return REPLY_ROUTE_DIRECT_OUT_PATH;
return REPLY_ROUTE_FLOOD;
}
/**
* \brief Which transport scope a flooded reply should be sent with.
*/
enum ReplyScope : uint8_t {
REPLY_SCOPE_REQUEST, // re-use the scope the request arrived on
REPLY_SCOPE_DEFAULT, // fall back to this node's default region scope
REPLY_SCOPE_NONE, // send un-scoped (ROUTE_TYPE_FLOOD)
};
/**
* \param request_scope_known request arrived scoped, and we resolved its Region's key
* \param request_was_unscoped_flood request arrived as an un-scoped flood
* \param default_scope_known this node has a default Region with a usable transport key
*/
inline ReplyScope chooseReplyScope(bool request_scope_known, bool request_was_unscoped_flood,
bool default_scope_known) {
if (request_scope_known) return REPLY_SCOPE_REQUEST;
if (request_was_unscoped_flood) return REPLY_SCOPE_NONE; // requester chose un-scoped, so mirror it
if (default_scope_known) return REPLY_SCOPE_DEFAULT; // scope unknowable: DIRECT, or unresolved Region
return REPLY_SCOPE_NONE;
}
}
+2 -1
View File
@@ -19,7 +19,6 @@ public:
((CustomLR1110 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); ((CustomLR1110 *)_radio)->setMaxPayloadMillis(pm.payloadMillis);
} }
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); }
bool isReceivingPacket() override { bool isReceivingPacket() override {
return ((CustomLR1110 *)_radio)->isReceiving(); return ((CustomLR1110 *)_radio)->isReceiving();
} }
@@ -50,4 +49,6 @@ public:
bool getRxBoostedGainMode() const override { bool getRxBoostedGainMode() const override {
return ((CustomLR1110 *)_radio)->getRxBoostedGainMode(); 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" #include "MeshCore.h"
class CustomLR2021 : public LR2021 { class CustomLR2021 : public LR2021 {
uint32_t _preambleMillis = 66;
uint32_t _maxPayloadMillis = 3934;
uint32_t _activityAt = 0;
bool _headerSeen = false;
bool _rx_boosted = false; bool _rx_boosted = false;
public: public:
@@ -66,11 +70,62 @@ class CustomLR2021 : public LR2021 {
bool getRxBoostedGainMode() const { return _rx_boosted; } 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() { bool isReceiving() {
uint32_t irq = getIrqStatus(); uint32_t irq = getIrqStatus();
bool detected = ((irq & RADIOLIB_LR2021_IRQ_SYNCWORD_VALID) || (irq & RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED)); bool preamble = irq & RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED; // bit 5
return detected; 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; } uint8_t getSpreadingFactor() const { return spreadingFactor; }
}; };
@@ -22,6 +22,10 @@ public:
((CustomLR2021 *)_radio)->setCodingRate(cr); ((CustomLR2021 *)_radio)->setCodingRate(cr);
updatePreamble(sf); updatePreamble(sf);
applySideDetectorConfig(); 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 { 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; } uint8_t getSpreadingFactor() const override { return ((CustomSTM32WLx *)_radio)->spreadingFactor; }
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); }
}; };
+1 -2
View File
@@ -54,8 +54,6 @@ public:
((CustomSX1262 *)_radio)->sleep(false); ((CustomSX1262 *)_radio)->sleep(false);
} }
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
// Power-save RX = hardware RX duty-cycle (SX126x SetRxDutyCycle, datasheet // Power-save RX = hardware RX duty-cycle (SX126x SetRxDutyCycle, datasheet
// 13.1.7). The chip's sequencer cycles RX↔sleep on its own, latches a preamble // 13.1.7). The chip's sequencer cycles RX↔sleep on its own, latches a preamble
// of the configured length and then stays in RX to receive the packet, raising // of the configured length and then stays in RX to receive the packet, raising
@@ -106,4 +104,5 @@ public:
((CustomSX1262 *)_radio)->setRxBoostedGainMode(_wd_rx_boosted_gain); ((CustomSX1262 *)_radio)->setRxBoostedGainMode(_wd_rx_boosted_gain);
return true; return true;
} }
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; } uint8_t getSpreadingFactor() const override { return ((CustomSX1268 *)_radio)->spreadingFactor; }
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
bool setRxBoostedGainMode(bool en) override { bool setRxBoostedGainMode(bool en) override {
return ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; return ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;
} }
bool getRxBoostedGainMode() const override { bool getRxBoostedGainMode() const override {
return ((CustomSX1268 *)_radio)->getRxBoostedGainMode(); 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). // Full receiver reset for LR11x0-family chips (LR1110, LR1120, LR1121).
// Warm sleep powers down analog, calibrate(0x3F) refreshes all calibration blocks, // Warm sleep powers down analog, calibrate(0x3F) refreshes all calibration blocks,
// then re-applies RX settings that calibration may reset. // 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->sleep(true, 0);
radio->standby(RADIOLIB_LR11X0_STANDBY_RC, true); 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); radio->calibrateImageRejection(freqMHz - 4.0f, freqMHz + 4.0f);
#ifdef RX_BOOSTED_GAIN #ifdef RX_BOOSTED_GAIN
radio->setRxBoostedGainMode(RX_BOOSTED_GAIN); radio->setRxBoostedGainMode(rx_boost_gain);
#endif #endif
} }
@@ -193,7 +193,9 @@ void RadioLibWrapper::loop() {
} }
_floor_sample_sum = 0; _floor_sample_sum = 0;
#ifdef MESH_DEBUG_NOISE_FLOOR
MESH_DEBUG_PRINTLN("RadioLibWrapper: noise_floor = %d", (int)_noise_floor); MESH_DEBUG_PRINTLN("RadioLibWrapper: noise_floor = %d", (int)_noise_floor);
#endif
if (_nf_calib_active) { if (_nf_calib_active) {
_nf_calib_active = false; // fresh floor published -- back to duty-cycle _nf_calib_active = false; // fresh floor published -- back to duty-cycle
+3 -3
View File
@@ -166,10 +166,10 @@ public:
void random(uint8_t* dest, size_t sz) override { void random(uint8_t* dest, size_t sz) override {
#ifdef USE_CC310_HW_CRYPTO #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.Random.generate(dest, (uint16_t)sz);
nRFCrypto.end(); for (int i = 0; i < sz; i++) {
dest[i] ^= _radio->randomByte() ^ (::random(0, 256) & 0xFF); // combine with Radio's entropy
}
#else #else
for (int i = 0; i < sz; i++) { for (int i = 0; i < sz; i++) {
dest[i] = _radio->randomByte() ^ (::random(0, 256) & 0xFF); 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). // Full receiver reset for all SX126x-family chips (SX1262, SX1268, LLCC68, STM32WLx).
// Warm sleep powers down analog, Calibrate(0x7F) refreshes ADC/PLL/image calibration, // Warm sleep powers down analog, Calibrate(0x7F) refreshes ADC/PLL/image calibration,
// then re-applies RX settings that calibration may reset. // 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->sleep(true);
radio->standby(RADIOLIB_SX126X_STANDBY_RC, true); radio->standby(RADIOLIB_SX126X_STANDBY_RC, true);
@@ -26,7 +26,7 @@ inline void sx126xResetAGC(SX126x* radio) {
radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif #endif
#ifdef SX126X_RX_BOOSTED_GAIN #ifdef SX126X_RX_BOOSTED_GAIN
radio->setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); radio->setRxBoostedGainMode(rx_boost_gain);
#endif #endif
#ifdef SX126X_REGISTER_PATCH #ifdef SX126X_REGISTER_PATCH
uint8_t r_data = 0; uint8_t r_data = 0;
+17 -1
View File
@@ -12,7 +12,23 @@ bool SH1106Display::i2c_probe(TwoWire &wire, uint8_t addr)
bool SH1106Display::begin() bool SH1106Display::begin()
{ {
return display.begin(DISPLAY_ADDRESS, true) && i2c_probe(Wire, DISPLAY_ADDRESS); // 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. 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() void SH1106Display::turnOn()
@@ -0,0 +1,83 @@
#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; }
};
#if 0
// Re-enable test once we can SET fem_ values in companion
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);
}
#endif
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -1,6 +1,14 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "helpers/ConfigSerializer.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_S "56"
#define TEST_INT 56 #define TEST_INT 56
#define TEST_FLOAT_S "-6.123" #define TEST_FLOAT_S "-6.123"
@@ -21,6 +29,19 @@ public:
class MockPrintStream : public Stream { class MockPrintStream : public Stream {
int len = 0; int len = 0;
uint8_t _buf[1024]; 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: public:
size_t write(uint8_t b) override { size_t write(uint8_t b) override {
if (len < sizeof(_buf)) { if (len < sizeof(_buf)) {
@@ -30,17 +51,17 @@ public:
return 0; 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(unsigned char v, int r) override { return printUnsigned(v); }
size_t print(int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } size_t print(int v, int r) override { return printSigned(v); }
size_t print(unsigned 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 { return printUnsigned(v); }
size_t print(long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } size_t print(long v, int r) override { return printSigned(v); }
size_t print(unsigned 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 { return printUnsigned(v); }
size_t print(long 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 { return printSigned(v); }
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(unsigned long long v, int r) override { return printUnsigned(v); }
size_t print(double v, int p = 2) override { size_t print(double v, int p = 2) override {
if (p == 6) return Print::print(TEST_DOUBLE_S); char text[32];
if (p == 4) return Print::print(TEST_FLOAT_S); snprintf(text, sizeof(text), "%.*f", p, v);
return 0; return Print::print(text);
} }
int getLength() const { return len; } int getLength() const { return len; }
@@ -171,6 +192,28 @@ TEST(ConfigSerializer, LoadSerial_IgnoreUnknowns) {
EXPECT_TRUE(match); 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 ─────────────────────────────────────────────────────── // ── main ───────────────────────────────────────────────────────
@@ -0,0 +1,110 @@
#include <gtest/gtest.h>
#include "helpers/RoutingPolicy.h"
using namespace mesh;
static Packet makeFlood(uint8_t route_type, uint8_t payload_type, uint8_t hops) {
Packet p;
p.header = route_type | (payload_type << PH_TYPE_SHIFT);
p.setPathHashSizeAndCount(1, hops);
p.payload_len = 1;
return p;
}
TEST(FloodHopLimit, UnscopedFloodIsDroppedAtFirstHopWhenMaxUnscopedIsZero) {
auto pkt = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_RESPONSE, 0);
EXPECT_TRUE(isFloodHopLimitExceeded(&pkt, 64, 0, 8));
}
TEST(FloodHopLimit, ScopedFloodIsForwardedWhenMaxUnscopedIsZero) {
for (uint8_t hops = 0; hops < 4; hops++) {
auto pkt = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_RESPONSE, hops);
EXPECT_FALSE(isFloodHopLimitExceeded(&pkt, 64, 0, 8)) << "hops=" << (int)hops;
}
}
TEST(FloodHopLimit, UnscopedFloodSurvivesUpToMaxUnscopedHops) {
// matches the reported workaround: raising flood.max.unscoped to the expected hop count
auto ok = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_RESPONSE, 2);
EXPECT_FALSE(isFloodHopLimitExceeded(&ok, 64, 3, 8));
auto too_far = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_RESPONSE, 3);
EXPECT_TRUE(isFloodHopLimitExceeded(&too_far, 64, 3, 8));
}
TEST(FloodHopLimit, ScopedFloodStillHonoursFloodMaxAndAdvertMax) {
auto beyond_max = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_RESPONSE, 5);
EXPECT_TRUE(isFloodHopLimitExceeded(&beyond_max, 5, 64, 8));
auto advert = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_ADVERT, 8);
EXPECT_TRUE(isFloodHopLimitExceeded(&advert, 64, 64, 8));
}
// flood.max.unscoped=0 hits adverts too, well before flood_max_advert applies: a node
// still advertising un-scoped is invisible past its immediate neighbours
TEST(FloodHopLimit, UnscopedAdvertIsAlsoDroppedAtHopZero) {
auto advert = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_ADVERT, 0);
EXPECT_TRUE(isFloodHopLimitExceeded(&advert, 64, 0, 8));
auto scoped = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_ADVERT, 0);
EXPECT_FALSE(isFloodHopLimitExceeded(&scoped, 64, 0, 8));
}
TEST(ReplyRoute, FloodRequestGetsAPathReturn) {
EXPECT_EQ(REPLY_ROUTE_PATH_RETURN,
chooseReplyRoute(true, false, false));
EXPECT_EQ(REPLY_ROUTE_PATH_RETURN,
chooseReplyRoute(true, false, true));
}
TEST(ReplyRoute, DirectRequestWithSuppliedPathRepliesDirect) {
EXPECT_EQ(REPLY_ROUTE_DIRECT_SUPPLIED, chooseReplyRoute(false, true, false));
}
// the reported bug: a DIRECT login (app already has a path) was answered by flooding, even
// with an out_path stored. Under flood.max.unscoped=0 that reply never arrives.
TEST(ReplyRoute, DirectRequestWithKnownOutPathRepliesDirect) {
EXPECT_EQ(REPLY_ROUTE_DIRECT_OUT_PATH,
chooseReplyRoute(false, false, true));
}
TEST(ReplyRoute, SuppliedPathWinsOverStoredOutPath) {
EXPECT_EQ(REPLY_ROUTE_DIRECT_SUPPLIED, chooseReplyRoute(false, true, true));
}
TEST(ReplyRoute, DirectRequestWithNoReturnPathFallsBackToFlood) {
EXPECT_EQ(REPLY_ROUTE_FLOOD, chooseReplyRoute(false, false, false));
}
TEST(ReplyScope, MirrorsTheRequestScopeWhenKnown) {
EXPECT_EQ(REPLY_SCOPE_REQUEST, chooseReplyScope(true,
false,
false));
EXPECT_EQ(REPLY_SCOPE_REQUEST, chooseReplyScope(true, false, true));
}
// un-scoped is itself a known scope, so mirror it. Replying scoped would change a path that
// works today, and repeaters not holding our default Region would drop it anyway.
TEST(ReplyScope, RepliesUnscopedToAnUnscopedFloodEvenWhenADefaultScopeExists) {
EXPECT_EQ(REPLY_SCOPE_NONE, chooseReplyScope(false,
true,
true));
}
// second half of the bug: a DIRECT request carries no transport codes, so recv_pkt_region is
// always NULL. Un-scoped is dropped under flood.max.unscoped=0, and floods the mesh otherwise.
TEST(ReplyScope, FallsBackToDefaultScopeWhenRequestScopeUnknown) {
EXPECT_EQ(REPLY_SCOPE_DEFAULT, chooseReplyScope(false,
false,
true));
}
TEST(ReplyScope, SendsUnscopedOnlyWhenNoScopeIsAvailableAtAll) {
EXPECT_EQ(REPLY_SCOPE_NONE, chooseReplyScope(false, false, false));
EXPECT_EQ(REPLY_SCOPE_NONE, chooseReplyScope(false, true, false));
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+1 -1
View File
@@ -104,7 +104,7 @@
#define PIN_SPI_SCK (32 + 8) #define PIN_SPI_SCK (32 + 8)
#define PIN_SPI_NSS LORA_CS #define PIN_SPI_NSS LORA_CS
#define PIN_SPI1_MISO (-1) #define PIN_SPI1_MISO (0)
#define PIN_SPI1_MOSI (0+17) #define PIN_SPI1_MOSI (0+17)
#define PIN_SPI1_SCK (0+20) #define PIN_SPI1_SCK (0+20)
+3 -4
View File
@@ -25,8 +25,8 @@
#define ST7735_SDA (0 + 24) #define ST7735_SDA (0 + 24)
#define ST7735_SCK (32 + 0) #define ST7735_SCK (32 + 0)
#define ST7735_RESET (0 + 20) #define ST7735_RESET (0 + 20)
#define ST7735_MISO (-1) #define ST7735_MISO (0) // 0 maps to 0xff on this device which is NRFX_SPIM_PIN_NOT_USED
#define ST7735_BUSY (-1) #define ST7735_BUSY (0) // 0 maps to 0xff on this device which is NRFX_SPIM_PIN_NOT_USED
#define ST7735_BL (0 + 15) #define ST7735_BL (0 + 15)
#define VTFT_CTRL (0 + 13) #define VTFT_CTRL (0 + 13)
@@ -66,8 +66,7 @@
// UART // UART
// No longer populated on PCB. // No longer populated on PCB.
#define PIN_SERIAL2_RX (-1) // Removed the pin definitions to avoid potential issues with Uart.
#define PIN_SERIAL2_TX (-1)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// I2C // I2C
@@ -119,3 +119,8 @@ lib_deps =
${LilyGo_T-Echo_Card.lib_deps} ${LilyGo_T-Echo_Card.lib_deps}
end2endzone/NonBlockingRTTTL@^1.3.0 end2endzone/NonBlockingRTTTL@^1.3.0
densaugeo/base64 @ ~1.4.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/>
+1 -1
View File
@@ -107,7 +107,7 @@
#define P_LORA_NSS (11) // P0.11 #define P_LORA_NSS (11) // P0.11
#define SX126X_RXEN (33) // P1.01 #define SX126X_RXEN (33) // P1.01
#define SX126X_TXEN (27) // P0.27 #define SX126X_TXEN (27) // P0.27
#define SX126X_DIO3_TCXO_VOLTAGE (1.8f) #define SX126X_DIO3_TCXO_VOLTAGE (3.0f)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
+2 -2
View File
@@ -15,7 +15,6 @@ build_flags = ${nrf52_base.build_flags}
-D SX126X_DIO3_TCXO_VOLTAGE=3.0 ;https://github.com/Xinyuan-LilyGO/T-Echo-Lite/issues/14 -D SX126X_DIO3_TCXO_VOLTAGE=3.0 ;https://github.com/Xinyuan-LilyGO/T-Echo-Lite/issues/14
-D SX126X_CURRENT_LIMIT=140 -D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1 -D SX126X_RX_BOOSTED_GAIN=1
-D SX126X_USE_REGULATOR_LDO=1
-D P_LORA_TX_LED=LED_GREEN -D P_LORA_TX_LED=LED_GREEN
-D DISABLE_DIAGNOSTIC_OUTPUT -D DISABLE_DIAGNOSTIC_OUTPUT
-D ENV_INCLUDE_GPS=1 -D ENV_INCLUDE_GPS=1
@@ -159,7 +158,7 @@ build_flags =
-D WRAPPER_CLASS=CustomSX1262Wrapper -D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22 -D LORA_TX_POWER=22
-D SX126X_POWER_EN=30 -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_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1 -D SX126X_RX_BOOSTED_GAIN=1
-D P_LORA_TX_LED=LED_GREEN -D P_LORA_TX_LED=LED_GREEN
@@ -204,6 +203,7 @@ build_flags =
-D WRAPPER_CLASS=CustomSX1262Wrapper -D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22 -D LORA_TX_POWER=22
-D SX126X_POWER_EN=30 -D SX126X_POWER_EN=30
-D SX126X_DIO3_TCXO_VOLTAGE=3.0
-D SX126X_CURRENT_LIMIT=140 -D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1 -D SX126X_RX_BOOSTED_GAIN=1
-D P_LORA_TX_LED=LED_GREEN -D P_LORA_TX_LED=LED_GREEN
+6 -5
View File
@@ -58,7 +58,7 @@
#define PIN_SPI_MISO _PINNUM(0, 17) // (MISO) #define PIN_SPI_MISO _PINNUM(0, 17) // (MISO)
#define PIN_SPI_MOSI _PINNUM(0, 15) // (MOSI) #define PIN_SPI_MOSI _PINNUM(0, 15) // (MOSI)
#define PIN_SPI_SCK _PINNUM(0, 13) // (SCK) #define PIN_SPI_SCK _PINNUM(0, 13) // (SCK)
#define PIN_SPI_NSS (-1) #define PIN_SPI_NSS (0)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// QSPI FLASH // QSPI FLASH
@@ -105,13 +105,14 @@
#define LORA_CS _PINNUM(0, 11) #define LORA_CS _PINNUM(0, 11)
#define SX126X_POWER_EN _PINNUM(0, 30) #define SX126X_POWER_EN _PINNUM(0, 30)
#define SX126X_DIO1 _PINNUM(1, 8) #define SX126X_DIO1 _PINNUM(1, 8)
#define SX1262_DIO2 _PINNUM(0, 5) #define SX126X_DIO2 _PINNUM(0, 5)
#define SX126X_BUSY _PINNUM(0, 14) #define SX126X_BUSY _PINNUM(0, 14)
#define SX126X_RESET _PINNUM(0, 7) #define SX126X_RESET _PINNUM(0, 7)
#define SX126X_RXEN _PINNUM(1, 1) #define SX126X_RXEN _PINNUM(1, 1)
#define SX126X_TXEN _PINNUM(0, 27) #define SX126X_TXEN _PINNUM(0, 27)
#define P_LORA_DIO_1 SX126X_DIO1 #define P_LORA_DIO_1 SX126X_DIO1
#define P_LORA_DIO_2 SX126X_DIO2
#define P_LORA_NSS LORA_CS #define P_LORA_NSS LORA_CS
#define P_LORA_RESET SX126X_RESET #define P_LORA_RESET SX126X_RESET
#define P_LORA_BUSY SX126X_BUSY #define P_LORA_BUSY SX126X_BUSY
@@ -122,7 +123,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// SPI1 // SPI1
#define PIN_SPI1_MISO (-1) // Not used for Display #define PIN_SPI1_MISO (0) // Not used for Display, 0 maps to 0xff on this device which is NRFX_SPIM_PIN_NOT_USED
#define PIN_SPI1_MOSI _PINNUM(0, 20) #define PIN_SPI1_MOSI _PINNUM(0, 20)
#define PIN_SPI1_SCK _PINNUM(0, 19) #define PIN_SPI1_SCK _PINNUM(0, 19)
@@ -134,7 +135,7 @@ extern const int SCK;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Display // Display
// #define DISP_MISO (-1) // Not used for Display // #define DISP_MISO (0) // Not used for Display, 0 maps to 0xff on this device which is NRFX_SPIM_PIN_NOT_USED
#define DISP_MOSI _PINNUM(0, 20) #define DISP_MOSI _PINNUM(0, 20)
#define DISP_SCLK _PINNUM(0, 19) #define DISP_SCLK _PINNUM(0, 19)
#define DISP_CS _PINNUM(0, 22) #define DISP_CS _PINNUM(0, 22)
@@ -142,7 +143,7 @@ extern const int SCK;
#define DISP_RST _PINNUM(0, 28) #define DISP_RST _PINNUM(0, 28)
#define DISP_BUSY _PINNUM(0, 3) #define DISP_BUSY _PINNUM(0, 3)
#define DISP_POWER _PINNUM(1, 12) #define DISP_POWER _PINNUM(1, 12)
// #define DISP_BACKLIGHT (-1) // Display has no backlight // #define DISP_BACKLIGHT (0) // Display has no backlight, 0 maps to 0xff on this device which is NRFX_SPIM_PIN_NOT_USED
#define PIN_DISPLAY_CS DISP_CS #define PIN_DISPLAY_CS DISP_CS
#define PIN_DISPLAY_DC DISP_DC #define PIN_DISPLAY_DC DISP_DC
@@ -98,3 +98,8 @@ build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter}
lib_deps = lib_deps =
${LilyGo_TETH_Elite_sx1262.lib_deps} ${LilyGo_TETH_Elite_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0 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/>
+1 -1
View File
@@ -108,7 +108,7 @@
#define PIN_DISPLAY_DC (31) #define PIN_DISPLAY_DC (31)
#define PIN_DISPLAY_RST (32 + 4) #define PIN_DISPLAY_RST (32 + 4)
#define PIN_SPI1_MISO (-1) #define PIN_SPI1_MISO (0) // 0 maps to 0xff on this device which is NRFX_SPIM_PIN_NOT_USED
#define PIN_SPI1_MOSI (20) #define PIN_SPI1_MOSI (20)
#define PIN_SPI1_SCK (22) #define PIN_SPI1_SCK (22)
+5
View File
@@ -130,3 +130,8 @@ lib_deps = ${MeshTracker_X1.lib_deps}
stevemarple/MicroNMEA @ ^2.0.6 stevemarple/MicroNMEA @ ^2.0.6
end2endzone/NonBlockingRTTTL@^1.3.0 end2endzone/NonBlockingRTTTL@^1.3.0
adafruit/Adafruit DRV2605 Library @ ^1.2.4 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 -D ENV_INCLUDE_INA219=1
build_src_filter = ${nrf52_base.build_src_filter} build_src_filter = ${nrf52_base.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
+<../variants/minewsemi_me25ls01> +<../variants/minewsemi_me25ls01>
+<helpers/sensors> +<helpers/sensors>
debug_tool = jlink debug_tool = jlink
@@ -55,7 +56,6 @@ build_flags = ${me25ls01.build_flags}
;-D PIN_BUZZER=25 ;-D PIN_BUZZER=25
;-D PIN_BUZZER_EN=37 ;-D PIN_BUZZER_EN=37
build_src_filter = ${me25ls01.build_src_filter} build_src_filter = ${me25ls01.build_src_filter}
+<helpers/ui/NullDisplayDriver.cpp>
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-orig/*.cpp> +<../examples/companion_radio/ui-orig/*.cpp>
@@ -63,10 +63,6 @@ build_src_filter = ${me25ls01.build_src_filter}
[env:Minewsemi_me25ls01_repeater] [env:Minewsemi_me25ls01_repeater]
extends = me25ls01 extends = me25ls01
build_flags = ${me25ls01.build_flags} 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_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
@@ -85,10 +81,6 @@ build_src_filter = ${me25ls01.build_src_filter}
[env:Minewsemi_me25ls01_room_server] [env:Minewsemi_me25ls01_room_server]
extends = me25ls01 extends = me25ls01
build_flags = ${me25ls01.build_flags} 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_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
@@ -103,15 +95,10 @@ build_flags = ${me25ls01.build_flags}
-D DISPLAY_CLASS=NullDisplayDriver -D DISPLAY_CLASS=NullDisplayDriver
build_src_filter = ${me25ls01.build_src_filter} build_src_filter = ${me25ls01.build_src_filter}
+<../examples/simple_room_server> +<../examples/simple_room_server>
+<helpers/ui/NullDisplayDriver.cpp>
[env:Minewsemi_me25ls01_terminal_chat] [env:Minewsemi_me25ls01_terminal_chat]
extends = me25ls01 extends = me25ls01
build_flags = ${me25ls01.build_flags} 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_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
@@ -126,7 +113,6 @@ build_flags = ${me25ls01.build_flags}
-D DISPLAY_CLASS=NullDisplayDriver -D DISPLAY_CLASS=NullDisplayDriver
build_src_filter = ${me25ls01.build_src_filter} build_src_filter = ${me25ls01.build_src_filter}
+<../examples/simple_secure_chat/main.cpp> +<../examples/simple_secure_chat/main.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
[env:Minewsemi_me25ls01_companion_radio_usb] [env:Minewsemi_me25ls01_companion_radio_usb]
extends = me25ls01 extends = me25ls01
@@ -137,7 +123,6 @@ build_flags = ${me25ls01.build_flags}
-D MAX_CONTACTS=350 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
;-D BLE_PIN_CODE=123456 ;-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
@@ -147,7 +132,6 @@ build_flags = ${me25ls01.build_flags}
-D ENABLE_USB_INTERFACE -D ENABLE_USB_INTERFACE
build_src_filter = ${me25ls01.build_src_filter} build_src_filter = ${me25ls01.build_src_filter}
+<helpers/nrf52/*.cpp> +<helpers/nrf52/*.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-orig/*.cpp> +<../examples/companion_radio/ui-orig/*.cpp>
+2 -2
View File
@@ -4,7 +4,6 @@
MinewsemiME25LS01Board board; MinewsemiME25LS01Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board); WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock rtc_clock; VolatileRTCClock rtc_clock;
@@ -18,7 +17,8 @@ extern EnvironmentSensorManager sensors;
#endif #endif
#ifdef DISPLAY_CLASS #ifdef DISPLAY_CLASS
NullDisplayDriver display; DISPLAY_CLASS display;
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
#endif #endif
#ifndef LORA_CR #ifndef LORA_CR
+5 -4
View File
@@ -11,16 +11,17 @@
#include <helpers/sensors/EnvironmentSensorManager.h> #include <helpers/sensors/EnvironmentSensorManager.h>
#ifdef DISPLAY_CLASS #ifdef DISPLAY_CLASS
#include <helpers/ui/NullDisplayDriver.h> #include <helpers/ui/NullDisplayDriver.h>
#endif #include <helpers/ui/MomentaryButton.h>
#ifdef DISPLAY_CLASS
extern NullDisplayDriver display;
#endif #endif
extern MinewsemiME25LS01Board board; extern MinewsemiME25LS01Board board;
extern WRAPPER_CLASS radio_driver; extern WRAPPER_CLASS radio_driver;
extern VolatileRTCClock rtc_clock; extern VolatileRTCClock rtc_clock;
extern EnvironmentSensorManager sensors; extern EnvironmentSensorManager sensors;
#ifdef DISPLAY_CLASS
extern DISPLAY_CLASS display;
extern MomentaryButton user_btn;
#endif
bool radio_init(); bool radio_init();
mesh::LocalIdentity radio_new_identity(); mesh::LocalIdentity radio_new_identity();
+1 -1
View File
@@ -10,6 +10,7 @@ build_flags = ${nrf52_base.build_flags}
-D NRF52_POWER_MANAGEMENT -D NRF52_POWER_MANAGEMENT
-D RADIO_CLASS=CustomSX1262 -D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper -D WRAPPER_CLASS=CustomSX1262Wrapper
-D DISPLAY_CLASS=NullDisplayDriver
-D LORA_TX_POWER=22 -D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=140 -D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1 -D SX126X_RX_BOOSTED_GAIN=1
@@ -100,7 +101,6 @@ build_src_filter = ${R1Neo.build_src_filter}
+<../examples/companion_radio/ui-orig/*.cpp> +<../examples/companion_radio/ui-orig/*.cpp>
lib_deps = lib_deps =
${R1Neo.lib_deps} ${R1Neo.lib_deps}
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0 densaugeo/base64 @ ~1.4.0
end2endzone/NonBlockingRTTTL@^1.3.0 end2endzone/NonBlockingRTTTL@^1.3.0
+8 -3
View File
@@ -4,12 +4,17 @@
R1NeoBoard board; R1NeoBoard board;
DISPLAY_CLASS display;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board); 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; VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock); AutoDiscoverRTCClock rtc_clock(fallback_clock);
-13
View File
@@ -4,19 +4,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <helpers/NRF52Board.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 PIN_VBAT_READ 17
#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking #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" #include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = { const uint32_t g_ADigitalPinMap[] = {
8, 6, 17, 20, 22, 24, 32, 11, 36, 38, 8, // P0.08 = 0
9, 10, 43, 45, 47, 2, 29, 31, 6, // P0.06 = 1
33, 34, 37, 17, // P0.17 = 2
13, 15 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() {}
{
}
+14
View File
@@ -79,4 +79,18 @@
#define PIN_BUTTON1 (6) #define PIN_BUTTON1 (6)
#define BUTTON_PIN PIN_BUTTON1 #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)
+5
View File
@@ -101,3 +101,8 @@ build_src_filter = ${SenseCap_Solar.build_src_filter}
lib_deps = lib_deps =
${SenseCap_Solar.lib_deps} ${SenseCap_Solar.lib_deps}
densaugeo/base64 @ ~1.4.0 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
View File
@@ -242,8 +242,6 @@ lib_deps =
[env:Station_G2_kiss_modem] [env:Station_G2_kiss_modem]
extends = Station_G2 extends = Station_G2
build_unflags =
-DARDUINO_USB_MODE=0
build_flags = build_flags =
${Station_G2.build_flags} ${Station_G2.build_flags}
-D ARDUINO_USB_MODE=1 -D ARDUINO_USB_MODE=1
@@ -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" #include "StationG3Board.h"
void StationG3Board::powerOff() { void StationG3Board::powerOff() {
loRaFEMControl.setSleepModeEnable();
#ifdef P_PA1_EN #ifdef P_PA1_EN
setPAModeHigh(false);
rtc_gpio_hold_en((gpio_num_t)P_PA1_EN); rtc_gpio_hold_en((gpio_num_t)P_PA1_EN);
#endif #endif
#ifdef P_PRIMARY_LNA_EN #ifdef P_PRIMARY_LNA_EN
setPrimaryLNAControl(true);
rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN); rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN);
#endif #endif
ESP32Board::powerOff(); 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 <Arduino.h>
#include <helpers/ESP32Board.h> #include <helpers/ESP32Board.h>
#include <driver/rtc_io.h> #include <driver/rtc_io.h>
#include "LoRaFEMControl.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
class StationG3Board : public ESP32Board { 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: public:
LoRaFEMControl loRaFEMControl;
void begin() { void begin() {
ESP32Board::begin(); ESP32Board::begin();
loRaFEMControl.init();
#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
esp_reset_reason_t reason = esp_reset_reason(); esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_DEEPSLEEP) { if (reason == ESP_RST_DEEPSLEEP) {
@@ -56,23 +26,30 @@ public:
} }
void setPrimaryLNAEnable(bool enabled) { void setPrimaryLNAEnable(bool enabled) {
setPrimaryLNAControl(enabled); loRaFEMControl.setLNAEnable(enabled);
} }
void setPrimaryPAHighPower(bool enabled) { void setPrimaryPAHighPower(bool enabled) {
setPAModeHigh(enabled); loRaFEMControl.setPAGainEnable(enabled);
} }
void onBeforeTransmit() override { void onBeforeTransmit() override {
ESP32Board::onBeforeTransmit(); ESP32Board::onBeforeTransmit();
setPrimaryLNAControl(false); loRaFEMControl.setTxModeEnable();
} }
void onAfterTransmit() override { void onAfterTransmit() override {
ESP32Board::onAfterTransmit(); 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; void powerOff() override;
uint16_t getBattMilliVolts() override { uint16_t getBattMilliVolts() override {
+2 -2
View File
@@ -17,11 +17,11 @@ build_flags =
-D P_LORA_SCLK=12 -D P_LORA_SCLK=12
-D P_LORA_MISO=14 -D P_LORA_MISO=14
-D P_LORA_MOSI=13 -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_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=10 ; Primary Slot LNA Mode: LOW/open is LNA on, HIGH/short is LNA off.
-D P_PRIMARY_LNA_EN_ACTIVE=LOW -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 MAX_LORA_TX_POWER=22
; -D P_LORA_TX_LED=35 ; -D P_LORA_TX_LED=35
-D PIN_BOARD_SDA=5 -D PIN_BOARD_SDA=5