I have been doing software for almost as long as I have memory. I have also been following the open source hardware movement since its inception a few years ago, but I have never found the time or the disposition to attempt a hardware project myself.

I have decided to do a small hardware project to get my feet wet, a project that I will fully document and discuss here with the hope to encourage and motivate others to do the same.

Here is the list I came up with:

Arduino board: Arduino Uno R3(https://www.amazon.com/Arduino-Rev-3...g=thirdwish-20)
Motor controller: AH286-YL-13(datasheet:http://www.kynix.com/uploadfiles/pdf/AH286-YL-13.pdf)
Distance sensor: HC-SR04(https://www.amazon.com/Ultrasonic-Mo...g=thirdwish-20)
Bluetooth slave: BT2S Bluetooth to Serial Slave
Prototyping board and cables:microtivity IB401
USB cable
Vehicle kit

arduino-robot-01.jpg
The remote control driver interface

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

Qt Code:
  1. /**
  2.  * @file remote_control.h
  3.  * @brief remote control driver definition for the Michelino robot.
  4.  * @author Miguel Grinberg
  5.  */
  6.  
  7. namespace Michelino
  8. {
  9. class RemoteControlDriver
  10. {
  11. public:
  12. /**
  13.   * @brief abstract representation of a remote command.
  14.   */
  15. struct command_t {
  16. enum key_t { keyNone, keyF1, keyF2, keyF3, keyF4 };
  17. int left; /**< left side speed, between -255 and 255. */
  18. int right; /**< right side speed, between -255 and 255. */
  19. key_t key; /**< function key. */
  20.  
  21. command_t() : left(0), right(0), key(keyNone) {}
  22.  
  23. // conversion functions
  24. void goForward();
  25. void goBack();
  26. void turnLeft();
  27. void turnRight();
  28. void stop();
  29. void leftAndRightSliders(int l, int r);
  30. void forwardBackAndLeftRightSliders(int fb, int lf);
  31. void joystick(int x, int y);
  32. };
  33.  
  34. /**
  35.   * @brief Class constructor.
  36.   */
  37. RemoteControlDriver() {}
  38.  
  39. /**
  40.   * @brief Return the next remote command, if available.
  41.   * @param cmd a reference to a command_t struct where the command
  42.   * information will be stored.
  43.   * @return true if a remote command is available, false if not.
  44.   */
  45. virtual bool getRemoteCommand(command_t& cmd) = 0;
  46. };
  47. };
To copy to clipboard, switch view to plain text mode 
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.

Let's look at the implementation of the conversions:

Qt Code:
  1. void goForward()
  2. {
  3. left = right = 255;
  4. }
  5. void goBack()
  6. {
  7. left = right = -255;
  8. }
  9. void turnLeft()
  10. {
  11. left = -255;
  12. right = 255;
  13. }
  14. void turnRight()
  15. {
  16. left = 255;
  17. right = -255;
  18. }
  19. void stop()
  20. {
  21. left = right = 0;
  22. }
  23. void leftAndRightSliders(int l, int r)
  24. {
  25. left = l;
  26. right = r;
  27. }
  28. void forwardBackAndLeftRightSliders(int fb, int lr)
  29. {
  30. left = fb - lr;
  31. right = fb + lr;
  32. if (left < -255)
  33. left = -255;
  34. else if (left > 255)
  35. left = 255;
  36. if (right < -255)
  37. right = -255;
  38. else if (right > 255)
  39. right = 255;
  40. }
  41. void joystick(int x, int y)
  42. {
  43. forwardBackAndLeftRightSliders(y, x);
  44. }
To copy to clipboard, switch view to plain text mode 
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 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:

Qt Code:
  1. /**
  2.  * @file bluestick_remote_control.h
  3.  * @brief remote control driver for the BlueStick Android remote control app.
  4.  * @author Miguel Grinberg
  5.  */
  6.  
  7. #include "remote_control.h"
  8.  
  9. namespace Michelino
  10. {
  11. class RemoteControl : public RemoteControlDriver
  12. {
  13. public:
  14. /**
  15.   * @brief Class constructor.
  16.   */
  17. RemoteControl() : RemoteControlDriver(), lastKey(command_t::keyNone) {}
  18.  
  19. virtual bool getRemoteCommand(command_t& cmd)
  20. {
  21. cmd.stop();
  22. cmd.key = command_t::keyNone;
  23.  
  24. if (BTSerial.available() <= 0)
  25. return false; // no commands available
  26. char ch = BTSerial.read();
  27. switch (ch) {
  28. case '8': // up
  29. cmd.goForward();
  30. break;
  31. case '2': // down
  32. cmd.goBack();
  33. break;
  34. case '4': // left
  35. cmd.turnLeft();
  36. break;
  37. case '6': // right
  38. cmd.turnRight();
  39. break;
  40. case 'A': // function key #1
  41. case 'C':
  42. cmd.key = command_t::keyF1;
  43. break;
  44. case 'B': // function key #2
  45. case 'D':
  46. cmd.key = command_t::keyF2;
  47. break;
  48. case 'E': // function key #3
  49. cmd.key = command_t::keyF3;
  50. break;
  51. case 'F': // function key #4
  52. cmd.key = command_t::keyF4;
  53. break;
  54. default:
  55. break;
  56. }
  57. if (cmd.key != command_t::keyNone && cmd.key == lastKey) {
  58. // repeated key, ignore it
  59. return false;
  60. }
  61. lastKey = cmd.key;
  62. return true;
  63. }
  64.  
  65. private:
  66. command_t::key_t lastKey;
  67. };
  68. };
To copy to clipboard, switch view to plain text mode 

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.