Compare commits

..

18 Commits

Author SHA1 Message Date
Matthias Wientapper
e22e63c9f8 Disable flood adverts 2026-02-28 15:55:34 +01:00
Matthias Wientapper
81406cdb25 Set RAK boot lock voltage to 0V 2026-02-28 15:55:34 +01:00
Matthias Wientapper
baa17d204b Integration of upstrem PR #1810 2026-02-28 15:55:34 +01:00
Matthias Wientapper
cf5c268cfd Integration of upstrem PR #1743 2026-02-28 15:55:34 +01:00
Matthias Wientapper
1ae9b18e10 Integration of upstrem PR #1338 2026-02-28 15:55:34 +01:00
Matthias Wientapper
291eb6d305 Integration of upstrem PR #1297 2026-02-28 15:55:34 +01:00
Matthias Wientapper
37d3afc17e Disable flood_adverts per default 2026-02-28 15:49:52 +01:00
Matthias Wientapper
516d784087 Add cli config flood.advert.base
0 = forwarding flood adverts off
1 = forwarding flood adverts on (unrestricted)
0.308 (default) = prob. forwarding according to #1338
2026-02-28 15:29:20 +01:00
Matthias Wientapper
3088d1862a Limit flood advert packet forwarding for roomservers as well 2026-02-28 15:29:20 +01:00
Matthias Wientapper
6f1e01a750 Limit flood advert packet forwarding, implements #1223 2026-02-28 15:25:43 +01:00
Wessel Nieboer
87717610f5 fix comment, we know the band now after checking LR1110 user manual 2026-02-26 23:16:02 +01:00
Wessel Nieboer
dcfc83fb3d Calibrate configured frequency for AGC reset 2026-02-26 23:16:02 +01:00
Wessel Nieboer
b90da8e1c0 Also implement LR11x10 AGC reset
Similar to SX126x but simpler.
2026-02-26 23:16:01 +01:00
Wessel Nieboer
cb2ebbf2f8 make it more dry 2026-02-26 23:16:01 +01:00
Wessel Nieboer
34c1f13d55 reset noise_floor sampling after agc reset 2026-02-26 23:16:01 +01:00
Wessel Nieboer
bb5bdcf9e5 when doing AGC reset, call Calibrate(0x7F)
1. warm sleep
2. wake to stdby
3. Calibrate(0x7F) to reset all internal blocks
4. re-apply DIO2 RF / boosted gain & register patch to make sure
everything is as it was
2026-02-26 23:16:00 +01:00
Wessel Nieboer
de9100785e fix agc reset 2026-02-26 23:16:00 +01:00
Matthias Wientapper
b039af0b52 Rak bootlock voltage set to 0x 2026-02-21 19:21:38 +01:00
25 changed files with 72 additions and 221 deletions

View File

@@ -51,7 +51,7 @@
- `time <epoch_seconds>`
**Parameters:**
- `epoch_seconds`: Unix epoch time
- `epoc_seconds`: Unix epoc time
---
@@ -134,7 +134,7 @@
---
### End capture of rx log to node storage
### End capture of rx log to node sotrage
**Usage:** `log stop`
---
@@ -198,7 +198,7 @@
**Default:** Varies by board
**Notes:** This setting only controls the power level of the LoRa chip. Some nodes have an additional power amplifier stage which increases the total output. Refer to the node's manual for the correct setting to use. **Setting a value too high may violate the laws in your country.**
**Notes:** This setting only controls the power level of the LoRa chip. Some nodes have an additional power amplifier stage which increases the total output. Referr to the node's manual for the correct setting to use. **Setting a value too high may violate the laws in your country.**
---
@@ -228,7 +228,6 @@
**Default:** `869.525`
**Note:** Requires reboot to apply
**Serial Only:** `set freq <frequency>`
### System
@@ -294,16 +293,17 @@
#### View or change this node's admin password
**Usage:**
- `password <new_password>`
- `get password`
- `set password <password>`
**Parameters:**
- `new_password`: New admin password
- `password`: Admin password
**Set by build flag:** `ADMIN_PASSWORD`
**Default:** `password`
**Note:** Command reply echoes the updated password for confirmation.
**Note:** Echoed back for confirmation
**Note:** Any node using this password will be added to the admin ACL list.
@@ -768,7 +768,7 @@ region save
- `gps advert <policy>`
**Parameters:**
- `policy`: `none`|`share`|`prefs`
- `policy`: `none`|`shared`|`prefs`
- `none`: don't include location in adverts
- `share`: share gps location (from SensorManager)
- `prefs`: location stored in node's lat and lon settings

View File

@@ -229,7 +229,6 @@ 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_interval, sizeof(_prefs.gps_interval)); // 86
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();
}
@@ -266,7 +265,6 @@ 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_interval, sizeof(_prefs.gps_interval)); // 86
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();
}

View File

@@ -318,10 +318,6 @@ bool MyMesh::shouldOverwriteWhenFull() const {
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) {
_store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage
if (_serial->isConnected()) {
@@ -1789,16 +1785,12 @@ void MyMesh::handleCmdFrame(size_t len) {
}
} else if (cmd_frame[0] == CMD_SET_AUTOADD_CONFIG) {
_prefs.autoadd_config = cmd_frame[1];
if (len >= 3) {
_prefs.autoadd_max_hops = min(cmd_frame[2], (uint8_t)64);
}
savePrefs();
writeOKFrame();
writeOKFrame();
} else if (cmd_frame[0] == CMD_GET_AUTOADD_CONFIG) {
int i = 0;
out_frame[i++] = RESP_CODE_AUTOADD_CONFIG;
out_frame[i++] = _prefs.autoadd_config;
out_frame[i++] = _prefs.autoadd_max_hops;
_serial->writeFrame(out_frame, i);
} else if (cmd_frame[0] == CMD_GET_ALLOWED_REPEAT_FREQ) {
int i = 0;

View File

@@ -119,7 +119,6 @@ protected:
bool isAutoAddEnabled() const override;
bool shouldAutoAddContactType(uint8_t type) const override;
bool shouldOverwriteWhenFull() const override;
uint8_t getAutoAddMaxHops() const override;
void onContactsFull() 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;

View File

@@ -30,5 +30,4 @@ struct NodePrefs { // persisted to file
uint8_t autoadd_config; // bitmask for auto-add contacts config
uint8_t client_repeat;
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)
};

View File

@@ -396,23 +396,6 @@ File MyMesh::openAppend(const char *fname) {
#endif
}
static uint8_t max_loop_minimal[] = { 0, /* 1-byte */ 4, /* 2-byte */ 2, /* 3-byte */ 1 };
static uint8_t max_loop_moderate[] = { 0, /* 1-byte */ 2, /* 2-byte */ 1, /* 3-byte */ 1 };
static uint8_t max_loop_strict[] = { 0, /* 1-byte */ 1, /* 2-byte */ 1, /* 3-byte */ 1 };
bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[]) {
uint8_t hash_size = packet->getPathHashSize();
uint8_t hash_count = packet->getPathHashCount();
uint8_t n = 0;
const uint8_t* path = packet->path;
while (hash_count > 0) { // count how many times this node is already in the path
if (self_id.isHashMatch(path, hash_size)) n++;
hash_count--;
path += hash_size;
}
return n >= max_counters[hash_size];
}
bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
@@ -420,29 +403,14 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet");
return false;
}
if (packet->isRouteFlood() && _prefs.loop_detect != LOOP_DETECT_OFF) {
const uint8_t* maximums;
if (_prefs.loop_detect == LOOP_DETECT_MINIMAL) {
maximums = max_loop_minimal;
} else if (_prefs.loop_detect == LOOP_DETECT_MODERATE) {
maximums = max_loop_moderate;
} else {
maximums = max_loop_strict;
}
if (isLooped(packet, maximums)) {
MESH_DEBUG_PRINTLN("allowPacketForward: FLOOD packet loop detected!");
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
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
double roll_dice = (double)rand() / RAND_MAX;
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
if (roll_dice > forw_prob)
return false;
}
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;
}

View File

@@ -128,7 +128,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
mesh::Packet* createSelfAdvert();
File openAppend(const char* fname);
bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]);
protected:
float getAirtimeBudgetFactor() const override {

View File

@@ -276,17 +276,16 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
// Limit flood advert packet forwarding using a probabilistic reduction defined by P(h) = base^(hops-1)
// 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
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
double roll_dice = (double)rand() / RAND_MAX;
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
if (roll_dice > forw_prob)
return false;
}
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;
}
@@ -624,7 +623,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.disable_fwd = 1;
_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.interference_threshold = 0; // disabled

View File

@@ -141,15 +141,6 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
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();
if (from == NULL) {
ContactInfo ci;

View File

@@ -98,7 +98,6 @@ protected:
virtual bool shouldAutoAddContactType(uint8_t type) const { return true; }
virtual void onContactsFull() {};
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 onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0;
virtual ContactInfo* processAck(const uint8_t *data) = 0;

View File

@@ -64,8 +64,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->bw, sizeof(_prefs->bw)); // 116
file.read((uint8_t *)&_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120
file.read((uint8_t *)&_prefs->path_hash_mode, sizeof(_prefs->path_hash_mode)); // 121
file.read((uint8_t *)&_prefs->loop_detect, sizeof(_prefs->loop_detect)); // 122
file.read(pad, 1); // 123
file.read(pad, 2); // 122
file.read((uint8_t *)&_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
file.read((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.read((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126
@@ -155,8 +154,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->bw, sizeof(_prefs->bw)); // 116
file.write((uint8_t *)&_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120
file.write((uint8_t *)&_prefs->path_hash_mode, sizeof(_prefs->path_hash_mode)); // 121
file.write((uint8_t *)&_prefs->loop_detect, sizeof(_prefs->loop_detect)); // 122
file.write(pad, 1); // 123
file.write(pad, 2); // 122
file.write((uint8_t *)&_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
file.write((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.write((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126
@@ -211,10 +209,6 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
// Reset clock
getRTCClock()->setCurrentTime(1715770351); // 15 May 2024, 8:50pm
_board->reboot(); // doesn't return
} else if (memcmp(command, "advert.zerohop", 14) == 0 && (command[14] == 0 || command[14] == ' ')) {
// send zerohop advert
_callbacks->sendSelfAdvertisement(1500, false); // longer delay, give CLI response time to be sent first
strcpy(reply, "OK - zerohop advert sent");
} else if (memcmp(command, "advert", 6) == 0) {
// send flood advert
_callbacks->sendSelfAdvertisement(1500, true); // longer delay, give CLI response time to be sent first
@@ -342,16 +336,6 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
*reply = 0; // set null terminator
} else if (memcmp(config, "path.hash.mode", 14) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->path_hash_mode);
} else if (memcmp(config, "loop.detect", 11) == 0) {
if (_prefs->loop_detect == LOOP_DETECT_OFF) {
strcpy(reply, "> off");
} else if (_prefs->loop_detect == LOOP_DETECT_MINIMAL) {
strcpy(reply, "> minimal");
} else if (_prefs->loop_detect == LOOP_DETECT_MODERATE) {
strcpy(reply, "> moderate");
} else {
strcpy(reply, "> strict");
}
} else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) {
sprintf(reply, "> %d", (int32_t) _prefs->tx_power_dbm);
} else if (memcmp(config, "freq", 4) == 0) {
@@ -595,26 +579,6 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else {
strcpy(reply, "Error, must be 0,1, or 2");
}
} else if (memcmp(config, "loop.detect ", 12) == 0) {
config += 12;
uint8_t mode;
if (memcmp(config, "off", 3) == 0) {
mode = LOOP_DETECT_OFF;
} else if (memcmp(config, "minimal", 7) == 0) {
mode = LOOP_DETECT_MINIMAL;
} else if (memcmp(config, "moderate", 8) == 0) {
mode = LOOP_DETECT_MODERATE;
} else if (memcmp(config, "strict", 6) == 0) {
mode = LOOP_DETECT_STRICT;
} else {
mode = 0xFF;
strcpy(reply, "Error, must be: off, minimal, moderate, or strict");
}
if (mode != 0xFF) {
_prefs->loop_detect = mode;
savePrefs();
strcpy(reply, "OK");
}
} else if (memcmp(config, "tx ", 3) == 0) {
_prefs->tx_power_dbm = atoi(&config[3]);
savePrefs();
@@ -688,7 +652,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
};
} else if (memcmp(config, "flood.advert.base ", 18) == 0) {
float f = atof(&config[18]);
if(f >= 0 && f <= 1) {
if((f > 0) || (f<1)) {
_prefs->flood_advert_base = f;
savePrefs();
strcpy(reply, "OK");
@@ -802,7 +766,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_prefs->advert_loc_policy = ADVERT_LOC_SHARE;
savePrefs();
strcpy(reply, "ok");
} else if (memcmp(command+11, "prefs", 5) == 0) {
} else if (memcmp(command+11, "prefs", 4) == 0) {
_prefs->advert_loc_policy = ADVERT_LOC_PREFS;
savePrefs();
strcpy(reply, "ok");

View File

@@ -13,11 +13,6 @@
#define ADVERT_LOC_SHARE 1
#define ADVERT_LOC_PREFS 2
#define LOOP_DETECT_OFF 0
#define LOOP_DETECT_MINIMAL 1
#define LOOP_DETECT_MODERATE 2
#define LOOP_DETECT_STRICT 3
struct NodePrefs { // persisted to file
float airtime_factor;
char node_name[32];
@@ -59,7 +54,6 @@ struct NodePrefs { // persisted to file
float adc_multiplier;
char owner_info[120];
uint8_t path_hash_mode; // which path mode to use when sending
uint8_t loop_detect;
};
class CommonCLICallbacks {

View File

@@ -683,7 +683,7 @@ void EnvironmentSensorManager::start_gps() {
_location->begin();
_location->reset();
#ifndef PIN_GPS_EN
#ifndef PIN_GPS_RESET
MESH_DEBUG_PRINTLN("Start GPS is N/A on this board. Actual GPS state unchanged");
#endif
}
@@ -707,9 +707,7 @@ void EnvironmentSensorManager::loop() {
static long next_gps_update = 0;
#if ENV_INCLUDE_GPS
if (gps_active) {
_location->loop();
}
_location->loop();
if (millis() > next_gps_update) {
if(gps_active){

View File

@@ -79,10 +79,7 @@ public :
if (_pin_en != -1) {
digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE);
}
if (_pin_reset != -1) {
digitalWrite(_pin_reset, GPS_RESET_FORCE);
}
if (_peripher_power) _peripher_power->release();
if (_peripher_power) _peripher_power->release();
}
bool isEnabled() override {

View File

@@ -32,11 +32,6 @@ build_flags =
-D PIN_TFT_LEDA_CTL=21 ; LEDK (switches on/off via mosfet to create the ground)
-D PIN_GPS_RX=33
-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_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140

View File

@@ -16,8 +16,7 @@ WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
// 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);
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
HWTSensorManager sensors = HWTSensorManager(nmea);
#ifdef DISPLAY_CLASS
@@ -59,16 +58,18 @@ mesh::LocalIdentity radio_new_identity() {
void HWTSensorManager::start_gps() {
if (!gps_active) {
_location->begin(); // Claims periph_power via RefCountedDigitalPin
board.periph_power.claim();
gps_active = true;
Serial1.println("$CFGSYS,h35155*68"); // Configure GPS for all constellations
Serial1.println("$CFGSYS,h35155*68");
}
}
void HWTSensorManager::stop_gps() {
if (gps_active) {
gps_active = false;
_location->stop(); // Releases periph_power via RefCountedDigitalPin
board.periph_power.release();
}
}

View File

@@ -28,7 +28,6 @@ public:
const char* getSettingName(int i) const override;
const char* getSettingValue(int i) const override;
bool setSettingValue(const char* name, const char* value) override;
LocationProvider* getLocationProvider() override { return _location; }
};
extern HeltecV3Board board;

View File

@@ -6,26 +6,18 @@ void HeltecTrackerV2Board::begin() {
pinMode(PIN_ADC_CTRL, OUTPUT);
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);
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);
digitalWrite(P_LORA_PA_EN,HIGH);
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN);
pinMode(P_LORA_PA_TX_EN, OUTPUT);
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();
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_DEEPSLEEP) {
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)
@@ -56,9 +48,7 @@ void HeltecTrackerV2Board::begin() {
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
// 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);
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); //It also needs to be enabled in receive mode
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

View File

@@ -17,11 +17,11 @@ build_flags =
-D P_LORA_SCLK=9
-D P_LORA_MISO=11
-D P_LORA_MOSI=10
-D P_LORA_PA_POWER=7 ; VFEM_Ctrl - GC1109 LDO power enable
-D P_LORA_PA_EN=4 ; CSD - GC1109 chip enable (HIGH=on)
-D P_LORA_PA_TX_EN=46 ; CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass)
-D LORA_TX_POWER=10 ; 10dBm + ~11dB GC1109 gain = ~21dBm output
-D MAX_LORA_TX_POWER=22 ; Max SX1262 output -> ~28dBm at antenna
-D P_LORA_PA_POWER=7 ;power en
-D P_LORA_PA_EN=4
-D P_LORA_PA_TX_EN=46 ;enable tx
-D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm.
-D MAX_LORA_TX_POWER=22 ;Max SX1262 output
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140

View File

@@ -7,26 +7,19 @@ void HeltecV4Board::begin() {
pinMode(PIN_ADC_CTRL, OUTPUT);
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);
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);
digitalWrite(P_LORA_PA_EN,HIGH);
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN);
pinMode(P_LORA_PA_TX_EN, OUTPUT);
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();
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_DEEPSLEEP) {
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)
@@ -57,9 +50,7 @@ void HeltecV4Board::begin() {
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
// 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);
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); //It also needs to be enabled in receive mode
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

View File

@@ -1,7 +1,5 @@
[ikoka_handheld_nrf]
extends = nrf52_base
board = seeed-xiao-afruitnrf52-nrf52840
board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags}
-I lib/nrf52/s140_nrf52_7.3.0_API/include
@@ -50,8 +48,7 @@ build_src_filter = ${ikoka_handheld_nrf.build_src_filter}
+<../examples/companion_radio/*.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble]
extends = ikoka_handheld_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
extends = ikoka_nrf52
build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D BLE_PIN_CODE=123456
-D LORA_TX_POWER=20
@@ -59,8 +56,7 @@ build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble]
extends = ikoka_handheld_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
extends = ikoka_nrf52
build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D BLE_PIN_CODE=123456
-D LORA_TX_POWER=20
@@ -69,22 +65,20 @@ build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb]
extends = ikoka_handheld_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
extends = ikoka_nrf52
build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D LORA_TX_POWER=20
build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
[env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb]
extends = ikoka_handheld_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
extends = ikoka_nrf52
build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D LORA_TX_POWER=20
-D DISPLAY_ROTATION=2
build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
[env:ikoka_handheld_nrf_e22_30dbm_repeater]
extends = ikoka_handheld_nrf
extends = ikoka_nrf52
build_flags =
${ikoka_handheld_nrf.build_flags}
-D ADVERT_NAME='"ikoka_handheld Repeater"'
@@ -97,7 +91,7 @@ build_src_filter = ${ikoka_handheld_nrf.build_src_filter}
+<../examples/simple_repeater/*.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_room_server]
extends = ikoka_handheld_nrf
extends = ikoka_nrf52
build_flags =
${ikoka_handheld_nrf.build_flags}
-D ADVERT_NAME='"ikoka_handheld Room"'

View File

@@ -20,29 +20,13 @@ void RAK3401Board::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);
digitalWrite(PIN_3V3_EN, HIGH);
// Enable SKY66122-11 FEM on the RAK13302 module.
// CSD and CPS are tied together on the RAK13302 PCB, routed to IO3 (P0.21).
// HIGH = FEM active (LNA for RX, PA path available for TX).
// TX/RX switching (CTX) is handled by SX1262 DIO2 via SetDIO2AsRfSwitchCtrl.
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);
}
#ifdef P_LORA_PA_EN
// Initialize RAK13302 1W LoRa transceiver module PA control pin
pinMode(P_LORA_PA_EN, OUTPUT);
digitalWrite(P_LORA_PA_EN, LOW); // Start with PA disabled
delay(10); // Allow PA module to initialize
#endif
}

View File

@@ -38,6 +38,13 @@ public:
return "RAK 3401";
}
// TX/RX switching is handled by SX1262 DIO2 -> SKY66122 CTX (hardware-timed).
// No onBeforeTransmit/onAfterTransmit overrides needed.
#ifdef P_LORA_PA_EN
void onBeforeTransmit() override {
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
};

View File

@@ -11,7 +11,6 @@ build_flags = ${nrf52_base.build_flags}
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=140
-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}
+<../variants/rak3401>
+<helpers/sensors>

View File

@@ -147,15 +147,8 @@ static const uint8_t AREF = PIN_AREF;
#define SX126X_BUSY (9)
#define SX126X_RESET (4)
// SKY66122-11 FEM control on the RAK13302 module:
// 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_POWER_EN (21)
// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
@@ -166,6 +159,7 @@ static const uint8_t AREF = PIN_AREF;
#define P_LORA_DIO_1 SX126X_DIO1
#define P_LORA_BUSY SX126X_BUSY
#define P_LORA_RESET SX126X_RESET
#define P_LORA_PA_EN 31
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings