Servo manipulation via Firmata *should* be working.
This commit is contained in:
parent
d0b9d84481
commit
90e2cc6228
12 changed files with 513 additions and 86 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <FreeRTOS_TEENSY4.h>
|
||||
#include <Arduino.h>
|
||||
|
@ -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__
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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, <<channel>>)
|
||||
|
||||
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, <<channel, 1>>)
|
||||
|
||||
def servo_channel_enabled(channel, false)
|
||||
when is_channel(channel),
|
||||
do: send_sysex(3, <<channel, 0>>)
|
||||
|
||||
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, <<channel>>)
|
||||
|
||||
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, <<channel, sign, lsb, msb>>)
|
||||
end
|
||||
|
||||
defp send_sysex(command, payload) do
|
||||
command
|
||||
|
|
|
@ -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: <<channel, 1>>})
|
||||
when channel >= 0 and channel < 16,
|
||||
do: {:ok, {:command, :servo_channel_enabled}, {:port, channel, true}}
|
||||
|
||||
def decode(%SystemExclusive{vendor_id: 3, payload: <<channel, 1>>})
|
||||
when channel >= 16 and channel < 32,
|
||||
do: {:ok, {:command, :servo_channel_enabled}, {:starboard, channel - 16, true}}
|
||||
|
||||
def decode(%SystemExclusive{vendor_id: 3, payload: <<channel, 0>>})
|
||||
when channel >= 0 and channel < 16,
|
||||
do: {:ok, {:command, :servo_channel_enabled}, {:port, channel, false}}
|
||||
|
||||
def decode(%SystemExclusive{vendor_id: 3, payload: <<channel, 0>>})
|
||||
when channel >= 16 and channel < 32,
|
||||
do: {:ok, {:command, :servo_channel_enabled}, {:starboard, channel - 16, false}}
|
||||
|
||||
def decode(%SystemExclusive{vendor_id: 4, payload: <<channel, 0, lsb, msb>>})
|
||||
when channel >= 0 and channel < 16,
|
||||
do: {:ok, {:command, :servo_channel_position}, {:port, channel, lsb + (msb <<< 7)}}
|
||||
|
||||
def decode(%SystemExclusive{vendor_id: 4, payload: <<channel, 1, lsb, msb>>})
|
||||
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: <<channel, 0, lsb, msb>>})
|
||||
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: <<channel, 1, lsb, msb>>})
|
||||
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"}
|
||||
|
||||
|
|
|
@ -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"})%>
|
||||
</div>
|
||||
<div class="cell small-12 medium-6 large-4">
|
||||
<%= live_render(@socket, AugieWeb.GpsLive, id: :gps) %>
|
||||
</div>
|
||||
<div class="cell small-12 medium-6 large-4">
|
||||
<%= 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"}) %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,14 +3,56 @@
|
|||
<h4><%= @driver_name %> Servo Driver</h4>
|
||||
</div>
|
||||
<div class="card-section">
|
||||
<%= if @state_known do %>
|
||||
<%= if @enabled do %>
|
||||
<button type="button" class="button primary expanded" phx-click="disable-servos">Disable Servos</button>
|
||||
<%= if @connected do %>
|
||||
<%= if @state_known do %>
|
||||
<%= if @enabled do %>
|
||||
<button type="button" class="button primary expanded" phx-click="disable-servos">Disable Servos</button>
|
||||
<% else %>
|
||||
<button type="button" class="button alert expanded" phx-click="enable-servos">Enable Servos</button>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<button type="button" class="button alert expanded" phx-click="enable-servos">Enable Servos</button>
|
||||
<button type="button" class="button secondary expanded" disabled>Updating...</button>
|
||||
<% end %>
|
||||
|
||||
|
||||
<%= for {id, servo} <- @servos do %>
|
||||
<div class="grid-x">
|
||||
<div class="call auto">
|
||||
<strong><%= servo.name %></strong>
|
||||
</div>
|
||||
<div class="cell auto text-right">
|
||||
<input id="servo_<%= id %>_enabled"
|
||||
type="checkbox"
|
||||
phx-click="toggle-servo"
|
||||
phx-value-servo-id="<%= id %>"
|
||||
<%= if servo.enabled, do: "checked" %>>
|
||||
<label for="servo_<%= id %>_enabled">
|
||||
<%= if servo.enabled do %>
|
||||
<%= servo.value %>º
|
||||
<% else %>
|
||||
Enable
|
||||
<% end %>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-x">
|
||||
<div class="cell">
|
||||
<form phx-change="set-servo">
|
||||
<input type="hidden" name="servo-id" value="<%= id %>">
|
||||
<input type="range"
|
||||
min="-135"
|
||||
max="135"
|
||||
step="1"
|
||||
name="servo-value"
|
||||
value="<%= servo.value %>"
|
||||
phx-debounce="500"
|
||||
<%= unless servo.enabled, do: "disabled"%>>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<button type="button" class="button secondary expanded" disabled>Connecting...</button>
|
||||
<% end %>
|
||||
<em>Disconnected</em>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Reference in a new issue