Project tutorial

Arduino-Powered Robot Controlled with The Tactigon © CC BY

We used The Tactigon to control an Arduino-powered robot via BLE. Moving The Tactigon as a 3D steering wheel is awesome!

  • 4,309 views
  • 0 comments
  • 21 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
Tactigonfronte3 5x2msol6vb
The Tactigon One
wearable board with SDK Arduino
×1
Bluetooth Low Energy (BLE) Module (Generic)
×1

Apps and online services

About this project

Overview

This post will show how to take advantages of The Tactigon’s BLE Central capabilities. We wanted to control our robot by using The Tactigon as a “3D steering wheel” controlling speed with pitch and steering with roll. We made so few changes in the Alphabot2 original Bluetooth example code and wrote a sketch for The Tactigon to connect to the robot’s BLE characteristic and write in wheels speeds.

What we need

The Tactigon with a configured Arduino IDE Robot. We used a 2 wheels robot with Arduino board and BLE radio interfaced with UART. Other kind of robots or custom ones can work as well. Robot BLE MAC Address and Characteristic Fun

Gathering BLE MAC Address and Characteristic

After our environment is configured and our boards are ON, we need to gather BLE MAC Address and Characteristic. To do so we used a free android application called BLE Scanner.

Few seconds after the application should show the robot’s BLE:

As we see, all BLE devices around us are showed in this section. We need to write down the Waveshare_BLE MAC address: in this instance it is: 00:0E:0B:0C:4A:00 By clicking on the CONNECT button we access to device’s informations as attribute, service and custom characteristic.

Here we need to write down the CUSTOM CHARACTERISTIC UUID, in this case: 0000ffe1-0000-1000-8000-00805f9b34fb. With this items we can set our Tactigon BLE to act as BLE Central in the setup() section of the code.

The Tactigon sketch

loop()

In this section we have the core of the sketch. At a frequency of 50Hz, we update quaternions and euler angles.

Analizyng pitch angle provided by Tactigon library, we can determine steering radius by slowing internal wheel and accelerating external wheel.

Analizying roll, instead, we can determine travel speed of the robot.

With a sprintf we prepare the buffer to write in the characteristic.

Robot sketch

Since our Bluetooth sends received data over UART, we get wheels speed directly in the serial buffer. We have set robot pins as follows, all as output:

To parse the command we first read all the serial buffer and verify if it is longer than 0:

If the command contains “Wh” we can so parse the string and gather leftSpeed and rightSpeed.

The direct_motor function assign the speed transmitted by The Tactigon to each wheel of the robot. By doing so The Tactigon will act as a virtual-steering wheel!

Final Considerations

This sketch shows a potential application of The Tactigon, with the BLE Central mode is possible to connect to existing BLE devices and gather informations or control them.

Stay tuned for more Tactigon’s code!

Code

Robot.inoArduino
This is the sketch to run on an Arduino UNO connected to the Alphabot 2 Rover
#include <Adafruit_NeoPixel.h>

#define PWMA   6           //Left Motor Speed pin (ENA)
#define AIN2   A0          //Motor-L forward (IN2).
#define AIN1   A1          //Motor-L backward (IN1)
#define PWMB   5           //Right Motor Speed pin (ENB)
#define BIN1   A2          //Motor-R forward (IN3)
#define BIN2   A3          //Motor-R backward (IN4)
#define PIN 7

String comdata = "";
int Speed = 150;
uint16_t i, j;
unsigned long lasttime = 0;
byte flag = 1;
Adafruit_NeoPixel RGB = Adafruit_NeoPixel(4, PIN, NEO_GRB + NEO_KHZ800);

void forward();
void backward();
void right();
void left();
void stop();
uint32_t Wheel(byte WheelPos);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  RGB.begin();
  RGB.show(); // Initialize all pixels to 'off'
  pinMode(PWMA,OUTPUT);                     
  pinMode(AIN2,OUTPUT);      
  pinMode(AIN1,OUTPUT);
  pinMode(PWMB,OUTPUT);       
  pinMode(AIN1,OUTPUT);     
  pinMode(AIN2,OUTPUT);  
  Serial.println("Bluetooth control example");
  stop();
}

void loop() {
  // put your main code here, to run repeatedly:
  while (Serial.available() > 0)  
  {
    comdata += char(Serial.read());
    delay(2);
  }
  if (comdata.length() > 0)
  {
    Serial.println(comdata);
    const char* command = comdata.c_str();
    if(strcmp(command,"Forward") == 0)          //Forward
    {
      flag = 0;
      RGB.setPixelColor(0, 0xFF0000);
      RGB.setPixelColor(1, 0xFF0000);
      RGB.setPixelColor(2, 0xFF0000);
      RGB.setPixelColor(3, 0xFF0000);
      RGB.show();
      forward();
    }
    else if(strcmp(command,"Backward") == 0)    //Backward
    {
      flag = 0;
      RGB.setPixelColor(0, 0x00FF00);
      RGB.setPixelColor(1, 0x00FF00);
      RGB.setPixelColor(2, 0x00FF00);
      RGB.setPixelColor(3, 0x00FF00);
      RGB.show();  
      backward();
    }
    else if(strcmp(command,"Left") == 0)        //Left
    {
      flag = 0;
      RGB.setPixelColor(0, 0xFFFF00);
      RGB.setPixelColor(1, 0xFFFF00);
      RGB.setPixelColor(2, 0xFFFF00);
      RGB.setPixelColor(3, 0xFFFF00);
      RGB.show();
      left();
    }
    else if(strcmp(command,"Right") == 0)       //Right
    {
      flag = 0;
      RGB.setPixelColor(0, 0x0000FF);
      RGB.setPixelColor(1, 0x0000FF);
      RGB.setPixelColor(2, 0x0000FF);
      RGB.setPixelColor(3, 0x0000FF);
      RGB.show();  
      right();
    }
    else if(strcmp(command,"Stop") == 0)        //Stop
    {
      flag = 1;
      stop();
    }
    else if(strcmp(command,"Low") == 0)         //Low
    {
      Speed = 50;
    }
    else if(strcmp(command,"Medium") == 0)      //Medium
    {
      Speed = 150;
    }
    else if(strcmp(command,"High") == 0)        //High
    {
      Speed = 250;
    }
    else if(strstr(command,"Wh")!=0)  //Direct wheels control
    {
      int leftSpeed,rightSpeed; 
      sscanf(command,"Wh(%d)(%d)",&leftSpeed,&rightSpeed);
      direct_motor(leftSpeed,rightSpeed);
    }
    
    comdata = "";
  }
  if(millis() - lasttime >20){  
    lasttime = millis();   
    for(i=0; i< RGB.numPixels(); i++) {
      RGB.setPixelColor(i, Wheel(((i * 256 / RGB.numPixels()) + j) & 255));
    }
    if(flag)RGB.show();
    if(j++ > 256*5) j= 0;
  }
}

void direct_motor(int leftSpeed,int rightSpeed)
{
  if(leftSpeed==0)
  {
    digitalWrite(AIN1,LOW);
    digitalWrite(AIN2,LOW);
  }
  else
  {
    if(leftSpeed>0)
    {
      analogWrite(PWMA,leftSpeed);
      digitalWrite(AIN1,LOW);
      digitalWrite(AIN2,HIGH);

    }
    else
    {
      analogWrite(PWMA,-leftSpeed);
      digitalWrite(AIN1,HIGH);
      digitalWrite(AIN2,LOW);

    }
  }


  if(rightSpeed==0)
  {
    digitalWrite(BIN1,LOW);
    digitalWrite(BIN2,LOW);
  }
  else
  {
    if(rightSpeed>0)
    {
      analogWrite(PWMB,rightSpeed);
      digitalWrite(BIN1,LOW);
      digitalWrite(BIN2,HIGH);

    }
    else
    {
      analogWrite(PWMB,-rightSpeed);
      digitalWrite(BIN1,HIGH);
      digitalWrite(BIN2,LOW);

    }
  }
}

void forward()
{
  analogWrite(PWMA,Speed);
  analogWrite(PWMB,Speed);
  digitalWrite(AIN1,LOW);
  digitalWrite(AIN2,HIGH);
  digitalWrite(BIN1,LOW);  
  digitalWrite(BIN2,HIGH); 
}

void backward()
{
  analogWrite(PWMA,Speed);
  analogWrite(PWMB,Speed);
  digitalWrite(AIN1,HIGH);
  digitalWrite(AIN2,LOW);
  digitalWrite(BIN1,HIGH); 
  digitalWrite(BIN2,LOW);  
}

void right()
{
  analogWrite(PWMA,80);
  analogWrite(PWMB,80);
  digitalWrite(AIN1,LOW);
  digitalWrite(AIN2,HIGH);
  digitalWrite(BIN1,HIGH); 
  digitalWrite(BIN2,LOW);  
}

void left()
{
  analogWrite(PWMA,80);
  analogWrite(PWMB,80);
  digitalWrite(AIN1,HIGH);
  digitalWrite(AIN2,LOW);
  digitalWrite(BIN1,LOW); 
  digitalWrite(BIN2,HIGH);  
}

void stop()
{
  analogWrite(PWMA,0);
  analogWrite(PWMB,0);
  digitalWrite(AIN1,LOW);
  digitalWrite(AIN2,LOW);
  digitalWrite(BIN1,LOW); 
  digitalWrite(BIN2,LOW);  
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return RGB.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return RGB.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return RGB.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
Tactigon.inoArduino
This is the sketch to run on The Tactigon to control the alphabot 2 rover.
#include <tactigon_led.h>
#include <tactigon_IMU.h>
#include <tactigon_BLE.h>


extern int ButtonPressed;

T_Led rLed, bLed, gLed;



T_QUAT qMeter;
T_QData qData;

T_BLE bleManager;
UUID targetUUID;
uint8_t targetMAC[6] = {0x00,0x0e,0x0b,0x0c,0x4a,0x00};
T_BLE_Characteristic accChar, gyroChar, magChar, qChar;

int ticks, ticksLed, stp, cnt, printCnt;
float roll, pitch, yaw;



void setup() {
  // put your setup code here, to run once:
  ticks = 0;
  ticksLed = 0;
  stp = 0;
  cnt = 0;

  //init leds
  rLed.init(T_Led::RED);
  gLed.init(T_Led::GREEN);
  bLed.init(T_Led::BLUE);
  rLed.off();
  gLed.off();
  bLed.off();

  //init BLE 
  bleManager.setName("Tactigon");
  bleManager.InitRole(TACTIGON_BLE_CENTRAL);                  //role: CENTRAL
  targetUUID.set("0000ffe1-0000-1000-8000-00805f9b34fb");     //target characteristic
  bleManager.setTarget(targetMAC, targetUUID);                //target: mac device and its char UUID
}

void loop() {

  char buffData[24];  
  int deltaWheel, speedWheel;
  int pitchThreshold, rollThreshold, th1, th2;

  //update BLE characteristics @ 50Hz (20msec)
  if(GetCurrentMilli() >= (ticks +(1000 / 50)))
  {    
    ticks = GetCurrentMilli();
        
    //get quaternions and Euler angles
    qData = qMeter.getQs();      

    //Euler angles: rad/sec --> degrees/sec    
    roll = qData.roll * 360/6.28;
    pitch = qData.pitch * 360/6.28;
    yaw = qData.yaw * 360/6.28;


    //build command to rover depending on Euler angles                  
    //left/right
    pitchThreshold = 15;
    if(pitch < -pitchThreshold || pitch > pitchThreshold)
    {
      if(pitch<-pitchThreshold)
      {
        deltaWheel =- (fabs(pitch) - pitchThreshold)*3;
      }
      else
      {
        deltaWheel =+ (fabs(pitch) - pitchThreshold)*3;
      }      
    }
    else
    {
      deltaWheel=0;
    }

    //forward/backword
    rollThreshold = 15;
    th1 = 90 + rollThreshold;
    th2 = 90 - rollThreshold;
    roll = fabs(roll);
    if(roll > th1)
    {
      speedWheel = (roll - th1) * 3;
    }
    else if(roll < th2)
    {
      speedWheel = (roll - th2) * 3;
    }
    else
    {
      speedWheel = 0;
    }
    
    //command in buffData
    sprintf(buffData,"Wh(%d)(%d)", speedWheel-(-deltaWheel/2), speedWheel+(-deltaWheel/2));

    
    //if connected and attached to peripheral characteristic write in it
    if(bleManager.getStatus() == 3)
    {
      //signal that connection is on
      bLed.on();
      
      //send command every 100msec
      rLed.off();
      cnt++;
      if(cnt > 5)
      {
        cnt = 0;
        bleManager.writeToPeripheral((unsigned char *)buffData, strlen(buffData));
        rLed.on();        
      }    
    }


    //say hello on serial monitor every second and blink green led
    printCnt++;
    rLed.off();
    if(printCnt > 50)
    {      
      //Serial.println("Hello!");
      //Serial.println(roll);
      printCnt = 0;
      rLed.on();
    }    
    
  }  
}

Comments

Similar projects you might like

EODbot with Camera Controlled with Tactigon Skin

Project tutorial by Massimiliano and Luca Sevà

  • 2,671 views
  • 1 comment
  • 14 respects

Bluetooth Controlled Pick And Place Robot

Project tutorial by Ahmed Ibrahim Ahmed

  • 14,321 views
  • 16 comments
  • 53 respects

Simple Gesture Controlled Robot Using Arduino

Project tutorial by Jithin Sanal

  • 5,018 views
  • 1 comment
  • 22 respects
Add projectSign up / Login