Bluetooth RC Car – Magic wand (Part 3 of 4)

In the first part of this series, we built the mechanum wheel car. In the second part we built an associated smartphone app to control the car.

However, instead of the smartphone app wouldn’t it be more fun to control the car with just the wave of a magic wand. After all, why be a Steve Jobs when you can be a Harry Potter.

Let’s built this Magic Wand.

Making the Magic Wand

Making this is super simple. I am using Arduino Nano 33 BLE Sense which comes with a built in bluetooth and a magnetometer sensor. The only other thing needed, is power management. Thats it. The rest is the intelligence programmed into it.

Following are the components needed:

  • Arduino Nano 33 BLE Sense
  • Small 3.7V LiPo battery
  • MT3608 Boost convertor to convert 3.7V to 5V needed by the Arduino
  • Jumper wires
  • Micro USB type B cable and
  • An Ice Cream stick

I have connected and taped all the components to an ice cream stick. Granted, it is not as elegant as Harry Potter’s magic wand, but will make do for this demo.

Code

Code for this is also pretty simple. After initializing the sensor, bluetooth and serial port(optional), we record the reference/baseline orientation values (pitch, roll, yaw) using magnetometer part of the IMU sensor. Any change in orientation of the wand from here on, will be calculated with reference to the baseline values recorded in the beginning. After this, the highest absolute value in X and Y axis is compared against the preset threshold values. If it exceeds those preset values, that movement will qualify for direction determination.

For example:

  • If threshold value is set at 20
  • Positive X values represents Forward motion and Negative X values represents Back motion
  • Similarly, Positive Y values represents motion to the Right and Negative Y values represents motion to the Left
  • Now, if magnetometer reads X = -25 and Y = +15, then the direction is in the X axis towards the negative side i.e. Back

Once the direction is determined, corresponding code is sent over to the car using BLE. Note that the values are magnetometer values measured in gauss and not angular degrees. From my research I understand that our smartphones uses additional smarts from accelerometer to get true orientation information in degrees.

Setting up automatic connection between bluetooth of the magic wand and the bluetooth of the car is a bit tricky. BLE 4.0 works on a Central-Peripheral role and Publisher-Subscriber model. So both devices need to be set up correctly before they will be able to communicate.

You can find more details on Arduino’s website https://www.arduino.cc/en/Reference/ArduinoBLE. Additionally, there is an excellent website from Martyn Currey, generally on Bluetooth and particularly on HM-10 http://www.martyncurrey.com/hm-10-bluetooth-4ble-modules/. Its has lots of information and instructions on how to set up the bluetooth modules.


/*
  CAR IMU Control

  This sketch is to scan for HM-10 bluetooth module for 
  - service UUID "FFE0"
  - local name as "KK-HM10" and
  - characteristic as UUID "FFE1" 
  
  After successfully connecting, it measures angular velocity using onboard gyroscope sensor
  Based on threshold values of angular velocity in various axis, a charcter is sent over the bluetooth connection
  
  These notification can be used to remotely control the BLE Peripheral's CAR, when the Nano BLE is moved

  The partner script:
  - KK_bluetooth_car_generic
  for Arduino UNO onboard the remote controlled car. This is a generic script to control various 
  motors of the car. It receives inputs from serial TX RX pins of Uno. It is independant of the bluetooth module connected
  HM10 or HC05 for communication with UNO. It is also independant of the ways inputs are coming from the bluetooth connection
  from a phone/app/BLE 33 Sense
  However, Nano BLE 33 Sense can only talk to HM10 and not HC05 therefore HM10 needs to be connected to Uno for this script 
  
  The circuit:
  - Arduino Nano 33 BLE Sense board.
  - (Optional) Button with pull-up resistor connected to pin 2.
 
  Created by Koustubh Kashalkar

  This example code is in the public domain.
*/

#include 
#include 

// variables for button
// const int buttonPin = 2;
// int oldButtonState = LOW;

float x, y, z;                //Measured by IMU
float xref, yref, zref;       //Storing reference values for baseline
float xdelta, ydelta, zdelta; //Actaul gesture value used for movement determinaiton
char xdir, ydir, zdir;        //Flags for direction in each axis

//Positive and negative threshold values beyond which the movement is considered  
float pthreshold = 10;
float nthreshold = -10;

void setup() {
  // initialize serial port
  Serial.begin(9600);
//  while (!Serial);
  Serial.println("Started");

  // initialize the BLE hardware
  if (!BLE.begin()) {
    Serial.println("Failed to initialize BLE!");
    while (1);
  }  

  // initialize IMU sensor
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }  

  delay(500);

  // initialize IMU sensor
  if (IMU.magneticFieldAvailable()) {
    IMU.readMagneticField(xref, yref, zref);
    Serial.print("X ref");
    Serial.print(xref);
    Serial.print('\t');
    Serial.print("Y ref");
    Serial.print(yref);
    Serial.print('\t');
    Serial.print("Z ref");
    Serial.println(zref);
  }
   
  // configure the button pin as input
  // pinMode(buttonPin, INPUT);

  Serial.println("Nano IMU Car Control - initialized");

 }

void loop() {
  // start scanning for bluetooth devices with UUID FFE0 of HM10
  // if connected prioviously, HM10 will automatically connect to last device if available
  BLE.scanForUuid("FFE0");

  // check if any bluetooth device with requested service UUID has been discovered
  BLEDevice peripheral = BLE.available();

  if (peripheral) {
    // list all bluetooth devices with the requested UUID
    // print out address, local name, and advertised service
    Serial.print("Found ");
    Serial.print(peripheral.address());
    Serial.print(" '");
    Serial.print(peripheral.localName());
    Serial.print("' ");
    Serial.print(peripheral.advertisedServiceUuid());
    Serial.println();

     // keep looking for your bluetooth device connected to your car
     if (peripheral.localName() != "KK-HM10") {
      return;
     }

    // stop scanning if bluetooth of the car is found
    BLE.stopScan();
    Serial.println("Car's bluetooth found");

    // connect to the peripheral device
    Serial.println("Connecting to car ...");
    if (peripheral.connect()) {
      Serial.println("Connected");
    } 
    else {
      Serial.println("Failed to connect!");
      return;
    }

    // discover attributes of the car
    Serial.println("Discovering attributes ...");
    if (peripheral.discoverAttributes()) {
      Serial.println("Attributes discovered");
    } 
    else {
      Serial.println("Attribute discovery failed!");
      peripheral.disconnect();
      return;
    }

    // retrieve the characteristic of the car UUID FFE1 of HM10
    BLECharacteristic carCharacteristic = peripheral.characteristic("FFE1");
  
    if (!carCharacteristic) {
      Serial.println("Car does not have required characteristic! Diconnecting ...");
      peripheral.disconnect();
      return;
    } 
    else if (!carCharacteristic.canWrite()) {
      Serial.println("Car does not have a controllable characteristic! Diconnecting ...");
      peripheral.disconnect();
      return;
    }
  
    // prepare the serial screen for IMU measurements
    Serial.print("Magnetic field sample rate = ");
    Serial.print(IMU.magneticFieldSampleRate());
    Serial.println(" uT");
    Serial.println("Magnetic Field in uT");

    // while car is connected, measure IMU and send control signals over bluetooth
    while (peripheral.connected())  {
      controlCar(peripheral, carCharacteristic);
    }
       
    Serial.println("Peripheral disconnected");    
  }
  
}

void controlCar(BLEDevice peripheral, BLECharacteristic carCharacteristic) {

  //*********speed******** to be removed **********
  carCharacteristic.writeValue("4");

  if (IMU.magneticFieldAvailable()) {
    IMU.readMagneticField(x, y, z);
    xdelta = x - xref;
    ydelta = y - yref;
    zdelta = z - zref;
  }
  
  //combination of x and y values will be used for determinaiton of 8 directions
  //while z value will be used for rotation movements
  if(xdelta > pthreshold){
    xdir = 'F'; //forward
    }
  else if(xdelta  pthreshold){
    ydir = 'L'; //left
    }
  else if(ydelta  pthreshold){
    zdir = 'L'; //rotate left
    }
  else if(zdelta < nthreshold){
    zdir = 'R'; //rotate right
    }
  else{
    zdir = 'N'; //null
    }  
//********zdir set up temporarily to null for testing    
    zdir = 'N';
    
    Serial.print("X ");
    Serial.print(x);
    Serial.print('\t');
    Serial.print("Xref ");
    Serial.print(xref);
    Serial.print('\t');
    Serial.print("Xdelta ");
    Serial.print(xdelta);
    Serial.print('\t');
    Serial.print("XPthreshold ");
    Serial.print(pthreshold);
    Serial.print('\t');
    Serial.print("XNthreshold ");
    Serial.print(nthreshold);
    Serial.print('\t');
    Serial.print("Xdir ");
    Serial.print(xdir);
    Serial.println();

    Serial.print("Y ");
    Serial.print(y);
    Serial.print('\t');
    Serial.print("Yref ");
    Serial.print(yref);
    Serial.print('\t');
    Serial.print("Ydelta ");
    Serial.print(ydelta);
    Serial.print('\t');
    Serial.print("YPthreshold ");
    Serial.print(pthreshold);
    Serial.print('\t');
    Serial.print("YNthreshold ");
    Serial.print(nthreshold);
    Serial.print('\t');
    Serial.print("Ydir ");
    Serial.print(ydir);
    Serial.println();

    Serial.print("Z ");
    Serial.print(z);
    Serial.print('\t');
    Serial.print("Zref ");
    Serial.print(zref);
    Serial.print('\t');
    Serial.print("Zdelta ");
    Serial.print(zdelta);
    Serial.print('\t');
    Serial.print("ZPthreshold ");
    Serial.print(pthreshold);
    Serial.print('\t');
    Serial.print("ZNthreshold ");
    Serial.print(nthreshold);
    Serial.print('\t');
    Serial.print("Zdir ");
    Serial.print(zdir);
    Serial.println();
    delay(500);

  if(xdir == 'N' && ydir == 'N' && zdir == 'N'){
    carCharacteristic.writeValue("S");
    Serial.println("stop");
    Serial.println(' ');
    //delay(100);
   }
  else if(xdir == 'F' && ydir == 'N'){
    carCharacteristic.writeValue("F");
    Serial.println("Forward");
    Serial.println(' ');
    //delay(100);
   }
  else if(xdir == 'B' && ydir == 'N'){
    carCharacteristic.writeValue("B");
    Serial.println("Reverse");
    Serial.println(' ');
    //delay(100);
  }
  else if(xdir == 'N' && ydir == 'L'){
    carCharacteristic.writeValue("L");
    Serial.println("Left");
    Serial.println(' ');
    //delay(100);
   }
  else if(xdir == 'N' && ydir == 'R'){
    carCharacteristic.writeValue("R");
    Serial.println("Right");
    Serial.println(' ');
    //delay(100);
  }
  else if(xdir == 'F' && ydir == 'L'){
    carCharacteristic.writeValue("G");
    Serial.println("Forward Left");
    Serial.println(' ');
    //delay(100);
  }
  else if(xdir == 'F' && ydir == 'R'){
    carCharacteristic.writeValue("I");
    Serial.println("Forward Right");
    Serial.println(' ');
    //delay(100);
  }
  else if(xdir == 'B' && ydir == 'L'){
    carCharacteristic.writeValue("H");
    Serial.println("Back Left");
    Serial.println(' ');
    //delay(100);
  }
  else if(xdir == 'B' && ydir == 'R'){
    carCharacteristic.writeValue("J");
    Serial.println("Back Right");
    Serial.println(' ');
    //delay(100);
  }
//  else if(xdir == 'N' && ydir == 'N' && zdir == 'L'){
//    carCharacteristic.writeValue("W");
//    Serial.println("Rotate Left");
//    Serial.println(' ');
//    //delay(100);
//  }
//  else if(xdir == 'N' && ydir == 'N' && zdir == 'R'){
//    carCharacteristic.writeValue("U");
//    Serial.println("Rotate Right");
//    Serial.println(' ');
//    //delay(100);
//  }
}

Next Step

But what if, a magic wand this powerful, falls into the hands of Voldemort? Therefore, it needs to be secured by a secret spell.

This is exactly what we will do in the fourth and last part of this series. We will be using machine learning and TensorFlow Lite for microcontrollers to train the magic wand to recognize a secret gesture or a spell which will unlock all its powers.

Here are all the 3 parts that we have built so far in this series.

Here is the link to other parts of this series

Bluetooth RC Car – Android App (Part 2 of 4)

An android app for Arduino Bluetooth RC car projects. Control the car by voice commands, gyroscope steering & buttons. One touch connection. Full customization of connections, BLE codes & voice commands. Works out of box with BLE 4.0 HM-10 module.

Keep reading

You may also like

Leave a comment

Website Powered by WordPress.com.

Up ↑