Elixir library for Sequent Microsystems SM4rel4in HAT - a 4-relay/4-input stackable HAT for Raspberry Pi.
- 4 x Relay Outputs: SPDT relays, 8A/250VAC, 5A/30VDC
- 4 x Universal Inputs: Digital, AC detection, pulse counting, PWM measurement
- 2 x Quadrature Encoders: Using input pairs 1-2 and 3-4
- Current Measurement: For relay outputs
- 4 x Configurable LEDs: Auto (follow input) or manual mode
- Stackable: Up to 8 boards with I2C addressing
- Interrupt Support: GPIO interrupt-driven updates via Circuits.GPIO
Add sm_4rel4in to your list of dependencies in mix.exs:
def deps do
[
{:sm_4rel4in, "~> 1.0.0"},
{:circuits_i2c, "~> 2.0"},
{:circuits_gpio, "~> 2.0"}
]
end# Start the server (stack level 0, interrupt on GPIO 4)
{:ok, pid} = SM4rel4in.start_link(stack: 0, interrupt_pin: 4)
# Control relays
SM4rel4in.set_relay(1, true) # Turn on relay 1
SM4rel4in.set_all_relays(15) # Turn on all relays (binary 1111)
# Read inputs
{:ok, state} = SM4rel4in.get_digital_input(1)
{:ok, bitmap} = SM4rel4in.get_all_digital_inputs()
# Enable pulse counting
SM4rel4in.set_counting_enabled(1, true)
{:ok, count} = SM4rel4in.get_pulse_count(1)
# Subscribe to changes
SM4rel4in.subscribe()
# Receive: {:sm4rel4in_change, stack_level, changes}For production applications, add to your supervision tree:
children = [
{SM4rel4in, [stack: 0, interrupt_pin: 4, name: :sm4rel4in]}
]
Supervisor.start_link(children, strategy: :one_for_one)Stack multiple HATs with different stack levels:
children = [
{SM4rel4in, [stack: 0, interrupt_pin: 4, name: :hat_0]},
{SM4rel4in, [stack: 1, interrupt_pin: 17, name: :hat_1]},
{SM4rel4in, [stack: 2, interrupt_pin: 27, name: :hat_2]}
]# Single relay control
SM4rel4in.set_relay(server, relay, state)
{:ok, state} = SM4rel4in.get_relay(server, relay)
# All relays (bitmap: bit 0 = relay 1, bit 1 = relay 2, etc.)
SM4rel4in.set_all_relays(server, 0b1010) # Relays 2&4 on
{:ok, bitmap} = SM4rel4in.get_all_relays(server)# Single input
{:ok, state} = SM4rel4in.get_digital_input(server, channel)
{:ok, ac_detected} = SM4rel4in.get_ac_input(server, channel)
# All inputs
{:ok, bitmap} = SM4rel4in.get_all_digital_inputs(server)
{:ok, ac_bitmap} = SM4rel4in.get_all_ac_inputs(server){:ok, frequency_hz} = SM4rel4in.get_frequency(server, channel)
{:ok, duty_cycle_percent} = SM4rel4in.get_pwm_fill(server, channel)
{:ok, pulses_per_second} = SM4rel4in.get_pulse_rate(server, channel)# Enable/disable counting
SM4rel4in.set_counting_enabled(server, channel, true)
{:ok, enabled} = SM4rel4in.get_counting_enabled(server, channel)
# Read and reset counters
{:ok, count} = SM4rel4in.get_pulse_count(server, channel)
SM4rel4in.reset_pulse_count(server, channel)# Enable encoder (channel 1 = inputs 1&2, channel 2 = inputs 3&4)
SM4rel4in.set_encoder_enabled(server, encoder_channel, true)
# Read encoder (signed 32-bit, positive/negative for direction)
{:ok, count} = SM4rel4in.get_encoder_count(server, encoder_channel)
SM4rel4in.reset_encoder_count(server, encoder_channel)# Read relay output currents
{:ok, current_a} = SM4rel4in.get_current(server, relay_channel)
{:ok, rms_current_a} = SM4rel4in.get_rms_current(server, relay_channel)# Set LED mode
SM4rel4in.set_led_mode(server, led, :auto) # Follow input state
SM4rel4in.set_led_mode(server, led, :manual) # Software control
# Control LEDs (manual mode only)
SM4rel4in.set_led(server, led, true)
SM4rel4in.set_all_leds(server, 0b1010) # LEDs 2&4 on# Subscribe to all changes
SM4rel4in.subscribe(server)
# In your process, receive messages:
receive do
{:sm4rel4in_change, stack_level, changes} ->
# changes = [{:digital_inputs, old_map, new_map}, ...]
IO.inspect(changes)
endEnable I2C on your Raspberry Pi:
sudo raspi-config # Interface Options -> I2C -> EnableSet the stack level using jumpers on the HAT:
- Stack 0: No jumpers
- Stack 1: A0 jumper
- Stack 2: A1 jumper
- Stack 3: A0 + A1 jumpers
- etc.
Connect the interrupt pin from the HAT to a GPIO pin on your Raspberry Pi for real-time updates.
The library consists of several modules:
- SM4rel4in: Main API module
- SM4rel4in.Server: GenServer managing HAT state and interrupts
- SM4rel4in.Relays: Relay control functions
- SM4rel4in.Inputs: Input reading functions
- SM4rel4in.Counters: Pulse counting functions
- SM4rel4in.Encoders: Quadrature encoder functions
- SM4rel4in.LEDs: LED control functions
- SM4rel4in.I2C: Low-level I2C communication
- SM4rel4in.Registers: Register definitions and validation
defmodule MyApp.HAT do
use GenServer
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_opts) do
# Start HAT server
{:ok, _pid} = SM4rel4in.start_link(stack: 0, interrupt_pin: 4)
# Subscribe to changes
SM4rel4in.subscribe()
# Enable pulse counting on input 1
SM4rel4in.set_counting_enabled(1, true)
{:ok, %{}}
end
def handle_info({:sm4rel4in_change, _stack, changes}, state) do
# Handle input changes
Enum.each(changes, fn
{:digital_inputs, _old, new} ->
# React to digital input changes
if Map.get(new, 1) == 1 do
SM4rel4in.set_relay(1, true) # Turn on relay 1
else
SM4rel4in.set_relay(1, false)
end
{:pulse_counts, _old, new} ->
# Log pulse count changes
Logger.info("Pulse count updated: #{inspect(new)}")
_ ->
:ok
end)
{:noreply, state}
end
endMIT