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("");
  }
}

Laser Tag Project

Over the last couple of months, I have started development of a new microcontroller project in my spare time.

The aim of my project is to produce an inexpensive “Laser Tag” system that can be used to play multiplayer games. Of course, you can buy such systems from a toy shop, see here…

http://www.hamleys.com/laser-x-2-player-pack.ir

But this is a very simple “point score” implementation of the concept that does not allow for the creation of custom games such as “Capture the flag” or team based games.

My aim is to produce a system where a centralised server (think Raspberry Pi or a laptop) controls the game, all devices communicate with the game server over WiFi and the game devices (such as a tactical vest or gun) simply send messages to, or receive command messages from the main server.

This will allow for a great deal of flexibility in the game creation and should make for a very exciting platform that can be expanded upon.

I have a few criteria that must be met.

  • The hardware must be cheap
    (we are talking < £100 for enough equipment to run a five-person game)
  • It must be possible to write games without a knowledge of microcontrollers or electronics.
  • The game must perform well
  • The game should allow the possibility that it may be integrated with external systems and frameworks (such as a mobile application or Unity3D app)
  • Audio should be considered at some point during the implementation.

I am currently doing experiments with the ESP8266 microcontroller to assess it’s suitability for this project, it seems like a good candidate as it has Wifi, a number of GPIO pins and runs at a decent clock speed (up to 240Mhz under normal “warranty safe” conditions)

Specifically, I am looking at these areas at the moment;

  • Infrared send & receive
  • Wifi communication speed & latency via various techniques
    • Which one is most reliable, HTTP, UDP packets, MQTT?
    • Can a good message rate be sent to the microcontroller and is it dealt with in a timely fashion?
    • Can the microcontroller send messages to the central server at an adequate rate?

These questions are deliberately vague on the details so that I can explore these questions from a few different angles and hopefully come up with the best way of establishing “real-time” communication between the server and game devices.

I will write more as I work on this.

 

 

 

 

Hello

To introduce myself, I am Paul Maidment and I am a software engineer. By day, I write and maintain software in Java and sometimes PHP for a leading affiliate marketing company. In my leisure time I have diverse interests, including electronics and microcontrollers (at this time ESP8266, ESP32 and Atmel 328 devices) C#, Java, Unity3D, Virtual & Augmented reality, this list is by no means exhaustive.

I finally decided to start writing a blog so that I can share some of the things I am doing on social media and with my friends from the MakerSpace I am involved with.

My current project is “Laser Tag” powered by ESP8266 microcontrollers, it is very much at an early phase where I am deciding how I will build the platform from a high level, running experiments on breadboards and so on.

It is my intention to keep this blog up to date with information on different parts of the project as I work on them, hopefully this will be of interest to some readers.