Skip to content

Commit

Permalink
Port Adafruit DHT
Browse files Browse the repository at this point in the history
This cleans up the C to unify a single driver for all targets.

Also adjusts the port to be request based. Polling will be handled via
Elixir
  • Loading branch information
jjcarstens committed Jun 17, 2020
1 parent b294bfc commit e7260b4
Show file tree
Hide file tree
Showing 24 changed files with 1,652 additions and 112 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ erl_crash.dump
*.ez
*.beam
/config/*.secret.exs
/tmp
92 changes: 92 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Makefile for building the DHT* port executable
#
# Makefile targets:
#
# all/install build and install
# clean clean build products and intermediates
#
# Variables to override:
#
# MIX_APP_PATH path to the build directory
#
# CC C compiler. MUST be set if crosscompiling
# CFLAGS compiler flags for compiling all C files
# ERL_CFLAGS additional compiler flags for files using Erlang header files
# ERL_EI_INCLUDE_DIR include path to ei.h (Required for crosscompile)
# ERL_EI_LIBDIR path to libei.a (Required for crosscompile)
# LDFLAGS linker flags for linking all binaries
# ERL_LDFLAGS additional linker flags for projects referencing Erlang libraries

TOP := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))

PREFIX = $(MIX_COMPILE_PATH)/../priv
BUILD = $(MIX_COMPILE_PATH)/../obj

# Set Erlang-specific compile and linker flags
ERL_CFLAGS ?= -I$(ERL_EI_INCLUDE_DIR)
ERL_LDFLAGS ?= -L$(ERL_EI_LIBDIR) -lei_st

LDFLAGS +=
CFLAGS ?= -O2 -Wall -Wextra -Wno-unused-parameter -Werror
CFLAGS += -std=gnu99 -Impl

DHT_BIN = $(PREFIX)/dht

RPI := rpi rpi0
RPI2 := rpi2 rpi3 rpi3a rpi4
BBB := bbb

SRC = src/main.c src/erlcmd.c

ifneq ($(filter $(MIX_TARGET),$(RPI)),)
TARGET=1
SRC += src/pi_dht_read.c src/pi_mmio.c src/common_dht_read.c
else ifneq ($(filter $(MIX_TARGET),$(RPI2)),)
TARGET=2
SRC += src/pi_2_dht_read.c src/pi_2_mmio.c src/common_dht_read.c
else ifneq ($(filter $(MIX_TARGET),$(BBB)),)
TARGET=3
SRC += $(wildcard src/bbb*.c) src/common_dht_read.c
else ifeq ($(MIX_TARGET), host)
TARGET=0
CC = gcc
endif

CC ?= $(CROSSCOMPILER)-gcc

OBJ = $(SRC:src/%.c=$(BUILD)/%.o)

calling_from_make:
mix compile

all: $(BUILD) $(PREFIX) $(DHT_BIN)

$(OBJ): Makefile

$(BUILD)/%.o: src/%.c
$(CC) -D TARGET=$(TARGET) -c $(ERL_CFLAGS) $(CFLAGS) -o $@ $<

$(PREFIX) $(BUILD):
mkdir -p $@

$(DHT_BIN): $(OBJ)
$(CC) $^ $(ERL_LDFLAGS) $(LDFLAGS) -o $@

clean:
$(RM) $(DHT_BIN) $(OBJ)

format:
astyle \
--style=kr \
--indent=spaces=4 \
--align-pointer=name \
--align-reference=name \
--convert-tabs \
--attach-namespaces \
--max-code-length=100 \
--max-instatement-indent=120 \
--pad-header \
--pad-oper \
src/*.c

.PHONY: all clean calling_from_make
10 changes: 10 additions & 0 deletions lib/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule DHT.Application do
use Application

def start(_type, _args) do
opts = [strategy: :one_for_one, name: DHT.Supervisor]
children = [DHT.Port]

Supervisor.start_link(children, opts)
end
end
104 changes: 0 additions & 104 deletions lib/dht.ex
Original file line number Diff line number Diff line change
@@ -1,106 +1,2 @@
defmodule DHT do
use GenServer

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

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: []}}

: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 \\ <<>>)

def print([{time, _val} = val | rem], acc) do
IO.puts("#{inspect(val)}")
unless rem == [] do
[{next_time, _} | _rem] = rem
timing = (next_time - time) |> Kernel.div(1000) |> round()
bit = if (timing - 50) in 26..28, do: 0, else: 1
IO.puts("\t\t|-- #{timing} us")
IO.puts("\t\t|-- #{bit}")
print(rem, acc <> <<bit>>)
end
inspect(acc)
end

def print([], acc), do: inspect(acc)

def read(server \\ __MODULE__), do: GenServer.call(server, :read)

def restart(server \\ __MODULE__) do
GenServer.stop(server)
start_link()
end

def refresh(server \\ __MODULE__) do
GenServer.cast(server, :refresh)
end

def handle_call(:read, _from, state) do
# Sort them all by timestamp so they are in order
# data = Enum.sort_by(state.data, &elem(&1, 0))
data = Enum.reverse(state.data)

{: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)

# 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)

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

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

def handle_info({:circuits_gpio, _pin, time, val} = msg, %{data: data} = state) 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
37 changes: 37 additions & 0 deletions lib/port.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule DHT.Port do
use GenServer

def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end

def read(pin, sensor) do
GenServer.call(__MODULE__, {:read, pin, sensor})
end

@impl true
def init(_opts) do
exec =
Application.app_dir(:dht, "priv")
|> Path.join("dht")

port = Port.open({:spawn_executable, exec}, [{:packet, 2}, :binary, :use_stdio, :exit_status])

{:ok, %{port: port}}
end

@impl true
def handle_call({:read, pin, sensor}, from, state) do
message = {:read, from, {pin, sensor}}

send(state.port, {self(), {:command, :erlang.term_to_binary(message)}})
{:noreply, state}
end

@impl true
def handle_info({_, {:data, data}}, state) do
{condition, result, from} = :erlang.binary_to_term(data)
GenServer.reply(from, {condition, result})
{:noreply, state}
end
end
43 changes: 37 additions & 6 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,28 +1,59 @@
defmodule Dht.MixProject do
use Mix.Project

@source_url "https://github.com/jjcarstens/dht"
@version "0.1.0"

def project do
[
app: :dht,
version: "0.1.0",
version: @version,
elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
deps: deps()
build_embedded: Mix.env() == :prod,
compilers: [:elixir_make | Mix.compilers()],
make_targets: ["all"],
make_clean: ["clean"],
deps: deps(),
description: description(),
docs: docs(),
package: package()
]
end

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

# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:pigpiox, "~> 0.1"},
{:circuits_gpio, "~> 0.4"}
{:circuits_gpio, "~> 0.4"},
{:elixir_make, "~> 0.6"}
]
end

defp description() do
"Drive of DHT 11 and DHT 22 (temperature and humidity sensor)"
end

defp docs do
[
extras: ["README.md"],
main: "readme",
source_ref: "v#{@version}",
source_url: @source_url
]
end

defp package() do
[
name: "nerves_dht",
files: ["src", "lib", "mix.exs", "README.md", "LICENSE", "Makefile"],
licenses: ["GNU 3.0"],
links: %{"GitHub" => @source_url}
]
end
end
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,5 +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"},
"circuits_gpio": {:hex, :circuits_gpio, "0.4.2", "becda6b468271a2dea0c8a500b23b2b5d425501a7d7664c55f96e292af8aef30", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "d30787fd140bd09c6bd7fad8f3e7da6a1fa475eb3a6aff5f0c267c6f98cfcd47"},
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"},
"pigpiox": {:hex, :pigpiox, "0.1.2", "efbd55fb4b4507aaf44193cf6bf5fbdff288a351bdfed3f31bdc9377ee5623fd", [:mix], [], "hexpm"},
}
Loading

0 comments on commit e7260b4

Please sign in to comment.