Skip to content

Commit

Permalink
experiment with pigpio
Browse files Browse the repository at this point in the history
  • Loading branch information
jjcarstens committed Sep 22, 2019
1 parent 635b001 commit a66c5b9
Show file tree
Hide file tree
Showing 19 changed files with 460 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,17 @@ To attempt to trigger and read, run:
DHT.refresh # this triggers sensor to start sending data
DHT.read # shows the data captured from the last trigger attempt
```

This experiment is testing with `circuits_gpio` and `pigpiox`. You can switch between the two:
```elixir
# circuits_gpio by default. But you can also specify
DHT.start_link(17, :circuits_gpio)

# For pigpio
DHT.start_link(17, :pigpiox)
```

You can use the included `weather_man` nerves app to quickly build firmware
```elixir
$ cd weather_man && mix firmware
```
46 changes: 40 additions & 6 deletions dht.ex → lib/dht.ex
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
defmodule DHT do
use GenServer

def start_link(pin_num \\ 17) do
GenServer.start_link(__MODULE__, pin_num, name: __MODULE__)
def start_link(pin_num \\ 17, source \\ :circuits_gpio) do
GenServer.start_link(__MODULE__, {pin_num, source}, name: __MODULE__)
end

def init(pin_num) do
{:ok, pin} = Circuits.GPIO.open(pin_num, :input)
Circuits.GPIO.set_interrupts(pin, :both)
def init({pin_num, source}) do
case source do
:circuits_gpio ->
{:ok, pin} = Circuits.GPIO.open(pin_num, :input)
Circuits.GPIO.set_interrupts(pin, :both)
{:ok, %{pin: pin, data: []}}

{:ok, %{pin: pin, data: []}}
:pigpiox ->
Pigpiox.GPIO.set_mode(pin_num, :input)
{:ok, watch_pid} = Pigpiox.GPIO.watch(pin_num)
{:ok, %{pin: pin_num, watch_pid: watch_pid, data: []}}

unknown ->
{:stop, "Don't know how to use source #{inspect(unknown)}"}
end
end

def print(list, acc \\ <<>>)
Expand Down Expand Up @@ -48,6 +58,25 @@ defmodule DHT do
{:reply, %{state | data: data}, state}
end

def handle_cast(:refresh, %{pin: pin} = state) when is_number(pin) do
Pigpiox.GPIO.set_mode(pin, :output)

# Datasheet says minimum of 1ms, but can allow up to 10ms
# for the trigger. This is here to alter more easily
trigger_time = Application.get_env(:dht, :trigger_time, 1)

Pigpiox.GPIO.write(pin, 0)
# 1 ms trigger
:timer.sleep(trigger_time)
Pigpiox.GPIO.write(pin, 1)
Pigpiox.GPIO.set_mode(pin, :input)

Pigpiox.GPIO.watch(pin)

# Circuits.GPIO.set_interrupts(pin, :both)
{:noreply, %{state | data: []}}
end

def handle_cast(:refresh, %{pin: pin} = state) do
Circuits.GPIO.set_direction(pin, :output)

Expand All @@ -69,4 +98,9 @@ defmodule DHT do
IO.inspect(msg)
{:noreply, %{state | data: [{time, val} | data]}}
end

def handle_info({:gpio_leveL_change, _gpio, val} = msg, %{data: data} = state) do
IO.inspect(msg)
{:noreply, %{state | data: [{System.monotonic_time(:nanosecond), val} | data]}}
end
end
28 changes: 28 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule Dht.MixProject do
use Mix.Project

def project do
[
app: :dht,
version: "0.1.0",
elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:pigpiox, "~> 0.1"},
{:circuits_gpio, "~> 0.4"}
]
end
end
5 changes: 5 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
%{
"circuits_gpio": {:hex, :circuits_gpio, "0.4.2", "becda6b468271a2dea0c8a500b23b2b5d425501a7d7664c55f96e292af8aef30", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"},
"pigpiox": {:hex, :pigpiox, "0.1.2", "efbd55fb4b4507aaf44193cf6bf5fbdff288a351bdfed3f31bdc9377ee5623fd", [:mix], [], "hexpm"},
}
1 change: 1 addition & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()
4 changes: 4 additions & 0 deletions weather_man/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
17 changes: 17 additions & 0 deletions weather_man/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
32 changes: 32 additions & 0 deletions weather_man/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# WeatherMan

**TODO: Add description**

## Targets

Nerves applications produce images for hardware targets based on the
`MIX_TARGET` environment variable. If `MIX_TARGET` is unset, `mix` builds an
image that runs on the host (e.g., your laptop). This is useful for executing
logic tests, running utilities, and debugging. Other targets are represented by
a short name like `rpi3` that maps to a Nerves system image for that platform.
All of this logic is in the generated `mix.exs` and may be customized. For more
information about targets see:

https://hexdocs.pm/nerves/targets.html#content

## Getting Started

To start your Nerves app:
* `export MIX_TARGET=my_target` or prefix every command with
`MIX_TARGET=my_target`. For example, `MIX_TARGET=rpi3`
* Install dependencies with `mix deps.get`
* Create firmware with `mix firmware`
* Burn to an SD card with `mix firmware.burn`

## Learn more

* Official docs: https://hexdocs.pm/nerves/getting-started.html
* Official website: https://nerves-project.org/
* Forum: https://elixirforum.com/c/nerves-forum
* Discussion Slack elixir-lang #nerves ([Invite](https://elixir-slackin.herokuapp.com/))
* Source: https://github.com/nerves-project/nerves
31 changes: 31 additions & 0 deletions weather_man/config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.
use Mix.Config

config :weather_man, target: Mix.target()

# Customize non-Elixir parts of the firmware. See
# https://hexdocs.pm/nerves/advanced-configuration.html for details.

config :nerves, :firmware, rootfs_overlay: "rootfs_overlay"

# Use shoehorn to start the main application. See the shoehorn
# docs for separating out critical OTP applications such as those
# involved with firmware updates.

config :shoehorn,
init: [:nerves_runtime, :nerves_init_gadget],
app: Mix.Project.config()[:app]

# Use Ringlogger as the logger backend and remove :console.
# See https://hexdocs.pm/ring_logger/readme.html for more information on
# configuring ring_logger.

config :logger, backends: [RingLogger]

if Mix.target() != :host do
import_config "target.exs"
end
44 changes: 44 additions & 0 deletions weather_man/config/target.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use Mix.Config

# Authorize the device to receive firmware using your public key.
# See https://hexdocs.pm/nerves_firmware_ssh/readme.html for more information
# on configuring nerves_firmware_ssh.

keys =
[
Path.join([System.user_home!(), ".ssh", "id_rsa.pub"]),
Path.join([System.user_home!(), ".ssh", "id_ecdsa.pub"]),
Path.join([System.user_home!(), ".ssh", "id_ed25519.pub"])
]
|> Enum.filter(&File.exists?/1)

if keys == [],
do:
Mix.raise("""
No SSH public keys found in ~/.ssh. An ssh authorized key is needed to
log into the Nerves device and update firmware on it using ssh.
See your project's config.exs for this error message.
""")

config :nerves_firmware_ssh,
authorized_keys: Enum.map(keys, &File.read!/1)

# Configure nerves_init_gadget.
# See https://hexdocs.pm/nerves_init_gadget/readme.html for more information.

# Setting the node_name will enable Erlang Distribution.
# Only enable this for prod if you understand the risks.
node_name = if Mix.env() != :prod, do: "weather_man"

config :nerves_init_gadget,
ifname: "usb0",
address_method: :dhcpd,
mdns_domain: "nerves.local",
node_name: node_name,
node_host: :mdns_domain

# Import target specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
# Uncomment to use target specific configurations

# import_config "#{Mix.target()}.exs"
18 changes: 18 additions & 0 deletions weather_man/lib/weather_man.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule WeatherMan do
@moduledoc """
Documentation for WeatherMan.
"""

@doc """
Hello world.
## Examples
iex> WeatherMan.hello
:world
"""
def hello do
:world
end
end
42 changes: 42 additions & 0 deletions weather_man/lib/weather_man/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule WeatherMan.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false

use Application

def start(_type, _args) do
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: WeatherMan.Supervisor]
children =
[
# Children for all targets
# Starts a worker by calling: WeatherMan.Worker.start_link(arg)
# {WeatherMan.Worker, arg},
] ++ children(target())

Supervisor.start_link(children, opts)
end

# List all child processes to be supervised
def children(:host) do
[
# Children that only run on the host
# Starts a worker by calling: WeatherMan.Worker.start_link(arg)
# {WeatherMan.Worker, arg},
]
end

def children(_target) do
[
# Children for all targets except host
# Starts a worker by calling: WeatherMan.Worker.start_link(arg)
# {WeatherMan.Worker, arg},
]
end

def target() do
Application.get_env(:weather_man, :target)
end
end
73 changes: 73 additions & 0 deletions weather_man/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule WeatherMan.MixProject do
use Mix.Project

@app :weather_man
@version "0.1.0"
@all_targets [:rpi, :rpi0, :rpi2, :rpi3, :rpi3a, :rpi4, :bbb, :x86_64]

def project do
[
app: @app,
version: @version,
elixir: "~> 1.9",
archives: [nerves_bootstrap: "~> 1.6"],
start_permanent: Mix.env() == :prod,
build_embedded: true,
aliases: [loadconfig: [&bootstrap/1]],
deps: deps(),
releases: [{@app, release()}],
preferred_cli_target: [run: :host, test: :host]
]
end

# Starting nerves_bootstrap adds the required aliases to Mix.Project.config()
# Aliases are only added if MIX_TARGET is set.
def bootstrap(args) do
Application.start(:nerves_bootstrap)
Mix.Task.run("loadconfig", args)
end

# Run "mix help compile.app" to learn about applications.
def application do
[
mod: {WeatherMan.Application, []},
extra_applications: [:logger, :runtime_tools]
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
# Dependencies for all targets
{:nerves, "~> 1.5.0", runtime: false},
{:shoehorn, "~> 0.6"},
{:ring_logger, "~> 0.6"},
{:toolshed, "~> 0.2"},
{:dht, path: "../"},

# Dependencies for all targets except :host
{:nerves_runtime, "~> 0.6", targets: @all_targets},
{:nerves_init_gadget, "~> 0.4", targets: @all_targets},

# Dependencies for specific targets
{:nerves_system_rpi, "~> 1.8", runtime: false, targets: :rpi},
{:nerves_system_rpi0, "~> 1.8", runtime: false, targets: :rpi0},
{:nerves_system_rpi2, "~> 1.8", runtime: false, targets: :rpi2},
{:nerves_system_rpi3, "~> 1.8", runtime: false, targets: :rpi3},
{:nerves_system_rpi3a, "~> 1.8", runtime: false, targets: :rpi3a},
{:nerves_system_rpi4, "~> 1.8", runtime: false, targets: :rpi4},
{:nerves_system_bbb, "~> 2.3", runtime: false, targets: :bbb},
{:nerves_system_x86_64, "~> 1.8", runtime: false, targets: :x86_64},
]
end

def release do
[
overwrite: true,
cookie: "#{@app}_cookie",
include_erts: &Nerves.Release.erts/0,
steps: [&Nerves.Release.init/1, :assemble],
strip_beams: Mix.env() == :prod
]
end
end
Loading

0 comments on commit a66c5b9

Please sign in to comment.