My smart home @ 433 MHz

In this post I want to share some details about my simple low budget smart home system based on 433.92 MHz receiver/transmitter controlled by Arduino. It can be easily extended with many cheap wireless devices, such as door bells, remote sockets, smoke alarms, leak detectors, etc. I’ll describe how to control remote socket, receive alarms from wireless smoke detector and draw a plot of room temperature obtained from regular wired sensor.

Hardware: Raspberry Pi, Arduino, 433MHz RF kit, 433MHz remote socket and alarm, DHT22 temperature sensor. The choice of remote devices is always a bit of a lottery, but it seems that anything that is compatible with cheap GSM alarm systems should do. There’s also a list of working devices you can check out.
Software: Raspbian, openHAB, Arduino IDE, RC-Switch, DHT library.

Contents:

Arduino

I downloaded rc-switch and Adafruit DHT libraries and installed them via Arduino IDE. Both have nice tutorials if you need help with wiring and testing.

First I needed to decode commands used to control remote socket and find out smoke alarm code. To do that I used ReceiveDemo sketch which comes with rc-switch library. I just launched it, opened Serial Monitor and started pushing buttons on socket remote control and test button on the alarm. I made sure that the codes for the sockets work by modifying and running SendDemo sketch. In my case the tricky part was to use 180 ms pulse length instead of 178 as reported by ReceiveDemo.

Then I came up with very basic protocol for serial interface. To operate sockets, I send “sXu” and “sXd” strings to Arduino where X is socket number and “u” means “on” and “d” means “off”. For example, “s2d” turns off socket #2. When rc-switch receives an alarm, Arduino sends out “raY” string where Y is detector number.

#include <RCSwitch.h>
#include <DHT.h>
#define S1_ON 12345  // Remote socket codes obtained with ReceiveDemo.
#define S1_OFF 12346
#define A1 54321  // Alarm code obtained with ReceiveDemo.
#define DHTPIN 12     // DHT22 pin.
#define DHTTYPE DHT22   // DHT22 (AM2302), AM2321
RCSwitch mySwitch = RCSwitch();
DHT dht(DHTPIN, DHTTYPE);
String input;  // Received serial data.
void setup() {
  Serial.begin(9600);
  mySwitch.enableTransmit(10);  // Transmitter pin.
  mySwitch.setPulseLength(180);  // Pulse length.
  mySwitch.setRepeatTransmit(3);  // Repeat transmission just in case.
  mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => pin #2.
}
void loop() {
  if (mySwitch.available()) {
    int value = mySwitch.getReceivedValue();
    if (value == 0) {
      Serial.print("Unknown encoding");
    } else {
      switch(value) {
        case S1_ON:  // Remote control button was pressed.
          Serial.println("rs1_on");
          break;
        case S1_OFF:
          Serial.println("rs1_off");
          break;
        case A1:  // Detector alarm went off.
          Serial.println("ra1");
          break;
        default:  // Unknown signal received.
          Serial.print("ruk_");
          Serial.println(value);
          break;
      }
    }
    mySwitch.resetAvailable();
  }
  input = Serial.readString();  // Read the serial port.
  if (input == "s1u") {  // Turn socket 1 on.
    mySwitch.send(S1_ON, 24);
    Serial.println("ss1_on");
  }
  if (input == "s1d") {  // Turn socket 1 off.
    mySwitch.send(S1_OFF, 24);
    Serial.println("ss1_off");
  }
  if (input == "t1r") {  // Read temperature and print it back.
    float t = dht.readTemperature();
    Serial.print("st1_");
    Serial.println(t);
  }
}

openHAB installation

There’s an openHAB repository for Raspbian, so installation is pretty easy.
First, make sure you have Java Runtime Environment installed:

sudo apt-get install openjdk-7-jre-headless

Then follow openHAB Wiki to add repo and install openHAB runtime. We’ll need some additional packages:

sudo apt-get install openhab-addon-binding-serial openhab-addon-persistence-rrd4j

Copy distribution config file:

sudo cp /etc/openhab/configurations/openhab_default.cfg /etc/openhab/configurations/openhab.cfg

Don’t forget to enable permissions for serial access as described in Wiki:

sudo usermod -a -G dialout openhab

openHAB configuration

This was the most complicated pаrt for me because openHAB is very complex and flexible. First one has to define items which are, for example, switches, temperature values and strings coming to and from serial interface. Then there are rules which tell openHAB what to do when something changes (commands are received, values change or scheduled time comes). To display something on the web page or mobile app one needs to setup a sitemap. Finally, to store values like temperatures and switches’ states, persistence is required.
Let’s define a remote socket, serial communication string, and temperature value:

default.items:

Group All
Group Sockets (All)
Group Temperatures (All)
Group Humidities (All)
Switch Socket1 "Socket #1"  (Sockets)
String Serial_string "Serial [%s]" { serial="/dev/ttyACM0" }
Number Temperature1 "Temperature [%.1f °C]"  (Temperatures)

Now add some rules to check temperature every minute, receive radio signals and send radio codes when switches are clicked in openHAB:

default.rules:

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
var boolean sentAlarm = false
rule "Request temperature"  // Request temperature every minute.
when
  Time cron "0 * * * * ?"
then
  Serial_string.sendCommand("t1r")
end
rule "Serial control"
when
  Item Serial_string received update
then
  var String upd = Serial_string.state.toString.trim
  // Socket was toggled externally.
  if(upd.equals("rs1_on"))  postUpdate("Socket1", "ON")
  if(upd.equals("rs1_off")) postUpdate("Socket1", "OFF")
  // Temperature received.
  if(upd.contains("st1_")) {
    var Number temp = Float::parseFloat(upd.substring(4, 8))
    Temperature1.postUpdate(temp)
  }
  // Alarm received.
  if(!sentAlarm && upd.contains("ra")) {
    executeCommandLine("echo@@'Alarm@@went@@off!'@@|@@festival@@--tts")
    sentAlarm = true
    waitTimer = createTimer(now.plusMinutes(5))[|
      sentAlarm = false
  }
end
rule "Socket 1 control"  // Button was toggled in web interface.
when
  Item Socket1 received command
then
  if(receivedCommand==ON)  Serial_string.sendCommand("s1u")
  if(receivedCommand==OFF) Serial_string.sendCommand("s1d")
end

Of course you can use any other external command (e. g. sendmail) or internal openHAB commands to react on events like alarms. Please see openHAB Actions Wiki page for details.

Now let’s make a nice interface!

default.sitemap:

sitemap default label="Home"
{
  Frame label="General" {
    Text item=Temperature1
    Switch item=Socket1
  }
  Frame label="Plots" {
    Text label="Temperature" icon="line" {
      // Create chart with 24 hours of data refreshing every 30 seconds.
      Chart item=Temperature1 period=D refresh=30000
    }
  }
}

Store socket state and temperature in rrd4j Persistence Service (the one we installed earlier):

rrd4j.persist:

Strategies {
  everyMinute : "0 * * * * ?"
}
Items {
  // Store switches states.
  Socket1 : strategy = everyChange, everyMinute, restoreOnStartup
  // Store temperature values.
  Temperature1 : strategy = everyMinute, restoreOnStartup
}

Configure openHAB to use this persistence by editing main config file (/etc/openhab/configurations/openhab.cfg):

persistence:default=rrd4j

If everything’s OK you should be able to access openHAB web interface at http://[raspberry_pi_address]:8080/openhab.app?sitemap=default. The main log files to check if things don’t work are /var/log/openhab/openhab.log and /var/log/openhab/events.log.

Final touches

When everything is working as expected it’s recommended to reduce logging verbosity and config polling as described in Performance Tuning. You can also install mobile app with native interface. To be able to access your openHAB instance from the outside world, you can either set up port forwarding on your router or set up my.openHAB account. The latter is much easier and gives you additional features, like mobile app or SMS notifications. Happy hacking!

Your comment: