Arduino SmartShield Garage Controller

Thank you! I’d be lost…

Hi, I thought I’d just post my version of this garage controller.

A few additions:

  1. The addition of a global virtual contact sensor for use with displaying on the main tile and also for use with other apps like security apps etc. (The Arduino code handles this as the “anyDoor” device) This could potentially be handled using Apps, but works easier this way.
  2. Cleaned up the code and changed how communication with the Zigbee hub worked. Code is shorter and has less errors.

ArduinoGarage.groovy

/**
 *  Arduino Garage
 *
 *  Author: Marius Piedallu van Wyk
 *  Date: 2014-07-27
 */

metadata {
    // Automatically generated. Make future change here.
    definition (name: "Arduino Garage", author: "Marius Piedallu van Wyk") {
        capability "Switch"
        capability "Sensor"
        capability "Contact Sensor"

        attribute "contact",   "string"

        attribute "leftDoor",  "string"
        attribute "rightDoor", "string"
        attribute "backDoor",  "string"

        command "pushLeft"
        command "pushRight"
    }

    simulator {
    }

    // Preferences

    // tile definitions
    tiles {
        standardTile("contact", "device.contact", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
            state "closed",  label: 'Closed',  icon: "st.doors.garage.garage-closed",  backgroundColor: "#79b821"
            state "open",    label: 'Open',    icon: "st.doors.garage.garage-open",    backgroundColor: "#ffa81e"
        }

        standardTile("leftDoor", "device.leftDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
            state "closed",  label: "Closed",  icon: "st.doors.garage.garage-closed",  backgroundColor: "#79b821", action: "pushLeft", nextState: "open"
            state "open",    label: "Open",    icon: "st.doors.garage.garage-open",    backgroundColor: "#ffa81e", action: "pushLeft", nextState: "toggle"
            state "toggle",  label: "Toggle",  icon: "st.doors.garage.garage-opening", backgroundColor: "#89C2E8", action: "pushLeft", nextState: "toggle"
        }
        standardTile("rightDoor", "device.rightDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
            state "closed",  label: "Closed",  icon: "st.doors.garage.garage-closed",  backgroundColor: "#79b821", action: "pushRight", nextState: "open"
            state "open",    label: "Open",    icon: "st.doors.garage.garage-open",    backgroundColor: "#ffa81e", action: "pushRight", nextState: "toggle"
            state "toggle",  label: "Toggle",  icon: "st.doors.garage.garage-opening", backgroundColor: "#89C2E8", action: "pushRight", nextState: "toggle"
        }
        standardTile("backDoor", "device.backDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
            state "closed",  label: "Closed",  icon: "st.contact.contact.closed",      backgroundColor: "#79b821"
            state "open",    label: "Open",    icon: "st.contact.contact.open",        backgroundColor: "#ffa81e"
        }

        main "contact"

        details(["leftDoor", "rightDoor", "backDoor"])
    }

}

def parse(String description) {
    def msg = zigbee.parse(description)?.text
    log.debug "Parse got '${msg}'"

    def parts = msg.split(" ")
    def name  = parts.length>0?parts[0].trim():null
    def value = parts.length>1?parts[1].trim():null

    name = value != "ping" ? name : null

    def result
    if(name == "anyDoor") {
        // Use anyDoor as the virtual contact sensor for whole space:
        result = createEvent(name: "contact", value: value)
    } else {
        result = createEvent(name: name, value: value)
    }

    log.debug result

    return result
}

def pushLeft() {
    log.debug "Left Button pressed"
    zigbee.smartShield(text: "pushLeft").format()
}

def pushRight() {
    log.debug "Right Button pressed"
    zigbee.smartShield(text: "pushRight").format()
}

arduino-garage.ino

/**
 *  Arduino Garage
 *
 *  Author: Marius Piedallu van Wyk
 *  Date: 2014-07-27
 */

//*****************************************************************************
#include <SoftwareSerial.h>
#include <SmartThings.h>

#define DO_BACK_DOOR

//*****************************************************************************
// Pin Definitions
//*****************************************************************************
#define PIN_LED              13
#define PIN_THING_RX          3
#define PIN_THING_TX          2
#define PIN_BACK_DOOR_CONTACT 4     // input
#define PIN_RIGHT             6     // out
#define PIN_RIGHT_CONTACT     7     // input
#define PIN_LEFT_CONTACT      8     // input
#define PIN_LEFT              9     // out

#define OPEN                  0     // HIGH
#define CLOSED                1     // LOW
#define UNKNOWN               2     // --- reset / force update

#define PUSH_DELAY            1200  // milliseconds to keep the button "pushed"

//*****************************************************************************
// Global Variables
//*****************************************************************************
SmartThingsCallout_t messageCallout;    // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout);  // constructor

int leftClosed  = UNKNOWN;
int rightClosed = UNKNOWN;
int backClosed  = UNKNOWN;

int anyOpen     = UNKNOWN;

bool isDebugEnabled=false;    // enable or disable debug in this example
int stateLED;           // state to track last set value of LED
int stateNetwork;       // state of the network

//*****************************************************************************
// Local Functions
//*****************************************************************************
void pushLeft()
{
  smartthing.shieldSetLED(0, 0, 2); // blue
  digitalWrite(PIN_LEFT,LOW);
  delay(PUSH_DELAY);
  digitalWrite(PIN_LEFT,HIGH);
  smartthing.shieldSetLED(0, 0, 0); // off
}

//*****************************************************************************
void pushRight()
{
  smartthing.shieldSetLED(0, 2, 0); // green
  digitalWrite(PIN_RIGHT,LOW);
  delay(PUSH_DELAY);
  digitalWrite(PIN_RIGHT,HIGH);
  smartthing.shieldSetLED(0, 0, 0); // off
}

int isClosed(int pin)
{
  // LOW  -> closed
  // HIGH -> open
  return (digitalRead(pin) == LOW)?CLOSED:OPEN;
}

void updateDoorState()
{
  int newState;
  char* msg = NULL;

  newState = isClosed(PIN_LEFT_CONTACT);
  if (leftClosed != newState)
  {
    leftClosed = newState;
    if(leftClosed == CLOSED) msg = "leftDoor closed";
    else                     msg = "leftDoor open";

    smartthing.send(msg);
    if(isDebugEnabled) Serial.println(msg);
    return; // only one message per loop
  }

  newState = isClosed(PIN_RIGHT_CONTACT);
  if (rightClosed != newState)
  {
    rightClosed = newState;
    if(rightClosed == CLOSED) msg = "rightDoor closed";
    else                      msg = "rightDoor open";

    smartthing.send(msg);
    if(isDebugEnabled) Serial.println(msg);
    return; // only one message per loop
  }


#ifdef DO_BACK_DOOR
  newState = isClosed(PIN_BACK_DOOR_CONTACT);
  if (backClosed != newState)
  {
    backClosed = newState;
    if(backClosed == CLOSED) msg = "backDoor closed";
    else                     msg = "backDoor open";

    smartthing.send(msg);
    if(isDebugEnabled) Serial.println(msg);
    return; // only one message per loop
  }
#endif

  newState = CLOSED;

#ifdef DO_BACK_DOOR
  if(rightClosed == OPEN || leftClosed == OPEN || backClosed == OPEN) newState = OPEN;
#else
  if(rightClosed == OPEN || leftClosed == OPEN) newState = OPEN;
#endif

  if(anyOpen != newState)
  {
    anyOpen = newState;
    if(anyOpen == CLOSED) msg = "anyDoor closed";
    else                  msg = "anyDoor open";

    smartthing.send(msg);
    if(isDebugEnabled) Serial.println(msg);
    return; // only one message per loop
  }

}

//*****************************************************************************
void setNetworkStateLED()
{
  SmartThingsNetworkState_t tempState = smartthing.shieldGetLastNetworkState();
  if (tempState != stateNetwork)
  {
    switch (tempState)
    {
      case STATE_NO_NETWORK:
        if (isDebugEnabled) Serial.println("NO_NETWORK");
        smartthing.shieldSetLED(2, 2, 0); // red
        break;
      case STATE_JOINING:
        if (isDebugEnabled) Serial.println("JOINING");
        smartthing.shieldSetLED(2, 2, 0); // yellow
        break;
      case STATE_JOINED:
        if (isDebugEnabled) Serial.println("JOINED");
        smartthing.shieldSetLED(0, 0, 0); // off

        // force report of current door state
        leftClosed  = UNKNOWN;
        rightClosed = UNKNOWN;
        backClosed  = UNKNOWN;
        anyOpen     = UNKNOWN;

        break;
      case STATE_JOINED_NOPARENT:
        if (isDebugEnabled) Serial.println("JOINED_NOPARENT");
        smartthing.shieldSetLED(2, 0, 2); // purple
        break;
      case STATE_LEAVING:
        if (isDebugEnabled) Serial.println("LEAVING");
        smartthing.shieldSetLED(2, 2, 0); // yellow
        break;
      default:
      case STATE_UNKNOWN:
        if (isDebugEnabled) Serial.println("UNKNOWN");
        smartthing.shieldSetLED(2, 0, 0); // red
        break;
    }
    stateNetwork = tempState;
  }
}

//*****************************************************************************
// API Functions    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                  V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void setup()
{
  // setup default state of global variables
  isDebugEnabled = true;
  stateLED = 0;                 // matches state of hardware pin set below
  stateNetwork = STATE_JOINED;  // set to joined to keep state off if off

  // setup hardware pins
  pinMode(PIN_LED, OUTPUT);     // define PIN_LED as an output
  pinMode(PIN_RIGHT, OUTPUT);
  pinMode(PIN_LEFT, OUTPUT);
  digitalWrite(PIN_RIGHT, HIGH);
  digitalWrite(PIN_LEFT, HIGH);
  digitalWrite(PIN_LED, LOW);   // set value to LOW (off) to match stateLED=0

  pinMode(PIN_LEFT_CONTACT, INPUT_PULLUP);
  pinMode(PIN_RIGHT_CONTACT, INPUT_PULLUP);
  pinMode(PIN_BACK_DOOR_CONTACT, INPUT_PULLUP);

  if(isDebugEnabled)
  { // setup debug serial port
    Serial.begin(9600);         // setup serial with a baud rate of 9600
    Serial.println("setup..");  // print out 'setup..' on start
  }

}

//*****************************************************************************
void loop()
{
  // run smartthing logic
  smartthing.run();

  // Check network connections (and send initial states on Join)
  setNetworkStateLED();

  if(stateNetwork == STATE_JOINED)
  {
    // Check the open/closed state of the doors
    updateDoorState();
  }
}

//*****************************************************************************
void messageCallout(String message)
{
  smartthing.shieldSetLED(2, 2, 2); // white

  // if debug is enabled print out the received message
  if(isDebugEnabled)
  {
    Serial.print("Received message: '");
    Serial.print(message);
    Serial.println("' ");
  }

  if(message.equals("pushLeft"))
  {
    pushLeft();
  }
  else if(message.equals("pushRight"))
  {
    pushRight();
  }

  smartthing.shieldSetLED(0, 0, 0); // off
}


Many thanks to @ogiewon and @stevesell for getting me started!

Marius,

@lailoken thanks for sharing your work as well. I really like the optimizations you’ve made to the Arduino code, and I especially like the concept of a “Master Contact Sensor” for the Arduino to represent the state of all of the doors. Looks like I have some updates to make to my code!

One question for you… One idea that I have had for an optimization would be to send up all of the states of the doors in one single Zigbee packet to the ST Cloud/Groovy Device handler. Within the Groovy Device handler, the code would have to parse out the string and create events for each door. The big advantage this would have is to minimize the data transfer time from the Arduino to the ST Cloud. Do you think this is possible? I was not sure how to even start parsing the string in Groovy as I am more of an old-school C programmer. Also, I was not sure whether or not ST wants one parse event to somehow return multiple device events…

Thanks!

Hi Steve,

I am actually trying to load the garagedoor code on arduino mega with ST shield. I have created a device file and I loaded the arduino code. It is going into a unknown state. I did not get a received message in the serial monitor.

I have not wired anything physically, I am trying to use the simulator. I am not able to use the virtual device also. Can you tell me what am I doing wrong? any ideas will be helpfull.

Thank you,

Did you uncomment the debug #define statement to enable the debug messages? Otherwise I have no idea… should work as far as I know… unless it’s a Mega-specific problem…

@ogiewon Thanks. I’m also an old-school C/C++ programmer. Dabbled in some PIC programming as well. I considered myself quite a C++ expert. :stuck_out_tongue: But I could be a bit rusty, and have zero Zigbee or Arduino experience before the week-end I started (and finished) this.

I did consider creating a bitmask to report an entire array of inputs and outputs, but I thought to keep this type of operation on the Arduino to keep the pressure off the Smartthings device. (It would help in future as well if I had to move to another platform which may not have the flexibility of this groovy scripting)

But yes, it does seem to put some pressure on the comms… but I think my addition to throttle the updates to one per hearbeat seems to fix the congestion issue that I think some people might have (with dropped events). So this should not be too big an issue…

You only have about 4 or 5 inputs… and the hearbeat is relatively fast… so I don’t think the delays should be significant.

A bit off-topic:

I wrote a building management system way back in 1997 … client/server type system with linux servers and windows Gui front-ends. Used PIC PCI cards on servers communicating with RS485 to multiplexors controlling video feeds.

Did all sorta stuff like access control, card reading, video motion detection, jpeging, OCR, fingerprint etc. etc… It was long ago before I even started writing real compilers and my code was relatively simple.

Was way fun writing this, at some clients we had over 200 cameras and 50 card readers… was a load-balancing nightmare at the time of pentiums.

So I think 5 inputs should be fine… :wink:

1 Like

@ogiewon I finally got around to implementing this. I really like the ability to have each door on the Arduino exposed as virtual devices so I can add them to the dashboard and for use with regular apps. I did run into some challenges though, when I add both garage doors to the dashboard I can’t open or close them from there, I can however from their device tiles directly. Also, I’m not sure what smartapp you’re using with your doors, but I’m not having any luck using the “ridiculously automated” app with your code which might be a deal breaker for me unless I can get that sorted out.

Sorry folks! I have been away for a while. Looks like you guys have really gone to town on the original code! For what it’s worth, I bought a small temp/humidity sensor and added that to the arduino… so now I have temp and humidity of the garage.

Here’s the device I bought: http://amzn.com/B00CDHH3WQ

If we ever get iBeacon integration, I’m definitely going to have the door just open when I drive up and close when I leave!

lailoken, I mainly brought a arduino board and smart things shield to reuse existing wiring in the house for doors. I was happy to see your project which is exactly what I was looking for. Thank you saved me a ton of time !

I installed the device types 3 of them and 1 smart app and the arduino code. when I loaded the arduino code it goes into unknown state which I am still trying to figure out. on the smart app simulator I am trying to set my hub and then using the virtual contact sensor and virtual ardiono I tried to install. I am getting an exception saying simulator is not currently running. What am I doing wrong? I am also getting this error message."grails.validation.ValidationException: Validation Error(s) occurred during save():

  • Field error in object ‘physicalgraph.device.Device’ on field ‘name’: rejected value [null]; codes [physicalgraph.device.Device.name.nullable.error.physicalgraph.device.Device.name,physicalgraph.device.Device.name.nullable.error.name,physicalgraph.device.Device.name.nullable.error.java.lang.String,physicalgraph.device.Device.name.nullable.error,device.name.nullable.error.physicalgraph.device.Device.name,device.name.nullable.error.name,device.name.nullable.error.java.lang.String,device.name.nullable.error,physicalgraph.device.Device.name.nullable.physicalgraph.device.Device.name,physicalgraph.device.Device.name.nullable.name,physicalgraph.device.Device.name.nullable.java.lang.String,physicalgraph.device.Device.name.nullable,device.name.nullable.physicalgraph.device.Device.name,device.name.nullable.name,device.name.nullable.java.lang.String,device.name.nullable,nullable.physicalgraph.device.Device.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class physicalgraph.device.Device]; default message [{0} cannot be null]"

I am assuming I should be able to test it with smart app simulator but not sure though. Do I need a breadboard and some resistor etc,… to test the code or can it be done through the smart app simulator?

Thank you much!

I’m glad I could help… I had similar help from other people to build upon.

Also I’m sorry I only saw this now, for some reason I did not get notified of the response… Just saw it per chance. (I think you need to address someone like this: @fortune to get their attention… not sure.)

Alas, I’m not sure what your problem is, my debugging worked well enough… just make sure you enable the debugging options (#define) in the Arduino Code. I was using an Uno… not sure if that could be the cause.

My experience on the Arduino is pretty limited, this was a week-end project for me.

@lailoken, Thank you for responding.

I gave up trying to simulate. I hooked up some resistors and wired them and it does show open and close properly. Now I am trying to see if I can send a text to myself when a door opens when I am away. I tried to introduce some code in the smart app the other day but i was unsuccessful. Need to spend some more time to get that working. In the mean time I am waiting for the battery to power the arduino.

I must say for a weekend project, you did pretty good. This would have taken me weeks!

well will update where I am at in a few days…

I think it was a combination of years of programming, with some PIC programming experience, a smidgen of electronics knowledge and a whole lot of luck I guess.

Anyone figure out how to enable turning on and off the garage light or even enabling the “lock” for when in away mode? At least these are the feature on my lift master it would be great to incorporate.

@stevesell Can you share your code for the temp sensor and any hints on wiring? This is my first Arduino project. I am assuming that the pins referenced in the arduino code are the corresponding ones you wire to on the shield?

@afewremarks Hi Mark, I just finished my install of this project this weekend. I will take some pictures to help you once I get home this evening. I wasn’t able to figure out a way to get the light to turn on, but I do have a way to lock the system. Basically you would run the power for the garage door opener through the Arduino controlled relay. The interesting thing is that on my opener (Genie) when you return power to the opener, the light comes on.

@afewremarks Hi Mark, Here are some images from my install that will hopefully help you out. The pins referenced in the Arduino code are indeed the locations that you wire to on your Uno. Please note that my setup will likely be different from yours. I have only 1 garage door attached to my relay, but I installed a 4 relay module because I will be adding more items to control in the future.

Overall view

Relay closeup (please note that this is a test setup, in the live the wires are not exposed!) :slight_smile:

Garage Door Opener attachment. This is a Genie opener, and the wires coming from the bottom are where mine attach. (Same place as the wall control unit)

Arduino reed sensor module and magnet mounted to steel inner door.

Hopefully this helps, I found that it took a little experimentation to get the relay wired to the opener, so that it worked and kept the control panel active.

Cheers,
David

@DavidCreed awesome thanks for the pics. Got mine up and working as well. Always interesting to see how different people implement these things. Now i just need to figure out the temp/humidity sensor.

Since this is the thread where it all started for me, I thought I’d share…

I have just posted a new topic about a project, ST_Anything, my son and I have developed to make using an Arduino with SmatThings as simple as we believe possible.

Check it out at Announcing the "ST_Anything" Arduino/ThingShield Project

Hopefully the ST_Anything project will at least provide some examples of what is possible. It also includes an upgraded Arduino SmartThings library which you can about in the other post as well and on the GitHub repo at https://github.com/DanielOgorchock/ST_Anything

Dan

1 Like

Dan,

This project is amazing. How did I not find this before?

I do have a question.

I have created the device types for:
Arduino
Virtual Garage Door
Virtual Contact Sensor

I have created the smart app:
VirtualtoArduinoMultiplexer

I have installed the Arduino code in my mega.

I have configured my ST Shield to be of the Arduino Device Type.

I have configured the VtAM Smart App to point all things to my Arduino.

At this point, I have the Arduino in my “things” list and when I toggle pins 5, 7, 8, 9, 10 and 11 the corresponding icon on the sub screen of the thing changes color.

I now want to add Virtual sensors. What I don’t know is how to tell the virtual sensor which pin it is related to.

I tried setting the Name, Label, Zigbee ID and Device Network ID to frontDoor thinking one of those would be what tells the virtual contact which one to trigger from, but that did not work.

How do I make the logical connection from the Virtual Sensor to Arduino Pin?

Thank you!!!
-Todd

Looking at the SmarThings live logging, it looks like it doesn’t like openme in the virtual sensor:

6181b483-490c-4c8d-bd5f-0b1272de9a70 1:47:50 PM EST: error java.lang.IllegalArgumentException: Command ‘openme’ is not supported. Supported commands: [poll, refresh, pushLeft, pushRight] @ line 147
6181b483-490c-4c8d-bd5f-0b1272de9a70 1:47:50 PM EST: debug arduinoevent(frontDoor: open: 1892e981-dc27-429a-95f8-83730308bb7a)
1892e981-dc27-429a-95f8-83730308bb7a 1:47:50 PM EST: debug [value:open, name:frontDoor, handlerName:frontDoor open, linkText:AlarmThing, descriptionText:AlarmThing was frontDoor open, isStateChange:true, displayed:true]
1892e981-dc27-429a-95f8-83730308bb7a 1:47:50 PM EST: debug Value is frontDoor open