Packages
scenic_driver_oled
0.1.0
This library provides a [Scenic](https://github.com/boydm/scenic) driver implementation for OLED
Current section
Files
Jump to
Current section
Files
lib/driver.ex
defmodule OLED.Scenic.Driver do
@moduledoc """
## Requirements
The Scenic driver requires `rpi_fb_capture` and `scenic_driver_nerves_rpi` to work:
```elixir
{:scenic_driver_nerves_rpi, "~> 0.10", targets: @all_targets},
{:rpi_fb_capture, "~> 0.3.0"}
```
## Configuration
The driver can be configured in two ways:
Passing the entire configuration for the display:
```elixir
config :my_app, :viewport, %{
name: :main_viewport,
default_scene: {MyApp.Scene.Default, nil},
size: {128, 64},
opts: [scale: 1.0],
drivers: [
%{
module: OLED.Scenic.Driver,
opts: [
display: [
driver: :ssd1306,
type: :i2c,
device: "i2c-1",
address: 60,
width: 128,
height: 32
],
dithering: :sierra
]
}
]
}
```
Passing the display module:
```elixir
config :my_app, :viewport, %{
name: :main_viewport,
default_scene: {MyApp.Scene.Default, nil},
size: {128, 64},
opts: [scale: 1.0],
drivers: [
%{
module: OLED.Scenic.Driver,
opts: [
display: MyApp.MyDisplay,
dithering: :sierra
]
}
]
}
```
### Dithering
OLED takes avatage of the `rpi_db_capture` dithering feature to improve the visualization.
The current modes supported for the `dithering` key are:
* `:none` - No dithering applied
* `:floyd_steinberg` - Floyd–Steinberg
* `:sierra` - Sierra (also called Sierra-3)
* `:sierra_2row` - Two-row Sierra
* `:sierra_lite` - Sierra Lite
"""
use Scenic.ViewPort.Driver
@impl true
def init(viewport, size, config) do
vp_supervisor = vp_supervisor(viewport)
rpi_driver_config = {vp_supervisor, size, %{module: Scenic.Driver.Nerves.Rpi}}
with {:ok, rpi_driver} <- Scenic.ViewPort.Driver.start_link(rpi_driver_config),
{:ok, display} <- get_display(config[:display]),
{:ok, width, height} <- OLED.Display.Server.get_dimensions(display),
{:ok, cap} <- RpiFbCapture.start_link(width: width, height: height, display: 0) do
configure_dithering(cap, config[:dithering])
send(self(), :capture)
{:ok,
%{
viewport: viewport,
display: display,
cap: cap,
rpi_driver: rpi_driver,
last_crc: -1
}}
else
error ->
{:stop, error}
end
end
@impl true
def handle_info(:capture, state) do
{:ok, frame} = RpiFbCapture.capture(state.cap, :mono_column_scan)
crc = :erlang.crc32(frame.data)
if crc != state.last_crc do
OLED.Display.Server.display_frame(state.display, frame.data, memory_mode: :vertical)
end
Process.send_after(self(), :capture, 50)
{:noreply, %{state | last_crc: crc}}
end
defp configure_dithering(_cap, nil),
do: nil
defp configure_dithering(cap, dithering),
do: :ok = RpiFbCapture.set_dithering(cap, dithering)
defp get_display(display_config) when is_list(display_config),
do: OLED.Display.Server.start_link(display_config)
defp get_display(display_name) when is_atom(display_name),
do: {:ok, display_name}
defp get_display(_),
do: {:error, :display_config}
defp vp_supervisor(viewport) do
[supervisor_pid | _] =
viewport
|> Process.info()
|> get_in([:dictionary, :"$ancestors"])
supervisor_pid
end
end