ESP8266 & Google Assistant = Voice controlled remote

I just received my first batch of ESP8266 boards from China, having been playing around with a single unit that I bought on Ebay while I waited.

As I mentioned before, these boards are great candidates for the Laser Tag project due to their low cost (if you order from China, around about £2.40 for a board.)

They have Wifi on them, as well as a number of GPIO pins, making them ideal for Internet of Things (IOT) projects.

I decided to build a simple infrared remote control that could be operated by voice using Google Assistant. As the ESP8266 would have to run 24/7 and remain connected to a network, I figure that this could be a great way to discover (and eventually mitigate) any issues that may affect the reliability of the board when connected for long periods of time.

I have managed to make a simple prototype, with the infrared LED on Dupont wires. Essentially an adaption of this project.

http://www.instructables.com/id/Easiest-ESP8266-Learning-IR-Remote-Control-Via-WIF/

I used the wiring from this instructable, though instead of using the code suggested on this page, I made something that would work on the ESP8266 Arduino SDK instead. I have included a listing at the bottom of this page.

I then wrote a small REST layer to receive commands from the outside world (a simple PHP project, dependency managed by composer and using the Slim framework to serve up simple endpoints.) After making sure that all the right port forwards were in place to expose it on a public address from my home server, I was ready to wire it into the “Fulfilment” section of my Google Assistant app on DialogFlow.com

Discussion of building a DialogFlow app is a little long for this page, so I recommend that you simply check it out. You can sign up for a free developer account and simply link your Google account into it. Without too much trouble you can hook your application up to your Google Home to start giving voice commands.

Here is the code that I use to drive the hardware component of this, essentially a very simple REST server, it is based on the WiFi example from this post here https://randomnerdtutorials.com/esp8266-web-server/

The IR functionality is provided by a library based on the work of Ken Shirriff https://github.com/z3t0/Arduino-IRremote and adapted by Mark Szazbo to work on the ESP8266 https://github.com/markszabo/IRremoteESP8266

#include <ESP8266WiFi.h>
#include <Arduino.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRutils.h>

uint16_t SEND_PIN = 4;
uint16_t RECV_PIN = 5;
IRsend irsend(SEND_PIN);
IRrecv irrecv(RECV_PIN);
decode_results results;

// Replace with your network credentials
const char* ssid     = "Some_SSID";
const char* password = "xxxxxxxx";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

void setup() {
  Serial.begin(115200);
  irsend.begin();
  irrecv.enableIRIn(); 
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void sendPowerButtonSamsung()
{
  //Send a burst of signals just in case there is a transmission error
  Serial.println("Sending power off IR signal for Samsung TV");
  for (int i = 0; i < 4 ; i++) {   
    irsend.sendSAMSUNG(0xE0E040BF, 32);
  } 
}

void sendVolumeDownSamsung()
{
  //Send a burst of signals just in case there is a transmission error
  Serial.println("Sending volume down IR signal for LG TV");  
  irsend.sendSAMSUNG(0xE0E0D02F, 32);
}

void sendVolumeUpSamsung()
{
  //Send a burst of signals just in case there is a transmission error
  Serial.println("Sending volume down IR signal for LG TV");  
  irsend.sendSAMSUNG(0xE0E0E01F, 32);
}

void sendPowerButtonLG()
{
  //Send a burst of signals just in case there is a transmission error
  Serial.println("Sending power off IR signal for Samsung TV");
  for (int i = 0; i < 4 ; i++) {   
    irsend.sendNEC(0x20DF10EF, 32);
  } 
}

void sendVolumeDownLG()
{
  //Send a burst of signals just in case there is a transmission error
  Serial.println("Sending volume down IR signal for LG TV");  
  irsend.sendNEC(0x20DFC03F, 32);
}

void sendVolumeUpLG()
{
  //Send a burst of signals just in case there is a transmission error
  Serial.println("Sending volume down IR signal for LG TV");  
  irsend.sendNEC(0x20DF40BF, 32);
}

void loop(){
  
  if (irrecv.decode(&results)) {
    // print() & println() can't handle printing long longs. (uint64_t)
    serialPrintUint64(results.value, HEX);
    Serial.println("");
    irrecv.resume();  // Receive the next value
  }
  
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:application/json");
            client.println("Connection: close");
            client.println();
            
            // turns the GPIOs on and off
            if (header.indexOf("GET /samsung/power") >= 0) {
              Serial.println("Samsung power signal requested");
              client.println("{\"mode\": \"Samsung\",\"command\": \"power\", \"status\":\"OK\"}");
              sendPowerButtonSamsung();
            } else if (header.indexOf("GET /samsung/volume/down") >= 0) {
              Serial.println("LG volume down signal requested");
              client.println("{\"mode\": \"Samsung\",\"command\": \"volume down\", \"status\":\"OK\"}");
              sendVolumeDownSamsung();
            } else if (header.indexOf("GET /samsung/volume/up") >= 0) {
              Serial.println("LG volume up signal requested");
              client.println("{\"mode\": \"Samsung\",\"command\": \"volume up\", \"status\":\"OK\"}");
              sendVolumeUpSamsung();
            }  else if (header.indexOf("GET /lg/power") >= 0) {
              Serial.println("Samsung power signal requested");
              client.println("{\"mode\": \"LG\",\"command\": \"power\", \"status\":\"OK\"}");
              sendPowerButtonLG();
            } else if (header.indexOf("GET /lg/volume/down") >= 0) {
              Serial.println("LG volume down signal requested");
              client.println("{\"mode\": \"LG\",\"command\": \"volume down\", \"status\":\"OK\"}");
              sendVolumeDownLG();
            } else if (header.indexOf("GET /lg/volume/up") >= 0) {
              Serial.println("LG volume up signal requested");
              client.println("{\"mode\": \"LG\",\"command\": \"volume up\", \"status\":\"OK\"}");
              sendVolumeUpLG();
            }  else {
              client.println("\"status\":\"ERROR\", \"message\", \"unknown command\"}");
            }
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *