Introduction: Low Cost WiFi-Controlled Motor Using Lolin D1 Mini With ESP8266 Processor
Target Audience
No experience required! Written for beginners in electronics and C++ programming, interested in "getting your hands dirty" using the amazing-bang-for-the-buck ESP8266 WiFi Microcontroller. Extra time is taken to explain the overall process, where information can be found, and what the code is doing. Does not cover how to solder, what a (Windows) computer is, or the meaning of life.
Introduction
We are going to run C++ on a very low-cost processor with built-in WiFi, the "ESP8266". We’ll use a printed circuit board (PCB) made by LOLIN (formerly WEMOS), called a “D1 Mini". This board can be used as the "brains" for all sorts of electronics projects. We will use it to control a hobby motor from a web browser.
Applications for the ESP8266 don't have to be developed in the Arduino environment, but it's the easiest way to do it. But wait, isn't Arduino a different language? Actually, no. If you Google "there is no arduino language", you will find this article: https://hackaday.com/2015/07/28/embed-with-elliot-there-is-no-arduino-language. It explains how the "Arduino language" is just C++ with a couple of things renamed. So, we are going to write code in the "Arduino language", knowing that it is really just C++.
WARNING: A static discharge can destroy electronic circuits without you even feeling it! Be very careful not to touch your circuits unless you are sure you've discharged any static.
What You Need
AC/DC Adapter with any DC output at least 8V and not more than 15V*
Wire
Soldering Iron with small tip
Solder
Breadboard
Multimeter
* These products appear to be equivalent to the ones that I actually used.
Step 1: Bringing the D1 Mini to Life
Google "wemos d1 mini getting started guide", and under "Videos" this should be near the top: https://www.youtube.com/watch?v=q2k3CzT5qE0. The following steps are based on Innovative Tom’s instructions in the video:
- Solder headers (pins) to the D1 Mini board. Use female headers with long pins. Position them as shown in the picture. (Note: This is the opposite of what is shown in the video.) The video shows how to use a breadboard and additional headers to ensure that the soldered headers don’t lean to either side.
- Go to https://www.arduino.cc/en/Main/Software and download the Windows Installer for the latest stable version (not the "hourly build"). As of this writing, the version is 1.8.5. The default installation options are fine. Say yes to any security questions.
- Go to https://www.arduino.cc/en/Main/Software Download and install the CH340G driver. The file you execute is CH341SER.EXE. Not in video: plug a USB data cable into the D1 Mini board and into your PC. (A power-only USB cable won't work.) The PC may announce that drivers are being installed. If so, note the new COM port.
- Following the instructions on the “Get Started In Arduino” page from the previous step, open the Arduino IDE and go to File -> Preferences. Paste the "Boards Manager” link (http://arduino.esp8266.com/versions/2.3.0/package_esp8266com_index.json) where it says "Additional Boards Manager URLs" and click "OK".
- Within the Arduino IDE:
- Go to Sketch -> Include Library -> Manage Libraries. Type "ESP8266 Microgear" in the search window. Select "ESP8266 Microgear" and install it.
- Go to Tools -> Board: ... -> Boards Manager. Type "ESP8266" in the search window. Select "ESP8266 by ESP8266 Community" and install it.
- Choose Tools -> Board: ... -> WeMos D1 R2 & Mini.
- Go to Tools -> Port and select a COM port other than COM1. If there is more than one choice, you might need to experiment to find the one connected to your D1 Mini.
- Go to File -> Examples -> ESP8266 and choose "Blink".
- On the Blink page, choose the arrow icon to compile & upload the program to the ESP8266. The blinking blue LED is like "Hello World" -- congratulations!
Step 2: Strategy for Adding a Shield
We are going to add a "shield" (a.k.a. a daughterboard) to the D1 Mini, to give our project more abilities. The basic strategy for adding a shield is:
- Find a shield that does what you need. If you Google "d1 mini shields", you'll find this helpful link: https://wiki.wemos.cc/products:d1_mini_shields. All of the shields pictured above, and more, can be added to your D1 Mini. None of them are used in this project.
- Attach the shield to your D1 Mini. You'll need to solder headers to the shield, but if it is a D1 Mini shield, it will then connect to the D1 Mini with no wires required.
- Get a library of C++ commands to control the new shield, and add it to your Arduino environment. By using the library, we don't need to know the gory details of making the hardware work. Instead, we get easy-to-use commands. Almost all shields have a corresponding library.
- Use your C++ programming skills to make the new shield do something cool.
Step 3: Adding a Motor Shield
Now we are going to give our project the ability to control hobby motors.
- If we Google "wemos D1 mini motor shield", a top result is https://wiki.wemos.cc/products:d1_mini_shields:motor_shield. Glancing through the specifications, it appears to be what we're looking for. So we buy one.
- Another top result is https://www.gadgetsthatgo.com/blogs/how-to/wemos-motor-shield-examples. The rest of this tutorial is loosely based on it.
- Let's prepare the hardware:
- The motor shield may come in a sealed package. Open it. Inside you will find a board and four headers. Grab two headers with long pins on one side of the plastic and short pins on the other side. Position them as shown in the photos, with the short pins going through the board.
- When your headers are inserted into the board, flip the whole assembly over and push it into your breadboard. Anywhere it will fit is fine. The purpose is simply to ensure that the pins are straight up. Now the ends of the short pins should be exposed. Solder the short pins to their surrounding holes. Don't allow cold-solder joints! Cold-solder joints cause bizarre and hard-to-pinpoint problems.
- Now it is time to connect a motor and power for the motor. We will connect the power supply and Motor A as shown in the image from HobbyComponents.com (another result in our "wemos d1 mini motor shield" Google search).
The motor we will use is a surprisingly-powerful 59-cent-sale-price hobby motor purchased from MCM Electronics (now Newark). We don't have a datasheet; it was sold as an "8V-to-35V (DC) Hobby Motor". So the motor can handle up to 35 volts, but the motor shield cannot. The WeMos Motor Shield web page says the VM input (VM could stand for Voltage for Motor) can handle up to 15V. This means we can use any old DC power supply that puts out a voltage between 8V and 15V. Most "bricks" that plug into the wall are labelled with their output voltage. Find one with an output of at least 8V DC, but less than 15V DC.
We also need two wires to connect our motor. Their length isn't critical, but should be long enough to allow us to mount the motor apart from our circuit boards. As for gauge (diameter), they should also be small enough to solder to our board, but big enough to carry a modest amount of electrical current. A good guess would be a wire that, with the insulation removed, will barely fit through the holes on the circuit board. - When the soldering is done, inspect your work for shorts. Did your solder accidentally touch anywhere that it should not? If you have a multimeter, use it to check for shorts.
- Disconnect the D1 Mini from USB to power it down, and unplug your motor’s DC power supply so the shield is powered down. Then plug your motor shield into the top (USB connector side) of your D1 Mini board. Make sure that the TX pin on the shield plugs into the TX pin on the D1 Mini.
Step 4: Install the Library for the Motor Shield
Now we want to install a library for the motor shield. If you Google "how to add a library to arduino", a top result is https://www.arduino.cc/en/Guide/Libraries. That article is the basis of the following instructions:
- Open a new browser page and point it to https://github.com/wemos/WEMOS_Motor_Shield_Arduino_Library
- On the GitHub page, click Clone or Download -> Download ZIP. Note: It doesn't matter where you put the download, but you will need to know where it is.
- Start the Arduino IDE. Go to Sketch -> Include Library -> Add .ZIP Library. Tell it where to find the WEMOS_Motor_Shield_Arduino_Library-master.zip file you just downloaded.
- Now, if you go back to Include Library, WEMOS Motor Shield should be in the list.
- Now it is time to see if it works. If you haven't already, reconnect the D1 Mini to your computer.
- In the Arduino IDE, choose File -> Examples -> WEMOS Motor Shield -> motor_base_2.
- Click on the arrow icon to compile and upload. What happens? To get more information, select Tools -> Serial Monitor. If you see a bunch of garbage, adjust the baud rate. Note that motor_base_2 includes this command: Serial.begin(250000). The Serial Monitor shows what the D1 Mini board is saying via the USB cable.
- If the motor's speed seems to match the values on your Serial Monitor, congratulations!
Step 5: Understanding the Motor_base_2 Example Code
Let's walk through the example program and see what it does.
float pwm;
- Declares a variable "pwm" of type "float".
Motor M1(0x30,_MOTOR_A, 1000);
- Declares a "motor object" (object-oriented programming here) called M1. In parenthesis are the address (0x30), the pins to control (A2, A2), and the frequency (1000 Hertz = 1KHz).
void setup () {
- Part of a standard Arduino program, this function includes the things that only have to be done once, to get our hardware ready to go.
Serial.begin(250000);
- Sets the baud rate - the speed at which characters are sent. The receiver must know what rate to expect, or it won't correctly interpret what it receives. So, the setting in the Serial Monitor running on your computer must match the value set here.
void loop() {
- This code in this function will execute forever, over and over, until we unplug our D1 Mini.
Serial.printf(...
- "Prints" a text message out the serial port, i.e. over the USB cable to the host PC. We will see these messages on the Serial Monitor of the Arduino IDE.
for (pwm = 30; pwm <= 100; pwm+=0.1) {
- A loop from 30 to 100. How many times will it execute? (Hint: Not 70.)
M1.setmotor( _CW, pwm);
- Says to rotate the motor clockwise, with speed equal to the value of pwm.
M1.setmotor(_STOP);
- Stop the motor.
delay(200);
- Halt the program for 200ms (0.2 seconds).
When you boil this program down, you will realize that we're controlling the motor with just two distinct lines of code:
Motor my_motor (address, output, pwm frequency)
- Declare a motor object called "my_motor", specify where we can reach it (an attribute of the shield which requires soldering to change), tell it which output pins to control, and say how fast to toggle its output.
my_motor.setmotor (command, value)
- Send a command to the motor object you declared earlier.
Some commands require a value. Using WordPad (the Arduino IDE won’t open this file type) to open WEMOS_Motor.h (in the motor shield library; you may need to search for it), we find a list of commands:
#define _SHORT_BRAKE 0
#define _CCW 1
#define _CW 2
#define _STOP 3
#define _STANDBY 4
Collectively, these are just an enumerated type, i.e. assigning meaningful names to integer values. We now know what 5 commands we can use. _CW = ClockWise and _CCW = CounterClockWise.
You can look at and try the other example programs to learn more about what these commands do. Don't be afraid to experiment! But do be afraid to have the motor instantly change directions. It's really hard on the motor.
Step 6: Use the D1 Mini As a Web Server
Let's say we want to control things from a web browser. It could even be from your smartphone. Sounds hard, right? But, you already have example code in your Arduino environment, and all we need to do is tweak it to make it do what we want.
- In the Arduino environment, choose File -> Examples -> ESP8266WiFi -> WiFiWebServer.
- In the program file, replace "your-ssid" with your SSID (the name of your home WiFi network). Then set "password" to your WiFi password.
- Before going any further, save your modified program by going to File -> Save As... "wifi_motor_control".
- Let's test wifi_motor_control. First, open the Serial Monitor in the Arduino environment. Make sure the monitor's serial speed (baud rate) matches the speed specified in the setup() function of your program.
- Upload wifi_motor_control to the D1 Mini board. On the Serial Monitor window, you should see something like "Connecting to My_WiFi_SSID", followed by "WiFi connected", "Server started", and an IP address. Make a note of the IP address; we'll need it later.
Step 7: Modify Web Server Code to Control a Motor
We're going to evolve our working but not-very-useful program to control our motor. Test your code by frequently compiling as you go, so you can easily find and fix any mistakes made along the way.
Remember motor_base_2? Open it. Every copy in this section is from motor_base_2 and every paste is to wifi_motor_control.
- We need the motor control library.
Copy this line:
#include "WEMOS_Motor.h"
Paste it after this line:
#include <ESP8266WiFi.h> - We want to create an instance of Motor in our program, so we can control our real, physical motor.
Copy this line:
Motor M1(0x30,_MOTOR_A, 1000);//Motor A
Paste it after this line:
WiFiServer server(80);
"M1" is now a global object that we can access from anywhere in our program. - We need a global variable to store the speed of our motor.
After this line:
Motor M1(0x30,_MOTOR_A, 1000)
Add this line:
float motor_speed = 0; - Let’s create a little function called set_motor().
Above this line:
void setup() {
Add these three lines:
void set_motor() {
M1.setmotor(_CW, motor_speed);
} - Instead of having our web server toggle GPIO2, let’s have it set the speed of our motor.
Delete all of the following lines:
// prepare GPIO2
pinMode(2, OUTPUT);
digitalWrite(2, 0);
int val;
val = 0;
val = 1;
// Set GPIO2 according to the request
digitalWrite(2, val);
s += (val)?"high":"low"; - Now we'll add motor control commands.
After this line:
if (req.indexOf("/gpio/0) != -1)
Add this line:
motor_speed -= 5; - After this line:
else if (req.indexOf("/gpio/1") != -1)
Add this line:
motor_speed += 5; - Before this line:
client.flush();
Add this line:
set_motor(); - Modify this line:
String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nGPIO is now ";
Replace GPIO with Motor speed. - Before this line:
s += "</html>\n";
Add this line:
s += motor_speed; - Compile your program, then upload it.
Step 8: Testing Our Motor Control and Finding a Bug
- Open a browser, and type in the address /gpio/1 and press Enter.
- If you’ve followed all instructions so far, you should see this in your browser:
Motor speed is now 5.00. - Now that you know the address is correct, bookmark it on your browser’s toolbar so you can use it quickly and repeatedly.
- Is the motor actually running? Probably not. There are two reasons why:
- The motor needs more than 5% of full power to get going. Mine starts spinning at 25%. Even if your project needs a higher value to get going, this is normal and to be expected.
- The Lolin motor shield firmware has a bug! It goes to sleep after about 10 seconds, and won’t wake up again. motor_base_2 hides this bug by constantly changing the speed of the motor. Google “wemos motor shield bug” if you want to learn more. Or, just know that the bug exists, and we must either fix it or work around it. We’re going to do the latter.
- To see your motor spin, do this:
- Have your web browser ready to go.
- Find a little button on the side of your D1 Mini board. It’s the reset button. Press it.
- Immediately click your bookmark at least 5 times. See the value on your screen go up each time. Your motor should start to spin. What happens when the value goes over 100%?
- Also bookmark /gpio/0, which makes the motor go 5% slower. Now you have full control of your motor speed from any browser, including from a mobile device. The commands are going to your D1 Mini over WiFi. You have achieved the goal of Remote Control Via WiFi!
Step 9: Working Around the Motor Shield Firmware Bug
Near the top of your “wemos motor shield bug” search results, there is an article on how to replace the buggy firmware with something better. But it sounds like a lot of trouble. Do we have another option? What if we just never let the motor shield go to sleep? Processors, including the ESP8266, have timers that can call code at a specified frequency. We’ll use a timer to call our set_motor() function once a second. As usual, we don’t have to figure out the details; we easily find an example already in the Arduino environment to help us. Follow along as we borrow and modify the code for our needs.
Google “ESP8266 timer example”. Found on the list is this article: https://circuits4you.com/2018/01/02/esp8266-timer-ticker-example. In the very first paragraph it says “To avoid crash issues I recommend use of Ticker instead of Timer. Ticker performs same function as timer.” We’ll follow this advice.
Under File -> Examples -> Ticker, find and open TickerBasic. Every copy in this section is from TickerBasic and every paste is to wifi_motor_control.
- We’ll need to use the Ticker library.
Copy this line:
#include <Ticker.h>
Paste it after this line:
#include "WEMOS_Motor.h" - We need to create an instance of Ticker.
Copy this line:
Ticker flipper;
Paste it after this line:
float motor_speed = 0; - flipper isn’t a very good name. Change it to ticker_0.
- To enable our Ticker:
Copy this line:
flipper.attach(0.3, flip);
Paste it at the very end of the setup() function. - To make the code we just pasted work for our program:
Change flipper to ticker_0. ticker_0 is the name of our ticker instance.
Change 0.3 to 1.0. Now we have a one-second ticker.
Change flip to set_motor. Now the ticker will call our set_motor function. - We probably shouldn’t have our main loop and the ticker both trying to call set_motor. It could cause problems if they both happen simultaneously.
Delete this line in loop():
set_motor(); - Compile and upload your program.
- Use your browser to make the motor run.
- Let a minute pass.
- Use your browser to change the speed. It should work. Congratulations, you just worked around a Lolin firmware bug! Now, once a second, the motor’s speed with be updated with the current value of motor_speed.
Step 10: Ways to Make It Better
We’ve achieved our objective. If this were a government contract, we could collect our pay and go home. But we want to make it better. Here are some ideas, more or less in order of difficulty:
- Clean up the comments in our code.
- Skip over motor speeds between 0 and 25. (What happens if the speed is negative?)
- Limit our motor speed value to 100, since that’s full speed.
- Have buttons for motor control on a web page.
- Have more features for motor control on the web page, like STOP, DIRECTION, etc.
- Make the LED on the D1 Mini board blink out the board’s IP address.
- Connect a second motor to the motor shield and add software controls for it.
- Add a power shield so the project doesn’t have to be plugged into USB power.
- Stick the project in a case and a propeller on the motor shaft. (Remote-control fan!)
- Eliminate the crashes that sometimes happen when you change the speed.
- Come up with your own ideas. Even better, share them!
Step 11: A Web Page With Buttons for Motor Control
Let's take an idea from our brainstorming session, “Have buttons for motor control…”, and do it together. Teaching HTML is beyond the scope of this tutorial, so I will just present the code here and let you research it if you want to know more.
- /gpio/0 and /gpio/1 are poor names for speed control. Let’s change them to - and +, respectively. Note that your browser bookmarks no longer work; you should delete them.
- We’re going to have a single page with control buttons. So, delete this code:
else {
Serial.println("invalid request");
client.stop();
return;
} - This line is overly complex:
String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nMotor speed is now ";
Replace it with this:
String s = "<!DOCTYPE HTML>\r\n"; - Now, on the very next line, add these two lines to add buttons:
s += "<a href = +><button>FASTER</button></a>"; - We still want to display the speed, so add one more line to give it a label:
s += "<p>Speed: "; - Upload your improved code.
- Navigate your browser to the D1 Mini’s IP address.
- Enjoy!