Tuesday, 20 March 2018

IoT Relay/Switch using the ESP8266 NodeMCU ESP-12E Lua V3 and Arduino IDE

IoT Switch: From top: 5V relay PCB, Level Translator PCB and ESP-12E
As mentioned in an earlier post, lighting up a LED is very similar to controlling a relay. In place of the relay, we use a relay PCB with an optically-isolated input. I bought mine for RM9.99 from here.

5V opto-isolated Relay PCB
Since the ESP8266 ESP-12E is a 5V device, we need to translate its output to 5V. I am usually inclined to use a 1.8V zener diode like the TZS4678-GS08, costing some RM1.14. However an Arduino-type 4-channel  level converter is only RM1.35.

4-Channel Logic Level Converter
Unlike the resistor network or the zener diode method, this is an active circuit and uses the MOSFET BSS138:

There is a full explanation on the schematic here.

The wiring is quite straightforward:

ESP-12E    Level Converter   2-Channel Relay
3.3V           LV             HV        5V
GND          GND         GND      GND
D6              LV1           HV1      IN1
D7              LV2           HV2      IN2

Top wiring to ESP-12E, bottom wiring to 5V Relay PCB
The program, a Lua 'sketch' for the Arduino IDE, is much the same as the last post, tweaked a little to add a second channel. The only fiddle here is if we used D5, it seems to interfere with the programming of the ESP-12E via its USB serial port. If you do use D5, simply disconnect the level translator from the ESP-12E before you program it.

 *  This sketch demonstrates how to set up a simple HTTP-like server.
 *  The server will set a GPIO pin depending on the request
 *    http://server_ip:8080/gpio/0 will set the GPIO2 low,
 *    http://server_ip:8080/gpio/1 will set the GPIO2 high
 *  server_ip is the IP address of the ESP8266 module, will be
 *  printed to Serial when the module is connected.

static const uint8_t D0 = 16;
static const uint8_t D1 = 5;
static const uint8_t D2 = 4;
static const uint8_t D3 = 0;
static const uint8_t D4 = 2;
static const uint8_t D5 = 14;
static const uint8_t D6 = 12;
static const uint8_t D7 = 13;
static const uint8_t D8 = 15;
static const uint8_t D9 = 3;
static const uint8_t D10 = 1;

#include <ESP8266WiFi.h>

const char* ssid = "YourAcessPoint";
const char* password = "YourSecretPassword";

// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(8080);

void setup() {

  // prepare GPIO
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  digitalWrite(D6, HIGH);
  digitalWrite(D7, HIGH);
  Serial.println("Relays off");

  // Connect to WiFi network
  Serial.print("Connecting to ");

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
  Serial.println("WiFi connected");

  // Start the server
  Serial.println("Server started");

  // Print the IP address

int val = 1;
int val2 = 1;

void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {

  // Wait until the client sends some data
  Serial.println("new client");

  // Read the first line of the request
  String req = client.readStringUntil('\r');

  // Match the request
  String s = "";
  // Prepare the response
  Serial.print("Input string is ");
  if (req.indexOf("/D6/0") != -1) {
    val = 0;
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nD6 is now ";
    s += (val)?"high":"low";
    s += "</html>\n";
  else if (req.indexOf("/D6/1") != -1) {
    val = 1;
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nD6 is now ";
    s += (val)?"high":"low";
    s += "</html>\n";
  else if (req.indexOf("/D7/0") != -1) {
    val2 = 0;
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nD7 is now ";
    s += (val2)?"high":"low";
    s += "</html>\n";
  else if (req.indexOf("/D7/1") != -1) {
    val2 = 1;
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nD7 is now ";
    s += (val2)?"high":"low";
    s += "</html>\n";
  else if (req.indexOf("/index.html") != -1) {
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nHello, world.</html>\n";
  else if (req.indexOf("/favicon.ico") != -1) {
    Serial.println("End of command");

  else {
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nInvalid request: ";
    s += req;
    s += "</html>\n";
    Serial.println("invalid request");

  // Set GPIO2 according to the request
  if (val == 0)
    digitalWrite(D6, LOW);
  if (val == 1)
    digitalWrite(D6, HIGH);
  if (val2 == 0)
    digitalWrite(D7, LOW);
  if (val2 == 1)
    digitalWrite(D7, HIGH);


  // Send the response to the client
  Serial.println("Client disonnected");

  // The client will actually be disconnected
  // when the function returns and 'client' object is destroyed

As before, you turn on the first relay using:

And off using:

The commands for the second relay are:

The total cost of the parts was something like RM21.90 + RM9.99 + RM1.35 or RM33.24, not counting the 5V power supply and  the various bits of wires and connectors.

This is very similar to the Sonoff ITEAD IoT switch which sells for only RM30.84:

Sonoff ITEAD
There you have it: your DIY IoT switch actually sets you back some RM2.40. But as we shall see later, the Sonoff, among others, has some serious security problems, like the Krack exploit. Rolling our own IoT relay switch is the first step to taking control of our IoT security, rather than relying on the vendors' updates, both of the firmware and the accompanying app.

You can actually upgrade the Sonoff firmware yourself. Happy trails.  

No comments:

Post a Comment