Dr. Charif Mahmoudi
Published © GPL3+

Universal Remote Control with Alexa and IR

I hate looking where is the remote controller or grab my phone to control my TV or other IR devices. Alexa and hacking saved my day!

IntermediateFull instructions provided4 hours9,518
Universal Remote Control with Alexa and IR

Things used in this project

Story

Read more

Schematics

top

picture of the top

IR

IR led connection

IR scematic

Code

Lambda

JavaScript
The lambda code. Don't forget to change the credentials marked by XXXXXXXXXXXX
var mqtt = require('mqtt'),
    my_topic_name = 'charif/f/onoff';

var client = {
    'connected': false
};;
exports.handler = function (request, context) {
    if (request.directive.header.namespace === 'Alexa.Discovery' && request.directive.header.name === 'Discover') {
        log("DEBUG:", "Discover request",  JSON.stringify(request));
        handleDiscovery(request, context, "");
    }
    else if (request.directive.header.namespace === 'Alexa.PowerController') {
        if (request.directive.header.name === 'TurnOn' || request.directive.header.name === 'TurnOff') {
            log("DEBUG:", "TurnOn or TurnOff Request", JSON.stringify(request));
            handlePowerControl(request, context);
        }
    }
    else{
        log("DEBUG:", "UNKNOWN Request", JSON.stringify(request));
    }







    function handleDiscovery(request, context) {
        var payload = {
            "endpoints":
                [
                    {
                        "endpointId": "ir_adapter",
                        "manufacturerName": "Charif Mahmoudi",
                        "friendlyName": "TV",
                        "description": "Smart Switch for TVs",
                        "displayCategories": ["SWITCH"],
                        "cookie": {
                            "onoff": "supported",
                            "sources": "not yet"
                        },
                        "capabilities":
                            [
                                {
                                    "type": "AlexaInterface",
                                    "interface": "Alexa",
                                    "version": "3"
                                },
                                {
                                    "interface": "Alexa.PowerController",
                                    "version": "3",
                                    "type": "AlexaInterface",
                                    "properties": {
                                        "supported": [
                                            {
                                                "name": "powerState"
                                            }
                                        ],
                                        "retrievable": false,
                                        "proactivelyReported": false
                                    }
                                }
                            ]
                    }
                ]
        };
        var header = request.directive.header;
        header.name = "Discover.Response";
        log("DEBUG", "Discovery Response: ", JSON.stringify({ header: header, payload: payload }));
        context.succeed({ event: { header: header, payload: payload } });
    }

    function log(message, message1, message2) {
        console.log(message + message1 + message2);
    }

    function handlePowerControl(request, context) {
        // get device ID passed in during discovery
        var requestMethod = request.directive.header.name;
        // get user token pass in request
        var requestToken = request.directive.endpoint.scope.token;
        var powerResult;


        if (requestMethod === "TurnOn") {

            // Make the call to your device cloud for control
            // powerResult = stubControlFunctionToYourCloud(endpointId, token, request);
            powerResult = "ON";

        }
        else if (requestMethod === "TurnOff") {
            // Make the call to your device cloud for control and check for success
            // powerResult = stubControlFunctionToYourCloud(endpointId, token, request);
            powerResult = "OFF";
        }
        var contextResult = {
            "properties": [{
                "namespace": "Alexa.PowerController",
                "name": "powerState",
                "value": powerResult,
                "timeOfSample": "2017-09-03T16:20:50.52Z", //retrieve from result.
                "uncertaintyInMilliseconds": 50
            }]
        };
        var responseHeader = request.directive.header;
        var responseEndpont = request.directive.endpoint;
        responseHeader.namespace = "Alexa";
        responseHeader.name = "Response";
        responseHeader.messageId = responseHeader.messageId + "-R";
        var response = {
            context: contextResult,
            event: {
                header: responseHeader,
                endpoint: responseEndpont,
                payload: {}
            }

        };
        log("DEBUG", "Alexa.PowerController ", JSON.stringify(response));

        if (!client.connected){
            log("DEBUG", "Alexa.PowerController ", "MQTT NOT connected " );
            /*
            Tell lambda to stop when I issue the callback.
            This is super important or the lambda funciton will always go until it hits the timeout limit you set.
            */
            context.callbackWaitsForEmptyEventLoop = false;

            client = new mqtt.connect('mqtts://io.adafruit.com',{
                port: 8883,
                username: 'XXXXXXXXXXX',
                password: 'XXXXXXXXXXXXX'
            });
            client.on('connect', function () {
                log("DEBUG", "Alexa.PowerController ", "MQTT Connected ");
                log("DEBUG", "Alexa.PowerController ", "MQTT publishing " + requestMethod);
                context.callbackWaitsForEmptyEventLoop = true;
                client.publish(my_topic_name, requestMethod);
                context.succeed(response);
            });
            client.on('error', function () {
                log("DEBUG", "Alexa.PowerController ", "MQTT error ");
            });



        }else{
            log("DEBUG", "Alexa.PowerController ", "MQTT publishing " + requestMethod);
            context.callbackWaitsForEmptyEventLoop = true;
            client.publish(my_topic_name, requestMethod);
            context.succeed(response);
        }

    }
};

Arduino

Arduino
Cade for Adafruit HUZZAH ESP8266 but should work with arduino with few modifications
Credentials to change are marked by XXXXXXXXXXXX
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ESP8266WiFi.h>

#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "charif-2.4"
#define WLAN_PASS       "mahmoudi"

/************************* Adafruit.io Setup *********************************/

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "XXXXXXXXXXXXXXX"
#define AIO_KEY         "XXXXXXXXXXXXXXXXXXXXXX"

/************ Global State (you don't need to change this!) ******************/


uint16_t Samsung_power_toggle[71] = {
    38000, 1, 1, 170, 170, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20, 20, 20, 20,
    20, 20, 20, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
    20, 20, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 63, 20,
    20, 20, 63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 1798};

IRsend irsend(4);  // An IR LED is controlled by GPIO pin 4 (D2)

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);

/****************************** Feeds ***************************************/


// Setup a feed called 'onoff' for subscribing to changes to the button
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, "charif/f/onoff", MQTT_QOS_1);

/*************************** Sketch Code ************************************/




void onoffcallback(char *data, uint16_t len) {
  Serial.print("Hey we're in a onoff callback, the button value is: ");
  Serial.println(data);

   Serial.println("Toggling power");
#if SEND_GLOBALCACHE
  irsend.sendGC(Samsung_power_toggle, 71);
#else  // SEND_GLOBALCACHE
  Serial.println("Can't send because SEND_GLOBALCACHE has been disabled.");
#endif  // SEND_GLOBALCACHE
  digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH
  delay(1000);
   digitalWrite(LED_BUILTIN, LOW);
}


void setup() {

  irsend.begin();
   pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  delay(10);

  Serial.println(F("Adafruit MQTT IR Remote"));

  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: "); Serial.println(WiFi.localIP());

 
  onoffbutton.setCallback(onoffcallback);
  
  // Setup MQTT subscription for time feed.

  mqtt.subscribe(&onoffbutton);

}

uint32_t x=0;

void loop() {
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  // this is our 'wait for incoming subscription packets and callback em' busy subloop
  // try to spend your time here:
  //mqtt.processPackets(10000);
  
  // ping the server to keep the mqtt connection alive
  // NOT required if you are publishing once every KEEPALIVE seconds
  
  if(! mqtt.ping()) {
    Serial.println("Ping Failed, disconnecting ");
    mqtt.disconnect();
  }
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    //Serial.println("MQTT Already connected");
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 10 seconds...");
       mqtt.disconnect();
       delay(10000);  // wait 10 seconds
       retries--;
       if (retries == 0) {
         // basically die and wait for WDT to reset me
          Serial.println("MQTT connect failed");
          abort();
         //while (1);
       }
  }
  Serial.println("MQTT Connected!");
}

Credits

Dr. Charif Mahmoudi

Dr. Charif Mahmoudi

13 projects • 125 followers
PhD, Actually PostDoc. My areas of research are on distributed systems, cloud-computing, mobile computing, and IoT.

Comments