Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

2. Fledex: Some simple tests

livebooks/2_fledex_first_steps.livemd

2. Fledex: Some simple tests

# We start to define some libraries we need. If you are running on a laptop
# the libraries will be loaded, but on a nerves_livebook you can only use
# what has been compiled in.
Mix.install([
  {:fledex, "~>0.5"}
])

# we define a couple of aliases to not have to type that much
alias Fledex.Leds
alias Fledex.LedStrip
alias Fledex.Driver.Impl.Kino
alias Fledex.Driver.Impl.Spi
alias Fledex.Color.Correction

:ok

Defining a strip

Fledex has several drivers which can also be used in parallel. It’s probably best to only use a single driver to start with. The Kino driver does not have any hardware dependency and therefore is probably a good start to ensure that the library is loaded correctly.

# frame = Kino.Frame.new() |> Kino.render()

{:ok, pid} =
  LedStrip.start_link(
    LedStrip,
    [
      {Kino, update_freq: 1, color_correction: Correction.no_color_correction()}
      # {Spi, color_correction: Correction.define_correction(
      #   Correction.Color.typical_smd5050(),
      #   Correction.Temperature.uncorrected_temperature()
      # )}
    ],
    timer_only_dirty_update: false,
    merge_strategy: :cap
  )

We now define a couple of helper functions. The first 2 functions are talking binaries directly with the LED driver, the latter two are making use of the Leds client module that helps to create nice effects like a rainbow.

defmodule Helpers do
  def red(namespace) do
    Enum.each(1..10, fn _index ->
      LedStrip.set_leds(LedStrip, namespace, [0xFF0000, 0x000000, 0x000000, 0x000000, 0x00000])
      Process.sleep(600)
      LedStrip.set_leds(LedStrip, namespace, [0x000000, 0xFF0000, 0x000000, 0x000000, 0x00000])
      Process.sleep(600)
      LedStrip.set_leds(LedStrip, namespace, [0x000000, 0x000000, 0xFF0000, 0x000000, 0x00000])
      Process.sleep(600)
      LedStrip.set_leds(LedStrip, namespace, [0x000000, 0x000000, 0x000000, 0xFF0000, 0x00000])
      Process.sleep(600)
      LedStrip.set_leds(LedStrip, namespace, [0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000])
      Process.sleep(600)
    end)
  end

  def blue(namespace) do
    Enum.each(1..10, fn _index ->
      LedStrip.set_leds(LedStrip, namespace, [
        0x000000,
        0x000000,
        0x000000,
        0x000000,
        0x0000FF,
        0x00FF00
      ])

      Process.sleep(600)

      LedStrip.set_leds(LedStrip, namespace, [
        0x000000,
        0x000000,
        0x000000,
        0x0000FF,
        0x000000,
        0x00FF00
      ])

      Process.sleep(600)

      LedStrip.set_leds(LedStrip, namespace, [
        0x000000,
        0x000000,
        0x0000FF,
        0x000000,
        0x000000,
        0x00FF00
      ])

      Process.sleep(600)

      LedStrip.set_leds(LedStrip, namespace, [
        0x000000,
        0x0000FF,
        0x000000,
        0x000000,
        0x000000,
        0x00FF00
      ])

      Process.sleep(600)

      LedStrip.set_leds(LedStrip, namespace, [
        0x0000FF,
        0x000000,
        0x000000,
        0x000000,
        0x000000,
        0x00FF00
      ])

      Process.sleep(600)
    end)
  end

  def rainbow(namespace) do
    Enum.each(0..10000, fn index ->
      config = %{
        num_leds: 50,
        reversed: true
      }

      Leds.leds(50)
      |> Leds.rainbow(config)
      |> Leds.send(%{server_name: LedStrip, namespace: namespace, offset: index})

      # before sending the next update we sleep a bit
      Process.sleep(100)
    end)
  end

  def gradient(namespace) do
    Enum.each(0..10000, fn index ->
      Leds.leds(50)
      |> Leds.gradient(0xFF0000, 0x0000FF, %{num_leds: 50})
      |> Leds.send(%{
        server_name: LedStrip,
        namespace: namespace,
        offset: index,
        rotate_left: true
      })

      # before sending the next update we sleep a bit
      Process.sleep(100)
    end)
  end
end

Rainbow

Now use the above functions. We can run the functions by spawning a new thread. This allows to also see what happens if another thread works in parallel. Here we create a rainbow effect that will be send with an changing offset so that the rainbow rotates through the LEDs

LedStrip.define_namespace(LedStrip, :default)
spawn(fn -> Helpers.rainbow(:default) end)

Gradient

Here we create a gradient between red and blue. Also this one we let rotate through. Note, depending on whether you started the rainbow above or not, you might see different results. Try this section with and without running the Rainbow section.

LedStrip.define_namespace(LedStrip, :gradient)
spawn(fn -> Helpers.gradient(:gradient) end)

We can drop the namespaces to see what happens

LedStrip.drop_namespace(LedStrip, :default)
LedStrip.drop_namespace(LedStrip, :gradient)

As we can see our animation continues to be visible, since the namespace gets recreated in every iteration. Not quite what we expected or want. We’ll look on how to fix this in as we explore the Fledex library.

Several animations

This test is running a couple of different ligth definitions in parallel.

LedStrip.define_namespace(LedStrip, :john)
LedStrip.define_namespace(LedStrip, :jane)
LedStrip.define_namespace(LedStrip, :default)

spawn(fn -> Helpers.blue(:jane) end)
spawn(fn -> Helpers.red(:john) end)
spawn(fn -> Helpers.rainbow(:default) end)

As you can see the animations overlap each other. We will work with this principle going forward.

Once we are done we should stop our GenServer (even though I rarely run this one ;-) but it’s good to know how to do it).

Note: you might see some warnings, that are caused by our above functions that will automatically start the server if hasn’t been started yet. You can safely ignore those for now.

LedStrip.stop(LedStrip)