Compare commits
22 Commits
pr-1338
...
v1.13.0-ev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e22e63c9f8 | ||
|
|
81406cdb25 | ||
|
|
baa17d204b | ||
|
|
cf5c268cfd | ||
|
|
1ae9b18e10 | ||
|
|
291eb6d305 | ||
|
|
37d3afc17e | ||
|
|
516d784087 | ||
|
|
3088d1862a | ||
|
|
6f1e01a750 | ||
|
|
87717610f5 | ||
|
|
dcfc83fb3d | ||
|
|
b90da8e1c0 | ||
|
|
cb2ebbf2f8 | ||
|
|
34c1f13d55 | ||
|
|
bb5bdcf9e5 | ||
|
|
de9100785e | ||
|
|
f864c5f547 | ||
|
|
b039af0b52 | ||
|
|
519b97a90a | ||
|
|
30d6588792 | ||
|
|
eb4fa032ff |
@@ -807,7 +807,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
|||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
memset(&_prefs, 0, sizeof(_prefs));
|
memset(&_prefs, 0, sizeof(_prefs));
|
||||||
_prefs.airtime_factor = 1.0; // one half
|
_prefs.airtime_factor = 1.0;
|
||||||
strcpy(_prefs.node_name, "NONAME");
|
strcpy(_prefs.node_name, "NONAME");
|
||||||
_prefs.freq = LORA_FREQ;
|
_prefs.freq = LORA_FREQ;
|
||||||
_prefs.sf = LORA_SF;
|
_prefs.sf = LORA_SF;
|
||||||
|
|||||||
@@ -403,6 +403,14 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
|
|||||||
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");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1)
|
||||||
|
// https://github.com/meshcore-dev/MeshCore/issues/1223
|
||||||
|
double_t roll_dice = (double)rand() / RAND_MAX;
|
||||||
|
double_t forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
|
||||||
|
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// all other packets
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +514,10 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
|||||||
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||||
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
|
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
|
||||||
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
|
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
|
||||||
if (region_map.getWildcard().flags & REGION_DENY_FLOOD) {
|
if ((pkt->getPayloadType() == PAYLOAD_TYPE_GRP_TXT ||
|
||||||
|
pkt->getPayloadType() == PAYLOAD_TYPE_GRP_DATA ||
|
||||||
|
pkt->getPayloadType() == PAYLOAD_TYPE_ADVERT) &&
|
||||||
|
region_map.getWildcard().flags & REGION_DENY_FLOOD) {
|
||||||
recv_pkt_region = NULL;
|
recv_pkt_region = NULL;
|
||||||
} else {
|
} else {
|
||||||
recv_pkt_region = ®ion_map.getWildcard();
|
recv_pkt_region = ®ion_map.getWildcard();
|
||||||
@@ -824,7 +835,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
memset(&_prefs, 0, sizeof(_prefs));
|
memset(&_prefs, 0, sizeof(_prefs));
|
||||||
_prefs.airtime_factor = 1.0; // one half
|
_prefs.airtime_factor = 1.0;
|
||||||
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
|
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
|
||||||
_prefs.tx_delay_factor = 0.5f; // was 0.25f
|
_prefs.tx_delay_factor = 0.5f; // was 0.25f
|
||||||
_prefs.direct_tx_delay_factor = 0.3f; // was 0.2
|
_prefs.direct_tx_delay_factor = 0.3f; // was 0.2
|
||||||
@@ -838,7 +849,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
_prefs.cr = LORA_CR;
|
_prefs.cr = LORA_CR;
|
||||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||||
_prefs.flood_advert_interval = 12; // 12 hours
|
_prefs.flood_advert_interval = 0; // 12 hours
|
||||||
|
_prefs.flood_advert_base = 0.308f;
|
||||||
_prefs.flood_max = 64;
|
_prefs.flood_max = 64;
|
||||||
_prefs.interference_threshold = 0; // disabled
|
_prefs.interference_threshold = 0; // disabled
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,15 @@ 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() && packet->getPathHashCount() >= _prefs.flood_max) return false;
|
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
|
||||||
|
|
||||||
|
// Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1)
|
||||||
|
// https://github.com/meshcore-dev/MeshCore/issues/1223
|
||||||
|
double_t roll_dice = (double)rand() / RAND_MAX;
|
||||||
|
double_t forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
|
||||||
|
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// all other packets
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,7 +608,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
memset(&_prefs, 0, sizeof(_prefs));
|
memset(&_prefs, 0, sizeof(_prefs));
|
||||||
_prefs.airtime_factor = 1.0; // one half
|
_prefs.airtime_factor = 1.0;
|
||||||
_prefs.rx_delay_base = 0.0f; // off by default, was 10.0
|
_prefs.rx_delay_base = 0.0f; // off by default, was 10.0
|
||||||
_prefs.tx_delay_factor = 0.5f; // was 0.25f;
|
_prefs.tx_delay_factor = 0.5f; // was 0.25f;
|
||||||
_prefs.direct_tx_delay_factor = 0.2f; // was zero
|
_prefs.direct_tx_delay_factor = 0.2f; // was zero
|
||||||
@@ -614,7 +623,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||||
_prefs.disable_fwd = 1;
|
_prefs.disable_fwd = 1;
|
||||||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||||
_prefs.flood_advert_interval = 12; // 12 hours
|
_prefs.flood_advert_interval = 0; // 12 hours
|
||||||
|
_prefs.flood_advert_base = 0.308f;
|
||||||
_prefs.flood_max = 64;
|
_prefs.flood_max = 64;
|
||||||
_prefs.interference_threshold = 0; // disabled
|
_prefs.interference_threshold = 0; // disabled
|
||||||
#ifdef ROOM_PASSWORD
|
#ifdef ROOM_PASSWORD
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ public:
|
|||||||
{
|
{
|
||||||
// defaults
|
// defaults
|
||||||
memset(&_prefs, 0, sizeof(_prefs));
|
memset(&_prefs, 0, sizeof(_prefs));
|
||||||
_prefs.airtime_factor = 2.0; // one third
|
_prefs.airtime_factor = 1.0;
|
||||||
strcpy(_prefs.node_name, "NONAME");
|
strcpy(_prefs.node_name, "NONAME");
|
||||||
_prefs.freq = LORA_FREQ;
|
_prefs.freq = LORA_FREQ;
|
||||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||||
|
|||||||
@@ -706,7 +706,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
|||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
memset(&_prefs, 0, sizeof(_prefs));
|
memset(&_prefs, 0, sizeof(_prefs));
|
||||||
_prefs.airtime_factor = 1.0; // one half
|
_prefs.airtime_factor = 1.0;
|
||||||
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
|
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
|
||||||
_prefs.tx_delay_factor = 0.5f; // was 0.25f
|
_prefs.tx_delay_factor = 0.5f; // was 0.25f
|
||||||
_prefs.direct_tx_delay_factor = 0.2f; // was zero
|
_prefs.direct_tx_delay_factor = 0.2f; // was zero
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
namespace mesh {
|
namespace mesh {
|
||||||
|
|
||||||
#define MAX_RX_DELAY_MILLIS 32000 // 32 seconds
|
#define MAX_RX_DELAY_MILLIS 32000 // 32 seconds
|
||||||
|
#define MIN_TX_BUDGET_RESERVE_MS 100 // min budget (ms) required before allowing next TX
|
||||||
|
#define MIN_TX_BUDGET_AIRTIME_DIV 2 // require at least 1/N of estimated airtime as budget before TX
|
||||||
|
|
||||||
#ifndef NOISE_FLOOR_CALIB_INTERVAL
|
#ifndef NOISE_FLOOR_CALIB_INTERVAL
|
||||||
#define NOISE_FLOOR_CALIB_INTERVAL 2000 // 2 seconds
|
#define NOISE_FLOOR_CALIB_INTERVAL 2000 // 2 seconds
|
||||||
@@ -20,12 +22,34 @@ void Dispatcher::begin() {
|
|||||||
_err_flags = 0;
|
_err_flags = 0;
|
||||||
radio_nonrx_start = _ms->getMillis();
|
radio_nonrx_start = _ms->getMillis();
|
||||||
|
|
||||||
|
duty_cycle_window_ms = getDutyCycleWindowMs();
|
||||||
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||||
|
tx_budget_ms = (unsigned long)(duty_cycle_window_ms * duty_cycle);
|
||||||
|
last_budget_update = _ms->getMillis();
|
||||||
|
|
||||||
_radio->begin();
|
_radio->begin();
|
||||||
prev_isrecv_mode = _radio->isInRecvMode();
|
prev_isrecv_mode = _radio->isInRecvMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Dispatcher::getAirtimeBudgetFactor() const {
|
float Dispatcher::getAirtimeBudgetFactor() const {
|
||||||
return 2.0; // default, 33.3% (1/3rd)
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dispatcher::updateTxBudget() {
|
||||||
|
unsigned long now = _ms->getMillis();
|
||||||
|
unsigned long elapsed = now - last_budget_update;
|
||||||
|
|
||||||
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||||
|
unsigned long max_budget = (unsigned long)(getDutyCycleWindowMs() * duty_cycle);
|
||||||
|
unsigned long refill = (unsigned long)(elapsed * duty_cycle);
|
||||||
|
|
||||||
|
if (refill > 0) {
|
||||||
|
tx_budget_ms += refill;
|
||||||
|
if (tx_budget_ms > max_budget) {
|
||||||
|
tx_budget_ms = max_budget;
|
||||||
|
}
|
||||||
|
last_budget_update = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Dispatcher::calcRxDelay(float score, uint32_t air_time) const {
|
int Dispatcher::calcRxDelay(float score, uint32_t air_time) const {
|
||||||
@@ -61,11 +85,24 @@ void Dispatcher::loop() {
|
|||||||
if (outbound) { // waiting for outbound send to be completed
|
if (outbound) { // waiting for outbound send to be completed
|
||||||
if (_radio->isSendComplete()) {
|
if (_radio->isSendComplete()) {
|
||||||
long t = _ms->getMillis() - outbound_start;
|
long t = _ms->getMillis() - outbound_start;
|
||||||
total_air_time += t; // keep track of how much air time we are using
|
total_air_time += t;
|
||||||
//Serial.print(" airtime="); Serial.println(t);
|
//Serial.print(" airtime="); Serial.println(t);
|
||||||
|
|
||||||
// will need radio silence up to next_tx_time
|
updateTxBudget();
|
||||||
next_tx_time = futureMillis(t * getAirtimeBudgetFactor());
|
|
||||||
|
if (t > tx_budget_ms) {
|
||||||
|
tx_budget_ms = 0;
|
||||||
|
} else {
|
||||||
|
tx_budget_ms -= t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_budget_ms < MIN_TX_BUDGET_RESERVE_MS) {
|
||||||
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||||
|
unsigned long needed = MIN_TX_BUDGET_RESERVE_MS - tx_budget_ms;
|
||||||
|
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle));
|
||||||
|
} else {
|
||||||
|
next_tx_time = _ms->getMillis();
|
||||||
|
}
|
||||||
|
|
||||||
_radio->onSendFinished();
|
_radio->onSendFinished();
|
||||||
logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len);
|
logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len);
|
||||||
@@ -235,9 +272,20 @@ void Dispatcher::processRecvPacket(Packet* pkt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Dispatcher::checkSend() {
|
void Dispatcher::checkSend() {
|
||||||
if (_mgr->getOutboundCount(_ms->getMillis()) == 0) return; // nothing waiting to send
|
if (_mgr->getOutboundCount(_ms->getMillis()) == 0) return;
|
||||||
if (!millisHasNowPassed(next_tx_time)) return; // still in 'radio silence' phase (from airtime budget setting)
|
|
||||||
if (_radio->isReceiving()) { // LBT - check if radio is currently mid-receive, or if channel activity
|
updateTxBudget();
|
||||||
|
|
||||||
|
uint32_t est_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT);
|
||||||
|
if (tx_budget_ms < est_airtime / MIN_TX_BUDGET_AIRTIME_DIV) {
|
||||||
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||||
|
unsigned long needed = est_airtime / MIN_TX_BUDGET_AIRTIME_DIV - tx_budget_ms;
|
||||||
|
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!millisHasNowPassed(next_tx_time)) return;
|
||||||
|
if (_radio->isReceiving()) {
|
||||||
if (cad_busy_start == 0) {
|
if (cad_busy_start == 0) {
|
||||||
cad_busy_start = _ms->getMillis(); // record when CAD busy state started
|
cad_busy_start = _ms->getMillis(); // record when CAD busy state started
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,8 +122,12 @@ class Dispatcher {
|
|||||||
bool prev_isrecv_mode;
|
bool prev_isrecv_mode;
|
||||||
uint32_t n_sent_flood, n_sent_direct;
|
uint32_t n_sent_flood, n_sent_direct;
|
||||||
uint32_t n_recv_flood, n_recv_direct;
|
uint32_t n_recv_flood, n_recv_direct;
|
||||||
|
unsigned long tx_budget_ms;
|
||||||
|
unsigned long last_budget_update;
|
||||||
|
unsigned long duty_cycle_window_ms;
|
||||||
|
|
||||||
void processRecvPacket(Packet* pkt);
|
void processRecvPacket(Packet* pkt);
|
||||||
|
void updateTxBudget();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PacketManager* _mgr;
|
PacketManager* _mgr;
|
||||||
@@ -142,6 +146,9 @@ protected:
|
|||||||
_err_flags = 0;
|
_err_flags = 0;
|
||||||
radio_nonrx_start = 0;
|
radio_nonrx_start = 0;
|
||||||
prev_isrecv_mode = true;
|
prev_isrecv_mode = true;
|
||||||
|
tx_budget_ms = 0;
|
||||||
|
last_budget_update = 0;
|
||||||
|
duty_cycle_window_ms = 3600000;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
|
virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
|
||||||
@@ -159,6 +166,7 @@ protected:
|
|||||||
virtual uint32_t getCADFailMaxDuration() const;
|
virtual uint32_t getCADFailMaxDuration() const;
|
||||||
virtual int getInterferenceThreshold() const { return 0; } // disabled by default
|
virtual int getInterferenceThreshold() const { return 0; } // disabled by default
|
||||||
virtual int getAGCResetInterval() const { return 0; } // disabled by default
|
virtual int getAGCResetInterval() const { return 0; } // disabled by default
|
||||||
|
virtual unsigned long getDutyCycleWindowMs() const { return 3600000; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void begin();
|
void begin();
|
||||||
@@ -168,8 +176,9 @@ public:
|
|||||||
void releasePacket(Packet* packet);
|
void releasePacket(Packet* packet);
|
||||||
void sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0);
|
void sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0);
|
||||||
|
|
||||||
unsigned long getTotalAirTime() const { return total_air_time; } // in milliseconds
|
unsigned long getTotalAirTime() const { return total_air_time; }
|
||||||
unsigned long getReceiveAirTime() const {return rx_air_time; }
|
unsigned long getReceiveAirTime() const {return rx_air_time; }
|
||||||
|
unsigned long getRemainingTxBudget() const { return tx_budget_ms; }
|
||||||
uint32_t getNumSentFlood() const { return n_sent_flood; }
|
uint32_t getNumSentFlood() const { return n_sent_flood; }
|
||||||
uint32_t getNumSentDirect() const { return n_sent_direct; }
|
uint32_t getNumSentDirect() const { return n_sent_direct; }
|
||||||
uint32_t getNumRecvFlood() const { return n_recv_flood; }
|
uint32_t getNumRecvFlood() const { return n_recv_flood; }
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||||||
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||||
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||||
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||||
// 290
|
file.read((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
|
||||||
|
|
||||||
|
// 294
|
||||||
|
|
||||||
// sanitise bad pref values
|
// sanitise bad pref values
|
||||||
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
||||||
@@ -110,6 +112,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||||||
_prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1);
|
_prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1);
|
||||||
_prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2);
|
_prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2);
|
||||||
|
|
||||||
|
_prefs->flood_advert_base = constrain(_prefs->flood_advert_base, 0, 1);
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +172,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
|||||||
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||||
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||||
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||||
// 290
|
file.write((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
|
||||||
|
|
||||||
|
// 294
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
@@ -385,6 +391,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
|||||||
} else {
|
} else {
|
||||||
sprintf(reply, "> %.3f", adc_mult);
|
sprintf(reply, "> %.3f", adc_mult);
|
||||||
}
|
}
|
||||||
|
} else if (memcmp(config, "flood.advert.base", 17) == 0) {
|
||||||
|
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->flood_advert_base));
|
||||||
// Power management commands
|
// Power management commands
|
||||||
} else if (memcmp(config, "pwrmgt.support", 14) == 0) {
|
} else if (memcmp(config, "pwrmgt.support", 14) == 0) {
|
||||||
#ifdef NRF52_POWER_MANAGEMENT
|
#ifdef NRF52_POWER_MANAGEMENT
|
||||||
@@ -642,6 +650,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
|||||||
_prefs->adc_multiplier = 0.0f;
|
_prefs->adc_multiplier = 0.0f;
|
||||||
strcpy(reply, "Error: unsupported by this board");
|
strcpy(reply, "Error: unsupported by this board");
|
||||||
};
|
};
|
||||||
|
} else if (memcmp(config, "flood.advert.base ", 18) == 0) {
|
||||||
|
float f = atof(&config[18]);
|
||||||
|
if((f > 0) || (f<1)) {
|
||||||
|
_prefs->flood_advert_base = f;
|
||||||
|
savePrefs();
|
||||||
|
strcpy(reply, "OK");
|
||||||
|
} else {
|
||||||
|
strcpy(reply, "Error: base must be between 0 and 1");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sprintf(reply, "unknown config: %s", config);
|
sprintf(reply, "unknown config: %s", config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ struct NodePrefs { // persisted to file
|
|||||||
uint8_t flood_max;
|
uint8_t flood_max;
|
||||||
uint8_t interference_threshold;
|
uint8_t interference_threshold;
|
||||||
uint8_t agc_reset_interval; // secs / 4
|
uint8_t agc_reset_interval; // secs / 4
|
||||||
|
float flood_advert_base;
|
||||||
// Bridge settings
|
// Bridge settings
|
||||||
uint8_t bridge_enabled; // boolean
|
uint8_t bridge_enabled; // boolean
|
||||||
uint16_t bridge_delay; // milliseconds (default 500 ms)
|
uint16_t bridge_delay; // milliseconds (default 500 ms)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CustomLLCC68.h"
|
#include "CustomLLCC68.h"
|
||||||
#include "RadioLibWrappers.h"
|
#include "RadioLibWrappers.h"
|
||||||
|
#include "SX126xReset.h"
|
||||||
|
|
||||||
class CustomLLCC68Wrapper : public RadioLibWrapper {
|
class CustomLLCC68Wrapper : public RadioLibWrapper {
|
||||||
public:
|
public:
|
||||||
@@ -19,4 +20,6 @@ public:
|
|||||||
int sf = ((CustomLLCC68 *)_radio)->spreadingFactor;
|
int sf = ((CustomLLCC68 *)_radio)->spreadingFactor;
|
||||||
return packetScoreInt(snr, sf, packet_len);
|
return packetScoreInt(snr, sf, packet_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ class CustomLR1110 : public LR1110 {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getFreqMHz() const { return freqMHz; }
|
||||||
|
|
||||||
bool isReceiving() {
|
bool isReceiving() {
|
||||||
uint16_t irq = getIrqStatus();
|
uint16_t irq = getIrqStatus();
|
||||||
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
#include "CustomLR1110.h"
|
#include "CustomLR1110.h"
|
||||||
#include "RadioLibWrappers.h"
|
#include "RadioLibWrappers.h"
|
||||||
|
#include "LR11x0Reset.h"
|
||||||
|
|
||||||
class CustomLR1110Wrapper : public RadioLibWrapper {
|
class CustomLR1110Wrapper : public RadioLibWrapper {
|
||||||
public:
|
public:
|
||||||
CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||||
bool isReceivingPacket() override {
|
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); }
|
||||||
|
bool isReceivingPacket() override {
|
||||||
return ((CustomLR1110 *)_radio)->isReceiving();
|
return ((CustomLR1110 *)_radio)->isReceiving();
|
||||||
}
|
}
|
||||||
float getCurrentRSSI() override {
|
float getCurrentRSSI() override {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CustomSTM32WLx.h"
|
#include "CustomSTM32WLx.h"
|
||||||
#include "RadioLibWrappers.h"
|
#include "RadioLibWrappers.h"
|
||||||
|
#include "SX126xReset.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
class CustomSTM32WLxWrapper : public RadioLibWrapper {
|
class CustomSTM32WLxWrapper : public RadioLibWrapper {
|
||||||
@@ -20,4 +21,6 @@ public:
|
|||||||
int sf = ((CustomSTM32WLx *)_radio)->spreadingFactor;
|
int sf = ((CustomSTM32WLx *)_radio)->spreadingFactor;
|
||||||
return packetScoreInt(snr, sf, packet_len);
|
return packetScoreInt(snr, sf, packet_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CustomSX1262.h"
|
#include "CustomSX1262.h"
|
||||||
#include "RadioLibWrappers.h"
|
#include "RadioLibWrappers.h"
|
||||||
|
#include "SX126xReset.h"
|
||||||
|
|
||||||
class CustomSX1262Wrapper : public RadioLibWrapper {
|
class CustomSX1262Wrapper : public RadioLibWrapper {
|
||||||
public:
|
public:
|
||||||
@@ -22,4 +23,6 @@ public:
|
|||||||
virtual void powerOff() override {
|
virtual void powerOff() override {
|
||||||
((CustomSX1262 *)_radio)->sleep(false);
|
((CustomSX1262 *)_radio)->sleep(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CustomSX1268.h"
|
#include "CustomSX1268.h"
|
||||||
#include "RadioLibWrappers.h"
|
#include "RadioLibWrappers.h"
|
||||||
|
#include "SX126xReset.h"
|
||||||
|
|
||||||
class CustomSX1268Wrapper : public RadioLibWrapper {
|
class CustomSX1268Wrapper : public RadioLibWrapper {
|
||||||
public:
|
public:
|
||||||
@@ -19,4 +20,6 @@ public:
|
|||||||
int sf = ((CustomSX1268 *)_radio)->spreadingFactor;
|
int sf = ((CustomSX1268 *)_radio)->spreadingFactor;
|
||||||
return packetScoreInt(snr, sf, packet_len);
|
return packetScoreInt(snr, sf, packet_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||||
};
|
};
|
||||||
|
|||||||
21
src/helpers/radiolib/LR11x0Reset.h
Normal file
21
src/helpers/radiolib/LR11x0Reset.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <RadioLib.h>
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
radio->sleep(true, 0);
|
||||||
|
radio->standby(RADIOLIB_LR11X0_STANDBY_RC, true);
|
||||||
|
|
||||||
|
radio->calibrate(RADIOLIB_LR11X0_CALIBRATE_ALL);
|
||||||
|
|
||||||
|
// calibrate(0x3F) defaults image calibration to 902-928MHz band.
|
||||||
|
// Re-calibrate for the actual operating frequency (band=4MHz matches RadioLib default).
|
||||||
|
radio->calibrateImageRejection(freqMHz - 4.0f, freqMHz + 4.0f);
|
||||||
|
|
||||||
|
#ifdef RX_BOOSTED_GAIN
|
||||||
|
radio->setRxBoostedGainMode(RX_BOOSTED_GAIN);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -53,13 +53,24 @@ void RadioLibWrapper::triggerNoiseFloorCalibrate(int threshold) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioLibWrapper::doResetAGC() {
|
||||||
|
_radio->sleep(); // warm sleep to reset analog frontend
|
||||||
|
}
|
||||||
|
|
||||||
void RadioLibWrapper::resetAGC() {
|
void RadioLibWrapper::resetAGC() {
|
||||||
// make sure we're not mid-receive of packet!
|
// make sure we're not mid-receive of packet!
|
||||||
if ((state & STATE_INT_READY) != 0 || isReceivingPacket()) return;
|
if ((state & STATE_INT_READY) != 0 || isReceivingPacket()) return;
|
||||||
|
|
||||||
// NOTE: according to higher powers, just issuing RadioLib's startReceive() will reset the AGC.
|
doResetAGC();
|
||||||
// revisit this if a better impl is discovered.
|
|
||||||
state = STATE_IDLE; // trigger a startReceive()
|
state = STATE_IDLE; // trigger a startReceive()
|
||||||
|
|
||||||
|
// Reset noise floor sampling so it reconverges from scratch.
|
||||||
|
// Without this, a stuck _noise_floor of -120 makes the sampling threshold
|
||||||
|
// too low (-106) to accept normal samples (~-105), self-reinforcing the
|
||||||
|
// stuck value even after the receiver has recovered.
|
||||||
|
_noise_floor = 0;
|
||||||
|
_num_floor_samples = 0;
|
||||||
|
_floor_sample_sum = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioLibWrapper::loop() {
|
void RadioLibWrapper::loop() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ protected:
|
|||||||
void startRecv();
|
void startRecv();
|
||||||
float packetScoreInt(float snr, int sf, int packet_len);
|
float packetScoreInt(float snr, int sf, int packet_len);
|
||||||
virtual bool isReceivingPacket() =0;
|
virtual bool isReceivingPacket() =0;
|
||||||
|
virtual void doResetAGC();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; }
|
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; }
|
||||||
|
|||||||
37
src/helpers/radiolib/SX126xReset.h
Normal file
37
src/helpers/radiolib/SX126xReset.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <RadioLib.h>
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
radio->sleep(true);
|
||||||
|
radio->standby(RADIOLIB_SX126X_STANDBY_RC, true);
|
||||||
|
|
||||||
|
uint8_t calData = RADIOLIB_SX126X_CALIBRATE_ALL;
|
||||||
|
radio->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, &calData, 1, true, false);
|
||||||
|
radio->mod->hal->delay(5);
|
||||||
|
uint32_t start = millis();
|
||||||
|
while (radio->mod->hal->digitalRead(radio->mod->getGpio())) {
|
||||||
|
if (millis() - start > 50) break;
|
||||||
|
radio->mod->hal->yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calibrate(0x7F) defaults image calibration to 902-928MHz band.
|
||||||
|
// Re-calibrate for the actual operating frequency.
|
||||||
|
radio->calibrateImage(radio->freqMHz);
|
||||||
|
|
||||||
|
#ifdef SX126X_DIO2_AS_RF_SWITCH
|
||||||
|
radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
|
||||||
|
#endif
|
||||||
|
#ifdef SX126X_RX_BOOSTED_GAIN
|
||||||
|
radio->setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
|
||||||
|
#endif
|
||||||
|
#ifdef SX126X_REGISTER_PATCH
|
||||||
|
uint8_t r_data = 0;
|
||||||
|
radio->readRegister(0x8B5, &r_data, 1);
|
||||||
|
r_data |= 0x01;
|
||||||
|
radio->writeRegister(0x8B5, &r_data, 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -106,7 +106,7 @@ extern "C"
|
|||||||
|
|
||||||
// Power management boot protection threshold (millivolts)
|
// Power management boot protection threshold (millivolts)
|
||||||
// Set to 0 to disable boot protection
|
// Set to 0 to disable boot protection
|
||||||
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV)
|
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV)
|
||||||
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
|
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
|
||||||
// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ
|
// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ
|
||||||
#define PWRMGT_LPCOMP_AIN 3
|
#define PWRMGT_LPCOMP_AIN 3
|
||||||
|
|||||||
Reference in New Issue
Block a user