Skip to content

Commit

Permalink
Very big refactoring of the led_strip and driver settings
Browse files Browse the repository at this point in the history
CAUTION: THIS VERSION IS NOT COMPATIBLE WITH PREVIOUS VERSIONS

To adapt to this version only rather small changes are required The biggest change is to not use atoms for drivers, but modules and it defines the default settings. Those can be overwritten through keywords.

This commit does NOT fix all remaining issues after this refactoring, but it's functional enough. Check the TODOs for the remaining parts
  • Loading branch information
a-maze-d committed Jul 31, 2024
1 parent 26dbd73 commit bf3d6aa
Show file tree
Hide file tree
Showing 48 changed files with 1,218 additions and 1,033 deletions.
5 changes: 3 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--
Copyright 2023, Matthias Reik <fledex@reik.org>
Copyright 2023-2024, Matthias Reik <fledex@reik.org>
SPDX-License-Identifier: Apache-2.0
-->
Expand Down Expand Up @@ -34,4 +34,5 @@ Please delete options that are not relevant.
- [ ] I run `mix docs` and no issues were found
- [ ] I run through the different `livebook/`s to ensure that they all work.
- [ ] I added a new livebook (or modified an exisitng one) to explain the new functionality (only applies for major functionality)
- [ ] I run `pipx run reuse lint` and fixed all issues
- [ ] I run `mix reuse` (or `pipx run reuse lint`) and fixed all issues
- [ ] I run `mix format`
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--
Copyright 2023, Matthias Reik <fledex@reik.org>
Copyright 2023-2024, Matthias Reik <fledex@reik.org>
SPDX-License-Identifier: Apache-2.0
-->
Expand All @@ -14,4 +14,4 @@ By making a pull request you agree to the [CLA (= Contributor License Agreement)
You can also agree to the agreement outside a pull request (even though that's more complicated, but might make sense for companies). Please contact me in that case

## Right
As a contributor you have the right to add yourself ot the [Contributors file](CONTRIBUTORS.md).
As a contributor you have the right to add yourself to the [Contributors file](CONTRIBUTORS.md).
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--
Copyright 2023, Matthias Reik <fledex@reik.org>
Copyright 2023-2024, Matthias Reik <fledex@reik.org>
SPDX-License-Identifier: Apache-2.0
-->
Expand All @@ -19,7 +19,7 @@ Fledex is a small [Elixir](https://elixir-lang.org/) library It really is intend
It is written for a RaspberryPi Zero W running [Nerves](https://nerves-project.org/) especially with a [Nerves-Livebook](https://hexdocs.pm/nerves/getting-started.html#nerves-livebook).
The intent of the library is to simplify the programming of a programmable LED strip (currently based on a [WS2801 chip](https://cdn-shop.adafruit.com/datasheets/WS2801.pdf)) and thereby to make it accessible even for kids.

The idea is to introduce similarly easy concepts for the programming of LEDs as [SonicPi](https://sonic-pi.net/) did for music. The library was developped in collaboration with my son and hopefully we can push it to become better over time. For my son the goal will be to connect the LEDs to some music and to animate the LEDs depending on the beat.
The idea is to introduce similarly easy concepts for the programming of LEDs as [SonicPi](https://sonic-pi.net/) did for music. The library was developed in collaboration with my son and hopefully we can push it to become better over time. For my son the goal will be to connect the LEDs to some music and to animate the LEDs depending on the beat.

Quite a lot of inspiration came from the [FastLED project](http://fastled.io/) and quite a few of their functions got reimplemented in Elixir. If you look at the implementation of some of those functions you might want to look at their comments.

Expand Down
29 changes: 25 additions & 4 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--
Copyright 2023, Matthias Reik <fledex@reik.org>
Copyright 2023-2024, Matthias Reik <fledex@reik.org>
SPDX-License-Identifier: Apache-2.0
-->
Expand All @@ -13,10 +13,25 @@ Even though this library is published, there are things I still want to do befor
- [ ] Add installation instructions (v0.5)
- [ ] Add Branching sections in the livebooks to separate things that don't need to be together? (v0.5)
- [ ] Testing
- [x] Perform an extra round of testing on hardware (v0.4)
- [x] Fix the issue in the livebooks that `simple_broadcast/1` gets called directly now, since the function gets imported (v0.4)
- [ ] Go through all Livebooks and make sure they all work after all the refactorings, there seem to be some minor issues. (v0.4)
- [ ] Add more error handling scenarios (v0.5)
- [ ] parse the config for missing functions, so that errors can be detected early on, instead of failing when trying to paint things (v0.6)
- [ ] Driver improvements
- [x] Fix the LedsDriver.reinit so that a reconfiguration of drivers is possible. Currently the new config is not passed in (v0.5)
- [ ] Give it an extra test drive
- [ ] Fix the SPI driver so that it compares the settings
- [x] Improve the LedsDriver config (v0.5)
- [ ] Update the documentation
- [ ] Cleanup
- [x] Update the livebooks
- [ ] Add some more tests
- [ ] Remove the "change_config" function
- [ ] Some extensive testing
- [ ] Work down the TODOs
- [ ] Cleanup
- [ ] Perform an extra round of testing on hardware (v0.4)
- [ ] Improve the LedsDriver config (v0.5)
- [x] `LedStrip` is partially directly called in the `Manager` (v0.5)
- [ ] Enable Telemetry? (v0.5)
- [ ] Missing functionality
- [ ] Add the possibility to clear the LEDs when initializing the LedsDriver (v0.5)
Expand All @@ -25,8 +40,14 @@ Even though this library is published, there are things I still want to do befor
- [ ] Clustering
- [ ] Provide examples on how to cluster (v0.5)
- [ ] Add an example where several nodes are connected to transfer pubsub messages accross nodes (v0.5)
- [ ] Implement music beat through clustering
- [ ] Do we need to have language packs that allows to adjust to other languages? At least for color it would be quite easy with some `defdelegate`
- [ ] Create smartcells (v0.6)
- [ ] Increase consumption
- [ ] Use in school project (v0.4)
- [x] Use in school project (v0.4). The learnings:
- [ ] setting up livebook (a really working version) on windows is anything than easy :-( Can we do something about it? Investigate (v0.5?)
- [x] Reconfiguration of the strip is an important thing (see TODO item above)
- [ ] Useful to provide a full story about colors (additive / subtractive colors), hardware setup (analogy with a bus letting 24 passangers off the bus at every led-bus-stop). Create a comprehensive write-up
- [ ] Create a video (v0.5)
- [ ] Talk on meetups? (v0.5 or v0.6)
- [ ] Migrate outstanding TODOs to github (v0.6?)
Expand Down
14 changes: 8 additions & 6 deletions docs/cheatsheet.cheatmd
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ In addition a `Fledex.Animation.Manager` will be started in the background (if t
{: .col-2}

### Example

```elixir
led_strip :name, :kino do
animation :seconds do
Expand All @@ -61,6 +62,7 @@ led_strip :name, :kino do
end
end
```

### Parameters

The `led_strip` takes the following parameters:
Expand Down Expand Up @@ -88,6 +90,7 @@ See below for more details about them
{: .col-2}

### Example

```elexir
animation name, options do
leds(10)
Expand Down Expand Up @@ -153,7 +156,7 @@ Contrary to the `animation` the `static` structure only get called once and does

Any `animation` can be wrapped into an effect that impacts how the `animation` is perceived.

### Example:
### Example

```elexir
effect Fledex.Effect.Rotation, trigger_name: :john do
Expand Down Expand Up @@ -286,7 +289,6 @@ leds(3)

The above functions would be enough, but some special functions make life easier:


| Function | Description |
|--- |--- |
| `repeat/2` | This function does what it says. It will repeat the current sequence n-times and make the whole sequence n-times longer. |
Expand All @@ -296,7 +298,7 @@ The above functions would be enough, but some special functions make life easier
## The colors
{: .col-2}

Fledex has a very extensive color list as can be found on [Wikipedia](https://en.wikipedia.org/wiki/List_of_colors:_A%E2%80%93F). Take a look at `Fledex.Color.Names` for the full list of colors, but here the most important colours:
Fledex has a very extensive color list as can be found on [Wikipedia](https://en.wikipedia.org/wiki/List_of_colors:_A%E2%80%93F). Take a look at `Fledex.Color.Names` for the full list of colors, but here the most important colors:

### Examples

Expand All @@ -320,16 +322,16 @@ Fledex has a very extensive color list as can be found on [Wikipedia](https://en

### Usage

There are several ways on how the color can be applied to an `Fledex.Leds` struct:
There are several ways on how a color can be applied to an `Fledex.Leds` struct:

#### Atom

```elexir
leds(1) |> light(:red)
```
```

#### Function

```elexir
leds(1) |> red()
```
```
108 changes: 108 additions & 0 deletions docs/driver_reinit_logic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<!--
Copyright 2024, Matthias Reik <fledex@reik.org>
SPDX-License-Identifier: Apache-2.0
-->
# Intro
This is just some working material to reflect on what I would like to do, how I want to impelemnt it.

The Led strip gets initalized with a set of drivers and that configuration can be updated with a new set.
Ideally we don't tear everything down, but only those parts that are really requried and we reconfigure those parts that only have minor changes.

This document is investigating the approach we want to take.

Note:
the different drivers are NOT uniquely identified (and it would be a too big burden on the user to do so), so that we have to figure out the most appropriate update from the information that we have at our hands.

Note2:
I'm not sure whether it's really necessary to go through all those troubles to keep existing drivers active. Maybe it's much easier to tear everything down. Maybe we should KISS. The only issue is that, when we recompile a strip (when changing the configuration), we do have to "reconfigure" the drivers.
Usually the drivers don't change in this scenario, so tearing everything down and rebuildnig it feels wrong.
A bit of a middle ground could be that we reinit ONLY if the number of drivers, their types, and their orders is exactly the same.

# driver structure
Before we look at the information available, let's look at the driver/config structure.
A driver consist always of two parts:

1. The driver module (which is also responsible for the default configuration of the driver)
2. The driver configuration, which specifies any of the driver settings, mainly those that are different from the default configuration. The two configurations get merged together to form the actual configuration.

It is important to realize that additional settings can be specified at runtime that are not available during configuration time that we want to preserve as far as possible (even though this is not the main use case)

The LedStrip takes a list of those drivers. It should be noted that the driver module is not necessarily unique, but can be repeated several times. This is improtant, because it allows to use the SPI driver several times but on different SPI ports.

# Info available
The information that is at our disposal is the previous configuration and the new configuration. Thus, we should compare the two with each other. This comparison is, however not super trival. Therefore we need to look on how we can make this comparison as robust as possible to match the drivers (and their configurations) as close as possible

# Recommendation
The ideas is to sort the driver modules by name, so that we have a stable order.
As a secondary sorting criteria we can use the (merged) configuration.

# Questions
How can we make sure we compare the most important parts of a driver config first? For example, in the case of an SPI driver, it would probably be the most important compare the SPI device first before we compare all the other settings.

It turns out that the standard Elixir sorting for collection types is already doing hwat we are looking for (except maybe sorting the keywords in the correct order), see:
https://hexdocs.pm/elixir/1.15.7/Kernel.html#module-structural-comparison.

Thus (as a first step) it would be as simple as doing the following:

```elixir
Enum.sort(drivers)
```

Here is a simple example that demonstrates the effect:

```elixir
iex> drivers = [
...> {String, [a: 1, b: 2, c: "abc"]},
...> {String, [a: 2, b: 2, c: "abc"]},
...> {String, [a: 1, b: 3, c: "abc"]},
...> {String, [a: 1, b: 2, c: "xyz"]},
...> {List, [a: 1, b: 2, c: "abc"]}
...> ]
[
{String, [a: 1, b: 2, c: "abc"]},
{String, [a: 2, b: 2, c: "abc"]},
{String, [a: 1, b: 3, c: "abc"]},
{String, [a: 1, b: 2, c: "xyz"]},
{List, [a: 1, b: 2, c: "abc"]}
]

iex(7)> Enum.sort(drivers)
[
{List, [a: 1, b: 2, c: "abc"]},
{String, [a: 1, b: 2, c: "abc"]},
{String, [a: 1, b: 2, c: "xyz"]},
{String, [a: 1, b: 3, c: "abc"]},
{String, [a: 2, b: 2, c: "abc"]}
]
```

# Conclusion
We start with simply sorting our drivers.

# Next step
As a next step we'll look how to then make a decision whether we need to remove an existing driver, we need to update a driver or we have to add a new driver.

The end goal is to have the same list as the `new_drivers` and therefore we iterate over the `new_drivers` list and try to find the most appropriate driver in the `old_drivers` list.

Thus could look like the following:
```elixir
def find_usable_driver_index({module, config}, drivers, first_index) do
# remove the part of the drivers that are not interesting
index = drivers
|> Enum.slice(first_index, length(drivers))
|> Enum.find_index(module)

# correct the found index since we had a limited search range
case index do
nil -> nil
x -> x + first_index
end
end

Enum.reduce(new_drivers, {index: 0, actions: []}, fn {{module, config}, {index: first_index, actions: actions} = acc}) ->
case find_usable_driver_index({module, config}, old_drivers, first_index) do
x when x >=0 -> {index: x, actions:[{:update, x, {module, config}}]}
nil -> {index: first_index, actions: [{:new, {module, config}}]}
end)
```
2 changes: 1 addition & 1 deletion docs/fledex_structure_thoughts.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The `led_strip` takes a list of config structs and creates the led strip configu
It should also be possible to configure jobs and coordinators (on root level), i.e.:
``` elixir
use Fledex
led_strip :john, :none do
led_strip :john, Null do
animation :joe, do
leds(10)
end
Expand Down
14 changes: 10 additions & 4 deletions lib/fledex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ defmodule Fledex do
import Fledex.Color.Names
import Fledex.Utils.PubSub

alias Fledex.Driver.Impl.Kino
alias Fledex.Driver.Impl.Logger
alias Fledex.Driver.Impl.Null
alias Fledex.Driver.Impl.PubSub
alias Fledex.Driver.Impl.Spi
alias Fledex.Utils.Dsl

Dsl.init(opts)
Expand Down Expand Up @@ -200,7 +205,7 @@ defmodule Fledex do
example livebook](5_fledex_weather_example.livemd)):
```elixir
Fledex.Utils.PubSub.simple_broadcast(%{temperature: -15.2})
simple_broadcast(%{temperature: -15.2})
```
Each job consists of:
Expand Down Expand Up @@ -229,7 +234,7 @@ defmodule Fledex do
job :clock, ~e[@secondly]e do
date_time = DateTime.utc_now()
Fledex.Utils.PubSub.simple_broadcast(%{
simple_broadcast(%{
clock_hour: date_time.hour,
clock_minute: date_time.minute,
clock_second: date_time.second
Expand Down Expand Up @@ -257,7 +262,7 @@ defmodule Fledex do
This introduces a new led_strip.
The `strip_options` specifies the driver configuration that should be used.
A set of default drivers exist for conenience that can be used like `:spi`, `:kino`, ...
A set of default drivers exist for conenience that can be used like `Spi`, `Null`, ...
(see `Fledex.LedStrip` for details)
A special driver `:config` exists that will simply return the converted dsl to the
Expand All @@ -269,12 +274,13 @@ defmodule Fledex do
"""

# @spec led_strip(atom, atom | keyword, Macro.t) :: Macro.t | map()
defmacro led_strip(strip_name, strip_options \\ :kino, do: block) do
defmacro led_strip(strip_name, drivers, strip_options \\ [], do: block) do
configs_ast = Dsl.ast_extract_configs(block)

quote do
Dsl.configure_strip(
unquote(strip_name),
unquote(drivers),
unquote(strip_options),
unquote(configs_ast)
)
Expand Down
Loading

0 comments on commit bf3d6aa

Please sign in to comment.