Files
MeshCore/src/helpers/CarrierWaveCLI.h
T
mikecarper 9de1373bd9 Add timed cw and cw2 commands for SX1262 and LR1110
Support fractional seconds, guard active packet work, keep the MCU awake, and restore reception after carrier tests. Include regression coverage and independent-radio RF validation for the V4 and T1000-E.
2026-09-14 19:23:23 -07:00

70 lines
2.9 KiB
C++

#pragma once
#include <Dispatcher.h>
#include <helpers/CLICommandUtils.h>
namespace mesh {
// Volatile maintenance commands shared by every role using RadioProfileCLI.
inline bool handleCarrierWaveCommand(Radio* radio, const char* command,
char* reply, size_t capacity) {
const char* text = command;
bool query = false;
if (!strncmp(text, "get ", 4)) { text += 4; query = true; }
else if (!strncmp(text, "set ", 4)) text += 4;
const size_t length = strcspn(text, " \t");
const bool secondary = length == 3 && !strncmp(text, "cw2", 3);
if (!secondary && !(length == 2 && !strncmp(text, "cw", 2))) return false;
const char* name = secondary ? "cw2" : "cw";
const char* args = cli::skipRecentRepeaterSpaces(text + length);
if (!radio || !radio->supportsCarrierWave()) {
snprintf(reply, capacity, "Error: CW unsupported on this radio");
return true;
}
if (!*args) {
if (radio->isCarrierWaveActive()) {
const uint32_t remaining = radio->carrierWaveRemainingMillis();
snprintf(reply, capacity, "> %s on; %lu.%03lu seconds left",
radio->carrierWaveProfile() ? "cw2" : "cw",
(unsigned long)(remaining / 1000), (unsigned long)(remaining % 1000));
} else snprintf(reply, capacity, "> off");
return true;
}
uint32_t duration = 0;
const size_t action_length = strcspn(args, " \t");
const char* seconds = cli::skipRecentRepeaterSpaces(args + action_length);
const bool on = action_length == 2 && !strncmp(args, "on", 2);
const bool off = action_length == 3 && !strncmp(args, "off", 3);
bool valid = !query && (on || (off && !*seconds));
if (on) {
duration = Radio::CarrierWaveDefaultMillis;
if (*seconds) {
float value;
valid = valid && cli::parseDecimalStrict(seconds, value)
&& value >= 0.001f && value <= Radio::CarrierWaveMaxMillis / 1000.0f;
if (valid) duration = static_cast<uint32_t>(value * 1000.0f + 0.5f);
}
}
if (!valid) {
snprintf(reply, capacity, "Error: use %s on [seconds 0.001-60] | %s off", name, name);
return true;
}
if (on && secondary && (!radio->profiles() || !radio->profiles()->canTransmit(1))) {
snprintf(reply, capacity, "Error: cw2 requires active radio2 rxtx");
return true;
}
const auto result = radio->setCarrierWave(secondary ? 1 : 0, duration);
if (result == RadioParamApplyResult::BUSY) {
snprintf(reply, capacity, "Error: radio busy; stop active CW or retry when idle");
} else if (result != RadioParamApplyResult::APPLIED) {
snprintf(reply, capacity, "Error: CW transition failed; check radio recovery");
} else if (on) {
snprintf(reply, capacity, "OK - %s on for %lu.%03lu seconds; RX paused", name,
(unsigned long)(duration / 1000), (unsigned long)(duration % 1000));
} else snprintf(reply, capacity, "OK - carrier off%s",
radio->isInRecvMode() ? ", receive resumed" : "");
return true;
}
} // namespace mesh