Merge IoTThinks PowerSaving-v17
Port current-compatible power-saving updates while retaining newer local implementations.
This commit is contained in:
commit
c76ee004f1
10 files changed
+115
-31
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ./build-iotthinks.sh
|
||||
export FIRMWARE_VERSION="PowerSaving17"
|
||||
export FIRMWARE_VERSION="PowerSaving17.1"
|
||||
|
||||
############# Repeaters #############
|
||||
# Commonly-used boards
|
||||
|
||||
@@ -1094,6 +1094,18 @@ Companion firmware defaults this setting to `on`. Full Companion accepts the com
|
||||
|
||||
---
|
||||
|
||||
#### View or set the reboot interval (Repeater and room server)
|
||||
**Usage:**
|
||||
- `get reboot.interval`
|
||||
- `set reboot.interval <hours>`
|
||||
|
||||
**Parameters:**
|
||||
- `hours`: `0-255`; `0` disables scheduled reboots.
|
||||
|
||||
**Default:** `0` (disabled)
|
||||
|
||||
---
|
||||
|
||||
#### Control an exposed GPIO
|
||||
|
||||
**Availability:** ESP32 Repeater, Room Server, Bridge, and Sensor firmware. Companion firmware does not expose these commands. On nRF52, the commands are enabled only for Sensor builds on the Heltec T096, ProMicro, RAK3401, and RAK4631. GPIO expanders are not supported.
|
||||
@@ -1129,6 +1141,25 @@ The pin number is the Arduino pin number used by that target (the normal GPIO nu
|
||||
|
||||
### Routing
|
||||
|
||||
#### View or set the direct path override for the current remote client
|
||||
**Usage:**
|
||||
- `get outpath`
|
||||
- `set outpath <hop1_hex,hop2_hex,...>`
|
||||
- `set outpath direct`
|
||||
- `set outpath clear`
|
||||
- `set outpath flood`
|
||||
|
||||
**Parameters:**
|
||||
- `hopN_hex`: Hop hash with `2`, `4`, or `6` hexadecimal characters. Every hop must use the same width.
|
||||
|
||||
**Notes:**
|
||||
- These commands require remote client context and update the caller's ACL entry.
|
||||
- `direct` selects a zero-hop route for a directly reachable caller.
|
||||
- `clear` forgets the override so normal path discovery can repopulate it.
|
||||
- `flood` forces replies to use flood packets until the client logs in again.
|
||||
|
||||
---
|
||||
|
||||
#### View or change this node's repeat flag
|
||||
**Usage:**
|
||||
- `get repeat`
|
||||
|
||||
@@ -114,6 +114,8 @@ class HomeScreen : public UIScreen {
|
||||
CompanionNodePrefs* _node_prefs;
|
||||
uint8_t _page;
|
||||
bool _shutdown_init;
|
||||
uint32_t _uptime_last_millis;
|
||||
uint64_t _uptime_millis;
|
||||
AdvertPath recent[UI_RECENT_LIST_SIZE];
|
||||
|
||||
|
||||
@@ -138,6 +140,31 @@ class HomeScreen : public UIScreen {
|
||||
int iconY = 0;
|
||||
display.setColor(UIColor::title_txt);
|
||||
|
||||
// Track uptime across the 32-bit millis() rollover and show the two most
|
||||
// useful units beside the battery icon.
|
||||
uint32_t now = millis();
|
||||
_uptime_millis += (uint32_t)(now - _uptime_last_millis);
|
||||
_uptime_last_millis = now;
|
||||
|
||||
uint64_t uptime_minutes = _uptime_millis / 60000ULL;
|
||||
unsigned long days = (unsigned long)(uptime_minutes / 1440ULL);
|
||||
unsigned long hours = (unsigned long)((uptime_minutes % 1440ULL) / 60ULL);
|
||||
unsigned long minutes = (unsigned long)(uptime_minutes % 60ULL);
|
||||
char uptime[16];
|
||||
if (days > 0) {
|
||||
snprintf(uptime, sizeof(uptime), "%lud %luh", days, hours);
|
||||
} else if (hours > 0) {
|
||||
snprintf(uptime, sizeof(uptime), "%luh %lum", hours, minutes);
|
||||
} else {
|
||||
snprintf(uptime, sizeof(uptime), "%lum", minutes);
|
||||
}
|
||||
|
||||
display.setTextSize(1);
|
||||
int uptimeWidth = display.getTextWidth(uptime);
|
||||
int spaceWidth = display.getTextWidth(" ");
|
||||
display.setCursor(iconX - uptimeWidth - spaceWidth, iconY);
|
||||
display.print(uptime);
|
||||
|
||||
// battery outline
|
||||
display.drawRect(iconX, iconY, iconWidth, iconHeight);
|
||||
|
||||
@@ -187,7 +214,8 @@ class HomeScreen : public UIScreen {
|
||||
public:
|
||||
HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, CompanionNodePrefs* node_prefs)
|
||||
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0),
|
||||
_shutdown_init(false), sensors_lpp(200) { }
|
||||
_shutdown_init(false), _uptime_last_millis(millis()), _uptime_millis(0),
|
||||
sensors_lpp(200) { }
|
||||
|
||||
void showFirstPage() { _page = HomePage::FIRST; }
|
||||
|
||||
|
||||
@@ -329,6 +329,7 @@ protected:
|
||||
|
||||
#if ENV_INCLUDE_GPS == 1
|
||||
void applyGpsPrefs() {
|
||||
sensors.setPowerSavingEnabled(_prefs.powersaving_enabled != 0);
|
||||
sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -224,6 +224,7 @@ private:
|
||||
|
||||
#if ENV_INCLUDE_GPS == 1
|
||||
void applyGpsPrefs() {
|
||||
sensors.setPowerSavingEnabled(_prefs.powersaving_enabled != 0);
|
||||
sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -86,6 +86,11 @@ protected:
|
||||
}
|
||||
|
||||
int startReceiveMode() override {
|
||||
// Do not abort a frame that started while a calibration transition was
|
||||
// being scheduled. recvRaw() will move it to continuous RX afterward.
|
||||
if (_nf_calib_active && _rx_ps_armed && isPacketPendingOrReceiving()) {
|
||||
return RADIOLIB_ERR_NONE;
|
||||
}
|
||||
if (_rx_ps_armed) {
|
||||
// stop the previous duty-cycle sequence with an explicit standby before
|
||||
// reconfiguring the radio
|
||||
|
||||
@@ -99,6 +99,11 @@ public:
|
||||
|
||||
protected:
|
||||
int startReceiveMode() override {
|
||||
// Do not abort a frame that started while a calibration transition was
|
||||
// being scheduled. recvRaw() will move it to continuous RX afterward.
|
||||
if (_nf_calib_active && _rx_ps_armed && isPacketPendingOrReceiving()) {
|
||||
return RADIOLIB_ERR_NONE;
|
||||
}
|
||||
if (_rx_ps_armed) {
|
||||
// leaving duty-cycle mode (after RxDone or a reconfig): stop the
|
||||
// sequencer and the still-running RTC, or its pending event can
|
||||
|
||||
@@ -235,25 +235,16 @@ void RadioLibWrapper::doResetAGC() {
|
||||
|
||||
void RadioLibWrapper::resetAGC() {
|
||||
// make sure we're not mid-receiving and mid-sending of packet!
|
||||
if ((state & STATE_INT_READY) != 0 || isReceivingPacket() || (state == STATE_TX_WAIT)) return;
|
||||
if (isPacketPendingOrReceiving() || (state == STATE_TX_WAIT)) return;
|
||||
|
||||
doResetAGC();
|
||||
state = STATE_IDLE; // trigger a startReceive()
|
||||
if (_rx_boosted_gain_valid) applyRxBoostedGainMode(_cur_rx_boosted_gain);
|
||||
|
||||
// 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;
|
||||
_noise_floor_valid = false;
|
||||
_nf_calib_active = false;
|
||||
_nf_refresh_requested = true;
|
||||
_nf_last_calib = 0;
|
||||
_nf_sample_from = 0;
|
||||
_num_floor_samples = 0;
|
||||
_floor_sample_sum = 0;
|
||||
_nf_calib_deadline = 0; // starts after reset recovery reaches RX
|
||||
// Preserve the last published value while a fresh post-reset batch is
|
||||
// collected. Invalidating the sample gate lets the new floor converge even
|
||||
// when the old value was stuck at its lower clamp.
|
||||
recalibrateNoiseFloor();
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::recoverRadio(bool hard) {
|
||||
@@ -371,6 +362,20 @@ void RadioLibWrapper::rxPsWatchdogCheck() {
|
||||
// sleep windows and settling right after each wake), so a requested refresh
|
||||
// drops receive mode to plain continuous RX, collects a fresh sample batch,
|
||||
// and re-arms the duty cycle. Requests are rate-limited and coalesced.
|
||||
void RadioLibWrapper::requestRestartRecv() {
|
||||
// An RX interrupt can arrive between the caller's idle check and this state
|
||||
// transition. Preserve that flag so the completed packet is still consumed.
|
||||
noInterrupts();
|
||||
if ((state & ~STATE_INT_READY) != STATE_TX_WAIT) {
|
||||
state &= STATE_INT_READY;
|
||||
}
|
||||
interrupts();
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::isPacketPendingOrReceiving() {
|
||||
return (state & STATE_INT_READY) != 0 || isReceivingPacket();
|
||||
}
|
||||
|
||||
void RadioLibWrapper::noiseFloorCalibCheck(unsigned long now) {
|
||||
if (_nf_calib_active) {
|
||||
if (!_rx_ps_enabled
|
||||
@@ -379,11 +384,15 @@ void RadioLibWrapper::noiseFloorCalibCheck(unsigned long now) {
|
||||
// powersaving turned off mid-window, or the batch couldn't complete
|
||||
// (busy channel / stuck filter) - keep the previous floor
|
||||
endNoiseFloorCalib(now);
|
||||
} else if (_rx_ps_armed && !isPacketPendingOrReceiving()) {
|
||||
// A packet can delay the transition from RXPS to continuous RX. Retry
|
||||
// once the radio is idle without discarding a pending interrupt.
|
||||
requestRestartRecv();
|
||||
}
|
||||
} else if (_nf_refresh_requested && _rx_ps_enabled && _rx_ps_armed && state == STATE_RX
|
||||
&& ((!_noise_floor_valid && _nf_last_calib == 0)
|
||||
|| now - _nf_last_calib >= NF_CALIB_INTERVAL_MS)
|
||||
&& !isReceivingPacket()) {
|
||||
&& !isPacketPendingOrReceiving()) {
|
||||
// never interrupt an ongoing reception to calibrate (a TX in flight is
|
||||
// already excluded by state == STATE_RX); retries next loop iteration
|
||||
_nf_calib_active = true;
|
||||
@@ -391,8 +400,9 @@ void RadioLibWrapper::noiseFloorCalibCheck(unsigned long now) {
|
||||
_nf_sample_from = now + NF_CALIB_SETTLE_MS;
|
||||
_num_floor_samples = 0; // start a fresh batch for this window
|
||||
_floor_sample_sum = 0;
|
||||
state = STATE_IDLE; // recvRaw() re-arms; startReceiveMode() sees the
|
||||
// active flag and starts continuous RX, not duty-cycle
|
||||
if (!isPacketPendingOrReceiving()) {
|
||||
requestRestartRecv(); // startReceiveMode() selects continuous RX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,8 +414,9 @@ void RadioLibWrapper::endNoiseFloorCalib(unsigned long now) {
|
||||
// force a receive re-arm back into duty-cycle mode, but don't clobber a
|
||||
// completed-but-unread packet or an in-flight TX (recvRaw()/onSendFinished()
|
||||
// will re-arm right after those anyway; same guard style as setRxPowerSaving)
|
||||
if ((state & STATE_INT_READY) == 0 && (state & ~STATE_INT_READY) != STATE_TX_WAIT) {
|
||||
state = STATE_IDLE;
|
||||
if ((state & ~STATE_INT_READY) != STATE_TX_WAIT
|
||||
&& !isPacketPendingOrReceiving()) {
|
||||
requestRestartRecv();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ protected:
|
||||
void startRecv() override;
|
||||
void rxPsWatchdogCheck();
|
||||
void requestNoiseFloorRefresh();
|
||||
void requestRestartRecv();
|
||||
bool isPacketPendingOrReceiving();
|
||||
void noiseFloorCalibCheck(unsigned long now);
|
||||
void endNoiseFloorCalib(unsigned long now);
|
||||
void finishReceiveProcessing();
|
||||
|
||||
@@ -426,16 +426,16 @@ bool ST7735Display::i2c_probe(TwoWire& wire, uint8_t addr) {
|
||||
#define PIN_TFT_LEDA_CTL_ACTIVE HIGH
|
||||
#endif
|
||||
|
||||
// Color scheme
|
||||
ColorVal UIColor::window_bkg = ST77XX_WHITE;
|
||||
ColorVal UIColor::title_bkg = ST77XX_BLUE;
|
||||
ColorVal UIColor::title_txt = ST77XX_WHITE;
|
||||
ColorVal UIColor::primary_txt = ST77XX_BLACK;
|
||||
ColorVal UIColor::secondary_txt = (18 << 11) | (36 << 5) | 18; // mid-gray
|
||||
ColorVal UIColor::warning_txt = ST77XX_ORANGE;
|
||||
ColorVal UIColor::popup_bkg = ST77XX_CYAN;
|
||||
ColorVal UIColor::popup_txt = ST77XX_BLACK;
|
||||
ColorVal UIColor::corp_blue = 0x001A;
|
||||
// Dark colors reduce panel current on the PowerSaving builds.
|
||||
ColorVal UIColor::window_bkg = ST77XX_BLACK;
|
||||
ColorVal UIColor::title_bkg = ST77XX_BLACK;
|
||||
ColorVal UIColor::title_txt = ST77XX_GREEN;
|
||||
ColorVal UIColor::primary_txt = ST77XX_GREEN;
|
||||
ColorVal UIColor::secondary_txt = ST77XX_YELLOW;
|
||||
ColorVal UIColor::warning_txt = ST77XX_RED;
|
||||
ColorVal UIColor::popup_bkg = ST77XX_BLACK;
|
||||
ColorVal UIColor::popup_txt = ST77XX_WHITE;
|
||||
ColorVal UIColor::corp_blue = ST77XX_BLUE;
|
||||
|
||||
bool ST7735Display::begin() {
|
||||
if (!sprite) {
|
||||
|
||||
Reference in new issue
Block a user