HcSr501 Occupation

Nerves, or similar, Elixir library for interfacing with a HC-SR501 passive infra-red motion sensor.

It broadcasts to subscribing processes when movement is detected, no longer detected. Additionally it will broadcast when its operational area has not seen movement in long enough period for it to decide that the area has become occupied. Similarly it will broadcast when an area deemed unoccupied becomes occupied.

installation

Installation

def deps do
  [
    {:hc_sr_501_occupation, "~> 0.1.0"}
  ]
end

usage

Usage

Define the sensor in your project.

An example is below: the HC-SR501 out pin is attached to GPIO pin 17; we deem the area monitored to be unoccupied after 3 minutes of no movement detection.

defmodule Movement.Sensor do
  use HcSr501Occupation.MovementSensor

  @impl HcSr501Occupation.MovementSensor
  def pin, do: 17

  @impl HcSr501Occupation.MovementSensor
  def occupation_timeout, do: :timer.seconds(180)
end

In your application (or other supervisor) include your module as a child:

defmodule Movement.Application do
  use Application

  @impl true
  def start(_type, _args) do
    children = [
      Movement.Sensor
    ]

    opts = [strategy: :one_for_one, name: Movement.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Note that the module's pin/0 and occoupation_timeout/0 are read at start; modifying the values after that point will have no impact without going in and killing processes.

A processes can subscribe to receive a message when a movement event has occurred, with the subscribe/0 function that has been added to your module, eg

MovementSensor.subscribe()

On subscription your process will receive an occupation message with its current state. If movement has been detected since startup, then the last movement detection will also be sent.

Subsequent occupation and movement detection messages will also be sent to subscribed processes. Each message is a tuple, the first element of which is the name of your module - in our example Movement.Sensor

The following messages can be received:

movement-detected

Movement detected

The HC-SR501 has detected movement and will send a message like

{Movement.Sensor, :movement_detected, ~U[2023-02-01 12:34:40.883973Z]}

The last element is the DateTime at which movement was detected.

movement-stopped

Movement stopped

When the HC-SR501 stops detecting movement the following type of message will be sent to subscribers.

{Movement.Sensor, :movement_stopped, ~U[2023-02-01 12:34:40.883973Z]}

The last element is the DateTime at which the movement stopped signal was received.

occupied

Occupied

When we determine that a previously unoccupied are has become occupied, by detecting any movement, then the following form of message will be sent to subscribers

{Movement.Sensor, :occupied, ~U[2023-02-01 12:34:40.883973Z]}

By its nature this will always be preceeded by a :movement_detected message, with the same timestamp as in this message.

unoccupied

Unoccupied

An area is deemed unoccupied either at startup (of the process monitoring for occupation) or after no movement has been detected for the configured time when the area is deemed to have previously been occupied.

When we determine that an area is unoccupied then subscribers will receive messages of the form

{Movement.Sensor, :unoccupied, ~U[2023-02-01 12:34:40.883973Z]}

The DateTime in a message received when starting up will be the startup time. The DateTime in a message caused by an occupied area being deemed to have become unoccupied, will be the time at which the last :unoccupied message was received.

setting-the-occupation-status

Setting the occupation status

Should your project persist the occupation status, you might want to set it on startup with HcSr501Occupation.MovementSensor.set_occupied/2.