A cheap 1 channel relaymodule for ESP8266-01. Modifications revisited

In an earlier post, I discussed 2 popular 1 channel ESP8266-01 relay boards that are widely available on  ebay and chinese webstores. One of these boards -the simplest- seems to cause a lot of problems with the unsuspecting buyer, as it doesn’t work.
So, it might be good to address the problems around this board again, in a more practical session.

Be aware that this circuit is intended for the ESP8266-01S, but it can be made to work with the regular ESP8266-01.

The main reason why this board  does not work, is because the designer of  the PCB was drunk, or high on drugs when he designed it, or maybe he just didn’t understand the workings of the ESP8266-01.
His failure becomes immediately clear when we look at the circuit:
The designer used GPIO0 to trigger the relay pin.
There is nothing wrong with that, but as GPIO0 needs to be pulled high to let the ESP8266 start in UART mode (i.e. a normal start). The consequence is that the relay is activated on start-up. “No problem” must the designer have thought, “I just add a 2k resistor to pull it down”. Sure, that works, but the ESP8266 then just will not boot anymore.
So, we need to remove that 2k resistor that is indicated in the circuit above. On your PCB it is indicated as R2 and labelled with ‘202’.

A simple nudge with a hot soldering iron suffices, but make sure you remove the right one.

The next issue is that there is no connection between the Chipselect (EN or CH_PD) and the Vcc. So that needs to be fixed. Ideally that can be done with a 10k resistor, but it is most practical to just make the wire bridge as indicated in the circuit above, like this:

If you have an ESP8266-01S, then you are set, the board should work.

If you have a classic 512kRAM ESP8266-01, then you need to do one more thing: you need to add 10k pullup resistors between GPIO0 and 3V3 and between GPIO2 and 3V3.
When you have done this, the module will also work with an ESP8266-01.

Now  you have your 01 or 01S working  but, the relay will be activated on startup. That can be handled in several ways:
Add a “digitalWrite(0,HIGH);” instruction to the beginning of your program….or….
Use the NC contact to make your connection….or….
reroute the track to pin 0, to another pin (GPIO 1 or GPIO3) and give that pin a pulldown resistor (which could be an unaltered R2): Leave R2 in place, give R1 a little nudge and connect it to another pin of the ESP8266.

If you want to control the board via MQTT, this program might be of interest to you. If you want to use a webpage on your phone, a simple Webserver will do.

Just one more thing about removing the 2k resistor. You may have come across people that do not mention removing the 2k resistor. They solder a 3k3 resistor between 3V3 and GPIO0. Well, yes that works. Why? Well effectively they make the pull up resistor on GPIO0  about 2k5 (the 10k in combo with the 3k3). So that gives a voltage divider of 2k5 over 2k, which makes 1.46 Volt out of  3.3V, which is high enough to be seen as HIGH by gpio0. That is indeed a less elegant method, but it works.

A tight fit

Download 3 D file here.

Now if you happen to have the below relay board, the one with the STC15F104-SOP8 micro controller in addition to the ESP8266, and you don’t get that working, begin with checking if the CH_PD/ EN pin is indeed pulled up to 3V3.


MQTT -HTTP-OTA program template

I provide a template that can be used as a start for your own MQTT program on an ESP8266. It connects either through DHCP or via a static LAN IP.
It implements basic MQTT functions (subscribe and publish) and I have added a hoist of functions/MQTT messages that you might find useful.
For instance:

  • IPnr
  • MACnr
  • ChipId
  • Uptime
  • Heartbeat
  • TimeDate (internet), including the ‘Day of Year’
  • RSSI
  • Filename
  • A number of memory and core functions
  • HTTP-OTA Download here.

The WebOTA is quite basic, upon sending an update command via MQTT, the program seeks a bin file on a server that you need to specify. You can get a bin file from your program by pressing Alt-Ctrl-S in the Arduino-IDE.

The name the OTA expects -and what is generated by Alt-Ctrl-S – is based on the filename plus the board, followed by ‘bin’. So in this case that is “Template.ino.generic.bin”. If you compile it for a Wemos D1, that file will become “Template.ino.d1_mini.bin”

Sending ESP8266 sensordata to GoogleSheet

Note: The googlesheet interface as described here has changed. I currently have no time for a rewrite, It should however still be more or less self explainatory. “Publish” is now a button called “deploy”.

If you want to store sensor data coming from your ESP8266 (or ESP32) project ‘in the cloud’, then there is a hoist of options. One option is what most people will already have available, namely GoogleSheet.
There are a number of tutorials on how to set that up, and i will just describe how I did it here, as part of a larger project I will publish later.

What we will do is:

  • Set up a new Google Sheet
  • Add a script to that sheet that places data in the sheet
  • Make a function for the ESP8266 that calls that script and hands it the data we want stored.

During the course of creating the GoogleSheet and the GoogleScript, we will need to take note of two numbers: The “sheet ID” and the “script ID”. The latter is also referred to as the GAS_ID
The GAS_ID usually slooks something like: “AKfycbxp……………………”
We will put the GAS_ID in the ESP code calling the script we created.
We will put the sheet ID in the GoogleScript, so the script knows in what sheet to place it.

Creating the Google sheet and acquiring the sheet ID
Go to your google drive on “drive.google.com”
Select New-GoogleSheets
A new sheet will becreated, give it a meaningful name. If you then look at the URL that will look something like:
The part between “/d/” and “/edit$gid-0” is the sheet ID. Copy that somewhere.

Creating the Google script and acquiring the GAS_ID
In your new GoogleSheet, go to “Tools-Script Editor”
Give the script the same name as you gave your Sheet

Paste the following code:

function doGet(e) { 
  Logger.log( JSON.stringify(e) );  // view parameters
  var result = 'Ok'; // assume success
  if (e.parameter == 'undefined') {
    result = 'No Parameters';
  else {
    var sheet_id = '197ZGbUbbFmKtV-TPEtxYCXVRxt7YB00HQukSo-QwX14'; 		// Spreadsheet ID
    var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();		// get Active sheet //getSheetByName('naam') voor ander blad
    var newRow = sheet.getLastRow() + 1;						
    var rowData = [];
    var sheet2=SpreadsheetApp.openById(sheet_id).getSheetByName('Sheet2');
    var newRow2=sheet2.getLastRow() + 1;
    var rowData2 = [];
    rowData[0] = new Date();    // Timestamp in column A
    rowData2[0] = new Date();
    for (var param in e.parameter) {
      Logger.log('In for loop, param=' + param);
      var value = stripQuotes(e.parameter[param]);
      Logger.log(param + ':' + e.parameter[param]);
      switch (param) {
        case 'temperature': //Parameter
          rowData[1] = value; //Value in column B
          result = 'Written on Column B';
          rowData2[1]= value;
        case 'humidity': //Parameter
          rowData[2] = value; //Value in column C
          result += ', Written on column 3';
        case 'moisture' :
          result += ', Written in Column D';
          result = "unsupported parameter";
    // Write new row below
    var newRange = sheet.getRange(newRow, 1, 1, rowData.length);
    var newRange2= sheet2.getRange(newRow2,1,1, rowData.length)
  // Return result of operation
  return ContentService.createTextOutput(result);
* Remove leading and trailing single or double quotes
function stripQuotes( value ) {
  return value.replace(/^["']|['"]$/g, "");

Change the spreadsheet ID into your spreadsheet ID

Next go to Publish-Deploy as Web App

Change the acces type to ‘anyone, even anonymous’ and deploy

After deployment, you will be shown a url. Copy that as it contains the GAS_ID

The URL will look like https://script.google.com/macros/s/AKfycbxPrTJNtUgXxqMX…
The Script ID  (GAS_ID) is AKfycbxPrTJNtUgXxqMX…
This is the ID we will use in the ESP8266 code.
Before you leave the Google Sheet, go back to the Sheet itself and make sure there is a sheet1 and a sheet2.

This is not an absolute necessity, but it allows me to show how you can write to different sheets in your main sheet. In your neck of the woods those might be called “Sheet1” and “Sheet2”

Setting “less secure apps”
A final thing you need to do is to tell Google it should allow your applications (in this case, a connection coming from your ESP). To do that go to the less secure aps setting, and switch that ON

Switch to “ON”

The ESP8266 code

String GAS_ID = "";//getactivespreadsheetID
const char* fingerprint = "46 B2 C3 44 9C 59 09 8B 01 B6 F8 BD 4C FB 00 74 91 2F EF F6";
const char* host = "script.google.com";
const int httpsPort = 443;

void sendData(int x, int y, byte z)
  Serial.print("connecting to ");
  if (!gsclient.connect("script.google.com", httpsPort)) {
    Serial.println("connection failed");

  if (gsclient.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  String string_x     =  String(x, DEC);
  String string_y     =  String(y, DEC);
  String string_z     =  String(z, DEC);
  String url = "/macros/s/" + GAS_ID + "/exec?temperature=" + string_x + "&humidity=" + string_y + "&moisture=" + string_z;
  Serial.print("requesting URL: ");

  gsclient.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" +
                 "User-Agent: BuildFailureDetectorESP8266\r\n" +
                 "Connection: close\r\n\r\n");

  Serial.println("request sent");

  while (gsclient.connected()) {
    String line = gsclient.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
  String line = gsclient.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  Serial.println("reply was:");
  Serial.println("closing connection");

This function takes 3 arguments that will be placed in  the Google sheet.
Back to the Script

The Lines:

var sheet_id = '1DdQ-dc6bpsP7EtvtvBHvl-cZai1qUC6QfNtaxTnh6w0'; 	
var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();

go to the proper sheet and open the sheet that is active.
The lines:

var newRow = sheet.getLastRow() + 1;						
    var rowData = [];
    var sheet2=SpreadsheetApp.openById(sheet_id).getSheetByName('Sheet2');
    var newRow2=sheet2.getLastRow() + 1;
    var rowData2 = [];

Create a new Row in the first sheet and create a new Row in the second sheet. Beware ‘Sheet2’ is the NAME ofyor second sheet. if you have given it a different name, that shoud be reflected here
The lines:

rowData[0] = new Date();    // Timestamp in column A
rowData2[0] = new Date(); //Timestamp Sheet2 column A

insert a timestamp in the first column of both sheets
The lines:

for (var param in e.parameter) {
      Logger.log('In for loop, param=' + param);
      var value = stripQuotes(e.parameter[param]);
      Logger.log(param + ':' + e.parameter[param]);
      switch (param) {
        case 'temperature': //Parameter
          rowData[1] = value; //Value in column B
          result = 'Written on Column B';
          rowData2[1]= value;
        case 'humidity': //Parameter
          rowData[2] = value; //Value in column C
          result += ', Written on column 3';
        case 'moisture' :
          result += ', Written in Column D';
          result = "unsupported parameter";

insert the 3 parameters that are received in the first sheet and the second sheet. The sole reason I create two sheets that get the same data is to demonstrate how to use two sheets.
The example is only logging 3 values, but when looking at the code it should be quite obvious on how to add more data.
One warning: when you edit the script, the previous versions remain, when done, you need to select the right version of the script to use:

An ESP8266 with Static IP, sending DHT11 data via MQTT, going into DeepSleep

Sometimes, all you need is a quick program that needs to send some sensor data from a location that you don’t want or can’t put a power cable to.
Battery power, sending an ESP8266 into deepsleep for most of the time can be the answer.

The program here does just that. For reasons of speed it uses a static IP set up.
Dont forget to connect GPIO16 to the RST pin on your ESP8266.  A bare ESP8266 is your best choice. If you are using an ESP8266-01, you will need to have good eyes to solder a wire between pin 16 and the RST pin.

You can download the full program here.

MX1508 vs L9110S vs TB6612 vs L293D Motordriver board

Three popular, cheap motordriver boards that are available on chinese webstores are the MX1508, L9110S and TB6612.

At Aliexpress and Banggood, the MX1508 is listed as a “Dual Channel L298N DC Motor Driver Board PWM Speed Dual H Bridge Stepper Module”. However it is an MX1508.

This board can drive two motors independently. It is an H-bridge configuration for each motor and so can drive the motors in either direction. It is rated at 1.5A on each motor with a peak of 2.5A.
The module itself is not really breadboard friendly. The pin spacing is 0.1″ but the various connectors are at aberrant spacing.

The MX1508 from Shenzhen Guanghui Electronics Co., Ltd. has a working voltage of 1.8-5Volt, but can drive motors with an operating voltage of 2-9.6 Volt. The module however, has only 1 Voltage input, that supposedly can take 2-10 Volt. The digital part of the MX1508 however is fed through a 220 ohm resistor with the resulting voltage being capped by what I presume to be a zenerdiode of 5 Volt. With most hobby motors being either 6 or 12 Volt, 10 Volt may not be the most practical voltage.
When analyzing the PCB, one odd thing becomes clear: Pin 4 (VDD1), the input for the operating voltage of Motor A, seems to be not connected to anything. Yet the module works. Connecting it (to pin 8) seems the right way to go though. It is very well possible that it is an unintentional mistake in the PCB.

The MX1508 has no fly back/free-wheeling diodes present, though they are advised by the chip manufacturer. As a result, there might be big spikes on the powerline when you are reversing the direction, which could lead to problems with other devices connected. Connect the motor like in the figure to the right. Controlling a DC motor with an H-bridge such as the MX1508 is in fact quite simple. Yet there is a library available.

The only (currently) available datasheet is in chinese. It advises to put a 100nF over each motor output, something that is not done on the module PCB. The MX1508 is also sold (e.g. by LCSC) under the alias TC1508.

There is also an MX1208 with a current of 1.3A (2A peak). These are only available as a bare chip, not as a module.

Although the chinese webstores market the MX1508 as a replacement for the L298N motordriver, that is with its 46 Volts and 4 amp total, quite a strech.

The L9110S
So, if 10 Volt motorvoltage is not enough, there is the L9119S from Asic. The available module has in fact two L9110 chips, as they each control only one motor. The module can control 2 motors that according to the datasheet can be 2.5-12V with a maximal continuous current of 800mA , with a peak of 1.5~2 Amp.
The labeling on the input pins can be a bit confusing, but it is like this:

  1. B-IA: Motor B Input A
  2. B-IB: Motor B Input B
  3. GND: ground
  4. VCC: 2.5V-12V DC
  5. A-IA: Motor A Input A
  6. A-IB: Motor B Input B

The L9110 chips each have 2Vcc and Ground pins, but there is no indication that these should get different voltages (like one for the chip and one for the motor) and on the module PCB all grounds are connected and all Vcc’s are connected. There is however a small point of concern in using the module with a non-TTL level like 12 Volt: The inputs are pulled up via 10k resistors to the Vcc. That means that if you use 12 Volt DC for the motor and a 3v3 microprocessor to control the motor, on a LOW level there will be possibly flowing 1.2 mA into the uC pin. That does not need to be a major problem, but it is something that needs to be taken into account. If it is a problem, a simple optocoupler circuit can be put in between. An ILQ615 or TLP281-4 (available as pre-built module) is very suitable for this. If you want to make it yourself, 4 of the below circuit should do:


The optocoupler is left open collector as the input pins of the L9110 module already do have pullups. Other than an optocoupler a transistor or Fet can be used as well.

This circuit does invert, but that can easily be fixed in software. The TLP281 module referenced above has added transistors the un-invert the signal.

Though motors are easy to control with an H-bridge such as this one, there are several libraries for the L9110S.

The datasheet is very minimal. There is one table that is worth a closer look:


according to this table a voltage of 2.5Volt on its input pins is considered a HIGH. that means that both 5Volt Arduino’s as well as 3V3 ESP’s or Raspberry Pi’s can be used. However, this is at 9Volt Vcc. The datasheet gives no info at all about this level if fed with 12 Volt. If an optocoupler is used as described above, this should not be a point of concern.

The TB6612 from Toshiba is another relatively cheap board available at chinese webstores. It can drive 2 DC motors (or one stepper) with 1.2A per channel (3A peak). It runs at 2.7V-5V logic (2.7-5.5V according to some sources). The motor voltage is separate from the 2.7-5.5 Volt logic voltage. Good for motor voltages from 4.5V up to 13.5V. The H-bridge is built with FETs and it comes with built in kick-back diodes.
The control of the TB6612 is a bit akin to the L293. Where usually the speed of a motor in an H-bridge is controlled by a PWM signal on one of the 2 control pins, the TB6612 has a seperate PWM pin, next to the 2 control pins. It can be pulled up if not used. So you set the direction with 2 pins and the speed with a 3rd pin. Of course it is still possible to use the H bridge control pins with PWM. It also has a standby pin to rapidly disable both motors. Connect to ground to disable, pull-up for normal operation. On the ‘Chinese’ boards, it is usually left floating. The adafruit module has it pulled up to 5V via a 10k resistor.
Though DC motors are easy to control with an H-bridge such as this one, there is a library for the TB6612.


The L293D for a long time has been a workhorse among the motordrivers. The module displayed here is available cheaply and works well (but read on).
Contrary to some other L293D boards it comes with a 78M05 (the DPAK chip on the board) stabilizer that takes the motor voltage and brings that back to 5Volt to feed the L293 chip. The Vcc pins on the module are therefore outputting 5Volt with 0.5A max current, you don’t have to provide 5Volt to this board.

The 5 pins at the bottom do not have a silkscreen label, but their function is clarified in the picture. The Vcc is connected to pin16 (Vcc1). That pin is the 5V supply for the chip (and you can feed your micro controller from it.

The ‘Vin’ on the screw connector is attached to pin8 (Vcc2) which is for the 4.5-36V motor voltage. On this particular module the Enable pins are pulled HIGH with jumpers. These Enable pins can also be used for PWM, but with the Enable pin HIGH, PWM can also be fed to the IN pins.

This particular module does not seem to follow the cooling PCB recommendations made in the datasheet, so an additional heat sink may be necessary.
A problem rose when I wanted to use the module with 12 Volt, which should be well within its specs. A pair of 0.6 amp motors did well on a smooth surface but on a more demanding surface (grass), the motors seemed to shut down, which I attribute to thermal shut down of the L293. This also happened when I fed the same motors with 6 Volt.
I have no experience with the somewhat simpler modules such as these that come without a 7805:L293D Motor Driving Module


Though the first 3 modules are clearly no competition for the L298, how do they compare to the L293?

MX1508 L9110 TB6612 L293
Motor Voltage 2-10V 2.5-12V 4.5-13.5 4.5-36V
Logic Voltage 1.8-5V 2.5-12V 2.7-5.5 5V
Current 1.5A 800mA 1.2A 1A (0.6 for 293D)
PeakCurrent 2.5A 1.5-2A 3A 2A (1.2 for 293D)

Given the fact that all these modules are fairly small and come without heatsink, it is my opinion they are only suitable for toy style motors that operate well within the specs of the chip. For any more serious work at least a heatsink should be added.

drv8825Other popular motordriver boards -especially in 3D printers- are the A4988 and DRV8825. You will find a very good discussion of those two in this comparison. The various TMC2xxx drivers (such as the TMC2200 or TMC2208) are generally seen as  more silent (and more expensive) replacements for those.

For anyone interested in using the more powerful TB6600 or TB6560 modules or the TB67S109AFTG chip for say a NEMA23 or NEMA24 motor, you will find an excellent article here. If you consider those drivers, consider the the digital DM556 or DM542 drivers as well.