Control remote controlled outlets via Raspberry PI


Keywords
diy, golang, gpio, home-automation, outlet, raspberry-pi, react
License
MIT
Install
go get github.com/martinohmann/rfoutlet

Documentation

rfoutlet

Build Status codecov Go Report Card PkgGoDev

Outlet control via web interface for Raspberry PI 2/3/4. The transmitter and receiver logic has been ported from the great rc-switch C++ project to golang.

Raspberry PI Setup

Contents

Screenshots

Some screenshots of the web app that controls the outlets.

Main Outlet View Schedule View Settings View
Screenshot Screenshot Raspberry PI Setup

Stability note

The master branch may be broken at any time. Please check out the latest tag or use the latest release.

Breaking changes

v0.5.0 to v1.0.0

Due to a complete rewrite of the internal logic, many API inconsistencies have been fixed. This introduces non-backwards-compatible changes.

  • The config file format changed completely, please have a look at the example and adjust your config accordingly.

  • The state file format changed, so that old file cannot be loaded with the new version. It is advised to start with a fresh state file. If you have a lot of state, make a backup before upgrading and manually convert the state file json to the new format. In the following example foo and bar are the IDs of the outlets in the state file.

    Old format:

    {"switch_states":{"bar":1,"foo":1},"schedules":{"foo":[{"enabled": true,"weekdays":[1],"from":{"hour":0,"minute":59},"to":{"hour":2,"minute":1}}]}}

    New format:

    {"bar":{"state":1},"foo":{"state":1,"schedule":[{"enabled": true,"weekdays":[1],"from":{"hour":0,"minute":59},"to":{"hour":2,"minute":1}}]}}
  • rfoutlet serve:

    • --gpio-pin flag was renamed to --transmit-pin as it now also supports --receive-pin for state drift detection.
  • rfoutlet sniff:

    • --gpio-pin flag was renamed to --pin.
  • rfoutlet transmit:

    • --gpio-pin flag was renamed to --pin.
    • The command now accepts a list of codes that are transmitted sequentially.
  • The websocket message format changed slightly, so the web frontend needs to be rebuilt (see installation section).

  • In the package pkg/gpio the usage of gpio was replaced with gpiod to get rid of the dependency on the deprecated GPIO interface which may be removed from the linux kernel this year. This was also a good opportunity to improve the public API of pkg/gpio.

  • Instead of /dev/gpiomem, rfoutlet now depends on /dev/gpiochip0.

  • Since v1.0.0 does not depend on the sysfs anymore, the GPIO pins have to be unexported there to avoid device busy errors. This can be done like this:

    # Replace 17 and 27 with the GPIO pin you are using for transmitting and
    # receving rf codes if you are not using the defaults.
    echo "17" > /sys/class/gpio/unexport
    echo "27" > /sys/class/gpio/unexport

Prerequisites

See the Raspberry PI Setup section for setup of hardware and required software.

Hardware

  • Raspberry PI 2, 3 or 4
  • Remote controlled outlets (see Outlets section for suggestions)
  • Receiver/Transmitter (e.g. this)
  • SD Card
  • Power supply
  • Wiring
  • Breadboard (optional)

Software

  • I use Arch Linux on the Raspberry PI, but Raspbian should also work
  • nodejs 15.0+
  • golang 1.13+
  • make

Older software versions may also work, but I did not test that.

Installation

Using go get

Obtain the source, build and install it as follows:

go get -u github.com/martinohmann/rfoutlet
cd $GOPATH/src/github.com/martinohmann/rfoutlet
make all
make install

You will find a new binaries below $GOPATH/bin: rfoutlet.

If you only want to use the gpio transmitter and receiver code below pkg/gpio/ for your own project, just go get the project and check out the code in cmd/sniff.go and cmd/transmit.go for example usage.

go get -u github.com/martinohmann/rfoutlet/pkg/gpio

Using docker

Pre-built images of tagged releases and master are available on Docker Hub. mohmann/rfoutlet:latest will always point to the latest tagged release.

If you prefer building locally, run:

docker build -t mohmann/rfoutlet .

Running via docker-compose

Simply run:

docker-compose up -d

This pulls mohmann/rfoutlet:latest and starts the rfoutlet container listening on port 3333.

Running via docker

Start the container and browse to <raspberry-ip-address>:3333:

docker run \
    --rm \
    --privileged \
    -p 3333:3333 \
    -v /etc/localtime:/etc/localtime:ro \
    -v $(pwd)/configs/config.yml:/etc/rfoutlet/config.yml:ro \
    mohmann/rfoutlet:latest

The container has to run in privileged mode in order to be able to access /dev/gpiochip0.

Commands

Note: all commands requires sudo in order to access /dev/gpiochip0.

serve command

This command starts a server which listens on 0.0.0.0:3333 by default.

By default it looks for its configuration file at /etc/rfoutlet/config.yml. Check configs/config.yml for an example config file with all available config values. Use the sniff command for reading the on/off codes, protocol and pulse lengths for your outlets to put into the configuration file.

Start the server:

sudo rfoutlet serve --listen-address 0.0.0.0:3333 \
  --config /etc/rfoutlet/config.yml --transmit-pin 17

By default rfoutlet uses gpio pin 17 (physical 11 / wiringPi 0) for transmission of the rf codes. A different pin can be use by providing the --transmit-pin flag. Check out the Raspberry Pi pinouts for reference.

If you want the outlet switch states to be persisted, pass the --state-file flag, e.g:

sudo rfoutlet serve --state-file /var/lib/rfoutlet/state.json

sniff command

This command listens on a gpio pin and tries to sniff codes sent out by 433 Mhz remote controls. It will print the received code, protocol, pulse length and bit length to stdout when you press the on/off buttons on your remote.

sudo rfoutlet sniff --pin 27

transmit command

This command sends out remote control codes on the provided gpio pin. It can be used for testing or you can wrap it for use in another application.

Example for sending out the code 123456:

sudo rfoutlet transmit --pin 17 --protocol 1 --pulse-length 189 123456

Raspberry PI Setup

Install required software

On Arch Linux the following commands should be sufficient to install the required software:

sudo pacman -Sy go nodejs npm make

On Raspbian the following should do (untested):

wget https://storage.googleapis.com/golang/go1.15.5.linux-armv6l.tar.gz
sudo tar -C /usr/local -xvf go1.15.5.linux-armv6l.tar.gz
rm go1.15.5.linux-armv6l.tar.gz
curl -sL https://deb.nodesource.com/setup_15.x | sudo -E bash -
sudo apt-get install -y build-essential nodejs

Set up $GOPATH afterwards:

cat >> ~/.bashrc << 'EOF'
export GOPATH=$HOME/go
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
EOF
source ~/.bashrc

Wiring transmitter and receiver

The wiring of transmitter and receiver is straight forward and can be best described using images:

Transmitter Receiver
Transmitter Receiver

To increase the range of the transmitter I initially used a 25cm wire as antenna. I just twisted it with a pair of pliers to hold it in place, soldering is optional. This covers my whole appartment (105sqm). YMMV. Switched to transmitters and receivers with antennas already soldered on later.

Outlets

I achieved good results with the following remote controlled outlets:

Please let me know about others that work well too, so I can extend the list here.

Running rfoutlet as systemd service

See init/systemd/rfoutlet.service for an example systemd service file.

Development / Testing

rfoutlet is meant to run on a Raspberry PI 2/3/4 to work properly. However, for development purposes you can also run it on your local machine. If your development box does not have /dev/gpiochip0, you can load the gpio-mockup kernel module to create a mockup:

sudo make load-gpio-mockup

To unload the gpio-mockup kernel module again, run:

sudo make unload-gpio-mockup

A more convenient way to automatically load the gpio-mockup kernel module and unload it again is to simply pass the --gpio-mockup flag to rfoutlet:

sudo rfoutlet --gpio-mockup [...]

Run make without arguments to see available commands for building and testing.

Todo

  • implement code transmitter (see cmd/transmit.go)
  • implement code receiver (see cmd/sniff.go)
  • make transmitter/receiver code available as library below pkg/gpio/
  • persist outlet state across server restarts
  • use receiver to detect outlet state changes (see --detect-state-drift flag of the serve command)
  • time switch: switch outlets on/off using user defined rules (e.g. fixed time or relative)
  • use web sockets for communication to be able to push outlet state changes to multiple clients

License

The source code of this is released under the MIT License. See the bundled LICENSE file for details.

Creative Commons License

The images belonging to this project are licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Resources