98 lines
3.3 KiB
C++
98 lines
3.3 KiB
C++
#include <ESP32Servo.h>
|
|
#include <Preferences.h>
|
|
#include "app.h"
|
|
#include "config.h"
|
|
|
|
static Servo servo;
|
|
static Preferences prefs;
|
|
|
|
static int volumeToUs(uint8_t volume) {
|
|
return map(volume, 0, 100, SERVO_MIN_US, SERVO_MAX_US);
|
|
}
|
|
|
|
static void applyRelay(bool on) {
|
|
digitalWrite(PIN_RELAY, on ? RELAY_ACTIVE_LEVEL : !RELAY_ACTIVE_LEVEL);
|
|
}
|
|
|
|
// Owns the servo, the relay and NVS. Other tasks only send commands.
|
|
void controlTask(void *arg) {
|
|
pinMode(PIN_RELAY, OUTPUT);
|
|
|
|
prefs.begin("volume", false);
|
|
AppState st;
|
|
st.volume = prefs.getUChar("vol", 30);
|
|
st.relay = prefs.getBool("relay", false);
|
|
setState(st);
|
|
applyRelay(st.relay);
|
|
|
|
servo.setPeriodHertz(50);
|
|
int currentUs = volumeToUs(st.volume);
|
|
servo.attach(PIN_SERVO, SERVO_MIN_US, SERVO_MAX_US);
|
|
servo.writeMicroseconds(currentUs);
|
|
TickType_t lastMove = xTaskGetTickCount();
|
|
|
|
bool dirty = false;
|
|
TickType_t lastChange = 0;
|
|
|
|
Serial.printf("[control] restored volume=%u relay=%d\n", st.volume, st.relay);
|
|
|
|
for (;;) {
|
|
int targetUs = volumeToUs(st.volume);
|
|
bool moving = currentUs != targetUs;
|
|
TickType_t wait = (moving || dirty || servo.attached())
|
|
? pdMS_TO_TICKS(SERVO_UPDATE_MS)
|
|
: portMAX_DELAY;
|
|
|
|
Command cmd;
|
|
if (xQueueReceive(cmdQueue, &cmd, wait) == pdTRUE) {
|
|
AppState prev = st;
|
|
switch (cmd.type) {
|
|
case CmdType::VolumeDelta:
|
|
st.volume = constrain((int)st.volume + cmd.value, 0, 100);
|
|
break;
|
|
case CmdType::VolumeSet:
|
|
st.volume = constrain((int)cmd.value, 0, 100);
|
|
break;
|
|
case CmdType::RelayToggle:
|
|
st.relay = !st.relay;
|
|
break;
|
|
case CmdType::RelaySet:
|
|
st.relay = cmd.value != 0;
|
|
break;
|
|
}
|
|
if (st.relay != prev.relay) {
|
|
applyRelay(st.relay);
|
|
Serial.printf("[control] relay %s\n", st.relay ? "ON" : "OFF");
|
|
}
|
|
if (st.volume != prev.volume || st.relay != prev.relay) {
|
|
setState(st);
|
|
dirty = true;
|
|
lastChange = xTaskGetTickCount();
|
|
}
|
|
targetUs = volumeToUs(st.volume);
|
|
}
|
|
|
|
TickType_t now = xTaskGetTickCount();
|
|
|
|
// Smooth servo movement toward the target
|
|
if (currentUs != targetUs) {
|
|
int diff = targetUs - currentUs;
|
|
int step = constrain(diff, -SERVO_SPEED_US_PER_STEP, SERVO_SPEED_US_PER_STEP);
|
|
currentUs += step;
|
|
if (!servo.attached()) servo.attach(PIN_SERVO, SERVO_MIN_US, SERVO_MAX_US);
|
|
servo.writeMicroseconds(currentUs);
|
|
lastMove = now;
|
|
} else if (servo.attached() && now - lastMove >= pdMS_TO_TICKS(SERVO_DETACH_IDLE_MS)) {
|
|
servo.detach();
|
|
}
|
|
|
|
// Delayed save to spare flash wear while the encoder is being turned
|
|
if (dirty && now - lastChange >= pdMS_TO_TICKS(SAVE_DELAY_MS)) {
|
|
prefs.putUChar("vol", st.volume);
|
|
prefs.putBool("relay", st.relay);
|
|
dirty = false;
|
|
Serial.printf("[control] saved volume=%u relay=%d\n", st.volume, st.relay);
|
|
}
|
|
}
|
|
}
|