ESP8266: NodeMCU Motor Shield Review

Do you like this post? A regular project like the PlaneSpotter Color takes many hours of my free time to develop and maintain. Would you offer me a beer for my hard work if I was your neighbour? Modern technology called teleportation makes it easy to send me a beer. Just click here. Thank you!

If you want to engage with your ESP8266 into robotics there is now an almost ready-to-go solution: the NodeMCU Motor Shield. If my research is correct then it was the Doctors of Intelligence & Technology (http://doit.am/) who created it and also are offering some LUA code to get started. This post shares my experience with the motorshield and also with a two engine “Smart Car” kit that nicely combines with the motorshield.

Motorshield

If you want to drive motors with your ESP8266 you have to be careful. The General Purpose Input/Output (GPIO) pins of the ESP8266 (as well as the similar Arduinos) don’t allow you to draw the amount of current required for a motor. But how to do that? You could build something on your own by using a few electronic components (transistor, capacitor, etc) or you could just pick something off the shelf that does exactly that for you. That’s where the Doctors of Intelligence & Technology and their motor shield comes in. It is designed to carry a NodeMCU V1.0 on its back. Note: the motor shield won’t fit for a NodeMCU V0.9! It has convenient terminals to attach two two-ways motors (or according to some source one stepper motor). It also has terminals to attach the power source that drives the motors and the NodeMCU. And as a clever addition you can decide to have just one power source for both motors and NodeMCU by setting a jumper accordingly.
One of the two terminals (Vin for NodeMCU) can be cut off with a power switch which saves you from taking out the batteries.
Besides the terminals there are also male pins which lead out GPIO 0-8 (in NodeMCU terminology), the reset pin, the analog/digital converter, the UART pins and the SPI pins.
NodeMCU V1.0 Powershield
A sign on the PCB makes it easy enough to plugin the NodeMCU the correct way: just make sure that the antenna of the ESP8266 and the antenna symbol on the motor shield are on the same side.

Smart Car Kit

The Smart Car Kit is the ideal companion for the NodeMCU if you want to get started into WiFi controlled cars or robotics. Some vendors even sell it in one big package for about USD $22 (see here). It comes with 2 two-ways motors with reduction gear, two main wheels and one support wheel, a chassis, a battery pack for 4 AA cells and additional screw driver.
You can put everything to getter within just about 15 minutes once you have figured out where the parts have to go. I only had two issues with the kit: while you might be able to attach the wires to the engine without soldering I would recommend that you do this with a soldering iron. The other (bigger) problem was the supporting wheel. It turns easy enough around the axis of the wheel itself but the z-axis which should adjust when the car makes a turn is not working so smoothly. This can have the effect that even if both motors go at the same speed the supporting wheel is stuck in a weird position and keeps your car to pull to one side. I solved this by using some duct tape and fixing the wheel at the “forward” position. This might be suboptimal for turning but works good enough on smooth floors. It might be a problem though on soft carpets but I haven’t tried that.
The Smart Car Kit

Bringing it together: the WiFi Car

The next step was to put all the pieces (Smart Car, battery pack, NodeMCU V1.0 and Motor Shield) together. The holes of the motor shield fit perfectly into the holes of the acrylic chassis and the kit comes with enough screws and nuts to attach it. Also the battery pack can be screwed easily onto the chassis. The way I placed the components onto the chassis left only little space between the motor shield and the battery pack which made it hard to attach the wires with the terminals.

Programming the Car from the Arduino Environment

The inventors of the kit have sample code for LUA on their website. But I decided to try to program the car with the Arduino IDE. And that is really easy: 4 pins control the engines. Pins 4 and 5 (Arduino Terminology) control the speed of the engines with PWM in a default range of 0..1023 while pins 0 and 2 define the direction the engines will turn. 0 is backwards and 1 is forward but this might depend on who you wired the engines up to the shield. In my case I had to swap the wires of one of the two engines so a “1” would symbolise the same direction for both engines.
First I wrote a little Arduino sketch to test if everything was working alright and if I understood the interface correctly. From there I incrementally improved the code and I’m now able to steer it with any device with a modern browser, a built in accelerometer by just balancing the device in the direction I want to car to move. While it is still far from perfect it works surprisingly well: a javascript program reads out the accelerometer of the controlling device (e.g. an iPhone) and sends AJAX requests to the WiFi connected Smart Car. These commands are then taken to steer the motors.
In this scenario a big part of the logic is in the javascript code. While this could be hosted on the ESP8266 EEPROM I decided to load it from an external server which makes it really easy to program: I only have to change the file on the remote server. The ESP8266 firmware remains unchanged for most of the time.
To connect to the device you only have to find out the IP that has been assigned to the NodeMCU. Then open that IP in your Smart Phone browser and move level your device to steer the car.
Here is a little video of that setup:

Shopping List:

Posted by squix78

7 comments

  1. Hello Dani,
    Thank you very much for your nice development.
    I tried it, and found some parts, I like to change:
    wifi-car.ino

    …style="font-size:24pt

    In smartcar.js the "onclick/ondownkey" function does not work with the Chrome browser version 46.0.2490.76 under Android version 5.1.0.
    Do you have an idea, how to fix that?
    In some statements I hat to change the Math.min to Math.max to get it working right.
    smartcar.js
    var lastMove = 0;
    var e_dsp = 0;
    var version = 1;
    function move(left, right) {
    var now = Date.now();
    if (lastMove + 200 < now) { // orig. 200 ms
    lastMove = now;
    var request = new XMLHttpRequest();
    // if direction is opposite, change sign of +left and +right
    request.open('GET', '/engines/' + Math.round(-left) + "," + Math.round(-right), true);
    request.send(null);
    }
    }
    document.onkeydown = function detectKey(event) {
    var e = event.keyCode;
    e_dsp = e; // save for display
    if (e==87){ move(600, 600);}
    if (e==83){ move(600, -600);}
    if (e==65){ move(-600, 600);}
    if (e==68){ move(-600, -600);}
    }
    if (window.DeviceMotionEvent) {
    window.addEventListener('devicemotion', deviceMotionHandler, false);
    } else {
    document.getElementById("dmEvent").innerHTML = "Accelerometer not supported."
    }
    function deviceMotionHandler(eventData) {
    acceleration = eventData.accelerationIncludingGravity;
    var left = 0;
    var right = 0;
    if (Math.abs(acceleration.y) > 1) { // back-/forward
    var speed = acceleration.y * 100;
    if (acceleration.y > 0) { // add 300 to decrease dead zone
    left = Math.min(1023, speed + acceleration.x * 40 + 300);
    right = Math.min(1023, speed – acceleration.x * 40 + 300);
    } else {
    left = Math.max(-1023, speed + acceleration.x * 40 – 300);
    right = Math.max(-1023, speed – acceleration.x * 40 – 300);
    }
    } else if (Math.abs(acceleration.x) > 1) { // circle only
    var speed = Math.min(1023, Math.abs(acceleration.x) * 100);
    if (acceleration.x > 0) {
    left = Math.min(1023, speed + 300);
    right = Math.max(-1023, -speed – 300);
    } else {
    left = Math.max(-1023, -speed – 300);
    right = Math.min(1023, speed + 300);
    }
    }
    if (Math.abs(left) > 200 || Math.abs(right) > 200) { // orig. 100,100
    move(left, right);
    }
    var direction = "stop";
    // if direction is opposite, change sign of +left and +right
    direction = "[" + Math.round(acceleration.x) + "," + Math.round(acceleration.y) + "," + Math.round(acceleration.z) + "]
    " + Math.round(-left) + ", " + Math.round(-right) + "
    key: " + e_dsp;
    document.getElementById("vector").innerHTML =direction;
    document.getElementById("block").style.fontSize = "x-large";
    }

  2. There is an app called Blynk that I am trying to do this with. Could you give me some advise on the motor control part of my code?

    //#define BLYNK_DEBUG
    //#define BLYNK_PRINT Serial // Comment this out to disable prints and save space
    #define BLYNK_PRINT Serial // Comment this out to disable prints and save space
    #include
    #include

    int motorA ;
    int motorB ;
    int X=0;
    int Y=0;
    int factor=0;
    int maximo=0;

    // You should get Auth Token in the Blynk App.
    // Go to the Project Settings (nut icon).
    char auth[] = "b41ff7f1659b4badb694be4c59601c2c";

    void setup()
    {
    // Set console baud rate
    Serial.begin(9600);

    Blynk.begin(auth,"100Grand","Mob4life");

    pinMode(motorA, OUTPUT);
    pinMode(motorB, OUTPUT);
    pinMode(0,OUTPUT);
    pinMode(2,OUTPUT);

    }

    BLYNK_WRITE(V1)
    {
    int X1 = param[0].asInt();
    X=X1;
    int Y1 = param[1].asInt();
    Y=Y1;

    }

    BLYNK_WRITE(V0)// slider de 100 a 255!!!!
    {
    int vel = param.asInt();
    maximo=vel;
    }

    void loop()
    {

    if(X == 128 && Y == 128) // Stop
    {
    motorA = 0;
    motorB = 0;
    analogWrite(5, motorA);
    analogWrite(4, motorA);
    analogWrite(0, motorB);
    analogWrite(2, motorB);
    }

    if(X > 123 && X < 132 && Y >= 129) //Forward
    {
    motorA = Y;
    motorB = Y;

    motorA = map(motorA, 129,255 , 450,maximo);
    analogWrite(5, motorA);
    digitalWrite(0,LOW);
    motorB = map(motorB, 129,255 , 450,maximo);
    analogWrite(4, motorB);
    digitalWrite(2,HIGH);
    }

    else if(X > 123 && X < 132 && Y <= 127) //Reverse
    {
    motorA = Y;
    motorB = Y;

    motorA = map(motorA, 127,0 , 450,maximo);
    analogWrite(5, motorA);
    digitalWrite(0,HIGH);
    motorB = map(motorB, 127,0 , 450,maximo);//something is wrong with HIGH signal
    analogWrite(4, motorB);
    digitalWrite(2,LOW);
    }

    else if(Y > 123 && Y < 132 && X <= 127) //Reverse
    {
    motorA = X;
    motorB = X;

    motorA = map(motorA, 127,0 , 450,maximo);
    analogWrite(5, motorA);
    digitalWrite(0,HIGH);
    motorB = map(motorB, 127,0 , 450,maximo);//something is wrong with HIGH signal
    analogWrite(4, motorB);
    digitalWrite(2,HIGH);
    }

    Any help you could provide would be greatly appreciated. Thanks!

    Blynk.run();
    }

  3. There is an app called Blynk that I am trying to do this with. Could you give me some advise on the motor control part of my code?

    //#define BLYNK_DEBUG
    //#define BLYNK_PRINT Serial // Comment this out to disable prints and save space
    #define BLYNK_PRINT Serial // Comment this out to disable prints and save space
    #include
    #include

    int motorA ;
    int motorB ;
    int X=0;
    int Y=0;
    int factor=0;
    int maximo=0;

    // You should get Auth Token in the Blynk App.
    // Go to the Project Settings (nut icon).
    char auth[] = "b41ff7f1659b4badb694be4c59601c2c";

    void setup()
    {
    // Set console baud rate
    Serial.begin(9600);

    Blynk.begin(auth,"100Grand","Mob4life");

    pinMode(motorA, OUTPUT);
    pinMode(motorB, OUTPUT);
    pinMode(0,OUTPUT);
    pinMode(2,OUTPUT);

    }

    BLYNK_WRITE(V1)
    {
    int X1 = param[0].asInt();
    X=X1;
    int Y1 = param[1].asInt();
    Y=Y1;

    }

    BLYNK_WRITE(V0)// slider de 100 a 255!!!!
    {
    int vel = param.asInt();
    maximo=vel;
    }

    void loop()
    {

    if(X == 128 && Y == 128) // Stop
    {
    motorA = 0;
    motorB = 0;
    analogWrite(5, motorA);
    analogWrite(4, motorA);
    analogWrite(0, motorB);
    analogWrite(2, motorB);
    }

    if(X > 123 && X < 132 && Y >= 129) //Forward
    {
    motorA = Y;
    motorB = Y;

    motorA = map(motorA, 129,255 , 450,maximo);
    analogWrite(5, motorA);
    digitalWrite(0,LOW);
    motorB = map(motorB, 129,255 , 450,maximo);
    analogWrite(4, motorB);
    digitalWrite(2,HIGH);
    }

    else if(X > 123 && X < 132 && Y <= 127) //Reverse
    {
    motorA = Y;
    motorB = Y;

    motorA = map(motorA, 127,0 , 450,maximo);
    analogWrite(5, motorA);
    digitalWrite(0,HIGH);
    motorB = map(motorB, 127,0 , 450,maximo);//something is wrong with HIGH signal
    analogWrite(4, motorB);
    digitalWrite(2,LOW);
    }

    else if(Y > 123 && Y < 132 && X <= 127) //Reverse
    {
    motorA = X;
    motorB = X;

    motorA = map(motorA, 127,0 , 450,maximo);
    analogWrite(5, motorA);
    digitalWrite(0,HIGH);
    motorB = map(motorB, 127,0 , 450,maximo);//something is wrong with HIGH signal
    analogWrite(4, motorB);
    digitalWrite(2,HIGH);
    }

    Any help you could provide would be greatly appreciated. Thanks!

    Blynk.run();
    }

  4. hi,

    i am using the same motor shield with node mcu.
    i want A+ and A- to be (+,-) when motor is in clock wise direction and (-,+) when in counter clock.
    this was working with L9110S DC/Stepper Motor Driver Module H Bridge.

    but now with node mcu motor shield i only get A (+,-) and not A(-,+)

    .
    in my arduino code i have used the following pin.

    //Motorized Valve pins
    const int Direction_01 = 4; // 4 or 0
    const int Direction_02 = 5; // 5 or 2

    // set the motor control pins to outputs
    pinMode(Direction_01, OUTPUT);
    pinMode(Direction_02, OUTPUT);
    digitalWrite(Direction_01, LOW);
    digitalWrite(Direction_02, LOW);

    void openValve(){
    digitalWrite(Direction_01,LOW);
    digitalWrite(Direction_02,HIGH);
    }

    void closeValve(){
    digitalWrite(Direction_01,HIGH);
    digitalWrite(Direction_02,LOW);
    }

  5. Hi, do you have any idea how the pins on this thing map to the code? For example, these d0-d8 or the others. I can’t seem to find it anywhere. If you have any idea, let me know, I want to attach some lasers to my robot 🙂

Leave a Reply