Adding an MCP23017 16 port IO expander to Arduino or Esp8266

I am not claiming that what I am describing here is earth shattering or trailblazing, because in fact it is very simple and no doubt has been done by many already. But sometimes what is simple for the one, is still a question mark for the other, so here is quick ‘how-to’ of adding 16 I/O ports to your microprocessor. This is especially handy when working with a chip like the ESP8266 that has only limited I/O
The MCP23017 is an I2C enabled 16 I/O port chip. That means that you only need 2 pins (yes with Vcc and ground it makes 4) to control the chip and the added advantage is that you can share I2C with various other devices as well.

The 16 I/O lines are divided into an 8 I/O PORT A and an 8 I/O PORT B. Both can be used as input as well as output. The chip also has 2 configurable interrupts (that I will not be using). The physical layout of the chip makes it quite easy to use it on a piece of strip board.

The circuit (at right) is rather simple. At a last moment I decided to leave out the pull up resistors so it would be more flexible to use together with other I/O devices. The 3 Address pins A0-A2 determine the I2C address that ranges from 0x20 (all pins on ground) to 0x27 (all pins on Vcc).
The chip  can take a Vcc from 2.7V to 5V and this is perfect for 3.3 Volt devices as  the modern arduino’s and the ESP8266 range.

Using the chip in a program is fairly easy. There are good libraries available, but it might help if you know how to program the chip without a library.
In my case I have all  address lines tied to ground and therefore my I2C address is 0x20. Suppose I want to use all PORT A lines as outputs. I do that  as follows:

Wire.beginTransmission(0x20);
Wire.write(0x00); // IODIRA register
Wire.write(0x00); // set entire PORT A to output
Wire.endTransmission();

For PORT B that  is rather similar:

Wire.beginTransmission(0x20);
Wire.write(0x01); // IODIRB register
Wire.write(0x00); // set entire PORT B to output
Wire.endTransmission();

If we then want to send a specific value ‘X’ to that PORT A, we do that as follows

Wire.beginTransmission(0x20);
Wire.write(0x12); // address port A
Wire.write(X);  // value to send
Wire.endTransmission();

‘X’ ofcourse is a byte value that determines whether we set a specific port HIGH or LOW.
If for instance ‘X’is ‘0’ that means we write a LOW to all PORT A outputs. If it is 255 that means we write a HIGH to all PORT A outputs.
To determine what value to send, consider the 8 I/O lines of PORT A as a byte in which the individual bits determine HIGH or LOW.
So if we only want to make PORTA.0 HIGH and the rest LOW, we write a binary value of 0b00000001 =1 to the A register. If we want to make PORTA.0 and PORTA.2 HIGH and the rest LOW we write a binary value of 0b00000101 = 5.
For PORT B it is similar:

Wire.beginTransmission(0x20);
Wire.write(0x13); // address PORT B
Wire.write(X);  // value to send
Wire.endTransmission();

If we want to use PORT B (or PORT A for that matter) as input, we do that as follows:

Wire.beginTransmission(0x20);
Wire.write(0x13); // address PORT B
Wire.endTransmission();
Wire.requestFrom(0x20, 1); // request one byte of data
byte input=Wire.read(); // store incoming byte into "input"

The byte “input” will vary between 0 and 255, in which the individual bits determine the input on the corresponding IO line. So if ‘input’  reads ‘3’  which in binary is 0b00000011, that means that both IO line 0 and 1  were HIGH and the rest LOW

#include <Wire.h> // Wire.h
byte input=0;
void setup()
{
  Serial.begin(9600);
  Wire.begin(); // wake up I2C bus
  Wire.beginTransmission(0x20);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // set entire PORT A as output
  Wire.endTransmission();
}
 
void loop()
{
  // read the inputs of bank B
  Wire.beginTransmission(0x20);
  Wire.write(0x13);
  Wire.endTransmission();
  Wire.requestFrom(0x20, 1);
  input=Wire.read();
 
  // now send the input data to bank A
  Wire.beginTransmission(0x20);
  Wire.write(0x12); // address PORT A
  Wire.write(input);    // PORT A
  Wire.endTransmission();
  delay(100); // for debounce
}

That’s basically it if you want to do the adressing yourself. Using a library, such as the one from Adafruit, makes it much easier though as it has commands to write and read from individual IO lines. One of the example programs to read a single button, looks  for instance like this:

#include <Wire.h> // Wire.h
#include "Adafruit_MCP23017.h"

// Basic pin reading and pullup test for the MCP23017 I/O expander
// public domain!
// Connect pin #12 of the expander to Analog 5 (i2c clock)
// Connect pin #13 of the expander to Analog 4 (i2c data)
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
// Connect pin #9 of the expander to 5V (power)
// Connect pin #10 of the expander to ground (common ground)
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)
// Input #0 is on pin 21 so connect a button or switch from there to ground

Adafruit_MCP23017 mcp;

void setup() 
{
mcp.begin();      // use default address 0
mcp.pinMode(0, INPUT);
mcp.pullUp(0, HIGH);  // turn on a 100K pullup internally
pinMode(13, OUTPUT);  // use the p13 LED as debugging
}

void loop() {
// The LED will 'echo' the button
digitalWrite(13, mcp.digitalRead(0));
}

Calculating Sunrise and Sunset on Arduino (or other microcontroller)

Knowing the hours of sunset and sunrise may be handy in a variety of situations, an automated chicken coop door might be only one example.

There are several ways to get the proper times: a lookup table in EEPROM, the Timelord library or one of its successors, the Dusk2Dawn library, a rather complicated calculation including the Julian calender, or a fairly simple approximation that I will discuss here.

This method uses the average of the earliest and latest sunset and then for any given day  adds or subtracts a certain amount of time with a maximum of half  of the difference between the earliest and latest sunrise.

Rob Tillaert discusses the method here. It presumes that the sunrise time follows a (co)sinoidal function. I will try to visualize it with a simple example:

Say that on June 23 the earliest sunrise of the year occurs at 4am, and that the latest sunrise of the year occurs at 23 December at 6 am.

Then you know that on any other day the sunrise is between 4 and 6 am. If you take the average that is 5 am, then you know that every other sunrise that year is either 0-1 hr later than 5 am or 0-1 hr earlier than 5 am.

It is the latter that is captured in the formula:

t=avg+0.5Δ*cos((doy+8)/58.09)

  • avg is average sunrise time, in minutes since midnight
  • Δ the difference between the earliest and latest sunrise time
  • doy is the day of the year
  • the 8 is there because we start on the wintersolstice: 23 December is 8 days before jan 1
  • 58.09 is 365/2π. That is necessary because the cosinusfunction has max 2π as input.

If you live in a DST zone, the earliest sunrise wil be under DST, however you need the non-DST corrected time: the sun knows no DST. Calculate firstm then add DST later

For my location the earliest and latest sunrise are:

earliest sunrise is at 4.19 am
latest sunrise is at 8.51
in order to use them in our equation, we have to calculate them in minutes past midnight:
4.19= 4×60+19=259
8.51= 8*60+51=531
The average is (259+531)/2=395
the difference or delta is 531-259=272. We need half of that which is 137.
The equation then becomes:

395+137*cos((doy+8)/58.09)

To check the accuracy of the approximation, I plotted the actual sunrise times (blue curve) against the calculated sunrise time (red curve).

As it shows, the first half of the year is a perfect fit, the second half of the year seems to follow a more linear curve with the max deviation being 20 minutes, that may or may not be accurate enough for your project. With the aid of this curve though I could opt for a linear approximation for the 2nd half of the year.

For sunset we can practically use the same formula, be it that we now have to subtract the variable part rather than add it.
For my location the sunset is as follows:
latest: 22:07 =1327
Earliest: 16:27= 987
avg=2287/2=1144
delta=240 ->170
sunset=1144-170*cos((doy+8)/58.09)

That gives the following graph:

This time I didnt bother to enter all the  real sunset times, but it is clearly visible that there is a reasonable fit that could maybe be enhanced a bit by shifting it slightly more to left or decreasing the delta a bit. Again Red graph is the calculated sunset, the blue is the actual sunset. None of the graphs has been corrected for DST.

A procedure for the Arduino would look as follows:
Where DST is a byte indicating whether DST is active (1)  or not active (0).
The day of the year I pull from my RTC library but it can also be calculated as follows:
int(((month-1)*30.5)+dayOfMonth)

The sunrise and sunset are both given in minutes after midnight. The hour and minutes of the sunrise (and sunset) can be calculated by:
hour=sunrise/60
minute=sunrise%60

MQTT with the W5100 Ethernetshield

In a previous post I discussed the use of an old ENC28J60 ethernetshield for an MQTT node. The biggest setback of the ENC28J60 shield is that it uses a lot of memory. When using the newer W5100 Ethernetshield, there is a bit more room to play with. So here is a simple framework  for an MQTT node that doesnt’t only publish, but also reacts to simple commands that come in with a subscription.

MQTT-Spy output
MQTT-Spy output
/*
          Arduino UNO with W5100 Ethernetshield or  W5100 Ethernet module, used as MQTT client
          It will connect over Wifi to the MQTT broker and controls a digital output (LED, relay)
          and gives the Temperature and Humidity, as well as the state of some switches
          The topics have the format "home/br/sb" for southbound messages and  "home/nb" for northbound messages
          Southbound are messages going to the client, northbound are messages coming from the client
          As the available memory of a UNO  with Ethernetcard is limited, I have kept the topics short
          Also, the payloads  are kept short
          The Northbound topics are
          home/br/nb/temp  for temperature
          home/br/nb/humid  for humidity
          home/br/nb/deur  for a door switch
          home/br/nb/l for  the lightintensity
          home/br/nb/pr  for the status of a PIR sensor
          home/br/nb/ip showing the IP number of the client
          home/br/nb/relay showing the relaystate

          There is only one southbound topic:
          home/br/sb
          The payload here determines the action:
          0 -Switch the relay off
          1-Switch the  relay on
          2-Publish the IP number of the client
          3 Ask for the relaystate REMOVED

          On Startup, the Client publishes the IP number

*/
#include 
#include "PubSubClient.h"
#include "DHT.h"
//kennelijk  geeft update van DHT sensor library boven 1.2.1 een fout
#define CLIENT_ID       "Hal"
//#define TOPIC           "temp"
#define PUBLISH_DELAY   3000
#define PUB_TOPIC "my_username/f/temperature" //Adafruit dashboard
#define PUB_TOPIC_h "my_username/f/humidity" //Adafruit dashboard
#define DHTPIN          3
#define DHTTYPE         DHT11
#define ledPin 13
#define relayPin 8
String ip = "";
bool statusKD = HIGH;
bool statusBD = HIGH;
bool statusGD = HIGH;
bool relaystate = LOW;
bool pir = LOW;
bool startsend = HIGH;
int lichtstatus;
uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x06};

EthernetClient ethClient;
PubSubClient mqttClient;
DHT dht(DHTPIN, DHTTYPE);

long previousMillis;

void setup() {
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(relayPin, OUTPUT);

  // setup serial communication

  Serial.begin(9600);
  while (!Serial) {};
  Serial.println(F("MQTT Arduino Demo"));
  Serial.println();

  // setup ethernet communication using DHCP
  if (Ethernet.begin(mac) == 0) {
    //Serial.println(F("Unable to configure Ethernet using DHCP"));
    for (;;);
  }

  Serial.println(F("Ethernet configured via DHCP"));
  Serial.print("IP address: ");
  Serial.println(Ethernet.localIP());
  Serial.println();
  /*
    Serial.println(Ethernet.localIP()[0]);
    Serial.println(Ethernet.localIP()[1]);
    Serial.println(Ethernet.localIP()[2]);
    Serial.println(Ethernet.localIP()[3]);
  */
  ip = String (Ethernet.localIP()[0]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[1]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[2]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[3]);
  //Serial.println(ip);

  // setup mqtt client
  mqttClient.setClient(ethClient);
  //mqttClient.setServer(mqttServer, 1883);
  // mqttClient.setServer("test.mosquitto.org", 1883);
  //  mqttClient.setServer( "raspberrypi.local",1883);
  //mqttClient.setServer("io.adafruit.com",1883);
  mqttClient.setServer( "192.168.1.102", 1883);
  //Serial.println(F("MQTT client configured"));
  mqttClient.setCallback(callback);
  // setup DHT sensor
  dht.begin();
  Serial.println(F("DHT sensor initialized"));

  Serial.println();
  Serial.println(F("Ready to send data"));
  previousMillis = millis();
  mqttClient.publish("home/br/nb/ip", ip.c_str());
}

void loop() {

  statusBD = digitalRead(4);// FrontdoorSwitch
  statusGD = digitalRead(5);// Garagedoor Switch
  statusKD = (digitalRead(6));//LivingRoom Switch

  lichtstatus = analogRead(A0);//Reads an LDR
  pir = digitalRead(7);//Reads a PIR sensor
  relaystate = digitalRead(relayPin);// Reads the state of a relay

  // it's time to send new data?
  if (millis() - previousMillis > PUBLISH_DELAY) {
    sendData();
    previousMillis = millis();

  }

  mqttClient.loop();
}

void sendData() {

  char msgBuffer[20];
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.println("oC");
  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.println("%");
  Serial.print("Relay is: ");
  Serial.println((relaystate == LOW) ? "OPEN" : "CLOSED");
  if (mqttClient.connect(CLIENT_ID)) {
    mqttClient.publish("home/br/nb/temp", dtostrf(t, 6, 2, msgBuffer));
    mqttClient.publish("home/br/nb/humid", dtostrf(h, 6, 2, msgBuffer));
    mqttClient.publish("home/br/nb/deur", (statusBD == HIGH) ? "OPEN" : "CLOSED");
    mqttClient.publish("home/br/nb/garage", (statusGD == HIGH) ? "OPEN" : "DICHT");
    mqttClient.publish("home/br/nb/bel", (statusKD == HIGH) ? "OPEN" : "CLOSED");
    mqttClient.publish("home/br/nb/l", dtostrf(lichtstatus, 4, 0, msgBuffer));
    mqttClient.publish("home/br/nb/p", (pir == HIGH) ? "OPEN" : "CLOSED");
    mqttClient.publish("home/br/nb/relay", (relaystate == LOW) ? "OPEN" : "CLOSED");
    mqttClient.subscribe("home/br/sb");
    if (startsend) {
     // mqttClient.publish("home/br/nb/relay", (relaystate == LOW) ? "OPEN" : "CLOSED");
      mqttClient.publish("home/br/nb/ip", ip.c_str());
      startsend = LOW;
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  char msgBuffer[20];
  // I am only using one ascii character as command, so do not need to take an entire word as payload
  // However, if you want to send full word commands, uncomment the next line and use for string comparison
   //payload[length] = '\0';            // terminate string with '0'
  //String strPayload = String((char*)payload);  // convert to string
  // Serial.println(strPayload); //can use this if using longer southbound topics
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");//MQTT_BROKER
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  Serial.println(payload[0]);

  // Examine only the first character of the message
  if (payload[0] == 49)             // Message "1" in ASCII (turn output ON)
  {
    digitalWrite(LED_BUILTIN, HIGH);    //
    digitalWrite(relayPin, HIGH);
  } else if (payload[0] == 48)      // Message "0" in ASCII (turn output OFF)
  {
    digitalWrite(relayPin, LOW);     //
    digitalWrite(LED_BUILTIN, LOW);
  } else if (payload[0] == 50)
  {
    mqttClient.publish("home/br/nb/ip", ip.c_str());// publish IP nr
  } else {
    Serial.println("Unknown value");
    mqttClient.publish("home/br/nb", "Syntax Error");
  }

}

You will find the full code for download here.

Setting up a local MQTT broker on the Raspberry Pi

On the web there are a ton of instructions and how to’s on how to do this, but I found  it easiest to just  issue two commands on Raspian Jessie:
sudo apt-get update
sudo apt-get install mosquitto mosquitto-clients python-mosquitto

then to test if it was all working, I opened 2 ssh terminals to my raspberry with:
ssh pi@192.168.1.102  (the latter being the ip nr from my raspberry)
and then in one of those terminals I entered:
mosquitto_sub -d -t hello/world
this made the terminal I will call Terminal 1 subscribe to the topic “hello/world”
in the other ssh terminal window (Terminal 2) I entered
mosquitto_pub -d -t hello/world -m "Greetings from Terminal2"
and immediately I got that message published in Terminal 1
(actually you don’t need the ‘-d’ option. That is just for diagnostics)
So I knew my local Mosquitto broker was working, at least within one machine, the raspberry on which it was installed. Time to check if it also worked on other machines. To do that I opened a third terminal on my desktop that didnt ssh to the raspberry but just gave a command line to the machine I was working on (a regular Linux desktop).

Before I could have that machine subscribe to an MQTT topic, I first had to install the MQTT client with:
sudo apt-get install mosquitto-clients

when that was done I entered:
mosquitto_sub -h 192.168.1.102 -t hello/world

mosquitto-terminalNow when I published a message on terminal2 (thats the raspberry) it got published on terminal1 (that same raspberry) and on terminal3 (a regular desktop), so now I knew everything was hunky dory.
However, having to use terminal command lines to check your MQTT broker is not the most convenient. I can advise everybody who works with MQTT to install MQTT-spy. That is a jar file that basically is a publisher and a client that keeps track of MQTT messages on your network.

It comes pre-configured with 3 public brokers but adding your local broker is very simple:
mqtt-localAfter starting MQTT-spy go to  ‘Connections-New Connection” Fill out a name for your connection and under the ‘connectivity tab’ add (in my case): 192.168.1.102:1883  with 1883 being the  standard MQTT port.
Then go to the ‘other’tab and check ‘auto subscribe when opened’.
Now in the main window add a subscription tab called ‘hello/world’ and republish  the messagemqttspy-3 from Terminal2: it should appear in the receive window of mqtt-spy. Vice versa, if  you publish a message from within mqtt spy, it will appear on terminal 1 also

Sending data to Carriots IoT platform with ESP8266

carriots

Carriots.com is a flexible IoT platform that can connect to a variety of devices and can collect data in XML or JSON format. It can process that data and on the basis of that send out data, e.g. to mail or SMS and I believe it can even Tweet. Apparently it is quite popular to attach a PIR sensor that then can send you a mail id it detects motion

Carriots does have free accounts. Those are limited in the number of devices one can connect (only 2), the number of emails (max 100/day) and the number of SMS’ (max 5/day) but it is enough for the average hobby use.

For now, I will focus  on how to get the data into Carriots, so i will not go into detail about the inner workings  of carriots, that in fact can be a bit overwhelming for those used to ‘simple’ Thingspeak, but I think it took me about 10-15 minutes to figure it all out and set up  my account. One warning though, one of the setting screens does have some info in a non scrolling sidebar. If you have set your screen to zoom in (Ctrl +) you may not see that info unless you zoom out with Ctrl-. Therefore I advise to take the quick tour after signing up so you have some quick orientation about the various screens.
Anyway after signing up most of the work is done from the cpanel, but it comes down to “connecting” a device that then defines a datastream. Don’t be put off, once you are there, it becomes quite clear. The ESP8266 is not between the default devices so just take “Other”. Once you are done you need to go the API screen to get your API and you need to make note of your DeviceID. The latter is often said to be “streamname@userid”, but in my case it was “streamname@userid.userid”.
You need the API Key and the DeviceID to send data to Carriots.
The below code reads the Analog port of the ESP8266 and uploads that to your Carriot datastream

#include "ESP8266WiFi.h"
const char* ssid     = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* server = "api.carriots.com";
// Replace with your Carriots apikey
const String APIKEY = "47777777777778c98cbb";
const String DEVICE = "xxxxx@yyy.yyy"; // your deviceID
WiFiClient client;

int val = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);
  // start wifi
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}
// Send data to  Carriot

void sendStream()
{
  //const int httpPort = 80;
  if (client.connect(server, 80)) {   // If there's a successful connection
    Serial.println(F("connected"));

    // construct the data json
    String json = "{\"protocol\":\"v2\",\"device\":\"" + DEVICE + "\",\"at\":\"now\",\"data\":{\"moisture\":\"" + val + "\"}}";

    // Make an HTTP request
    client.println("POST /streams HTTP/1.1");
    client.println("Host: api.carriots.com");
    client.println("Accept: application/json");
    client.println("User-Agent: Arduino-Carriots");
    client.println("Content-Type: application/json");
    client.print("carriots.apikey: ");
    client.println(APIKEY);
    client.print("Content-Length: ");
    int thisLength = json.length();
    client.println(thisLength);
    client.println("Connection: close");
    client.println();
    client.println(json);
  }
  else {
    // If server connection failed:
    Serial.println(F("connection failed"));
  }
}
void loop() {
  val = analogRead(A0);
  Serial.println(val);
  Serial.println(F("Send Data"));
  sendStream();
  delay(30000);
}

People who want to do this with an Arduino and Ethernetcard can use this library.

Reviewing the Wemos Battery Shield

wemosb3The Wemos battery shield seems an easy way to start battery feeding your Wemos D1 mini, but after using one for a while, it became obvious it is not for any serious battery use, mainly for two reasons: It is not efficient in using the battery power and the Wemos D1 itself is not efficient to be used with batteries.

The battery shield like all shields is to be plugged onto the Wemos D1 mini. It has two jacks: one for a battery and one for an USB plug that can be used to charge the battery. The charging circuit is built around a TP5410 Lipo charger. One therefore would expect a standard LiPo cell connector on the board but the LiPo cell connector on the board is an XH2.54 connector, whereas most LiPo cells come with a JST-PH connector. Another disappointment was that it came with male headers only. Apparently it is thought this shield should come on top of every other possible shield

Apparently he 5410 cranks up the battery voltage to 5 Volt and feeds this to the 5 Volt pin of the Wemos D1. Though this makes the Shield handy to use in combination with other shields that may rely on the voltage coming from the 5 Volt pin, it is likely a less efficient way of using the battery power as opposed to bringing it to 3.3 Volt directly via an LDO.

wemosbatteryshieldcircuitThere is not much technical information on the battery shield, but I did find a circuit that seems to almost come right out of a chinese datasheet of the TP5410, in which several configurations are shown.
Studying the circuit it seems that the 5 Volt coming from the USB, is connected to the 5 Volt pin of the shield, only through an SS32 Skottky diode. In itself that is no problem, unless one decides to use another input to that connector, e.g. a solar cell. The Wemos D1 mini has an RT9013 LDO regulator that has a max input of 5.5 Volt with an absolute Max rating of 6 Volt.
Considering that the SS32 Skottky diode has a forward voltage of 200 mV@200mA, a voltage of > 6.2 on the USB connector of the battery shield (say a 6 Volt solarpanel on a bright day) could already kill the RT9013 LDO on the Wemos D1 board, Eventhough Wemos states the shield can be supplied with 10 Volt, but maybe that is without it being connected to the Wemos D1 mini

Anyway, I hooked up a 720mAh Lipocell to the battery shield and uploades a sketch that measures the battery voltage (through a resistor on A0) and did an upload to Thingspeak every minute, being in deep sleep in between, just to see how long it would last.

My first observation was that the shield apparently does not fully charge the LiPo cell. It came to a max of 4.05 Volt before it switched to ‘Standby’. I made sure by using a Multimeter and indeed, only 4.05-4.1 volt on the cell.
shield5-6It worked pretty well after that, uploading the voltage to Thingspeak for 5 days and 5 hrs, when suddenly it stopped, with the battery voltage at 3.56 Volt, far above the 3 Volt minimum charge of a LiPo. Strange. The RT9013 has a drop of 250mV at 500mA, so even at 500mA the voltage on the ESP8266 still would have been 3.31 Volt. I have fed the 8266 with as low as 2.9 Volt and it was still working, so this was a strange finding. Resetting the Wemos did not bring it back to life… which was not so surprising because when i measured the LiPo (this was almost a day after the sketch stopped working… I was busy shopping for Newyears Eve), it was at 1.3 Volt. A quick connection to a USB port brought it back to life. A very strange finding indeed that warranted me to repeat the test (See below). But for now it doesnt seem like the battery shield is  the best solution for a serious battery dependend project.

As said before, the Wemos D1 Mini, also is not the most suitable for a battery operated project. True, the ESP8266 can be put to deepsleep and will only use around 77uA, but the Wemos board also has the CH340 FTDI to TTL chip on board. As that is directly connected to the 3.3 Volt line, it will always be active, drawing around 50uA which is sort of in the same ballparc as the ESP8266 itself. Without that CH340 it could operate 1.6 times longer.

Battery Voltage Wemos battery shield
Battery Voltage Wemos battery shield

The repeated test showed similar results: Charge to 4.05 V (should be 4.2 Volt) and apparently shuts off at 3.5 volt (should be 2.7 Volt)

 

 

 

 

batterijspaning10minA final test, in which the Wemos was put to sleep for 10 minutes, the battery lasted 18 days before it was shut off at 3.59 Volt

Apparently Harald has a more positive experience with the shield.

Sending mail with an ESP8266

Sending mail with an ESP8266 can be handy for a variety of things. I use it to occasionally have a remote ESP8266 send me a message it is still ok and functioning.
Though it is possible to have the ESP8266 directly access your mail server and send a message through that, that is usually not a good idea as many mailservers will refuse mail that is being sent from a different domain (your ip) than the mailserver’s.
it is therefore safer to use a service like smtp2go.com. As long as you stay below a certain limit of emails, one can get a free account.

After signing up  for smtp2go, you will need to choose a user id and password for your smtp log in. You thus have two sets of id and password: one for your user account and one for the mails you send.
The latter, you need to encode in base 64 to use  from your ESP8266. You can do that with an online encoder.
As there is no need to re-invent the wheel, I used this program as a basis and reworked that to my needs, but as your needs might be different from mine, I will just give a general example.
In order to send something more useful than ‘Hello World’, we are going to send the supply voltage and the chip ID. In real life I do not send the suply voltage as that is not so useful, but I send the battery voltage. But to keep it simple in this example we will stick to the supply voltage, which we get with ESP.getVcc().
The program is like this: (NOTE: the code might be badly formatted by wordpress, make sure you copy it completely)

#include <ESP8266WiFi.h>  // the ESP8266WiFi.h  lib
const char* SSID = "YourSSID";
const char* PASS = "YourPW";
char server[] = "mail.smtpcorp.com";
ADC_MODE(ADC_VCC);

WiFiClient client;
void setup()
{
  Serial.begin(115200);
  delay(10);
  Serial.println("");
  Serial.println("");
  Serial.print("Connecting To ");
  Serial.println(SSID);
  WiFi.begin(SSID, PASS);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  byte ret = sendEmail();
}

void loop()
{
}

byte sendEmail()
{
  byte thisByte = 0;
  byte respCode;

  if (client.connect(server, 2525) == 1) {
    Serial.println(F("connected"));
  } else {
    Serial.println(F("connection failed"));
    return 0;
  }
  if (!eRcv()) return 0;

  Serial.println(F("Sending EHLO"));
  client.println("EHLO www.example.com");
  if (!eRcv()) return 0;
  Serial.println(F("Sending auth login"));
  client.println("auth login");
  if (!eRcv()) return 0;
  Serial.println(F("Sending User"));
  // Change to your base64, ASCII encoded user
  client.println("ZV83MTAwMEBnbWFljC5jb31="); // SMTP UserID
  if (!eRcv()) return 0;
  Serial.println(F("Sending Password"));
  // change to your base64, ASCII encoded password
  client.println("X5pqVU9vYlJjY7Bq");//  SMTP Passw
     if (!eRcv()) return 0;
    Serial.println(F("Sending From"));   // change to your email address (sender)
   client.println(F("MAIL From: yrmail@gmail.com"));// not important 
   if (!eRcv()) return 0;   // change to recipient address
    Serial.println(F("Sending To"));
    client.println(F("RCPT To: receiver@gmail.com"));
    if (!eRcv()) return 0;
    Serial.println(F("Sending DATA"));
    client.println(F("DATA"));
    if (!eRcv()) return 0;
    Serial.println(F("Sending email"));   // change to recipient address
   client.println(F("To: receiver@gmail.com"));   // change to your address
   client.println(F("From: sender@gmail.com"));
 client.println(F("Subject: Emails from ESp8266\r\n"));
    client.print(F("Power is: "));
    client.print(ESP.getVcc());
    client.println(F("mV"));
    client.print(F("Device Chip ID: "));
    client.println(ESP.getChipId());
    Serial.print(F("Voltage is: "));
    Serial.print(ESP.getVcc());
    client.println(F("."));
    if (!eRcv()) return 0;
    Serial.println(F("Sending QUIT"));
    client.println(F("QUIT"));
    if (!eRcv()) return 0;
    client.stop();
    Serial.println(F("disconnected"));
    return 1;
  }
  byte eRcv()
  {
    byte respCode;
    byte thisByte;
    int loopCount = 0;
    while (!client.available())
  {
      delay(1);
      loopCount++;     // if nothing received for 10 seconds, timeout
      if (loopCount > 10000) {
      client.stop();
      Serial.println(F("\r\nTimeout"));
      return 0;
    }
  }

  respCode = client.peek();
  while (client.available())
  {
    thisByte = client.read();
    Serial.write(thisByte);
  }

  if (respCode >= '4')
  {
    //  efail();
    return 0;
  }
  return 1;
}

In the example I use “gmail” but ofcourse this can be any other mailservice>
In the program you will also see a line with “yrmail@gmail.com"));// not important“. I may be wrong but it is not that important what that says. It is the identity under which your mails are grouped in the smpt2go dashboard. Maybe it is easiest to make that equal to the sender address, but it isnot important for the functioning of the program.

The strings that are send are rather flexible. Instead of
client.println(F("Subject: Emails from ESp8266\r\n"));
On can also do:
client.print(F("Subject: "));
if (condition == met)
client.println(F(PREDEFINED_MESSAGE));