diff --git a/firmware/src/firmata_commands.hpp b/firmware/src/firmata_commands.hpp index ec3eefa..444079c 100644 --- a/firmware/src/firmata_commands.hpp +++ b/firmware/src/firmata_commands.hpp @@ -4,10 +4,9 @@ enum FirmataCommands { TELEMETRY = 0x01, - SERVO_SET_ENABLE_PORT, - SERVO_SET_ENABLE_STARBOARD, - SERVO_GET_ENABLE_PORT, - SERVO_GET_ENABLE_STARBOARD + SERVO_DRIVER_ENABLED, + SERVO_CHANNEL_ENABLED, + SERVO_CHANNEL_POSITION, }; enum FirmataTelemetryType diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index efbbe0d..1ad883e 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -54,34 +54,146 @@ PowerTask powerTask(sensorMutex, firmataTask); ServoDriverTask portServos(SERVO_ENABLE_PORT_PIN, SERVO_ADDRESS_PORT, servoMutex); ServoDriverTask starboardServos(SERVO_ENABLE_STARBOARD_PIN, SERVO_ADDRESS_STARBOARD, servoMutex); +// bytes: +// set: channel, sign, value lsb, value msb +// get: channel, _ +void servoChannelPosition(byte bytec, byte *payload) +{ + if (bytec == 0) + return; + + ServoDriverTask *servoTask; + + uint8_t channel = payload[0]; + uint8_t driverChannel = channel; + if (channel < 16) + servoTask = &portServos; + else if (channel < 32) + { + servoTask = &starboardServos; + driverChannel -= 16; + } + else + return; + + if (bytec == 4) + { + // Set position + int16_t position = payload[2] + (payload[3] << 7); + + if (payload[1] == 1) + position = 0 - position; + + servoTask->setPosition(driverChannel, position); + + // Just echo the new position back to the caller because the servo hasn't + // actually been moved yet. + firmataTask.sendCommand(FirmataCommands::SERVO_CHANNEL_POSITION, 4, payload); + } + else + { + // Get position + int16_t position = servoTask->getPosition(driverChannel); + uint16_t abs_position = abs(position); + + byte lsb = abs_position & 0x7f; + byte msb = abs_position >> 7; + + byte sign = 0; + if (position < 0) + sign = 1; + + byte response[4]; + response[0] = channel + (sign << 8); + response[1] = sign; + response[2] = lsb; + response[3] = msb; + + firmataTask.sendCommand(FirmataCommands::SERVO_CHANNEL_POSITION, 4, (byte *)&response); + } +} + +// bytes: +// set: channel, enabled bool +// get: channel +void servoChannelEnabled(byte bytec, byte *payload) +{ + if (bytec == 0) + return; + + ServoDriverTask *servoTask; + + uint8_t channel = payload[0]; + uint8_t driverChannel = channel; + if (channel < 16) + servoTask = &portServos; + else if (channel < 32) + { + servoTask = &starboardServos; + driverChannel -= 16; + } + else + return; + + if (bytec == 2) + { + // Set Enabled/Disabled + servoTask->enableChannel(driverChannel, (bool)payload[1]); + } + + // Get enabled/disabled. + byte response[2]; + response[0] = channel; + response[1] = (byte)servoTask->isEnabled(driverChannel); + firmataTask.sendCommand(FirmataCommands::SERVO_CHANNEL_ENABLED, 2, (byte *)&response); +} + +// bytes: +// set: driver, _, bool, _ +// get: driver, _ +void servoDriverEnabled(byte bytec, byte *payload) +{ + if (bytec == 0) + return; + + ServoDriverTask *servoTask; + + if (payload[0] == 0) + { + servoTask = &portServos; + } + else + { + servoTask = &starboardServos; + } + + if (bytec == 2) + { + // Setting the value + servoTask->enable((bool)payload[1]); + } + + // Getting the value + byte response[2]; + response[0] = payload[0] & 0x1; + response[1] = (byte)servoTask->isEnabled(); + firmataTask.sendCommand(FirmataCommands::SERVO_DRIVER_ENABLED, 2, (byte *)&response); +} + void sysexCallback(byte command, byte bytec, byte *payload) { - byte responseBuffer[1]; - switch (command) { - case FirmataCommands::SERVO_SET_ENABLE_PORT: - if (bytec > 0 && payload[0] > 0) - portServos.enable(true); - else - portServos.enable(false); - break; + case FirmataCommands::SERVO_DRIVER_ENABLED: + return servoDriverEnabled(bytec, payload); - case FirmataCommands::SERVO_SET_ENABLE_STARBOARD: - if (bytec > 0 && payload[0] > 0) - starboardServos.enable(true); - else - starboardServos.enable(false); - break; + case FirmataCommands::SERVO_CHANNEL_ENABLED: + return servoChannelEnabled(bytec, payload); - case FirmataCommands::SERVO_GET_ENABLE_PORT: - responseBuffer[0] = (byte)portServos.isEnabled(); - Firmata.sendSysex(FirmataCommands::SERVO_GET_ENABLE_PORT, 1, (byte *)&responseBuffer); - break; + case FirmataCommands::SERVO_CHANNEL_POSITION: + return servoChannelPosition(bytec, payload); - case FirmataCommands::SERVO_GET_ENABLE_STARBOARD: - responseBuffer[0] = (byte)starboardServos.isEnabled(); - Firmata.sendSysex(FirmataCommands::SERVO_GET_ENABLE_STARBOARD, 1, (byte *)&responseBuffer); + default: break; } } diff --git a/firmware/src/tasks/firmata_task.cpp b/firmware/src/tasks/firmata_task.cpp index 726dc8b..a7d1296 100644 --- a/firmware/src/tasks/firmata_task.cpp +++ b/firmware/src/tasks/firmata_task.cpp @@ -61,24 +61,24 @@ void FirmataTask::sendTelemetry(FirmataTelemetryType telemetryType, byte byteCou } } +// Write a sysex command with unencoded payload. void FirmataTask::sendCommand(byte command, byte byteCount, byte *data) { - byte encodedSize = 0; - byte *encodedData = new byte[byteCount * 2]; + FIRMATA_PORT.write(START_SYSEX); + FIRMATA_PORT.write(command); for (int i = 0; i < byteCount; i++) { - byte value = data[i]; - encodedData[encodedSize++] = value & 0x7f; - encodedData[encodedSize++] = (value >> 7) & 0x7f; + FIRMATA_PORT.write(data[i]); } - TickType_t delay = Ticks::fromMillis(100L); - if (xSemaphoreTake(this->mutex, delay) == pdTRUE) - { - Firmata.sendSysex(command, encodedSize, encodedData); - xSemaphoreGive(this->mutex); - } + FIRMATA_PORT.write(END_SYSEX); +} + +// Write a sysex command with encoded payload. +void FirmataTask::sendCommandEncoded(byte command, byte byteCount, byte *data) +{ + Firmata.sendSysex(command, byteCount, data); } void FirmataTask::init() diff --git a/firmware/src/tasks/firmata_task.hpp b/firmware/src/tasks/firmata_task.hpp index f82a452..32c0e04 100644 --- a/firmware/src/tasks/firmata_task.hpp +++ b/firmware/src/tasks/firmata_task.hpp @@ -14,6 +14,7 @@ public: void run(); void sendCommand(byte command, byte byteCount, byte *data); + void sendCommandEncoded(byte command, byte byteCount, byte *data); void sendTelemetry(FirmataTelemetryType telemetryType, byte byteCount, byte *data); static void startTask(void *arg); diff --git a/firmware/src/tasks/servo_driver.cpp b/firmware/src/tasks/servo_driver.cpp index 26e9a1a..00b6dda 100644 --- a/firmware/src/tasks/servo_driver.cpp +++ b/firmware/src/tasks/servo_driver.cpp @@ -1,24 +1,38 @@ #include "servo_driver.hpp" #include "ticks.hpp" +uint16_t degreesToPwm(int16_t degrees) +{ + return map(degrees, SERVO_DEGMIN, SERVO_DEGMAX, SERVO_MIN, SERVO_MAX); +} + ServoDriverTask::ServoDriverTask(const uint8_t enablePin, const uint8_t busAddress, SemaphoreHandle_t &busMutex) : enablePin(enablePin), busMutex(busMutex) { this->pwmDriver = Adafruit_PWMServoDriver(busAddress, SERVO_I2C_BUS); pinMode(this->enablePin, OUTPUT); - digitalWrite(this->enablePin, SERVO_DRIVER_DISABLED); + digitalWrite(this->enablePin, SERVO_DRIVER_PIN_DISABLED); this->enablePinState = false; + + for (int i = 0; i < 16; i++) + { + this->lastPosition[i] = 0; + this->nextPosition[i] = 0; + this->channelEnabled[i] = false; + this->lastPwm[i] = 0; + this->nextPwm[i] = 0; + } } void ServoDriverTask::enable(bool enable) { if (enable) { - digitalWrite(this->enablePin, SERVO_DRIVER_ENABLED); + digitalWrite(this->enablePin, SERVO_DRIVER_PIN_ENABLED); this->enablePinState = true; } else { - digitalWrite(this->enablePin, SERVO_DRIVER_DISABLED); + digitalWrite(this->enablePin, SERVO_DRIVER_PIN_DISABLED); this->enablePinState = false; } } @@ -28,6 +42,42 @@ bool ServoDriverTask::isEnabled() return this->enablePinState; } +bool ServoDriverTask::isEnabled(byte channel) +{ + return this->channelEnabled[channel]; +} + +void ServoDriverTask::enableChannel(byte channel, bool enable) +{ + if (channel >= 0 and channel < 16) + { + this->channelEnabled[channel] = enable; + if (enable) + this->nextPwm[channel] = degreesToPwm(this->nextPosition[channel]); + else + this->nextPwm[channel] = 0; + } +} + +void ServoDriverTask::setPosition(byte channel, int16_t position) +{ + if (channel >= 0 and channel < 16) + { + this->nextPosition[channel] = position; + if (this->channelEnabled[channel]) + this->nextPwm[channel] = degreesToPwm(position); + } +} + +int16_t ServoDriverTask::getPosition(byte channel) +{ + if (channel >= 0 and channel < 16) + { + return this->lastPosition[channel]; + } + return 0; +} + void ServoDriverTask::init() { TickType_t delay = Ticks::fromMillis(100L); @@ -47,10 +97,23 @@ void ServoDriverTask::init() void ServoDriverTask::run() { - TickType_t delay = Ticks::fromMillis(100L); + TickType_t delay = Ticks::fromHz((long)SERVO_FREQ); while (1) { + if (xSemaphoreTake(this->busMutex, 10L) == pdTRUE) + { + for (int i = 0; i < 16; i++) + { + if (this->nextPwm[i] != this->lastPwm[i]) + { + this->pwmDriver.setPWM(i, this->nextPwm[i], 0); + this->lastPwm[i] = this->nextPwm[i]; + } + this->lastPosition[i] = this->nextPosition[i]; + } + xSemaphoreGive(this->busMutex); + } vTaskDelay(delay); } } diff --git a/firmware/src/tasks/servo_driver.hpp b/firmware/src/tasks/servo_driver.hpp index 7d37e05..581d00b 100644 --- a/firmware/src/tasks/servo_driver.hpp +++ b/firmware/src/tasks/servo_driver.hpp @@ -17,6 +17,10 @@ #define SERVO_MAX 600 #endif +#ifndef SERVO_DEFAULT_POSITION +#define SERVO_DEFAULT_POSITION (SERVO_MAX - SERVO_MIN) / 2 +#endif + #ifndef SERVO_USMIN #define SERVO_USMIN 600 #endif @@ -25,8 +29,16 @@ #define SERVO_USMAX 2400 #endif -#define SERVO_DRIVER_ENABLED LOW -#define SERVO_DRIVER_DISABLED HIGH +#ifndef SERVO_DEGMIN +#define SERVO_DEGMIN -90 +#endif + +#ifndef SERVO_DEGMAX +#define SERVO_DEGMAX 90 +#endif + +#define SERVO_DRIVER_PIN_ENABLED LOW +#define SERVO_DRIVER_PIN_DISABLED HIGH #include #include @@ -45,12 +57,21 @@ public: void enable(bool enable); bool isEnabled(); + bool isEnabled(byte channel); + void enableChannel(byte channel, bool enable); + void setPosition(byte channel, int16_t position); + int16_t getPosition(byte channel); private: uint8_t enablePin; SemaphoreHandle_t &busMutex; Adafruit_PWMServoDriver pwmDriver; bool enablePinState; + int16_t lastPosition[16]; + int16_t nextPosition[16]; + bool channelEnabled[16]; + uint16_t lastPwm[16]; + uint16_t nextPwm[16]; }; #endif // __SERVO_DRIVER_TASK_HPP__ diff --git a/webapp/assets/css/app.scss b/webapp/assets/css/app.scss index caa77ed..23af68b 100644 --- a/webapp/assets/css/app.scss +++ b/webapp/assets/css/app.scss @@ -6,5 +6,6 @@ @import "~foundation-sites/scss/foundation"; @include foundation-everything(); @include foundation-meter-element; +@include foundation-range-input; @import "sparkline.scss"; @import "../node_modules/nprogress/nprogress.css"; diff --git a/webapp/lib/augie/firmata/commands.ex b/webapp/lib/augie/firmata/commands.ex index 3ad9801..b659ab9 100644 --- a/webapp/lib/augie/firmata/commands.ex +++ b/webapp/lib/augie/firmata/commands.ex @@ -2,14 +2,66 @@ defmodule Augie.Firmata.Commands do alias Augie.Firmata alias MidiProto.Message alias MidiProto.Message.SystemExclusive + use Bitwise - def servo_set_enable(:port, true), do: send_sysex(2, <<1, 0>>) - def servo_set_enable(:port, false), do: send_sysex(2, <<0, 0>>) - def servo_set_enable(:starboard, true), do: send_sysex(3, <<1, 0>>) - def servo_set_enable(:starboard, false), do: send_sysex(3, <<0, 0>>) + @moduledoc """ + Handles sending Firmata commands to the bot. + """ - def servo_get_enable(:port), do: send_sysex(4, <<>>) - def servo_get_enable(:starboard), do: send_sysex(5, <<>>) + @servo_min -135 + @servo_max 135 + + defguardp is_channel(n) when is_integer(n) and n >= 0 and n <= 32 + defguardp is_servo_angle(n) when is_integer(n) and n >= @servo_min and n <= @servo_max + + def servo_driver_enabled?(:port), do: send_sysex(2, <<0>>) + def servo_driver_enabled?(:starboard), do: send_sysex(2, <<1>>) + + def servo_driver_enabled(:port, true), do: send_sysex(2, <<0, 1>>) + def servo_driver_enabled(:port, false), do: send_sysex(2, <<0, 0>>) + def servo_driver_enabled(:starboard, true), do: send_sysex(2, <<1, 1>>) + def servo_driver_enabled(:starboard, false), do: send_sysex(2, <<1, 0>>) + + def servo_channel_enabled?(:port, channel), do: servo_channel_enabled?(channel) + def servo_channel_enabled?(:starboard, channel), do: servo_channel_enabled?(channel + 16) + + def servo_channel_enabled?(channel) when is_channel(channel), + do: send_sysex(3, <>) + + def servo_channel_enabled(:port, channel, enabled), do: servo_channel_enabled(channel, enabled) + + def servo_channel_enabled(:starboard, channel, enabled), + do: servo_channel_enabled(channel + 16, enabled) + + def servo_channel_enabled(channel, true) + when is_channel(channel), + do: send_sysex(3, <>) + + def servo_channel_enabled(channel, false) + when is_channel(channel), + do: send_sysex(3, <>) + + def servo_channel_position?(:port, channel), do: servo_channel_position?(channel) + def servo_channel_position?(:starboard, channel), do: servo_channel_position?(channel + 16) + + def servo_channel_position?(channel) + when is_channel(channel), + do: send_sysex(4, <>) + + def servo_channel_position(:port, channel, position), + do: servo_channel_position(channel, position) + + def servo_channel_position(:starboard, channel, position), + do: servo_channel_position(channel + 16, position) + + def servo_channel_position(channel, position) + when is_channel(channel) and is_servo_angle(position) do + sign = if position < 0, do: 1, else: 0 + position = abs(position) + lsb = position &&& 0x7F + msb = position >>> 7 + send_sysex(4, <>) + end defp send_sysex(command, payload) do command diff --git a/webapp/lib/augie/firmata/decoder.ex b/webapp/lib/augie/firmata/decoder.ex index d42f01d..b9559a1 100644 --- a/webapp/lib/augie/firmata/decoder.ex +++ b/webapp/lib/augie/firmata/decoder.ex @@ -40,21 +40,52 @@ defmodule Augie.Firmata.Decoder do end end - def decode(%SystemExclusive{vendor_id: 2, payload: binary}) do - {:ok, {:command, :servo_set_enable_port}, binary} - end + def decode(%SystemExclusive{vendor_id: 2, payload: <<0, 1>>}), + do: {:ok, {:command, :servo_driver_enabled}, {:port, true}} - def decode(%SystemExclusive{vendor_id: 3, payload: binary}) do - {:ok, {:command, :servo_set_enable_starboard}, binary} - end + def decode(%SystemExclusive{vendor_id: 2, payload: <<1, 1>>}), + do: {:ok, {:command, :servo_driver_enabled}, {:starboard, true}} - def decode(%SystemExclusive{vendor_id: 4, payload: binary}) do - {:ok, {:command, :servo_get_enable_port}, binary} - end + def decode(%SystemExclusive{vendor_id: 2, payload: <<0, 0>>}), + do: {:ok, {:command, :servo_driver_enabled}, {:port, false}} - def decode(%SystemExclusive{vendor_id: 5, payload: binary}) do - {:ok, {:command, :servo_get_enable_starboard}, binary} - end + def decode(%SystemExclusive{vendor_id: 2, payload: <<1, 0>>}), + do: {:ok, {:command, :servo_driver_enabled}, {:starboard, false}} + + def decode(%SystemExclusive{vendor_id: 3, payload: <>}) + when channel >= 0 and channel < 16, + do: {:ok, {:command, :servo_channel_enabled}, {:port, channel, true}} + + def decode(%SystemExclusive{vendor_id: 3, payload: <>}) + when channel >= 16 and channel < 32, + do: {:ok, {:command, :servo_channel_enabled}, {:starboard, channel - 16, true}} + + def decode(%SystemExclusive{vendor_id: 3, payload: <>}) + when channel >= 0 and channel < 16, + do: {:ok, {:command, :servo_channel_enabled}, {:port, channel, false}} + + def decode(%SystemExclusive{vendor_id: 3, payload: <>}) + when channel >= 16 and channel < 32, + do: {:ok, {:command, :servo_channel_enabled}, {:starboard, channel - 16, false}} + + def decode(%SystemExclusive{vendor_id: 4, payload: <>}) + when channel >= 0 and channel < 16, + do: {:ok, {:command, :servo_channel_position}, {:port, channel, lsb + (msb <<< 7)}} + + def decode(%SystemExclusive{vendor_id: 4, payload: <>}) + when channel >= 0 and channel < 16, + do: {:ok, {:command, :servo_channel_position}, {:port, channel, 0 - (lsb + (msb <<< 7))}} + + def decode(%SystemExclusive{vendor_id: 4, payload: <>}) + when channel >= 16 and channel < 32, + do: + {:ok, {:command, :servo_channel_position}, {:starboard, channel - 16, lsb + (msb <<< 7)}} + + def decode(%SystemExclusive{vendor_id: 4, payload: <>}) + when channel >= 16 and channel < 32, + do: + {:ok, {:command, :servo_channel_position}, + {:starboard, channel - 16, 0 - (lsb + (msb <<< 7))}} def decode(_), do: {:error, "Unknown message"} diff --git a/webapp/lib/augie_web/live/dashboard_live.html.leex b/webapp/lib/augie_web/live/dashboard_live.html.leex index 2b43daa..1df8c09 100644 --- a/webapp/lib/augie_web/live/dashboard_live.html.leex +++ b/webapp/lib/augie_web/live/dashboard_live.html.leex @@ -29,13 +29,13 @@ <%= live_render(@socket, AugieWeb.OrientationLive, id: :orientation) %> <%= live_render(@socket, AugieWeb.TemperatureLive, id: :temperature) %> <%= live_render(@socket, AugieWeb.PowerLive, id: :power) %> - <%= live_render(@socket, AugieWeb.ServoLive, id: :port, session: %{"driver" => "port"})%> - <%= live_render(@socket, AugieWeb.ServoLive, id: :starboard, session: %{"driver" => "starboard"})%>
<%= live_render(@socket, AugieWeb.GpsLive, id: :gps) %>
<%= live_render(@socket, AugieWeb.CameraLive, id: :camera) %> + <%= live_render(@socket, AugieWeb.ServoLive, id: :port, session: %{"driver" => "port"}) %> + <%= live_render(@socket, AugieWeb.ServoLive, id: :starboard, session: %{"driver" => "starboard"}) %>
diff --git a/webapp/lib/augie_web/live/servo_live.ex b/webapp/lib/augie_web/live/servo_live.ex index 782a8aa..d5d382e 100644 --- a/webapp/lib/augie_web/live/servo_live.ex +++ b/webapp/lib/augie_web/live/servo_live.ex @@ -1,55 +1,160 @@ defmodule AugieWeb.ServoLive do use Phoenix.LiveView + alias Augie.Firmata alias Augie.Firmata.Commands alias Phoenix.PubSub @moduledoc false def mount(_params, %{"driver" => driver}, socket) do - driver_as_atom = String.to_atom(driver) driver_name = String.capitalize(driver) + driver = String.to_atom(driver) + firmata_connected = Firmata.connected?() if connected?(socket) do - PubSub.subscribe(Augie.PubSub, "command.servo_get_enable_#{driver}") - IO.inspect({:wat, driver_as_atom}) - Commands.servo_get_enable(driver_as_atom) + PubSub.subscribe(Augie.PubSub, "command.servo_driver_enabled") + PubSub.subscribe(Augie.PubSub, "command.servo_channel_enabled") + PubSub.subscribe(Augie.PubSub, "command.servo_channel_position") + PubSub.subscribe(Augie.PubSub, "serial_telemetry") + + if firmata_connected do + Commands.servo_driver_enabled?(driver) + + for i <- 0..15 do + Commands.servo_channel_enabled?(driver, i) + Commands.servo_channel_position?(driver, i) + end + end end + servos = + 0..15 + |> Enum.map(fn i -> + {i, + %{ + id: i, + name: "Servo #{i + 1}", + enabled: false, + value: 0 + }} + end) + |> Enum.into(%{}) + {:ok, assign(socket, - driver: driver_as_atom, + driver: driver, driver_name: driver_name, switch_id: "#{driver}", state_known: false, - enabled: false + enabled: false, + servos: servos, + connected: firmata_connected )} end - def handle_info({:servo_get_enable_port, <<0, 0>>}, socket) do - {:noreply, assign(socket, state_known: true, enabled: false)} + def handle_info({:connected, false}, socket), do: {:noreply, assign(socket, connected: false)} + + def handle_info({:connected, _}, %{assigns: %{driver: driver}} = socket) do + Commands.servo_driver_enabled?(driver) + + for i <- 0..15 do + Commands.servo_channel_enabled?(driver, i) + Commands.servo_channel_position?(driver, i) + end + + {:noreply, assign(socket, connected: true)} end - def handle_info({:servo_get_enable_port, <<1, 0>>}, socket) do - {:noreply, assign(socket, state_known: true, enabled: true)} + def handle_info( + {:servo_driver_enabled, {driver, value}}, + %{assigns: %{driver: driver}} = socket + ) do + {:noreply, assign(socket, state_known: true, enabled: value)} end - def handle_info({:servo_get_enable_starboard, <<0, 0>>}, socket) do - {:noreply, assign(socket, state_known: true, enabled: false)} + def handle_info({:servo_driver_enabled, _}, socket), do: {:noreply, socket} + + def handle_info( + {:servo_channel_enabled, {driver, channel, value}}, + %{assigns: %{servos: servos, driver: driver}} = socket + ) do + servo = + servos + |> Map.get(channel) + |> Map.put(:enabled, value) + + servos = + servos + |> Map.put(channel, servo) + + {:noreply, assign(socket, servos: servos)} end - def handle_info({:servo_get_enable_starboard, <<1, 0>>}, socket) do - {:noreply, assign(socket, state_known: true, enabled: true)} + def handle_info({:servo_channel_enabled, _}, socket), do: {:noreply, socket} + + def handle_info( + {:servo_channel_position, {driver, channel, value}}, + %{assigns: %{servos: servos, driver: driver}} = socket + ) do + servo = + servos + |> Map.get(channel) + |> Map.put(:value, value) + + servos = + servos + |> Map.put(channel, servo) + + {:noreply, assign(socket, servos: servos)} end + def handle_info({:servo_channel_position, _}, socket), do: {:noreply, socket} + def handle_event("enable-servos", _, %{assigns: %{driver: driver}} = socket) do - Commands.servo_set_enable(driver, true) - Commands.servo_get_enable(driver) + Commands.servo_driver_enabled(driver, true) {:noreply, socket} end def handle_event("disable-servos", _, %{assigns: %{driver: driver}} = socket) do - Commands.servo_set_enable(driver, false) - Commands.servo_get_enable(driver) + Commands.servo_driver_enabled(driver, false) {:noreply, socket} end + + def handle_event( + "toggle-servo", + %{"servo-id" => servo_no}, + %{assigns: %{servos: servos, driver: driver}} = socket + ) do + servo_no = + servo_no + |> String.to_integer() + + servo = + servos + |> Map.get(servo_no) + + Commands.servo_channel_enabled(driver, servo_no, !servo.enabled) + + {:noreply, assign(socket, servos: servos)} + end + + def handle_event( + "set-servo", + %{"servo-value" => degrees, "servo-id" => servo_no}, + %{assigns: %{servos: servos, driver: driver}} = socket + ) do + degrees = + degrees + |> String.to_integer() + + servo_no = + servo_no + |> String.to_integer() + + Commands.servo_channel_position(driver, servo_no, degrees) + + {:noreply, socket} + end + + def handle_event("set-servo", _, socket), do: {:noreply, socket} end diff --git a/webapp/lib/augie_web/live/servo_live.html.leex b/webapp/lib/augie_web/live/servo_live.html.leex index 0765aab..47281da 100644 --- a/webapp/lib/augie_web/live/servo_live.html.leex +++ b/webapp/lib/augie_web/live/servo_live.html.leex @@ -3,14 +3,56 @@

<%= @driver_name %> Servo Driver

- <%= if @state_known do %> - <%= if @enabled do %> - + <%= if @connected do %> + <%= if @state_known do %> + <%= if @enabled do %> + + <% else %> + + <% end %> <% else %> - + + <% end %> + + + <%= for {id, servo} <- @servos do %> +
+
+ <%= servo.name %> +
+
+ > + +
+
+
+
+
+ + > +
+
+
<% end %> <% else %> - - <% end %> + Disconnected + <% end %>