Controlling the LEGO Mindstorms NXT with Node.js Part 2: It's Moving

In the first part of this series we made sure that we can connect to our NXT and send commands via Node.js. Today, we’re exploring how we can control the motors of our LEGO Mindstorms robot to make it move.

As it turns out, the nodejs-nxt package is quite simple and has no comfort features to make the robot run faster or slower, or maybe even help with curves and turns. But that’s fine, because it’s a great opportunity to learn a thing or two about good old Serialport along the way.

If you want to take a closer look at the code used in this article, you can check out this GitHub repository.

The final result: the robot moving forward, turning and moving back

How to control Lego NXT Motors

Let’s start with the basics. To control the NXT motors via Bluetooth, we need to send the correct commands. Commands are sent to the NXT via so-called Telegrams. A telegram is basically a bulk of bytes, each byte containing certain information about how the robot should behave. If you want to read more about this you can read the How to Control Lego NXT Motors article on robotappstore.com.

Add missing byte enumerations in nodejs-nxt

Out of the box, nodejs-nxt already provides most of the enumerations for the bytes we need to send, but unfortunately enumerations for the power and the turn ratio are missing. So let’s add those.

As I wrote in the first article, I have already created a fork of the nodejs-nxt package – we use the forked version to make our changes.

// nxt.js
exports.Power = {
  n100: 0x9C,
  n75: 0xB5,
  n50: 0xCE,
  n25: 0xE7,
  0: 0x00,
  25: 0x19,
  50: 0x32,
  75: 0x4B,
  100: 0x64
};

First we add enumerations for controlling the power of the motors of the NXT in percent. 0-100 are percentage values of how much power the motor should receive. The n in n25-100 means negative so those values make the motor turn in the opposite direction.

// nxt.js
exports.TurnRatio = {
  n100: 0x9C,
  n75: 0xB5,
  n50: 0xCE,
  n25: 0xE7,
  0: 0x00,
  25: 0x19,
  50: 0x32,
  75: 0x4B,
  100: 0x64
};

We also want to make our robot turn, so we declare enumerations for the turnRatio byte.

 exports.Mode = {
   MotorOn: 0x01,
   Brake: 0x02,
-  Regulated: 0x04
+  Regulated: 0x05
 };

In addition, there is a little bug in the original nodejs-nxt package. The 0x04 value for the motor mode byte does nothing, instead we have to use 0x05 to switch the motor into regulated mode.

Controlling the NXT motors with Node.js

So now that we know the basics about how to send commands to the NXT and preparing nodejs-nxt we can go ahead and write our abstraction to make our robot move.

// nxt.js
const Nxt = require('nodejs-nxt');

// ...

const drive = (nxt, {
  port,
  power = Nxt.Power[100],
  mode = Nxt.Mode.Regulated,
  regulationMode = Nxt.RegulationMode.MotorSync,
  turnRatio = Nxt.TurnRatio[0],
  runState = Nxt.RunState.Running,
  tachoLimit = 0x00,
}) => new Promise((resolve, reject) => {
  nxt.SetOutputState(
    port,
    power,
    mode,
    regulationMode,
    turnRatio,
    runState,
    tachoLimit,
    (error, response) => {
      if (error) reject(error);
      resolve(response);
    },
  );
});

const stop = (nxt, { port }) => drive(nxt, {
  port,
  power: Nxt.Power[0],
  runState: Nxt.RunState.RampDown,
});

module.exports = {
  drive,
  stop,
  // ...
};

Above you can see that we’ve created two wrapper functions around the nodejs-nxt SetOutputState method. The first drive function sends some bytes to make the NXT move forward.

Even more important than the drive function is the stop function because the motors keeps running until you send a command to stop them again (or the batteries die, of course).

There and back again

Now let’s use our two new convenience functions to make our robot move in one direction, turn around and move back in the other direction.

// programs/there-and-back-again.js
const Nxt = require('nodejs-nxt');

const {
  drive,
  makeNxt,
  stop,
} = require('../nxt');
const { pause } = require('../utils');

const run = async () => {
  const nxt = await makeNxt();

  // Move forward.
  await Promise.race([
    drive(nxt, { port: Nxt.MotorPort.B }),
    drive(nxt, { port: Nxt.MotorPort.C }),
  ]);
  await pause(2000);

  // Turn.
  await Promise.race([
    drive(nxt, { port: Nxt.MotorPort.B, turnRatio: Nxt.TurnRatio[75] }),
    drive(nxt, { port: Nxt.MotorPort.C, turnRatio: Nxt.TurnRatio[75] }),
  ]);
  await pause(1700);
  nxt.ResetMotorPosition(Nxt.MotorPort.B, true);
  nxt.ResetMotorPosition(Nxt.MotorPort.C, true);
  await pause(30);

  // Move forward.
  await Promise.race([
    drive(nxt, { port: Nxt.MotorPort.B }),
    drive(nxt, { port: Nxt.MotorPort.C }),
  ]);
  await pause(2000);

  // Stop.
  await Promise.race([
    stop(nxt, { port: Nxt.MotorPort.B }),
    stop(nxt, { port: Nxt.MotorPort.C }),
  ]);
  await pause(1000);

  nxt.Disconnect();
};

run();

The final result: the robot moving forward, turning and moving back

Wrapping it up

It took me a lot of time to figure out that I had to reset the motor position after making the robot turn. Or at least this is how I solved the problem that the robot would always turn back in the other direction immediately after a turn. Unfortunately, there really isn’t a lot of documentation about that stuff out there.

But for now I’m quite happy I got the robot to do pretty much what I imagined it to do.

References


Do you want to learn how to build advanced Vue.js applications?

Register for the Newsletter of my upcoming book: Advanced Vue.js Application Architecture.



Do you enjoy reading my blog?

You can buy me a ☕️ on Ko-fi!

☕️ Support Me on Ko-fi