Arduino SmartShield Garage Controller

I am using 1 garage not two what parts need to change? How do you wire it into the garage?

Steve,

Thank you for sharing the DeviceType code as well as the Arduino code. I am brand new to the SmartThings world and had the exact same idea as you (i.e. use an Arduino + Shield to create a Garage Door Sensor/Controller.) Your code will certainly save me a ton of time!

I have created a developer account and I have my Arduino/Shield connected to my hub running your Arduino sketch successfully. I also created a new DeviceType under my account using your Device code above. After changing the device type of my Arduino/Shield via the “MyDevices” + Edit feature to select my (actually yours) new custom DeviceType, I am hitting a slight bump in the road. When I tap on either of the two garage doors icons on my iPhone, I do not ever get a command issued to the Arduino. I have the Arduino Serial Monitor running and I can see the initial setup logging as shown below

setup…
Getting Door State…
leftDoor open
rightDoor open
UNKNOWN
JOINED
Received message: ''
Received message: ''
Received message: ''
Received message: ‘’

But I never receive a “pushLeft” or “pushRight” no matter how many times I press the tiles. I have tried killing the iPhone app and restarting it, I have pulled down the Arduino app in the Arduino device view to cause it to refresh, but nothing seems to allow the Server-Side Groovy code to send a message to the Arduino. I even added logging to groovy code to output a message to console log window if either tile is pressed, and I see nothing written to the log except

‎10‎:‎17‎:‎16‎ ‎PM: error java.lang.ArrayIndexOutOfBoundsException
‎10‎:‎17‎:‎16‎ ‎PM: debug Value is ping
‎10‎:‎16‎:‎13‎ ‎PM: error java.lang.ArrayIndexOutOfBoundsException
‎10‎:‎16‎:‎13‎ ‎PM: debug Value is ping

Do you have any tips for what I am doing wrong? There is not a lot of documentation that explains the Arduino Shield very well (at least none that I have found so far.)

Any help you can provide would be very welcome!

Thanks,

Dan

Steve,

Communications from the shield to the SmartThings cloud and then to my iPhone “Garage Door” application works fine. The state of the doors goes from Open to Closed based on the two digital input pins (8 & 9). So I know the Arduino and Shield are fine, and they are paired with my hub.

If I change MyDevice type to the “On/Off Shield” device, the I can tap the tile and it sends either “on” or “off” to the Shield properly. The Serial Monitor logs these incoming requests.

My only issue is that the Garage Door device will not send data to the Shield when I tap each garage door tile in the iOS App for some reason.

Any ideas what I am doing wrong? I have tried to “Publish” the new DeviceType to myself… No change. I feel like I am missing something very basic about creating a new Device Type… Although, if I copy the sample code in the IDE for the “On/Off Shield” device, it works properly (just like the official published On/Off Shield device.)

Thanks,

Dan

OK, for anyone curious…I managed to figure out what the problem was. It appears that when you copy and paste DeviceType Groovy code, you must first create the custom attributes (leftDoor & rightDoor) and the custom commands (pushLeft and pushRight) on the form where you initially create your new device type (along with your name, namespace, device type name, etc…) After doing this, I was finally able to get Steve’s Garage Door application to work as expected.

Hope this helps someone else in the future!

I’ve been using this setup for a while now and it works flawlessly. I’d like to be able to use the magnetic contact sensors that are connected to the Arduino shield with regular smartapps so they can be leveraged as regular door open/close contacts. Seeing as the left and right doors appear as things underneath the master tile, it’s not possible. @stevesell Do you know how/if I can have each door contact broken out as individual “Virtual Contacts”, or if there’s some other workaround to expose each door sensor to regular apps?

@sgonsalves I have been working on an Arduino project as well based on Steve’s original work. I now have created three “Device Types” and one “Smart App” plus the Arduino code to achieve something very similar to what you’re looking for. Each of the contact sensors (I have 6 currently) is exposed as either a Virtual Garage Door Tile (which accepts button presses and shows all four door states on its Garage Door Tile as well as having a traditional contact sensor tile for compatibility with other SmartApps) or a Virtual Contact Sensor for normal doors.

I have created an Arduino Device Type, a Virtual Garage Door Device Type, a Virtual Contact Sensor Device Type, and a Smart App to handle the multiplexing/demultiplexing between the Arduino and Virtual Devices. I have also modified Steve’s original Arduino code extensively, as I have added 4 other magnetic contact sensors to monitor traditional doors in my house.

Code can be found at https://gist.github.com/ogiewon

Let me know if you have any questions. I know I have been struggling for a while to try and figure this groovy language out. I am very grateful that others have been willing to share their code, so I feel it is only right that I try to help others as well.

One caveat - this is a work in progress. I have not installed my Arduino in my garage yet, but everything is working great on the workbench. I am planning on making some more optimizations to improve performance. One feature I added to the Arduino code was to send the door status updates to the ST Cloud every 30 seconds, if nothing had been sent within the past 30 seconds. This helps to ensure the tiles always have the correct values.

1 Like

Thanks, taking a look at it now. In order for me to use your virtual contact sensor code, do I need to revisit my current setup (stevesell’s device type and arduino code + the ridiculously automated garage door app modified to fire each door discreetly)? Not quite sure which pieces I need to use from your code and where to start.

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.