PROTO power meter

Measuring AC power with the Industruino PROTO

Tom

This project shows how to build an Arduino based electricity consumption monitor using the Industruino PROTO platform. We can use it to measure the power consumption of an AC appliance such as a water cooker, a TV, a laptop charger, anything you plug into a wall socket. Alternatively you can also use it in your electricity cabinet to measure the power consumption in your entire house (at least one phase).

The challenge is to measure an alternating current (AC) of a relatively high voltage (220-240V) with our direct current 5V Arduino microcontroller. This may seem dangerous, but we will use a non-invasive Current Transformer (CT),  so our Arduino remains galvanically isolated from the high voltage AC.

This prototype is based on the excellent open source project OpenEnergyMonitor. It uses parts of the its standard emonTx hardware and software to report the AC apparent power consumption, based on measurements of a Current Transformer as in the picture on the left. The original project also allows to measure 3 phase and/or real power, but for our prototype here we are only measuring the current of one phase, not its voltage which would require an AC/AC adaptor.

The CT, as shown in the picture above, is clamped around 1 wire of the mains (live or neutral, not both!), and it has 2000 windings and a max current of 50mA so it can read up to 100A. More info here.

We want to capture the small alternating current that is proportional (1:2000) to the AC that goes through the main wire. We convert this current into a voltage by putting through a burden resistor, as in the picture on the right. 

I used a burden resistor of 30 ohm, and a voltage divider 10K/10K for the DC bias. This creates a voltage around 2.5V proportional to the current flowing through the CT, as explained in above link. The circuit is complete with one 10uF capacitor. The output goes to the PROTO's A6 (=D4).

For a detailed explanation, see OpenEnergyMonitor.org 

The CT has a 3.5mm (mono) jack and i put a socket on the Industruino PROTO's baseboard, enlarging the soldering holes to fit the pins.

What you see in the picture on the left is actually all you need to measure the AC power!

The sketch is quite simple, as all calculations are done in the emon library.

The RMS voltage is considered a constant, to be calibrated with a multimeter.

The maximum current of this setup is 100A, so it can measure up to 20kW. This can be adjusted by changing the burden resistor.

The LCD shows the AC power (in Watt) as a product of the calibrated AC voltage (in Volt) and the measured current (in Ampere). The final version also shows the average power consumption since startup (in Watt), and the accumulated energy consumption (in kWh). This kWh number is probably not very accurate as the sketch does not use precise timing.

Although the 32u4 topboard that we used for this prototype only has 28K available, i could use the U8G display library with no problem: the code is quite small and i used subsets of the 3 fonts to reduce the file size.

Below is the full setup:

  • the white cable plugged into the electricity wall socket (out of view)

  • the CT on the blue power line

  • the kettle's black cable plugged into the white socket

  • the CT plugged into the PROTO (without its case)

  • the PROTO powered by a USB power bank

LOGGING YOUR DATA INTO THE CLOUD

There are 2 options: 

  • using the Industruino Ethernet module
  • installing an ESP8266 WiFi module inside the PROTO

The Ethernet option is straightforward: just connect your Ethernet module to the PROTO using the supplied cable, and plug in an ethernet cable connected to your LAN, as in the picture on the left (PROTO and Ethernet modules shown without casings).

See ETHERNET sketch below.

Installing the ESP9266 WiFi module requires a bit of soldering, as in the picture on the right, details below.

Our favourite cloud solution is the open source http://emoncms.org/ where you can create nice visualisations and dashboards. 

This is a graph on emoncms.org with 3 hours of data for my laptop charger, which consumes around 40W when the laptop screen is on, and less than 10W when off.

I have also added a counter in the code to keep track of kiloWatt-hours (kWh) since startup of the Industruino, and average power consumption. The timing is not exact; as far as i can tell it's accurate to about 3%.

As the WiFi module has to restart completely, each data transmission takes around 9 seconds. During that period there are no power measurements so i use the last reading for that entire period.

Below is the sketch; it is easy to remove the Ethernet/WiFi part if needed.

PROTO baseboard implementation with ESP8266 WiFi module (ESP-01)

The diagram on the right shows you how to wire the sensor conditioning circuit and ESP8266 module onto the prototyping area of the Industruino.

I used the minimal ESP-01 package in a 4x2 female header on the PROTO board. I used the onboard 3.3V with a large capacitor (1500uF) to deal with the high current requirements during transmission. And i used the Serial1 hardware serial port on D0/D1 at baud rate 115200, with the standard AT firmware on the ESP8266.

When the WiFi connection is active, there is a lot of interference with the analog measurement, so i shutdown the WiFi module during the sampling (by pulling the CH_PD pin to GND), and restart it after a fixed interval, e.g. 1 min, to send the power data. 

See WIFI sketch below.

/*
  INDUSTRUINO PROTO with Leonardo style 32u4  + ETHERNET MODULE
  measuring AC power consumption

  based on the emonCMS project's emonTX https://github.com/openenergymonitor/emonTxFirmware/tree/master/emonTxV3
  in particular the sketch: emonTxV3_2_DiscreteSampling
  Current Transformers: https://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface
  possible to read up to 10W even when nothing is connected (ADC resolution) https://openenergymonitor.org/emon/buildingblocks/measurement-implications-of-adc-resolution-at-low-current-values

  hardware: 10K/10K voltage divider for DC bias
  30 ohm burden resistor and 10uF cap
  CT: YHDC SCT-013-000 with 2000 windings, 100A max

  Industruino Ethernet module

  LCD shows:
  > apparent power (W) -- we do not measure the voltage so cannot know the phase shift
  > measured current (A) -- RMS
  > preset voltage (V) -- RMS
  > kWh spent since startup (kWh)  -- cumulative per second, stored as Watt-seconds

  Tom Tobback, Nov 2016
*/


#include "U8glib.h"
U8GLIB_MINI12864 u8g(21, 20, 19, 22);    // SPI Com: SCK = 21, MOSI = 20, CS = 19, A0 = 22

#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor emon;                   // Create an instance

#include <avr/wdt.h>

#include <SPI.h>
#include <EthernetIndustruino.h>

// Enter a MAC address and IP address for your controller below.
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, 1, 145);
// Initialize the Ethernet client library
EthernetClient client;

#define DST_IP "80.243.190.58"    // emoncms.org

const byte  Vrms = 225;                                 // Vrms for apparent power readings (when no AC-AC voltage sample is present)
// measured by Tom in Mui Wo
const int   no_of_samples = 1480;

const float Ical = 66.6;                                // (2000 turns / 30 Ohm burden) = 66.6

float Irms;
unsigned long lcd_timestamp;
unsigned long sending_timestamp;
unsigned long sending_interval = 60;         // wifi data sending interval in seconds
unsigned long watt_seconds = 0;              // to store the energy measured since startup
unsigned long seconds = 0;                   // counter since start

////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {

  pinMode(13, OUTPUT);    // LED
  digitalWrite(13, LOW);    // LOW = backlight ON -- do not use analogWrite

  pinMode(2, OUTPUT);    // in case the ESP8266 is installed need to pull CH_PD down
  digitalWrite(2, LOW);  // ESP off

  emon.current(6, Ical);             // Current: input pin A6=D4, calibration factor

  Serial.begin(9600);         // for Serial Monitor

  wdt_enable(WDTO_8S); // options: WDTO_1S, WDTO_2S,  WDTO_4S, WDTO_8S

  u8g.begin();
  u8g.setRot180();

  drawIntro();         // LCD intro screen
  delay(2000);

  checkEthernet();      // check Ethernet connection
  delay(2000);

  sending_timestamp = millis();   // reset counters
  lcd_timestamp = millis();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() {

  wdt_reset();      // confirm to watchdog timer that all is well

  Irms = emon.calcIrms(no_of_samples);  // Calculate Irms only
  int power = Irms * Vrms;

  if (millis() - lcd_timestamp > 925L) {    // in theory should be 1000 but needs to be calibrated (accuracy 3% over 15min)

    lcd_timestamp = millis();         // reset counter to include the time spent doing the display

    watt_seconds += power;
    seconds++;
    int power_average = watt_seconds / seconds;

    Serial.print("Measured power (W): ");
    Serial.print(power);       // Apparent power
    Serial.print("\t");
    Serial.print(Irms);          // Irms
    Serial.print("A");
    Serial.print("\t");
    Serial.print(watt_seconds);          // Wattseconds
    Serial.print("Ws");
    Serial.print("\t");
    Serial.print(seconds);          // seconds counter
    Serial.print("s");
    Serial.print("\t");
    Serial.print(power_average);          // seconds counter
    Serial.println("W average");

    drawPower(power, power_average);
  }

  if (millis() - sending_timestamp > sending_interval * 1000L) {
    unsigned long start_sending = millis();
    drawSending(power);
    Serial.println();
    sendData(power);        // send data to cloud server
    delay(200);                  // to avoid interference

    float seconds_sending = (millis() - start_sending) / 1000.0;
    Serial.print("seconds spent sending = ");
    Serial.println(seconds_sending);
    watt_seconds += power * seconds_sending;
    seconds += seconds_sending;

    lcd_timestamp = millis();     // reset timers
    sending_timestamp = millis();
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////

void drawIntro(void) {                                                // INTRO
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13r);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.drawStr(10, 25, "Energy Monitor");
    u8g.drawStr(10, 40, "Ethernet version");
//    u8g.drawStr(10, 60, "Tom Tobback v2");
  } while ( u8g.nextPage() );
}

void drawPower(int p, int avg) {                                                // main screen
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13r);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.setPrintPos(10, 45);
    u8g.setFont(u8g_font_fub25n);
    u8g.print(p);
    u8g.setFont(u8g_font_6x13r);               // on 1286 board can use nicer font for this line: u8g_font_fub14r   (on 32u4 not enough memory)
    u8g.print("W");
    u8g.setFont(u8g_font_6x13r);
    u8g.setPrintPos(10, 60);
    u8g.print(Irms, 1);
    u8g.print("A");
    u8g.setPrintPos(45, 60);
    u8g.print(Vrms);
    u8g.print("V");
    u8g.setPrintPos(80, 60);
    u8g.print(float (watt_seconds / (3600.0 * 1000.0)), 1);
    u8g.print("kWh");
    u8g.setPrintPos(95, 25);
    u8g.print("avg:");
    u8g.setPrintPos(95, 38);
    u8g.print(avg);
    u8g.print("W");
  } while ( u8g.nextPage() );
}

void checkEthernet(void) {                                                // check ethernet
  Serial.println("Checking Ethernet..");
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13r);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.drawStr(10, 25, "Ethernet ");
    u8g.drawStr(10, 35, "testing connection.. ");
  } while ( u8g.nextPage() );

  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP, trying with fixed IP");
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_6x13r);
      u8g.drawStr(10, 10, "Industruino emonTx");
      u8g.drawStr(10, 25, "Ethernet ");
      u8g.drawStr(10, 45, "DHCP failed, trying");
      u8g.setPrintPos(10, 58);
      u8g.print(ip);
    } while ( u8g.nextPage() );
    Ethernet.begin(mac, ip);
  }
  else {
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_6x13r);
      u8g.drawStr(10, 10, "Industruino emonTx");
      u8g.drawStr(10, 25, "Ethernet");
      u8g.drawStr(10, 45, "DHCP is OK");
      u8g.setPrintPos(10, 58);
      u8g.print(Ethernet.localIP());
    } while ( u8g.nextPage() );
    Serial.print("Ethernet connected via DHCP, IP=");
    Serial.println(Ethernet.localIP());
  }
}

void drawSending(int p) {                                                // sending screen
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13r);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.setPrintPos(10, 45);
    u8g.setFont(u8g_font_fub25n);
    u8g.print(p);
    u8g.setFont(u8g_font_6x13r);                        // on 1286 board can use nicer font for this line: u8g_font_fub14r   (on 32u4 not enough memory)
    u8g.print("W");
    u8g.setFont(u8g_font_6x13r);
    u8g.drawStr(10, 60, "sending data..");
  } while ( u8g.nextPage() );
}

///////////////////////////////// ETHERNET /////////////////////////////////////////

void sendData(int p) {

  wdt_reset();      // confirm to watchdog timer that all is well

  Serial.print("Trying to connect to server.. ");

  if (client.connect(DST_IP, 80)) {
    Serial.print("connected - ");
    // send an HTTP GET request:
    client.print("GET /input/post.json?node=29&apikey=xx&csv=");
    client.print(p);
    client.println(" HTTP/1.0\r\n\r\n");
    client.println("Host: www.emoncms.org");
    client.println("Connection: close");
    client.println();

    delay(100);                                                     // minimum delay here depends on the speed of your internet connection
    if (client.find("200 OK")) {                                    // find function will wait for timeout
      Serial.println("server replies OK!");
      client.stop();
    }
    else {                                                          // no reply could be due to slow connection:
      delay(1000);                                                  // wait extra second and find again
      if (client.find("200 OK")) {
        Serial.println("server replies OK!");
        client.stop();
      }
      else {
        Serial.println("no reply from server");                      // no confirmation received from server
        client.stop();
      }
    }
  }
  else {
    Serial.println("connection failed");                            // failed to connect to server
    client.stop();
  }

  wdt_reset();      // confirm to watchdog timer that all is well

}

/*
  INDUSTRUINO PROTO with Leonardo style 32u4 + ESP8266 WIFI
  measuring AC power consumption

  based on the emonCMS project's emonTX https://github.com/openenergymonitor/emonTxFirmware/tree/master/emonTxV3
  in particular the sketch: emonTxV3_2_DiscreteSampling
  Current Transformers: https://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface
  possible to read up to 10W even when nothing is connected (ADC resolution) https://openenergymonitor.org/emon/buildingblocks/measurement-implications-of-adc-resolution-at-low-current-values

  hardware: 10K/10K voltage divider for DC bias
  30 ohm burden resistor and 10uF cap
  CT: YHDC SCT-013-000 with 2000 windings, 100A max

  ESP826-01 connected to hardware serial1, standard firmware (AT commands):
  Vcc   3.3V
  GND   GND
  TX    D0=RX
  RX    D1=TX
  RST   D3
  CH_PD D2   (if no power down, a lot of interference with CT measurements

  LCD shows:
  > apparent power (W) -- we do not measure the voltage so cannot know the phase shift
  > measured current (A) -- RMS
  > preset voltage (V) -- RMS
  > kWh spent since startup (kWh)  -- cumulative per second, stored as Watt-seconds

  Tom Tobback, Nov 2016
*/


#include "U8glib.h"
U8GLIB_MINI12864 u8g(21, 20, 19, 22);    // SPI Com: SCK = 21, MOSI = 20, CS = 19, A0 = 22

#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor emon;                   // Create an instance

#include <avr/wdt.h>

#define SSID "xx"
#define PASS "xx"
#define DST_IP "80.243.190.58"    // emoncms.org

const byte  Vrms = 225;                                 // Vrms for apparent power readings (when no AC-AC voltage sample is present)
// measured by Tom in Mui Wo
const int   no_of_samples = 1480;

const float Ical = 66.6;                                // (2000 turns / 30 Ohm burden) = 66.6
const int esp_rst = 3;
const int esp_pd = 2;

float Irms;
unsigned long lcd_timestamp;
unsigned long wifi_timestamp;
unsigned long sending_interval = 60;         // wifi data sending interval in seconds
unsigned long watt_seconds = 0;              // to store the energy measured since startup
unsigned long seconds = 0;                   // counter since start

////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {

  pinMode(13, OUTPUT);    // LED
  digitalWrite(13, LOW);    // LOW = backlight ON -- do not use analogWrite

  pinMode(esp_rst, OUTPUT);    // ESP reset
  pinMode(esp_pd, OUTPUT);    // ESP power down
  digitalWrite(esp_pd, LOW);  // ESP off
  
  emon.current(6, Ical);             // Current: input pin A6=D4, calibration factor

  Serial.begin(9600);         // for Serial Monitor
  Serial1.begin(115200);        // for ESP8266

  wdt_enable(WDTO_8S); // options: WDTO_1S, WDTO_2S,  WDTO_4S, WDTO_8S

  u8g.begin();
  u8g.setRot180();

  drawIntro();         // LCD intro screen
  delay(2000);

  digitalWrite(esp_pd, HIGH);    // ESP on
  checkWifi();
  digitalWrite(esp_pd, LOW);   // ESP off
  delay(2000);

  wifi_timestamp = millis();   // reset counters
  lcd_timestamp = millis();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() {

  wdt_reset();      // confirm to watchdog timer that all is well

  Irms = emon.calcIrms(no_of_samples);  // Calculate Irms only
  int power = Irms * Vrms;

  if (millis() - lcd_timestamp > 925L) {    // in theory should be 1000 but needs to be calibrated (accuracy 3% over 15min)
    
    lcd_timestamp = millis();         // reset counter to include the time spent doing the display
    
    watt_seconds += power;
    seconds++;
    int power_average = watt_seconds / seconds;
    
    Serial.print("Measured power (W): ");
    Serial.print(power);       // Apparent power
    Serial.print("\t");
    Serial.print(Irms);          // Irms
    Serial.print("A");
    Serial.print("\t");
    Serial.print(watt_seconds);          // Wattseconds
    Serial.print("Ws");
    Serial.print("\t");
    Serial.print(seconds);          // seconds counter
    Serial.print("s");
    Serial.print("\t");
    Serial.print(power_average);          // seconds counter
    Serial.println("W average");
    
    drawPower(power, power_average);
  }

  if (millis() - wifi_timestamp > sending_interval * 1000L) {
    unsigned long start_sending = millis();
    drawSending(power);
    digitalWrite(esp_pd, HIGH);   // ESP on
    sendData(power);        // send data to cloud server
    digitalWrite(esp_pd, LOW);    // ESP off
    delay(200);                  // to avoid interference
    
    float seconds_sending = (millis() - start_sending) / 1000.0;
    Serial.print("seconds spent sending = ");
    Serial.println(seconds_sending);
    watt_seconds += power * seconds_sending;
    seconds += seconds_sending;

    lcd_timestamp = millis();     // reset timers
    wifi_timestamp = millis();    
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////

void drawIntro(void) {                                                // INTRO
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.drawStr(10, 25, "Energy Monitor");
//    u8g.drawStr(10, 60, "Tom Tobback v2");
  } while ( u8g.nextPage() );
}

void drawPower(int p, int avg) {                                                // main screen
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.setPrintPos(10, 45);
    u8g.setFont(u8g_font_fub25n);
    u8g.print(p);
    u8g.setFont(u8g_font_fub14r);
    u8g.print("W");
    u8g.setFont(u8g_font_6x13);
    u8g.setPrintPos(10, 60);
    u8g.print(Irms, 1);
    u8g.print("A");
    u8g.setPrintPos(45, 60);
    u8g.print(Vrms);
    u8g.print("V");
    u8g.setPrintPos(80, 60);
    u8g.print(float (watt_seconds / (3600.0 * 1000.0)), 1);
    u8g.print("kWh");
    u8g.setPrintPos(95, 25);
    u8g.print("avg:");
    u8g.setPrintPos(95, 38);
    u8g.print(avg);
    u8g.print("W");
  } while ( u8g.nextPage() );
}

void checkWifi(void) {                                                // check Wifi
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.drawStr(10, 25, "WiFi: ");
    u8g.drawStr(50, 25, SSID);
    u8g.drawStr(10, 35, "testing connection.. ");
  } while ( u8g.nextPage() );

  if (connectWiFi()) {
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_6x13);
      u8g.drawStr(10, 10, "Industruino emonTx");
      u8g.drawStr(10, 25, "WiFi: ");
      u8g.drawStr(50, 25, SSID);
      u8g.drawStr(10, 45, "connection OK");
    } while ( u8g.nextPage() );
  }
  else {
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_6x13);
      u8g.drawStr(10, 10, "Industruino emonTx");
      u8g.drawStr(10, 25, "WiFi: ");
      u8g.drawStr(50, 25, SSID);
      u8g.drawStr(10, 45, "unable to connect");
    } while ( u8g.nextPage() );
  }
}

void drawSending(int p) {                                                // sending screen
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x13);
    u8g.drawStr(10, 10, "Industruino emonTx");
    u8g.setPrintPos(10, 45);
    u8g.setFont(u8g_font_fub25n);
    u8g.print(p);
    u8g.setFont(u8g_font_fub14r);
    u8g.print("W");
    u8g.setFont(u8g_font_6x13);
    u8g.drawStr(10, 60, "sending over WiFi..");
  } while ( u8g.nextPage() );
}

///////////////////////////////// WIFI /////////////////////////////////////////

boolean sendData(int p) {

  if (!connectWiFi()) {
    Serial.println(F("cannot connect to wifi"));
    return false;
  }

  wdt_reset();      // confirm to watchdog timer that all is well

  //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);
  // Serial.println(cmd);

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

  wdt_reset();      // confirm to watchdog timer that all is well

  String action;
  action = "GET /emoncms/input/post.json?node=29&apikey=xx&csv=";
  action += p;
  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);
  //  delay(1000);

  if (Serial1.find("SEND OK"))
  {
    Serial.println(F("send OK.."));
  }
  else {
    return false;
  }
}

///////////////////////////////////////////////////////////////

boolean connectWiFi() {

  Serial.print(F("reset ESP8266.."));
  //test if the module is ready

  digitalWrite(esp_rst, LOW);     // reset the ESP module
  delay(100);
  digitalWrite(esp_rst, HIGH);

  Serial1.println("AT+RST");

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

  wdt_reset();      // confirm to watchdog timer that all is well

  Serial1.println("AT+CWMODE=1");
  delay(1000);

  while (Serial1.available()) {           // flush Serial1 - seems necessary to clear serial buffer
    Serial1.read(); }
  
  wdt_reset();      // confirm to watchdog timer that all is well

  String cmd = "AT+CWJAP=\"";
  cmd += SSID;
  cmd += "\",\"";
  cmd += PASS;
  cmd += "\"";
  // Serial.println(cmd);
  Serial1.println(cmd);
  delay(5000);
  
//  while (Serial1.available()) {           // flush Serial1 - for debugging
//    Serial.write(Serial1.read()); }
  
  wdt_reset();      // confirm to watchdog timer that all is well

  if (Serial1.find("CONNECTED")) {                // response depends on your ESP AT firmware, used to be 'OK'
    Serial.print(F("connected to wifi.."));
    return true;
  } else
  {
    Serial.println(F("cannot connect to wifi"));
    return false;
  }
}