Files
SpotMicroESP32-Leika/esp32/include/peripherals/servo_controller.h
T
2026-02-01 00:44:32 +01:00

148 lines
4.8 KiB
C++

#ifndef ServoController_h
#define ServoController_h
#include <peripherals/drivers/pca9685.h>
#include <event_bus/event_bus.h>
#include <utils/math_utils.h>
#include <platform_shared/api.pb.h>
#include <platform_shared/message.pb.h>
#include <esp_http_server.h>
#ifndef FACTORY_SERVO_PWM_FREQUENCY
#define FACTORY_SERVO_PWM_FREQUENCY 50
#endif
#ifndef FACTORY_SERVO_OSCILLATOR_FREQUENCY
#define FACTORY_SERVO_OSCILLATOR_FREQUENCY 27000000
#endif
enum class SERVO_CONTROL_STATE { DEACTIVATED, PWM, ANGLE };
using ServoSettings = api_ServoSettings;
inline ServoSettings ServoSettings_defaults() {
ServoSettings settings = {};
settings.servos_count = 12;
const api_Servo defaults[12] = {
{306, -1, 0, 2.0f, "Servo1"}, {306, 1, -45, 2.0f, "Servo2"}, {306, 1, 90, 2.0f, "Servo3"},
{306, -1, 0, 2.0f, "Servo4"}, {306, -1, 45, 2.0f, "Servo5"}, {306, -1, -90, 2.0f, "Servo6"},
{306, 1, 0, 2.0f, "Servo7"}, {306, 1, -45, 2.0f, "Servo8"}, {306, 1, 90, 2.0f, "Servo9"},
{306, 1, 0, 2.0f, "Servo10"}, {306, -1, 45, 2.0f, "Servo11"}, {306, -1, -90, 2.0f, "Servo12"}};
for (int i = 0; i < 12; i++) {
settings.servos[i] = defaults[i];
}
return settings;
}
class ServoController {
public:
ServoController() {}
void begin() {
_settingsHandle = EventBus::subscribe<api_ServoSettings>(
[this](const api_ServoSettings &settings) { onSettingsChanged(settings); });
_pwmHandle = EventBus::subscribe<socket_message_ServoPWMData>(
[this](const socket_message_ServoPWMData &data) { setServoPWM(data.servo_id, data.servo_pwm); });
_stateHandle = EventBus::subscribe<socket_message_ServoStateData>(
[this](const socket_message_ServoStateData &data) { data.active ? activate() : deactivate(); });
api_ServoSettings initialSettings;
if (EventBus::peek(initialSettings)) {
onSettingsChanged(initialSettings);
}
initializePCA();
}
esp_err_t getSettings(httpd_req_t *request);
esp_err_t updateSettings(httpd_req_t *request, api_Request *protoReq);
void pcaWrite(int index, int value) {
if (value < 0 || value > 4096) {
ESP_LOGE("Peripherals", "Invalid PWM value %d for %d :: Valid range 0-4096", value, index);
return;
}
_pca.setPWM(index, 0, value);
}
void activate() {
if (is_active) return;
control_state = SERVO_CONTROL_STATE::ANGLE;
is_active = true;
_pca.wakeup();
}
void deactivate() {
if (!is_active) return;
is_active = false;
control_state = SERVO_CONTROL_STATE::DEACTIVATED;
_pca.sleep();
}
void setServoPWM(int32_t servo_id, uint32_t pwm) {
control_state = SERVO_CONTROL_STATE::PWM;
if (servo_id < 0) {
uint16_t pwms[12];
std::fill_n(pwms, 12, static_cast<uint16_t>(pwm));
_pca.setMultiplePWM(pwms, 12);
} else {
_pca.setPWM(servo_id, 0, pwm);
}
ESP_LOGI("SERVO_CONTROLLER", "Setting servo %d to %d", servo_id, pwm);
}
void updateActiveState() { is_active ? activate() : deactivate(); }
void setMode(SERVO_CONTROL_STATE newMode) { control_state = newMode; }
void setAngles(float new_angles[12]) {
for (int i = 0; i < 12; i++) {
target_angles[i] = new_angles[i];
}
}
void calculatePWM() {
uint16_t pwms[12];
for (int i = 0; i < 12; i++) {
angles[i] = lerp(angles[i], target_angles[i], 0.1);
auto &servo = _settings.servos[i];
float angle = servo.direction * angles[i] + servo.center_angle;
uint16_t pwm = angle * servo.conversion + servo.center_pwm;
pwms[i] = pwm = std::clamp<uint16_t>(pwm, 125, 600);
}
_pca.setMultiplePWM(pwms, 12);
}
void update() {
if (control_state == SERVO_CONTROL_STATE::ANGLE) calculatePWM();
}
private:
static constexpr const char *TAG = "ServoController";
void onSettingsChanged(const api_ServoSettings &newSettings) { _settings = newSettings; }
void initializePCA() {
_pca.begin();
_pca.setOscillatorFrequency(FACTORY_SERVO_OSCILLATOR_FREQUENCY);
_pca.setPWMFreq(FACTORY_SERVO_PWM_FREQUENCY);
_pca.sleep();
}
api_ServoSettings _settings = ServoSettings_defaults();
EventBus::Handle<api_ServoSettings> _settingsHandle;
EventBus::Handle<socket_message_ServoPWMData> _pwmHandle;
EventBus::Handle<socket_message_ServoStateData> _stateHandle;
PCA9685Driver _pca;
SERVO_CONTROL_STATE control_state = SERVO_CONTROL_STATE::DEACTIVATED;
bool is_active {false};
float angles[12] = {0, 90, -145, 0, 90, -145, 0, 90, -145, 0, 90, -145};
float target_angles[12] = {0, 90, -145, 0, 90, -145, 0, 90, -145, 0, 90, -145};
};
#endif