Skip to content
This repository was archived by the owner on Jan 11, 2024. It is now read-only.

Commit 2dc2f06

Browse files
committed
Add example project
1 parent 13852ad commit 2dc2f06

15 files changed

Lines changed: 494 additions & 0 deletions

File tree

example/.formatter.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Used by "mix format"
2+
[
3+
inputs: [
4+
"{mix,.formatter}.exs",
5+
"{config,lib,test}/**/*.{ex,exs}",
6+
"rootfs_overlay/etc/iex.exs"
7+
]
8+
]

example/.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# The directory Mix will write compiled artifacts to.
2+
/_build/
3+
4+
# If you run "mix test --cover", coverage assets end up here.
5+
/cover/
6+
7+
# The directory Mix downloads your dependencies sources to.
8+
/deps/
9+
10+
# Where third-party dependencies like ExDoc output generated docs.
11+
/doc/
12+
13+
# Ignore .fetch files in case you like to edit your project deps locally.
14+
/.fetch
15+
16+
# If the VM crashes, it generates a dump, let's ignore it too.
17+
erl_crash.dump

example/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Example
2+
3+
Example of driving GPIOs via the character device driver.
4+
5+
This example provides switching between two LEDs via a button. When the button
6+
is not press `:led1` will be active and when the button is pressed `:led2` will
7+
be active.
8+
9+
By default this will try to use `gpiochip0` for the GPIO device. `:led1` will be
10+
set to line `17` and `:led2` will be set to line `22`. The button events will be
11+
sent to line `27`. This defaults should work for Raspberry PI based systems.
12+
However, if you need to configure the system you can in the `target.exs` file
13+
like:
14+
15+
```elixir
16+
config :example,
17+
config: [
18+
chip: <chip_name>,
19+
led1: <led1_line_number>
20+
led2: <led2_line_number>,
21+
button: <button_line_number>
22+
]
23+
```
24+
25+
![Circuit Example rpi0](assets/cdev_example_rpi0.png)
26+
27+
## Notes
28+
29+
In the latest Nerves system the character device driver will not work, so this
30+
example ensures that we use the `1.13` series systems to make sure the Linux
31+
headers support the GPIO character driver. Once the Nerves systems update to a
32+
capable Linux header version this project will be updated.
33+
34+
## Targets
35+
36+
Nerves applications produce images for hardware targets based on the
37+
`MIX_TARGET` environment variable. If `MIX_TARGET` is unset, `mix` builds an
38+
image that runs on the host (e.g., your laptop). This is useful for executing
39+
logic tests, running utilities, and debugging. Other targets are represented by
40+
a short name like `rpi3` that maps to a Nerves system image for that platform.
41+
All of this logic is in the generated `mix.exs` and may be customized. For more
42+
information about targets see:
43+
44+
https://hexdocs.pm/nerves/targets.html#content
45+
46+
## Getting Started
47+
48+
To start your Nerves app:
49+
* `export MIX_TARGET=my_target` or prefix every command with
50+
`MIX_TARGET=my_target`. For example, `MIX_TARGET=rpi3`
51+
* Install dependencies with `mix deps.get`
52+
* Create firmware with `mix firmware`
53+
* Burn to an SD card with `mix firmware.burn`
54+
55+
## Learn more
56+
57+
* Official docs: https://hexdocs.pm/nerves/getting-started.html
58+
* Official website: https://nerves-project.org/
59+
* Forum: https://elixirforum.com/c/nerves-forum
60+
* Discussion Slack elixir-lang #nerves ([Invite](https://elixir-slackin.herokuapp.com/))
61+
* Source: https://github.com/nerves-project/nerves
202 KB
Loading

example/config/config.exs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# This file is responsible for configuring your application
2+
# and its dependencies with the aid of the Mix.Config module.
3+
#
4+
# This configuration file is loaded before any dependency and
5+
# is restricted to this project.
6+
import Config
7+
8+
# Enable the Nerves integration with Mix
9+
Application.start(:nerves_bootstrap)
10+
11+
config :example, target: Mix.target()
12+
13+
# Customize non-Elixir parts of the firmware. See
14+
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
15+
16+
config :nerves, :firmware, rootfs_overlay: "rootfs_overlay"
17+
18+
# Set the SOURCE_DATE_EPOCH date for reproducible builds.
19+
# See https://reproducible-builds.org/docs/source-date-epoch/ for more information
20+
21+
config :nerves, source_date_epoch: "1615393459"
22+
23+
# Use Ringlogger as the logger backend and remove :console.
24+
# See https://hexdocs.pm/ring_logger/readme.html for more information on
25+
# configuring ring_logger.
26+
27+
config :logger, backends: [RingLogger]
28+
29+
if Mix.target() == :host or Mix.target() == :"" do
30+
import_config "host.exs"
31+
else
32+
import_config "target.exs"
33+
end

example/config/host.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Config
2+
3+
# Add configuration that is only needed when running on the host here.

example/config/target.exs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import Config
2+
3+
# Use shoehorn to start the main application. See the shoehorn
4+
# docs for separating out critical OTP applications such as those
5+
# involved with firmware updates.
6+
7+
config :shoehorn,
8+
init: [:nerves_runtime, :nerves_pack],
9+
app: Mix.Project.config()[:app]
10+
11+
# Nerves Runtime can enumerate hardware devices and send notifications via
12+
# SystemRegistry. This slows down startup and not many programs make use of
13+
# this feature.
14+
15+
config :nerves_runtime, :kernel, use_system_registry: false
16+
17+
# Erlinit can be configured without a rootfs_overlay. See
18+
# https://github.com/nerves-project/erlinit/ for more information on
19+
# configuring erlinit.
20+
21+
config :nerves,
22+
erlinit: [
23+
hostname_pattern: "nerves-%s"
24+
]
25+
26+
# Configure the device for SSH IEx prompt access and firmware updates
27+
#
28+
# * See https://hexdocs.pm/nerves_ssh/readme.html for general SSH configuration
29+
# * See https://hexdocs.pm/ssh_subsystem_fwup/readme.html for firmware updates
30+
31+
keys =
32+
[
33+
Path.join([System.user_home!(), ".ssh", "id_rsa.pub"]),
34+
Path.join([System.user_home!(), ".ssh", "id_ecdsa.pub"]),
35+
Path.join([System.user_home!(), ".ssh", "id_ed25519.pub"])
36+
]
37+
|> Enum.filter(&File.exists?/1)
38+
39+
if keys == [],
40+
do:
41+
Mix.raise("""
42+
No SSH public keys found in ~/.ssh. An ssh authorized key is needed to
43+
log into the Nerves device and update firmware on it using ssh.
44+
See your project's config.exs for this error message.
45+
""")
46+
47+
config :nerves_ssh,
48+
authorized_keys: Enum.map(keys, &File.read!/1)
49+
50+
# Configure the network using vintage_net
51+
# See https://github.com/nerves-networking/vintage_net for more information
52+
config :vintage_net,
53+
regulatory_domain: "US",
54+
config: [
55+
{"usb0", %{type: VintageNetDirect}},
56+
{"eth0",
57+
%{
58+
type: VintageNetEthernet,
59+
ipv4: %{method: :dhcp}
60+
}},
61+
{"wlan0", %{type: VintageNetWiFi}}
62+
]
63+
64+
config :mdns_lite,
65+
# The `host` key specifies what hostnames mdns_lite advertises. `:hostname`
66+
# advertises the device's hostname.local. For the official Nerves systems, this
67+
# is "nerves-<4 digit serial#>.local". mdns_lite also advertises
68+
# "nerves.local" for convenience. If more than one Nerves device is on the
69+
# network, delete "nerves" from the list.
70+
71+
host: [:hostname, "nerves"],
72+
ttl: 120,
73+
74+
# Advertise the following services over mDNS.
75+
services: [
76+
%{
77+
name: "SSH Remote Login Protocol",
78+
protocol: "ssh",
79+
transport: "tcp",
80+
port: 22
81+
},
82+
%{
83+
name: "Secure File Transfer Protocol over SSH",
84+
protocol: "sftp-ssh",
85+
transport: "tcp",
86+
port: 22
87+
},
88+
%{
89+
name: "Erlang Port Mapper Daemon",
90+
protocol: "epmd",
91+
transport: "tcp",
92+
port: 4369
93+
}
94+
]
95+
96+
# Import target specific config. This must remain at the bottom
97+
# of this file so it overrides the configuration defined above.
98+
# Uncomment to use target specific configurations
99+
100+
# import_config "#{Mix.target()}.exs"

example/lib/example.ex

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
defmodule Example do
2+
@moduledoc false
3+
4+
use GenServer
5+
6+
alias Circuits.GPIO.Chip
7+
8+
def start_link(args) do
9+
GenServer.start_link(__MODULE__, args)
10+
end
11+
12+
@impl GenServer
13+
def init(args) do
14+
chip = Keyword.get(args, :chip, "gpiochip0")
15+
led1 = Keyword.get(args, :led1, 17)
16+
led2 = Keyword.get(args, :led2, 22)
17+
button = Keyword.get(args, :button, 27)
18+
19+
{:ok, line} = Chip.request_lines(chip, [led1, led2], :output)
20+
21+
# Set initial values led1 will be on and led2 will be off
22+
:ok = Chip.set_values(line, [1, 0])
23+
24+
:ok = Chip.listen_event("gpiochip0", button)
25+
26+
{:ok, line}
27+
end
28+
29+
@impl GenServer
30+
def handle_info({:circuits_cdev, 27, _timestamp, 1}, line) do
31+
Chip.set_values(line, [0, 1])
32+
33+
{:noreply, line}
34+
end
35+
36+
def handle_info({:circuits_cdev, 27, _timestamp, 0}, line) do
37+
Chip.set_values(line, [1, 0])
38+
39+
{:noreply, line}
40+
end
41+
end

example/lib/example/application.ex

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
defmodule Example.Application do
2+
# See https://hexdocs.pm/elixir/Application.html
3+
# for more information on OTP Applications
4+
@moduledoc false
5+
6+
use Application
7+
8+
def start(_type, _args) do
9+
# See https://hexdocs.pm/elixir/Supervisor.html
10+
# for other strategies and supported options
11+
opts = [strategy: :one_for_one, name: Example.Supervisor]
12+
13+
children =
14+
[
15+
# Children for all targets
16+
# Starts a worker by calling: Example.Worker.start_link(arg)
17+
# {Example.Worker, arg},
18+
] ++ children(target())
19+
20+
Supervisor.start_link(children, opts)
21+
end
22+
23+
# List all child processes to be supervised
24+
def children(:host) do
25+
[
26+
# Children that only run on the host
27+
# Starts a worker by calling: Example.Worker.start_link(arg)
28+
# {Example.Worker, arg},
29+
]
30+
end
31+
32+
def children(_target) do
33+
example_args = Application.get_env(:example, :config, [])
34+
35+
[
36+
{Example, example_args}
37+
]
38+
end
39+
40+
def target() do
41+
Application.get_env(:example, :target)
42+
end
43+
end

example/mix.exs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
defmodule Example.MixProject do
2+
use Mix.Project
3+
4+
@app :example
5+
@version "0.1.0"
6+
@all_targets [:rpi, :rpi0, :rpi2, :rpi3, :rpi3a, :rpi4, :bbb, :osd32mp1, :x86_64]
7+
8+
def project do
9+
[
10+
app: @app,
11+
version: @version,
12+
elixir: "~> 1.9",
13+
archives: [nerves_bootstrap: "~> 1.10"],
14+
start_permanent: Mix.env() == :prod,
15+
build_embedded: true,
16+
deps: deps(),
17+
releases: [{@app, release()}],
18+
preferred_cli_target: [run: :host, test: :host]
19+
]
20+
end
21+
22+
# Run "mix help compile.app" to learn about applications.
23+
def application do
24+
[
25+
mod: {Example.Application, []},
26+
extra_applications: [:logger, :runtime_tools]
27+
]
28+
end
29+
30+
# Run "mix help deps" to learn about dependencies.
31+
defp deps do
32+
[
33+
# Dependencies for all targets
34+
{:nerves, "~> 1.7.0", runtime: false},
35+
{:shoehorn, "~> 0.7.0"},
36+
{:ring_logger, "~> 0.8.1"},
37+
{:toolshed, "~> 0.2.13"},
38+
39+
# Dependencies for all targets except :host
40+
{:nerves_runtime, "~> 0.11.3", targets: @all_targets},
41+
{:nerves_pack, "~> 0.4.0", targets: @all_targets},
42+
{:circuits_cdev, path: "../", targets: @all_targets},
43+
44+
# Dependencies for specific targets
45+
{:nerves_system_rpi, "~> 1.13.0", runtime: false, targets: :rpi},
46+
{:nerves_system_rpi0, "~> 1.13.0", runtime: false, targets: :rpi0},
47+
{:nerves_system_rpi2, "~> 1.13.0", runtime: false, targets: :rpi2},
48+
{:nerves_system_rpi3, "~> 1.13.0", runtime: false, targets: :rpi3},
49+
{:nerves_system_rpi3a, "~> 1.13.0", runtime: false, targets: :rpi3a},
50+
{:nerves_system_rpi4, "~> 1.13.0", runtime: false, targets: :rpi4},
51+
{:nerves_system_bbb, "~> 2.8.3", runtime: false, targets: :bbb},
52+
{:nerves_system_osd32mp1, "~> 0.4", runtime: false, targets: :osd32mp1},
53+
{:nerves_system_x86_64, "~> 1.13.0", runtime: false, targets: :x86_64}
54+
]
55+
end
56+
57+
def release do
58+
[
59+
overwrite: true,
60+
# Erlang distribution is not started automatically.
61+
# See https://hexdocs.pm/nerves_pack/readme.html#erlang-distribution
62+
cookie: "#{@app}_cookie",
63+
include_erts: &Nerves.Release.erts/0,
64+
steps: [&Nerves.Release.init/1, :assemble],
65+
strip_beams: Mix.env() == :prod or [keep: ["Docs"]]
66+
]
67+
end
68+
end

0 commit comments

Comments
 (0)