All telemetry going again in web app.

This commit is contained in:
James Harton 2020-05-14 22:05:26 +12:00
parent 1ea1d5477f
commit 679f2e1913
10 changed files with 175 additions and 68 deletions

View file

@ -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

View file

@ -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

View file

@ -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}")

View file

@ -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,
<<bus_voltage::little-float-size(32), shunt_voltage::little-float-size(32),
current::little-float-size(32),
power::little-float-size(32)>>} <- 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,
<<ox::little-float-size(64), oy::little-float-size(64), oz::little-float-size(64),
ow::little-float-size(64),
temp::little-signed-integer-size(8)>>} <- 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,
<<latitude::little-signed-integer-size(32), longitude::little-signed-integer-size(32),
altitude::little-signed-integer-size(32), speed::little-signed-integer-size(32),
heading::little-float-size(32), satellites::little-integer-size(8),
status::little-integer-size(8)>>} <- 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(<<result::binary, value::integer-size(8)>>, rest)
end
defp depad_binary(_result, <<_>>), do: {:error, "Lost synchronisation"}
end

View file

@ -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) %>
</div>
<div class="cell small-12 medium-6 large-4">
<%= live_render(@socket, AugieWeb.GpsLive, id: :gps) %>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,47 @@
<div class="card">
<div class="card-divider">
<h4>Power</h4>
</div>
<div class="card-section">
<div class="grid-x">
<div class="cell auto"><strong>Bus voltage</strong></div>
<div class="cell auto text-right">
<%= if @bus_voltage do %>
<%= Float.round(@bus_voltage, 2) %>V
<% else %>
-
<% end %>
</div>
</div>
<div class="grid-x">
<div class="cell auto"><strong>Shunt voltage</strong></div>
<div class="cell auto text-right">
<%= if @shunt_voltage do %>
<%= Float.round(@shunt_voltage, 2) %>V
<% else %>
-
<% end %>
</div>
</div>
<div class="grid-x">
<div class="cell auto"><strong>Current</strong></div>
<div class="cell auto text-right">
<%= if @current do %>
<%= Float.round(@current, 2) %>mA
<% else %>
-
<% end %>
</div>
</div>
<div class="grid-x">
<div class="cell auto"><strong>Power</strong></div>
<div class="cell auto text-right">
<%= if @power do %>
<%= Float.round(@power, 2) %>W
<% else %>
-
<% end %>
</div>
</div>
</div>
</div>

View file

@ -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