Adding WiFi to the Industruino (ESP8266)

via Serial connection for PROTO and IND.I/O

Tom

Connecting the cheap ESP8266 WiFi module to an Industruino

The ESP8266 is a very popular WiFi SoC that can cost as little as US$3 in its simplest for, the ESP-01 package which we're using here. This is an 8-pin module with a PCB antenna, that comes with firmware allowing us to communicate with our MCU over a Serial connection, using AT commands.

Here we will show how to connect this WiFi module to the pins available on the Industruino's 14-pin IDC expansion port that is normally used to connect the Ethernet module. For both the PROTO and the IND.I/O the digital I/O pins on this IDC port are directly connected to the MCU.

The obvious choice is to use the MCU's extra hardware Serial, 'Serial1' on D0 and D1 for this purpose. This Serial1 is available on both the 32u4 and the 1286 MCU. However, on the IND.I/O baseboard, these 2 pins are connected to the RS485 port which also uses the Serial1. Unfortunately at this stage the only solution is to unsolder pin 5 on the ADUM1402 chip to disconnect D0 from the RS485. A good alternative is to use SoftwareSerial, see the example below. So to summarise:

  • PROTO baseboard: can use hardware Serial1 (D0/D1) OR SoftwareSerial
  • IND.I/O baseboard: should use SoftwareSerial (or unsolder 1 pin on the PCB if RS485 is not needed)

We only need to connect only 5 pins:

  • Vcc should be 3.3V and it is important to make sure enough current can be supplied (up to 1A, see below)
  • GND
  • TX to RX on the MCU
  • RX to TX on the MCU
  • RST is the reset pin and should also be pulled high; here we will connect it to a MCU pin.

CH_PD should be pulled high, e.g. with a pull-up resistor.

ESP8266 power supply

The ESP needs a lot of power to send data over WiFi, and while its I/O pins are 5V tolerant, Vcc should be around 3.3V.
The 3.3V of the MCU is not available on the IDC connector, but the PROTO baseboard has a 3.3V pin available on one of the green screw connectors.
It is possible to power the ESP module directly from this 3.3V pin; this may work depending on length of the HTTP requests and distance to the WiFi router. The left image shows the Vcc dipping to 1V, and with a large capacitor over 3.3V and GND, it still dips to 2V.
It is probably better to use a voltage regulator on the main 5V of the IDC connector, and bring it down to 3.3V e.g. using the MCP1826 which can deliver 1A. This is shown in the title picture above.
On the IND.I/O baseboard there is no 3.3V pin available so we have to use a voltage regulator on the 5V available on the IDC connector. The IND.I/O gets its 5V from an isolated DC/DC converter with a theoretical max current of 400mA but this seems not be critical; the voltage drops measured on the ESP's Vcc are small, even if the current goes up to 1A for a few milliseconds. 

Hardware Serial

Hardware Serial is available on the IDC expansion port: D0 as RX and D1 as TX. They can be connected straight to the ESP module, D0 to its TX and D1 to its RX.

Software Serial

Software Serial needs a pin with PCINT for its RX: an interrupt needs to be triggered when receiving data. On the 32u4 topboard, we can use pin 8, 9, 10, 11 or the SPI pins 14, 15, 16, same as on the standard Arduino Leonardo. On the 1286 topboard the pins 8, 9, 10, 11 do not have PCINT, so we are limited to the SPI pins on the IDC connector, any one of them:

  • D14 = MISO
  • D15 = SCK
  • D16 = MOSI
Please note that for the 1286 topboard, you need to update one of the board definition files: pins_arduino.h with the new version available on our github page.
For TX, we can use any available pin. In the example below we use D4.

The diagram on the left show a possible integration of the ESP8266 on a PROTO baseboard, as used in the Power Meter project, using the Hardware Serial on D0/D1, and D3 for Reset (can be any digital pin). The Power-Down (CH_PD) is optional (here on D2), it can just be pull-up with a resistor. As far as our experience goes, the ESP8266 GPIO pins seem to be 5V tolerant.

The example sketches below connect to a WiFi network and then send an HTTP GET request to a server, in this case http://openweathermap.org/ which has weather data. You need an API key to retrieve these data. The sketch then parses the reply to extract several data fields and display them on the LCD.

Example sketch with Hardware Serial

Below sketch works on PROTO with 32u4 and 1286 topboards, and also on IND.I/O with a small hardware modification (RS485)
/*
* INDUSTRUINO proto/indio with ESP8266 on IDC expansion port using HARDWARE SERIAL
* D0 => TX on ESP
* D1 => RX on ESP
* D3 => RST on ESP
* GND to GND
* Vcc to 3.3V (straight on PROTO) or with voltage regulator for increased stability

* PROTO works with hardware SERIAL1
* IND.I/O does not work because of RS485: only works when unsoldering pin 5 on Adum1402 isolator
*/

#include <UC1701.h>
static UC1701 lcd;

// WIFI VARIABLES
#define SSID "xx"
#define PASS "xx"
#define DST_IP "192.241.169.168"

void setup() {

  pinMode(3, OUTPUT);    // ESP reset pin
  digitalWrite(3, HIGH);

  Serial.begin(9600);
  //  while (!Serial);
  Serial1.begin(9600);

  analogWrite(13, 0);
  lcd.begin();  //sets the resolution of the LCD screen
  lcd.clear();
  lcd.setCursor(1, 2);
  lcd.print("hello Industruino!");
  lcd.setCursor(1, 3);
  lcd.print("wifi: HK weather");
  lcd.setCursor(1, 4);
  lcd.print("ESP8266 on Serial1");
}

void loop() {
  checkWeather();
  delay(3000);
}

boolean checkWeather()
{
  Serial1.println("AT+RST");
  delay(500);

  if (Serial1.find("Ready"))
  {
    Serial.print(F("module is ready.."));
  }
  else
  {
    Serial.println(F("module has no response."));
    return false;
  }

  //connect to the wifi
  boolean connected = false;

  for (int i = 0; i < 5; i++)
  {
    if (connectWiFi())
    {
      connected = true;
      break;
    }
  }
  if (!connected) {
    Serial.println(F("tried 5 times to connect, hard reset"));
    digitalWrite(3, LOW);
    delay(200);
    digitalWrite(3, HIGH);
    return false;
  }
  delay(500);

  //set the single connection mode
  Serial1.println("AT+CIPMUX=0");
  delay(500);

  String cmd;
  cmd = "AT+CIPSTART=\"TCP\",\"";
  cmd += DST_IP;
  cmd += "\",80";
  Serial1.println(cmd);

  if (Serial1.find("Error")) return false;

  String action;
  action = "GET /data/2.5/weather?id=1818209&units=metric&APPID=xxxxxx";
  action += " HTTP/1.0\r\n\r\n";

  Serial1.print("AT+CIPSEND=");
  Serial1.println(action.length());

  if (Serial1.find(">"))
  {
    Serial.print(">");
  } else
  {
    Serial1.println("AT+CIPCLOSE");
    Serial.println(F("connect timeout"));
    return false;
  }

  Serial1.println(action);
  Serial.println(action);

  lcd.clear();
  lcd.setCursor(1, 1);
  lcd.print("wifi: HK weather");

  if (Serial1.find("main\":\""))
  {
    String main = Serial1.readStringUntil('"');
    Serial.print("found main: ");
    Serial.println(main);
    lcd.setCursor(1, 3);
    lcd.print(main);
  }
  if (Serial1.find("temp\":"))
  {
    float temp = Serial1.parseFloat();
    Serial.print("found temp: ");
    Serial.println(temp);
    lcd.setCursor(1, 4);
    lcd.print(temp);
    lcd.print(" degC");
  }
  if (Serial1.find("pressure\":"))
  {
    int pres = Serial1.parseInt();
    Serial.print("found pressure: ");
    Serial.println(pres);
    lcd.setCursor(1, 5);
    lcd.print(pres);
    lcd.print(" hpa");
  }
  if (Serial1.find("humidity\":"))
  {
    int hum = Serial1.parseInt();
    Serial.print("found humidity: ");
    Serial.println(hum);
    lcd.setCursor(1, 6);
    lcd.print(hum);
    lcd.print("%");
  }
}

boolean connectWiFi() {
  Serial1.println("AT+CWMODE=1");
  delay(1000);
  String cmd = "AT+CWJAP=\"";
  cmd += SSID;
  cmd += "\",\"";
  cmd += PASS;
  cmd += "\"";
  // Serial.println(cmd);
  Serial1.println(cmd);
  delay(5000);

  if (Serial1.find("OK")) {
    Serial.print(F("connected to wifi.."));
    return true;
  } else
  {
    Serial.println(F("cannot connect to wifi"));
    return false;
  }
}

Example sketch with Software Serial

The standard Software Serial RX buffer is only 64 bytes, which is not sufficient for below sketch. It is easy to increase this to 256 by editing the SoftwareSerial.h library file. 
For the 32u4 topboard, this is the SoftwareSerial library that comes standard with the Arduino IDE, on my Linux laptop in folder /home/tom/arduino/arduino-1.6.5-r5/hardware/arduino/avr/libraries/SoftwareSerial/
For the 1286 topboard, the SoftwareSerial is part of the Industruino board file package, on my Linux laptop in folder /home/tom/arduino/arduino-1.6.5-r5/hardware/Industruino/avr/libraries/SoftwareSerial/
#define _SS_MAX_RX_BUFF 256 // RX buffer size  ***** CHANGED FROM 64 by TomT
Below example sketch works on all combinations of topboard/baseboards.
/*
 * INDUSTRUINO proto/indio with ESP8266 on IDC expansion port
 * using SoftwareSerial, works on INDIO and PROTO
 * D14/MISO => TX on ESP (works on both 32u4 and 1286; on 32u4 you can also use D10)
 * D4 => RX on ESP
 * D5 => RST on ESP
 * GND to GND
 * Vcc to 3.3V (straight on PROTO) or with voltage regulator for increased stability
 * 
// 32u4 can have for SoftwareSerial RX: 8,9,10,11,14,15,16 (because all have PCINT)
// 1286 can only use 14 miso, 15 sck, 16 mosi (no PCINT on others)
 */

#include <SoftwareSerial.h>                // need to update softwareserial.h to increase RX_buffer to 256 from 64
SoftwareSerial espSerial(14, 4); // RX, TX

#include <UC1701.h>
static UC1701 lcd;

// WIFI VARIABLES
#define SSID "xx"
#define PASS "xx"
#define DST_IP "192.241.169.168"

void setup() {

  pinMode(5, OUTPUT);    // ESP reset pin
  digitalWrite(5, LOW);  // ESP reset
  delay(200);
  digitalWrite(5, HIGH);
  
  Serial.begin(9600);
  //  while (!Serial);
  espSerial.begin(9600);

  analogWrite(13, 0);
  lcd.begin();  //sets the resolution of the LCD screen
  lcd.clear();
  lcd.setCursor(1, 2);
  lcd.print("hello Industruino!");
  lcd.setCursor(1, 3);
  lcd.print("wifi: HK weather");
  lcd.setCursor(1, 4);
  lcd.print("ESP8266 on softSerial");
}

void loop() {
  checkWeather();
  delay(3000);
}

boolean checkWeather()
{
  espSerial.println("AT+RST");
  delay(500);

//  while (espSerial.available()) {
//    Serial.write(espSerial.read());
//  }
  
  if (espSerial.find("Ready"))
  {
    Serial.print(F("module is ready.."));
  }
  else
  {
    Serial.println(F("module has no response."));
    return false;
  }

  //connect to the wifi
  boolean connected = false;

  for (int i = 0; i < 5; i++)
  {
    if (connectWiFi())
    {
      connected = true;
      break;
    }
  }
  if (!connected) {
    Serial.println(F("tried 5 times to connect, hard reset"));
    digitalWrite(5, LOW);
    delay(200);
    digitalWrite(5, HIGH);
    return false;
  }
  delay(500);

  //set the single connection mode
  espSerial.println("AT+CIPMUX=0");
  delay(500);

  String cmd;
  cmd = "AT+CIPSTART=\"TCP\",\"";
  cmd += DST_IP;
  cmd += "\",80";
  espSerial.println(cmd);

  if (espSerial.find("Error")) return false;

  String action;
  action = "GET /data/2.5/weather?id=1818209&units=metric&APPID=xxx";
  action += " HTTP/1.0\r\n\r\n";

  espSerial.print("AT+CIPSEND=");
  espSerial.println(action.length());

  if (espSerial.find(">"))
  {
    Serial.print(">");
  } else
  {
    espSerial.println("AT+CIPCLOSE");
    Serial.println(F("connect timeout"));
    return false;
  }

  espSerial.println(action);
  Serial.println(action);

  lcd.clear();
  lcd.setCursor(1, 1);
  lcd.print("wifi: HK weather");

  if (espSerial.find("main\":\""))
  {
    String main = espSerial.readStringUntil('"');
    Serial.print("found main: ");
    Serial.println(main);
    lcd.setCursor(1, 3);
    lcd.print(main);
  }
  if (espSerial.find("temp\":"))
  {
    float temp = espSerial.parseFloat();
    Serial.print("found temp: ");
    Serial.println(temp);
    lcd.setCursor(1, 4);
    lcd.print(temp);
    lcd.print(" degC");
  }
  if (espSerial.find("pressure\":"))
  {
    int pres = espSerial.parseInt();
    Serial.print("found pressure: ");
    Serial.println(pres);
    lcd.setCursor(1, 5);
    lcd.print(pres);
    lcd.print(" hpa");
  }
  if (espSerial.find("humidity\":"))
  {
    int hum = espSerial.parseInt();
    Serial.print("found humidity: ");
    Serial.println(hum);
    lcd.setCursor(1, 6);
    lcd.print(hum);
    lcd.print("%");
  }
  while (espSerial.available()) {   // flush serial input
    espSerial.read();
  }
}

boolean connectWiFi() {
  espSerial.println("AT+CWMODE=1");
  delay(1000);
  String cmd = "AT+CWJAP=\"";
  cmd += SSID;
  cmd += "\",\"";
  cmd += PASS;
  cmd += "\"";
  // Serial.println(cmd);
  espSerial.println(cmd);
  delay(5000);

  if (espSerial.find("OK")) {
    Serial.print(F("connected to wifi.."));
    return true;
  } else
  {
    Serial.println(F("cannot connect to wifi"));
    return false;
  }
}