Hello,
I had started to work on two “convenience” libraries some time ago : DtsBuddy and Pwmx. Due to my SAAS starting to get clients, my Nerves work has halted for the time being.
I thought it would be better to still share them, if only because Frank gave me some of his time. Even without leveraging my code, they document the dts
loading ceremony and the sysfs
pwm interface. The biggest challenge is of course live-testing on real boards.
DtsBuddy
DtsBuddy (GitHub - Lucassifoni/dts-buddy: Research on a device tree source buddy to help people compile & load DTBOs at runtime in Elixir) is useful to load device tree overlays at runtime. It provides a few system checks, and a friendly-ish API to load runtime overlays.
iex>DtsBuddy.checks()
[{:device_tree_compiler_present, true}, {:overlays_enabled, false}]
iex> DtsBuddy.overlays_enabled?()
false
iex> DtsBuddy.enable_overlays()
:ok
iex> DtsBuddy.overlays_enabled?()
true
iex> import DtsBuddy.Sigil
iex> overlay = ~DTS"""
/dts-v1/;
/plugin/;
/* Example DTS for the MangoPI by Frank Hunleth */
&{/} {
gpios_leds {
compatible = "gpio-leds";
test_led@36 {
label = "test-led-gpio36";
gpios = <&pio 1 4 0>; /* GPIO36/PB4 */
/* Blink LED at 1 Hz (500 ms on, off) */
linux,default-trigger = "pattern";
led-pattern = <1 500 1 0 0 500 0 0>;
};
};
};
"""test
{:ok, "/data/test.dtbo", "test"}
iex> DtsBuddy.load(overlay)
:ok
iex> DtsBuddy.status("test")
:applied
There also is a Diagnostic module (dts-buddy/lib/dts_buddy/diagnostic.ex at main · Lucassifoni/dts-buddy · GitHub) based on the Circuits.GPIO diagnostics features, but it currently only works with the above overlay thought for a MangoPI MQ Pro. It would need to be extended to other boards.
iex> {:ok, gpio} = Circuits.GPIO.open("GPIO12", :input)
{:ok, %Circuits.GPIO.CDev{}}
iex> reader = fn () -> Circuits.GPIO.read(gpio)
#Function<>
iex> DtsBuddy.Diagnostic.run_diagnostic(reader)
[DtsBuddy.Diagnostic] Running system checks
[DtsBuddy.Diagnostic] All system checks passed
[DtsBuddy.Diagnostic] Loading a sample overlay
[DtsBuddy.Diagnostic] Sampling GPIO with the user-supplied reader function for ~5 seconds
[DtsBuddy.Diagnostic] Behavior of the activated GPIO looks correct with 5 LOW samples and 5 HIGH samples
[DtsBuddy.Diagnostic] Unloading overlay testled
[DtsBuddy.Diagnostic] Everything looks OK
:ok
Pwmx
Pwmx (GitHub - Lucassifoni/pwmx: Pure Elixir interface to Sysfs hardware-pwm on Linux) is the simpler one and gives a few convenience helpers to work with Linux PWM :
iex> Pwmx.list_available_outputs()
[
{"virtualchip0", 0},
{"virtualchip0", 1},
{"virtualchip0", 2},
{"virtualchip0", 3}
]
The idea was to give both a “stateful” API :
iex> import Pwmx.Output
iex> {:ok, pid} = Pwmx.open("pwmchip0", 1)
pid |> Pwmx.Output.enable |> Pwmx.Output.set_period(1_000_000, :ns) |> Pwmx.Output.set_duty_cycle(500_000)
# or
pid |> Pwmx.Output.enable |> Pwmx.Output.set_period(1_000_000, :ns) |> Pwmx.Output.set_duty_cycle_normalized(0.5)
As well as a “stateless” API used by the stateful one :
iex> Pwmx.Backend.Sysfs.Ops.get_duty_cycle("pwmchip0", "1")
There’s a virtual backend allowing for unit testing too.
It would need to be tested on live boards too, but I did not manage to get PWM channels to output on the MangoPI MQ Pro’s header after having written a lot of overlays.
I hope something there will be useful to someone !
Best,
Lucas