Compare commits
37 Commits
v1.13.0-ev
...
v1.13.0-ev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4da42d02d4 | ||
|
|
16faa821eb | ||
|
|
08341eb27a | ||
|
|
4d352d646a | ||
|
|
aee066dd0f | ||
|
|
cdd3d5f34e | ||
|
|
7c594ebc50 | ||
|
|
ba3d9e264e | ||
|
|
d7ad89046b | ||
|
|
67779aded8 | ||
|
|
bbd621ba85 | ||
|
|
6431cd2d47 | ||
|
|
1d190ad944 | ||
|
|
2cb08775c0 | ||
|
|
c016db86d5 | ||
|
|
00566741f6 | ||
|
|
8a9a0dca5f | ||
|
|
59d9770ab9 | ||
|
|
9bae9d0ed2 | ||
|
|
85f764a114 | ||
|
|
f54948e06d | ||
|
|
b2032e11b6 | ||
|
|
9106ab46e1 | ||
|
|
a2dc2eb50c | ||
|
|
f81ec4b14c | ||
|
|
49d8313501 | ||
|
|
5a5568ed56 | ||
|
|
ac2aa03b09 | ||
|
|
70f1ad4aeb | ||
|
|
d9e67222f5 | ||
|
|
2bb6f636a4 | ||
|
|
329e408197 | ||
|
|
f864c5f547 | ||
|
|
b039af0b52 | ||
|
|
519b97a90a | ||
|
|
30d6588792 | ||
|
|
eb4fa032ff |
@@ -229,6 +229,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
|
|||||||
file.read((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
|
file.read((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
|
||||||
file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
|
file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
|
||||||
file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
|
file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
|
||||||
|
file.read((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
@@ -265,6 +266,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
|
|||||||
file.write((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
|
file.write((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
|
||||||
file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
|
file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
|
||||||
file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
|
file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
|
||||||
|
file.write((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,6 +318,10 @@ bool MyMesh::shouldOverwriteWhenFull() const {
|
|||||||
return (_prefs.autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) != 0;
|
return (_prefs.autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t MyMesh::getAutoAddMaxHops() const {
|
||||||
|
return _prefs.autoadd_max_hops;
|
||||||
|
}
|
||||||
|
|
||||||
void MyMesh::onContactOverwrite(const uint8_t* pub_key) {
|
void MyMesh::onContactOverwrite(const uint8_t* pub_key) {
|
||||||
_store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage
|
_store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage
|
||||||
if (_serial->isConnected()) {
|
if (_serial->isConnected()) {
|
||||||
@@ -807,7 +811,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;
|
||||||
@@ -1785,12 +1789,16 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||||||
}
|
}
|
||||||
} else if (cmd_frame[0] == CMD_SET_AUTOADD_CONFIG) {
|
} else if (cmd_frame[0] == CMD_SET_AUTOADD_CONFIG) {
|
||||||
_prefs.autoadd_config = cmd_frame[1];
|
_prefs.autoadd_config = cmd_frame[1];
|
||||||
|
if (len >= 3) {
|
||||||
|
_prefs.autoadd_max_hops = min(cmd_frame[2], (uint8_t)64);
|
||||||
|
}
|
||||||
savePrefs();
|
savePrefs();
|
||||||
writeOKFrame();
|
writeOKFrame();
|
||||||
} else if (cmd_frame[0] == CMD_GET_AUTOADD_CONFIG) {
|
} else if (cmd_frame[0] == CMD_GET_AUTOADD_CONFIG) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
out_frame[i++] = RESP_CODE_AUTOADD_CONFIG;
|
out_frame[i++] = RESP_CODE_AUTOADD_CONFIG;
|
||||||
out_frame[i++] = _prefs.autoadd_config;
|
out_frame[i++] = _prefs.autoadd_config;
|
||||||
|
out_frame[i++] = _prefs.autoadd_max_hops;
|
||||||
_serial->writeFrame(out_frame, i);
|
_serial->writeFrame(out_frame, i);
|
||||||
} else if (cmd_frame[0] == CMD_GET_ALLOWED_REPEAT_FREQ) {
|
} else if (cmd_frame[0] == CMD_GET_ALLOWED_REPEAT_FREQ) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ protected:
|
|||||||
bool isAutoAddEnabled() const override;
|
bool isAutoAddEnabled() const override;
|
||||||
bool shouldAutoAddContactType(uint8_t type) const override;
|
bool shouldAutoAddContactType(uint8_t type) const override;
|
||||||
bool shouldOverwriteWhenFull() const override;
|
bool shouldOverwriteWhenFull() const override;
|
||||||
|
uint8_t getAutoAddMaxHops() const override;
|
||||||
void onContactsFull() override;
|
void onContactsFull() override;
|
||||||
void onContactOverwrite(const uint8_t* pub_key) override;
|
void onContactOverwrite(const uint8_t* pub_key) override;
|
||||||
bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
|
bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
|
||||||
|
|||||||
@@ -30,4 +30,5 @@ struct NodePrefs { // persisted to file
|
|||||||
uint8_t autoadd_config; // bitmask for auto-add contacts config
|
uint8_t autoadd_config; // bitmask for auto-add contacts config
|
||||||
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)
|
||||||
};
|
};
|
||||||
@@ -514,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();
|
||||||
@@ -832,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
|
||||||
|
|||||||
@@ -608,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
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check hop limit for new contacts (0 = no limit, 1 = direct (0 hops), N = up to N-1 hops)
|
||||||
|
uint8_t max_hops = getAutoAddMaxHops();
|
||||||
|
if (max_hops > 0 && packet->getPathHashCount() >= max_hops) {
|
||||||
|
ContactInfo ci;
|
||||||
|
populateContactFromAdvert(ci, id, parser, timestamp);
|
||||||
|
onDiscoveredContact(ci, true, packet->path_len, packet->path); // let UI know
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
from = allocateContactSlot();
|
from = allocateContactSlot();
|
||||||
if (from == NULL) {
|
if (from == NULL) {
|
||||||
ContactInfo ci;
|
ContactInfo ci;
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ protected:
|
|||||||
virtual bool shouldAutoAddContactType(uint8_t type) const { return true; }
|
virtual bool shouldAutoAddContactType(uint8_t type) const { return true; }
|
||||||
virtual void onContactsFull() {};
|
virtual void onContactsFull() {};
|
||||||
virtual bool shouldOverwriteWhenFull() const { return false; }
|
virtual bool shouldOverwriteWhenFull() const { return false; }
|
||||||
|
virtual uint8_t getAutoAddMaxHops() const { return 0; } // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops
|
||||||
virtual void onContactOverwrite(const uint8_t* pub_key) {};
|
virtual void onContactOverwrite(const uint8_t* pub_key) {};
|
||||||
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0;
|
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0;
|
||||||
virtual ContactInfo* processAck(const uint8_t *data) = 0;
|
virtual ContactInfo* processAck(const uint8_t *data) = 0;
|
||||||
|
|||||||
@@ -766,7 +766,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
|||||||
_prefs->advert_loc_policy = ADVERT_LOC_SHARE;
|
_prefs->advert_loc_policy = ADVERT_LOC_SHARE;
|
||||||
savePrefs();
|
savePrefs();
|
||||||
strcpy(reply, "ok");
|
strcpy(reply, "ok");
|
||||||
} else if (memcmp(command+11, "prefs", 4) == 0) {
|
} else if (memcmp(command+11, "prefs", 5) == 0) {
|
||||||
_prefs->advert_loc_policy = ADVERT_LOC_PREFS;
|
_prefs->advert_loc_policy = ADVERT_LOC_PREFS;
|
||||||
savePrefs();
|
savePrefs();
|
||||||
strcpy(reply, "ok");
|
strcpy(reply, "ok");
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -707,7 +707,9 @@ void EnvironmentSensorManager::loop() {
|
|||||||
static long next_gps_update = 0;
|
static long next_gps_update = 0;
|
||||||
|
|
||||||
#if ENV_INCLUDE_GPS
|
#if ENV_INCLUDE_GPS
|
||||||
_location->loop();
|
if (gps_active) {
|
||||||
|
_location->loop();
|
||||||
|
}
|
||||||
if (millis() > next_gps_update) {
|
if (millis() > next_gps_update) {
|
||||||
|
|
||||||
if(gps_active){
|
if(gps_active){
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ public :
|
|||||||
if (_pin_en != -1) {
|
if (_pin_en != -1) {
|
||||||
digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE);
|
digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE);
|
||||||
}
|
}
|
||||||
if (_peripher_power) _peripher_power->release();
|
if (_pin_reset != -1) {
|
||||||
|
digitalWrite(_pin_reset, GPS_RESET_FORCE);
|
||||||
|
}
|
||||||
|
if (_peripher_power) _peripher_power->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEnabled() override {
|
bool isEnabled() override {
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ build_flags =
|
|||||||
-D PIN_TFT_LEDA_CTL=21 ; LEDK (switches on/off via mosfet to create the ground)
|
-D PIN_TFT_LEDA_CTL=21 ; LEDK (switches on/off via mosfet to create the ground)
|
||||||
-D PIN_GPS_RX=33
|
-D PIN_GPS_RX=33
|
||||||
-D PIN_GPS_TX=34
|
-D PIN_GPS_TX=34
|
||||||
|
-D PIN_GPS_EN=35 ; N-ch MOSFET Q2 drives P-ch high-side switch → active HIGH (default)
|
||||||
|
-D PIN_GPS_RESET=36
|
||||||
|
-D PIN_GPS_RESET_ACTIVE=LOW
|
||||||
|
-D GPS_BAUD_RATE=115200
|
||||||
|
-D ENV_INCLUDE_GPS=1
|
||||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||||
-D SX126X_CURRENT_LIMIT=140
|
-D SX126X_CURRENT_LIMIT=140
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ WRAPPER_CLASS radio_driver(radio, board);
|
|||||||
|
|
||||||
ESP32RTCClock fallback_clock;
|
ESP32RTCClock fallback_clock;
|
||||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
|
// GPS_EN (GPIO35) drives N-ch MOSFET → P-ch high-side switch; GPS_RESET (GPIO36) active LOW
|
||||||
|
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power);
|
||||||
HWTSensorManager sensors = HWTSensorManager(nmea);
|
HWTSensorManager sensors = HWTSensorManager(nmea);
|
||||||
|
|
||||||
#ifdef DISPLAY_CLASS
|
#ifdef DISPLAY_CLASS
|
||||||
@@ -58,18 +59,16 @@ mesh::LocalIdentity radio_new_identity() {
|
|||||||
|
|
||||||
void HWTSensorManager::start_gps() {
|
void HWTSensorManager::start_gps() {
|
||||||
if (!gps_active) {
|
if (!gps_active) {
|
||||||
board.periph_power.claim();
|
_location->begin(); // Claims periph_power via RefCountedDigitalPin
|
||||||
|
|
||||||
gps_active = true;
|
gps_active = true;
|
||||||
Serial1.println("$CFGSYS,h35155*68");
|
Serial1.println("$CFGSYS,h35155*68"); // Configure GPS for all constellations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HWTSensorManager::stop_gps() {
|
void HWTSensorManager::stop_gps() {
|
||||||
if (gps_active) {
|
if (gps_active) {
|
||||||
gps_active = false;
|
gps_active = false;
|
||||||
|
_location->stop(); // Releases periph_power via RefCountedDigitalPin
|
||||||
board.periph_power.release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public:
|
|||||||
const char* getSettingName(int i) const override;
|
const char* getSettingName(int i) const override;
|
||||||
const char* getSettingValue(int i) const override;
|
const char* getSettingValue(int i) const override;
|
||||||
bool setSettingValue(const char* name, const char* value) override;
|
bool setSettingValue(const char* name, const char* value) override;
|
||||||
|
LocationProvider* getLocationProvider() override { return _location; }
|
||||||
};
|
};
|
||||||
|
|
||||||
extern HeltecV3Board board;
|
extern HeltecV3Board board;
|
||||||
|
|||||||
@@ -6,18 +6,26 @@ void HeltecTrackerV2Board::begin() {
|
|||||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||||
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
||||||
|
|
||||||
|
// Set up digital GPIO registers before releasing RTC hold. The hold latches
|
||||||
|
// the pad state including function select, so register writes accumulate
|
||||||
|
// without affecting the pad. On hold release, all changes apply atomically
|
||||||
|
// (IO MUX switches to digital GPIO with output already HIGH — no glitch).
|
||||||
pinMode(P_LORA_PA_POWER, OUTPUT);
|
pinMode(P_LORA_PA_POWER, OUTPUT);
|
||||||
digitalWrite(P_LORA_PA_POWER,HIGH);
|
digitalWrite(P_LORA_PA_POWER,HIGH);
|
||||||
|
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER);
|
||||||
|
|
||||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN);
|
|
||||||
pinMode(P_LORA_PA_EN, OUTPUT);
|
pinMode(P_LORA_PA_EN, OUTPUT);
|
||||||
digitalWrite(P_LORA_PA_EN,HIGH);
|
digitalWrite(P_LORA_PA_EN,HIGH);
|
||||||
|
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN);
|
||||||
pinMode(P_LORA_PA_TX_EN, OUTPUT);
|
pinMode(P_LORA_PA_TX_EN, OUTPUT);
|
||||||
digitalWrite(P_LORA_PA_TX_EN,LOW);
|
digitalWrite(P_LORA_PA_TX_EN,LOW);
|
||||||
|
|
||||||
periph_power.begin();
|
|
||||||
|
|
||||||
esp_reset_reason_t reason = esp_reset_reason();
|
esp_reset_reason_t reason = esp_reset_reason();
|
||||||
|
if (reason != ESP_RST_DEEPSLEEP) {
|
||||||
|
delay(1); // GC1109 startup time after cold power-on
|
||||||
|
}
|
||||||
|
|
||||||
|
periph_power.begin();
|
||||||
if (reason == ESP_RST_DEEPSLEEP) {
|
if (reason == ESP_RST_DEEPSLEEP) {
|
||||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||||
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
||||||
@@ -48,7 +56,9 @@ void HeltecTrackerV2Board::begin() {
|
|||||||
|
|
||||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||||
|
|
||||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); //It also needs to be enabled in receive mode
|
// Hold GC1109 FEM pins during sleep to keep LNA active for RX wake
|
||||||
|
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER);
|
||||||
|
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN);
|
||||||
|
|
||||||
if (pin_wake_btn < 0) {
|
if (pin_wake_btn < 0) {
|
||||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ build_flags =
|
|||||||
-D P_LORA_SCLK=9
|
-D P_LORA_SCLK=9
|
||||||
-D P_LORA_MISO=11
|
-D P_LORA_MISO=11
|
||||||
-D P_LORA_MOSI=10
|
-D P_LORA_MOSI=10
|
||||||
-D P_LORA_PA_POWER=7 ;power en
|
-D P_LORA_PA_POWER=7 ; VFEM_Ctrl - GC1109 LDO power enable
|
||||||
-D P_LORA_PA_EN=4
|
-D P_LORA_PA_EN=4 ; CSD - GC1109 chip enable (HIGH=on)
|
||||||
-D P_LORA_PA_TX_EN=46 ;enable tx
|
-D P_LORA_PA_TX_EN=46 ; CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass)
|
||||||
-D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm.
|
-D LORA_TX_POWER=10 ; 10dBm + ~11dB GC1109 gain = ~21dBm output
|
||||||
-D MAX_LORA_TX_POWER=22 ;Max SX1262 output
|
-D MAX_LORA_TX_POWER=22 ; Max SX1262 output -> ~28dBm at antenna
|
||||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||||
-D SX126X_CURRENT_LIMIT=140
|
-D SX126X_CURRENT_LIMIT=140
|
||||||
|
|||||||
@@ -7,19 +7,26 @@ void HeltecV4Board::begin() {
|
|||||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||||
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
||||||
|
|
||||||
|
// Set up digital GPIO registers before releasing RTC hold. The hold latches
|
||||||
|
// the pad state including function select, so register writes accumulate
|
||||||
|
// without affecting the pad. On hold release, all changes apply atomically
|
||||||
|
// (IO MUX switches to digital GPIO with output already HIGH — no glitch).
|
||||||
pinMode(P_LORA_PA_POWER, OUTPUT);
|
pinMode(P_LORA_PA_POWER, OUTPUT);
|
||||||
digitalWrite(P_LORA_PA_POWER,HIGH);
|
digitalWrite(P_LORA_PA_POWER,HIGH);
|
||||||
|
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER);
|
||||||
|
|
||||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN);
|
|
||||||
pinMode(P_LORA_PA_EN, OUTPUT);
|
pinMode(P_LORA_PA_EN, OUTPUT);
|
||||||
digitalWrite(P_LORA_PA_EN,HIGH);
|
digitalWrite(P_LORA_PA_EN,HIGH);
|
||||||
|
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN);
|
||||||
pinMode(P_LORA_PA_TX_EN, OUTPUT);
|
pinMode(P_LORA_PA_TX_EN, OUTPUT);
|
||||||
digitalWrite(P_LORA_PA_TX_EN,LOW);
|
digitalWrite(P_LORA_PA_TX_EN,LOW);
|
||||||
|
|
||||||
|
esp_reset_reason_t reason = esp_reset_reason();
|
||||||
|
if (reason != ESP_RST_DEEPSLEEP) {
|
||||||
|
delay(1); // GC1109 startup time after cold power-on
|
||||||
|
}
|
||||||
|
|
||||||
periph_power.begin();
|
periph_power.begin();
|
||||||
|
|
||||||
esp_reset_reason_t reason = esp_reset_reason();
|
|
||||||
if (reason == ESP_RST_DEEPSLEEP) {
|
if (reason == ESP_RST_DEEPSLEEP) {
|
||||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||||
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
||||||
@@ -50,7 +57,9 @@ void HeltecV4Board::begin() {
|
|||||||
|
|
||||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||||
|
|
||||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); //It also needs to be enabled in receive mode
|
// Hold GC1109 FEM pins during sleep to keep LNA active for RX wake
|
||||||
|
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER);
|
||||||
|
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN);
|
||||||
|
|
||||||
if (pin_wake_btn < 0) {
|
if (pin_wake_btn < 0) {
|
||||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
||||||
|
|||||||
@@ -20,13 +20,29 @@ void RAK3401Board::begin() {
|
|||||||
|
|
||||||
Wire.begin();
|
Wire.begin();
|
||||||
|
|
||||||
|
// PIN_3V3_EN (WB_IO2, P0.34) controls the 3V3_S switched peripheral rail
|
||||||
|
// AND the 5V boost regulator (U5) on the RAK13302 that powers the SKY66122 PA.
|
||||||
|
// Must stay HIGH during radio operation — do not toggle for power saving.
|
||||||
pinMode(PIN_3V3_EN, OUTPUT);
|
pinMode(PIN_3V3_EN, OUTPUT);
|
||||||
digitalWrite(PIN_3V3_EN, HIGH);
|
digitalWrite(PIN_3V3_EN, HIGH);
|
||||||
|
|
||||||
#ifdef P_LORA_PA_EN
|
// Enable SKY66122-11 FEM on the RAK13302 module.
|
||||||
// Initialize RAK13302 1W LoRa transceiver module PA control pin
|
// CSD and CPS are tied together on the RAK13302 PCB, routed to IO3 (P0.21).
|
||||||
pinMode(P_LORA_PA_EN, OUTPUT);
|
// HIGH = FEM active (LNA for RX, PA path available for TX).
|
||||||
digitalWrite(P_LORA_PA_EN, LOW); // Start with PA disabled
|
// TX/RX switching (CTX) is handled by SX1262 DIO2 via SetDIO2AsRfSwitchCtrl.
|
||||||
delay(10); // Allow PA module to initialize
|
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||||
|
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||||
|
delay(1); // SKY66122 turn-on settling time (tON = 3us typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NRF52_POWER_MANAGEMENT
|
||||||
|
void RAK3401Board::initiateShutdown(uint8_t reason) {
|
||||||
|
// Disable SKY66122 FEM (CSD+CPS LOW = shutdown, <1 uA)
|
||||||
|
digitalWrite(SX126X_POWER_EN, LOW);
|
||||||
|
|
||||||
|
// Disable 3V3 switched peripherals and 5V boost
|
||||||
|
digitalWrite(PIN_3V3_EN, LOW);
|
||||||
|
|
||||||
|
enterSystemOff(reason);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
@@ -38,13 +38,6 @@ public:
|
|||||||
return "RAK 3401";
|
return "RAK 3401";
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef P_LORA_PA_EN
|
// TX/RX switching is handled by SX1262 DIO2 -> SKY66122 CTX (hardware-timed).
|
||||||
void onBeforeTransmit() override {
|
// No onBeforeTransmit/onAfterTransmit overrides needed.
|
||||||
digitalWrite(P_LORA_PA_EN, HIGH); // Enable PA before transmission
|
|
||||||
}
|
|
||||||
|
|
||||||
void onAfterTransmit() override {
|
|
||||||
digitalWrite(P_LORA_PA_EN, LOW); // Disable PA after transmission to save power
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ build_flags = ${nrf52_base.build_flags}
|
|||||||
-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
|
||||||
|
-D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX with SKY66122 FEM
|
||||||
build_src_filter = ${nrf52_base.build_src_filter}
|
build_src_filter = ${nrf52_base.build_src_filter}
|
||||||
+<../variants/rak3401>
|
+<../variants/rak3401>
|
||||||
+<helpers/sensors>
|
+<helpers/sensors>
|
||||||
|
|||||||
@@ -147,8 +147,15 @@ static const uint8_t AREF = PIN_AREF;
|
|||||||
#define SX126X_BUSY (9)
|
#define SX126X_BUSY (9)
|
||||||
#define SX126X_RESET (4)
|
#define SX126X_RESET (4)
|
||||||
|
|
||||||
#define SX126X_POWER_EN (21)
|
// SKY66122-11 FEM control on the RAK13302 module:
|
||||||
// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
|
// CSD + CPS are tied together on the PCB, routed to WisBlock IO3 (P0.21).
|
||||||
|
// Setting IO3 HIGH enables the FEM (LNA for RX, PA path for TX).
|
||||||
|
// CTX is connected to SX1262 DIO2 — the radio handles TX/RX switching
|
||||||
|
// in hardware via SetDIO2AsRfSwitchCtrl (microsecond-accurate, no GPIO needed).
|
||||||
|
// The 5V boost for the PA is enabled by WB_IO2 (P0.34 = PIN_3V3_EN).
|
||||||
|
#define SX126X_POWER_EN (21) // P0.21 = IO3 -> SKY66122 CSD+CPS (FEM enable)
|
||||||
|
|
||||||
|
// CTX is driven by SX1262 DIO2, not a GPIO
|
||||||
#define SX126X_DIO2_AS_RF_SWITCH
|
#define SX126X_DIO2_AS_RF_SWITCH
|
||||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||||
|
|
||||||
@@ -159,7 +166,6 @@ static const uint8_t AREF = PIN_AREF;
|
|||||||
#define P_LORA_DIO_1 SX126X_DIO1
|
#define P_LORA_DIO_1 SX126X_DIO1
|
||||||
#define P_LORA_BUSY SX126X_BUSY
|
#define P_LORA_BUSY SX126X_BUSY
|
||||||
#define P_LORA_RESET SX126X_RESET
|
#define P_LORA_RESET SX126X_RESET
|
||||||
#define P_LORA_PA_EN 31
|
|
||||||
|
|
||||||
// enables 3.3V periphery like GPS or IO Module
|
// enables 3.3V periphery like GPS or IO Module
|
||||||
// Do not toggle this for GPS power savings
|
// Do not toggle this for GPS power savings
|
||||||
|
|||||||
@@ -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