Ruchir Sharma
Published © GPL3+

Smart Switch Using Arduino and Alexa

Temperature sensor is used to control a switch (relay) to turn on or off the device.

IntermediateFull instructions provided2 hours15,000
Smart Switch Using Arduino and Alexa

Things used in this project

Hardware components

Relay (generic)
×1
DHT11 Temperature & Humidity Sensor (4 pins)
DHT11 Temperature & Humidity Sensor (4 pins)
×1
ESP8266 ESP-01
Espressif ESP8266 ESP-01
Either use NodeMCU or ESP with Arduino
×1
Arduino UNO
Arduino UNO
×1
4n 26 optocoupler
×1
Texas Instruments LM1117 voltage regulator
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Arduino_ESP

Used for programming the ESP

VUI Diagram

Arduino_Alexa

Used after uploading the program to the ESP

Code

TempHumswitch

Arduino
#include <dht.h>
#include <ArduinoJson.h> 
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <Hash.h>
// @@@@@@@@@@@@@@@ You only need to modify wi-fi and domain info @@@@@@@@@@@@@@@@@@@@
const char* ssid     = ""; //enter your ssid/ wi-fi(case sensitive) router name - 2.4 Ghz only
const char* password = "";     // enter ssid password (case sensitive)
char host[] = "iottempswitch.herokuapp.com"; //- better your Heroku domain name like  "iottempswitch.herokuapp.com" 
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define dht_dpin 5          // GPIO 5 (D1) OR for ESP change it to GPIO0
const int relayPin = 16;    // GPIO 16 (D0) OR change it to GPIO2
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
dht DHT;
int port = 80;
char path[] = "/ws"; 
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
DynamicJsonBuffer jsonBuffer; 
String currState;
int pingCount = 0;
String triggerName ="";
String triggerVal ="";
int triggerEnabled = 0;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { //uint8_t *


    switch(type) {
        case WStype_DISCONNECTED:
           Serial.println("Disconnected! ");
           Serial.println("Connecting...");
               webSocket.begin(host, port, path);
               webSocket.onEvent(webSocketEvent);
            break;
            
        case WStype_CONNECTED:
            {
             Serial.println("Connected! ");
          // send message to server when Connected
            webSocket.sendTXT("Connected");
            }
            break;
            
        case WStype_TEXT:
            Serial.println("Got data");
              //data = (char*)payload;
           processWebScoketRequest((char*)payload);
            break;
            
        case WStype_BIN:

            hexdump(payload, length);
            Serial.print("Got bin");
            // send data to server
            webSocket.sendBIN(payload, length);
            break;
    }

}

void setup() {
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    
    pinMode(relayPin, OUTPUT);
    
      for(uint8_t t = 4; t > 0; t--) {
          delay(1000);
      }
    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    
    //Serial.println(ssid);
    WiFiMulti.addAP(ssid, password);

    //WiFi.disconnect();
    while(WiFiMulti.run() != WL_CONNECTED) {
      Serial.print(".");
        delay(100);
    }
    Serial.println("Connected to wi-fi");
    webSocket.begin(host, port, path);
    webSocket.onEvent(webSocketEvent);

}

void loop() {
    getTemp();
    webSocket.loop();

    if (triggerEnabled==1){
      setTrigger(triggerName,triggerVal);
    }
    //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  //If you make change to delay make sure adjust the ping
    delay(2000);
	// make sure after every 40 seconds send a ping to Heroku
	//so it does not terminate the websocket connection
	//This is to keep the conncetion alive between ESP and Heroku
    	if (pingCount > 20) {
    		pingCount = 0;
    		webSocket.sendTXT("\"heartbeat\":\"keepalive\"");
    	}else {pingCount += 1;}
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
}

void processWebScoketRequest(String data){
            String jsonResponse = "{\"version\": \"1.0\",\"sessionAttributes\": {},\"response\": {\"outputSpeech\": {\"type\": \"PlainText\",\"text\": \"<text>\"},\"shouldEndSession\": true}}";
            JsonObject& root = jsonBuffer.parseObject(data);
            String query = root["query"];
            String message="";
            Serial.println(data);
            
            if(query == "cmd"){ //if query check state
                      String value = root["value"];  
                      Serial.println("Recieved command!");
                    if(value=="on"){
                      digitalWrite(relayPin, HIGH);
                      message = "{\"state\":\"ON\"}";
                      currState = "ON";
                    }else if (value=="off"){
                      digitalWrite(relayPin, LOW);
                      message = "{\"state\":\"OFF\"}";
                      currState = "OFF";
                    }else if (value=="deactivate"){
                      //deactivate trigger
                      triggerEnabled = 0;
                    }else{
                      String object = root["object"];
                      //set trigger for temp and humidity
                      triggerName = object;
                      triggerVal = value;
                      triggerEnabled = 1;
                      
                        
                    }
                    jsonResponse.replace("<text>", "It is done");
                  
            }else if(query == "?"){ //if command then execute   
              Serial.println("Recieved query!");
              int state = digitalRead(relayPin);
              String value = root["value"];
              //Serial.print("Value-->");
              //Serial.print(value);
                if(value=="switch"){
                 if(currState=="ON"){
                      message = "{\"state\":\"ON\"}";
                    }else{
                      message = "{\"state\":\"OFF\"}";
                    }
                }else if(value=="humidity"){
                  //response with current humidity DHT.humidity
                  Serial.println("Humidity response...");
                  jsonResponse.replace("<text>", "current humidity is " + String(DHT.humidity) + " percent");
                  
                }else if(value=="temperature"){  
                  //response with current temperature DHT.temperature /Celcius2Fahrenheit(DHT.temperature)
                  Serial.println("Temp response...");
                  jsonResponse.replace("<text>", "current temperature is " + String(Celcius2Fahrenheit(DHT.temperature))+ " fahrenheit");
                }
            }else{//can not recognized the command
              Serial.println("Command is not recognized!");
            }
            //jsonResponse.replace("<text>", "Garage door " + instance + " is " + message );
            Serial.print("Sending response back");
            Serial.println(jsonResponse);
                  // send message to server
                  webSocket.sendTXT(jsonResponse);
                  if(query == "cmd" || query == "?"){webSocket.sendTXT(jsonResponse);}
}

void setTrigger(String obj, String val){
  Serial.print("Trigger is set for ");
  Serial.print(val.toFloat());
  Serial.print(" ");
  Serial.print(triggerName);
  Serial.println("");
  
  if(String("fahrenheit") == obj){
    if(Celcius2Fahrenheit(DHT.temperature)>=val.toFloat()){
      Serial.println("Fahrenheit trigger on!");
        digitalWrite(relayPin, HIGH);
      }else{
        digitalWrite(relayPin, LOW);
        }
  }else if(String("celsius") == obj){
    //Celcius2Fahrenheit(DHT.temperature)
        if(DHT.temperature>=val.toFloat()){
          Serial.println("Celcius trigger on!");
            digitalWrite(relayPin, HIGH);
          }else{
            digitalWrite(relayPin, LOW);
          }
  }else{
    //DHT.humidity
        if(DHT.humidity>=val.toFloat()){
          Serial.println("Humidity trigger on!");
          digitalWrite(relayPin, HIGH);
          }else{digitalWrite(relayPin, LOW);
          }
  }
}


void getTemp(){
    DHT.read11(dht_dpin);
    Serial.print("Current humidity = ");
    Serial.print(DHT.humidity);
    Serial.print("%  ");
    Serial.print("temperature = ");
    Serial.print(DHT.temperature); 
    Serial.print("C  ");
    Serial.print(Celcius2Fahrenheit(DHT.temperature)); 
    Serial.println("F  ");
  delay(800);//Don't try to access too frequently... in theory
  //should be once per two seconds, fastest,
  //but seems to work after 0.8 second.
}

double Celcius2Fahrenheit(double celsius){return celsius * 9 / 5 + 32;}

app.py

Python
import websockets
import asyncio
import json
import time, os


class HttpWSSProtocol(websockets.WebSocketServerProtocol):
    rwebsocket = None
    rddata = None

    async def handler(self):
        try:
            #while True:
            request_line, headers = await websockets.http.read_message(self.reader)
            #print(headers)
            method, path, version = request_line[:-2].decode().split(None, 2)
            #print(self.reader)
        except Exception as e:
            #print(e.args)
            self.writer.close()
            self.ws_server.unregister(self)

            raise

        # TODO: Check headers etc. to see if we are to upgrade to WS.
        if path == '/ws':
            # HACK: Put the read data back, to continue with normal WS handling.
            self.reader.feed_data(bytes(request_line))
            self.reader.feed_data(headers.as_bytes().replace(b'\n', b'\r\n'))

            return await super(HttpWSSProtocol, self).handler()
        else:
            try:
                return await self.http_handler(method, path, version)
            except Exception as e:
                print(e)
            finally:

                self.writer.close()
                self.ws_server.unregister(self)


    async def http_handler(self, method, path, version):
        response = ''
        try :
            alexaRequest = self.reader._buffer.decode('utf-8')
            #print("Req-->"+alexaRequest)
            RequestJson = json.loads(alexaRequest)['request']['intent']['slots']

            if 'is' not in RequestJson['query'].values() and 'what' not in RequestJson['query'].values():

                print('cmd')
                if 'value' not in RequestJson['Switch_State'].keys():
                    value = RequestJson['Numbers']['value']
                    obj = RequestJson['tmp_scale']['value']
                    print({"object":obj,"value":value,"query":"cmd"})
                    jsonRequest = {"object": obj.lower(), "value": value, "query": "cmd"}
                else:
                    state = RequestJson['Switch_State']['value']
                    print(RequestJson['Switch_State']['value'])
                    print({"object": "switch", "value": state, "query": "cmd"})
                    jsonRequest = {"object": "switch", "value": state, "query": "cmd"}
            else:
                if 'value' in RequestJson['Sensor_Values'].keys():
                    if 'temperature' in RequestJson['Sensor_Values']['value']:
                        #print('What is the current temperature?')
                        print({"object": "temperature", "value": "temperature", "query": "?"})
                        jsonRequest = {"object": "temperature", "value": "temperature", "query": "?"}
                        # {"object":"humidity","query":"?"}
                    else:
                        print('What is the current humidity?')
                        # {"object":"humidity","query":"?"}
                        print({"object": "humidity", "value": "humidity", "query": "?"})
                        jsonRequest = {"object": "humidity", "value": "humidity", "query": "?"}
                else:
                    print('What is the switch state?')
                    # {"object":"switch","query":"?"}
                    print({"object": "switch", "value": "state", "query": "?"})
                    jsonRequest = {"object": "switch", "value": "state", "query": "?"}
            with open('data.json', 'w') as outfile:
                json.dump(json.dumps(jsonRequest), outfile)
                #await self.rwebsocket.send(alexaRequest)
            await self.rwebsocket.send(json.dumps(jsonRequest))

            # #wait for response and send it back to IFTTT
            self.rddata = await self.rwebsocket.recv()
            #
            #val ='{"version": "1.0","sessionAttributes": {},"response": {"outputSpeech": {"type": "PlainText","text": "It is done"},"shouldEndSession": true}}'
            response = '\r\n'.join([
                'HTTP/1.1 200 OK',
                'Content-Type: text/json',
                '',
                '' + self.rddata,
            ])
        except Exception as e:
            print(e)
        self.writer.write(response.encode())



def updateData(data):
    HttpWSSProtocol.rddata = data

async def ws_handler(websocket, path):
    game_name = 'g1'
    try:
        with open('data.json') as data_file:
            data = json.load(data_file)
        HttpWSSProtocol.rwebsocket = websocket
        await websocket.send(data)
        data ='{"empty":"empty"}'
        while True:
            data = await websocket.recv()
            updateData(data)
    except Exception as e:
        print(e)
    finally:
        print("")

def _read_ready(self):
    if self._conn_lost:
        return
    try:
        time.sleep(.10)
        data = self._sock.recv(self.max_size)
    except (BlockingIOError, InterruptedError):
        pass
    except Exception as exc:
        self._fatal_error(exc, 'Fatal read error on socket transport')
    else:
        if data:
            self._protocol.data_received(data)
        else:
            if self._loop.get_debug():
                print("%r received EOF")
            keep_open = self._protocol.eof_received()
            if keep_open:
                # We're keeping the connection open so the
                # protocol can write more, but we still can't
                # receive more, so remove the reader callback.
                self._loop._remove_reader(self._sock_fd)
            else:
                self.close()

asyncio.selector_events._SelectorSocketTransport._read_ready = _read_ready

port = int(os.getenv('PORT', 5687))#5687
start_server = websockets.serve(ws_handler, '', port, klass=HttpWSSProtocol)
# logger.info('Listening on port %d', port)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

app.json

JSON
{
  "name": "Alexa SmartSwitch Temp",
  "description": "Works with IOT devices over the firewall with Alexa skills",
  "repository": "https://github.com/ruchir1675/Smart-Switch/tree/master/Smart-Switch",
  "logo": "https://node-js-sample.herokuapp.com/node.png",
  "keywords": ["esp8266", "google home", "wihtout ifttt", "iot", "arduino", "Alexa Skills", "Temperature"]
}

data.json

JSON
"{\"object\": \"switch\", \"value\": \"on\", \"query\": \"cmd\"}"

Credits

Ruchir Sharma

Ruchir Sharma

12 projects • 182 followers

Comments