Sunday, September 20, 2015

Home automation with Raspberry PI

This a tutorial about how to build home automation system for switching lights.

Raspberry PI perfect home automation. It's small, always connected to the internet and has a full-blown Linux with a direct access to GPIO ports.
At first I looked at X10, but dismissed it because it's not that popular in Europe and quite expensive. 
Then I found these bad boys:
nRF24L01+
nRF24L01+ is a tiny transceiver with SPI interface which operates on 2.4GHz frequency. It's an ISM band which means that you can use it without any certifications and permissions.

SPI interface means that it can be easily connected to Arduino as a receiver and Raspberry PI as the transmitter. I wont describe here how to connect nRFs to SPI interfaces because it's all over the internet already.

I used RF24 library which is available for Arduino and a Raspberry PI. I would recommend to try to compile one of the examples and establish communication between an Arduino and a Raspberry PI before going further.

Hardware

Receiver hardware consists of 3 parts - Atmega328P, 5v power adapter and a relay.

Atmega328p

I didn't want to use a full blown Arduino, so I just programmed an Atmega chip, made sure that it worked and then run in a standalone mode.

5v power supply

Power requirements of the whole thing are extremely small, so almost any 5v power supply would suffice. I bought this one from DealExtreme:
5v power supply
If you have phone chargers lying around they might also be a good option :)

Relay

I bought one of the 5v relays available from DealExtreme or any other similar site:
5v relay
It operates under 5v, has a built-in transistor so it can be directly connected to an AVR chip and has a tiny led indicator.

The receiver


As you can see I didn't use any board for the build. The main reason for that is flexibility - this way I can easily bend it the way I want, so it can be placed neatly inside of most lamps:


Raspberry Pi hardware

On the Raspberry PI side all is pretty standard - it's connected to the Ethernet network and has nRF chip connected to it via SPI protocol.

Software

Receiver code

#include <SPI.h> 
#include "nRF24L01.h"
#include "RF24.h"

#define RF_SETUP 0x17

// Set up nRF24L01 radio on SPI pin for CE, CSN
RF24 radio(8,9);

int relay = 4;

const uint64_t id = 0xF0F0F0F0E4LL;

void setup(void) {
  
  pinMode(relay, OUTPUT);
  digitalWrite(relay, HIGH);

  radio.begin();
   
  // Enable this seems to work better
  radio.enableDynamicPayloads();
  // RF24_1MBPS RF24_250KBPS
  radio.setDataRate(RF24_1MBPS);
  radio.setPALevel(RF24_PA_MAX);
  radio.setChannel(76);
  radio.setRetries(15,15);
  
  radio.openReadingPipe(1, id);

  radio.startListening();
  
  delay(1000);  
}

void loop(void) {

  while(radio.available()) {
    uint8_t len = radio.getDynamicPayloadSize();
    char receivePayload[32];
    radio.read(receivePayload, len);
    receivePayload[len] = 0;
    
    if (!strcmp(receivePayload, "on")) {
      digitalWrite(relay, HIGH);      
    } else {
      digitalWrite(relay, LOW);
    }
  }
}

The code is very straightforward - we initialize the transceiver and then react on incoming messages which could be either "on" or "off". If it's  "on" we set the relay pin to HIGH status, so it turns the light on and we do the opposite when we receive "off" message.
Light is switched on every time the chip gets power. This way it still operates like a normal switch if needed.
Each chip has it's own id, which is hard-coded and changed before each reflash.

Transmitter code

Transmitter code is run on Raspberry  PI and is available here https://github.com/Leonti/lights/blob/master/rf/onoff.cpp:

/* 
 *
 *  
 *  CE is connected to GPIO25
 *  CSN is connected to GPIO8 
 *
 *  Refer to RPi docs for GPIO numbers
 *
 */

#include <cstdlib>
#include <iostream>
#include "RF24.h"

using namespace std;

// CE and CSN pins On header using GPIO numbering (not pin numbers)
RF24 radio("/dev/spidev0.0",8000000,25);  // Setup for GPIO 25 CSN

void send(uint64_t id, int on)
{
 //
 // Refer to RF24.h or nRF24L01 DS for settings
 radio.begin();
 radio.enableDynamicPayloads();
 radio.setAutoAck(1);
 radio.setRetries(15,15);
 radio.setDataRate(RF24_1MBPS);
 radio.setPALevel(RF24_PA_MAX);
 radio.setChannel(76);
 radio.setCRCLength(RF24_CRC_16);

 radio.stopListening();
 radio.openWritingPipe(id);

 usleep(1000);

 char sendPayload[32];

 if (on) {
  strcpy(sendPayload, "on");
 } else {
  strcpy(sendPayload, "off");
 }

 if(radio.write(sendPayload, 32)) {
  printf("OK\n");
 } else {
  printf("ERROR\n");
 }
}


int main(int argc, char** argv) 
{

 if (argc > 2) {

        char *end;
        uint64_t id = strtoull(argv[1], &end, 16);

        int on = 0;
        if (!strcmp("on", argv[2])) {
            on = 1;
        }
  send(id, on);
 } 
 return 0;
}

Hello

SPI has to be enabled on the Raspberry PI for this code to work.
It accepts command line arguments which as in case with the receiver can be "on" or "off" and sends commands to the receiver with id provided as the first argument.
If everything works you should be able to turn lights on/off by running ./onoff 0xF0F0F0F0E1LL on

The UI

Even though being able to switch lights in your apartment via command line is immensely cool it's not a very convenient solution. I wrote a small Node.js app with a simple UI which allows to switch the light via web browser. With a simple port forwarding I'm able to switch switch them from the Internet. I use dynamic dns to access the page at the same url. Here is the code for the server https://github.com/Leonti/lights.

Android app

Since it's a web app it can be accessed from a variety of clients. Opening web browser each time you want to switch a light can be cumbersome, so I wrote a simple Android app with 8 buttons (on/off for 4 rooms) which sends the requests to the server + 2 buttons to turn everything off or on, very useful when going to sleep :)
With Android geolocation API it is very easy to listen to the events when a phone enters or exits a particular zone, which I used to turn lights off when I leave my apartment and turn them on when I'm back (of course it's only triggered after certain hour, so it's still a daytime lights stay off).
The source for the app is not released yet, because the address of my server is hard-coded in the app itself.

Lessons and improvements

  • Using Atmega328p directly was a good decision size- and cost-wise, but I have to take it out each time I want to reprogram it, which prevented me from improving the code over time. If I had to do it again I would choose Arduino Nano without the connectors:
    Arduino Nano
    It's only slightly bigger, but has an FTDI chip, so I could program it while it's still in operation. 
  • 5v power supplies are a bit noisy (they have a high-pitched sound), didn't bother me but might be annoying to some people.
  • Switch state could be queried from the lights, but I didn't find it all that useful - nRF has a confirmation when the command went through or not, so it's usually enough to be sure that a light is in a needed state.
  • Web interface has no authentication, so it needs at least BasicAuth to be secure.

Saturday, April 19, 2014

Mounting usb drive on boot on Raspberry PI

If you have a usb flash drive with FAT system on it and you want to mount it on boot so all users can read/write from/to it you have to add following entry to your /etc/fstab:

/dev/sda        /mnt/usb        vfat    user,umask=0000       0       0

http://superuser.com/questions/617777/how-do-i-auto-mount-a-usb-drive-that-all-users-can-write-to

Sunday, March 23, 2014

Retrofitting Roomba 530 with a Raspberry Pi. Hardware

I own Roomba 530 which had main board broken due to long period of inactivity.
Battery was dead so I had to buy a new one. Upon connecting I've got dreadful "Charging error 3" which means there is something wrong with a charging circuit. After closer look at main board I discovered that some paths got eaten by battery acid.
Fixing the board didn't look like fun and throwing away the cleaner didn't look like a good decision ether. I decided to replace the main board with a Raspberry Pi board.

Motors

Roomba 530 has 5 motors - 2 for wheels, 1 for brushes, 1 for vacuum and 1 for that funny brush you have on the side of the cleaner.
Motors responsible for cleaning can be connected straight to the power maybe with some PWM.
As far as wheel motors go we need to be able to control each motors in both directions individually. H-bridge is the perfect man for the job. I used a circuit based on widespread L298N chip. You can buy ready-to-use board with radiator, connectors and diodes.

Sensors

Roomba 530 has surprisingly big amount of sensors.

Hall sensors are used to count motor revelations. I couldn't make it work with the original ones after several tries. Luckily hall effect sensors are tiny, so I bought TLE 4905L which fits perfectly between the board above the motor and the magnetic disk - I used glue to fix it in there.

"Bump" sensors - the ones that detect when cleaner has hit something. They are pretty straight-forward - it's an optical pair - 5v power to the emitting diode and receiving diode to the digital IN.

Proximity or "wall" sensors - they are responsible for detection if Roomba is near the wall. There are 6 of them. During my experiments I fried some of them and I was also lacking specifications for them, so they were replaced with SFH409. Receiving diodes have to pick up IR light reflected from the wall or some other obstacle so SFH409 have to emit a lot of IR light. Doing so continuously will damage the diodes (I burned through at least 10 of them). You pulsate them for 20ms every 100ms with about 100mA of current. It does the trick.

Front wheel encoder. Front wheel is a black and white. Emitting and receiving diodes are placed above it. When wheel rotates it changes it's color - when it's white IR light is reflected and when it's black receiving diode closes, by counting the signals we can calculate the distance.
This encoder serves as an additional one to the wheel sensors which do the same job. It helps to determine if the cleaner is stuck - driving wheels can rotate even if the Roomba is not moving, bu the front one is passive - it only reports actual distance.

Stair sensors. These are basically the same as wall sensors but facing the floor. Help the cleaner not to fall down the stairs. As my apartment has no stairs they are not connected.

"Home base" sensor. The one on top of the Roomba 530 - locates charging station and also used for "virtual walls". Haven't found it all that useful so it's not connected either.

Dirt sensor.
This one is kinda useful but not implemented as of now. It uses piezoelectric sensor - placed above the brushes - dirt is hitting it while cleaning and if there is a lot of it - it hits harder :) The problem is that it has a small circuit connected to it which I have no documentation or communication protocol for, so for now it's just hanging in there.

Speaker

Roomba 530 has a speaker and Raspberry Pi has audio output. Although I have no use for it now seemed like a waste not to connect the two :) 
I used LM386 for amplifier.