Skip to content
Open
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
7 changes: 7 additions & 0 deletions examples/companion_radio/AbstractUITask.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,24 @@ enum class UIEventType {
ack
};

class ShutdownHandler {
public:
virtual void onBeforeShutdown() = 0;
};

class AbstractUITask {
protected:
mesh::MainBoard* _board;
BaseSerialInterface* _serial;
bool _connected;
ShutdownHandler* _shutdown_handler = nullptr;

AbstractUITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial) {
_connected = false;
}

public:
void setShutdownHandler(ShutdownHandler* h) { _shutdown_handler = h; }
void setHasConnection(bool connected) { _connected = connected; }
bool hasConnection() const { return _connected; }
uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); }
Expand Down
10 changes: 9 additions & 1 deletion examples/companion_radio/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ struct AdvertPath {
uint8_t path[MAX_PATH_SIZE];
};

class MyMesh : public BaseChatMesh, public DataStoreHost {
class MyMesh : public BaseChatMesh, public DataStoreHost, public ShutdownHandler {
public:
MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui=NULL);

Expand Down Expand Up @@ -166,6 +166,14 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {
public:
void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); }

void onBeforeShutdown() override {
if (dirty_contacts_expiry) {
saveContacts();
dirty_contacts_expiry = 0;
}
savePrefs();
}

#if ENV_INCLUDE_GPS == 1
void applyGpsPrefs() {
sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0");
Expand Down
1 change: 1 addition & 0 deletions examples/companion_radio/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ void setup() {

#ifdef DISPLAY_CLASS
ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved
ui_task.setShutdownHandler(&the_mesh);
#endif

board.onBootComplete();
Expand Down
43 changes: 25 additions & 18 deletions examples/companion_radio/ui-new/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,8 @@ void UITask::shutdown(bool restart){

#endif // PIN_BUZZER

if (_shutdown_handler) _shutdown_handler->onBeforeShutdown();

if (restart) {
_board->reboot();
} else {
Expand Down Expand Up @@ -821,26 +823,31 @@ void UITask::loop() {

#ifdef AUTO_SHUTDOWN_MILLIVOLTS
if (millis() > next_batt_chck) {
uint16_t milliVolts = getBattMilliVolts();
if (milliVolts > 0 && milliVolts < AUTO_SHUTDOWN_MILLIVOLTS) {

// show low battery shutdown alert
// we should only do this for eink displays, which will persist after power loss
#if defined(THINKNODE_M1) || defined(LILYGO_TECHO)
if (_display != NULL) {
_display->startFrame();
_display->setTextSize(2);
_display->setColor(DisplayDriver::RED);
_display->drawTextCentered(_display->width() / 2, 20, "Low Battery.");
_display->drawTextCentered(_display->width() / 2, 40, "Shutting Down!");
_display->endFrame();
}
#endif
uint32_t now = millis();
if (!_board->isBattReadSafe(now)) {
// TX just completed — voltage hasn't recovered yet; retry after settle window.
next_batt_chck = now + POST_TX_BATT_SETTLE_MS + 50;
} else {
uint16_t milliVolts = getBattMilliVolts();
if (milliVolts > 0 && milliVolts < AUTO_SHUTDOWN_MILLIVOLTS) {

// show low battery shutdown alert
// we should only do this for eink displays, which will persist after power loss
#if defined(THINKNODE_M1) || defined(LILYGO_TECHO)
if (_display != NULL) {
_display->startFrame();
_display->setTextSize(2);
_display->setColor(DisplayDriver::RED);
_display->drawTextCentered(_display->width() / 2, 20, "Low Battery.");
_display->drawTextCentered(_display->width() / 2, 40, "Shutting Down!");
_display->endFrame();
}
#endif

shutdown();
shutdown();

}
next_batt_chck = millis() + 8000;
}
next_batt_chck = millis() + 8000;
}
#endif
}
Expand Down
8 changes: 8 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
_cli.savePrefs(_fs);
}

void flushPending() {
if (dirty_contacts_expiry) {
acl.save(_fs);
dirty_contacts_expiry = 0;
}
savePrefs();
}

void sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size);

// CommonCLICallbacks
Expand Down
1 change: 1 addition & 0 deletions examples/simple_repeater/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ void loop() {
userBtnDownAt = millis();
} else if ((unsigned long)(millis() - userBtnDownAt) >= USER_BTN_HOLD_OFF_MILLIS) {
Serial.println("Powering off...");
the_mesh.flushPending();
board.powerOff(); // does not return
}
} else {
Expand Down
4 changes: 4 additions & 0 deletions examples/simple_sensor/SensorMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,10 @@ void SensorMesh::loop() {

uint32_t curr = getRTCClock()->getCurrentTime();
if (curr >= last_read_time + SENSOR_READ_INTERVAL_SECS) {
// Skip this cycle if TX just completed — the current spike sags battery
// voltage enough to falsely trigger low-battery alerts on weaker cells.
// The loop runs again in milliseconds, so the read is only deferred briefly.
if (!board.isBattReadSafe(millis())) return;
telemetry.reset();
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
// query other sensors -- target specific
Expand Down
21 changes: 21 additions & 0 deletions src/MeshCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,29 @@ namespace mesh {
#define BD_STARTUP_NORMAL 0 // getStartupReason() codes
#define BD_STARTUP_RX_PACKET 1

// Milliseconds to wait after TX completes before trusting battery ADC readings.
// LoRa TX causes a current spike that sags battery terminal voltage; on LiPo
// cells below ~50% SoC the sag is large enough to cross shutdown/alert thresholds.
#ifndef POST_TX_BATT_SETTLE_MS
#define POST_TX_BATT_SETTLE_MS 250
#endif

class MainBoard {

bool _tx_active = false;
uint32_t _last_tx_complete_ms = 0;

public:
// Called by the radio layer — not meant to be overridden.
void notifyTxStart() { _tx_active = true; }
void notifyTxComplete(uint32_t now_ms) { _tx_active = false; _last_tx_complete_ms = now_ms; }

// Returns true when it is safe to read the battery ADC (TX not in progress
// and enough time has elapsed since the last transmission for voltage to recover).
bool isBattReadSafe(uint32_t now_ms, uint32_t settle_ms = POST_TX_BATT_SETTLE_MS) const {
return !_tx_active && (now_ms - _last_tx_complete_ms >= settle_ms);
}

virtual uint16_t getBattMilliVolts() = 0;
virtual float getMCUTemperature() { return NAN; }
virtual bool setAdcMultiplier(float multiplier) { return false; };
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/radiolib/RadioLibWrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ uint32_t RadioLibWrapper::getEstAirtimeFor(int len_bytes) {
}

bool RadioLibWrapper::startSendRaw(const uint8_t* bytes, int len) {
_board->notifyTxStart();
_board->onBeforeTransmit();
int err = _radio->startTransmit((uint8_t *) bytes, len);
if (err == RADIOLIB_ERR_NONE) {
Expand All @@ -160,6 +161,7 @@ bool RadioLibWrapper::startSendRaw(const uint8_t* bytes, int len) {
MESH_DEBUG_PRINTLN("RadioLibWrapper: error: startTransmit(%d)", err);
idle(); // trigger another startRecv()
_board->onAfterTransmit();
_board->notifyTxComplete(millis());
return false;
}

Expand All @@ -175,6 +177,7 @@ bool RadioLibWrapper::isSendComplete() {
void RadioLibWrapper::onSendFinished() {
_radio->finishTransmit();
_board->onAfterTransmit();
_board->notifyTxComplete(millis());
state = STATE_IDLE;
}

Expand Down