How To: DIY Home Automation with NodeMCU And Amazon Alexa

Tutorial Type:

Tutorial Difficulty

Parts Required

Part Description

Amazon.com

Amazon Alexa Device Amazon Echo Dot
NodeMCU Development Board NodeMCU V1.0
4-Channel Relay Board SainSmart 4-Channel Relay Module
20x4 I2C Character LCD SainSmart IIC/I2C/TWI 20x4 LCD 
Dupont Style Jumper Wires Haitronic 120pc Dupont Jumper Wires
Bread Board Qunqi 830 tie point & 400 tie point Breadboard Kit

 

 

   

 

 

 

Tools Required

Part Description

URL

Arduino IDE Download the latest version of Arduino here
WeMos Source Code Download my version of the FauxMos source code here
NodeMCU Documentation Visit the NodeMCU project homepage here

 

 

   

If you read my last tutorial on DIY Home Automation with the LinkNode R4 and Amazon Alexa you already know the jist of this tutorial, but instead of using a purpose built board this time, I am going to walk you through the steps to accomplish the same thing using a NodeMCU and a 4-channel relay board. As with every home automation tutorial here at The Makers Workbench, the purpose of both of these tutorials is not to show you how to fully integrate the system into your home's infrastructure, but to give you a starting point of how to control Mains AC powered devices using low-voltage DC devices. 

What is a Node MCU? 

NodeMCU is an open source IoT platform that includes firmware which runs on the ESP8266 Wi-Fi SoC from Espressif Systems, and hardware which is based on the ESP-12 module. By default the NodeMCU  firmware uses the Lua scripting language which is based on the eLua project, and built on the Espressif Non-OS SDK for ESP8266. It uses many open source projects, such as lua-cjson, and spiffs.

 

NodeMCU + Arduino IDE Home Automation

As Arduino.cc began developing new MCU boards based on non-AVR processors like the ARM/SAM MCU and used in the Arduino Due, they needed to modify the Arduino IDE so that it would be relatively easy to change the IDE to support alternate tool chains to allow Arduino C/C++ to be compiled down to these new processors. They did this with the introduction of the Board Manager and the SAM Core. A "core" is the collection of software components required by the Board Manager and the Arduino IDE to compile an Arduino C/C++ source file down to the target MCU's machine language. Several creative ESP8266 enthusiasts have developed an Arduino core for the ESP8266 WiFi SoC that is available at the GitHub ESP8266 Core webpage. This is what is popularly called the "ESP8266 Core for the Arduino IDE" and it has become one of the leading software development platforms for the various ESP8266 based modules and development boards, including NodeMCUs.

If you have not read my previous post on this subject, to get started using a NodeMCU you will need to perform a few task in the Arduino IDE to be able to begin programming your NodeMCU with the Arduino IDE. Follow the steps below to get the Arduino IDE set up to work with the ESP8266 and NodeMCU. 

Follow the steps below to download the Arduino IDE, and prepare it to program the ESP8266. 

1. Download the latest version of the Arduino IDE here.

2. Once Arduino has been installed, run the program, and navigate to File > Preferences

3. Enter this URL “http://arduino.esp8266.com/stable/package_esp8266com_index.json” into the Additional Board Manager URLs field.

4. Now open the Boards manager that is located in Tools > Board Menu > Boards Manager

5. Then search and install the esp8266 platform (and don't forget to select your ESP8266 board from Tools --> Board menu after installation).

6. Finally, hover over the Tools menu and go to Tools ---> Board ---> and select the  NodeMCU 0.9 (ESP-12 Module) board if you are using a NodeMCU V0.9 board, or the NodeMCU 1.0 (ESP-12 Module) board if you are using a NodeMCU V1.0 board. 

The Schematic

The schematic for this project is fairly simple and about as straightforward as it can be. Follow the diagram above along with the pin to pin connections listed below to duplicate my setup. Note that the 20x4 Character LCD I am using has the I2C backpack already soldered on. If you wish to use a separate LCD and I2C backpack, follow the pinouts on your specific LCD and I2C backpack to ensure that they are connected properly. 

 

I2C Backpack to NodeMCU Connections

  • I2C Backpack GND Pin to NodeMCU GND Pin
  • I2C Backpack VSS Pin to NodeMCU 5Vin / 5V Pin
  • I2C Backpack SCL Pin to NodeMCU D1 Pin
  • I2C Backpack SDA Pin to NodeMCU D2 Pin

NodeMCU to 4-Channel Relay Board Connections

  • Relay Board GND Pin to NodeMCU GND Pin
  • Relay Board VCC Pin to NodeMCU 5Vin / 5V Pin
  • Relay Board Relay Pin 1 to NodeMCU Pin D5 / GPIO14
  • Relay Board Relay Pin 2 to NodeMCU Pin D8 / GPIO15
  • Relay Board Relay Pin 3 to NodeMCU Pin D9 / GPIO03
  • Relay Board Relay Pin 4 to NodeMCU Pin D10 / GPIO01

The Code

I will not be sharing all of the files needed to get the NodeMCU up and running with Amazon Alexa here as it would be a lot of code. Please visit my GitHub to download a zip archive of all of the necessary files needed. Note that the code below will not compile, or upload without these additional files, so head over to my github to grab them all

I will share the main Arduino sketch that I used so that you can have an idea of how everything ties together. I won't break down the entire code, but I will point out that I am creating four callbacks with unique names, four switches with unique names, and four integers which define the relays. I am also defining four unique invocation names for Alexa to understand, which are mapped to separate ports on the web server, and each unique invocation name has two settings, On and Off. 

Finally, I set the pinmode for each of the four relay pins as an output. Then it is as simple as setting up eight different functions for each relay. With the type of relay board I am using, I also have to set all four of the relay pins to a HIGH state in the setup code. This is necessary due to the way the coils are wired inside the relays that are used on the board. A HIGH signal turns the relays off, and a low signal turns them on. 

For example, when Alexa hears the “Turn on lightOne” command, the ESP8266 on the NodeMCU runs the lightOneOn function.  There is a lot more that goes on with the code that handles the FauxMo Belkin WeMo emulation, and to be quite honest, I am not fully sure that I understand every aspect of it just yet. I do know that it works, and adding new devices (up to 14 is supported by WeMo and Alexa) is as easy as duplicating the things I mentioned above, and giving each new device a unique name. So without further ado, here is the Arduino sketch that makes the magic you saw in the video above work. Remember that this code is useless without the other five files that can be found on my Github repo for this project. 

The LCD code and LCD hardware are completely optional and neither is required for this project to work. I included an LCD in the project for debugging / visual aid purposes only. If you do not wish to use an LCD in your project, simply comment out the LCD related code or remove it from the .ino file completely. 

Note: If you run into any issues with the code not compiling, make sure that the full set of files, six in total, are opened in the Arduino IDE, and that the folder they are stored in is named exactly like the main .ino file. Some users have experienced issues due to the folder name and the .ino file name being different. 

 

/******************************************
   The unmodified version of this code is originally
   by kakopappa and can be found at http://bit.ly/2kKQiRg.

   This version of the code has been modified by Charles Gantt
   and requires five additional files which 
   can be found at http://bit.ly/2lRDwAJ

   Find out more about this project on Charles' website
   http://www.themakersworkbench.com

   Follow Charles and TheMakersWorkbench on the following sites:
   YouTube: bit.ly/TMWB-on-YouTube
   TMWB on Facebook: bit.ly/TMWB-on-Facebook
   CJGanttMakes on Facebook: bit.ly/CJGanttMakes
   TMWB on Twitter: bit.ly/TMWB-on-Twitter
   Charles Gantt on Twitter: bit.ly/CJGanttOnTwitter
   Instructables: bit.ly/CJGanttOnInstructables
   TMWB Website: bit.ly/TheMakersWorkbench
   Charles Gantt on Element14: bit.ly/CJGantt-On-E14
   Charles Gantt on GitHub: bit.ly/CJGantt-on-Github
*/

#include "esp8266wifi.h"
#include "esp8266webserver.h"
#include "wifiudp.h"
#include "functional"
#include "switch.h"
#include "UpnpBroadcastResponder.h"
#include "CallbackFunction.h"
#include "wire.h"
#include "liquidcrystal_i2c.h"

// prototypes
boolean connectWifi();

//on/off callbacks
void lightOneOn();
void lightOneOff();
void lightTwoOn();
void lightTwoOff();
void outletOneOn();
void outletOneOff();
void outletTwoOn();
void outletTwoOff();

// Change this before you flash
const char* ssid = "Skynet";
const char* password = "8039795700";

boolean wifiConnected = false;

UpnpBroadcastResponder upnpBroadcastResponder;

Switch *lightOne = NULL;
Switch *lightTwo = NULL;
Switch *outletOne = NULL;
Switch *outletTwo = NULL;

// Set Relay Pins
int relayOne = 14;
int relayTwo = 15;
int relayThree = 03;
int relayFour = 01;

// Addr: 0x3F, 20 chars & 4 lines. 
//Sometimes display boards use address 0x27
LiquidCrystal_I2C lcd(0x3F, 4, 20); 

void setup()
{
  //Initalize LCD
  lcd.init();
  lcd.noBacklight();
  lcd.backlight();
  lcd.begin(20, 4);

  //Serial.begin(115200);

  // Initialise wifi connection
  wifiConnected = connectWifi();
  //Serial.print("WiFi Connected");

  if (wifiConnected) {
    upnpBroadcastResponder.beginUdpMulticast();

    // Show WiFi status on LCD along with SSID of network
    lcd.setCursor(0, 0);
    lcd.print("   WiFi Connected   ");
    lcd.print(ssid);

    // Define your switches here. Max 14
    // Format: 
    // Alexa invocation name, local port no, on callback, off callback
    lightOne = new Switch("Light One", 80, lightOneOn, lightOneOff);
    lightTwo = new Switch("Light Two", 81, lightTwoOn, lightTwoOff);
    outletOne = new Switch("Outlet One", 82, outletOneOn, outletOneOff);
    outletTwo = new Switch("Outlet Two", 83, outletTwoOn, outletTwoOff);

    //Serial.println("Adding switches upnp broadcast responder");
    upnpBroadcastResponder.addDevice(*lightOne);
    upnpBroadcastResponder.addDevice(*lightTwo);
    upnpBroadcastResponder.addDevice(*outletOne);
    upnpBroadcastResponder.addDevice(*outletTwo);

    //Set relay pins to outputs
    pinMode(14, OUTPUT);
    pinMode(15, OUTPUT);
    pinMode(03, OUTPUT);
    pinMode(01, OUTPUT);

    //Create Polling Message
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("   Polling Status   ");
    lcd.setCursor(0, 2);
    lcd.print("  Of Smart Devices  ");
    delay(2000);

    //Set each relay pin to HIGH this display status messagefor each relay
    digitalWrite(relayOne, HIGH);   // sets relayOne on
    lcd.clear();
    lcd.print("Light  One: Off     ");
    delay(500);
    digitalWrite(relayTwo, HIGH);   // sets relayOne on
    lcd.setCursor(0, 1);
    lcd.print("Light  Two: Off     ");
    delay(500);
    digitalWrite(relayThree, HIGH);   // sets relayOne on
    lcd.setCursor(0, 2);
    lcd.print("Outlet One: Off     ");
    delay(500);
    digitalWrite(relayFour, HIGH);   // sets relayOne on
    delay(500);
    lcd.setCursor(0, 3);
    lcd.print("Outlet Two: Off     ");

    //Create system initialized message
    lcd.clear();
    lcd.setCursor(0, 0);
    delay(1000);
    lcd.print(" System Initialzed  ");
    delay(1000);
    lcd.setCursor(0, 2);
    lcd.print(" Ready For Commands ");
    delay(2000);

    //Set up device status message
    lcd.clear();
    lcd.print("Light  One: Off     ");
    delay(500);
    lcd.setCursor(0, 1);
    lcd.print("Light  Two: Off     ");
    delay(500);
    lcd.setCursor(0, 2);
    lcd.print("Outlet One: Off     ");
    delay(500);
    lcd.setCursor(0, 3);
    lcd.print("Outlet Two: Off     ");


  }
}

void loop()
{
  if (wifiConnected) {
    upnpBroadcastResponder.serverLoop();
    lightOne->serverLoop();
    lightTwo->serverLoop();
    outletOne->serverLoop();
    outletTwo->serverLoop();
  }
}

void lightOneOn() {
  // Serial.print("Switch 1 turn on ...");
  digitalWrite(relayOne, LOW);   // sets relayOne on
  lcd.setCursor(0, 0);
  lcd.print("Light  One: On      ");
}

void lightOneOff() {
  // Serial.print("Switch 1 turn off ...");
  digitalWrite(relayOne, HIGH);   // sets relayOne off
  lcd.setCursor(0, 0);
  lcd.print("Light  One: Off     ");
}

void lightTwoOn() {
  // Serial.print("Switch 2 turn on ...");
  digitalWrite(relayThree, LOW);   // sets relayTwo on
  lcd.setCursor(0, 1);
  lcd.print("Light  Two: On      ");
}

void lightTwoOff() {
  // Serial.print("Switch 2 turn off ...");
  digitalWrite(relayThree, HIGH);   // sets relayTwo Off
  lcd.setCursor(0, 1);
  lcd.print("Light  Two: Off     ");
}

//sockets

void outletOneOn() {
  //  Serial.print("Socket 1 turn on ...");
  digitalWrite(relayFour, LOW);   // sets relayThree on
  lcd.setCursor(0, 2);
  lcd.print("Outlet One: On      ");
}

void outletOneOff() {
  // Serial.print("Socket 1 turn off ...");
  digitalWrite(relayFour, HIGH);   // sets relayThree off
  lcd.setCursor(0, 2);
  lcd.print("Outlet One: Off     ");
}

void outletTwoOn() {
  // Serial.print("Socket 2 turn on ...");
  digitalWrite(relayTwo, LOW);   // sets relayFour on
  lcd.setCursor(0, 3);
  lcd.print("Outlet Two: On      ");
}

void outletTwoOff() {
  // Serial.print("Socket 2 turn off ...");
  digitalWrite(relayTwo, HIGH);   // sets relayFour off
  lcd.setCursor(0, 3);
  lcd.print("Outlet Two: Off     ");
}

// connect to wifi – returns true if successful or false if not
boolean connectWifi() {
  boolean state = true;
  int i = 0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  // Serial.print("Connecting ...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    //Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }

  if (state) {
    //  Serial.println("");
    //  Serial.print("Connected to ");
    //  Serial.println(ssid);
    // Serial.print("IP address: ");
    //  Serial.println(WiFi.localIP());
  }
  else {
    // Serial.println("");
    //Serial.println("Connection failed.");
  }

  return state;
}

 

So that is going to wrap up this project for now. If you are interested in seeing more videos, and reading more tutorials / reviews like this, please head over to my YouTube Channel, and click on the subscribe button. Also remember to click the like button on the video above, and to leave me a comment on what you thought about this project. Stay tuned for more Amazon Alexa, and NodeMCU / ESP8266 related content. I have a whole series planned out, that will culminate in my entire office being automated. Watch the video above to see the project in action. 

Support The Makers Workbench on patreon and help us keep the dream alive!

If you would like more tutorials such as this, just let me know in the comments. Additionally, if you would like to see more tutorials like this, as well as the more of the other types of articles we post here at The Makers Workbench, head over to our Patreon page and consider donating monthly to help us keep the lights on. Every donation counts, and with your support, we can continue to post a couple of tutorials each week. 

 

 

Other Articles You Might Like

Review: Lulzbot AO-100 3D Printer part 1Lulzbot AO-100 / AO-101 reviewed

Tutorial: DIY Telescope to PC CableDIY Telescope to PC Cable

Project: LED Strip Bench LightingDIY RGB LED Strip Bench lighting

Review: ISO-TIP Portable  Soldering KitISO-Tip Portable Soldering Kit Review

Article: Arduino Due Overview

Project: 3D Printed Balloon Powered CarPrint a Balloon powered Jet Car with your 3D printer