Converted servocontroller to protobufs + persistance defaults

This commit is contained in:
Niklas Jensen
2026-01-24 17:25:24 +01:00
committed by nikguin04
parent a4e900fb65
commit dbca9bd0b7
10 changed files with 96 additions and 76 deletions
+43 -9
View File
@@ -2,11 +2,11 @@
#define ServoController_h #define ServoController_h
#include <peripherals/drivers/pca9685.h> #include <peripherals/drivers/pca9685.h>
#include <template/stateful_persistence.h> #include <template/stateful_persistence_pb.h>
#include <template/stateful_proto_endpoint.h>
#include <template/stateful_service.h> #include <template/stateful_service.h>
#include <template/stateful_endpoint.h>
#include <utils/math_utils.h> #include <utils/math_utils.h>
#include <settings/servo_settings.h> #include <platform_shared/api.pb.h>
#ifndef FACTORY_SERVO_PWM_FREQUENCY #ifndef FACTORY_SERVO_PWM_FREQUENCY
#define FACTORY_SERVO_PWM_FREQUENCY 50 #define FACTORY_SERVO_PWM_FREQUENCY 50
@@ -16,13 +16,47 @@
#define FACTORY_SERVO_OSCILLATOR_FREQUENCY 27000000 #define FACTORY_SERVO_OSCILLATOR_FREQUENCY 27000000
#endif #endif
#define SERVO_SETTINGS_FILE "/config/servoSettings.pb"
enum class SERVO_CONTROL_STATE { DEACTIVATED, PWM, ANGLE }; 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.2f, "Servo1"}, {306, 1, -45, 2.1055555f, "Servo2"},
{306, 1, 90, 1.96923f, "Servo3"}, {306, -1, 0, 2.2f, "Servo4"},
{306, -1, 45, 2.1055555f, "Servo5"}, {306, -1, -90, 1.96923f, "Servo6"},
{306, 1, 0, 2.2f, "Servo7"}, {306, 1, -45, 2.1055555f, "Servo8"},
{306, 1, 90, 1.96923f, "Servo9"}, {306, 1, 0, 2.2f, "Servo10"},
{306, -1, 45, 2.1055555f, "Servo11"}, {306, -1, -90, 1.96923f, "Servo12"}
};
for (int i = 0; i < 12; i++) {
settings.servos[i] = defaults[i];
}
return settings;
}
inline void ServoSettings_read(const ServoSettings &settings, ServoSettings &proto) {
proto = settings;
}
inline StateUpdateResult ServoSettings_update(const ServoSettings &proto, ServoSettings &settings) {
settings = proto;
return StateUpdateResult::CHANGED;
}
class ServoController : public StatefulService<ServoSettings> { class ServoController : public StatefulService<ServoSettings> {
public: public:
ServoController() ServoController()
: endpoint(ServoSettings::read, ServoSettings::update, this), : protoEndpoint(ServoSettings_read, ServoSettings_update, this,
_persistence(ServoSettings::read, ServoSettings::update, this, SERVO_SETTINGS_FILE) {} API_REQUEST_EXTRACTOR(servo_settings, ServoSettings),
API_RESPONSE_ASSIGNER(servo_settings, ServoSettings)),
_persistence(ServoSettings_read, ServoSettings_update, this,
SERVO_SETTINGS_FILE, api_ServoSettings_fields, api_ServoSettings_size,
ServoSettings_defaults()) {}
void begin() { void begin() {
_persistence.readFromFS(); _persistence.readFromFS();
@@ -78,8 +112,8 @@ class ServoController : public StatefulService<ServoSettings> {
for (int i = 0; i < 12; i++) { for (int i = 0; i < 12; i++) {
angles[i] = lerp(angles[i], target_angles[i], 0.1); angles[i] = lerp(angles[i], target_angles[i], 0.1);
auto &servo = state().servos[i]; auto &servo = state().servos[i];
float angle = servo.direction * angles[i] + servo.centerAngle; float angle = servo.direction * angles[i] + servo.center_angle;
uint16_t pwm = angle * servo.conversion + servo.centerPwm; uint16_t pwm = angle * servo.conversion + servo.center_pwm;
pwms[i] = pwm = std::clamp<uint16_t>(pwm, 125, 600); pwms[i] = pwm = std::clamp<uint16_t>(pwm, 125, 600);
} }
_pca.setMultiplePWM(pwms, 12); _pca.setMultiplePWM(pwms, 12);
@@ -89,7 +123,7 @@ class ServoController : public StatefulService<ServoSettings> {
if (control_state == SERVO_CONTROL_STATE::ANGLE) calculatePWM(); if (control_state == SERVO_CONTROL_STATE::ANGLE) calculatePWM();
} }
StatefulHttpEndpoint<ServoSettings> endpoint; StatefulProtoEndpoint<ServoSettings, ServoSettings> protoEndpoint;
private: private:
void initializePCA() { void initializePCA() {
@@ -98,7 +132,7 @@ class ServoController : public StatefulService<ServoSettings> {
_pca.setPWMFreq(FACTORY_SERVO_PWM_FREQUENCY); _pca.setPWMFreq(FACTORY_SERVO_PWM_FREQUENCY);
_pca.sleep(); _pca.sleep();
} }
FSPersistence<ServoSettings> _persistence; FSPersistencePB<ServoSettings> _persistence;
PCA9685Driver _pca; PCA9685Driver _pca;
+13
View File
@@ -63,6 +63,19 @@ inline uint32_t parseIPv4(const char *str) {
using APSettings = api_APSettings; using APSettings = api_APSettings;
inline APSettings APSettings_defaults() {
APSettings settings = {};
settings.provision_mode = FACTORY_AP_PROVISION_MODE;
strncpy(settings.ssid, FACTORY_AP_SSID, sizeof(settings.ssid) - 1);
strncpy(settings.password, FACTORY_AP_PASSWORD, sizeof(settings.password) - 1);
settings.channel = FACTORY_AP_CHANNEL;
settings.ssid_hidden = FACTORY_AP_SSID_HIDDEN;
settings.max_clients = FACTORY_AP_MAX_CLIENTS;
settings.local_ip = parseIPv4(FACTORY_AP_LOCAL_IP);
settings.gateway_ip = parseIPv4(FACTORY_AP_GATEWAY_IP);
settings.subnet_mask = parseIPv4(FACTORY_AP_SUBNET_MASK);
return settings;
}
inline void APSettings_read(const APSettings &settings, APSettings &proto) { inline void APSettings_read(const APSettings &settings, APSettings &proto) {
proto = settings; proto = settings;
-49
View File
@@ -1,49 +0,0 @@
#pragma once
#include <ArduinoJson.h>
#include <template/state_result.h>
typedef struct {
float centerPwm;
float direction;
float centerAngle;
float conversion;
const char *name;
// bool enable;
} servo_settings_t;
class ServoSettings {
public:
servo_settings_t servos[12] = {
{306, -1, 0, 2.2, "Servo1"}, {306, 1, -45, 2.1055555, "Servo2"}, {306, 1, 90, 1.96923, "Servo3"},
{306, -1, 0, 2.2, "Servo4"}, {306, -1, 45, 2.1055555, "Servo5"}, {306, -1, -90, 1.96923, "Servo6"},
{306, 1, 0, 2.2, "Servo7"}, {306, 1, -45, 2.1055555, "Servo8"}, {306, 1, 90, 1.96923, "Servo9"},
{306, 1, 0, 2.2, "Servo10"}, {306, -1, 45, 2.1055555, "Servo11"}, {306, -1, -90, 1.96923, "Servo12"}};
static void read(ServoSettings &settings, JsonVariant &root) {
JsonArray servos = root["servos"].to<JsonArray>();
for (auto &servo : settings.servos) {
JsonVariant newServo = servos.add<JsonVariant>();
newServo["center_pwm"] = servo.centerPwm;
newServo["direction"] = servo.direction;
newServo["center_angle"] = servo.centerAngle;
newServo["conversion"] = servo.conversion;
}
}
static StateUpdateResult update(JsonVariant &root, ServoSettings &settings) {
if (root["servos"].is<JsonArray>()) {
JsonArray servosJson = root["servos"];
int i = 0;
for (auto servo : servosJson) {
JsonVariant servoObject = servo.as<JsonVariant>();
uint8_t servoId = i; // servoObject["id"].as<uint8_t>();
settings.servos[servoId].centerPwm = servoObject["center_pwm"].as<float>();
settings.servos[servoId].centerAngle = servoObject["center_angle"].as<float>();
settings.servos[servoId].direction = servoObject["direction"].as<float>();
settings.servos[servoId].conversion = servoObject["conversion"].as<float>();
i++;
}
}
ESP_LOGI("ServoController", "Updating servo data");
return StateUpdateResult::CHANGED;
};
};
@@ -22,13 +22,15 @@ class FSPersistencePB {
FSPersistencePB(ProtoStateReader stateReader, ProtoStateUpdater stateUpdater, FSPersistencePB(ProtoStateReader stateReader, ProtoStateUpdater stateUpdater,
StatefulService<T> *statefulService, const char *filePath, StatefulService<T> *statefulService, const char *filePath,
const pb_msgdesc_t *msgDescriptor, size_t maxSize) const pb_msgdesc_t *msgDescriptor, size_t maxSize,
const T &defaultState)
: _stateReader(stateReader), : _stateReader(stateReader),
_stateUpdater(stateUpdater), _stateUpdater(stateUpdater),
_statefulService(statefulService), _statefulService(statefulService),
_filePath(filePath), _filePath(filePath),
_msgDescriptor(msgDescriptor), _msgDescriptor(msgDescriptor),
_maxSize(maxSize), _maxSize(maxSize),
_defaultState(defaultState),
_updateHandlerId(0) { _updateHandlerId(0) {
enableUpdateHandler(); enableUpdateHandler();
} }
@@ -115,6 +117,7 @@ class FSPersistencePB {
const char *_filePath; const char *_filePath;
const pb_msgdesc_t *_msgDescriptor; const pb_msgdesc_t *_msgDescriptor;
size_t _maxSize; size_t _maxSize;
T _defaultState;
HandlerId _updateHandlerId; HandlerId _updateHandlerId;
void mkdirs() { void mkdirs() {
@@ -127,9 +130,8 @@ class FSPersistencePB {
} }
protected: protected:
virtual void applyDefaults() { void applyDefaults() {
T defaultState = {};
_statefulService->updateWithoutPropagation( _statefulService->updateWithoutPropagation(
[this, &defaultState](T &state) { return _stateUpdater(defaultState, state); }); [this](T &state) { return _stateUpdater(_defaultState, state); });
} }
}; };
@@ -2,7 +2,7 @@
#include <esp_http_server.h> #include <esp_http_server.h>
#include <template/stateful_service.h> #include <template/stateful_service.h>
#include <communication/native_server.h> #include <communication/webserver.h>
#include <platform_shared/api.pb.h> #include <platform_shared/api.pb.h>
#include <pb_encode.h> #include <pb_encode.h>
#include <pb_decode.h> #include <pb_decode.h>
@@ -95,7 +95,7 @@ class StatefulProtoEndpoint {
_statefulService->read([this, &protoState](const T& settings) { _stateReader(settings, protoState); }); _statefulService->read([this, &protoState](const T& settings) { _stateReader(settings, protoState); });
_responseAssigner(res, protoState); _responseAssigner(res, protoState);
return NativeServer::sendProto(request, 200, res, api_Response_fields); return WebServer::sendProto(request, 200, res, api_Response_fields);
} }
/** Sends error wrapped in Response */ /** Sends error wrapped in Response */
@@ -103,7 +103,7 @@ class StatefulProtoEndpoint {
api_Response res = api_Response_init_zero; api_Response res = api_Response_init_zero;
res.status_code = statusCode; res.status_code = statusCode;
res.error_message = (char*)message; res.error_message = (char*)message;
return NativeServer::sendProto(request, statusCode == 200 ? 200 : 400, res, api_Response_fields); return WebServer::sendProto(request, statusCode == 200 ? 200 : 400, res, api_Response_fields);
} }
}; };
+3 -2
View File
@@ -8,7 +8,8 @@ APService::APService()
API_REQUEST_EXTRACTOR(ap_settings, api_APSettings), API_REQUEST_EXTRACTOR(ap_settings, api_APSettings),
API_RESPONSE_ASSIGNER(ap_settings, api_APSettings)), API_RESPONSE_ASSIGNER(ap_settings, api_APSettings)),
_persistence(APSettings_read, APSettings_update, this, _persistence(APSettings_read, APSettings_update, this,
AP_SETTINGS_FILE, api_APSettings_fields, api_APSettings_size) { AP_SETTINGS_FILE, api_APSettings_fields, api_APSettings_size,
APSettings_defaults()) {
addUpdateHandler([&](const std::string &originId) { reconfigureAP(); }, false); addUpdateHandler([&](const std::string &originId) { reconfigureAP(); }, false);
} }
@@ -23,7 +24,7 @@ esp_err_t APService::getStatusProto(httpd_req_t *request) {
res.status_code = 200; res.status_code = 200;
res.which_payload = api_Response_ap_status_tag; res.which_payload = api_Response_ap_status_tag;
statusProto(res.payload.ap_status); statusProto(res.payload.ap_status);
return NativeServer::sendProto(request, 200, res, api_Response_fields); return WebServer::sendProto(request, 200, res, api_Response_fields);
} }
void APService::statusProto(api_APStatus &proto) { void APService::statusProto(api_APStatus &proto) {
+1 -1
View File
@@ -251,7 +251,7 @@ void WebServer::on(const char* uri, httpd_method_t method, HttpPostHandler handl
} }
} }
void NativeServer::onProto(const char* uri, httpd_method_t method, HttpProtoHandler handler) { void WebServer::onProto(const char* uri, httpd_method_t method, HttpProtoHandler handler) {
HttpRoute route; HttpRoute route;
route.uri = uri; route.uri = uri;
route.method = method; route.method = method;
+3 -3
View File
@@ -59,9 +59,9 @@ void setupServer() {
}); });
#endif #endif
server.on("/api/servo/config", HTTP_GET, server.on("/api/servo/config", HTTP_GET,
[&](httpd_req_t *request) { return servoController.endpoint.getState(request); }); [&](httpd_req_t *request) { return servoController.protoEndpoint.getState(request); });
server.on("/api/servo/config", HTTP_POST, [&](httpd_req_t *request, JsonVariant &json) { server.onProto("/api/servo/config", HTTP_POST, [&](httpd_req_t *request, api_Request *protoReq) {
return servoController.endpoint.handleStateUpdate(request, json); return servoController.protoEndpoint.handleStateUpdate(request, protoReq);
}); });
server.on("/api/wifi/sta/settings", HTTP_GET, server.on("/api/wifi/sta/settings", HTTP_GET,
+3
View File
@@ -3,4 +3,7 @@ api.APSettings.password max_size:64
api.APStatus.mac_address max_size:18 api.APStatus.mac_address max_size:18
api.Servo.name max_size:16
api.ServoSettings.servos max_count:12
api.Response.error_message type:FT_POINTER api.Response.error_message type:FT_POINTER
+21 -5
View File
@@ -41,6 +41,24 @@ message APStatus {
message APSettingsRequest {} message APSettingsRequest {}
message APStatusRequest {} message APStatusRequest {}
// =============================================================================
// Servo Settings - shared data types
// =============================================================================
message Servo {
float center_pwm = 1;
float direction = 2;
float center_angle = 3;
float conversion = 4;
string name = 5;
}
message ServoSettings {
repeated Servo servos = 1; // max 12 servos
}
message ServoSettingsRequest {}
// ============================================================================= // =============================================================================
// REST API wrappers - used by HTTP endpoints // REST API wrappers - used by HTTP endpoints
// ============================================================================= // =============================================================================
@@ -51,8 +69,8 @@ message Request {
APSettings ap_settings = 10; APSettings ap_settings = 10;
APSettingsRequest ap_settings_request = 11; APSettingsRequest ap_settings_request = 11;
APStatusRequest ap_status_request = 12; APStatusRequest ap_status_request = 12;
// Future types: ServoSettings servo_settings = 20;
// MDNSSettings mdns_settings = 20; ServoSettingsRequest servo_settings_request = 21;
} }
} }
@@ -64,8 +82,6 @@ message Response {
oneof payload { oneof payload {
APSettings ap_settings = 10; APSettings ap_settings = 10;
APStatus ap_status = 11; APStatus ap_status = 11;
// Future types: ServoSettings servo_settings = 20;
// MDNSSettings mdns_settings = 20;
// MDNSStatus mdns_status = 21;
} }
} }