Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ duty-cycle auto src/helpers/CommonRadioPrefs.{cpp,h}, src/helpers/CommonCLI.{c
examples/simple_repeater/MyMesh.h, examples/simple_room_server/MyMesh.h,
examples/simple_secure_chat/main.cpp,
examples/companion_radio/{MyMesh.cpp,NodePrefs.h}, examples/simple_sensor/SensorMesh.cpp
gps default on src/helpers/CommonCLI.{cpp,h}, examples/companion_radio/{MyMesh.cpp,NodePrefs.h},
variants/sensecap_solar/platformio.ini, docs/cli_commands.md
variant tweaks variants/minewsemi_me25ls01/platformio.ini, variants/tenstar_c3/target.h
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,7 @@ region save
**Parameters:**
- `state`: `on`|`off`

**Default:** `off`
**Default:** `off`, except on boards built with `GPS_DEFAULT_ON` (SenseCAP Solar), where the first boot turns it `on`. That includes nodes upgraded from older firmware. A later `gps off` is kept across reboots.

**Note:** Output format:
- `off` when the GPS hardware is disabled
Expand Down
6 changes: 6 additions & 0 deletions examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "MyMesh.h"
#include <helpers/DutyCycleLimits.h>
#include <helpers/GpsDefault.h>

#include <Arduino.h> // needed for PlatformIO
#ifdef ENABLE_WIFI_INTERFACE
Expand Down Expand Up @@ -998,6 +999,11 @@ void MyMesh::begin() {
_prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER);
_prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1
_prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours
#ifdef GPS_DEFAULT_ON
if (applyGpsDefaultOn(_prefs.gps_enabled, _prefs.gps_default_applied)) {
savePrefs();
}
#endif

_active_ble_pin = _prefs.ble_pin;

Expand Down
2 changes: 2 additions & 0 deletions examples/companion_radio/NodePrefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class NodePrefs : public ConfigSerializer { // persisted to file
uint8_t vibe_quiet = 0;
uint8_t gps_enabled = 0; // GPS enabled flag (0=disabled, 1=enabled)
uint32_t gps_interval = 0; // GPS read interval in seconds
uint8_t gps_default_applied = 0; // GPS_DEFAULT_ON has been applied once (boolean)
uint8_t autoadd_config = 0; // bitmask for auto-add contacts config
uint8_t rx_boosted_gain = 0; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
uint8_t radio_fem_rxgain = 0; // external LoRa FEM RX gain (LNA)
Expand Down Expand Up @@ -134,6 +135,7 @@ class NodePrefs : public ConfigSerializer { // persisted to file
def("en", _parent->gps_enabled); // boolean
def("int", _parent->gps_interval); // interval in seconds
def("adv_loc", _parent->advert_loc_policy);
def("dflt_on", _parent->gps_default_applied);
}
public:
GPSPrefs(NodePrefs* parent) : _parent(parent) { }
Expand Down
6 changes: 6 additions & 0 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <Arduino.h>
#include "CommonCLI.h"
#include "DutyCycleLimits.h"
#include "GpsDefault.h"
#include "TxtDataHelpers.h"
#include "AdvertDataHelpers.h"
#include "TxtDataHelpers.h"
Expand Down Expand Up @@ -45,6 +46,11 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) {
// fs->remove("/com_prefs"); // remove old
}
}
#ifdef GPS_DEFAULT_ON
if (applyGpsDefaultOn(_prefs->gps_enabled, _prefs->gps_default_applied)) {
savePrefs(fs);
}
#endif
}

void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // Legacy prefs loader
Expand Down
2 changes: 2 additions & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class NodePrefs : public ConfigSerializer {
// Gps settings
uint8_t gps_enabled = 0;
uint32_t gps_interval = 0; // in seconds
uint8_t gps_default_applied = 0; // GPS_DEFAULT_ON has been applied once (boolean)
uint8_t advert_loc_policy = 0;
uint32_t discovery_mod_timestamp = 0;
float adc_multiplier = 0;
Expand Down Expand Up @@ -166,6 +167,7 @@ class NodePrefs : public ConfigSerializer {
def("en", _parent->gps_enabled); // boolean
def("int", _parent->gps_interval); // interval in seconds
def("adv_loc", _parent->advert_loc_policy);
def("dflt_on", _parent->gps_default_applied);
}
public:
GPSPrefs(NodePrefs* parent) : _parent(parent) { }
Expand Down
13 changes: 13 additions & 0 deletions src/helpers/GpsDefault.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <stdint.h>

// Boards built with GPS_DEFAULT_ON turn the GPS on once, including nodes whose saved
// prefs predate the marker. A later "gps off" persists, because the marker stays set.
// Returns true when the prefs changed and need saving.
inline bool applyGpsDefaultOn(uint8_t& gps_enabled, uint8_t& gps_default_applied) {
if (gps_default_applied) return false;
gps_enabled = 1;
gps_default_applied = 1;
return true;
}
102 changes: 102 additions & 0 deletions test/test_gps_default/test_gps_default.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include <gtest/gtest.h>

#include <cstdio>
#include <cstring>
#include <string>

#include "helpers/GpsDefault.h"
#include "../../examples/companion_radio/NodePrefs.h"

// CommonRadioPrefs.cpp needs target.h, so it is not in the native build. These tests use no radio keys.
bool CommonRadioPrefs::setByKey(const char*, const char*) { return false; }
bool CommonRadioPrefs::getByKey(const char*, char*, size_t) { return false; }

class ReplayStream : public Stream {
const char* _text;
int _pos = 0;
int _len;

public:
explicit ReplayStream(const char* text) : _text(text), _len(strlen(text)) { }

int available() override { return _len - _pos; }
int read() override { return _pos < _len ? _text[_pos++] : -1; }
int peek() override { return _pos < _len ? _text[_pos] : -1; }
};

class CaptureStream : public Stream {
std::string _text;

size_t emit(long long value) {
char text[24];
int length = snprintf(text, sizeof(text), "%lld", value);
return write(reinterpret_cast<const uint8_t*>(text), length);
}

public:
size_t write(uint8_t value) override {
_text.push_back(static_cast<char>(value));
return 1;
}

size_t write(const uint8_t* buffer, size_t size) override {
_text.append(reinterpret_cast<const char*>(buffer), size);
return size;
}

size_t print(unsigned char value, int = DEC) override { return emit(value); }
size_t print(int value, int = DEC) override { return emit(value); }
size_t print(unsigned int value, int = DEC) override { return emit(value); }
size_t print(long value, int = DEC) override { return emit(value); }
size_t print(unsigned long value, int = DEC) override { return emit(value); }
size_t print(long long value, int = DEC) override { return emit(value); }
size_t print(unsigned long long value, int = DEC) override { return emit(value); }

const std::string& text() const { return _text; }
};

TEST(GpsDefault, TurnsGpsOnOnce) {
uint8_t enabled = 0, applied = 0;
EXPECT_TRUE(applyGpsDefaultOn(enabled, applied));
EXPECT_EQ(1, enabled);
EXPECT_EQ(1, applied);
}

TEST(GpsDefault, KeepsGpsOffAfterTheDefaultWasApplied) {
uint8_t enabled = 0, applied = 1;
EXPECT_FALSE(applyGpsDefaultOn(enabled, applied));
EXPECT_EQ(0, enabled);
EXPECT_EQ(1, applied);
}

TEST(GpsDefault, PrefsWrittenBeforeTheMarkerExistedGetGpsOn) {
ReplayStream input("{gps:{en:0,int:0}}");
NodePrefs prefs;
ASSERT_TRUE(prefs.loadSerial(input));
EXPECT_EQ(0, prefs.gps_default_applied);

EXPECT_TRUE(applyGpsDefaultOn(prefs.gps_enabled, prefs.gps_default_applied));
EXPECT_EQ(1, prefs.gps_enabled);
}

TEST(GpsDefault, GpsOffChosenAfterTheMarkerSurvivesAReload) {
NodePrefs saved;
saved.gps_enabled = 0;
saved.gps_default_applied = 1;

CaptureStream output;
ASSERT_TRUE(saved.saveSerial(output));

ReplayStream input(output.text().c_str());
NodePrefs loaded;
ASSERT_TRUE(loaded.loadSerial(input));
EXPECT_EQ(1, loaded.gps_default_applied);

EXPECT_FALSE(applyGpsDefaultOn(loaded.gps_enabled, loaded.gps_default_applied));
EXPECT_EQ(0, loaded.gps_enabled);
}

int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
1 change: 1 addition & 0 deletions variants/sensecap_solar/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ build_flags = ${nrf52_base.build_flags}
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D ENV_INCLUDE_GPS=1
-D GPS_DEFAULT_ON=1
build_src_filter = ${nrf52_base.build_src_filter}
+<helpers/*.cpp>
+<helpers/sensors>
Expand Down