Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
Eric Tsai
Published © CC BY-NC

Physical Home Automation Interface

A physical smart home interface. A mini-me for your house.

IntermediateFull instructions provided24,015
Physical Home Automation Interface

Things used in this project

Hardware components

TowerPro Servo
×1
Small Hinges
×1
Buttons
×1
Arduino Yun
Arduino Yun
×1
100A current transformer
×1
resistors and capacitors
×7

Story

Read more

Schematics

mini-house servo

mini-house buttons

energy monitor

Code

smarthome_yun.ino

Plain text
// Eric Tsai
// 2015-04-13
// Arduino Yun code for controlling physical smart home interface
// Modify MQTT topic names to match your use
// Modify MQTT broker IP address

//new yun includes
#include <Bridge.h>
#include <yunclient.h>
#include <PubSubClient.h>

//old ethernet includes when using ethernet shield
/*
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
*/

#include <Servo.h>




//Ethernet shield setup
/*
IPAddress ip(192, 168, 2, 36);
byte mac[]    = {  0x90, 0xA2, 0xDA, 0x0D, 0x43, 0x13 };
byte server[] = { 192, 168, 1, 101 };
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

*/
unsigned long keepalivetime=0;
unsigned long MQTT_reconnect=0;

//use yunclient to bridge to network
YunClient yun;          //equivalent of ethernet client
PubSubClient client("192.168.1.101", 1883, callback, yun);

bool conn_ok;

//use LED for indicating MQTT connection status.
int ledPin = 4;     //indicate lights on
int statusPin = 7;  //indicate MQTT conn status
//servo control
int servo1Pin = 3;  //front door
int servo2Pin = 5;  //garage door
int servo3Pin = 6;  //energy usage meter
Servo servo1;   //door
Servo servo2;   //garage door
Servo servo3;   //energy
int button1 = 8; //button over front door
int button2 = 9;  //button over garage door

//servo operation:  target positions for servos
int servo1_target = 5;  //door, closed
int servo2_target = 5;  //garage, closed
int servo3_target = 180;  //energy, 180 is "0" watts, motor mounted backwards
int servo1_pos_cur = 5;
int servo2_pos_cur = 5;
int servo3_pos_cur = 180; // 180 is "0" watts, motor mounted backwards
int servo1Opened = 5;
int servo1Closed = 130;
int servo2Opened = 5;
int servo2Closed = 150;
unsigned long servo1_time = 0;
unsigned long servo2_time = 0;
unsigned long servo3_time = 0;

//debounce on buttons, so you don't spam publish
unsigned long button1_db = 0;
unsigned long button2_db = 0;
int button=0;

int callback_flag = 0;
char buff_message[12];
//---------------------------------------------------------------
// MQTT call back
// react to MQTT messages here
//---------------------------------------------------------------
void callback(char* topic, byte* payload, unsigned int length) {

  
  //convert topic to int.
  int mytopic = atoi (topic);
  //Serial.print(mytopic);

  //convert payload to int
  payload[length] = '\0';   //add a line return so atof can parse correctly
  float mymsg = atof( (const char *) payload);

  
  //led
  if (mytopic == 2822)
  {
    if (mymsg == 1)
    {
      digitalWrite(ledPin, HIGH);
    }
    if (mymsg == 0)
    {
      digitalWrite(ledPin, LOW);
    }
  }

  
  //servo 1, front door
  if (mytopic == 2832)
  {
    if (mymsg == 1) //opened
    {
      //Serial.println("servo1 opened");
      servo1_target = servo1Opened;
    }
    if (mymsg == 0)  //closed
    {
      //Serial.println("servo1 closed");
      servo1_target = servo1Closed;
    }
    //Serial.println(servo1_target);
  }

  /*
  //second garage door signal

	"closed" = off = send 1
	"open" = on = send 0
  */
  //servo 2, garage door
  if (mytopic == 2842)
  {
    if (mymsg == 1) //opened
    {
      servo2_target = servo2Opened;
    }
    if (mymsg == 0)  //closed
    {
      servo2_target = servo2Closed;
    }
    //callback_flag=1;
    //servo3_target = mymsg;
  }

  
  //servo 3, energy meter
  if (mytopic == 2852)
  {
    //message = watts
    //bound watts to meter max & min
    
    if (mymsg > 6000)
    {
      mymsg = 6000;
    }
    if (mymsg < 0)
    {
      mymsg = 0;
    }
    
    //bias meter from 180 degrees to 0 degrees
    //180 deg to 0 deg = 0 watt to 6000 watts, for example
    //set max watts to what makes sense for you.
    servo3_target = 180-((1-((6000-mymsg)/6000))*180)-25;
    
    callback_flag=1;

  }
}//end callback


void setup() 
{
  
  pinMode(ledPin, OUTPUT);    //LED indicating light on
  pinMode(statusPin, OUTPUT);
  pinMode(button1, INPUT_PULLUP);  //use pull-up resistor, invert logic
  pinMode(button2, INPUT_PULLUP);  //use pull-up resistor, invert logic

  
  digitalWrite(ledPin, HIGH);

  //yun bridge
  Bridge.begin();

  //connect to MQTT broker
  client.connect("yun smarthome");
  client.publish("watch", "Smart Home Connected!");
  
  keepalivetime=millis();
  
  //Wire.onReceive (receiveEvent);

  
  MQTT_reconnect = millis();
  

  //client.subscribe("#");   //test subscribe
  client.subscribe("2822");   //LED
  client.subscribe("2832");   //servo 1 topic
  client.subscribe("2842");   //servo 2 topic
  client.subscribe("2852");   //servo 3 topic
  
  digitalWrite(ledPin, LOW);
  
  //servo
  servo1.attach(servo1Pin);
  servo1.write(90);
  servo2.attach(servo2Pin);
  servo2.write(90);
  servo3.attach(servo3Pin);
  servo3.write(5);
}  // end of setup



void loop() 
{

  client.loop();  //MQTT processing needs this to run

  //For servo positioning, don't want to move too fast.
  //Must crawl from current position to target position
  //but also avoid using "delay" cmd, that just freezes up microcontroller
  
  //servo 1
  if ((millis() - servo1_time) > 20)
  {
    if (servo1_pos_cur > servo1_target)
    {
      servo1_pos_cur = servo1_pos_cur - 1;
    }
    if (servo1_pos_cur < servo1_target)
    {
      servo1_pos_cur = servo1_pos_cur + 1;
    }
    servo1.write(servo1_pos_cur);
    servo1_time = millis();
  } //end servo 1 target control
  
  
  //servo 2
  if ((millis() - servo2_time) > 20)
  {
    if (servo2_pos_cur > servo2_target)
    {
      servo2_pos_cur = servo2_pos_cur - 1;
    }
    if (servo2_pos_cur < servo2_target)
    {
      servo2_pos_cur = servo2_pos_cur + 1;
    }
    servo2.write(servo2_pos_cur);
    servo2_time = millis();
  } //end servo 2 target control


  //servo 3
  if ((millis() - servo3_time) > 20)
  {
    if (servo3_pos_cur > servo3_target)
    {
      servo3_pos_cur = servo3_pos_cur - 1;
    }
    if (servo3_pos_cur < servo3_target)
    {
      servo3_pos_cur = servo3_pos_cur + 1;
    }
    servo3.write(servo3_pos_cur);
    servo3_time = millis();
  } //end servo 3 target control

  if (callback_flag == 1)
  {
    
     callback_flag = 0;
     
     dtostrf (servo3_target, 4, 5, buff_message);
     //client.publish("watch","ssaw call back)");
     client.publish("watch", buff_message);
  }

  
  //button 1
  button=digitalRead(button1);
  if (button==0)  //inverted logic
  {
	if ( ((millis() - button1_db)>3000) || (button1_db > millis()))
	{
		button1_db=millis();
		client.publish("2862", "button1 yaya");
	}
  }
  
  
  //button 2
  button=digitalRead(button2);
  if (button==0)  //inverted logic
  {
	if ( ((millis() - button2_db)>3000) || (button2_db > millis()))
	{
		button2_db=millis();
		client.publish("2872", "button2 yaya");
	}
  }
  
  //check network connection to MQTT broker every 60 seconds.
  //reconnect if no longer connected
  if ((millis() - MQTT_reconnect) > 60000)
  {
    conn_ok = client.connected();
    if (conn_ok==1)
    {
      digitalWrite(statusPin, HIGH);
      //Serial.println("MQTT connected OK");
    }
    else
    {
      digitalWrite(statusPin, LOW);
      //Serial.println("MQTT NOT connected OK");
    }

    //no connection, reconnect
    if (conn_ok == 0)
    {
      client.disconnect();
      delay(5000);
      while (client.connect("smarthouse") != 1)
      {
        digitalWrite(statusPin, LOW);
        //Serial.println("Error connecting to MQTT");
        delay(4000);
      }
      digitalWrite(statusPin, HIGH);
    }
    //Serial.println("reconnected to MQTT");
    MQTT_reconnect = millis();
    client.publish("watch","smart home heartbeat!"); 

  }//end Mosquitto connection check
}  // end of loop

smarthome_ethernet.ino

Plain text
// Eric Tsai
// 2015-04-1
// Arduino Uno w/ Ethernet, code for controlling physical smart home interface
// Modify MQTT topic names to match your use
// Modify MQTT broker IP address

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

#include <Servo.h>

//Ethernet
byte mac[]    = {  0x90, 0xA2, 0xDA, 0x0D, 0x43, 0x13 };
byte server[] = { 192, 168, 1, 101 };
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
unsigned long keepalivetime=0;
unsigned long MQTT_reconnect=0;



bool conn_ok;

//use LED for indicating MQTT connection status.
int ledPin = 4;     //indicate lights on
int statusPin = 7;  //indicate MQTT conn status
//servo control
int servo1Pin = 3;  //front door
int servo2Pin = 5;  //garage door
int servo3Pin = 6;  //energy usage meter
Servo servo1;   //door
Servo servo2;   //garage door
Servo servo3;   //energy

//servo operation:  target positions for servos
int servo1_target = 5;  //door, closed
int servo2_target = 5;  //garage, closed
int servo3_target = 180;  //energy, 180 is "0" watts, motor mounted backwards
int servo1_pos_cur = 5;
int servo2_pos_cur = 5;
int servo3_pos_cur = 180; // 180 is "0" watts, motor mounted backwards
int servo1Opened = 5;
int servo1Closed = 120;
int servo2Opened = 5;
int servo2Closed = 150;
unsigned long servo1_time = 0;
unsigned long servo2_time = 0;
unsigned long servo3_time = 0;

//---------------------------------------------------------------
// MQTT call back
// react to MQTT messages here
//---------------------------------------------------------------
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.println("received MQTT");
  
  //convert topic to int.
  int mytopic = atoi (topic);
  //Serial.print(mytopic);

  //convert payload to int
  payload[length] = '\0';   //add a line return so atof can parse correctly
  float mymsg = atof( (const char *) payload);

  //print MQTT message
  Serial.println("");
  Serial.print("(");
  Serial.print(mytopic);
  Serial.print(", ");
  Serial.print(mymsg);
  Serial.println(")");
  
  //led
  if (mytopic == 2822)
  {
    if (mymsg == 1)
    {
      digitalWrite(ledPin, HIGH);
    }
    if (mymsg == 0)
    {
      digitalWrite(ledPin, LOW);
    }
  }

  
  //servo 1, front door
  if (mytopic == 2832)
  {
    if (mymsg == 1) //opened
    {
      Serial.println("servo1 opened");
      servo1_target = servo1Opened;
    }
    if (mymsg == 0)  //closed
    {
      Serial.println("servo1 closed");
      servo1_target = servo1Closed;
    }
    Serial.println(servo1_target);
  }


  
  /*
  //second garage door signal

	"closed" = off = send 1
	"open" = on = send 0
  */
  //servo 2, garage door
  if (mytopic == 2842)
  {
    if (mymsg == 1) //opened
    {
      servo2_target = servo2Opened;
    }
    if (mymsg == 0)  //closed
    {
      servo2_target = servo2Closed;
    }
  }

  
  //servo 3, energy meter
  if (mytopic == 2852)
  {
    //message = watts
    //error check
    if (mymsg > 6000)
    {
      mymsg = 6000;
    }
    if (mymsg < 0)
    {
      mymsg = 0;
    }
    //bias meter from 180 degrees to 0 degrees
    //180 deg to 0 deg = 0 watt to 6000 watts, for example
    //set max watts to what makes sense for you.
    servo3_target = 180-((1-((6000-mymsg)/6000))*180);
    //servo3_target = mymsg;
  }
}//end callback


void setup() 
{
  //ethernet
  //Ethernet.begin(mac, ip);
  //Wire.begin (MY_ADDRESS);
  
  Serial.begin (9600);
  
  Serial.println("starting");
  
  pinMode(ledPin, OUTPUT);    //LED indicating light on
  pinMode(statusPin, OUTPUT);
  
  digitalWrite(ledPin, HIGH);
  //wait for IP address
  while (Ethernet.begin(mac) != 1)
  {
    Serial.println("Error getting IP address via DHCP, trying again...");
    delay(5000);
  }
  

  Serial.println("ethernet OK");
  keepalivetime=millis();
  


  while (client.connect("smarthouse") != 1)
  {
    Serial.println("Error connecting to MQTT");
    //delay(3000);
    delay(4000); 
  }
  MQTT_reconnect = millis();
  Serial.println("setup complete");
  client.publish("smarthouse","hello world");    //test publish
  //client.subscribe("#");   //test subscribe
  client.subscribe("2822");   //LED
  client.subscribe("2832");   //servo 1 topic
  client.subscribe("2842");   //servo 2 topic
  client.subscribe("2852");   //servo 2 topic
  
  digitalWrite(ledPin, LOW);
  
  //servo
  servo1.attach(servo1Pin);
  servo1.write(90);
  servo2.attach(servo2Pin);
  servo2.write(90);
  servo3.attach(servo3Pin);
  servo3.write(5);
}  // end of setup



void loop() 
{

  client.loop();  //MQTT processing needs this to run

  //For servo positioning, don't want to move too fast.
  //Must crawl from current position to target position
  //but also avoid using "delay" cmd, that just freezes up microcontroller
  
  //servo 1
  if ((millis() - servo1_time) > 20)
  {
    if (servo1_pos_cur > servo1_target)
    {
      servo1_pos_cur = servo1_pos_cur - 1;
    }
    if (servo1_pos_cur < servo1_target)
    {
      servo1_pos_cur = servo1_pos_cur + 1;
    }
    servo1.write(servo1_pos_cur);
    servo1_time = millis();
  } //end servo 1 target control
  
  
  //servo 2
  if ((millis() - servo2_time) > 20)
  {
    if (servo2_pos_cur > servo2_target)
    {
      servo2_pos_cur = servo2_pos_cur - 1;
    }
    if (servo2_pos_cur < servo2_target)
    {
      servo2_pos_cur = servo2_pos_cur + 1;
    }
    servo2.write(servo2_pos_cur);
    servo2_time = millis();
  } //end servo 2 target control


  //servo 3
  if ((millis() - servo3_time) > 20)
  {
    if (servo3_pos_cur > servo3_target)
    {
      servo3_pos_cur = servo3_pos_cur - 1;
    }
    if (servo3_pos_cur < servo3_target)
    {
      servo3_pos_cur = servo3_pos_cur + 1;
    }
    servo3.write(servo3_pos_cur);
    servo3_time = millis();
  } //end servo 3 target control

  
  //check network connection to MQTT broker every 60 seconds.
  //reconnect if no longer connected
  if ((millis() - MQTT_reconnect) > 60000)
  {
    conn_ok = client.connected();
    if (conn_ok==1)
    {
      digitalWrite(statusPin, HIGH);
      Serial.println("MQTT connected OK");
    }
    else
    {
      digitalWrite(statusPin, LOW);
      Serial.println("MQTT NOT connected OK");
    }

    //no connection, reconnect
    if (conn_ok == 0)
    {
      client.disconnect();
      delay(5000);
      while (client.connect("smarthouse") != 1)
      {
        digitalWrite(statusPin, LOW);
        Serial.println("Error connecting to MQTT");
        delay(4000);
      }
      digitalWrite(statusPin, HIGH);
    }
    //Serial.println("reconnected to MQTT");
    MQTT_reconnect = millis();
    client.publish("smarthouse","heartbeat every minute!"); 
  }//end Mosquitto connection check

}  // end of loop

energy_monitor_yun.ino

Plain text
/*
Eric Tsai
2015-04-22
Arduino Yun code for publishing energy use to MQTT broker
Modify the "PubSubClient client" for your broker IP address
*/


#include <Bridge.h>
#include <yunclient.h>
#include <PubSubClient.h>
#include "EmonLib.h"                   // OpenEnergy Monitor project library


int LED = 3;
EnergyMonitor emon1;                   // open energy monitor
unsigned long MQTT_reconnect=0;
unsigned long read_energy=0;
double Irms;
bool conn_ok;
YunClient yun;          //equivalent of ethernet client


//really shouldn't need call back, but just in case it's needed.
void callback(char* topic, byte* payload, unsigned int length) {
  digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);              // wait for a second
  digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW
}



//use yunclient to bridge to network
PubSubClient client("192.168.1.101", 1883, callback, yun);


//**********************************************************************
void setup()
{
    // initialize digital pin 13 as an output.
  pinMode(LED, OUTPUT);

  digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW

  Bridge.begin();

  client.connect("yun_energy");
  client.publish("watch", "Energy Monitor Connected!");
  client.subscribe("yun_energy");
  
  digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(3000);              // wait for a second
  digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW

  
  emon1.current(5, 60);             // Current: input pin, calibration.
}



//**********************************************************************
void loop()
{
  char buff_message[12];    //message buffer for MQTT publish
  float value;
  client.loop();    //run this every loop to maintain MQTT connection
  
  Irms = emon1.calcIrms(1480);  // Calculate Irms only, 1480 means read Irms.
  
  //publish current every 10 seconds.
  if (millis() - read_energy > 10000)
  {
    //amps
    value = Irms;
    dtostrf (value, 4, 5, buff_message);
    client.publish("2853", buff_message);
    read_energy = millis();
    
    //watts
    value = Irms*115;
    dtostrf (value, 4, 5, buff_message);
    client.publish("2852", buff_message);
  }

  
  //maintain MQTT connection
  if ((millis() - MQTT_reconnect) > 60000)
  {
    conn_ok = client.connected();
    if (conn_ok==1)
    {
      digitalWrite(LED, HIGH);
    }
    else
    {
      digitalWrite(LED, LOW);
    }
    
    //no connection, reconnect
    if (conn_ok == 0)
    {
      client.disconnect();
      delay(5000);
      while (client.connect("yun_energy") != 1)
      {
        delay(4000);
      }
      digitalWrite(LED, HIGH);
      client.publish("watch", "Energy Monitor reconnected");
    }
    
    MQTT_reconnect = millis();
  }//end Mosquitto connection check
  
}

Credits

Eric Tsai
6 projects • 161 followers
I'm interested in approaches to IoT projects that maximize the number of people who can make things.

Comments