Arduino Integration?

Peter,

Here is the fixed Arduino Sketch and a Device Handler. Both are fully tested and work as far as two-way communications is concerned. If you look at the Device Handler, you will see it implements the SmartThings “Contact Sensor” and “Switch” device capabilities.

The “Contact Sensor” is what your ultrasonic sensor will update, based on the state of your garage door. This is the correct capability for showing the status of a door. Other SmartApps that typically work with doors will be looking for this capability.

The “Switch” is just a digital output I added back into your sketch as an example.

Where the “Contact Sensor” only transmits data to the cloud, the “Switch” is just the opposite, receiving data from the cloud (although it also sends to the cloud its status as a confirmation that the request was received and acted upon.)

Note: While I do have a HC-SR04 ultrasonic distance sensor, I could not seem to get it working at 3.3v provided by the ESP8266. Therefore, you will need to debug any of that device’s specific logic.

Here is the sketch

//*****************************************************************************
/// @file
/// @brief
///   Arduino SmartThings Ethernet ESP8266 WiFi On/Off with LED Example 
///
///   Revised by Dan Ogorchock on 2017-02-11 to work with new "SmartThings v2.0" Library
///
///   Notes: The NodeMCU ESP communicates via WiFi to your home network router,
///          then to the ST Hub, and eventually to the ST cloud servers.
///
///
//*****************************************************************************

#include <SmartThingsESP8266WiFi.h>
//#include <ESP8266WiFi.h>  //should not be needed - DanO
//#include <SoftwareSerial.h> //should not be needed here - DanO

//*****************************************************************************
// Pin Definitions    | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                    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
//*****************************************************************************
//******************************************************************************************
//NodeMCU ESP8266 Pin Definitions (makes it much easier as these match the board markings)
//******************************************************************************************

#define D6 12
#define D7 13
#define D8 15 //DanO

//Defining Echo/Trig Pin
#define echoPin D6 // Echo Pin
#define trigPin D7 // Trigger Pin

#define switchPin D8 //Digital Output Pin - DanO

//*****************************************************************************
// Global Variables   | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                    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
//*****************************************************************************
SmartThingsCallout_t messageCallout;    // call out function forward decalaration
//Application Global Variables
const double MIN_HEIGHT = 25; //in INCHES
const int DELAY_SEC = 15; //in seconds (to facilitate debugging!) - DanO
boolean isOpen = false;
long duration, inches, cm;
//******************************************************************************************
//ESP8266 WiFi Information    CHANGE THIS INFORMATION ACCORDINGLY FOR YOUR NETWORK!
//******************************************************************************************
String str_ssid     = "WIFI";                                   //  <---You must edit this line!
String str_password = "PASSWORD";                               //  <---You must edit this line!
IPAddress ip(192, 168, 1, 15);       // Device IP Address       //  <---You must edit this line!
IPAddress gateway(192, 168, 1, 1);    //router gateway          //  <---You must edit this line!
IPAddress subnet(255, 255, 255, 0);   //LAN subnet mask         //  <---You must edit this line!
IPAddress dnsserver(192, 168, 1, 1);  //DNS server              //  <---You must edit this line!
const unsigned int serverPort = 8090; // port to run the http server on

// Smartthings Hub Information
IPAddress hubIp(192, 168, 1, 201);    // smartthings hub ip     //  <---You must edit this line!
const unsigned int hubPort = 39500;   // smartthings hub port


//Create a SmartThings Ethernet ESP8266WiFi object
st::SmartThingsESP8266WiFi smartthing(str_ssid, str_password, ip, gateway, subnet, dnsserver, serverPort, hubIp, hubPort, messageCallout);

bool isDebugEnabled;    // enable or disable debug in this example
unsigned long previousMillis = 0; //needed for new timing mechanism - DanO


//*****************************************************************************
// 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;

  if (isDebugEnabled)
  { // setup debug serial port
    Serial.begin(9600);         // setup serial with a baud rate of 9600
    Serial.println("");
    Serial.println("setup..");  // print out 'setup..' on start
  }
  
  // setup hardware pins 
  //HC-SR04 Setup
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(switchPin, OUTPUT);     // define PIN_LED as an output

  //Run the SmartThings init() routine to make sure the ThingShield is connected to the ST Hub
  smartthing.init();
  
  //synch up the ST cloud 
  off();       // Set the digout to "off" and update ST Cloud.
  
  sendData(isOpen);  //Added here for debugging purposes.  May want to remove this before deploying!
}

//*****************************************************************************
void loop()
{
  smartthing.run();
  if (millis() - previousMillis > DELAY_SEC * 1000)
  {
    previousMillis = millis();
    checkSensor();
  }

}

//*****************************************************************************
// Local 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 checkSensor()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  inches = microsecondsToInches(duration);
  if((inches < MIN_HEIGHT) && (!isOpen)) {
    isOpen = true;
  }
  if((inches > MIN_HEIGHT) && (isOpen)) {
    isOpen = false;
  }
  if(isDebugEnabled)
  {
    Serial.print("Inches: ");
    Serial.println(inches);
    Serial.print("Garage Door Status: ");
    Serial.print("isOpen = ");
    Serial.println(isOpen);
  } 
   
  sendData(isOpen);
  
}

//*****************************************************************************
void sendData(boolean status)
{
  if(status)
  {
    if(isDebugEnabled)
    {
      Serial.println("Send: contact open");
    }
    smartthing.send("contact open");        // send message to cloud
  }
  else
  {
    if(isDebugEnabled)
    {
      Serial.println("Send: contact closed");
    }
    smartthing.send("contact closed");        // send message to cloud
  }
}

//*****************************************************************************
void on()
{
  digitalWrite(switchPin, HIGH); // turn output on

  if(isDebugEnabled)
  {
    Serial.println("Send: switch on");
  }
  
  smartthing.send("switch on");        // send message to cloud
}

//*****************************************************************************
void off()
{
  digitalWrite(switchPin, LOW); // turn output off

  if(isDebugEnabled)
  {
    Serial.println("Send: switch off");
  }

  smartthing.send("switch off");       // send message to cloud
}

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

  // if message contents equals to 'on' then call on() function
  // else if message contents equals to 'off' then call off() function
  if (message.equals("switch on"))
  {
    on();
  }
  else if (message.equals("switch off"))
  {
    off();
  }
}

long microsecondsToInches(long microseconds) {
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds) {
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;
}

And here is the Device Handler

/**
 *  PeterSun_Ethernet.device.groovy
 *
 *  Copyright 2017 Dan G Ogorchock 
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Change History:
 *
 *    Date        Who            What
 *    ----        ---            ----
 *    2017-02-08  Dan Ogorchock  Original Creation
 *    2017-02-12  Dan Ogorchock  Modified to work with Ethernet based devices instead of ThingShield
 *    2017-02-26  Dan Ogorchock  Modified for Peter Sun's project
 *
 */
 
metadata {
	definition (name: "PeterSunEthernet", namespace: "ogiewon", author: "Dan Ogorchock") {
		capability "Configuration"
		capability "Switch"
		capability "Sensor"
		capability "Contact Sensor"

	}

    simulator {
 
    }

    // Preferences
	preferences {
		input "ip", "text", title: "Arduino IP Address", description: "ip", required: true, displayDuringSetup: true
		input "port", "text", title: "Arduino Port", description: "port", required: true, displayDuringSetup: true
		input "mac", "text", title: "Arduino MAC Addr", description: "mac", required: true, displayDuringSetup: true
	}

	// Tile Definitions
	tiles {
         standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
			state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
			state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
		}
		standardTile("contact", "device.contact", width: 1, height: 1) {
			state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
			state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
		}
		standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
			state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
		}

        main(["switch","contact"])
        details(["switch","contact","configure"])
	}
}

// parse events into attributes
def parse(String description) {
	//log.debug "Parsing '${description}'"
	def msg = parseLanMessage(description)
	def headerString = msg.header

	if (!headerString) {
		//log.debug "headerstring was null for some reason :("
    }

	def bodyString = msg.body

	if (bodyString) {
        log.debug "BodyString: $bodyString"
    	def parts = bodyString.split(" ")
    	def name  = parts.length>0?parts[0].trim():null
    	def value = parts.length>1?parts[1].trim():null

    	def result = createEvent(name: name, value: value)

    	log.debug result

    return result
	}
}

private getHostAddress() {
    def ip = settings.ip
    def port = settings.port

	log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
    return ip + ":" + port
}


def sendEthernet(message) {
	log.debug "Executing 'sendEthernet' ${message}"
	new physicalgraph.device.HubAction(
    	method: "POST",
    	path: "/${message}?",
    	headers: [ HOST: "${getHostAddress()}" ]
	)
}

// handle commands

def on() {
	log.debug "Executing 'switch on'"
	sendEthernet("switch on")
}

def off() {
	log.debug "Executing 'switch off'"
	sendEthernet("switch off")
}


def configure() {
	log.debug "Executing 'configure'"
	updateDeviceNetworkID()
}

def updateDeviceNetworkID() {
	log.debug "Executing 'updateDeviceNetworkID'"
    if(device.deviceNetworkId!=mac) {
    	log.debug "setting deviceNetworkID = ${mac}"
        device.setDeviceNetworkId("${mac}")
	}
}

def updated() {
	if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 5000) {
		state.updatedLastRanAt = now()
		log.debug "Executing 'updated'"
    	runIn(3, updateDeviceNetworkID)
	}
	else {
		log.trace "updated(): Ran within last 5 seconds so aborting."
	}
}