Добавлено энергосбережение для увеличения работы от аккума:
- через 60 сек экран выключается и есп уходит в сон - слейв в ожидании мастера прослушивает не постоянно 20мс/100мс
This commit is contained in:
@@ -3,7 +3,10 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <esp_mac.h>
|
#include <esp_mac.h>
|
||||||
|
#include <esp_sleep.h>
|
||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
|
#include <esp32-hal-cpu.h>
|
||||||
|
#include <driver/gpio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -74,12 +77,15 @@ void App::finishInitialization(bool factoryReset) {
|
|||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
if (!receiver_.begin()) { Log::event("BOOT", "FATAL: capture peripheral init failed"); finish(false, FailReason::UNSUPPORTED); return; }
|
if (!receiver_.begin()) { Log::event("BOOT", "FATAL: capture peripheral init failed"); finish(false, FailReason::UNSUPPORTED); return; }
|
||||||
Log::printf("BOOT", "capture initialized: %s", receiver_.highRateBackend() ? "RMT DMA" : "RMT ping-pong");
|
Log::printf("BOOT", "capture initialized: %s", receiver_.highRateBackend() ? "RMT DMA" : "RMT ping-pong");
|
||||||
|
lastUserActivityMs_ = millis();
|
||||||
|
setActivePerformance(false);
|
||||||
printConfiguration();
|
printConfiguration();
|
||||||
if (static_cast<Role>(settings_.role) == Role::SLAVE) armSlave();
|
if (static_cast<Role>(settings_.role) == Role::SLAVE) armSlave();
|
||||||
else showIdle();
|
else showIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::update() {
|
void App::update() {
|
||||||
|
serviceIdlePowerSave();
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
const ButtonEvent startEvent = startButton_.update(now);
|
const ButtonEvent startEvent = startButton_.update(now);
|
||||||
const ButtonEvent modeEvent = modeButton_.update(now);
|
const ButtonEvent modeEvent = modeButton_.update(now);
|
||||||
@@ -87,6 +93,10 @@ void App::update() {
|
|||||||
Log::printf("INPUT", "START %s state=%s", buttonEventName(startEvent), appStateName(state_));
|
Log::printf("INPUT", "START %s state=%s", buttonEventName(startEvent), appStateName(state_));
|
||||||
if (modeEvent != ButtonEvent::NONE)
|
if (modeEvent != ButtonEvent::NONE)
|
||||||
Log::printf("INPUT", "MODE %s state=%s", buttonEventName(modeEvent), appStateName(state_));
|
Log::printf("INPUT", "MODE %s state=%s", buttonEventName(modeEvent), appStateName(state_));
|
||||||
|
if (startEvent != ButtonEvent::NONE || modeEvent != ButtonEvent::NONE) {
|
||||||
|
lastUserActivityMs_ = now;
|
||||||
|
leaveIdlePowerSave();
|
||||||
|
}
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
if (!startButton_.pressed() || !modeButton_.pressed()) finishInitialization(false);
|
if (!startButton_.pressed() || !modeButton_.pressed()) finishInitialization(false);
|
||||||
else if (now - bootCheckStartedMs_ >= FACTORY_RESET_HOLD_MS) finishInitialization(true);
|
else if (now - bootCheckStartedMs_ >= FACTORY_RESET_HOLD_MS) finishInitialization(true);
|
||||||
@@ -161,6 +171,8 @@ void App::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::showIdle() {
|
void App::showIdle() {
|
||||||
|
setActivePerformance(false);
|
||||||
|
lastUserActivityMs_ = millis();
|
||||||
char one[24]; snprintf(one, sizeof(one), "MODE: %s", roleName(static_cast<Role>(settings_.role)));
|
char one[24]; snprintf(one, sizeof(one), "MODE: %s", roleName(static_cast<Role>(settings_.role)));
|
||||||
display_.show(one, "START=RUN");
|
display_.show(one, "START=RUN");
|
||||||
}
|
}
|
||||||
@@ -219,6 +231,8 @@ void App::showMenu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::startTest() {
|
void App::startTest() {
|
||||||
|
leaveIdlePowerSave();
|
||||||
|
setActivePerformance(true);
|
||||||
params_ = store_.params(settings_); stageCount_ = frequencyPointCount(params_.startHz, params_.endHz);
|
params_ = store_.params(settings_); stageCount_ = frequencyPointCount(params_.startHz, params_.endHz);
|
||||||
stageIndex_ = 0; requestedHz_ = 0; pendingReason_ = FailReason::NONE;
|
stageIndex_ = 0; requestedHz_ = 0; pendingReason_ = FailReason::NONE;
|
||||||
havePeer_ = false; lastHeartbeatMs_ = 0; lastPeerSeenMs_ = 0;
|
havePeer_ = false; lastHeartbeatMs_ = 0; lastPeerSeenMs_ = 0;
|
||||||
@@ -243,6 +257,8 @@ void App::startTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool App::armSlave(bool preserveDisplay) {
|
bool App::armSlave(bool preserveDisplay) {
|
||||||
|
setActivePerformance(false);
|
||||||
|
lastUserActivityMs_ = millis();
|
||||||
params_ = store_.params(settings_);
|
params_ = store_.params(settings_);
|
||||||
stageIndex_ = 0; stageCount_ = frequencyPointCount(params_.startHz, params_.endHz);
|
stageIndex_ = 0; stageCount_ = frequencyPointCount(params_.startHz, params_.endHz);
|
||||||
requestedHz_ = 0; session_ = 0; sequence_ = 0; havePeer_ = false;
|
requestedHz_ = 0; session_ = 0; sequence_ = 0; havePeer_ = false;
|
||||||
@@ -253,6 +269,7 @@ bool App::armSlave(bool preserveDisplay) {
|
|||||||
display_.show("LINK FAILED", "RADIO ERROR");
|
display_.show("LINK FAILED", "RADIO ERROR");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
radio_.setWindowedReceive(true);
|
||||||
radio_.flush(); state_ = AppState::SLAVE_READY;
|
radio_.flush(); state_ = AppState::SLAVE_READY;
|
||||||
Log::event("TEST", "Slave automatically armed and waiting for Master");
|
Log::event("TEST", "Slave automatically armed and waiting for Master");
|
||||||
if (!preserveDisplay) display_.show("SLAVE READY", "WAIT MASTER");
|
if (!preserveDisplay) display_.show("SLAVE READY", "WAIT MASTER");
|
||||||
@@ -377,6 +394,9 @@ void App::handleRadio() {
|
|||||||
messageName(type), r.packet.session, r.packet.stage, r.packet.sequence);
|
messageName(type), r.packet.session, r.packet.stage, r.packet.sequence);
|
||||||
if ((state_ == AppState::SLAVE_READY || state_ == AppState::SLAVE_WAIT_START) &&
|
if ((state_ == AppState::SLAVE_READY || state_ == AppState::SLAVE_WAIT_START) &&
|
||||||
type == MessageType::DISCOVER && (!havePeer_ || !memcmp(peer_, r.mac, 6))) {
|
type == MessageType::DISCOVER && (!havePeer_ || !memcmp(peer_, r.mac, 6))) {
|
||||||
|
leaveIdlePowerSave();
|
||||||
|
setActivePerformance(true);
|
||||||
|
radio_.setWindowedReceive(false);
|
||||||
memcpy(peer_, r.mac, 6); havePeer_ = true; session_ = r.packet.session; stageIndex_ = 0; sequence_ = r.packet.sequence;
|
memcpy(peer_, r.mac, 6); havePeer_ = true; session_ = r.packet.session; stageIndex_ = 0; sequence_ = r.packet.sequence;
|
||||||
lastPeerSeenMs_ = millis();
|
lastPeerSeenMs_ = millis();
|
||||||
ProtocolPacket ack = makePacket(MessageType::DISCOVER_ACK); ack.sequence = r.packet.sequence; sendLinked(ack);
|
ProtocolPacket ack = makePacket(MessageType::DISCOVER_ACK); ack.sequence = r.packet.sequence; sendLinked(ack);
|
||||||
@@ -491,8 +511,9 @@ void App::handleRadio() {
|
|||||||
void App::updateMaster() {
|
void App::updateMaster() {
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
if (state_ == AppState::MASTER_DISCOVER) {
|
if (state_ == AppState::MASTER_DISCOVER) {
|
||||||
if (now - lastSendMs_ >= LINK_RETRY_INTERVAL_MS) {
|
if (now - lastSendMs_ >= DISCOVERY_RETRY_INTERVAL_MS) {
|
||||||
Log::event("ESP-NOW", "DISCOVER retry"); radio_.sendBroadcast(pendingPacket_);
|
if (++retries_ % 50U == 0U) Log::event("ESP-NOW", "DISCOVER burst continues");
|
||||||
|
radio_.sendBroadcast(pendingPacket_);
|
||||||
lastSendMs_ = now;
|
lastSendMs_ = now;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -590,6 +611,8 @@ void App::finish(bool pass, FailReason reason, bool preserveDisplay) {
|
|||||||
if (!pass && masterActive && havePeer_ && reason != FailReason::ABORTED) sendAbort(reason);
|
if (!pass && masterActive && havePeer_ && reason != FailReason::ABORTED) sendAbort(reason);
|
||||||
if (state_ != AppState::IDLE && state_ != AppState::MENU) radio_.end();
|
if (state_ != AppState::IDLE && state_ != AppState::MENU) radio_.end();
|
||||||
state_ = AppState::FINISHED; pendingReason_ = reason;
|
state_ = AppState::FINISHED; pendingReason_ = reason;
|
||||||
|
setActivePerformance(false);
|
||||||
|
lastUserActivityMs_ = millis();
|
||||||
if (slaveLinkLost) {
|
if (slaveLinkLost) {
|
||||||
char target[12], one[24];
|
char target[12], one[24];
|
||||||
Display::formatTestFrequency(actual_.actualHz ? actual_.actualHz : requestedHz_, target, sizeof(target));
|
Display::formatTestFrequency(actual_.actualHz ? actual_.actualHz : requestedHz_, target, sizeof(target));
|
||||||
@@ -618,6 +641,67 @@ void App::finish(bool pass, FailReason reason, bool preserveDisplay) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool App::idlePowerSaveAllowed() const {
|
||||||
|
return initialized_ && (state_ == AppState::IDLE || state_ == AppState::MENU ||
|
||||||
|
state_ == AppState::FINISHED || state_ == AppState::SLAVE_READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::setActivePerformance(bool active) {
|
||||||
|
const uint32_t targetMhz = active ? 160U : 80U;
|
||||||
|
if (getCpuFrequencyMhz() != targetMhz && !setCpuFrequencyMhz(targetMhz))
|
||||||
|
Log::printf("POWER", "CPU frequency change to %luMHz FAILED", targetMhz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::leaveIdlePowerSave(bool wakeDisplay) {
|
||||||
|
if (!idlePowerSave_) {
|
||||||
|
if (wakeDisplay) display_.setPower(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
idlePowerSave_ = false;
|
||||||
|
lastUserActivityMs_ = millis();
|
||||||
|
if (wakeDisplay) display_.setPower(true);
|
||||||
|
Log::event("POWER", "idle light sleep ended");
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::serviceIdlePowerSave() {
|
||||||
|
if (!idlePowerSaveAllowed()) {
|
||||||
|
leaveIdlePowerSave(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t now = millis();
|
||||||
|
if (!idlePowerSave_) {
|
||||||
|
if (now - lastUserActivityMs_ < IDLE_POWER_SAVE_TIMEOUT_MS) {
|
||||||
|
delay(1); // allow the FreeRTOS idle task to halt the CPU between UI polls
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
idlePowerSave_ = true;
|
||||||
|
display_.setPower(false);
|
||||||
|
Log::event("POWER", "idle timeout; OLED off and light sleep started");
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_wakeup_enable(static_cast<gpio_num_t>(GPIO_BUTTON_START),
|
||||||
|
BUTTON_ACTIVE_LEVEL == LOW ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||||
|
gpio_wakeup_enable(static_cast<gpio_num_t>(GPIO_BUTTON_MODE),
|
||||||
|
BUTTON_ACTIVE_LEVEL == LOW ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||||
|
esp_sleep_enable_gpio_wakeup();
|
||||||
|
esp_sleep_enable_timer_wakeup(IDLE_LIGHT_SLEEP_SLICE_US);
|
||||||
|
const esp_err_t result = esp_light_sleep_start();
|
||||||
|
if (result != ESP_OK) {
|
||||||
|
delay(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO) {
|
||||||
|
// The wake-up press is deliberately consumed. Holding or releasing it
|
||||||
|
// must not later turn into a SHORT, LONG, or REPEAT event.
|
||||||
|
startButton_.suppressUntilRelease();
|
||||||
|
modeButton_.suppressUntilRelease();
|
||||||
|
leaveIdlePowerSave();
|
||||||
|
Log::event("POWER", "button wake consumed; next press will perform the action");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void App::printConfiguration() {
|
void App::printConfiguration() {
|
||||||
if (SERIAL_MINIMAL_LOG) return;
|
if (SERIAL_MINIMAL_LOG) return;
|
||||||
const char *board = TARGET_IS_C3 ? "ESP32-C3" : "ESP32-S3";
|
const char *board = TARGET_IS_C3 ? "ESP32-C3" : "ESP32-S3";
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ class App {
|
|||||||
void sendCurrent(MessageType type);
|
void sendCurrent(MessageType type);
|
||||||
void updateHeartbeat();
|
void updateHeartbeat();
|
||||||
bool packetForCurrent(const ProtocolPacket &p) const;
|
bool packetForCurrent(const ProtocolPacket &p) const;
|
||||||
|
void serviceIdlePowerSave();
|
||||||
|
void leaveIdlePowerSave(bool wakeDisplay = true);
|
||||||
|
bool idlePowerSaveAllowed() const;
|
||||||
|
void setActivePerformance(bool active);
|
||||||
|
|
||||||
Button startButton_, modeButton_;
|
Button startButton_, modeButton_;
|
||||||
Display display_;
|
Display display_;
|
||||||
@@ -75,4 +79,6 @@ class App {
|
|||||||
uint32_t bootCheckStartedMs_ = 0;
|
uint32_t bootCheckStartedMs_ = 0;
|
||||||
uint32_t slaveRearmAtMs_ = 0;
|
uint32_t slaveRearmAtMs_ = 0;
|
||||||
bool stageStartConfirmed_ = false;
|
bool stageStartConfirmed_ = false;
|
||||||
|
uint32_t lastUserActivityMs_ = 0;
|
||||||
|
bool idlePowerSave_ = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,21 @@ void Button::begin() {
|
|||||||
changedAt_ = millis();
|
changedAt_ = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Button::suppressUntilRelease() {
|
||||||
|
suppressed_ = true;
|
||||||
|
raw_ = stable_ = (digitalRead(pin_) == BUTTON_ACTIVE_LEVEL);
|
||||||
|
changedAt_ = millis();
|
||||||
|
longSent_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
ButtonEvent Button::update(uint32_t now) {
|
ButtonEvent Button::update(uint32_t now) {
|
||||||
const bool sample = (digitalRead(pin_) == BUTTON_ACTIVE_LEVEL);
|
const bool sample = (digitalRead(pin_) == BUTTON_ACTIVE_LEVEL);
|
||||||
|
if (suppressed_) {
|
||||||
|
raw_ = stable_ = sample;
|
||||||
|
changedAt_ = now;
|
||||||
|
if (!sample) { suppressed_ = false; longSent_ = false; }
|
||||||
|
return ButtonEvent::NONE;
|
||||||
|
}
|
||||||
if (sample != raw_) { raw_ = sample; changedAt_ = now; }
|
if (sample != raw_) { raw_ = sample; changedAt_ = now; }
|
||||||
if (raw_ != stable_ && now - changedAt_ >= BUTTON_DEBOUNCE_MS) {
|
if (raw_ != stable_ && now - changedAt_ >= BUTTON_DEBOUNCE_MS) {
|
||||||
stable_ = raw_;
|
stable_ = raw_;
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ class Button {
|
|||||||
explicit Button(uint8_t pin) : pin_(pin) {}
|
explicit Button(uint8_t pin) : pin_(pin) {}
|
||||||
void begin();
|
void begin();
|
||||||
ButtonEvent update(uint32_t nowMs);
|
ButtonEvent update(uint32_t nowMs);
|
||||||
|
void suppressUntilRelease();
|
||||||
bool pressed() const { return stable_; }
|
bool pressed() const { return stable_; }
|
||||||
private:
|
private:
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
bool raw_ = false, stable_ = false, longSent_ = false;
|
bool raw_ = false, stable_ = false, longSent_ = false;
|
||||||
|
bool suppressed_ = false;
|
||||||
uint32_t changedAt_ = 0, pressedAt_ = 0, nextRepeat_ = 0;
|
uint32_t changedAt_ = 0, pressedAt_ = 0, nextRepeat_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ constexpr bool SERIAL_MINIMAL_LOG = true;
|
|||||||
#define PWM_SETTLE_CYCLES 5U
|
#define PWM_SETTLE_CYCLES 5U
|
||||||
|
|
||||||
constexpr uint32_t BUTTON_DEBOUNCE_MS = 30;
|
constexpr uint32_t BUTTON_DEBOUNCE_MS = 30;
|
||||||
constexpr uint32_t BUTTON_LONG_PRESS_MS = 800;
|
constexpr uint32_t BUTTON_LONG_PRESS_MS = 500;
|
||||||
constexpr uint32_t BUTTON_REPEAT_DELAY_MS = 600;
|
constexpr uint32_t BUTTON_REPEAT_DELAY_MS = 600;
|
||||||
constexpr uint32_t BUTTON_REPEAT_MS = 180;
|
constexpr uint32_t BUTTON_REPEAT_MS = 180;
|
||||||
constexpr uint32_t FACTORY_RESET_HOLD_MS = 1500;
|
constexpr uint32_t FACTORY_RESET_HOLD_MS = 1500;
|
||||||
@@ -46,6 +46,7 @@ constexpr uint32_t FACTORY_RESET_HOLD_MS = 1500;
|
|||||||
constexpr uint32_t LINK_REPLY_TIMEOUT_MS = 1500;
|
constexpr uint32_t LINK_REPLY_TIMEOUT_MS = 1500;
|
||||||
constexpr uint8_t LINK_PACKET_RETRIES = 10;
|
constexpr uint8_t LINK_PACKET_RETRIES = 10;
|
||||||
constexpr uint32_t LINK_RETRY_INTERVAL_MS = 1000;
|
constexpr uint32_t LINK_RETRY_INTERVAL_MS = 1000;
|
||||||
|
constexpr uint32_t DISCOVERY_RETRY_INTERVAL_MS = 20;
|
||||||
constexpr uint32_t LINK_HEARTBEAT_INTERVAL_MS = 500;
|
constexpr uint32_t LINK_HEARTBEAT_INTERVAL_MS = 500;
|
||||||
constexpr uint32_t LINK_HEARTBEAT_TIMEOUT_MS = 2500;
|
constexpr uint32_t LINK_HEARTBEAT_TIMEOUT_MS = 2500;
|
||||||
constexpr uint32_t FINAL_ACK_RETRY_INTERVAL_MS = 50;
|
constexpr uint32_t FINAL_ACK_RETRY_INTERVAL_MS = 50;
|
||||||
@@ -58,6 +59,13 @@ constexpr uint8_t RMT_QUEUE_BLOCKS = 8;
|
|||||||
constexpr uint16_t PERIOD_BATCH_SIZE = 128;
|
constexpr uint16_t PERIOD_BATCH_SIZE = 128;
|
||||||
constexpr uint8_t MEASUREMENT_PROGRESS_STEPS = 10;
|
constexpr uint8_t MEASUREMENT_PROGRESS_STEPS = 10;
|
||||||
constexpr uint32_t OLED_PROGRESS_UPDATE_MS = 15;
|
constexpr uint32_t OLED_PROGRESS_UPDATE_MS = 15;
|
||||||
|
|
||||||
|
constexpr uint32_t IDLE_POWER_SAVE_TIMEOUT_MS = 60000;
|
||||||
|
constexpr uint32_t IDLE_LIGHT_SLEEP_SLICE_US = 10000;
|
||||||
|
constexpr uint16_t SLAVE_LISTEN_INTERVAL_MS = 100;
|
||||||
|
constexpr uint16_t SLAVE_LISTEN_WINDOW_MS = 20;
|
||||||
|
static_assert(SLAVE_LISTEN_WINDOW_MS < SLAVE_LISTEN_INTERVAL_MS,
|
||||||
|
"Slave listen window must be shorter than its interval");
|
||||||
// Conservative sustained validation rate calibrated from real C3 logs.
|
// Conservative sustained validation rate calibrated from real C3 logs.
|
||||||
constexpr uint32_t RX_PROCESSING_PERIODS_PER_SECOND = 300000;
|
constexpr uint32_t RX_PROCESSING_PERIODS_PER_SECOND = 300000;
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,19 @@ bool Display::begin() {
|
|||||||
oled_.setRotation(OLED_ROTATION);
|
oled_.setRotation(OLED_ROTATION);
|
||||||
oled_.setTextColor(SSD1306_WHITE);
|
oled_.setTextColor(SSD1306_WHITE);
|
||||||
oled_.setTextSize(1);
|
oled_.setTextSize(1);
|
||||||
|
powered_ = true;
|
||||||
}
|
}
|
||||||
Log::printf("OLED", "initialization %s, I2C address=0x%02X", ok_ ? "OK" : "FAILED", OLED_ADDRESS);
|
Log::printf("OLED", "initialization %s, I2C address=0x%02X", ok_ ? "OK" : "FAILED", OLED_ADDRESS);
|
||||||
return ok_;
|
return ok_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Display::setPower(bool enabled) {
|
||||||
|
if (!ok_ || powered_ == enabled) return;
|
||||||
|
oled_.ssd1306_command(enabled ? SSD1306_DISPLAYON : SSD1306_DISPLAYOFF);
|
||||||
|
powered_ = enabled;
|
||||||
|
Log::printf("OLED", "display power %s", enabled ? "ON" : "OFF");
|
||||||
|
}
|
||||||
|
|
||||||
void Display::fit(char *s) {
|
void Display::fit(char *s) {
|
||||||
int16_t x, y; uint16_t w, h;
|
int16_t x, y; uint16_t w, h;
|
||||||
while (*s) {
|
while (*s) {
|
||||||
@@ -44,6 +52,7 @@ void Display::show(const char *a, const char *b, uint32_t progress, uint32_t pro
|
|||||||
// Serial is the primary UI mirror and remains available when OLED is absent.
|
// Serial is the primary UI mirror and remains available when OLED is absent.
|
||||||
Log::printf("UI", "%s | %s", one, two);
|
Log::printf("UI", "%s | %s", one, two);
|
||||||
if (!ok_) return;
|
if (!ok_) return;
|
||||||
|
setPower(true);
|
||||||
fit(one); fit(two);
|
fit(one); fit(two);
|
||||||
oled_.clearDisplay(); oled_.setCursor(0, 3); oled_.print(one);
|
oled_.clearDisplay(); oled_.setCursor(0, 3); oled_.print(one);
|
||||||
oled_.setCursor(0, 19); oled_.print(two);
|
oled_.setCursor(0, 19); oled_.print(two);
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ class Display {
|
|||||||
bool begin();
|
bool begin();
|
||||||
void show(const char *line1, const char *line2,
|
void show(const char *line1, const char *line2,
|
||||||
uint32_t progress = 0, uint32_t progressTotal = 0);
|
uint32_t progress = 0, uint32_t progressTotal = 0);
|
||||||
|
void setPower(bool enabled);
|
||||||
bool available() const { return ok_; }
|
bool available() const { return ok_; }
|
||||||
|
bool powered() const { return powered_; }
|
||||||
static void formatFrequency(float hz, char *out, size_t size);
|
static void formatFrequency(float hz, char *out, size_t size);
|
||||||
static void formatTestFrequency(uint32_t hz, char *out, size_t size);
|
static void formatTestFrequency(uint32_t hz, char *out, size_t size);
|
||||||
static void formatDuration(uint64_t us, char *out, size_t size);
|
static void formatDuration(uint64_t us, char *out, size_t size);
|
||||||
@@ -17,4 +19,5 @@ class Display {
|
|||||||
void fit(char *text);
|
void fit(char *text);
|
||||||
Adafruit_SSD1306 oled_;
|
Adafruit_SSD1306 oled_;
|
||||||
bool ok_ = false;
|
bool ok_ = false;
|
||||||
|
bool powered_ = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -55,9 +55,39 @@ void Radio::end() {
|
|||||||
if (queue_) xQueueReset(queue_);
|
if (queue_) xQueueReset(queue_);
|
||||||
if (heartbeatQueue_) xQueueReset(heartbeatQueue_);
|
if (heartbeatQueue_) xQueueReset(heartbeatQueue_);
|
||||||
if (instance_ == this) instance_ = nullptr;
|
if (instance_ == this) instance_ = nullptr;
|
||||||
|
windowedReceive_ = false;
|
||||||
Log::event("ESP-NOW", "stopped");
|
Log::event("ESP-NOW", "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Radio::setWindowedReceive(bool enabled) {
|
||||||
|
if (!active_) return false;
|
||||||
|
if (windowedReceive_ == enabled) return true;
|
||||||
|
|
||||||
|
const uint16_t window = enabled ? SLAVE_LISTEN_WINDOW_MS : UINT16_MAX;
|
||||||
|
const uint16_t interval = enabled ? SLAVE_LISTEN_INTERVAL_MS :
|
||||||
|
ESP_WIFI_CONNECTIONLESS_INTERVAL_DEFAULT_MODE;
|
||||||
|
const esp_err_t windowResult = esp_now_set_wake_window(window);
|
||||||
|
const esp_err_t intervalResult = esp_wifi_connectionless_module_set_wake_interval(interval);
|
||||||
|
const bool sleepResult = WiFi.setSleep(enabled);
|
||||||
|
const bool ok = windowResult == ESP_OK && intervalResult == ESP_OK && sleepResult;
|
||||||
|
|
||||||
|
bool continuousRestored = true;
|
||||||
|
if (!ok) {
|
||||||
|
continuousRestored = esp_now_set_wake_window(UINT16_MAX) == ESP_OK;
|
||||||
|
continuousRestored =
|
||||||
|
esp_wifi_connectionless_module_set_wake_interval(
|
||||||
|
ESP_WIFI_CONNECTIONLESS_INTERVAL_DEFAULT_MODE) == ESP_OK && continuousRestored;
|
||||||
|
continuousRestored = WiFi.setSleep(false) && continuousRestored;
|
||||||
|
windowedReceive_ = false;
|
||||||
|
} else windowedReceive_ = enabled;
|
||||||
|
|
||||||
|
Log::printf("ESP-NOW", "RX power-save %s window=%ums interval=%ums %s%s",
|
||||||
|
enabled ? "ON" : "OFF", window, interval,
|
||||||
|
ok ? "OK" : "FAILED; continuous RX restore ",
|
||||||
|
ok ? "" : (continuousRestored ? "OK" : "FAILED"));
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
bool Radio::ensurePeer(const uint8_t mac[6]) {
|
bool Radio::ensurePeer(const uint8_t mac[6]) {
|
||||||
if (esp_now_is_peer_exist(mac)) return true;
|
if (esp_now_is_peer_exist(mac)) return true;
|
||||||
esp_now_peer_info_t peer = {};
|
esp_now_peer_info_t peer = {};
|
||||||
@@ -80,7 +110,8 @@ bool Radio::sendTo(const uint8_t mac[6], ProtocolPacket p) {
|
|||||||
finalizePacket(p);
|
finalizePacket(p);
|
||||||
const bool ok = esp_now_send(mac, reinterpret_cast<const uint8_t *>(&p), sizeof(p)) == ESP_OK;
|
const bool ok = esp_now_send(mac, reinterpret_cast<const uint8_t *>(&p), sizeof(p)) == ESP_OK;
|
||||||
const MessageType type = static_cast<MessageType>(p.type);
|
const MessageType type = static_cast<MessageType>(p.type);
|
||||||
if (type != MessageType::HEARTBEAT && type != MessageType::HEARTBEAT_ACK && type != MessageType::PROGRESS)
|
if (type != MessageType::DISCOVER && type != MessageType::HEARTBEAT &&
|
||||||
|
type != MessageType::HEARTBEAT_ACK && type != MessageType::PROGRESS)
|
||||||
Log::printf("ESP-NOW", "TX %s to %s session=%08lX stage=%u seq=%u %s",
|
Log::printf("ESP-NOW", "TX %s to %s session=%08lX stage=%u seq=%u %s",
|
||||||
messageName(type), peer, p.session, p.stage, p.sequence, ok ? "QUEUED" : "FAILED");
|
messageName(type), peer, p.session, p.stage, p.sequence, ok ? "QUEUED" : "FAILED");
|
||||||
return ok;
|
return ok;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class Radio {
|
|||||||
bool sendBroadcast(ProtocolPacket packet);
|
bool sendBroadcast(ProtocolPacket packet);
|
||||||
bool sendTo(const uint8_t mac[6], ProtocolPacket packet);
|
bool sendTo(const uint8_t mac[6], ProtocolPacket packet);
|
||||||
bool receive(ReceivedPacket &received);
|
bool receive(ReceivedPacket &received);
|
||||||
|
bool setWindowedReceive(bool enabled);
|
||||||
void flush();
|
void flush();
|
||||||
uint32_t lastReceiveMs() const { return __atomic_load_n(&lastValidRxMs_, __ATOMIC_RELAXED); }
|
uint32_t lastReceiveMs() const { return __atomic_load_n(&lastValidRxMs_, __ATOMIC_RELAXED); }
|
||||||
static void macText(const uint8_t mac[6], char *out, size_t size);
|
static void macText(const uint8_t mac[6], char *out, size_t size);
|
||||||
@@ -28,5 +29,6 @@ class Radio {
|
|||||||
volatile bool active_ = false;
|
volatile bool active_ = false;
|
||||||
volatile uint32_t lastValidRxMs_ = 0;
|
volatile uint32_t lastValidRxMs_ = 0;
|
||||||
uint32_t lastChannelCheckMs_ = 0;
|
uint32_t lastChannelCheckMs_ = 0;
|
||||||
|
bool windowedReceive_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user