Pages

Friday, May 12, 2017

Let there be light




After getting the Amazon Echo to activate my central heating system one nice feature that fell out of the development was the ability to voice activate a couple of power sockets I was using as ZWave repeaters. This opened up a really convenient way of turning lamps on and off and my husband urged me to buy some more sockets.


The trouble is, ZWave kit is expensive and at about £30 per socket I wasn't sure the expense would be worth it. On the plus side, more ZWave sockets means more repeaters which will add to the robustness of the ZWave network. But before going down that route I looked at some other options.

The great thing about the ZWay system is that it is not dedicated to ZWave. As long as you can create an HTTP connection to something it can be integrated into ZWay and appear as a device. And that device can either be operated from their SmartHome UI, added to my own Android app or operated via my newly written Amazon Alexa skill.

Alternative Alexa enabled lighting solutions

There are numerous 'smart lights' which will already work with Alexa and could probably be integrated into ZWay. This article has a good list. 

The most promising candidates looked like
  • TP Link
  • Philips Hue
But still not that cheap, so ZWave wasn't looking that bad. Could I go cheaper?

The DIY lighting solution


Then I came across these RF power sockets
There are many manufacturers of similar products (some cheaper alternatives) but what I liked about this set was that you get two remotes and other people had already successfully sniffed (and replicated) the RF codes.

What I needed to do was to replicate the RF remote with a WiFi enabled microcontroller which could connect to my ZWay hub and out to the internet:

Replicating the RF remote


For the microcontroller, I started with an Arduino as I already had one going spare.

There are two alternatives to transmitting the RF signal. The more brutal approach is to take the remote apart and hook up the switches directly to the Arduino. I expected this would be my solution and that is why the 2 remote system appealed - if I wrecked it I would still have a usable set of sockets.

The more subtle approach is to use the Arduino to drive a separate RF transmitter module and replicate the codes from the original remote. I already had a cheap 433MHz receiver / transmitter pair which I had bought on ebay. (I would need the receiver to 'sniff' the codes.)



Copying the codes would have been difficult to achieve from scratch but thanks to the excellent RCSwitch library and Pat's well written blog post I had the Arduino connected up and the codes pouring out in no time:

Decimal: 5313843 (24Bit) Binary: 010100010001010100110011
PulseLength: 182 microseconds Protocol: 1


Decimal: 5313852 (24Bit) Binary: 010100010001010100111100
PulseLength: 182 microseconds Protocol: 1

These codes are the on/off for just one socket. I added them to the RCSwitch sample transmit sketch, set the pulse length to 182 and hooked up the transmit module to my Arduino. Next minute, the socket was turning on and off as if by magic.

Getting internet ready


As the Arduino does not have any wifi capabilities built-in I needed some way of adding this functionality. The ESP8266 board is the way most people seem to go, but is apparently tricky to get working. This ESP8266 based board was used in the blog previously mentioned but I was not sure it was available in the UK and decided to opt for the Adafruit version instead. It is a little pricey, but I was wanting to test it out as I have other projects in mind that require the same type of board.

I also bought this console cable in order to connect the laptop to the Huzzah board (and power it). The board comes pre-loaded with Lua, but rather than learn something new I decided to stick with the Arduino IDE. I followed the instructions on the Adafruit website (Using Arduino IDE) and had the standard blink sketch up and running in minutes.

Next I connected up the RF transmitter module:




and combined the sample RCSwitch transmit code with the sample ESP8266WebServer code to receive HTTP requests of the form:

http://<local ip address>:8080/socket?id=<n>&state=on and
http://<local ip address>:8080/socket?id=<n>&state=off

(where <n> is a socket id from 1 to 5)

and to send out the appropriate RF codes previously captured.

First hack of code:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <RCSwitch.h>

const char* ssid = ".......";
const char* password = "..........";

// RF stuff
RCSwitch mySwitch = RCSwitch();

char * socketOn[5];
char * socketOff[5];

int transmitPin = 12;
int pulseLength = 182;
int repeat = 3;

ESP8266WebServer server(80);

const int led = 13;

void handleRoot() {
  digitalWrite(led, 1);
  server.send(200, "text/plain", "hello from esp8266!");
  digitalWrite(led, 0);
}

void handleSocket() {
  byte socketID=server.arg("id")[0];
  String state=server.arg("state");

  char * bitSequence = "";

  Serial.write("handle socket\n");
  Serial.write(socketID);
  Serial.write("\n");

  if (state == "on") {
    switch (socketID) {
      case '1': bitSequence = socketOn[0];
                break;    
      case '2': bitSequence = socketOn[1];
                break;
      case '3': bitSequence = socketOn[2];
                break;
      case '4': bitSequence = socketOn[3];
                break;
      case '5': bitSequence = socketOn[4];
                break;
      default: Serial.write("invalid socket num\n");
      }
  }
  else if (state == "off") {
    switch (socketID) {
      case '1': bitSequence = socketOff[0];
                break;    
      case '2': bitSequence = socketOff[1];
                break;
      case '3': bitSequence = socketOff[2];
                break;
      case '4': bitSequence = socketOff[3];
                break;
      case '5': bitSequence = socketOff[4];
                break;
      default: Serial.write("invalid socket num\n");
      }  
  }
  if (bitSequence != "")
      mySwitch.send(bitSequence);

  if (state == "on"){
    digitalWrite(13, LOW);
    Serial.write("socket on\n");
  }
  else if (state == "off") {
    digitalWrite(13, HIGH);
    Serial.write("socket off\n");
  }
  server.send(200, "text/plain", "Socket is now " + state);
}

void handleNotFound(){
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

void setup(void){
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");

  socketOn[0] = "010100010001010100110011";
  socketOff[0] = "010100010001010100111100";
  socketOn[1] = "010100010001010111000011";
  socketOff[1] = "010100010001010111001100";
  socketOn[2] = "010100010001011100000011";
  socketOff[2] = "010100010001011100001100";
  socketOn[3] = "010100010001110100000011";
  socketOff[3] = "010100010001110100001100";
  socketOn[4] = "010100010011010100000011";
  socketOff[4] = "010100010011010100001100";

  // Transmitter is connected to Arduino Pin #12
  mySwitch.enableTransmit(12);

  // Optional set pulse length.
  mySwitch.setPulseLength(182);

  // Optional set protocol (default is 1, will work for most outlets)
  // mySwitch.setProtocol(2);

  // Optional set number of transmission repetitions.
  mySwitch.setRepeatTransmit(15);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);

  server.on("/socket", handleSocket);

  server.on("/inline", [](){
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
}

This was tested from a web browser before finally adding each socket as an HTTP device to the ZWay SmartHome UI:




Once added, these were picked up automatically by my Alexa skill during device discovery:


And could then be voice activated:

Providing Power


This was all well and good, but the Huzzah board was still being powered via my laptop so I purchased this breadboard power supply module and found an old suitable ac/dc adapter to power it.

I connected it as follows:



I am using the 3.3V power option on the breakout board and connecting it to the Huzzah V+ pin (rather than VBat). According to the Adafruit website I think I could (and should) use the 5V option but I was not confident enough with electronics to try that. Besides everything ended up working ok at 3.3V.

As I was still in the development phase it was useful to be able to have the option to connect the console cable to a serial monitor. This works by just connecting GND, TX and RX (as shown above).



Final Thoughts


I think that the solution works pretty well for the price and will be a great addition to my project. The Huzzah board is excellent, but I'm going to give the SweetPeas alternative version a tryout too. I have many other mini projects in mind for these boards.

Sometimes the on/off command is missed at the RF connection. I suspect this is to do with the antenna not being quite up to the job. Hopefully this can be improved quite easily, but I think some re-transmissions would help.

Unlike the ZWave sockets there is no feedback at the protocol level to say whether a socket is on or off. If I am in the room then I can see the status, but if I want to put lamps on timers or operate remotely, then I think the ZWave sockets are a better bet.

Primarily, I will be using these sockets as a means of operating several lamps simultaneously in a single room. This can be done by grouping in the Alexa skill, but is probably better done at the RF level by programming all of the Etekcity sockets to obey the same code (this is an option available via buttons on the sides of the sockets).