Monthly Archives: November 2013

Last updated by at .

Building a Garage Door Sensor with an Arduino Nano

The Problem

Our two car garage has a roller door on the front and back. The one on the front of the house is motorised and activated with a remote control. You cannot see if the front roller door is open from anywhere within our house so the only way to check if it is open is to walk outside and look. If you remember that is. The problem is that we do not want to go to bed and leave the front roller door on the garage open and we have done so a few times.

The Solution

Create some sort of sensor and indicator letting us know that the front roller door is open. The sensor should be reliable and the indicator should be obvious (especially when night has fallen) and not require us to walk outside to check on the status of the door. I’ve had an Arduino starter kit sitting on my desk for a while so (of course) I thought that the solution had to include an Arduino.

The Sensors

I wanted to detect if the door was up or down so this would require two sensors. Initially I was thinking along the lines of an ultrasonic detector but the cost of this was prohibitive as I needed two of them. In the end I settled on simple magnetic reed switches of the type you see in home security systems. One would be mounted to the door frame at the bottom and one at the top. A magnet would be fixed to the door to trigger the switches as the door moved up and down. The two door sensors with paired magnets were $5.

The Indicator

Initially I had thoughts of a two unit system. One sender unit mounted on the garage door and a receiver unit that sat inside with an indicator light with RF comms using one of the many different types of Arduino RF shields. But this was complete overkil so in the end I settled on 2 colour high intensity 3 watt LED that could be mounted outside our kitchen window and be driven by a cable. The LED module would need to be driven by a decent constant current power supply so I bought a couple of 330mA units at the same time. These PSU’s could be driven by a 0-5V PWM (pulse width modulated) signal to adjust the intensity of the driven LED. The LED module was $3, a heatsink to mount the LED on was $3, and the PSU’s were $9 each. A transparent poly-carbonate box to install the LED in was $9. The cable ended up being some 8 core alarm cable that cost about a dollar a meter and I used some 6 pin audio plugs for each end. Total cost for the cable was around $20 or so.

The Arduino

My Arduino kit contains a Arduino UNO clone. This was bigger than I wanted so I settled on a cloned Arduino Nano from eBay which was about $10. This had the digital inputs I wanted, more than enough memory to hold the simple sketch I’d write, could be run from a 12V DC plug pack, and the PWM outputs I’d need.

Enclosure and Breadboard

The enclosure would need to hold the Arduino Nano, the two LED PSU’s, and a small breadboard. It would also need to take the wires from the door sensors, have a female socket to take 12V and a female connector to plug our LED cable into. The breadboard was used to make a circuit that would ground out the sensor signals and route 3.3V to the sensors and 12V to the LED power supplies. All connections from the circuit to the Arduino and the Arduino to the power supply were done via connectors and cables I had laying around from old PC builds. Total cost of the enclosure and assorted components (and a 2A 12V DC plug pack) ran out to about $40.

Build Problems

One of the LED PSU’s was DOA so I settled on running just one of the LED colours. I chose to indicate different door states by flashing the LED at different rates. A second problem was that the roller door frame was significantly out of square and the magnets supplied with the reed switches were not strong enough to trigger the switches. I solved this by purchasing a rare earth magnet and gluing it to the roller door (cost was $12).

Arduino Code

I decided on a simple state engine for the code to run my indicator light. The first state was door down. In this state the Arduino was in a “door closed sleep” state waiting for an interrupt from the bottom door sensor. When the state of this sensor changed the Arduino would wake and enter a “door in transition” state and flash the LED rapidly. When the top sensor triggered the door would enter a third “door up” state and pulse the light slowly. When the roller door was lowered the Arduino would enter the “door in transition” state again and stay that way until the door closed and the bottom sensor was triggered. The Arduino would now enter a “door closed indicator” state and leave the LED on for 10 seconds before entering the “door closed sleep” state. Here’s the Arduino sketch I came up with to accomplish this:

#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>


const int blueLED = 3;
const int ledBrightness=50;
const int doorDownSensor=2;
const int doorUpSensor=4;
int doorDownSensorState=0;
int doorUpSensorState=0;
int oldDoorUpSensorState=0;
int oldDoorDownSensorState=0;
int tmpState=0;
int i=0;
int pulseStep=1;
int pulseBrightness=0;
unsigned long blueLEDStartTime;
String tmpString;

unsigned long endLastPulse;
unsigned long pulseDelay=3000;

void setup() {
  // put your setup code here, to run once:
  pinMode(blueLED,OUTPUT);
  pinMode(doorDownSensor,INPUT);
  pinMode(doorUpSensor,INPUT);
  
  Serial.begin(9600);
}

void loop() {
  tmpState=digitalRead(doorDownSensor);
  
  
  if (tmpState==HIGH)
    tmpString="HIGH ";
  else
    tmpString="LOW ";  

  if (tmpState != oldDoorDownSensorState)
  {
    doorDownSensorState=1-doorDownSensorState;
    delay(50);
    if (doorDownSensorState==1)
    {
      blueLEDStartTime=millis();  
    }
  }
  oldDoorDownSensorState=doorDownSensorState;
  
  tmpState=digitalRead(doorUpSensor);

  if (tmpState==HIGH)
    tmpString+="HIGH ";
  else
    tmpString+="LOW "; 
    
  if (tmpState != oldDoorUpSensorState)
  {
    doorUpSensorState=1-doorUpSensorState;
    delay(50);
  }
  oldDoorUpSensorState=doorUpSensorState;  
  
  tmpString+="DOWN_STATE::";
  tmpString+=doorDownSensorState;
  tmpString+=" UP_STATE::";
  tmpString+=doorUpSensorState; 
  Serial.println(tmpString); 
    
  if (doorDownSensorState==1)
  {
    if ((millis()-blueLEDStartTime)<=10000)
      analogWrite(blueLED,ledBrightness); 
    else
    {
      analogWrite(blueLED,0);
      sleepNow();
    }
  }

  
  if (doorUpSensorState==1)
  {
    if (endLastPulse==0 || (millis()-endLastPulse)>pulseDelay)
    {
      pulseBrightness+=pulseStep;
      if (pulseBrightness<0)
      {
        pulseBrightness=0;
        pulseStep=-1*pulseStep; 
        endLastPulse=millis();
      }
      if (pulseBrightness>ledBrightness)
      {
        pulseBrightness=ledBrightness;
        pulseStep=-1*pulseStep; 
      }      
      analogWrite(blueLED,pulseBrightness);
      delay(25);
    }

  }
  else
  {
    endLastPulse=0; 
  }

  
  if (doorDownSensorState!=1 && doorUpSensorState!=1)
  {
    if ((millis() % 1000)<500)
      analogWrite(blueLED,ledBrightness);    
    else
      analogWrite(blueLED,0);        
  }

  
}

void sleepNow()
{
    // Set pin 2 as interrupt and attach handler:
    attachInterrupt(0, pinInterrupt, LOW);
    delay(100);
    //
    // Choose our preferred sleep mode:
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    //
    // Set sleep enable (SE) bit:
    sleep_enable();
    //
    // Put the device to sleep:
    sleep_mode();
    //
    // Upon waking up, sketch continues from this point.
    sleep_disable();
}

void pinInterrupt(void)
{
  detachInterrupt(0);
}

There’s a couple of things to note here. First, the Nano only has interrupts on IO pins 2 and 3 so the bottom door sensor had to be on one of these pins. I used pin 2 which is interrupt 0. Also, I wanted to drive the LED power supply with a PWM signal so I had to use one of the PWM capable outputs, in my case I used pin 3.

I developed this code on my Arduino UNO with the circuit mocked up on a spring breadboard that came with my starters kit. Flashing the code to the Nano was troublesome because it turns out that the Nano I bought had a problem with the serial chip (UART) and just wouldn’t talk to my computer. After much googling I just needed to download and install an older driver for the USB/Serial driver on my PC and suddenly I could talk to the Nano.

Installation

The hardware mounted up easily enough. The reed switches included adhesive and stuck well enough to the door frame once I had cleaned the metal with some acetone. The door magnet was put in place with some cyano acrylate glue. Cabling from the sensors to the control box was cheap two core cable and the control box was mounted to the wall with some wall plugs I had lying around. A friend of mine helped me route the LED cable through my roof cavity and the LED box was stuck to one of the beams of the verandah outside our kitchen window.

Here’s one of the reed switches mounted to the top of the door frame. You can see the rare earth magnet (the silver disk) on the door itself.

Reed Sensor and Magnet

Reed Sensor and Magnet

And here’s the control box containing the Arduino Nano, home made circuit, and one of the constant current power supplies that drive the LED. The 12V power goes in via the plug at the bottom left while the cable coming out the top right goes into the roof cavity and drives the LED.

Arduino Enclosure

Arduino Enclosure

Finally, here’s the LED box mounted to a beam on our verandah. It’s the blue thing in the middle of the picture. You can see the cable running across to the top of the beam it’s mounted on.

LED Enclosure

LED Enclosure

Here’s what it looks like in operation:

innosetup and .NET 4.0

My simple timesheet software, Timesheets Lite is a a C# program based on Winforms and the DXperience library for WinForms. It is installed with an innosetup script. Up until yesterday the software required the .NET 2.0 library and the installer could detect if .NET 2.0 was installed and download and install it if it wasn’t. Windows 8 was an exception to this rule as .NET 2.0 (actually .NET 3.5) is a Windows feature that needs to be enabled rather than something that needed to be installed. This wasn’t too difficult to get around because you (used) to be able to use DISM to enable Windows features.

Previously my innosetup script detected if Windows 8 was the target OS and then could simply enable the .NET 3.5 feature using this:

Exec('Dism', ' /online /enable-feature /featurename:NetFx3 /All /NoRestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);

This had worked fine previously because Windows went away (presumably to the Windows Update site) and downloaded what it needed from there. But it doesn’t work any longer and to get DISM to enable .NET 3.5 the /source parameter must be specified pointing to a local copy of the Windows 8 installation media or an ISO. Clearly this isn’t acceptable for my purposes. Hence I decided to update Timesheets Lite to require the .NET 4.0 library (which is installed on W8 by default). This threw up some other problems though because now I needed to detect .NET 4.0 on earlier versions of Windows, and download and install it during the Timesheets Lite installation process.

This was (surprisingly) easy. I made use of the innosetup .NET detection function from this page and modified the inno script I’d written years ago to download and install .NET 2.0. Here’s some code from my innosetup script for giggles. Firstly, the InitializeSetup function which determines what version of Windows is the target and uses the IsDotNetDetected function to check for the .NET 4.0 client or full installation.

function InitializeSetup(): Boolean;
begin
    dotNetNeeded := false;
    Windows8 := false;
    Result := true;

    if (Version.Major=6) and (Version.Minor=2) then
      begin
        Windows8:=true;

      end;


    if (not IsDotNetDetected('v4\Client', 0)) and (not Windows8) then 
      begin
        if not IsDotNetDetected('v4\Full', 0) then
          begin
              dotNetNeeded := true;
          end
      end 

     if dotNetNeeded then
      begin
            if (not IsAdminLoggedOn()) then
                begin
                    MsgBox('This program needs the Microsoft .NET 4.0 Framework to be installed by an Administrator', mbInformation, MB_OK);
                    Result := false;
                end
            else
                begin

                    
                    dotnetRedistPath := ExpandConstant('{src}\dotnetfx.exe');
                    if not FileExists(dotnetRedistPath) then
                        begin
                            dotnetRedistPath := ExpandConstant('{tmp}\dotnetfx.exe');
                            if not FileExists(dotnetRedistPath) then
                                begin
                                    isxdl_AddFile(dotnetRedistURL, dotnetRedistPath);
                                    downloadNeeded := true;
                                end
                        end

                    SetIniString('install', 'dotnetRedist', dotnetRedistPath, ExpandConstant('{tmp}\dep.ini'));
                end
      end
end;

Next up I’ve added some code to the NextButtonClick function that runs when the current page is wpReady (read to install).


  dotnetRedistURL = 'http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe';

function NextButtonClick(CurPage: Integer): Boolean;

var
  hWnd: Integer;


begin

  Result := true;

  //*********************************************************************************            
  // Only run this at the "Ready To Install" wizard page.
  //*********************************************************************************
  if CurPage = wpReady then
    begin

        hWnd := StrToInt(ExpandConstant('{wizardhwnd}'));


        // don't try to init isxdl if it's not needed because it will error on < ie 3

        //*********************************************************************************
        // Download the .NET 4.0 redistribution file.
        //*********************************************************************************
        if downloadNeeded and (dotNetNeeded = true) and Windows8 = false then
            begin
                isxdl_SetOption('label', 'Downloading Microsoft .NET Framework 4.0');
                isxdl_SetOption('description', 'This program needs to install the Microsoft .NET Framework 4.0. Please wait while Setup is downloading extra files to your computer.');
                if isxdl_DownloadFiles(hWnd) = 0 then Result := false;
            end;

        //*********************************************************************************
        // Run the install file for .NET Framework 4.0. This is usually dotnetfx.exe
        //*********************************************************************************
            if ((dotNetNeeded = true)) then
            begin

                if Exec(ExpandConstant(dotnetRedistPath), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
                    begin

                        // handle success if necessary; ResultCode contains the exit code
                        if not (ResultCode = 0) then
                            begin

                                Result := false;

                            end
                    end
                    else
                        begin

                            // handle failure if necessary; ResultCode contains the error code
                            Result := false;

                        end;
            end;


    end;

end;

I tested this script on XP, Vista, Windows 7 and Windows 8 and it works very well. The .NET 4.0 download is about 50MB so it’s not too big an impost for users. And I suspect most people will have .NET 4.0 installed anyway. Note that you’ll have to to be using the isxdl innosetup plugin for this installation procedure to work.

Uploading to a Production Server with RSYNC

I knew you could use RSYNC to synchronize a local copy of a website with the files on your production server. In fact I’ve known it for long enough to have tried to get it working a few years ago but failed miserably. Well I’ve learned a bit since then (well I like to think so anyway) so I thought I’d give it another go this this morning. And, it was surprisingly easy. In this guide I refer to my local server (a teeny tiny Ubuntu server) as the LOCAL SERVER and the remote production server as the REMOTE SERVER.

Install SSH Key on Remote Server

Firstly I needed to install a SSH key on my REMOTE SERVER so my LOCAL SERVER could log into it without me needing to enter a password every time I wanted to synchronize the files. This is easy enough, on the LOCAL SERVER enter the following:

sudo ssh-keygen

Enter the default values as prompted and this will create a public key in ~/.ssh/ called id_rsa.pub. This file needs to be set to the remote server. This is done easily with:

scp ~/.ssh/id_rsa.pub user@remote_server.com:/home/user/uploaded_key.pub

Then this file needs to be appended to the authorized_keys file on the REMOTE SERVER with:

ssh user@remote_server.com "echo `cat ~/.ssh/uploaded_key.pub` >> ~/.ssh/authorized_keys"

Construct RSYNC Included and Excluded Files / Folders List

If you’re like me your development folders are littered with test files and directories that you don’t want on your production servers. So you’re going to need to create inclusion and exclusion lists for RSYNC to use so it knows what to upload and what not to upload. This is simply done using text files with file / folder specifications on separate lines. For example my include.txt looks something like this:

app
classes
create-scripts
cron-scripts
css
inc
js
images
tooltips
validation_scripts

While my exclude looks something like this:

/app/jscal
/css/img/Thumbs.db
/create-scripts/*.txt
app/test-first-day-of-week.htm
app/test-mcrypt.htm
app/images/Thumbs.db

These files can be written in nano and saved somewhere sensible (probably where you’re going to put your rsync bash script).

Write your BASH script

Next step is to write a very short bash script that includes your RSYNC command. My script looks like this:

#!/bin/bash
rsync  -v -u -r -a --rsh=ssh --stats --progress --files-from=include.txt --exclude-from exclude.txt /srv/windows-share/local-source-files/   user@remote-server:/srv/www/remote-source-files/

Don’t forget you need to make your script executable with something like:

sudo chmod 700 rsync_upload.bsh

Also you’re going to want to test the RSYNC command using the -n (or –dry-run) option to make sure everything works the way you expect it to. You’ll also need to specify the -r (recursive) option because the –files-from directive overrides the implied recursion you get from the -a (archive) option.

Run your Script

So now when you’re done developing and testing files on your development server it’s simply a matter of running your new script to sync the files on your production server. Something like the following will run your script:

sudo /path/to/script/rsync_upload.bsh