Robot Remote Control Using HM-10

From ArduinoInfo
Jump to navigation Jump to search

print this page

HM-10 Bluetooth Low Energy Example

This example uses an the Basic Robot Kit (more about it [/RobotKitMenu HERE]), and the HM-10 (more about it [/HM-10%20Bluetooth%20LE HERE]) to control the robot with an Android app. This How-To covers both the Arduino code, and creating the app in MIT's App Inventor 2.

Hardware:

This project assumes that you have assembled the Robot Kit following the how to HERE. Do this before starting this how
-to. The next step is to connect the HM-10. Connect RX to pin 4, TX to pin 3, Gnd to Gnd, and VCC to 5 V. The Servo and Ultrasonic sensors are not used in this example, but they can stay attached.

HM-10 Bluetooth Module

The HM-10 module works by talking to the Arduino over a serial connection. While the hardware serial port on the Arduino could be used, this would make it very difficult to program the Arduino or use the serial monitor. To avoid this we will use SoftwareSerial to create a new virtual serial port to talk to the HM-10. After the hardware is set up the first thing that should be done is to check that the HM-10 is working. We can do this using a serial communication sketch found [/HM-10%20Bluetooth%20LE#Setting%20Up%20the%20HM-10 HERE], and AT commands. Run the sketch and then open the serial monitor. Make sure that the serial monitor is set to both New Line (NL) and Carriage Return (CR). Type in AT and you should receive an OK back. If you do not get an OK there is something wrong with the HM-10, double check the wiring. Then type in AT+PIN, this will send back the pin that you will need when pairing with the HM-10 from another device. If you would like you can set this to a new more secure pin by sending AT+PIN<yourPin>. In addition AT+HELP will bring up a list of all the AT Commands you can use. It should be noted that unlike normal AT commands these to not require the “?” or “=” identifier.

Robot Control Sketch

The robot control sketch works by bringing in three bytes from the android app passed through the HM-10. The first is the Unit Byte, which tells the Arduino what subroutine to pass the message on to. The second is the Command Byte which gives commands to the subroutine, and the third is the Value Byte, for commands that also have a value with them.

The general framework of the sketch is as follows:
  • Bring in needed libraries

  • Setup: starts both the hardware and the software serial ports at a baud rate of 9600 bps, run a motor test to allow the user to know the motors are working.

  • Loop: Brings in Serial Data from the HM-10 if it is available and converts it from ASCII characters to numerical values. It then uses the Unit byte to call the correct subroutine (LED or Robot).

  • LED support routines: Turns on and off the on-board LED and any other LEDs attached to pin 13 (this is set up for headlights on the robot).

  • Robot support routines: Looks at the Command byte and controls the robot motors.


/*
  YourDuino Example: Bluetooth LE Temperature Reporting

  Description:
  Uses the HM-10 Bluetooth Low Energy module to control the YourDuino Basic Robot Kit from a phone.
  wireless to an Android app written in MIT App Inventor 2, running on an Android device.
  Get HM-10 Here: http://www.yourduino.com/sunshop/index.php?l=product_detail&p=529
  Get the Basic Robot Kit Here: http://yourduino.com/sunshop//index.php?l=product_detail&p=400
  See the how to and smartphone app Here: https://arduinoinfo.mywikis.net/wiki/Robot+Remote+Control+Using+HM-10
  Questions?    noah@yourduino.com

  Wiring:

  HM-10:
  RX to pin 4 (Sofware TX)
  TX to pin 3 (Sofware RX)
  Vcc to 5 V
  Gnd to Gnd

  Robot:
  Ultrasonic and Servo not used, but can be connected
  Enable B to pin 10
  Enable A to pin 9
  In A to pin 8
  In B to pin 7
  In C to pin 6
  In D to pin 5

  Headlight LEDs (optional):
  LEDs to pin 13


*/

/*-----( Import needed libraries )-----*/
//Sofware Serial Library
#include <SoftwareSerial.h>

#define  EnableA  9
#define  EnableB  10
#define  Motor1A  8
#define  Motor1B  7
#define  Motor2C  6
#define  Motor2D  5

//Software Serial
#define TXPIN 3 //Connected to TX on HM-10
#define RXPIN 4 //Connected to RX on HM-10

#define LEDPIN 13 // Pin on Arduino board to which status LED is connected
#define READ_RATE 200 // How often a temperature is sent
#define FLASH_RATE 100 // The on/off period in milliseconds, for the LED Flash status feedback


/*-----( Declare objects )-----*/
// Create software serial instance
SoftwareSerial BTserial(TXPIN, RXPIN);

// Declarations
byte unitIn;
byte unitInDec;
byte cmdIn;
byte cmdInDec;
byte valueIn;
byte valueInDec;

// Initialization
void setup() {
  digitalWrite(LEDPIN, LOW);
  pinMode(LEDPIN, OUTPUT);  // pin 13 (on-board LED) as OUTPUT
  Serial.begin(9600);       // start serial communication at 9600bps
  Serial.print("Sketch:   ");   Serial.println(__FILE__);
  Serial.print("Uploaded: ");   Serial.println(__DATE__);
  Serial.println(" ");
  BTserial.begin(9600);  //start software serial at 9600bps
  Serial.println("BTserial started at 9600");
  //tests motors on startup
  Serial.println("Motor Test");
  Forward();
  delay(200);
  Reverse();
  delay(200);
  Left();
  delay(200);
  Right();
  delay(200);
  Stop();
}


// Arduino Execution Loop
void loop()
{
  if (BTserial.available())//if there is serial data available from the HM-10
  {
    unitIn = BTserial.read(); //read in the 3 values
    cmdIn = BTserial.read();
    valueIn = BTserial.read();
    unitInDec = unitIn & 0x0F; //These three statements convert from ASCII characters to numerical values
    cmdInDec = cmdIn & 0x0F;   // for more about this see the How too
    valueInDec = valueIn & 0x0F;
    Serial.print("UnitByte:"); //print out the 3 values to the serial monitor for debug purposes
    Serial.print(unitInDec, DEC);
    Serial.print(" CommandByte:");
    Serial.print(cmdInDec, DEC);
    Serial.print(" ValueByte:");
    Serial.println(valueInDec, DEC);
    switch (unitInDec) // This is a switch, it's like a string of if then statesments, but more susicnt. it tests unitInDec and if it's equal to the case, it runs what is inside that case.
    {
      case 0: //LED unit
        LED(); // calls the LED routine
        break;
      case 1: //Relay unit (not used in this sketch)
        break;
      case 2: //Robot unit
        Robot(); //calls the robot routine
        break;
      default:
        Serial.println("Unknown Unit");
        Stop(); //if it doesn't recognize the comand tells the robot to stop, so that loss of data doesn't send the robot out of control.
        break;
    }
  }
  delay(READ_RATE); // this dealy keeps the data comming in from the HM-10 from getting mixed up
}

//Support Routines
//Flashes onboard LED n number of times
void flashLED(int n)
{
  // Flash the LED n times, to provide an on board status indicator
  for (int i = 1; i <= n; i++) //This is an iterative function, it does what is inside the loop n times.
  {
    digitalWrite (LEDPIN, HIGH);
    delay (FLASH_RATE);
    digitalWrite (LEDPIN, LOW);
    delay(FLASH_RATE);
  };
  return;
}

void LED()
{
  switch (cmdInDec)
  {
    case 0: //turn off LED
      digitalWrite(LEDPIN, LOW);
      break;
    case 1: //Turn on LED
      digitalWrite(LEDPIN, HIGH);
      break;
    case 2: //Flash LED 5 times
      flashLED(5);
      break;
    default:
      Stop(); //stop just in case
      Serial.println("Unknown LED Command");
      break;
  }
}

void Robot()
{
  switch (cmdInDec)//this switch statement looks at the Command Byte to control the motors
  {
    case 0: //Stop
      Stop();
      break;
    case 1: //Forward
      Forward();
      break;
    case 2: //Left
      Left();
      break;
    case 3: //Right
      Right();
      break;
    case 4: //Reverse
      Reverse();
      break;
    default:
      Serial.println("Unknown Robot Command");
      Stop(); //stop just in case
      break;
  }
}

//The motor driver takes in 6 inputs. the first two EnableA and EnableB allow the motors to come on.
//Then by setting Motor1 A and B to hight or low, the direction can be controlled, the same is true for Motor2

void Forward()
{
  //set the left motor to forward
  digitalWrite(Motor1A, LOW);
  digitalWrite(Motor1B, HIGH);
  //set the right motor to forward
  digitalWrite(Motor2C, LOW);
  digitalWrite(Motor2D, HIGH);
  //Enable both motors
  digitalWrite(EnableA, HIGH);
  digitalWrite(EnableB, HIGH);
}
/*---------------------------*/
void Reverse()
{
  digitalWrite(Motor1A, HIGH);
  digitalWrite(Motor1B, LOW);
  digitalWrite(Motor2C, HIGH);
  digitalWrite(Motor2D, LOW);
  digitalWrite(EnableA, HIGH);
  digitalWrite(EnableB, HIGH);
}
/*---------------------------*/
void Stop()
{
  digitalWrite(Motor1A, LOW);
  digitalWrite(Motor1B, LOW);
  digitalWrite(Motor2C, LOW);
  digitalWrite(Motor2D, LOW);
  digitalWrite(EnableA, LOW);
  digitalWrite(EnableB, LOW);
}
/*---------------------------*/
void Left()
{
  digitalWrite(Motor1A, HIGH);
  digitalWrite(Motor1B, LOW);
  digitalWrite(Motor2C, LOW);
  digitalWrite(Motor2D, HIGH);
  digitalWrite(EnableA, HIGH);
  digitalWrite(EnableB, HIGH);
}
/*---------------------------*/
void Right()
{
  digitalWrite(Motor1A, LOW);
  digitalWrite(Motor1B, HIGH);
  digitalWrite(Motor2C, HIGH);
  digitalWrite(Motor2D, LOW);
  digitalWrite(EnableA, HIGH);
  digitalWrite(EnableB, HIGH);
}
/*---------------------------*/



Robot Control Android App

This app needs to take user input and send it over the a Bluetooth link to the Arduino. This app also must handle finding and pairing with a Bluetooth device, in this case the HM-10. To achieve this the app sends 3 bytes of data at a time. The first is a Unit byte. This tells the Arduino what part of the code we want to talk to. Unit 0 is the the onboard LED and optional headlights. Unit 2 is the robot motors. The second byte, Command byte gives the Arduino a command for that unit. The third byte, Value byte is a value for the command, if one is necessary. The reason for setting up the code this way is because it allows for easy additions to the app and the Arduino sketch. If another component wants to be added it is easy to do it.

App Files


The .apk is the finished version of the app ready to be installed on a phone. Simply download it onto your phone and run it. The .aia is a project file for App Inventor 2, which can be imported and viewed/edited.
[/file/view/BluetoothLERobot.apk/619609693/BluetoothLERobot.apk BluetoothLERobot.apk]
[/file/view/BluetoothLERobot.apk/619609693/BluetoothLERobot.apk BluetoothLERobot.apk]
  • [/file/detail/BluetoothLERobot.apk Details]
  • [/file/view/BluetoothLERobot.apk/619609693/BluetoothLERobot.apk Download]
  • 3 MB
[/file/view/BluetoothLERobot.aia/619609597/BluetoothLERobot.aia BluetoothLERobot.aia]
[/file/view/BluetoothLERobot.aia/619609597/BluetoothLERobot.aia BluetoothLERobot.aia]
  • [/file/detail/BluetoothLERobot.aia Details]
  • [/file/view/BluetoothLERobot.aia/619609597/BluetoothLERobot.aia Download]
  • 672 KB



MIT's App Inventor 2

MIT's App Inventor 2 allows for the easy creation of fairly powerful android apps. It uses a drag and drop programming interface very similar to scratch, as well as having a window for laying out an apps user interface. One of the best features of App Inventor is the ability to link a smartphone to it as a companion. This allows the app to run on your phone as you write your code, allowing you to debug and troubleshoot in real-time. Some intro level tutorials are [HERE], it's strongly encouraged that you work through them to get a feel for App Inventor before you try to move on to the Bluetooth temperature app.


Setting Up

The first step is to open the app inventor and create a new project. You should see a blank screen template with toolbars on the left and right. We now need to add in an extension to communicate over Bluetooth LE. There are a few extensions that MIT has provided, a BTLE extension is one of them. Go to the link [HERE] and download the BluetoothLE extension. Then in the app inventor project window go down the the Extensions tab at the very bottom of the left side toolbar. Click import extension and follow the prompts to upload BluetoothLE. Now click and drag BluetoothLE onto the blank canvas. You should see it pop up in the Non-Visible Components area.

Now add a timer. Timers are under sensors, drag one onto the canvas and drop it. It should show up as timer1. Now click on it at the bottom of the canvas. On the components toolbar to the right, you should see the clock highlighted. Click Rename and name this BuffTimer1. Set it to 100 milliseconds. Uncheck the Enabled button, this timer will be enabled in the software when it's needed. This timer will be used to display messages for only a short period of time.

Next add a notifier from the user interface tab of the Pallet toolbar. This can be used to pop up error messages.

App Inventor Companion
Next we will set up the App Inventor Companion. The first step is to install the app inventor app on the Android device you want to use for testing. You can can find it in the Play Store [HERE], or simply scan this QR Code.
ai2companionQRHM10.png
Once you have the app installed open it up and click Scan QR code. Then go to app inventor on your computer and click connect on the uppermost toolbar. Click the AI companion button and a QR Code will be popped up for you to scan. The AI companion tool is not perfect, particularly when changing screen elements around, and it sometimes crashes. Usual this cause the app to hang. To reconnect close the companion app and restart it. Then in app inventor go back to the connect tab. Click reset connection and then hit AI companion to be issued a new QR code to connect with.

User Interface and Layout

To create the user interface start by going to the Layout tab of the Pallet toolbar. Drag and drop three horizontal arrangement blocks onto the blank canvas. Next go to the User Interface tab and drag and drop one button in the top arrangement, three in the next, one in the third arrangement. These will be our main control buttons. For each button use the Properties toolbar to set the font to bold size 14. Also set the width and height to 80 pixels. Set the text in them to Forward, Left, Stop, Right, and Reverse (going left to right, top to bottom). These are set in the Properties toolbar. In addition these buttons should be named using the Components toolbar. Name each one to correspond with what the text of the buttons says. It's very important to clearly label the on screen components, as it will make creating the code much easier. When you are done it should look like this:
Just Buttons.PNG



Now we will add another horizontal arrangement. Set the height of it to 10 pixels and name it spacer. As the name suggests this is simple to create a space between the buttons.

Adding the buttons for controlling the headlights is next. Drop another horizontal arrangement onto the canvas. Then drag and drop two buttons into the arrangement. Set the text to 14 bold. The text in button one should be "Headlights On" and in button two "Headlights Off" then name the buttons something descriptive like HeadOn, and HeadOff.

Drop in another spacer after the headlight buttons.

Next up is adding the buttons to allow us to connect to a particular Bluetooth device. A scan button will look around for available Bluetooth devices. A ListPicker will allow us to pick the correct device from those available. Finally a Disconnect button will sever the Bluetooth Connection. Create another horizontal arrangement that is set to Fill parent. From the User Interface tab add a Button, a ListPicker, and then another Button. Set the font for all to 16 and bold. For the first button set the text to Scan and name the button Scan as well. For the ListPicker set the text to No Devices and leave the name ListPicker1. The second button set the text to Disconnect and the name to Disconnect. Finally another horizontal arrangement is added to create a status notifier for the Bluetooth. Put two labels in this arrangement. Font size 16, bold. Name the first Link_Label and set its text to “Link Status:”. Name the second link_status, and set its text to “status”.
The finished layout should look like this:
Full Layout.PNG

Creating Code Blocks

The code in app inventor is written using a drag and drop interface that is very similar to Scratch. To the left in the blocks toolbar are all of the elements of the user interface as well as some basic elements; Control, Logic, Math, etc. Clicking on one of these elements brings up a menu of all the related blocks. It should be noted that with a large program, it may take a few seconds between clicking an element and the menu coming up.

The code is created in order of how the app will be used. We start with the Screen.Initalize Block.

To start click on the Screen1 element in the Blocks toolbar. In the menu that opens scroll down until you see the Screen1.Initialize block.
Screen Initalized.PNG
This block is called when the app starts and then does everything inside of it. Grab the block and drag it into the Viewer.
Go to the Blocks toolbar and click on the Link Status element. A menu of blocks will pop up. Scroll down until you see link status.text. Drag it into the viewer and clip it to the main block. You should hear a click. Next go to the Text element in the Blocks toolbar. Grab an empty text box and clip it to the .text block. Set the text to "Disconnected". Next go into the Blocks toolbar and click on Disconnect. Find the set Enabled block and drag it into the viewer. Clip it into the Screen.Initialize Block. Next go to the Logic element in the Blocks toolbar. Grab the False block and drag in into the viewer and clip it in. This will disable the button. The reason for this is that until there is a Bluetooth device connected a lot of buttons should not be pressable.

Now we want to set Forward to disabled as well. We could do this by repeating all the same steps as we did for the Disconnect button, but there is an easier way to do this. If we click on the set enabled block we already have, it will be selected. Then simply copy and paste. Now click where it says Disconnect and a drop down with all the buttons will appear. Set this to Forward instead. Do this for all the buttons we want to disable. Copying as pasting is very helpful for quickly creating code.

Code Blocks

From here on in the code will be given by showing the block and then explaining what it does. For the most part the blocks are in the order of how they will run, but this is only for our convenience.

ScanBlockHM10.JPG
This block activates when the Scan button is clicked. It calls the Bluetooth on the phone to search for available Bluetooth devices.

Bluetooth Device Found.PNG

This block is activated when the scan for devices finds a device. The set ListPicker1.ElementsFromString block reads in the device list and creates a menu. The text of the menu button is changed from “No Device” (what we set it to start with) to “Choose Device”.

After Picking.PNG

The AfterPicking Block runs when a device is chosen from the ListPicker1 menu. The first part of this block sets a global variable called device to just the first part of the selected device. It does this because the devices in the Listpicker1 have both device name and address. However when we use ConnectWithAddress it only wants the address in the argument. To separate these we split the selected device at the first space (which is between the address and the device name). Then we take just the first part (index of 1) and set that equal to the global variable device. For this to work we must also create the a global variable called device. To do this go to the Variables element in the Blocks toolbar. Grab the initialize global and create device. Then go to the Text element and grab a blank text box to initialize device to. It's recommended that you put all the variables you create in one spot on your Viewer. By the end of the code you will have a lot of variables.
Global Variables.PNG

The next section of this block sets the text of the link status to “Trying to connect to <device>” note the join function used, this simply joins two pieces of text together in order. Finally the BluetoothLE1.ConnectwithAddress creates a connection with the HM-10 Module.

Bluetooth Connected.PNG
This block gets called when the BluetoothLE1 detects that it has successfully connected with a device, it sets the link_status text to “Waiting For Data”. The next blocks enable all the buttons that should work now that we are connected to the Arduino. Finally the code sets two more global variables, service_UUID and characteristic_UUID to there correct values. These UUIDs (Unique Unit Identifier) are always the same for the HM-10. Note that they are not the same, service is ffe0 and characteristic is ffe1. These variables must be created and initialized just like the device variable.

Disconnect.PNG

This block is called when the Disconnect button is pressed. It sets the link_status text to “Disconnected” and then disconnects the Bluetooth link. It also disables all the buttons that shouldn't be clicked when there is no device connected.

Send Data.PNG
This block is what actual sends data too the Arduino. It as a Procedure (the App Inventor equivalent of a subroutine). Click on the procedure element and drag a blank procedure into the Viewer. Name it SendData. Then go to the BluetoothLE1 element to grab the WriteBytes block. The bytes must be written into it as a list. This is why we use the make a list block. Note that when you drag the make a list block into the viewer it will only have 2 elements. To add a third click the gear in the upper corner and add another element.

ControlButtons.jpg
These blocks are the heart of the code. The Unit for the motors is 2, so we always set the Unit byte to 2. For each of the direction buttons we want it to send a command to the Arduino when we click down on the button (move that direction), and then another when we let go of the button (stop). The command for Stop is 0, Forward is 1, Left is 2, Right is 3, and Reverse is 4. Note that the stop button is a click, this means it happens when you press and release the button. Finally the last two block in this section are for the headlights. So now the Unit is 0, and Command 0 is off and Command 1 is on. Note that the Enable and Disable blocks are used so that the button that has just been pressed gets turned off. This simply makes it clearer if the lights are on or off. Finally each of these blocks calls SendData. This is under the Procedures Element and runs the SendData function we have already written.

BuffTimerBlockHM10.JPG
BuffTimer1 is turned on when link_status is set to “Sending Data”. When the bufftimer1 goes off it sets the link_status back to “Waiting For Data” and then turns off the BuffTimer1, otherwise it would continue to reset the link_status.


ErrorBlockHM10.JPG
This block is called when the app detects and internal error. The local variables are created and set to values by the block. The notifier is used to pop up an error message detailing what the error was. Note the cancelable function being set to true this is important as otherwise there is no way to get rid of error messages without closing the app.

Putting it all Together

The final step is to put all the pieces together. Load the Arduino Code and you should see that the small red status light on the HM-10 module blinks. This means that it is actively looking for a device to pair with. Next go to android Bluetooth settings. You should see a Bluetooth device called MLT-BT05. Click on it and it should ask for a pin. This pin defaults to 123456. Once it is set up on the phone start up the app using the App Inventor companion app. Now set the robot on the ground and plug in the power. On the app press scan, then when the button next to it changes to “Choose Device” click it and pick MLT-BT05. The buttons should all become enabled, and the link status should say "Waiting For Data". Now try using the buttons, you should see the link status briefly say sending when you press or release a button, and the robot should move. Once you have the app working the way you want you can create a normal app on your phone by going to the app inventor and clicking build. It will download a file that you can use to install the app for real on your phone.