Wednesday, 15 March 2017

Arduino Light Switch -- Making it WiFi (AKA Look maw, no PC)



This entry will detail my bridge step in making a WiFi to RF24 self contained bridge without a Pi or any other computer.  The final step will be to get rid of the RF24 and just have the WiFi module control the servo directly.


So my WiFi Shield (or in this case Shia1d) showed up today in the mail even though I wasn't expecting it for another month or so so I figured I would see what info I could find about it, and it didn't look good...

This $6 eBay special (ESP-12E ESP8266 UART WIFI Wireless Shield for Arduino UNO R3) had all sorts of tales of woe from lack of documentation to firmware issues to pins not connecting to the right place, but one site aggregated all the info and really helped me get this thing going.

These will be labeled as follows:
ESP8266 WiFi Sheild Version 1.0 by WangTongze
elecshop.ml
Designed in Beijjing P.R. China


First off, you need to know there are at least 4 different versions of the board and there are no promises which one you will get.

DIP 1 and 2 are _supposed_ to connect and disconnect the ESP from pins 0/1 (hardware serial) but they often DON'T DO ANYTHING...
DIP 2 and 3 are used to enable firmware flashing mode on the board on all revisions

The version I got is unstable running as a web server.  When placed in the right location I can get 0% packet loss (it is very picky about location) but I often don't get a page back. It does seem to always read my request and execute my code, so my lights always switch correctly but 40% of the time I don't get the web page back from it, which is no big deal for my current application but could be a deal breaker for someone trying to get status or other information from the device.
While I had the serial interface connected I would sometimes see TIMEOUT from the library talking to the ESP usually after a web request so this would probably explain it... You may be able to add some error checking in the sketch if it really bugs you, or just reload a few times and it will eventually work...

According to Claus, the new version (with the maintenance pins not populated, the word shield spelled correctly and a different shape on the antenna on the ESP module) provides a fast and stable web interface so try and get that version if you can.


To start you want to flash the latest firmware on the device, we will use the Uno as a TTL to serial converter. (You can use a TTL to serial cable if you want)

Put a jumper between RST (pin 3 on the power rail) and GND. This will keep the MCU out of the picture.

Connect your shield as follows (not plugged into the top of your Uno yet, but next to it).

Debug Port RX => Uno Pin 0 (Rx)
Debug Port TX => Uno Pin 1 (Tx)
Debug Port 5v => Uno 5v
Debug Port GND => Uno GND

The ESP is meant to use 3.3v logic but the Uno uses 5v.  You can use a level shifter if you want but I have yet to hear about anyone wrecking their device on 5v (me included on 2 different ones so far).  If you do happen to let the smoke though, don't blame me.

Open your serial monitor and set the baud to 115200... When you reset your ESP with the button it should show some garbage (the garbage is actually the boot loader at a different baud rate) and then say ready.

Set your terminal to CR & LF then send AT.  You should get an OK back from the ESP... If you don't make sure you didn't plug the ESP into the top of your Uno, and make sure you wires are secure...

Download this flasher utility from  http://www.xess.com/blog/esp8266-reflash/
And the Firmware from https://github.com/sleemanj/ESP8266_Simple/tree/master/firmware
You want the 1.1.1 Version as of this writing and the 115200 subset.  Make sure the file is about a meg (I got a bunk file somehow and it took me a while to figure out what was wrong)
1,044,480 ai-thinker-v1.1.1-115200.bin

Unplug your Uno and set dip switches 3 and 4 to ON on the shield, this will put your ESP into programming mode, then plug it back it... Make sure your serial monitor is closed.

Open the flash utility:
Click on Bin and locate the bin file you downloaded (don't bother with the included one in the zip)
Change the COM to the com port your Arduino is using (you can check this under Tools -> Port in the IDE)
You can leave the 0x0000000 defaults alone and hit Download...
It will say Erasing and then Writing, this process should take around a minute or so... When it gets to Leaving at the end it WILL return an error you can ignore this, it's normal.
Close the flasher, unplug your Arduino, turn switches 3 and 4 off and then plug er back in.

Open the serial monitor again and check your device boots still and that you can still get the OK.

If it doesn't give you the ready anymore, you can change the baud rate to 78 something and you can see the boot loader messages... Usually this means you flashed bad firmware... Check everything and then repeat the flashing steps, I don't think you can actually brick it... You can always flash the included firmware in the zip to see if you can flash working firmware on the device, but you need to update to the latest version still...

Finally since we have to use software serial we will need to slow the ESP down to 9600 BPS or we will  have communication issues.

In your terminal enter the following command: AT+UART_DEF=9600,8,1,0,0
This should return OK and will be stored in NVRam so you only need to do this once.  Reboot it and change your serial console to 9600 to make sure it took effect.

Phew that's finally over, now we can start the fun part!

Remove all the jumpers from the Uno side, and remove the wires from +5v and GND from the debug port.  We will need the RX and TX still though don't pull them off...

Now stick the shield onto your Uno.. The first pin on the power rail will hit the Reset pin on the Arduino, the first 2 aren't populated. The last pins should all line up properly.  Make sure you don't have any bent or missing pins..

Now plug the Debug RX into the pass through for pin 2 and the Debux TX into the pass through for pin 3.     Since we need the hardware serial for programming the Uno and Serial debug we will need to use software serial.

Note: If you have issues programming your Uno or using the serial console while the shield is plugged in make sure DIP 1 and 2 are off.. If it still won't work, try bending pins 0 and 1 so they don't plug into the Uno, this is a problem on some revisions.

Don't forget to switch your serial console back to 115200 at this point.

Grab the WiFiESP library from here and install the zip file (see my last post if you don't know how to download and install libraries)

Load the example code WiFiESP -> ScanNetworks
You will need to update the pin numbers with the following line: "SoftwareSerial Serial1(6,7)" to SoftwareSerial Serial1(3,2) -- The 3,2 being the only change!
Compile and run that and your ESP should come to life and show a list of networks in range and their power levels in the serial monitor.  If this doesn't work check your connections and make sure your PC is able to supply enough power (at least 350mA).

If all goes well, move on to the webserverled example... You need to put your SSID and password into the sketch before running it, then follow the instruction in the serial monitor to connect to your device with your web browser!

If all is well you should see the status and be given links to toggle LED 13 (you need to peek inside as this LED is covered by the shield).  Click the links and make sure the LED toggles and the status updates in your browser.  It may occasionally not load, just hit refresh... I think this just happens sometimes but it seems pretty rare if you have good power.

Great now for the "bridge" part of this project, we will connect the RF24 module like before and then run a modified version of the webserverled sketch to send out commands...

See my last post for detailed instructions on the RF24 but for quick reference:
Brown Wire -> GND
Red Wire -> 3.3v
Orange Wire -> Pin 7
Yellow Wire -> Pin 8
Blue Wire -> Pin 11
Purple Wire -> Pin 12
Green Wire -> Pin 13

Load up the following sketch substituting your SSID and password where requested...

Most of the code for the RF24 is copied from the previous sketch, see it if you want more comments..

----------------------Start Arduino Sketch----------------------------

/*
 WiFiEsp example: WebServerLed
 MODIFIED BY GUYFROMHE TO BE A CLIENT FOR RF24 LIGHT SWITCH
 A simple web server that lets you turn on and of an LED via a web page.
 This sketch will print the IP address of your ESP8266 module (once connected)
 to the Serial monitor. From there, you can open that address in a web browser
 to turn on and off the LED on pin 13.

 For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
*/

// RF24 to WiFi bridge

#include "WiFiEsp.h"

#include <SPI.h>
#include "RF24.h"

// Setup RF24
RF24 radio(7,8); // Stand Alone

bool radioNumber = 1; // Stand Alone
bool role = 1; // Stand alone
unsigned long cmd=255;                             // Do not send anything by default

byte addresses[][6] = {"1Node","2Node"};  // You can change the node names here if you'd like, I left them alone

// Emulate Serial1 on pins 3/2 if not present
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
SoftwareSerial Serial1(3, 2); // RX, TX
#endif

char ssid[] = "YOURSSID";            // your network SSID (name)
char pass[] = "YOUR-WIFI-PASSWORD";        // your network password
int status = WL_IDLE_STATUS;

//int ledStatus = LOW;

WiFiEspServer server(80);

// use a ring buffer to increase speed and reduce memory allocation
RingBuffer buf(8);

void setup()
{
  // Setup RF24
  radio.begin();
  // It's now really close to my device and I want to conserve power. If you need more range change to MAX
  radio.setPALevel(RF24_PA_MIN);
  radio.openWritingPipe(addresses[1]);
  radio.openReadingPipe(1,addresses[0]);

  radio.startListening();

  // WiFi Stuff
  //pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output.
  Serial.begin(115200);   // initialize serial for debugging
  Serial1.begin(9600);    // initialize serial for ESP module
  WiFi.init(&Serial1);    // initialize ESP module

  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }

  // attempt to connect to WiFi network
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }

  Serial.println("You're connected to the network");
  printWifiStatus();

  // start the web server on port 80
  server.begin();
}


void loop()
{
  WiFiEspClient client = server.available();  // listen for incoming clients

  if (client) {                               // if you get a client,
    Serial.println("New client");             // print a message out the serial port
    buf.init();                               // initialize the circular buffer
    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
        buf.push(c);                          // push it to the ring buffer

        // printing the stream to the serial monitor will slow down
        // the receiving of data from the ESP filling the serial buffer
        //Serial.write(c);
     
        // you got two newline characters in a row
        // that's the end of the HTTP request, so send a response
        if (buf.endsWith("\r\n\r\n")) {
          sendHttpResponse(client);
          break;
        }

        // Check to see if the client request was "GET /H" or "GET /L":
        if (buf.endsWith("GET /1")) {
          Serial.println("Turn light ON");
          //ledStatus = HIGH;
       
          // Send RF24 command
          radio.stopListening();                                    // First, stop listening so we can talk.
          cmd = 1;
          Serial.println("Trying to send to radio");
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
       
       
          Serial.println("Thats it...");
          radio.startListening();                                    // Listen again

       
          //digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        }
        else if (buf.endsWith("GET /0")) {
          Serial.println("Turn light OFF");
       
          // Send RF24 command
          radio.stopListening();                                    // First, stop listening so we can talk.
          cmd = 0;
          Serial.println("Trying to send to radio");
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
       
       
          Serial.println("Thats it...");
          radio.startListening();                                    // Listen again

          //ledStatus = LOW;
          //digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        }
        else if (buf.endsWith("GET /T")) {
          Serial.println("Toggle Light");
       
          // Send RF24 command
          radio.stopListening();                                    // First, stop listening so we can talk.
          cmd = 2;
          Serial.println("Trying to send to radio");
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
       
       
          Serial.println("Thats it...");
          radio.startListening();                                    // Listen again

          //ledStatus = LOW;
          //digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        }
      }
    }
 
    // close the connection
    client.stop();
    Serial.println("Client disconnected");
  }
}


void sendHttpResponse(WiFiEspClient client)
{
  // 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:text/html");
  client.println();

  // the content of the HTTP response follows the header:
  client.print("<b>Office Lights Control</b>");
  //client.print("The LED is ");
  //client.print(ledStatus);
  client.println("<br>");
  client.println("<br>");

  client.println("Click <a href=\"/1\">here</a> turn the lights on<br>");
  client.println("Click <a href=\"/0\">here</a> turn the lights off<br>");
  client.println("Click <a href=\"/T\">here</a> toggle the lights<br>");

  // The HTTP response ends with another blank line:
  client.println();
}

void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print where to go in the browser
  Serial.println();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
  Serial.println();
}
------------------------------ END SKETCH --------------------------------

Upload the sketch and open the serial monitor... If your Uno starts disconnecting from USB you aren't providing enough power to keep it running.. You can probaly move it to a plug or USB charger now though assuming it's working and you have written down it's IP.

You can now send commands by browsing to your devices IP and clicking the links or accessing /1 /2 or /T URLs.   This should in turn control your servo half of the project without any changes.

You should get the following page in your browser:

The strings are obvious in the sketch, feel free to make them your own!!

Though the browser works fine, it will make spurious requests (like favicon) and send a ton of extra data which can slow down and overwhelm your little ESP so it's not ideal to access it this way all the time.. For best results use the script below...

Now all that's left is a super simple Python script to control the lights from any machine on your network for easy integration into your automation system.  


---------------------Start remote.py ----------------------------------
import urllib2, sys

ip = "192.168.0.xxx"     # This is the IP of your ESP

cmd = sys.argv[1]

out = ""
if cmd == "1": out = "1"
if cmd == "0": out = "0"
if cmd.lower() == "t": out = "T"


try:
x = urllib2.urlopen('http://%s/%s' % (ip,out), timeout=1).read()
print x
except:
try:
print "Retrying..."
x = urllib2.urlopen('http://%s/%s' % (ip,out), timeout=1).read()
print x
except:
pass

-----------------------End-----------------------------------------

Then just run python remote.py 0 for off, python remote.py 1 for on and python remote.py t for toggle.

Let me know if you have any questions or found this information helpful in the comments.

No comments:

Post a Comment