Allow both way sync of angles
This commit is contained in:
@@ -12,7 +12,14 @@ export const modes = ['idle', 'rest', 'stand', 'walk'] as const;
|
|||||||
|
|
||||||
export type Modes = (typeof modes)[number];
|
export type Modes = (typeof modes)[number];
|
||||||
|
|
||||||
export const mode: Writable<Modes> = writable('idle');
|
export enum ModesEnum {
|
||||||
|
Idle,
|
||||||
|
Rest,
|
||||||
|
Stand,
|
||||||
|
Walk
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mode: Writable<ModesEnum> = writable(ModesEnum.Idle);
|
||||||
|
|
||||||
export const outControllerData = writable(new Int8Array([0, 0, 0, 0, 0, 70, 0]));
|
export const outControllerData = writable(new Int8Array([0, 0, 0, 0, 0, 70, 0]));
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
connectToEventSource();
|
connectToEventSource();
|
||||||
connectToSocket()
|
connectToSocket()
|
||||||
addPublisher(outControllerData, "controller")
|
addPublisher(outControllerData, "controller")
|
||||||
addPublisher(mode, "mode")
|
addPublisher(mode as unknown as Writable<WebsocketOutData>, "mode")
|
||||||
addPublisher(servoAngles as unknown as Writable<WebsocketOutData>, "angles")
|
addPublisher(servoAngles as unknown as Writable<WebsocketOutData>, "angles")
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,6 +127,15 @@
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
NotificationSource.addEventListener(
|
||||||
|
'motion',
|
||||||
|
(event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
servoAngles.set(data.angles);
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
NotificationSource.addEventListener(
|
NotificationSource.addEventListener(
|
||||||
'error',
|
'error',
|
||||||
(event) => {
|
(event) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import nipplejs from 'nipplejs';
|
import nipplejs from 'nipplejs';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { capitalize, throttler, toInt8 } from '$lib/utilities';
|
import { capitalize, throttler, toInt8 } from '$lib/utilities';
|
||||||
import { input, outControllerData, mode, modes, type Modes } from '$lib/stores';
|
import { input, outControllerData, mode, modes, type Modes, ModesEnum } from '$lib/stores';
|
||||||
import type { vector } from '$lib/models';
|
import type { vector } from '$lib/models';
|
||||||
|
|
||||||
let throttle = new throttler();
|
let throttle = new throttler();
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const changeMode = (modeValue: Modes) => {
|
const changeMode = (modeValue: Modes) => {
|
||||||
mode.set(modeValue);
|
mode.set(modes.indexOf(modeValue));
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -89,12 +89,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="absolute bottom-0 z-10 p-4 gap-4 flex items-end">
|
<div class="absolute bottom-0 z-10 p-4 gap-4 flex items-end">
|
||||||
{#each modes as modeValue}
|
{#each modes as modeValue}
|
||||||
<button class="btn btn-outline" class:btn-active={$mode === modeValue} on:click={() => changeMode(modeValue)}>
|
<button class="btn btn-outline" class:btn-active={$mode === modes.indexOf(modeValue)} on:click={() => changeMode(modeValue)}>
|
||||||
{capitalize(modeValue)}
|
{capitalize(modeValue)}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
<div>
|
<div>
|
||||||
{#if $mode === 'walk'}
|
{#if $mode === ModesEnum.Walk}
|
||||||
<label for="speed">Speed</label>
|
<label for="speed">Speed</label>
|
||||||
<input type="range" name="speed" min="0" max="100" on:input={(e) => handleRange(e, 'speed')} class="range range-sm" />
|
<input type="range" name="speed" min="0" max="100" on:input={(e) => handleRange(e, 'speed')} class="range range-sm" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
ActuatorStateService::ActuatorStateService(
|
ActuatorStateService::ActuatorStateService(
|
||||||
PsychicHttpServer *server,
|
PsychicHttpServer *server,
|
||||||
|
NotificationEvents *notificationEvents,
|
||||||
SecurityManager *securityManager
|
SecurityManager *securityManager
|
||||||
) : _httpEndpoint(
|
) : _httpEndpoint(
|
||||||
ActuatorState::read,
|
ActuatorState::read,
|
||||||
@@ -32,7 +33,8 @@ ActuatorStateService::ActuatorStateService(
|
|||||||
server,
|
server,
|
||||||
ACTUATOR_SETTINGS_SOCKET_PATH,
|
ACTUATOR_SETTINGS_SOCKET_PATH,
|
||||||
securityManager,
|
securityManager,
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED)
|
AuthenticationPredicates::IS_AUTHENTICATED),
|
||||||
|
_notificationEvents(notificationEvents)
|
||||||
{
|
{
|
||||||
// Setup actuator hardware
|
// Setup actuator hardware
|
||||||
|
|
||||||
@@ -44,6 +46,15 @@ void ActuatorStateService::begin()
|
|||||||
_httpEndpoint.begin();
|
_httpEndpoint.begin();
|
||||||
_webSocketServer.begin();
|
_webSocketServer.begin();
|
||||||
onConfigUpdated();
|
onConfigUpdated();
|
||||||
|
xTaskCreatePinnedToCore(
|
||||||
|
this->_loopImpl, // Function that should be called
|
||||||
|
"Motion Service", // Name of the task (for debugging)
|
||||||
|
4096, // Stack size (bytes)
|
||||||
|
this, // Pass reference to this class instance
|
||||||
|
(tskIDLE_PRIORITY + 1), // task priority
|
||||||
|
NULL, // Task handle
|
||||||
|
ESP32SVELTEKIT_RUNNING_CORE // Pin to application core
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActuatorStateService::onConfigUpdated()
|
void ActuatorStateService::onConfigUpdated()
|
||||||
|
|||||||
@@ -17,14 +17,29 @@
|
|||||||
|
|
||||||
#include <HttpEndpoint.h>
|
#include <HttpEndpoint.h>
|
||||||
#include <WebSocketServer.h>
|
#include <WebSocketServer.h>
|
||||||
|
#include <NotificationEvents.h>
|
||||||
|
|
||||||
#define ACTUATOR_SETTINGS_ENDPOINT_PATH "/rest/actuators"
|
#define ACTUATOR_SETTINGS_ENDPOINT_PATH "/rest/actuators"
|
||||||
#define ACTUATOR_SETTINGS_SOCKET_PATH "/ws"
|
#define ACTUATOR_SETTINGS_SOCKET_PATH "/ws"
|
||||||
|
|
||||||
|
#define MOTION_INTERVAL 100
|
||||||
|
|
||||||
|
#define MAX_ESP_ANGLE_SIZE 256
|
||||||
|
|
||||||
|
enum class MOTION_STATE
|
||||||
|
{
|
||||||
|
IDLE,
|
||||||
|
REST,
|
||||||
|
STAND,
|
||||||
|
WALK
|
||||||
|
};
|
||||||
|
|
||||||
class ActuatorState
|
class ActuatorState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int16_t state[12] = {0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90};
|
int16_t angles[12] = {0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90};
|
||||||
|
int8_t controller[7] = {0, 0, 0, 0, 0, 0 ,0};
|
||||||
|
MOTION_STATE motionState = MOTION_STATE::IDLE;
|
||||||
|
|
||||||
static void read(ActuatorState &settings, JsonObject &root)
|
static void read(ActuatorState &settings, JsonObject &root)
|
||||||
{
|
{
|
||||||
@@ -32,20 +47,25 @@ public:
|
|||||||
JsonArray array = root.createNestedArray("data");
|
JsonArray array = root.createNestedArray("data");
|
||||||
for(int i = 0; i < 12; i++)
|
for(int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
array.add(settings.state[i]);
|
array.add(settings.angles[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static StateUpdateResult update(JsonObject &root, ActuatorState &actuatorState)
|
static StateUpdateResult update(JsonObject &root, ActuatorState &actuatorState)
|
||||||
{
|
{
|
||||||
|
if (root["type"] == "mode") {
|
||||||
|
if (actuatorState.motionState == (MOTION_STATE)root["data"].as<int>()) return StateUpdateResult::UNCHANGED;
|
||||||
|
actuatorState.motionState = (MOTION_STATE)root["data"].as<int>();
|
||||||
|
return StateUpdateResult::UNCHANGED;
|
||||||
|
}
|
||||||
if (root["type"] != "angles") return StateUpdateResult::UNCHANGED;
|
if (root["type"] != "angles") return StateUpdateResult::UNCHANGED;
|
||||||
JsonArray array = root["data"];
|
JsonArray array = root["data"];
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
for(int i = 0; i < 12; i++)
|
for(int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
if (actuatorState.state[i] != array[i].as<int16_t>())
|
if (actuatorState.angles[i] != array[i].as<int16_t>())
|
||||||
{
|
{
|
||||||
actuatorState.state[i] = array[i];
|
actuatorState.angles[i] = array[i];
|
||||||
//changed = true;
|
//changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,7 +77,7 @@ public:
|
|||||||
JsonArray array = root.createNestedArray("angles");
|
JsonArray array = root.createNestedArray("angles");
|
||||||
for(int i = 0; i < 12; i++)
|
for(int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
array.add(settings.state[i]);
|
array.add(settings.angles[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,9 +89,9 @@ public:
|
|||||||
bool changed = false;
|
bool changed = false;
|
||||||
for(int i = 0; i < 12; i++)
|
for(int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
if (actuatorState.state[i] != array[i].as<int16_t>())
|
if (actuatorState.angles[i] != array[i].as<int16_t>())
|
||||||
{
|
{
|
||||||
actuatorState.state[i] = array[i];
|
actuatorState.angles[i] = array[i];
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,9 +101,31 @@ public:
|
|||||||
|
|
||||||
class ActuatorStateService : public StatefulService<ActuatorState> {
|
class ActuatorStateService : public StatefulService<ActuatorState> {
|
||||||
public:
|
public:
|
||||||
ActuatorStateService(PsychicHttpServer *server, SecurityManager *securityManager);
|
ActuatorStateService(PsychicHttpServer *server, NotificationEvents *notificationEvents, SecurityManager *securityManager);
|
||||||
void begin();
|
void begin();
|
||||||
|
protected:
|
||||||
|
NotificationEvents *_notificationEvents;
|
||||||
|
static void _loopImpl(void *_this) { static_cast<ActuatorStateService *>(_this)->_loop(); }
|
||||||
|
void _loop()
|
||||||
|
{
|
||||||
|
TickType_t xLastWakeTime;
|
||||||
|
xLastWakeTime = xTaskGetTickCount();
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
_state.angles[1] = (_state.angles[1] + 5) % 90;
|
||||||
|
StaticJsonDocument<MAX_ESP_ANGLE_SIZE> doc;
|
||||||
|
String message;
|
||||||
|
JsonArray array = doc.createNestedArray("angles");
|
||||||
|
for(int16_t num : _state.angles) {
|
||||||
|
array.add(num);
|
||||||
|
}
|
||||||
|
doc["mode"] = (int)_state.motionState;
|
||||||
|
|
||||||
|
serializeJson(doc, message);
|
||||||
|
_notificationEvents->send(message, "motion", millis());
|
||||||
|
vTaskDelayUntil(&xLastWakeTime, MOTION_INTERVAL / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
};
|
||||||
private:
|
private:
|
||||||
HttpEndpoint<ActuatorState> _httpEndpoint;
|
HttpEndpoint<ActuatorState> _httpEndpoint;
|
||||||
WebSocketServer<ActuatorState> _webSocketServer;
|
WebSocketServer<ActuatorState> _webSocketServer;
|
||||||
|
|||||||
+3
-2
@@ -6,9 +6,10 @@
|
|||||||
|
|
||||||
DRAM_ATTR PsychicHttpServer server;
|
DRAM_ATTR PsychicHttpServer server;
|
||||||
|
|
||||||
DRAM_ATTR ESP32SvelteKit esp32sveltekit(&server, 120);
|
DRAM_ATTR ESP32SvelteKit esp32sveltekit(&server, 125);
|
||||||
|
|
||||||
|
ActuatorStateService actuatorStateService = ActuatorStateService(&server, esp32sveltekit.getNotificationEvents(), esp32sveltekit.getSecurityManager());
|
||||||
|
|
||||||
ActuatorStateService actuatorStateService = ActuatorStateService(&server, esp32sveltekit.getSecurityManager());
|
|
||||||
|
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
|
|||||||
Reference in New Issue
Block a user