diff --git a/webapp/lib/augie/sensors/imu.ex b/webapp/lib/augie/sensors/imu.ex index 938f34a..c9f6385 100644 --- a/webapp/lib/augie/sensors/imu.ex +++ b/webapp/lib/augie/sensors/imu.ex @@ -8,11 +8,11 @@ defmodule Augie.Sensor.IMU do @type t :: %IMU{ orientation: Quaternion.t(), - accelerometer: Vector.t(), - magnetometer: Vector.t(), - gyroscope: Vector.t(), - gravity: Vector.t(), - linear_acceleration: Vector.t(), + # accelerometer: Vector.t(), + # magnetometer: Vector.t(), + # gyroscope: Vector.t(), + # gravity: Vector.t(), + # linear_acceleration: Vector.t(), temperature: float } @@ -22,11 +22,11 @@ defmodule Augie.Sensor.IMU do @type t :: %__MODULE__{x: float, y: float, z: float, w: float} end - defmodule Vector do - @moduledoc "Holds the `x`, `y` and `z` values for a vector." - defstruct ~w[x y z]a - @type t :: %__MODULE__{x: float, y: float, z: float} - end + # defmodule Vector do + # @moduledoc "Holds the `x`, `y` and `z` values for a vector." + # defstruct ~w[x y z]a + # @type t :: %__MODULE__{x: float, y: float, z: float} + # end @doc """ Convert incoming telemetry into a structure. @@ -37,21 +37,21 @@ defmodule Augie.Sensor.IMU do orientation_y, orientation_z, orientation_w, - accelerometer_x, - accelerometer_y, - accelerometer_z, - magnetometer_x, - magnetometer_y, - magnetometer_z, - gyroscope_x, - gyroscope_y, - gyroscope_z, - gravity_x, - gravity_y, - gravity_z, - linear_acceleration_x, - linear_acceleration_y, - linear_acceleration_z, + # accelerometer_x, + # accelerometer_y, + # accelerometer_z, + # magnetometer_x, + # magnetometer_y, + # magnetometer_z, + # gyroscope_x, + # gyroscope_y, + # gyroscope_z, + # gravity_x, + # gravity_y, + # gravity_z, + # linear_acceleration_x, + # linear_acceleration_y, + # linear_acceleration_z, temperature ]) do {:ok, @@ -62,15 +62,15 @@ defmodule Augie.Sensor.IMU do z: orientation_z, w: orientation_w }, - accelerometer: %Vector{x: accelerometer_x, y: accelerometer_y, z: accelerometer_z}, - magnetometer: %Vector{x: magnetometer_x, y: magnetometer_y, z: magnetometer_z}, - gyroscope: %Vector{x: gyroscope_x, y: gyroscope_y, z: gyroscope_z}, - gravity: %Vector{x: gravity_x, y: gravity_y, z: gravity_z}, - linear_acceleration: %Vector{ - x: linear_acceleration_x, - y: linear_acceleration_y, - z: linear_acceleration_z - }, + # accelerometer: %Vector{x: accelerometer_x, y: accelerometer_y, z: accelerometer_z}, + # magnetometer: %Vector{x: magnetometer_x, y: magnetometer_y, z: magnetometer_z}, + # gyroscope: %Vector{x: gyroscope_x, y: gyroscope_y, z: gyroscope_z}, + # gravity: %Vector{x: gravity_x, y: gravity_y, z: gravity_z}, + # linear_acceleration: %Vector{ + # x: linear_acceleration_x, + # y: linear_acceleration_y, + # z: linear_acceleration_z + # }, temperature: temperature }} end diff --git a/webapp/lib/augie/sensors/logger.ex b/webapp/lib/augie/sensors/logger.ex deleted file mode 100644 index cb925cd..0000000 --- a/webapp/lib/augie/sensors/logger.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Augie.Sensor.Logger do - defstruct ~w[message at]a - alias __MODULE__ - - @moduledoc """ - Storage struct for telemetry logs. - """ - - @type t :: %Logger{message: String.t(), at: DateTime.t()} - - @doc """ - Convert incoming telemetry into a structure. - """ - @spec build([String.t()]) :: {:ok, Logger.t()} | {:error, any} - def build([message]) when is_binary(message), - do: {:ok, %Logger{message: message, at: DateTime.utc_now()}} - - def build(_data), do: {:error, :bad_data} -end diff --git a/webapp/lib/augie/serial_telemetry.ex b/webapp/lib/augie/serial_telemetry.ex index 45731b5..ace0474 100644 --- a/webapp/lib/augie/serial_telemetry.ex +++ b/webapp/lib/augie/serial_telemetry.ex @@ -2,7 +2,7 @@ defmodule Augie.SerialTelemetry do use GenServer alias Augie.SerialTelemetry.Decoder alias Circuits.UART - alias MidiProto.{Firmata, Parser} + alias MidiProto.Parser alias Phoenix.PubSub require Logger @@ -40,8 +40,15 @@ defmodule Augie.SerialTelemetry do {:ok, messages, parser} -> for message <- messages do case Decoder.decode(message) do - {:ok, message} -> Logger.debug("Decoded message: #{inspect(message)}") - {:error, _} -> Logger.debug("Cannot decode message: #{inspect(message)}") + {:ok, name, message} -> + PubSub.broadcast(Augie.PubSub, name, message) + + {:error, reason} -> + Logger.warn( + "Unable to decode incoming Firmata message: #{inspect(reason)}: #{ + inspect(message, limit: :infinity) + }" + ) end end @@ -87,7 +94,7 @@ defmodule Augie.SerialTelemetry do UART.open(UART, uart, active: true, speed: @baud_rate, - framing: UART.Framing.MIDI, + framing: UART.Framing.None, rx_framing_timeout: 0 ) do Logger.info("Connected to Teensy on #{uart}") diff --git a/webapp/lib/augie/serial_telemetry/decoder.ex b/webapp/lib/augie/serial_telemetry/decoder.ex index a3b2d14..eb7751c 100644 --- a/webapp/lib/augie/serial_telemetry/decoder.ex +++ b/webapp/lib/augie/serial_telemetry/decoder.ex @@ -1,17 +1,57 @@ defmodule Augie.SerialTelemetry.Decoder do + alias Augie.Sensor.{GPS, IMU, Power} alias MidiProto.Message.SystemExclusive use Bitwise - def decode(%SystemExclusive{vendor_id: 1, payload: <<2, 0, lsb, msb>>}) do - value = lsb + (msb <<< 7) - {:ok, %{name: :imu_temperature, value: value}} + @moduledoc """ + This file is a bit of an omega mess because it uses binary pattern matching to + decode the message payloads. + """ + + def decode(%SystemExclusive{vendor_id: 1, payload: <<2, 0, payload::binary>>}) do + with {:ok, + <>} <- depad_binary(<<>>, payload), + {:ok, update} <- Power.build([bus_voltage, shunt_voltage, current, power]) do + {:ok, "telemetry.power", update} + end end - # def decode(%SystemExclusive{ - # vendor_id: 1, - # payload: <<1, 0, lsb0, msb0, lsb1, msb1, lsb2, msb2, lsb3, msb3>> - # }) do - # end + def decode(%SystemExclusive{vendor_id: 1, payload: <<1, 0, payload::binary>>}) do + with {:ok, + <>} <- depad_binary(<<>>, payload), + {:ok, update} <- IMU.build([ox, oy, oz, ow, temp]) do + {:ok, "telemetry.imu", update} + end + end + + def decode(%SystemExclusive{vendor_id: 1, payload: <<0, 0, payload::binary>>}) do + with {:ok, + <>} <- depad_binary(<<>>, payload), + {:ok, update} <- + GPS.build([latitude, longitude, altitude, heading, speed, satellites, status]) do + {:ok, "telemetry.gps", update} + end + end def decode(_), do: {:error, "Unknown message"} + + defp depad_binary(result, ""), do: {:ok, result} + + defp depad_binary( + result, + <<0::integer-size(1), lsb::integer-size(7), 0::integer-size(1), msb::integer-size(7), + rest::binary>> + ) do + value = (msb <<< 7) + lsb &&& 0xFF + depad_binary(<>, rest) + end + + defp depad_binary(_result, <<_>>), do: {:error, "Lost synchronisation"} end diff --git a/webapp/lib/augie_web/live/dashboard_live.html.leex b/webapp/lib/augie_web/live/dashboard_live.html.leex index ce7ba28..c5a1d36 100644 --- a/webapp/lib/augie_web/live/dashboard_live.html.leex +++ b/webapp/lib/augie_web/live/dashboard_live.html.leex @@ -28,6 +28,7 @@ <%= 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.GpsLive, id: :gps) %> diff --git a/webapp/lib/augie_web/live/gps_live.ex b/webapp/lib/augie_web/live/gps_live.ex index 9c86057..6c37a25 100644 --- a/webapp/lib/augie_web/live/gps_live.ex +++ b/webapp/lib/augie_web/live/gps_live.ex @@ -20,7 +20,7 @@ defmodule AugieWeb.GpsLive do @map_opts [size: "640x320", key: "AIzaSyBibH_1Yibm3gshxsQDUKw7mjaH9SyMrgw", maptype: "hybrid"] def mount(_params, _context, socket) do - if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "GPS") + if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "telemetry.gps") socket = socket diff --git a/webapp/lib/augie_web/live/orientation_live.ex b/webapp/lib/augie_web/live/orientation_live.ex index f7d40b7..3350e9a 100644 --- a/webapp/lib/augie_web/live/orientation_live.ex +++ b/webapp/lib/augie_web/live/orientation_live.ex @@ -9,7 +9,7 @@ defmodule AugieWeb.OrientationLive do @moduledoc false def mount(_params, _context, socket) do - if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "IMU") + if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "telemetry.imu") {:ok, assign(socket, data_ready: false)} end diff --git a/webapp/lib/augie_web/live/power_live.ex b/webapp/lib/augie_web/live/power_live.ex new file mode 100644 index 0000000..be9163b --- /dev/null +++ b/webapp/lib/augie_web/live/power_live.ex @@ -0,0 +1,31 @@ +defmodule AugieWeb.PowerLive do + use Phoenix.LiveView + alias Augie.Sensor.Power + alias Phoenix.PubSub + + @moduledoc false + + def mount(_params, _context, socket) do + if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "telemetry.power") + + {:ok, assign(socket, bus_voltage: nil, shunt_voltage: nil, current: nil, power: nil)} + end + + def handle_info( + %Power{ + bus_voltage: bus_voltage, + shunt_voltage: shunt_voltage, + current: current, + power: power + }, + socket + ), + do: + {:noreply, + assign(socket, + bus_voltage: bus_voltage, + shunt_voltage: shunt_voltage, + current: current, + power: power + )} +end diff --git a/webapp/lib/augie_web/live/power_live.html.leex b/webapp/lib/augie_web/live/power_live.html.leex new file mode 100644 index 0000000..304af88 --- /dev/null +++ b/webapp/lib/augie_web/live/power_live.html.leex @@ -0,0 +1,47 @@ +
+
+

Power

+
+
+
+
Bus voltage
+
+ <%= if @bus_voltage do %> + <%= Float.round(@bus_voltage, 2) %>V + <% else %> + - + <% end %> +
+
+
+
Shunt voltage
+
+ <%= if @shunt_voltage do %> + <%= Float.round(@shunt_voltage, 2) %>V + <% else %> + - + <% end %> +
+
+
+
Current
+
+ <%= if @current do %> + <%= Float.round(@current, 2) %>mA + <% else %> + - + <% end %> +
+
+
+
Power
+
+ <%= if @power do %> + <%= Float.round(@power, 2) %>W + <% else %> + - + <% end %> +
+
+
+
diff --git a/webapp/lib/augie_web/live/temperature_live.ex b/webapp/lib/augie_web/live/temperature_live.ex index 6e0ae0f..d29898b 100644 --- a/webapp/lib/augie_web/live/temperature_live.ex +++ b/webapp/lib/augie_web/live/temperature_live.ex @@ -6,7 +6,7 @@ defmodule AugieWeb.TemperatureLive do @moduledoc false def mount(_params, _context, socket) do - if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "IMU") + if connected?(socket), do: PubSub.subscribe(Augie.PubSub, "telemetry.imu") {:ok, assign(socket, temperature: nil)} end