Twitter Controlled Robot

Twitter Controlled Robot © GPL3+

Control an Arduino robot with Twitter and the OneSheeld.

  • 1,745 views
  • 0 comments
  • 9 respects

Components and supplies

Apps and online services

About this project

Example on how to control a robot from Twitter by typing #kofecode and issuing a couple of commands.

Application Cases

  • Type #kofecode to enable trigger watch on current tweet
  • :1 :2 :3 :4 :5 to specify motion in different directions (forward, right, backward, left, and stop respectively)
  • Takes an option and converts it to an integer to determine how long to execute a specific command.

Eg. #kofecode :1-2000 :2-450 :4-450 :3-2000 :1-6000 will cause the robot to move forward for 2 seconds, turn right for 450ms, turn left for 450ms, move backwards for 2 seconds and move forward for 6 seconds;

In case an option is not stated with any of the : commands, :1 or :2 will have a delay of 2000, :2 and :4 will have a delay of 450

Note: This Tutorial will not cover assembling the robot. It will only cover how to use ONE SHEELD's twitter interface to queue commands to control your robot.

To Learn How to Assemble the robot kit, Click Here.

Note that you don't need to attach the sensor shield mention in the assembly tutorial since it caused a lot of issues when hooked up with the 1Sheeld

Install the 1Sheeld in its place as shown:

Download the OneSheeld library and App from https://1sheeld.com/

The project was built on 1Sheeld's Stock Twitter Example

/*
Twitter Shield Example
This example shows an application on 1Sheeld's Twitter shield.
By using this example, you can flash an LED once a certain keyword
is mentioned on Twitter.
OPTIONAL:
To reduce the library compiled size and limit its memory usage, you
can specify which shields you want to include in your sketch by
defining CUSTOM_SETTINGS and the shields respective INCLUDE_ define. 
*/
#define CUSTOM_SETTINGS
#define INCLUDE_TWITTER_SHIELD
/* Include 1Sheeld library. */
#include <OneSheeld.h>
/* A name for led on pin 13. */
int ledPin = 13;
/* A variable to track number of tracked tweets returned. */
int trackingCounter = 0;
void setup() {
  /* Start communication. */
  OneSheeld.begin();
  /* Set the LED pin as output. */
  pinMode(ledPin,OUTPUT);
  /* Subscribe to setOnSelected event for the Twitter shield. */
  Twitter.setOnSelected(&shieldSelection);
  /* Subscribe to onNewTweet event. */
  Twitter.setOnNewTweet(&myTweet);
}
void loop() 
{
  /* Leave the loop empty. */
}
void shieldSelection()
{
  /* Track keyword 1Sheeld on Twitter. */
  Twitter.trackKeyword("1Sheeld");
}
void myTweet(char * userName , char * userTweet)
{
    /* Check if the tweet has 1Sheeld keyword. */
    if(strstr(userTweet,":1")!=0)
    {
      /* Turn on the LED. */
      digitalWrite(ledPin,HIGH);
      /* Use OneSheeld delay for better performance. */
      OneSheeld.delay(1000);
      /* Increment the counter. */
      trackingCounter++;
      /* Turn off the LED. */
      digitalWrite(ledPin,LOW);
    }
    /* Check if the counter reached three times. */
    if(trackingCounter == 3)
    {
      /* Stop tracking the word 1Sheeld. */
      Twitter.untrackKeyword("1Sheeld");
      /* Reset the counter. */
      trackingCounter = 0;
    }
}

This simple example allows you to Track a specific (in this case :1 ) keyword in your posts, and if it contains a specific keyword, the led turns on.

What if we wanted to check for multiple keywords?

The above example will not work because strstr() will only check if :1 exists in the string, and depending on how you structure your if statements, the code will do every instance once even if you had typed each keyword multiple times. Example: OneSheeld :1 :2 :3 :4 :5 :1 :1 :3 :4. Using strstr() will only check for :1 once.

Solution

Using String Tokens

strtok() doesn't work on AVR platforms so we'll use strtok_r(). You can check the man on linux to learn more about String Tokens. Both commands are similar. (attached, see strtok.txt)

I will brush through the code quickly the code is heavily commented to explain what is happening at what point.

/*
   Twitter Remote Control Robot
   by Barnabas Nomo <https://github.com/Kowus/twitterControl.git>
  To reduce the library compiled size and limit its memory usage, you
  can specify which shields you want to include in your sketch by
  defining CUSTOM_SETTINGS and the shields respective INCLUDE_ define.
*/
#define CUSTOM_SETTINGS
#define INCLUDE_TWITTER_SHIELD
#define INCLUDE_TERMINAL_SHIELD
/* Include 1Sheeld library. */
#include <OneSheeld.h>
char *str1, *token, *subToken, *saveptr1, *saveptr2;
int j, it = 0, dsw = 0, i = 0, subtokens[30];
char *buf[80][1];
// Motor A pins (enableA = enable motor, pinA1 = forward, pinA2 = backward)
int enableA = 11;
int pinA1 = 6;
int pinA2 = 5;
//Motor B pins (enabledB = enable motor, pinB2 = forward, pinB2 = backward)
int enableB = 10;
int pinB1 = 4;
int pinB2 = 3;

Set the motor pins to OUTPUT, begin OneSheeld instance and write to phone terminal: waiting for tweet...

void setup() {
  //  Set Motor Pin Behaviours
  pinMode(enableA, OUTPUT);
  pinMode(pinA1, OUTPUT);
  pinMode(pinA2, OUTPUT);
  pinMode(enableB, OUTPUT);
  pinMode(pinB1, OUTPUT);
  pinMode(pinB2, OUTPUT);
  /* Start communication. */
  OneSheeld.begin();
  Terminal.println("Waiting for tweet...");
  /* Subscribe to setOnSelected event for the Twitter shield. */
  Twitter.setOnSelected(&shieldSelection);
  /* Subscribe to onNewTweet event. */
  Twitter.setOnNewTweet(&myTweet);
}

Leave the void loop blank:

void loop()
{
  /* Leave the loop empty. */
}

Create shieldSelection function to track keyword:

void shieldSelection()
{
  /* Track hashtag #kofecode on Twitter. */
  Twitter.trackKeyword("#kofecode");
}
void myTweet(char * userName , char * userTweet)
{
  for (j = 0, str1 = userTweet; ; j++, str1 = NULL) {
    token = strtok_r(str1, ":", &saveptr1);
    if (token == NULL) {
      dsw = 1;
      break;
    }
    buf[j][0] = token;
    for (i = 0; ; i++, token = NULL) {
      subToken = strtok_r(token, "-", &saveptr2);
      if (subToken == NULL)break;
      buf[j][i] = subToken;
      Terminal.println("Token "+String(j) + " = " + String(buf[j][0]) + \tSubtoken = " + String(buf[j][1]));
      subtokens[j] = String(buf[j][1]).toInt();
      OneSheeld.delay(100);
    }
  }
  int tempToken = 0;
  Terminal.println("\n\n       Blank       \n\n");
  while (dsw) {
    enableMotors();
    tempToken = String(buf[it][0]).toInt();
    switch (tempToken) {
      case 1:
        Terminal.println("case " + String(tempToken));
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          forward(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          forward(3000);
        }
        break;
      case 2:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          turnRight(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          turnRight(2700);
        }
        break;
      case 3:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        digitalWrite(blue, LOW);
        digitalWrite(green, HIGH);
        digitalWrite(red, LOW);
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          backward(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          backward(3000);
        }
        break;
      case 4:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          turnLeft(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          turnLeft(2700);
        }
        break;
      case 5:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          brake(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          brake(1000);
        }
        break;
      default:
        Terminal.print("defaulted: " + String(buf[it][0]) + ": ");
        if (it >= j) {
          Terminal.println("Overflow\texiting...");
          brake(100);
          dsw = 0;
          disableMotors();
          continue;
        }
        else {
          if (subtokens[it] != NULL) {
            Terminal.println(String(subtokens[it]) + " is Not Null\n");
            forward(subtokens[it]);
          } else {
            Terminal.println(String(subtokens[it]) + " is Null\n");
            forward(1000);
          }
          break;
        }
        break;
    }
    it++;
  }
  it=0;
  Terminal.println("Exited");
}

Motor Functions

void enableMotors()
{
  motorAOn();
  motorBOn();
}
void disableMotors()
{
  motorAOff();
  motorBOff();
}
void forward(int time)
{
  brake(50);
  motorAForward();
  motorBForward();
  OneSheeld.delay(time);
}
void backward(int time)
{
  brake(50);
  motorABackward();
  motorBBackward();
  OneSheeld.delay(time);
}
void turnLeft(int time)
{
  brake(50);
  motorABackward();
  motorBForward();
  OneSheeld.delay(time);
}
void turnRight(int time)
{
  brake(50);
  motorAForward();
  motorBBackward();
  OneSheeld.delay(time);
}
void brake(int time)
{
  motorABrake();
  motorBBrake();
  OneSheeld.delay(time);
}
//Define low-level H-bridge commands
//enable motors
void motorAOn()
{
  digitalWrite(enableA, HIGH);
}
void motorBOn()
{
  digitalWrite(enableB, HIGH);
}
//disable motors
void motorAOff()
{
  digitalWrite(enableB, LOW);
}
void motorBOff()
{
  digitalWrite(enableA, LOW);
}
//motor A controls
void motorAForward()
{
  digitalWrite(pinA1, HIGH);
  digitalWrite(pinA2, LOW);
}
void motorABackward()
{
  digitalWrite(pinA1, LOW);
  digitalWrite(pinA2, HIGH);
}
//motor B controls
void motorBForward()
{
  digitalWrite(pinB1, HIGH);
  digitalWrite(pinB2, LOW);
}
void motorBBackward()
{
  digitalWrite(pinB1, LOW);
  digitalWrite(pinB2, HIGH);
}
void motorABrake()
{
  digitalWrite(pinA1, HIGH);
  digitalWrite(pinA2, HIGH);
}
void motorBBrake()
{
  digitalWrite(pinB1, HIGH);
  digitalWrite(pinB2, HIGH);
}

Altogether now

/*
   Twitter Remote Control Robot
   by Barnabas Nomo <https://github.com/Kowus/twitterControl.git>
  To reduce the library compiled size and limit its memory usage, you
  can specify which shields you want to include in your sketch by
  defining CUSTOM_SETTINGS and the shields respective INCLUDE_ define.
*/
#define CUSTOM_SETTINGS
#define INCLUDE_TWITTER_SHIELD
#define INCLUDE_TERMINAL_SHIELD
/* Include 1Sheeld library. */
#include <OneSheeld.h>
/* A variable to track number of tracked tweets returned. */
//int trackingCounter = 0;
char *str1, *token, *subToken, *saveptr1, *saveptr2;
int j, it = 0, dsw = 0, i = 0, subtokens[30];
char *buf[80][1];
// Motor A pins (enableA = enable motor, pinA1 = forward, pinA2 = backward)
int enableA = 11;
int pinA1 = 6;
int pinA2 = 5;
//Motor B pins (enabledB = enable motor, pinB2 = forward, pinB2 = backward)
int enableB = 10;
int pinB1 = 4;
int pinB2 = 3;
void setup() {
  //  Set Motor Pin Behaviours
  pinMode(enableA, OUTPUT);
  pinMode(pinA1, OUTPUT);
  pinMode(pinA2, OUTPUT);
  pinMode(enableB, OUTPUT);
  pinMode(pinB1, OUTPUT);
  pinMode(pinB2, OUTPUT);
  /* Start communication. */
  OneSheeld.begin();
  Terminal.println("Waiting for tweet...");
  /* Subscribe to setOnSelected event for the Twitter shield. */
  Twitter.setOnSelected(&shieldSelection);
  /* Subscribe to onNewTweet event. */
  Twitter.setOnNewTweet(&myTweet);
}
void loop()
{
  /* Leave the loop empty. */
}
void shieldSelection()
{
  /* Track hashtag #kofecode on Twitter. */
  Twitter.trackKeyword("#kofecode");
}
void myTweet(char * userName , char * userTweet)
{
  for (j = 0, str1 = userTweet; ; j++, str1 = NULL) {
    token = strtok_r(str1, ":", &saveptr1);
    if (token == NULL) {
      dsw = 1;
      break;
    }
    buf[j][0] = token;
    for (i = 0; ; i++, token = NULL) {
      subToken = strtok_r(token, "-", &saveptr2);
      if (subToken == NULL)break;
      buf[j][i] = subToken;
      Terminal.println("Token " + String(j) + " = " + String(buf[j][0]) + "\tSubtoken = ") + String(buf[j][1]));
      subtokens[j] = String(buf[j][1]).toInt();
      OneSheeld.delay(100);
    }
  }
  int tempToken = 0;
  Terminal.println("\n\n       Blank       \n\n");
  while (dsw) {
    enableMotors();
    tempToken = String(buf[it][0]).toInt();
    switch (tempToken) {
      case 1:
        Terminal.println("case " + String(tempToken));
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          forward(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          forward(3000);
        }
        break;
      case 2:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          turnRight(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          turnRight(2700);
        }
        break;
      case 3:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          backward(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          backward(3000);
        }
        break;
      case 4:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          turnLeft(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          turnLeft(2700);
        }
        break;
      case 5:
        Terminal.print("case " + String(buf[it][0]) + ":\t");
        if (subtokens[it] != NULL) {
          Terminal.println(String(subtokens[it]) + " is Not Null\n");
          brake(subtokens[it]);
        } else {
          Terminal.println(String(subtokens[it]) + " is Null\n");
          brake(1000);
        }
        break;
      default:
        Terminal.print("defaulted: " + String(buf[it][0]) + ": ");
        if (it >= j) {
          Terminal.println("Overflow\texiting...");
          brake(100);
          dsw = 0;
          disableMotors();
          continue;
        }
        else {
          if (subtokens[it] != NULL) {
            Terminal.println(String(subtokens[it]) + " is Not Null\n");
            forward(subtokens[it]);
          } else {
            Terminal.println(String(subtokens[it]) + " is Null\n");
            forward(1000);
          }
          break;
        }
        break;
    }
    it++;
  }
  it=0;
  Terminal.println("Exited");
}
void enableMotors()
{
  motorAOn();
  motorBOn();
}
void disableMotors()
{
  motorAOff();
  motorBOff();
}
void forward(int time)
{
  brake(50);
  motorAForward();
  motorBForward();
  OneSheeld.delay(time);
}
void backward(int time)
{
  brake(50);
  motorABackward();
  motorBBackward();
  OneSheeld.delay(time);
}
void turnLeft(int time)
{
  brake(50);
  motorABackward();
  motorBForward();
  OneSheeld.delay(time);
}
void turnRight(int time)
{
  brake(50);
  motorAForward();
  motorBBackward();
  OneSheeld.delay(time);
}
void brake(int time)
{
  motorABrake();
  motorBBrake();
  OneSheeld.delay(time);
}
//Define low-level H-bridge commands
//enable motors
void motorAOn()
{
  digitalWrite(enableA, HIGH);
}
void motorBOn()
{
  digitalWrite(enableB, HIGH);
}
//disable motors
void motorAOff()
{
  digitalWrite(enableB, LOW);
}
void motorBOff()
{
  digitalWrite(enableA, LOW);
}
//motor A controls
void motorAForward()
{
  digitalWrite(pinA1, HIGH);
  digitalWrite(pinA2, LOW);
}
void motorABackward()
{
  digitalWrite(pinA1, LOW);
  digitalWrite(pinA2, HIGH);
}
//motor B controls
void motorBForward()
{
  digitalWrite(pinB1, HIGH);
  digitalWrite(pinB2, LOW);
}
void motorBBackward()
{
  digitalWrite(pinB1, LOW);
  digitalWrite(pinB2, HIGH);
}
void motorABrake()
{
  digitalWrite(pinA1, HIGH);
  digitalWrite(pinA2, HIGH);
}
void motorBBrake()
{
  digitalWrite(pinB1, HIGH);
  digitalWrite(pinB2, HIGH);
}

See Below, For a video of the Working Project:

Controlling a 4wd Arduino Motor Car with Tweets

Code

Github
https://github.com/Kowus/twitterControl

Comments

Similar projects you might like

Hand Gesture Controlled Robot

Project tutorial by Mayoogh Girish

  • 54,973 views
  • 78 comments
  • 65 respects

Bolt Controlled Robot Car

Project tutorial by Team Bolt

  • 1,508 views
  • 1 comment
  • 13 respects

Smartphone Controlled Arduino 4WD Robot Car

Project in progress by Andriy Baranov

  • 53,592 views
  • 43 comments
  • 99 respects
Add projectSign up / Login