Compare commits
2 Commits
9d474eb62b
...
862781fa6a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
862781fa6a | ||
|
|
f56595f767 |
@@ -53,6 +53,12 @@ const char *uiFailName(FailReason reason) {
|
|||||||
? UiText::FAIL_NAMES[index] : "UNKNOWN";
|
? UiText::FAIL_NAMES[index] : "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *roleCorner(Role role) {
|
||||||
|
static const char *markers[] = {"O", "M", "S"};
|
||||||
|
const uint8_t index = static_cast<uint8_t>(role);
|
||||||
|
return index < sizeof(markers) / sizeof(markers[0]) ? markers[index] : "?";
|
||||||
|
}
|
||||||
|
|
||||||
void formatMenuLine(const char *label, const char *value, char *out, size_t size) {
|
void formatMenuLine(const char *label, const char *value, char *out, size_t size) {
|
||||||
constexpr size_t OLED_TEXT_COLUMNS = 21;
|
constexpr size_t OLED_TEXT_COLUMNS = 21;
|
||||||
const size_t labelLength = utf8CharacterCount(label);
|
const size_t labelLength = utf8CharacterCount(label);
|
||||||
@@ -127,6 +133,7 @@ void App::update() {
|
|||||||
else if (now - bootCheckStartedMs_ >= FACTORY_RESET_HOLD_MS) finishInitialization(true);
|
else if (now - bootCheckStartedMs_ >= FACTORY_RESET_HOLD_MS) finishInitialization(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
serviceRxPinStateLog();
|
||||||
if (state_ != AppState::IDLE && state_ != AppState::MENU && state_ != AppState::FINISHED &&
|
if (state_ != AppState::IDLE && state_ != AppState::MENU && state_ != AppState::FINISHED &&
|
||||||
startEvent == ButtonEvent::LONG) { abortTest(); return; }
|
startEvent == ButtonEvent::LONG) { abortTest(); return; }
|
||||||
if (state_ != AppState::IDLE && state_ != AppState::MENU && state_ != AppState::FINISHED &&
|
if (state_ != AppState::IDLE && state_ != AppState::MENU && state_ != AppState::FINISHED &&
|
||||||
@@ -197,6 +204,7 @@ void App::update() {
|
|||||||
|
|
||||||
void App::showIdle() {
|
void App::showIdle() {
|
||||||
setActivePerformance(false);
|
setActivePerformance(false);
|
||||||
|
setStandbyOpticalOutput();
|
||||||
lastUserActivityMs_ = millis();
|
lastUserActivityMs_ = millis();
|
||||||
char one[64]; snprintf(one, sizeof(one), "%s%s", UiText::MODE_PREFIX,
|
char one[64]; snprintf(one, sizeof(one), "%s%s", UiText::MODE_PREFIX,
|
||||||
uiRoleName(static_cast<Role>(settings_.role)));
|
uiRoleName(static_cast<Role>(settings_.role)));
|
||||||
@@ -208,6 +216,19 @@ void App::sanitizeRange() {
|
|||||||
settings_.endIndex %= countOf(END_FREQ_OPTIONS_HZ);
|
settings_.endIndex %= countOf(END_FREQ_OPTIONS_HZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::serviceRxPinStateLog() {
|
||||||
|
#ifdef RX_PIN_CHANGE_TEST
|
||||||
|
const bool level = digitalRead(GPIO_RX) == HIGH;
|
||||||
|
const bool outsideTest = state_ == AppState::IDLE || state_ == AppState::MENU ||
|
||||||
|
state_ == AppState::SLAVE_READY || state_ == AppState::FINISHED;
|
||||||
|
if (rxPinStateKnown_ && level != rxPinState_ && outsideTest)
|
||||||
|
Log::printf("RX TEST", "GPIO=%u state=%s (%u)", GPIO_RX,
|
||||||
|
level ? "HIGH" : "LOW", level ? 1U : 0U);
|
||||||
|
rxPinState_ = level;
|
||||||
|
rxPinStateKnown_ = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void App::changeMenu(int d) {
|
void App::changeMenu(int d) {
|
||||||
sanitizeRange();
|
sanitizeRange();
|
||||||
uint8_t *value = nullptr; size_t count = 0;
|
uint8_t *value = nullptr; size_t count = 0;
|
||||||
@@ -258,6 +279,7 @@ void App::showMenu() {
|
|||||||
|
|
||||||
void App::startTest() {
|
void App::startTest() {
|
||||||
leaveIdlePowerSave();
|
leaveIdlePowerSave();
|
||||||
|
pwm_.stop();
|
||||||
setActivePerformance(true);
|
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;
|
||||||
@@ -284,6 +306,7 @@ void App::startTest() {
|
|||||||
|
|
||||||
bool App::armSlave(bool preserveDisplay) {
|
bool App::armSlave(bool preserveDisplay) {
|
||||||
setActivePerformance(false);
|
setActivePerformance(false);
|
||||||
|
pwm_.stop();
|
||||||
lastUserActivityMs_ = millis();
|
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);
|
||||||
@@ -292,7 +315,8 @@ bool App::armSlave(bool preserveDisplay) {
|
|||||||
if (!radio_.begin()) {
|
if (!radio_.begin()) {
|
||||||
state_ = AppState::FINISHED; pendingReason_ = FailReason::LINK_LOST;
|
state_ = AppState::FINISHED; pendingReason_ = FailReason::LINK_LOST;
|
||||||
slaveRearmAtMs_ = millis() + LINK_HEARTBEAT_TIMEOUT_MS;
|
slaveRearmAtMs_ = millis() + LINK_HEARTBEAT_TIMEOUT_MS;
|
||||||
display_.show(UiText::LINK_FAILED, UiText::RADIO_ERROR);
|
display_.show(UiText::LINK_FAILED, UiText::RADIO_ERROR,
|
||||||
|
0, 0, roleCorner(Role::SLAVE));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
radio_.setWindowedReceive(true);
|
radio_.setWindowedReceive(true);
|
||||||
@@ -359,6 +383,9 @@ void App::stagePassed() {
|
|||||||
void App::startMasterDiscovery() {
|
void App::startMasterDiscovery() {
|
||||||
session_ = esp_random(); if (!session_) session_ = 1;
|
session_ = esp_random(); if (!session_) session_ = 1;
|
||||||
sequence_ = 1; stageIndex_ = 0; requestedHz_ = 0; havePeer_ = false; radio_.flush();
|
sequence_ = 1; stageIndex_ = 0; requestedHz_ = 0; havePeer_ = false; radio_.flush();
|
||||||
|
opticalWakeActive_ = true;
|
||||||
|
lastOpticalWakeToggleMs_ = millis();
|
||||||
|
pwm_.active();
|
||||||
pendingPacket_ = makePacket(MessageType::DISCOVER); radio_.sendBroadcast(pendingPacket_);
|
pendingPacket_ = makePacket(MessageType::DISCOVER); radio_.sendBroadcast(pendingPacket_);
|
||||||
lastSendMs_ = millis(); retries_ = 0;
|
lastSendMs_ = millis(); retries_ = 0;
|
||||||
state_ = AppState::MASTER_DISCOVER; Log::printf("ESP-NOW", "discovery started session=%08lX", session_);
|
state_ = AppState::MASTER_DISCOVER; Log::printf("ESP-NOW", "discovery started session=%08lX", session_);
|
||||||
@@ -421,6 +448,7 @@ void App::handleRadio() {
|
|||||||
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();
|
leaveIdlePowerSave();
|
||||||
|
pwm_.stop();
|
||||||
setActivePerformance(true);
|
setActivePerformance(true);
|
||||||
radio_.setWindowedReceive(false);
|
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;
|
||||||
@@ -429,6 +457,8 @@ void App::handleRadio() {
|
|||||||
state_ = AppState::SLAVE_WAIT_START; display_.show(UiText::MASTER_SEEN, UiText::ACK_SENT); continue;
|
state_ = AppState::SLAVE_WAIT_START; display_.show(UiText::MASTER_SEEN, UiText::ACK_SENT); continue;
|
||||||
}
|
}
|
||||||
if (state_ == AppState::MASTER_DISCOVER && type == MessageType::DISCOVER_ACK && r.packet.session == session_) {
|
if (state_ == AppState::MASTER_DISCOVER && type == MessageType::DISCOVER_ACK && r.packet.session == session_) {
|
||||||
|
opticalWakeActive_ = false;
|
||||||
|
pwm_.stop();
|
||||||
memcpy(peer_, r.mac, 6); havePeer_ = true; lastPeerSeenMs_ = lastHeartbeatMs_ = millis();
|
memcpy(peer_, r.mac, 6); havePeer_ = true; lastPeerSeenMs_ = lastHeartbeatMs_ = millis();
|
||||||
requestedHz_ = frequencyAt(params_.startHz, params_.endHz, stageIndex_);
|
requestedHz_ = frequencyAt(params_.startHz, params_.endHz, stageIndex_);
|
||||||
sendCurrent(MessageType::PREPARE); state_ = AppState::MASTER_WAIT_READY; retries_ = 0; deadlineMs_ = millis() + LINK_REPLY_TIMEOUT_MS;
|
sendCurrent(MessageType::PREPARE); state_ = AppState::MASTER_WAIT_READY; retries_ = 0; deadlineMs_ = millis() + LINK_REPLY_TIMEOUT_MS;
|
||||||
@@ -537,6 +567,12 @@ 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 - lastOpticalWakeToggleMs_ >= OPTICAL_WAKE_HALF_PERIOD_MS) {
|
||||||
|
opticalWakeActive_ = !opticalWakeActive_;
|
||||||
|
if (opticalWakeActive_) pwm_.active();
|
||||||
|
else pwm_.stop();
|
||||||
|
lastOpticalWakeToggleMs_ = now;
|
||||||
|
}
|
||||||
if (now - lastSendMs_ >= DISCOVERY_RETRY_INTERVAL_MS) {
|
if (now - lastSendMs_ >= DISCOVERY_RETRY_INTERVAL_MS) {
|
||||||
if (++retries_ % 50U == 0U) Log::event("ESP-NOW", "DISCOVER burst continues");
|
if (++retries_ % 50U == 0U) Log::event("ESP-NOW", "DISCOVER burst continues");
|
||||||
radio_.sendBroadcast(pendingPacket_);
|
radio_.sendBroadcast(pendingPacket_);
|
||||||
@@ -637,6 +673,7 @@ 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;
|
||||||
|
setStandbyOpticalOutput();
|
||||||
setActivePerformance(false);
|
setActivePerformance(false);
|
||||||
lastUserActivityMs_ = millis();
|
lastUserActivityMs_ = millis();
|
||||||
if (slaveLinkLost) {
|
if (slaveLinkLost) {
|
||||||
@@ -644,7 +681,8 @@ void App::finish(bool pass, FailReason reason, bool preserveDisplay) {
|
|||||||
Display::formatTestFrequency(actual_.actualHz ? actual_.actualHz : requestedHz_, target, sizeof(target));
|
Display::formatTestFrequency(actual_.actualHz ? actual_.actualHz : requestedHz_, target, sizeof(target));
|
||||||
snprintf(one, sizeof(one), UiText::FAIL_FORMAT, target,
|
snprintf(one, sizeof(one), UiText::FAIL_FORMAT, target,
|
||||||
actual_.actualDutyPct > 0.0f ? actual_.actualDutyPct : params_.dutyPct);
|
actual_.actualDutyPct > 0.0f ? actual_.actualDutyPct : params_.dutyPct);
|
||||||
display_.show(one, uiFailName(reason), stageIndex_ + 1, stageCount_);
|
display_.show(one, uiFailName(reason), stageIndex_ + 1, stageCount_,
|
||||||
|
roleCorner(Role::SLAVE));
|
||||||
armSlave(true);
|
armSlave(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -661,9 +699,11 @@ void App::finish(bool pass, FailReason reason, bool preserveDisplay) {
|
|||||||
Display::formatTestFrequency(actual_.actualHz ? actual_.actualHz : requestedHz_, frequency, sizeof(frequency));
|
Display::formatTestFrequency(actual_.actualHz ? actual_.actualHz : requestedHz_, frequency, sizeof(frequency));
|
||||||
snprintf(one, sizeof(one), UiText::FAIL_FORMAT, frequency,
|
snprintf(one, sizeof(one), UiText::FAIL_FORMAT, frequency,
|
||||||
actual_.actualDutyPct > 0.0f ? actual_.actualDutyPct : params_.dutyPct);
|
actual_.actualDutyPct > 0.0f ? actual_.actualDutyPct : params_.dutyPct);
|
||||||
display_.show(one, uiFailName(reason), stageIndex_ + 1, stageCount_);
|
display_.show(one, uiFailName(reason), stageIndex_ + 1, stageCount_,
|
||||||
|
roleCorner(static_cast<Role>(settings_.role)));
|
||||||
} else {
|
} else {
|
||||||
display_.show(UiText::TEST_FAILED, uiFailName(reason));
|
display_.show(UiText::TEST_FAILED, uiFailName(reason), 0, 0,
|
||||||
|
roleCorner(static_cast<Role>(settings_.role)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,6 +712,11 @@ bool App::idlePowerSaveAllowed() const {
|
|||||||
state_ == AppState::FINISHED || state_ == AppState::SLAVE_READY);
|
state_ == AppState::FINISHED || state_ == AppState::SLAVE_READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::setStandbyOpticalOutput() {
|
||||||
|
if (static_cast<Role>(settings_.role) == Role::SLAVE) pwm_.stop();
|
||||||
|
else pwm_.active();
|
||||||
|
}
|
||||||
|
|
||||||
void App::setActivePerformance(bool active) {
|
void App::setActivePerformance(bool active) {
|
||||||
const uint32_t targetMhz = active ? 160U : 80U;
|
const uint32_t targetMhz = active ? 160U : 80U;
|
||||||
if (getCpuFrequencyMhz() != targetMhz && !setCpuFrequencyMhz(targetMhz))
|
if (getCpuFrequencyMhz() != targetMhz && !setCpuFrequencyMhz(targetMhz))
|
||||||
@@ -685,6 +730,15 @@ void App::leaveIdlePowerSave(bool wakeDisplay) {
|
|||||||
}
|
}
|
||||||
idlePowerSave_ = false;
|
idlePowerSave_ = false;
|
||||||
lastUserActivityMs_ = millis();
|
lastUserActivityMs_ = millis();
|
||||||
|
setStandbyOpticalOutput();
|
||||||
|
if (idleSleepRadioStopped_) {
|
||||||
|
idleSleepRadioStopped_ = false;
|
||||||
|
if (radio_.begin()) {
|
||||||
|
radio_.setWindowedReceive(true);
|
||||||
|
radio_.flush();
|
||||||
|
Log::event("POWER", "Slave ESP-NOW restored after external wake");
|
||||||
|
} else Log::event("POWER", "Slave ESP-NOW restore FAILED after external wake");
|
||||||
|
}
|
||||||
if (wakeDisplay) display_.setPower(true);
|
if (wakeDisplay) display_.setPower(true);
|
||||||
Log::event("POWER", "idle light sleep ended");
|
Log::event("POWER", "idle light sleep ended");
|
||||||
}
|
}
|
||||||
@@ -702,6 +756,11 @@ void App::serviceIdlePowerSave() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
idlePowerSave_ = true;
|
idlePowerSave_ = true;
|
||||||
|
pwm_.stop();
|
||||||
|
if (state_ == AppState::SLAVE_READY) {
|
||||||
|
radio_.end();
|
||||||
|
idleSleepRadioStopped_ = true;
|
||||||
|
}
|
||||||
display_.setPower(false);
|
display_.setPower(false);
|
||||||
Log::event("POWER", "idle timeout; OLED off and light sleep started");
|
Log::event("POWER", "idle timeout; OLED off and light sleep started");
|
||||||
}
|
}
|
||||||
@@ -710,8 +769,16 @@ void App::serviceIdlePowerSave() {
|
|||||||
BUTTON_ACTIVE_LEVEL == LOW ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
BUTTON_ACTIVE_LEVEL == LOW ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||||
gpio_wakeup_enable(static_cast<gpio_num_t>(GPIO_BUTTON_MODE),
|
gpio_wakeup_enable(static_cast<gpio_num_t>(GPIO_BUTTON_MODE),
|
||||||
BUTTON_ACTIVE_LEVEL == LOW ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
BUTTON_ACTIVE_LEVEL == LOW ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||||
|
if (static_cast<Role>(settings_.role) == Role::SLAVE) {
|
||||||
|
// Light-sleep GPIO wake is level-triggered in ESP-IDF. Arm the level
|
||||||
|
// opposite to the one sampled immediately before sleep, which makes a
|
||||||
|
// transition (either edge) necessary and prevents a steady RX level from
|
||||||
|
// waking Slave continuously.
|
||||||
|
const bool currentRxHigh = gpio_get_level(static_cast<gpio_num_t>(GPIO_RX)) != 0;
|
||||||
|
gpio_wakeup_enable(static_cast<gpio_num_t>(GPIO_RX),
|
||||||
|
currentRxHigh ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||||
|
} else gpio_wakeup_disable(static_cast<gpio_num_t>(GPIO_RX));
|
||||||
esp_sleep_enable_gpio_wakeup();
|
esp_sleep_enable_gpio_wakeup();
|
||||||
esp_sleep_enable_timer_wakeup(IDLE_LIGHT_SLEEP_SLICE_US);
|
|
||||||
const esp_err_t result = esp_light_sleep_start();
|
const esp_err_t result = esp_light_sleep_start();
|
||||||
if (result != ESP_OK) {
|
if (result != ESP_OK) {
|
||||||
delay(1);
|
delay(1);
|
||||||
@@ -719,12 +786,19 @@ void App::serviceIdlePowerSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO) {
|
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO) {
|
||||||
// The wake-up press is deliberately consumed. Holding or releasing it
|
const bool buttonWake = digitalRead(GPIO_BUTTON_START) == BUTTON_ACTIVE_LEVEL ||
|
||||||
// must not later turn into a SHORT, LONG, or REPEAT event.
|
digitalRead(GPIO_BUTTON_MODE) == BUTTON_ACTIVE_LEVEL;
|
||||||
startButton_.suppressUntilRelease();
|
if (buttonWake) {
|
||||||
modeButton_.suppressUntilRelease();
|
// The wake-up press is deliberately consumed. Holding or releasing it
|
||||||
leaveIdlePowerSave();
|
// must not later turn into a SHORT, LONG, or REPEAT event.
|
||||||
Log::event("POWER", "button wake consumed; next press will perform the action");
|
startButton_.suppressUntilRelease();
|
||||||
|
modeButton_.suppressUntilRelease();
|
||||||
|
leaveIdlePowerSave();
|
||||||
|
Log::event("POWER", "button wake consumed; next press will perform the action");
|
||||||
|
} else if (static_cast<Role>(settings_.role) == Role::SLAVE) {
|
||||||
|
leaveIdlePowerSave();
|
||||||
|
Log::event("POWER", "optical input woke Slave");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +854,7 @@ void App::showStageResult(const StageStats &s) {
|
|||||||
snprintf(two, sizeof(two), "%s", uiFailName(s.reason));
|
snprintf(two, sizeof(two), "%s", uiFailName(s.reason));
|
||||||
}
|
}
|
||||||
display_.show(one, two, overallProgress(stageIndex_, measurement_.progressStep()),
|
display_.show(one, two, overallProgress(stageIndex_, measurement_.progressStep()),
|
||||||
overallProgressTotal(stageCount_));
|
overallProgressTotal(stageCount_), roleCorner(static_cast<Role>(settings_.role)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char stage[12]; snprintf(stage, sizeof(stage), "%lu/%lu", stageIndex_ + 1, stageCount_);
|
char stage[12]; snprintf(stage, sizeof(stage), "%lu/%lu", stageIndex_ + 1, stageCount_);
|
||||||
@@ -827,7 +901,8 @@ void App::showRemoteResult(const ProtocolPacket &packet) {
|
|||||||
snprintf(two, sizeof(two), "%s", uiFailName(reason));
|
snprintf(two, sizeof(two), "%s", uiFailName(reason));
|
||||||
}
|
}
|
||||||
display_.show(one, two, overallProgress(stageIndex_, packet.progressStep),
|
display_.show(one, two, overallProgress(stageIndex_, packet.progressStep),
|
||||||
overallProgressTotal(stageCount_));
|
overallProgressTotal(stageCount_), reason == FailReason::NONE ? nullptr :
|
||||||
|
roleCorner(static_cast<Role>(settings_.role)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::fillMeasuredResult(ProtocolPacket &packet, const StageStats &stats) const {
|
void App::fillMeasuredResult(ProtocolPacket &packet, const StageStats &stats) const {
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ class App {
|
|||||||
void updateHeartbeat();
|
void updateHeartbeat();
|
||||||
bool packetForCurrent(const ProtocolPacket &p) const;
|
bool packetForCurrent(const ProtocolPacket &p) const;
|
||||||
void serviceIdlePowerSave();
|
void serviceIdlePowerSave();
|
||||||
|
void serviceRxPinStateLog();
|
||||||
void leaveIdlePowerSave(bool wakeDisplay = true);
|
void leaveIdlePowerSave(bool wakeDisplay = true);
|
||||||
bool idlePowerSaveAllowed() const;
|
bool idlePowerSaveAllowed() const;
|
||||||
|
void setStandbyOpticalOutput();
|
||||||
void setActivePerformance(bool active);
|
void setActivePerformance(bool active);
|
||||||
|
|
||||||
Button startButton_, modeButton_;
|
Button startButton_, modeButton_;
|
||||||
@@ -81,4 +83,8 @@ class App {
|
|||||||
bool stageStartConfirmed_ = false;
|
bool stageStartConfirmed_ = false;
|
||||||
uint32_t lastUserActivityMs_ = 0;
|
uint32_t lastUserActivityMs_ = 0;
|
||||||
bool idlePowerSave_ = false;
|
bool idlePowerSave_ = false;
|
||||||
|
bool idleSleepRadioStopped_ = false;
|
||||||
|
uint32_t lastOpticalWakeToggleMs_ = 0;
|
||||||
|
bool opticalWakeActive_ = false;
|
||||||
|
bool rxPinStateKnown_ = false, rxPinState_ = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,22 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
// ------------------------- Hardware configuration -------------------------
|
// ------------------------- Hardware configuration -------------------------
|
||||||
// Uncomment for the hand-wired prototype. The production PCB assignments
|
// Enabled for the hand-wired prototype. Comment out for the production PCB.
|
||||||
// below follow the physical header positions shown in the schematic.
|
// Both profiles map S3 signals by physical header position with 5V/GND aligned.
|
||||||
// #define MAKETKA
|
#define MAKETKA
|
||||||
|
|
||||||
|
// Additionally log raw GPIO_RX level changes while no test is running.
|
||||||
|
//#define RX_PIN_CHANGE_TEST
|
||||||
|
|
||||||
|
// Standalone PWM output check. While enabled, the normal application is not
|
||||||
|
// started: GPIO_PWM continuously outputs the frequency and duty below.
|
||||||
|
// Comment this define out after the hardware check.
|
||||||
|
//#define PWM_OUTPUT_TEST
|
||||||
|
constexpr uint32_t PWM_OUTPUT_TEST_FREQUENCY_HZ = 1000;
|
||||||
|
constexpr uint32_t PWM_OUTPUT_TEST_SWEEP_PERIOD_MS = 2000;
|
||||||
|
constexpr uint32_t PWM_OUTPUT_TEST_UPDATE_MS = 10;
|
||||||
|
constexpr uint8_t PWM_OUTPUT_TEST_MIN_DUTY_PCT = 5;
|
||||||
|
constexpr uint8_t PWM_OUTPUT_TEST_MAX_DUTY_PCT = 95;
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32C3
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
constexpr bool TARGET_IS_C3 = true;
|
constexpr bool TARGET_IS_C3 = true;
|
||||||
@@ -25,12 +38,13 @@ constexpr uint8_t GPIO_SCL = 7;
|
|||||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
constexpr bool TARGET_IS_C3 = false;
|
constexpr bool TARGET_IS_C3 = false;
|
||||||
#ifdef MAKETKA
|
#ifdef MAKETKA
|
||||||
constexpr uint8_t GPIO_PWM = 4;
|
// Same physical header contacts as the C3 MAKETKA profile when 5V/GND align.
|
||||||
constexpr uint8_t GPIO_RX = 5;
|
constexpr uint8_t GPIO_PWM = 12;
|
||||||
constexpr uint8_t GPIO_BUTTON_MODE = 0;
|
constexpr uint8_t GPIO_RX = 13;
|
||||||
constexpr uint8_t GPIO_BUTTON_START = 1;
|
constexpr uint8_t GPIO_BUTTON_MODE = 9;
|
||||||
constexpr uint8_t GPIO_SDA = 8;
|
constexpr uint8_t GPIO_BUTTON_START = 10;
|
||||||
constexpr uint8_t GPIO_SCL = 9;
|
constexpr uint8_t GPIO_SDA = 44;
|
||||||
|
constexpr uint8_t GPIO_SCL = 1;
|
||||||
#else
|
#else
|
||||||
// The S3 SuperMini is fitted so its 5V and GND pins occupy the same PCB
|
// The S3 SuperMini is fitted so its 5V and GND pins occupy the same PCB
|
||||||
// contacts as on the C3 SuperMini. Signals therefore follow header position.
|
// contacts as on the C3 SuperMini. Signals therefore follow header position.
|
||||||
@@ -57,8 +71,14 @@ constexpr bool SERIAL_LOG_TIMESTAMPS = true;
|
|||||||
constexpr bool SERIAL_MINIMAL_LOG = true;
|
constexpr bool SERIAL_MINIMAL_LOG = true;
|
||||||
|
|
||||||
#define BUTTON_ACTIVE_LEVEL LOW
|
#define BUTTON_ACTIVE_LEVEL LOW
|
||||||
#define RX_SIGNAL_INVERTED false
|
// Raw GPIO_RX level that means the optical receiver is active.
|
||||||
#define PWM_SAFE_LEVEL LOW
|
#define RX_ACTIVE_LEVEL LOW
|
||||||
|
// PWM_SAFE_LEVEL must switch the optical transmitter fully off and is used
|
||||||
|
// during tests whenever PWM is stopped, and while the controller sleeps.
|
||||||
|
// PWM_ACTIVE_LEVEL intentionally keeps the transmitter active while the
|
||||||
|
// controller is awake and no test is in progress.
|
||||||
|
#define PWM_SAFE_LEVEL HIGH
|
||||||
|
#define PWM_ACTIVE_LEVEL LOW
|
||||||
#define PWM_SETTLE_CYCLES 5U
|
#define PWM_SETTLE_CYCLES 5U
|
||||||
|
|
||||||
constexpr uint32_t BUTTON_DEBOUNCE_MS = 30;
|
constexpr uint32_t BUTTON_DEBOUNCE_MS = 30;
|
||||||
@@ -71,6 +91,9 @@ 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 DISCOVERY_RETRY_INTERVAL_MS = 20;
|
||||||
|
// During discovery Master alternates PWM_ACTIVE_LEVEL and PWM_SAFE_LEVEL to
|
||||||
|
// wake a sleeping Slave through the optical channel.
|
||||||
|
constexpr uint32_t OPTICAL_WAKE_HALF_PERIOD_MS = 50;
|
||||||
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;
|
||||||
@@ -85,7 +108,6 @@ 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_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_INTERVAL_MS = 100;
|
||||||
constexpr uint16_t SLAVE_LISTEN_WINDOW_MS = 20;
|
constexpr uint16_t SLAVE_LISTEN_WINDOW_MS = 20;
|
||||||
static_assert(SLAVE_LISTEN_WINDOW_MS < SLAVE_LISTEN_INTERVAL_MS,
|
static_assert(SLAVE_LISTEN_WINDOW_MS < SLAVE_LISTEN_INTERVAL_MS,
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ void Display::setPower(bool enabled) {
|
|||||||
Log::printf("OLED", "display power %s", enabled ? "ON" : "OFF");
|
Log::printf("OLED", "display power %s", enabled ? "ON" : "OFF");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Display::drawTextLine(const char *text, int16_t y) {
|
void Display::drawTextLine(const char *text, int16_t y, int16_t startX) {
|
||||||
int16_t x = 0;
|
int16_t x = startX;
|
||||||
while (text && *text && x + CyrillicFont::WIDTH <= oled_.width()) {
|
while (text && *text && x + CyrillicFont::WIDTH <= oled_.width()) {
|
||||||
const uint32_t codepoint = nextUtf8Codepoint(text);
|
const uint32_t codepoint = nextUtf8Codepoint(text);
|
||||||
const uint8_t *glyph = CyrillicFont::glyph(codepoint);
|
const uint8_t *glyph = CyrillicFont::glyph(codepoint);
|
||||||
@@ -75,7 +75,8 @@ void Display::drawTextLine(const char *text, int16_t y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Display::show(const char *a, const char *b, uint32_t progress, uint32_t progressTotal) {
|
void Display::show(const char *a, const char *b, uint32_t progress,
|
||||||
|
uint32_t progressTotal, const char *topRight) {
|
||||||
char one[64], two[64];
|
char one[64], two[64];
|
||||||
snprintf(one, sizeof(one), "%s", a ? a : ""); snprintf(two, sizeof(two), "%s", b ? b : "");
|
snprintf(one, sizeof(one), "%s", a ? a : ""); snprintf(two, sizeof(two), "%s", b ? b : "");
|
||||||
// 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.
|
||||||
@@ -85,6 +86,11 @@ void Display::show(const char *a, const char *b, uint32_t progress, uint32_t pro
|
|||||||
oled_.clearDisplay();
|
oled_.clearDisplay();
|
||||||
drawTextLine(one, 3);
|
drawTextLine(one, 3);
|
||||||
drawTextLine(two, 19);
|
drawTextLine(two, 19);
|
||||||
|
if (topRight && *topRight) {
|
||||||
|
oled_.fillRect(oled_.width() - CyrillicFont::ADVANCE, 0,
|
||||||
|
CyrillicFont::ADVANCE, CyrillicFont::HEIGHT + 3, SSD1306_BLACK);
|
||||||
|
drawTextLine(topRight, 3, oled_.width() - CyrillicFont::ADVANCE);
|
||||||
|
}
|
||||||
if (progressTotal) {
|
if (progressTotal) {
|
||||||
if (progress > progressTotal) progress = progressTotal;
|
if (progress > progressTotal) progress = progressTotal;
|
||||||
const uint16_t width = static_cast<uint16_t>(
|
const uint16_t width = static_cast<uint16_t>(
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class Display {
|
|||||||
Display();
|
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,
|
||||||
|
const char *topRight = nullptr);
|
||||||
void setPower(bool enabled);
|
void setPower(bool enabled);
|
||||||
bool available() const { return ok_; }
|
bool available() const { return ok_; }
|
||||||
bool powered() const { return powered_; }
|
bool powered() const { return powered_; }
|
||||||
@@ -16,7 +17,7 @@ class Display {
|
|||||||
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);
|
||||||
private:
|
private:
|
||||||
void drawTextLine(const char *text, int16_t y);
|
void drawTextLine(const char *text, int16_t y, int16_t startX = 0);
|
||||||
Adafruit_SSD1306 oled_;
|
Adafruit_SSD1306 oled_;
|
||||||
bool ok_ = false;
|
bool ok_ = false;
|
||||||
bool powered_ = false;
|
bool powered_ = false;
|
||||||
|
|||||||
@@ -1,7 +1,57 @@
|
|||||||
#include "App.h"
|
#include "App.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#ifdef PWM_OUTPUT_TEST
|
||||||
|
PwmGenerator pwmOutputTest;
|
||||||
|
uint8_t pwmOutputTestDuty = 50;
|
||||||
|
uint32_t pwmOutputTestUpdatedMs = 0;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(SERIAL_BAUD);
|
||||||
|
delay(200);
|
||||||
|
Serial.printf("\nPWM OUTPUT TEST: GPIO=%u requested=%luHz duty=%u..%u%% sine=%lums safe=%s\n",
|
||||||
|
GPIO_PWM, PWM_OUTPUT_TEST_FREQUENCY_HZ,
|
||||||
|
PWM_OUTPUT_TEST_MIN_DUTY_PCT, PWM_OUTPUT_TEST_MAX_DUTY_PCT,
|
||||||
|
PWM_OUTPUT_TEST_SWEEP_PERIOD_MS,
|
||||||
|
PWM_SAFE_LEVEL == HIGH ? "HIGH" : "LOW");
|
||||||
|
|
||||||
|
pwmOutputTest.begin();
|
||||||
|
ActualPwm actual = {};
|
||||||
|
if (pwmOutputTest.start(PWM_OUTPUT_TEST_FREQUENCY_HZ,
|
||||||
|
pwmOutputTestDuty, actual)) {
|
||||||
|
Serial.printf("PWM OUTPUT TEST STARTED: actual=%luHz duty=%.2f%% bits=%u\n",
|
||||||
|
actual.actualHz, actual.actualDutyPct, actual.bits);
|
||||||
|
} else {
|
||||||
|
Serial.println("PWM OUTPUT TEST FAILED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
const uint32_t now = millis();
|
||||||
|
if (now - pwmOutputTestUpdatedMs < PWM_OUTPUT_TEST_UPDATE_MS) return;
|
||||||
|
pwmOutputTestUpdatedMs = now;
|
||||||
|
|
||||||
|
constexpr float PWM_TWO_PI = 6.28318530718f;
|
||||||
|
const float phase = PWM_TWO_PI * (now % PWM_OUTPUT_TEST_SWEEP_PERIOD_MS) /
|
||||||
|
PWM_OUTPUT_TEST_SWEEP_PERIOD_MS;
|
||||||
|
const float center = (PWM_OUTPUT_TEST_MIN_DUTY_PCT + PWM_OUTPUT_TEST_MAX_DUTY_PCT) * 0.5f;
|
||||||
|
const float amplitude = (PWM_OUTPUT_TEST_MAX_DUTY_PCT - PWM_OUTPUT_TEST_MIN_DUTY_PCT) * 0.5f;
|
||||||
|
const uint8_t duty = static_cast<uint8_t>(center + amplitude * sinf(phase) + 0.5f);
|
||||||
|
if (duty == pwmOutputTestDuty) return;
|
||||||
|
|
||||||
|
ActualPwm actual = {};
|
||||||
|
if (pwmOutputTest.start(PWM_OUTPUT_TEST_FREQUENCY_HZ, duty, actual)) {
|
||||||
|
pwmOutputTestDuty = duty;
|
||||||
|
} else {
|
||||||
|
Serial.printf("PWM OUTPUT TEST UPDATE FAILED: duty=%u%%\n", duty);
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
App app;
|
App app;
|
||||||
|
|
||||||
void setup() { app.begin(); }
|
void setup() { app.begin(); }
|
||||||
void loop() { app.update(); }
|
void loop() { app.update(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,10 @@ bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
|||||||
stop();
|
stop();
|
||||||
bool ok = mcpwm_timer_set_period(mcpwmTimer, periodTicks) == ESP_OK;
|
bool ok = mcpwm_timer_set_period(mcpwmTimer, periodTicks) == ESP_OK;
|
||||||
ok = ok && mcpwm_comparator_set_compare_value(mcpwmComparator, activeTicks) == ESP_OK;
|
ok = ok && mcpwm_comparator_set_compare_value(mcpwmComparator, activeTicks) == ESP_OK;
|
||||||
ok = ok && mcpwm_generator_set_force_level(mcpwmGenerator, -1, false) == ESP_OK;
|
// stop() applies a continuous force level (hold_on=true). Remove that same
|
||||||
|
// continuous-force action; hold_on=false addresses a different, one-shot
|
||||||
|
// force mechanism and would leave the safe level permanently active.
|
||||||
|
ok = ok && mcpwm_generator_set_force_level(mcpwmGenerator, -1, true) == ESP_OK;
|
||||||
ok = ok && mcpwm_timer_start_stop(mcpwmTimer, MCPWM_TIMER_START_NO_STOP) == ESP_OK;
|
ok = ok && mcpwm_timer_start_stop(mcpwmTimer, MCPWM_TIMER_START_NO_STOP) == ESP_OK;
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
mcpwm_generator_set_force_level(mcpwmGenerator, PWM_SAFE_LEVEL, true);
|
mcpwm_generator_set_force_level(mcpwmGenerator, PWM_SAFE_LEVEL, true);
|
||||||
@@ -184,3 +187,18 @@ void PwmGenerator::stop() {
|
|||||||
#endif
|
#endif
|
||||||
running_ = false;
|
running_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PwmGenerator::active() {
|
||||||
|
// First detach/stop the PWM peripheral, then select the independently
|
||||||
|
// configured active level. The active and safe levels may be equal.
|
||||||
|
stop();
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
digitalWrite(GPIO_PWM, PWM_ACTIVE_LEVEL);
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
if (mcpwmGenerator) mcpwm_generator_set_force_level(mcpwmGenerator, PWM_ACTIVE_LEVEL, true);
|
||||||
|
else {
|
||||||
|
pinMode(GPIO_PWM, OUTPUT);
|
||||||
|
digitalWrite(GPIO_PWM, PWM_ACTIVE_LEVEL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ class PwmGenerator {
|
|||||||
public:
|
public:
|
||||||
void begin();
|
void begin();
|
||||||
bool start(uint32_t frequencyHz, uint8_t dutyPct, ActualPwm &actual);
|
bool start(uint32_t frequencyHz, uint8_t dutyPct, ActualPwm &actual);
|
||||||
|
void active();
|
||||||
void stop();
|
void stop();
|
||||||
bool running() const { return running_; }
|
bool running() const { return running_; }
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ void Radio::end() {
|
|||||||
if (heartbeatQueue_) xQueueReset(heartbeatQueue_);
|
if (heartbeatQueue_) xQueueReset(heartbeatQueue_);
|
||||||
if (instance_ == this) instance_ = nullptr;
|
if (instance_ == this) instance_ = nullptr;
|
||||||
windowedReceive_ = false;
|
windowedReceive_ = false;
|
||||||
|
if (wasActive) WiFi.mode(WIFI_OFF);
|
||||||
Log::event("ESP-NOW", "stopped");
|
Log::event("ESP-NOW", "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ bool PulseReceiver::configureRmt(uint32_t resolutionHz) {
|
|||||||
rmt_rx_channel_config_t cfg = {};
|
rmt_rx_channel_config_t cfg = {};
|
||||||
cfg.clk_src = RMT_CLK_SRC_DEFAULT; cfg.resolution_hz = resolutionHz;
|
cfg.clk_src = RMT_CLK_SRC_DEFAULT; cfg.resolution_hz = resolutionHz;
|
||||||
cfg.gpio_num = static_cast<gpio_num_t>(GPIO_RX);
|
cfg.gpio_num = static_cast<gpio_num_t>(GPIO_RX);
|
||||||
cfg.flags.invert_in = RX_SIGNAL_INVERTED;
|
// Internally the measurement code always treats HIGH as the active phase.
|
||||||
|
cfg.flags.invert_in = RX_ACTIVE_LEVEL == LOW;
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3
|
#if CONFIG_IDF_TARGET_ESP32S3
|
||||||
cfg.mem_block_symbols = 512;
|
cfg.mem_block_symbols = 512;
|
||||||
cfg.flags.with_dma = true;
|
cfg.flags.with_dma = true;
|
||||||
@@ -205,7 +206,7 @@ size_t PulseReceiver::readPeriods(PulsePeriod *periods, size_t capacity, TickTyp
|
|||||||
void IRAM_ATTR PulseReceiver::onGpio(void *ctx) {
|
void IRAM_ATTR PulseReceiver::onGpio(void *ctx) {
|
||||||
PulseReceiver *self = static_cast<PulseReceiver *>(ctx);
|
PulseReceiver *self = static_cast<PulseReceiver *>(ctx);
|
||||||
bool level = gpio_get_level(static_cast<gpio_num_t>(GPIO_RX));
|
bool level = gpio_get_level(static_cast<gpio_num_t>(GPIO_RX));
|
||||||
if (RX_SIGNAL_INVERTED) level = !level;
|
if (RX_ACTIVE_LEVEL == LOW) level = !level;
|
||||||
Edge e = {esp_cpu_get_cycle_count(), static_cast<uint8_t>(level)};
|
Edge e = {esp_cpu_get_cycle_count(), static_cast<uint8_t>(level)};
|
||||||
BaseType_t wake = pdFALSE;
|
BaseType_t wake = pdFALSE;
|
||||||
if (xQueueSendFromISR(self->queue_, &e, &wake) != pdTRUE)
|
if (xQueueSendFromISR(self->queue_, &e, &wake) != pdTRUE)
|
||||||
|
|||||||
Reference in New Issue
Block a user