Building an Arduino Robot, Part VI: Remote Control

Posted by
on under

Arduino Robot

Welcome to the sixth article in the tutorial series in which I'm building a remote controlled Arduino based vehicle robot.

Here is the list of articles I have published so far:

In the past two articles I have implemented the motor and distance sensor control for my little robot vehicle, using sound software development techniques that make this project easier to write and to maintain.

Today I will be implementing the most challenging (and let's admit it, also the most fun) aspect of my project: the ability to remotely control the robot from a smartphone over Bluetooth.

A small improvement

Before I get into the main topic of this article I'm going to tell you about a small modification I've made to my robot that helps with the remote control functionality.

If you recall the article in which I showed you how to program the Bluetooth module, it is not possible to upload a sketch over USB while the Bluetooth module is connected to pins 0 and 1, because those pins are used by the USB/serial communication.

When I started writing the remote control code I became a bit frustrated with having to constantly connect and disconnect Bluetooth and USB. To avoid this hassle I decided to use the SoftwareSerial.h library that comes with the Arduino software to move the Bluetooth connection to a different set of pins.

The change is pretty simple. I moved the connections from the Bluetooth module going into pins 0 and 1 to analog pins 2 and 3 (numbered 16 and 17), which are unused. Then I created a SoftwareSerial object at the top of the sketch:

#define BT_RX_PIN 16
#define BT_TX_PIN 17

#include <SoftwareSerial.h>
SoftwareSerial BTSerial(BT_RX_PIN, BT_TX_PIN);

And then, instead of using the Serial object like in my previous tests, I used the BTSerial object. The setup() in my sketch then changed to:

void setup()
{
    Serial.begin(9600);
    BTSerial.begin(9600);
}

Why keep the Serial.begin() call? Because I elected to keep sending the logging output to the regular serial interface. I now have access to two serial ports, so I will use the Bluetooth connection for remote commands and the USB one for logging and debugging output. Neat!

With the Bluetooth module connected in this way there is no collision with the USB serial interface so I could keep the USB cable connected at all times while developing and testing.

Remote Control device driver

Like in the previous articles, I'm going to start by designing a device driver that can abstract the main sketch from having to deal directly with hardware.

But this case is a little different. We really have two separate pieces to abstract. The most obvious is the communication method. My robot uses Bluetooth, but this is not the only mechanism by which a robot can be controlled. At least I can think of three more, WiFi, RF and infrared. I clearly need a device driver that can read remote commands, regardless of the wireless technology used.

The second, and less obvious dimension to this problem is the remote protocol. Different remote controllers will use different command codes to mean the same thing. For example, one remote control may send the command to turn left as a byte, maybe the letter "L". Another controller with a numeric keypad configuration may use "4" for the same thing. Another, joystick inspired remote control may not support a direct "go left" command at all and may just send instructions as vectors in a two-dimensional grid, so going left would be given as two numbers, (-1, 0). I do not really want to deal with these differences in the high-level robot code because that will make my code more complex. So clearly a driver that can hide these protocol differences from the main sketch is also needed.

So do I need two different drivers, then? Let's think about how the main sketch will obtain remote control commands. Ideally I want something simple, like a getCommand() function that just returns the next command if one is available. If that's all I need then it seems all the main sketch requires is access to one remote control driver that somehow knows how to receive remote controls and interpret them.

There are two ways this could work. If I implement two drivers, for communication and protocol separately, then the main sketch will only talk to the protocol driver, and it will be the protocol driver's job to know what communication driver to talk to. This provides great flexibility, because the same protocol can easily be supported over different communication channels.

And what would be the alternative? I could embed the communication part inside the protocol driver. This is less flexible because now the remote protocol is tied to a particular communication channel. But on the other side it is simpler to implement because I don't need to define a device driver for communication channels, each protocol driver does its own thing to read remote commands.

Which of these options is better? It really depends. If I was working with standard protocols that remote controls of different kinds implement, then having separate protocol and communication drivers makes sense. In this case, however, I believe the kinds of remote controls that I will find are mostly using proprietary protocols. My impression is that I will never need to use a protocol driver with more than one communication method.

Based on this analysis, I'm going to go with the simpler option for now, one driver for the combined protocol and communication handling. I can always break the driver into two in the future if I find the need.

The remote control driver interface

Given the variety of remote controls out there, I need to find a common format that all the remote drivers can use to represent commands. For the purpose of driving a vehicle, I can think of three types of remote controls that I might ever need to support:

  • Keypad based: the robot is controlled with directional keys or buttons.
  • Joystick based: the robot is controlled with a multi-directional stick that returns (X,Y) coordinates inside a circle.
  • Slider based: the robot is controlled with two sliders, in two possible configurations:
    • each slider controls one side of the vehicle
    • one slider controls forward/backward motion, the other controls left and right motion.

In all cases it is safe to assume that in addition to the directional control there are a few function buttons.

As you can see there are significant differences between the types of remote controls, so the job of defining a common format for all of them is not easy.

Obviously I cannot indicate direction with simple forward, backward, left and right commands, since many remote controls can provide a finer degree of directional control and it would be a pity to ignore all that extra information.

So I have to pick one of the more advanced remote control data representations. The one that translates more easily into motor instructions for my vehicle is the one where the controller uses two sliders, each controlling one side of the vehicle. This directly translates into speeds for my two motors, so the application can simply send the data from the remote control into the motors. Since I like to keep the main sketch simple this is the approach that I'm going to take.

But there is a downside to selecting a single data representation for all types of controllers. For remote controls that don't use the chosen format a conversion will be needed. To help with this task the base driver class will provide the conversion functions for all types of remote controls.

Let's take a look at the definition of the remote control device driver interface:

/**
 * @file remote_control.h
 * @brief remote control driver definition for the Michelino robot.
 * @author Miguel Grinberg
 */

namespace Michelino
{
    class RemoteControlDriver
    {
    public:
        /**
          * @brief abstract representation of a remote command.
          */
        struct command_t {
            enum key_t { keyNone, keyF1, keyF2, keyF3, keyF4 };
            int left;   /**< left side speed, between -255 and 255. */
            int right;  /**< right side speed, between -255 and 255. */
            key_t key;  /**< function key. */

            command_t() : left(0), right(0), key(keyNone) {}

            // conversion functions
            void goForward();
            void goBack();
            void turnLeft();
            void turnRight();
            void stop();
            void leftAndRightSliders(int l, int r);
            void forwardBackAndLeftRightSliders(int fb, int lf);
            void joystick(int x, int y);
        };

        /**
          * @brief Class constructor.
          */
        RemoteControlDriver() {}

        /**
         * @brief Return the next remote command, if available.
         * @param cmd a reference to a command_t struct where the command
         *   information will be stored.
         * @return true if a remote command is available, false if not.
         */
        virtual bool getRemoteCommand(command_t& cmd) = 0;
    };
};

For this driver I will use an auxiliary struct that represents a remote command. The representation of a command includes left and right slider values and a possible function key. A remote control driver can provide up to four function keys, all listed in the enum definition.

The getRemoteCommand() method will be implemented by remote control drivers. It takes a reference to a command_t structure and is expected to fill it out appropriately, using one of the provided conversion functions if necessary.

As far as conversion functions I know I'm going to need to cover the basic remotes, with four commands to represent the four directions that they provide and one more to make the robot stop. For the more advance remotes I need to handle left and right side sliders (which really require no conversion, but for the sake of abstraction and consistency I will provide a function as well) and forward/back and left/right sliders, which as you will see in a moment also covers the joystick type controllers.

Let's look at the implementation of the conversions:

            void goForward()
            {
                left = right = 255;
            }
            void goBack()
            {
                left = right = -255;
            }
            void turnLeft()
            {
                left = -255;
                right = 255;
            }
            void turnRight()
            {
                left = 255;
                right = -255;
            }
            void stop()
            {
                left = right = 0;
            }
            void leftAndRightSliders(int l, int r)
            {
                left = l;
                right = r;
            }
            void forwardBackAndLeftRightSliders(int fb, int lr)
            {
                left = fb - lr;
                right = fb + lr;
                if (left < -255)
                    left = -255;
                else if (left > 255)
                    left = 255;
                if (right < -255)
                    right = -255;
                else if (right > 255)
                    right = 255;
            }
            void joystick(int x, int y)
            {
                forwardBackAndLeftRightSliders(y, x);
            }

All but one of these are trivial. The first five just set the left and right sides appropriately, while the sixth copies the values given as arguments, since these match the internal data representation.

The next conversion is the one that requires some thought. Here I have a controller that has two sliders, one vertical to move forward or backward and one horizontal to move left or right. Both sliders can be operated independently, so at any given time the remote control provides forward/backward speed and left/right turn values. Somehow I need to use these two values to derive independent left and right values to power the motors.

The algorithm that I came up with for this conversion is relatively simple. The forward/backward slider is transferred directly into both sides, so that when there is no left/right motion the vehicle moves straight forward or backward. Then the value of the left/right slider is added to the right motor and subtracted from the left motor. If the left/right slider is positioned towards the left then it's value will be negative, so adding it to the right side will lower the power of that side, while subtracting it from the left side will make the left side stronger. And these changes will just make the robot go towards the left side, which is what I want. Of course, the reverse happens when the left/right slider is moved towards the right.

The last conversion function is for joystick type controllers. The good news is that I realized that the joystick's (x,y) coordinate values are really the same as the left/right and forward/backward values in the previous conversion functions, so I can use the same function for those.

Remote control driver implementation

My choice of controller is a free Android app called BlueStick. Here is a screenshot of this app:

Arduino Robot

This is a very simple controller with five directional commands and six function buttons. The directional commands can be triggered by touching arrow buttons on the screen or by tilting the phone. The app documentation provides the codes that are sent over Bluetooth for each of the commands:

'0' = Stop
'8' = Up
'2' = Down
'4' = Left
'6' = Right
'A' = Auto Grab
'B' = Auto Release 
'C' = Grab
'D' = Release
'E' = Rotate Left
'F' = Rotate Right

Note that while this remote control app gives specific names to its six function keys, I will ignore those names and just define my own meaning for these keys.

The above list defines the protocol for this remote control, so this is really all I need to know to be able to implement this driver. So here is the code for the BlueStick driver:

/**
 * @file bluestick_remote_control.h
 * @brief remote control driver for the BlueStick Android remote control app.
 * @author Miguel Grinberg
 */

#include "remote_control.h"

namespace Michelino
{
    class RemoteControl : public RemoteControlDriver
    {
    public:
        /**
          * @brief Class constructor.
          */
        RemoteControl() : RemoteControlDriver(), lastKey(command_t::keyNone) {}

        virtual bool getRemoteCommand(command_t& cmd)
        {
            cmd.stop();
            cmd.key = command_t::keyNone;

            if (BTSerial.available() <= 0)
                return false; // no commands available
            char ch = BTSerial.read();
            switch (ch) {
                case '8': // up
                    cmd.goForward();
                    break;
                case '2': // down
                    cmd.goBack();
                    break;
                case '4': // left
                    cmd.turnLeft();
                    break;
                case '6': // right
                    cmd.turnRight();
                    break;
                case 'A': // function key #1
                case 'C':
                    cmd.key = command_t::keyF1;
                    break;
                case 'B': // function key #2
                case 'D':
                    cmd.key = command_t::keyF2;
                    break;
                case 'E': // function key #3
                    cmd.key = command_t::keyF3;
                    break;
                case 'F': // function key #4
                    cmd.key = command_t::keyF4;
                    break;
                default:
                    break;
            }
            if (cmd.key != command_t::keyNone && cmd.key == lastKey) {
                // repeated key, ignore it
                return false; 
            }
            lastKey = cmd.key;
            return true;
        }

    private:
        command_t::key_t lastKey;
    };
};

If you need a refresher on how the Bluetooth slave module works feel free to reread my previous post about the topic.

This implementation is extremely simple. If the BTSerial class has a character in its queue, then I read it, and depending on what character it is I configure the command, using conversion functions for the directional commands or the key constants for the function keys. I have six function keys in this remote control app, but I decided to just use four in my driver, so I map the two extra keys as duplicates of another two.

I also need to explain what the lastKey member variable is for. As a test I connected the BlueStick app running on my cell phone to a Bluetooth terminal running on another phone, just to confirm that the commands the app sends are in alignment with the documentation. I found that they did match, but also found that the app is constantly sending commands. There isn't really a point in having to handle repeated keys, so I added the lastKey variable to keep track of the last key received. If I find that the new key is the same as the previous one then I just throw it away as a duplicate.

Another important thing to note about this implementation is that it tries to be tolerant of unknown codes. At the start I initialize the command structure as a stop with no function keys. If I receive an unknown character then the sketch will receive a stop command, which will make the robot stop and wait for more commands.

With this basic driver implemented I have enough to incorporate and test the remote control functionality into my sketch, so that's what I'm doing next.

Design the Remote Control Feature

But before I delve into code again, let's discuss how is the robot going to behave, because in the previous article I ended up with a really nice and neat firmware that made the robot run standalone avoiding obstacles, and I'm not really interested in throwing all that code away!

My goal for the remote controlled robot is to incorporate the automated mode I wrote in the previous article as an option that can be enabled with a special remote command. In lack of a better name I'm going to call the automatic mode the "Roomba" mode (I hope I don't get sued for copyright infringement!).

After some consideration I designed the following flow chart for how the robot will operate under remote control:

Arduino Robot

Translating the chart into words this is what I'm going to do:

  • The robot will begin by listening for remote commands, without moving.
  • If a command is received it will be executed, and then it will go back to listen for more commands.
  • There will be a "Roomba" mode command that acts as a toogle.
  • Any other commands sent while the robot is in "Roomba" mode will be ignored.

Adding remote control to the sketch

Okay, now I will move on to the most exciting part, which is to actually write the code to make the robot follow the remote commands.

Starting from the sketch as I left it in the previous article I begin by adding the remote control driver class to the top of the sketch, where all the drivers are:

#define ENABLE_BLUESTICK_REMOTE_CONTROL_DRIVER

#ifdef ENABLE_BLUESTICK_REMOTE_CONTROL_DRIVER
#include "bluestick_remote_control.h"
#define REMOTE_CONTROL_INIT
#endif

This class does not take any arguments to initialize, but I can't really be sure other drivers will be the same, so I keep the same style I used in previous drivers and just make the initialization constant empty this time.

Now I can add a remote control object to the Robot class:

private:
    RemoteControl remoteControl;

And I can initialize it in the Robot class constructor:

    Robot()
        : leftMotor(LEFT_MOTOR_INIT), rightMotor(RIGHT_MOTOR_INIT),
          distanceSensor(DISTANCE_SENSOR_INIT),
          distanceAverage(TOO_CLOSE * 10),
          remoteControl(REMOTE_CONTROL_INIT)
    {
        initialize();
    }

I had been using the concept of states to know if the robot was moving or turning. I now need to have an additional state for when the robot is under remote control:

private:
    enum state_t { stateStopped, stateMoving, stateTurning, stateRemote };

And since I have functions stop(), move() and turn() to enable the other states I should be consistent and have one to enter the remote control state:

    void remote()
    {
        leftMotor.setSpeed(0);
        rightMotor.setSpeed(0);
        state = stateRemote;
    }

I also had the tiny functions that returned true if a given state was enabled, to help make the code a bit more readable. Again, to be consistent I need one for the new state:

    bool remoteControlled() { return (state == stateRemote); }

To initialize the robot I simply put it in remote mode:

    void initialize()
    {
        randomSeed(analogRead(RANDOM_ANALOG_PIN));
        remote();
    }

And finally, I have a new version of my run() method that does all the fun stuff:

    void run()
    {
        unsigned long currentTime = millis();
        int distance = distanceAverage.add(distanceSensor.getDistance());
        RemoteControlDriver::command_t remoteCmd;
        bool haveRemoteCmd = remoteControl.getRemoteCommand(remoteCmd);
        log("state: %d, currentTime: %lu, distance: %u remote: (%d,l:%d,r:%d,k:%d)\n", 
            state, currentTime, distance, 
            haveRemoteCmd, remoteCmd.left, remoteCmd.right, remoteCmd.key);

        if (remoteControlled()) {
            if (haveRemoteCmd) {
                switch (remoteCmd.key) {
                case RemoteControlDriver::command_t::keyF1:
                    // start "roomba" mode
                    move();
                    break;
                case RemoteControlDriver::command_t::keyNone:
                    // this is a directional command
                    leftMotor.setSpeed(remoteCmd.left);
                    rightMotor.setSpeed(remoteCmd.right);
                    break;
                default:
                    break;
                }
            }
        }
        else {
            // "roomba" mode
            if (haveRemoteCmd && remoteCmd.key == RemoteControlDriver::command_t::keyF1) {
                remote();
            }
            else {
                if (moving()) {
                    if (obstacleAhead(distance))
                        turn(currentTime);
                }
                else if (turning()) {
                    if (doneTurning(currentTime, distance))
                        move();
                }
            }
        }
    }

While this new version of run() may seem scary at first, I assure you that only a small portion is new, a good part of it came from the previous article.

I begin by getting all the inputs that I'm going to need, which are the current time, averaged distance sensor reading and remote control command. In case I need to do debugging at some point I kept the log statement I had before, enhanced to include remote control data as well.

For the remote control I have to note if I have received a command or not. This is what haveRemoteCmd does. If this variable is true then the remoteCmd struct holds the command data. If the variable is false then remoteCmd has an undefined value and should not be used.

Once I gathered all my inputs I move on to control the robot behavior. In this new version of the sketch there are two different operating modes. The "Roomba" mode was introduced in the previous article, while today I'm obviously I'm adding the remote control mode.

The if (remoteControlled()) statement takes care of differentiating between the two modes. The if portion deals with the remote control mode, while the else portion does the "Roomba" mode.

Let's look at the part that handles the remote control in detail:

            if (haveRemoteCmd) {
                switch (remoteCmd.key) {
                case RemoteControlDriver::command_t::keyF1:
                    // start "roomba" mode
                    move();
                    break;
                case RemoteControlDriver::command_t::keyNone:
                    // this is a directional command
                    leftMotor.setSpeed(remoteCmd.left);
                    rightMotor.setSpeed(remoteCmd.right);
                    break;
                default:
                    break;
                }
            }

This is extremely simple. If there is a remote command available I then look at the key value that came with it. If the key is keyF1 value then I enable "Roomba" mode by calling move() (see the previous article for an explanation of this method). If the command came without a key (keyNone) then it is a directional command, so all I do is move the left and right values from the remote command into the motors. That's it! Believe it or not this short piece of code fully handles the remote control of the vehicle!

The "Roomba" mode handling is mostly the same as in the previous article, with the exception that I removed the 30 second timeout handling that I had in that version and replaced it with this:

            if (haveRemoteCmd && remoteCmd.key == RemoteControlDriver::command_t::keyF1) {
                // switch back to remote mode
                remote();
            }
            else {
                // ...
            }

And this code simply switches back to remote mode if a remote command is available and its key is keyF1. The rest of this part deals with the "Roomba" mode logic, and this did not change from the previous version of the sketch.

A more advanced remote control driver

To end this article I'm going to implement a second remote control driver. My choice is RocketBot, another Android app. Here is a screenshot of RocketBot:

Arduino Robot

RocketBot offers several different remote control interfaces:

  • Keypad: five buttons for forward, backward, left, right and stop. Exactly like the BlueStick app I implemented above.
  • Joystick: X and Y values obtained from the phone accelerometer.
  • Left/Right: individual controls for left and right motors, calculated from the phone accelerometer input.

The documentation describes the communication protocol in detail. For this controller all commands have two bytes, an operation byte and a data byte. A quick summary of the control codes I will be implementing (not the full list) is below:

  • "D1", "D2", "D3", "D4", "D5": Forward, Left, Right, Backward, and Stop respectively.
  • "A1", "A2", "A3", "A4": Four auxiliary function buttons.
  • "X<number>", "Y<number>": Joystick position. Values come in the 0-200 range, with 100 being the center position, 200 being full speed backward or right and 0 being full speed forward or left.
  • "L<number>", "R<number>": Left/Right controls. Values come in the 0-200 range, with 100 being the center position, 200 being full speed forward and 0 being full speed backward, on each of the sides.

The implementation is a bit more complex than the BlueTick remote, but follows more or less the same style. Here is the full driver implementation:

/**
 * @file rocketbot_remote_control.h
 * @brief remote control driver for the RocketBot Android remote control app.
 * @author Miguel Grinberg
 */

#include "remote_control.h"

namespace Michelino
{
    class RemoteControl : public RemoteControlDriver
    {
    public:
        /**
          * @brief Class constructor.
          * @param from the value in the 0-100 range that maps to 0 in remote control units.
              Any values below from will be interpreted as 0.
          * @param to the value in the 0-100 range that maps to 255 in remote control units.
              Any values above to will be interpreted as 255.
          */
        RemoteControl(int from, int to) : RemoteControlDriver(), 
            scaleFrom(from), scaleTo(to), 
            lastKey(command_t::keyNone), lastX(100), lastY(100), lastL(100), lastR(100) {}

        virtual bool getRemoteCommand(command_t& cmd)
        {
            cmd.stop();
            cmd.key = command_t::keyNone;

            if (BTSerial.available() < 2)
                return false; // no commands available
            unsigned char ch = BTSerial.read();
            unsigned char data = BTSerial.read();
            switch (ch) {
                case 'D': // keypad
                    switch (data) {
                    case 1:
                        cmd.goForward();
                        break;
                    case 2: // down
                        cmd.turnLeft();
                        break;
                    case 3: // left
                        cmd.turnRight();
                        break;
                    case 4: // right
                        cmd.goBack();
                        break;
                    case 5: // stop
                    default:
                        cmd.stop();
                        break;
                    }
                    break;
                case 'X':
                    lastX = data;
                    cmd.joystick(scale(lastX), -scale(lastY));
                    lastL = lastR = 100;
                    break;
                case 'Y':
                    lastY = data;
                    cmd.joystick(scale(lastX), -scale(lastY));
                    lastL = lastR = 100;
                    break;
                case 'L':
                    lastL = data;
                    cmd.leftAndRightSliders(scale(lastL), scale(lastR));
                    lastX = lastY = 100;
                    break;
                case 'R':
                    lastR = data;
                    cmd.leftAndRightSliders(scale(lastL), scale(lastR));
                    lastX = lastY = 100;
                    break;
                case 'A': // function keys
                    switch (data) {
                    case 1:
                        cmd.key = command_t::keyF1;
                        break;
                    case 2:
                        cmd.key = command_t::keyF2;
                        break;
                    case 3:
                        cmd.key = command_t::keyF3;
                        break;
                    case 4:
                        cmd.key = command_t::keyF4;
                        break;
                    }
                    break;
                default:
                    break;
            }
            if (cmd.key != command_t::keyNone && cmd.key == lastKey) {
                // repeated key, ignore it
                return false; 
            }
            lastKey = cmd.key;
            return true;
        }

    private:
        int scale(int value)
        {
            value -= 100;
            if (value >= 0) {
                if (value <= scaleFrom)
                    return 0;
                else if (value >= scaleTo)
                    return 255;
                return (value - scaleFrom) * 255 / (scaleTo - scaleFrom);
            }
            else {
                if (-value <= scaleFrom)
                    return 0;
                else if (-value >= scaleTo)
                    return -255;
                return -(-value - scaleFrom) * 255 / (scaleTo - scaleFrom);
            }
        }

    private:
        int scaleFrom;
        int scaleTo;
        command_t::key_t lastKey;
        int lastX;
        int lastY;
        int lastL;
        int lastR;
    };
};

Unfortunately the code to handle this remote is pretty long.

The RemoteControl class constructor for this controller takes two arguments. These are used to scale the values returned by the remote. For example, setting from = 10 and to = 50 will make this driver report a 0 value for the first 10% of the tilt range in every direction and reach the maximum at the 50% tilt position. This helps in two ways. First, if you hold the phone in your hand it would be impossible to keep it at a perfect zero tilt to make the vehicle stop, so with from = 10 there is a small range of tilt that will not affect the motors. Second, with to = 50 I can reach the maximum position with the phone at 45 degrees instead of a full 90 degrees in each direction.

The constructor also initializes a few member variables that remember the last known values for the different controller values. These variables are necessary because the protocol this remote uses does not send the whole information in a single command, instead each command sends only one value, so for example, when I get an updated X value I need to know what Y to use, so I take it from my lastY member variable.

The getRemoteCommand() method is structured similarly to the previous driver, but here I need to read two bytes from the serial port instead of one and I have a larger number of commands to handle in the switch statement.

The handling of the D and A commands is similar to the other driver. Here is the snippet that handles the new commands in this controller:

                case 'X':
                    lastX = data;
                    cmd.joystick(scale(lastX), -scale(lastY));
                    lastL = lastR = 100;
                    break;
                case 'Y':
                    lastY = data;
                    cmd.joystick(scale(lastX), -scale(lastY));
                    lastL = lastR = 100;
                    break;
                case 'L':
                    lastL = data;
                    cmd.leftAndRightSliders(scale(lastL), scale(lastR));
                    lastX = lastY = 100;
                    break;
                case 'R':
                    lastR = data;
                    cmd.leftAndRightSliders(scale(lastL), scale(lastR));
                    lastX = lastY = 100;
                    break;

The driver could be sending X and Y commands, or it could be sending L and R depending on which mode it is in. Any time I get a command I save it in the proper member variable, and then I use the appropriate conversion function to set the command_t struct.

Note that I have an auxiliary function called scale() that takes care of the scaling using the from and to values given in the constructor.

Also interesting to note that when I get an X or Y command I reset both the lastL and lastR, to make sure I don't keep any older values these two may have had before. Likewise, when I get an L or R I reset the lastX and lastY.

Final words

The updated firmware can be downloaded from github:

Download michelino v0.3, or clone it.

As always, I welcome your questions. Feel free to drop a comment below if there is anything you did not understand or need clarification on.

With the last major piece of my robot firmware completed I could call the project done. Unfortunately I'm not satisfied that easily, even though I have fully achieved the goals I stated at the start of this tutorial series I now have the itch to try other vehicle designs, which I will try to incorporate into my michelino firmware.

You may also want to keep track of the "master" branch of the project on github, as that's where updates will show up.

I'm particularly interested in supporting other motor controllers and more remote controls. If you have any you would like to see implemented let me know, or better yet, fork my project on github, implement it yourself and send me a pull request to get it included in the official project.

Thank you for following me on this tutorial, I hope my articles were of help to you. Best of luck with your Arduino based projects!

Miguel

Become a Patron!

Hello, and thank you for visiting my blog! If you enjoyed this article, please consider supporting my work on this blog on Patreon!

196 comments
  • #26 Nilesh said

    TouchOSC works only with WiFi and moreover its a MIDI controller. I found out there is no app in iOS to control the robot as Apple does not allow serial communication easily. We have to use BLE shield for bluetooth communication in iOS.

  • #27 Nilesh said

    Hi Miguel. I have successfully finished my android controlled robot vehicle. Many thanks to you :). I can now control my robot from my android phone. Both Bluestick and Rocketbot apps do not download in my country so I am using BlueTerm. I can just program any keys to control anything. It works pretty much the same way.

    I observed that to exit the 'roomba' mode keyF1 does not work so I changed it to keyF3 and works perfect.

    Also, while the robot is in 'remote' mode it does not detect walls. I do understand it becomes a contradiction for it to have its own brain while in remote mode but I think it is important it detects the obstacles. What do you think?

    Thanks again.

  • #28 Cadarrius said

    Does anyone know how to upload the sketch that he provided to the robot? Im new to robotics and i built a robot completely identical to his, but I cant get the sketch i download to work. Ive got the sketch to import into the arduino software.. I just need help on uploading it to the bot.. thanks alot!

  • #29 Miguel Grinberg said

    @Nilesh: You can easily make the robot refuse to run into a wall when it is in remote control mode and instead do a turn. All the pieces are there, you just need a new state that suppresses the remote control while the turn is happening.

  • #30 Adam Golinski said

    Well done, great tutorial.
    What could use some clarification is to mention in the previous part that you need to put randomSeed(...) function in the initialize() method if you want to use random() function in your code, since it wasn't obvious for me in this part where did it come from and I had to research the topic myself.

  • #31 Barry said

    Thanks for this great tutorial! It has done for me more than a whole book would have taught in C++ concepts.
    Quick question: I have an iphone therefore my options for remote control are dead. How can I just start the robot in 'roomba' mode without remote control initialization?

  • #32 Miguel Grinberg said

    @Barry: In the constructor, instead of calling "remote()" do "state = stateStopped". That will make it start in Roomba mode. Alternatively, you can use the version of code from the previous article, which just does Roomba.

  • #33 flava said

    Hi Miguel

    I loved your tutorial where you implemented the components with the RC car and very good explanation each step & I appreciate all your hard work, however, I would like to ask you a question regarding the accelerometer sensor, such as, ADXL345 triple axis accelerometer or MMA7455 triple axis accelerometer, how can I make the RC car move with same motion as the phone, for example, if I moved the phone from central point to the left the RC car also should move to left and also forward and brake and brake and when I don't move the phone no action, awaiting your reply kindly help

  • #34 Miguel Grinberg said

    @flava: I'm not sure if I missunderstand your question, but you don't need an accelerator sensor for that. Your phone already has an accelerator, all you need is an app that converts the accelerator's data into commands for the RC car motors. The RocketBot app that I show in this article can do that.

  • #35 flava said

    Hi Miguel again,

    Thanks for the reply so as you are saying that we don't need an accelerometer chip to do that rather to send those commands via Bluetooth and then it will do the trick which is with turning of your mobile in horizontally and moving up and down and the car can be controlled. Do you mean like this. I have another question can I have a look at the source code of the mobile application of both mobile application just to get an idea as my project is almost the same concept like your project

  • #36 Miguel Grinberg said

    @flava: I did not write the remote control apps, those are off-the-shelf apps that I found on the android market.

  • #37 flava said

    Hi again Miguel,

    Thanks for replying to me, just asking do you know where I can get some information to develop the android mobile application if possible, thanks

  • #38 Miguel Grinberg said

    @flava: I don't have much experience with Android development. The Android SDK page that documents the user of accelerometers is here: http://developer.android.com/guide/topics/sensors/index.html

  • #39 flava said

    Hi Miguel,

    Thanks for replying, and I have a question to ask how to control the car by using only the BlueStick Control App, because me & my college we tried to send a command to go forward from the App but there wasn't any response & we upload the final sketch to the arduino uno r3, please tell me what I can do as I'm running out of time as my deadline is on 2nd Jan 2014, thanks in advance & all the help is very much appreciated

    Best Regards,
    Flava

  • #40 Fahmid Chowdhury said

    Hi Miguel,

    I am trying to design a line following robot with some help from this article. Would this be possible to do and how difficult do you think it would be? Many thanks for this article!

  • #41 Miguel Grinberg said

    @Fahmid: Most of the code will be reusable for a line following robot. You will need to hook up two or three line sensors, write a driver for them and then generate motor speeds based on the readings of these sensors.

  • #42 Laydik said

    outstanding tutorials. I had given up on learning C until I came across your series. Thanks for explaining your code in a way that a beginner like me can understand.

  • #43 Laydik said

    Hi Miguel, I just have a quick question that I can't figure out (I printed out every driver and the firmware and read each line, visited many websites, but still can't find out why I get the error msg:'LEFT_MOTOR_INIT was not declared in this scope' same for right motor, it just sounds like a beginner's mistake, but what am I missing? Thanks again for taking the time to help. I will keep looking for a solution in the meantime.

  • #44 Miguel Grinberg said

    @Laydik: LEFT_MOTOR_INIT should appear in a #define statement near the top of the michelino.ino source file. Here is the definition: https://github.com/miguelgrinberg/michelino/blob/v0.3/michelino.ino#L57
    Do you have this line? If you do, then check if you defined ENABLE_ADAFRUIT_MOTOR_DRIVER above.

  • #45 Arturo B said

    Hi Miguel, Thanks for this great tutorial. I have learned a lot!

    My problem is that once i try to upload the prgm to my arduino, it keep telling me this error:

    michelino.ino: In constructor 'Michelino::Robot::Robot()':
    michelino:92: error: no matching function for call to 'Michelino::Motor::Motor(int)'
    /adafruit_motor_driver.h:20: note: candidates are: Michelino::Motor::Motor(int, int, int)
    /adafruit_motor_driver.h:12: note: Michelino::Motor::Motor(const Michelino::Motor&)
    michelino:92: error: no matching function for call to 'Michelino::Motor::Motor(int)'
    /adafruit_motor_driver.h:20: note: candidates are: Michelino::Motor::Motor(int, int, int)
    /adafruit_motor_driver.h:12: note: Michelino::Motor::Motor(const Michelino::Motor&)

    I'm a beginner in this, so i would thank you for your help. By the way, im using the arduino motor shield. I have also changed the adafuit driver, for the arduino one on the master branch.

    Thanks!

  • #46 Miguel Grinberg said

    @Arturo: the Arduino driver takes three int arguments in the constructor. It appears you are passing only one. Make sure that you have these definitions:

    <h1>define LEFT_MOTOR_INIT 12, 3, 9</h1> <h1>define RIGHT_MOTOR_INIT 13, 11, 8</h1>
  • #47 Arturo B said

    Thanks a lot Miguel! It worked!

    I need to say that so far, you are the only person who uploads a instructive so explained, and also answer to our questions.

    Thanks again, Merry Christmas

  • #48 Laydik said

    Thank you for such a quick response. I am glad Arturo mentioned his issue as well, because I am using the Arduino shield too. I did not enable the adafruit driver, instead enabled the arduino motor driver only. I am going to try the solution you suggested to him and report back. Do I still need to enable the adafruit driver in this case?

  • #49 Miguel Grinberg said

    @Laydik: No, you always enable one driver for each type of device. If you enable the Arduino motor shield you have to disable the Adafruit one.

  • #50 Laydik said

    @Miguel, thanks for your follow up. I have been enabling only the arduino driver while commenting out the Adafruit one, but I still can't get anything to work when I run the michelino firmware.
    I was able to test both motors and ultrasonic sensor seperately successfully. While I can connect to BT sensor via android phone I can't see any input in the serial monitor. No compiling errors with michelino.ino anymore however, I am still stuck. I have been working on this project for a week now and making progress, thanks to your tutorials and the comments section posts.

Leave a Comment