Md. Khairul Alam
Published © GPL3+

Smart Plug

Smart plug with remote monitoring (temperature, current, voltage, energy), control (automatic, manual, voice, proximity) and notification.

AdvancedFull instructions provided5 hours10,898
Smart Plug

Things used in this project

Hardware components

SparkFun ESP8266 Thing - Dev Board
SparkFun ESP8266 Thing - Dev Board
Any ESP8266 based module will work fine. I used SparkFun ESP8266 Thing Dev Board for development. Then I used a generic ESP8266-12E module to keep form factor small.
×1
DS18B20 Programmable Resolution 1-Wire Digital Thermometer
Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer
This is the latest DS18B20 1-Wire digital temperature sensor from Maxim IC. Reports degrees C with 9 to 12-bit precision, -55C to 125C (+/-0.5C).
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
This unit works great from 5 to 12V (datasheet shows 12V). You can also install a jumper wire past the 5V regulator on board to make this unit work at 3.3V. Sensor uses 1.6mA@3.3V.
×1
SparkFun Solid State Relay
You can also make your own solid state relay using Triac and Opto-isolated Triac driver. To know details follow the link: http://www.electronics-tutorials.ws/power/solid-state-relay.html
×1
SparkFun Low Current Sensor Breakout - ACS712
The ACS712 current sensor measures up to 5A of DC or AC current. SparkFun added an opamp gain stage for more sensitive current measurements. By adjusting the gain (from 4.27 to 47) we can measure very small currents.
×1
SparkFun USB Wall Charger - 5V, 1A
Any 5V AC to DC wall adapter will work well. Try to choose a small one so that you can easily integrate the circuit into the plug.
×1

Software apps and online services

Cayenne
myDevices Cayenne
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Mastech MS8217 Autorange Digital Multimeter
Digilent Mastech MS8217 Autorange Digital Multimeter
Access to a Computer
Access to a WiFi Network

Story

Read more

Schematics

Schematic (Smart Plug)

Code

Sketch

Arduino
Arduino sketch for ESP8266-12E without Alexa support.
/*
 * This code is for smart plug submitted in 
 * ESP8266 IoT Contest - Simplify the Connected World
 * Author: Md. Khairul Alam
 * Hackster: www.hackster.io/taifur
 * Date: 01 October, 2017
 * Under GPL licence
 * 
 */
 
//used for sending data to Cayenne cloud
#include <CayenneMQTTESP8266.h> 
// Include the libraries for DS18B20 Temeperature sensor
#include <OneWire.h>
#include <DallasTemperature.h>

//set up cayenne debug
#define CAYENNE_PRINT Serial
// Data types
#define TYPE_DIGITAL_SENSOR "digital_sensor" 
#define TYPE_VOLTAGE "voltage" 
#define TYPE_CURRENT "current" 
// Unit types
#define UNIT_DIGITAL "d" 
#define UNIT_VOLTS "v" 
#define UNIT_AMP "a" 

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

#define PLUG 5
#define pirPin 4

//variables
int timer_flag = 0;
int timer_time = 120;
int pir_flag = 0;
long timer_millis = 0;
int calibrationTime = 30;
long unsigned int lowIn;
long unsigned int pause = 60000;
boolean lockLow = true;
boolean takeLowTime;
int PIRValue = 0;
unsigned long lastMillis = 0;


int mVperAmp = 185; // use 100 for 20A Module and 66 for 30A Module
int RawValue= 0;
int ACSoffset = 500; //for esp8266 only
double Voltage = 0;
double current = 0;

float temp = 0;
// WiFi network info.
const char ssid[] = "taifur&mafi";
const char wifiPassword[] = "University";

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
const char username[] = "4f245760-6f09-11e7-8942-2bfc58a71315";
const char password[] = "b5f6214d8a77d9cb35410a81e7224ec189e3ce8c";
const char clientID[] = "1a8c0230-a1e7-11e7-98fe-97ee227ec16c";


void setup() {
	Serial.begin(9600);
  pinMode(PLUG, OUTPUT); 
  pinMode(pirPin, INPUT);
  //configure cayenne
	Cayenne.begin(username, password, clientID, ssid, wifiPassword);
  // Start up the temperature sensor library
  sensors.begin();
  delay(100);
  digitalWrite(PLUG, LOW); 
}

void loop() {
	Cayenne.loop();
  if(timer_flag){
    check_time();
    }
  if(pir_flag){
    PIRSensor();
    }
  read_temperature();
  int voltage = 220; //Standard voltage   
  read_current();
  
  float energy = 0.7 * voltage * current * millis()/(1000 * 60); //in kWh, assuming power factor 0.7

	if (millis() - lastMillis > 10000) {
		lastMillis = millis();
		//Write data to Cayenne here. This example just sends the current uptime in milliseconds.
	  Cayenne.virtualWrite(1, voltage, TYPE_VOLTAGE, UNIT_VOLTS);
    Cayenne.virtualWrite(2, current, TYPE_CURRENT, UNIT_AMP);
    Cayenne.virtualWrite(3, temp, TYPE_TEMPERATURE, UNIT_CELSIUS);
		Cayenne.virtualWrite(4, energy, TYPE_ENERGY, UNIT_KW_PER_H);  
	} 
}

CAYENNE_IN(5) //data receive from channel 5 (on/off) switch
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   digitalWrite(PLUG, value); //change valve state
   Cayenne.virtualWrite(7, value, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);

}

CAYENNE_IN(6) //data receive from channel 6 on/off time value
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   timer_time = value;
   
}

CAYENNE_IN(8) //data receive from channel 6 on/off proximity
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   pir_flag = value;
   
}

CAYENNE_IN(10) //data receive from channel 6 on/off timer enable
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   timer_flag = value;
   timer_millis = millis();   
}

//Default function for processing actuator commands from the Cayenne Dashboard.
//You can also use functions for specific channels, e.g CAYENNE_IN(1) for channel 1 commands.
CAYENNE_IN_DEFAULT()
{
	CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString());
	//Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");
}

void read_temperature(){
  // call sensors.requestTemperatures() to issue a global temperature 
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  // After we got the temperatures, we can print them here.
  // We use the function ByIndex, and as an example get the temperature from the first sensor only.
  Serial.print("Temperature for the device 1 (index 0) is: ");
  temp = sensors.getTempCByIndex(0);
  Serial.println(temp); 
}

void read_current(){
  for(int i = 0; i < 1000; i++) { // 1000 analogue readings for averaging
      RawValue = RawValue + analogRead(A0); // add each A/D reading to a total
  }
  Voltage = ((RawValue / 1000) / 1023.0) * 1000; // Gets you mV, max is 1000mV
  current = ((Voltage - ACSoffset) / mVperAmp);
}

void check_time() {
  if(((millis()-timer_millis)/60000) > timer_time){
    digitalWrite(PLUG, !digitalRead(PLUG));
    Cayenne.virtualWrite(7, !digitalRead(PLUG), TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
    timer_flag = 0;
    }
 }

void PIRSensor() {
   if(digitalRead(pirPin) == HIGH) {
      if(lockLow) {
         PIRValue = 1;
         lockLow = false;
         Serial.println("Motion detected.");
         digitalWrite(PLUG, HIGH);
         Cayenne.virtualWrite(7, 1, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
         delay(50);
      }
      takeLowTime = true;
   }
   if(digitalRead(pirPin) == LOW) {
      if(takeLowTime){
         lowIn = millis();takeLowTime = false;
      }
      if(!lockLow && millis() - lowIn > pause) {
         PIRValue = 0;
         lockLow = true;
         Serial.println("Motion ended.");
         digitalWrite(PLUG, LOW);
         Cayenne.virtualWrite(7, 0, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
         delay(50);
      }
   }
}

Sketch-with-Alexa

Arduino
Arduino Sketch with Alexa Support (not completely stable)
/*
 * This code is for smart plug submitted in 
 * ESP8266 IoT Contest - Simplify the Connected World
 * Author: Md. Khairul Alam
 * Hackster: www.hackster.io/taifur
 * Date: 01 October, 2017
 * Under GPL licence
 * 
 */
 
//used for sending data to Cayenne cloud
#include <CayenneMQTTESP8266.h> 
// Include the libraries for DS18B20 Temeperature sensor
#include <OneWire.h>
#include <DallasTemperature.h>

//Enabling Alexa voice
#include "WemoSwitch.h"
#include "WemoManager.h"
#include "CallbackFunction.h"

WemoManager wemoManager;
WemoSwitch *light = NULL;

//set up cayenne debug
#define CAYENNE_PRINT Serial
// Data types
#define TYPE_DIGITAL_SENSOR "digital_sensor" 
#define TYPE_VOLTAGE "voltage" 
#define TYPE_CURRENT "current" 
// Unit types
#define UNIT_DIGITAL "d" 
#define UNIT_VOLTS "v" 
#define UNIT_AMP "a" 

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

#define PLUG 5
#define pirPin 4

//variables
int timer_flag = 0;
int timer_time = 120;
int pir_flag = 0;
long timer_millis = 0;
int calibrationTime = 30;
long unsigned int lowIn;
long unsigned int pause = 60000;
boolean lockLow = true;
boolean takeLowTime;
int PIRValue = 0;
unsigned long lastMillis = 0;


int mVperAmp = 185; // use 100 for 20A Module and 66 for 30A Module
int RawValue= 0;
int ACSoffset = 500; //for esp8266 only
double Voltage = 0;
double current = 0;

float temp = 0;
// WiFi network info.
const char ssid[] = "taifur&mafi";
const char wifiPassword[] = "University";

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
const char username[] = "4f245760-6f09-11e7-8942-2bfc58a71315";
const char password[] = "b5f6214d8a77d9cb35410a81e7224ec189e3ce8c";
const char clientID[] = "1a8c0230-a1e7-11e7-98fe-97ee227ec16c";


void setup() {
	Serial.begin(9600);
  pinMode(PLUG, OUTPUT); 
  pinMode(pirPin, INPUT);
  //configure cayenne
	Cayenne.begin(username, password, clientID, ssid, wifiPassword);
  // Start up the temperature sensor library
  sensors.begin();
  //start up wemo library
  wemoManager.begin();
  // Format: Alexa invocation name, local port no, on callback, off callback
  light = new WemoSwitch("Smart Plug", 80, plugOn, plugOff);
  wemoManager.addDevice(*light);
  
  delay(100);
  digitalWrite(PLUG, LOW); 
}

void loop() {
	Cayenne.loop();
  wemoManager.serverLoop();
  if(timer_flag){
    check_time();
    }
  if(pir_flag){
    PIRSensor();
    }
  read_temperature();
  int voltage = 220; //Standard voltage   
  read_current();
  
  float energy = 0.7 * voltage * current * millis()/(1000 * 60); //in kWh, assuming power factor 0.7

	if (millis() - lastMillis > 10000) {
		lastMillis = millis();
		//Write data to Cayenne here. This example just sends the current uptime in milliseconds.
	  Cayenne.virtualWrite(1, voltage, TYPE_VOLTAGE, UNIT_VOLTS);
    Cayenne.virtualWrite(2, current, TYPE_CURRENT, UNIT_AMP);
    Cayenne.virtualWrite(3, temp, TYPE_TEMPERATURE, UNIT_CELSIUS);
		Cayenne.virtualWrite(4, energy, TYPE_ENERGY, UNIT_KW_PER_H);  
	} 
}

CAYENNE_IN(5) //data receive from channel 5 (on/off) switch
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   digitalWrite(PLUG, value); //change valve state
   Cayenne.virtualWrite(7, value, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);

}

CAYENNE_IN(6) //data receive from channel 6 on/off time value
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   timer_time = value;
   
}

CAYENNE_IN(8) //data receive from channel 6 on/off proximity
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   pir_flag = value;
   
}

CAYENNE_IN(10) //data receive from channel 6 on/off timer enable
{
   int value = getValue.asInt(); //accept and convert value to Int
   Serial.println(value);
   timer_flag = value;
   timer_millis = millis();   
}

//Default function for processing actuator commands from the Cayenne Dashboard.
//You can also use functions for specific channels, e.g CAYENNE_IN(1) for channel 1 commands.
CAYENNE_IN_DEFAULT()
{
	CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString());
	//Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");
}

void read_temperature(){
  // call sensors.requestTemperatures() to issue a global temperature 
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  // After we got the temperatures, we can print them here.
  // We use the function ByIndex, and as an example get the temperature from the first sensor only.
  Serial.print("Temperature for the device 1 (index 0) is: ");
  temp = sensors.getTempCByIndex(0);
  Serial.println(temp); 
}

void read_current(){
  for(int i = 0; i < 1000; i++) { // 1000 analogue readings for averaging
      RawValue = RawValue + analogRead(A0); // add each A/D reading to a total
  }
  Voltage = ((RawValue / 1000) / 1023.0) * 1000; // Gets you mV, max is 1000mV
  current = ((Voltage - ACSoffset) / mVperAmp);
}

void check_time() {
  if(((millis()-timer_millis)/60000) > timer_time){
    digitalWrite(PLUG, !digitalRead(PLUG));
    Cayenne.virtualWrite(7, !digitalRead(PLUG), TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
    timer_flag = 0;
    }
 }

void PIRSensor() {
   if(digitalRead(pirPin) == HIGH) {
      if(lockLow) {
         PIRValue = 1;
         lockLow = false;
         Serial.println("Motion detected.");
         digitalWrite(PLUG, HIGH);
         Cayenne.virtualWrite(7, 1, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
         delay(50);
      }
      takeLowTime = true;
   }
   if(digitalRead(pirPin) == LOW) {
      if(takeLowTime){
         lowIn = millis();takeLowTime = false;
      }
      if(!lockLow && millis() - lowIn > pause) {
         PIRValue = 0;
         lockLow = true;
         Serial.println("Motion ended.");
         digitalWrite(PLUG, LOW);
         Cayenne.virtualWrite(7, 0, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
         delay(50);
      }
   }
}

void plugOn() {
    Serial.print("Switch 1 turn on ...");
    digitalWrite(PLUG, HIGH);
    Cayenne.virtualWrite(7, 1, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
}

void plugOff() {
    Serial.print("Switch 1 turn off ...");
    digitalWrite(PLUG, LOW);
    Cayenne.virtualWrite(7, 0, TYPE_DIGITAL_SENSOR, UNIT_DIGITAL);
}

Credits

Md. Khairul Alam

Md. Khairul Alam

68 projects • 584 followers
Developer, Maker & Hardware Hacker. Currently working as a faculty at the University of Asia Pacific, Dhaka, Bangladesh.

Comments