NodeMCU Motor Shield Review

I just received my NodeMCU Motor Shield to go with my NodeMCU v1 ESP12e development board and robot chassis.

So the board has a bunch of screw terminals on it – A+, A-, B+ and B- to connect two motors, and then VM/GND (up to 9v for the microcontroller) and VIN/GND (up to 36v for the motors). However the mostly undocumented feature is that if you jumper the VIN/VM pins near to the power switch, you can supply up to 9v to VIN/GND and it will feed that into the motors and also into the NodeMCU’s 3.3v regulator. I’ve used this configuration with two 18650 Li-Ion batteries that provide about 8v when fully charged (nominal 3.7v each).

Alternatively, but with some caution you can power the motors and the ESP12e via the NodeMCU’s micro USB port.

I had a poke around with a multimeter and found that if you put 8v into the VIN screw terminal and jumper the VIN/VM pins you get:

  • 8v from the VM screw terminal;
  • 8v from the VIN pin on the nodemcu itself;
  • 8v from the two VIN pins on the motor shield;
  • 3.3v from the three 3.3v pins on the modemcu;
  • 3.3v from the 3.3v pin on the motor shield;

The motors seem to pull whatever they need – mine are 6v motors and they’re pulling about 6.25v when full on (1023 PWM) from the A+/B+ screw terminals.

If you power from USB the VIN pins run at about 4.4v and the motors run slower. The power button also does nothing. I think I’ll just use USB for programming rather than power, but you could use a USB power bank I guess, but at least it means you don’t have to unplug the NodeMCU from the motor shield to reprogram it, although it is a bit tight as the USB connector ends up only a few millimeters from the screw terminal blocks.

I initially wrote my own Arduino sketch which is a webserver that runs on the ESP12e and contains forward/backward/right/left/stop buttons.

// include libraries
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

// configure server
ESP8266WebServer server(80);

const char *form = "<center><form action='/'>"
"<button name='dir' type='submit' value='4'>Forward</button><br>"
"<button name='dir' type='submit' value='1'>Left</button> "
"<button name='dir' type='submit' value='2'>Right</button><br>"
"<button name='dir' type='submit' value='3'>Reverse</button><p>"
"<button name='dir' type='submit' value='5'>Stop</button>"
"</form></center>";

void stop(void)
{
    analogWrite(5, 0);
    analogWrite(4, 0);
}

void forward(void)
{
    analogWrite(5, 1023);
    analogWrite(4, 1023);
    digitalWrite(0, HIGH);
    digitalWrite(2, HIGH);
}

void backward(void)
{
    analogWrite(5, 1023);
    analogWrite(4, 1023);
    digitalWrite(0, LOW);
    digitalWrite(2, LOW);
}

void left(void)
{
    analogWrite(5, 1023);
    analogWrite(4, 1023);
    digitalWrite(0, LOW);
    digitalWrite(2, HIGH);
}

void right(void)
{
    analogWrite(5, 1023);
    analogWrite(4, 1023);
    digitalWrite(0, HIGH);
    digitalWrite(2, LOW);
}

void handle_form()
{
    // only move if we submitted the form
    if (server.arg("dir"))
    {
        // get the value of request argument "dir"
        int direction = server.arg("dir").toInt();

        // chose direction
        switch (direction)
        {
            case 1:
                left();
                break;
            case 2:
                right();
                break;
            case 3:
                backward();
                break;
            case 4:
                forward();
                break;
            case 5:
                stop();
                break;
        }

        // move for 300ms, gives chip time to update wifi also
        delay(300);
    }
    
    // in all cases send the response
    server.send(200, "text/html", form);
}

void setup()
{
    // connect to wifi network
    WiFi.begin("essid", "passphrase");

    // static ip, gateway, netmask
    WiFi.config(IPAddress(192,168,1,2), IPAddress(192,168,1,1), IPAddress(255,255,255,0));

    // connect
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(200);
    }
  
    // set up the callback for http server
    server.on("/", handle_form);

    // start the webserver
    server.begin();

    pinMode(5, OUTPUT); // 1,2EN aka D1 pwm left
    pinMode(4, OUTPUT); // 3,4EN aka D2 pwm right
    pinMode(0, OUTPUT); // 1A,2A aka D3
    pinMode(2, OUTPUT); // 3A,4A aka D4
}

void loop()
{
    // check for client connections
    server.handleClient();
}

I also tried the Blynk joystick code with my Android phone. I’m still not happy with either solution though, as I need a way to move in more than one direction at once to give a smooth turning action – for example I can rotate left/right, but can’t drive forward at the same time, so I need to play with timings a bit more. Also I found that PWM at 512 seemed quite slow, so sticking with ultra-fast 1023 for full speed motors!

There’s a bunch of pins broken out on the motor shield – GPIO 0-8, UART, ADC, SPI etc. so you could add a servo or LED’s. Here is another useful post, and here is the pretty useless documentation.

Robot Tinkering

I’ve started to receive the components I need for my 2WD obstacle-avoiding robot.

I got the motor shield which is a clone of the Adafruit one it seems, however its quite poorly made – the silkscreen that says which servo is which is backwards, one of the L293D chips wasn’t sitting straight in its socket, the high voltage terminal blocks for the motors are dangerously close to shorting on the Uno’s ICSP pins and USB socket (I fixed with some insulating tape) and the reset button is soldered at a bit of an angle! It does at least fit on the Uno SMD clone, I wasn’t sure it would due to all the extra pins they’ve added to the Uno.

I tried out both motors and servo’s using the shield and it worked. I found that its pushing it a bit to run both servo’s at once as they use the power from the Uno, not the external power, however I’m only using one, and I could get two to work if you don’t overwork them – like spinning from 0-180 in 15ms steps!

The motors work well, they certainly go faster with 6v applied to the ext_pwr terminals than 4.8v that 4xAA’s would give me, so I’m really considering that 7.4v LiPo now.

I found that the mini variable DC/DC converters I’ve got actually switch to a boost converter when the voltage drops to about 1v above what you want, so that’s not much use, but the micro models stay at the voltage they’re set to, so won’t suddenly go to 6v when you’re trying to get 5v from a discharging 7.4v LiPo!

My Salea clone logic analyser arrived and I can’t believe how small it is – about 1×5.5×2.5, smaller than a 9v PP3 battery! It works just fine with the new Logic v1.20 beta software and Sigrok’s Pulseview software. In some ways I prefer Pulseview as it has more decoders and a proper GTK+3 theme. I’ve not had much success with sniffing SPI/I2C off an ESP8266/nRF24L01+/DS1307 yet, but certainly got the duty cycle etc. from an Arduino running Blink just fine. I really need those test hooks!

Compiling CyanogenMod 9

I initally added instructions here for compiling ColdFusionX as I was helping to maintain the project. Now that it looks like we’ll get an official CM9 for ZTE Blade, its been deprecated, so here’s the CM9 build instructions. It takes about 50mins to compile (including kernel) which is only about 10mins more than CM7+kernel would have taken on my 12Gb RAM, 3.2GHz Intel Core i5 Linux box with SSD.

1. Download CM9 (use repo sync to update) this is about 15Gb worth:

mkdir -p ~/cm9/vendor/zte/blade
curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
chmod a+x ~/bin/repo
cd ~/cm9
repo init -u git://github.com/CyanogenMod/android.git -b ics

2. Download ZTE proprietary libs (use git pull to update) and copy into place. you’ll probably only have to do this once as they don’t get updated often:

cd
git clone https://github.com/koush/proprietary_vendor_zte.git -b ics
rsync -av --delete ~/proprietary_vendor_zte/blade/ ~/cm9/vendor/zte/blade/ --exclude='.git*'

3. Compile the rom – the first time you do this it will download the kernel source (you can add CM_EXTRAVERSION=-whatever on the end of the export line):

cd ~/cm9/vendor/cm/
./get-prebuilts
cd ~/cm9/
export USE_CCACHE=1 CM_SNAPSHOT=1
. build/envsetup.sh
lunch cm_blade-userdebug
make bacon -j8

Updating The Sun JDK On Debian

The final version of sun-jdk-6 in Debian Wheezy before it was removed was 6u26. The latest from Oracle (excluding Java7) is 6u30, so I decided to manually install it myself.

In fact it couldn’t be easier, all you do is download the jdk-6u30-linux-x64.bin file and run it, which extracts it to a directory called jdk1.6.0_30, which you then rename to java-6-sun-1.6.0_30 and move to /usr/lib/jvm, you then delete the java-6-sun symlink and recreate it to point to the newer directory:

root@localhost:/usr/lib/jvm$ ln -s java-6-sun-1.6.0_30 java-6-sun

As alternatives is setup to point to /usr/lib/jvm/java-6-sun there’s no need to update anything for the files we need to build Android for example:

root@localhost:/usr/lib/jvm$ update-alternatives --get-selections | grep java
...
appletviewer                   auto     /usr/lib/jvm/java-6-sun/bin/appletviewer
firefox-javaplugin.so          auto     /usr/lib/jvm/java-6-sun/jre/lib/amd64/libnpjp2.so
java                           manual   /usr/lib/jvm/java-6-sun/jre/bin/java
javac                          auto     /usr/lib/jvm/java-6-sun/bin/javac
javadoc                        auto     /usr/lib/jvm/java-6-sun/bin/javadoc
javah                          auto     /usr/lib/jvm/java-6-sun/bin/javah

jar seems to have been replaced by fastjar on Debian, so there’s no need to link the Oracle version in /usr/lib/jvm/java-6-sun/bin/jar

Finally its a good idea to add this environment variable in ~/.bashrc – again pointing to the symlink, not the specific version:

export JAVA_HOME="/usr/lib/jvm/java-6-sun"

Apparently Cyanogenmod7 will compile using OpenJDK6 (and 7 with a patch) even though AOSP won’t compile with anything other than Sun JDK6.

I’ve also updated the blog to WordPress 3.3

What A Recovery

I’ve been playing with compiling Cyanogenmod7’s fork of Koush’s ClockWorkMod Recovery 3.0.2.7

The github source is here and of course Gerrit for the latest code under review.

First you compile CM7 for your target platform as usual. This is needed to create some supporting libraries and the actual recovery.img file, including the kernel for your phone of course:

. build/envsetup.sh && brunch blade

Then you create the signed flashable zip:

TARGET_PRODUCT=cyanogen_blade make recoveryzip

That results in out/target/product/blade/utilities/update.zip

You can also set the product to zte_blade but because it will differ from the CM7 build target, it will recompile a lot of stuff, but it still works just fine (in fact that’s what I have installed).

ROMManager still reports the version as 3.0.2.7, but when you boot into recovery it shows as “CWM-based recovery 4.0.1.4”. It seems to be faster doing backups than Koush’s version as it doesn’t output verbose messages and progressbars.

Update: just noticed the zip isn’t actually suitable for a first-time recovery install, its only an update to replace a stock recovery or an earlier clockworkmod. the recovery.img file is is fine though – it includes the kernel and formats the /recovery partition.