Let's Make Robots!

Bot4Julia, a Start Here CD robot, Arduino clone

Wanders around avoiding obstacles
AttachmentSize
Bot4Julia.zip5.59 KB

This is a Start Here robot style I built for my daughter Julia, so she can have something to play with, because she keeps bugging me wanting to play with my MiniEric robot. (I allow her to remote control it some times, but she still grabs his arms and breaks the servo gears.)

Here is Bot4Julia. To build one, the recipe is simple. Get 2 CDs, drill holes for standoffs, attach 2 geared motors (I used GM17 just because I had them) with double side foam tape, a battery box, then mount the second CD on the standoffs, feed the wires through the second CD center hole, add some more standoffs for the PCB, plug the wires in the proper sockets, use some more double side tape to attach a small servo and a SeeedStudio US sensor (works like a Ping sensor, costs only $15) on the servo horn, plug them on the board and start programming. I used an old Ro-Bot-X board I designed years ago. This board had a MAX232 serial converter on board, but my laptop does not have a serial port anymore, so instead of making an adapter, I just unplugged the ATmega168 and plugged it in the Roboduino board for programming. This is OK for one time, but I had to repeat this operation about 20 times to be able to tweak out parts of the code. This board will soon be replaced by a uBotino board that will allow easy programming has everything needed for this robot. I did not intended this robot to be a tutorial, so I did not take pictures at the time of construction. Here are the pictures after the robot was built:

The code is simple, but I had to overcome some hardware problems. First, the motors do not have encoders, and one is spinning faster than the other. So I had to find a good PWM value to keep them running at the same speed. Then I had to find out the turning time so the robot turns 90 degrees good enough. So I made a Square function, that drives Forward for 2 seconds, turns Right 90 degrees and so on 4 times to make a square. Once this was done, I wrote some simple avoiding routine that needs improving, but for now it's good enough to have fun with the robot. I will add remote control and let the robot avoid objects when necessary. This way my daughter can tel the robot where to go, but there will still be some autonomous routines in there. She also wants me to add arms to the robot, I'll have to work on something...

Oh, I had to use the SoftwareServo library, because the motors use the Timer1 for PWM (uBotino avoids this using Timer0) so they were fighting for the same Timer.

Here is the code so far:

 


// Bot4Julia, a simple Start Here style robot, made with 2 CDs,

// 2 GM17 motors and wheels, one servo, one SeeedStudio US sensor,

// one battery box, one Ro-Bot-X board (will be replaced by a uBotino)

//

// Ro-Bot-X board pinout:

//

// Ext  RBX Funct Arduino  ATmega168      Arduino Funct RBX External

//                       +-----\/----+

//          Reset       1| PC6   PC5 |28  D19 A5  SCL

//          Rx    D0    2| PD0   PC4 |27  D18 A4  SDA

//          Tx    D1    3| PD1   PC3 |26  D17 A3        

//          Int0  D2    4| PD2   PC2 |25  D16 A2        

//          Int1  D3    5| PD3   PC1 |24  D15 A1        Ping

//      S1        D4    6| PD4   PC0 |23  D14 A0        

//                      7| VCC   GND |22  

//                      8| GND  AREF |21  

//          Xtal        9| PB6  AVCC |20  

//          Xtal       10| PB7   PB5 |19  D13      SCK        

//      S2  OC0B  D5   11| PD5   PB4 |18  D12      MISO M1B

//          OC0A  D6   12| PD6   PB3 |17  D11 OC2A MOSI       

//      Pan       D7   13| PD7   PB2 |16  D10 OC1B      M2A

//      M2B (S3)  D8   14| PB0   PB1 |15  D 9 OC1A (S4) M1A

//                       +-----------+

//



#include <SoftwareServo.h>

#include <Speaker.h>


//Inputs/outputs

//#define Encoder_1_ChA  2 // digital pin 2    // Right Encoder

//#define Encoder_1_ChB  6 // digital pin 6

//#define Encoder_2_ChA  3 // digital pin 3    // Left Encoder

//#define Encoder_2_ChB 14 // digital pin 14


#define Motor_1_PWM   9 // digital pin 9    // Right Motor

#define Motor_1_Dir  12 // digital pin 12

#define Motor_2_PWM  10 // digital pin 10   // Left Motor

#define Motor_2_Dir   8 // digital pin 8


//#define SleftPin  1     // analog pin 1

//#define SrightPin 2     // analog pin 2

#define PingPin  15     // digital pin 15

#define PanPin    4     // digital pin 4

#define SpeakerPin 13

#define center 90


//Variables

byte dir=0;

byte speed1=225;

byte speed2=255;

int turn90=500;

int turn45=250;

int stopTime=200;

int USdistance=0;

int treshold=20; //20cm min distance


SoftwareServo Pan; 

Speaker speaker = Speaker(SpeakerPin); 

//-----------------------------------------------------------------------------



void setup() { 

   

  // set motor pins as output and LOW so the motors are breaked

  pinMode(Motor_1_PWM, OUTPUT);

  pinMode(Motor_1_Dir, OUTPUT);

  pinMode(Motor_2_PWM, OUTPUT);

  pinMode(Motor_2_Dir, OUTPUT);

  Stop();


  pinMode(PingPin, OUTPUT); 

  digitalWrite(PingPin, LOW);

  

  Pan.attach(PanPin);

  Pan.write(center); //90

  StepDelay();


  pinMode(SpeakerPin, OUTPUT); 

  speaker.Beep();


  //Serial.begin (9600);

  //Serial.println("start");


  Forward();


void loop(){

  Drive();

  //square();

}


void square(){

  Forward();

  delay(2000);

  Stop();

  delay(stopTime);

  Right();

  delay(turn90);

  Stop();

  delay(stopTime);

  Forward();

  delay(2000);

  Stop();

  delay(stopTime);

  Right();

  delay(turn90);

  Stop();

  delay(stopTime);

  Forward();

  delay(2000);

  Stop();

  delay(stopTime);

  Right();

  delay(turn90);

  Stop();

  delay(stopTime);

  Forward();

  delay(2000);

  Stop();

  delay(stopTime);

  Right();

  delay(turn90);

  Stop();

  delay(stopTime);

}


void Drive(){

  USdistance=Read_Ping_Sensor();

  if (USdistance<5){

    Stop();

    speaker.Beep();

    StepDelay();

    TurnAround();

  }

  if (USdistance<treshold){

    Stop();

    speaker.Beep();

    StepDelay();

    Avoid();

    Forward();

  }

  delay(50);

}


void TurnAround(){

    Reverse();

    Pan.write(center);

    StepDelay();

    Stop();

    Left();

    delay(turn90);

    delay(turn90);

    Stop();

    StepDelay();

    Forward();

}


void Avoid(){

  int prev=0;

  for (byte i=0; i<5; i++){

    Pan.write(i*45);

    StepDelay();

    StepDelay();

    USdistance=Read_Ping_Sensor();

    if (USdistance>prev){

      dir=i;

      prev=USdistance;

    }

  }

  Pan.write(center);

  StepDelay();

  switch (dir){

    case 0:

      Right();

      delay(turn90);

      Stop();

      speaker.Beep();

      StepDelay();

      break;

    case 1:

      Right();

      delay(turn90); //turn45

      Stop();

      speaker.Beep();

      StepDelay();

      break;

    case 2:

      Forward();

      break;

    case 3:

      Left();

      delay(turn90); //turn45

      Stop();

      speaker.Beep();

      StepDelay();

      break;

    case 4:

      Left();

      delay(turn90);

      Stop();

      speaker.Beep();

      StepDelay();

      break;

  }

  delay(500);

}  


// Read Sensors

int Read_Ping_Sensor(){

  int cm=0;

  //trigger the sensor

  unsigned long value = 0;

  pinMode(PingPin, OUTPUT);

  digitalWrite(PingPin, LOW);

  delayMicroseconds(2);

  digitalWrite(PingPin, HIGH);

  delayMicroseconds(10);

  digitalWrite(PingPin, LOW);

  //receive the echo

  pinMode(PingPin, INPUT);

  digitalWrite(PingPin, HIGH); // turn on pull up resistor

  value = pulseIn(PingPin, HIGH);

  value=value/58;

  cm=int(value);

  return cm;

}


void StepDelay() {

    for (byte t=0; t<10; t++){

      SoftwareServo::refresh();

      delay(20);

    }

}



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void Forward(){

  digitalWrite(Motor_1_Dir, LOW); // forward

  digitalWrite(Motor_2_Dir, LOW); // forward

  analogWrite(Motor_1_PWM, speed1); // 

  analogWrite(Motor_2_PWM, speed2); //

}


void Reverse(){

  digitalWrite(Motor_1_Dir, HIGH); // reverse

  digitalWrite(Motor_2_Dir, HIGH); // reverse

  analogWrite(Motor_1_PWM, 255-speed1); // 

  analogWrite(Motor_2_PWM, 255-speed2); //

}


void Right(){

  digitalWrite(Motor_1_Dir, HIGH); // reverse

  digitalWrite(Motor_2_Dir, LOW); // forward

  analogWrite(Motor_1_PWM, 255-speed1); // 

  analogWrite(Motor_2_PWM, speed2); //

}


void Left(){

  digitalWrite(Motor_1_Dir, LOW); // forward

  digitalWrite(Motor_2_Dir, HIGH); // reverse

  analogWrite(Motor_1_PWM, speed1); // 

  analogWrite(Motor_2_PWM, 255-speed2); //

}


void Stop()

{

  digitalWrite(Motor_1_PWM, LOW);

  digitalWrite(Motor_1_Dir, LOW);

  digitalWrite(Motor_2_PWM, LOW);

  digitalWrite(Motor_2_Dir, LOW);

}  


 

 


I made this robot in about 4 hours, it was fun and rewarding to see my daughter so happy playing with it. You should all try that!

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

can i ask for the design of the remote..i think i love to do this stuff.. its wonderful though ..

pls..post the design of the remote  .. tnx

I used a universal TV remote programed for a Sony VCR. To receive the remote IR signal I used a TSOP4838 sensor. This sensor has 3 pins, Output, Gnd, Vcc. Tie the Gnd and Vcc pins to Arduino pins Gnd and 5V and the Output pin to any I/O pin. I can post the code if you want.

tnx for a quick reply i hope you post it..once again tnx

 

 

 

// Bot4Julia, a simple Start Here style robot, made with 2 CDs,
// 2 GM17 motors and wheels, one servo, one SeeedStudio US sensor,
// one battery box, one uBotino board
//
// uBotino board pinout:
//
// uBotino Funct Arduino  ATmega168      Arduino Funct uBotino
//                       +-----\/----+
//          Reset       1| PC6   PC5 |28  D19 A5  SCL  RightBumper
//          Rx    D0    2| PD0   PC4 |27  D18 A4  SDA  LeftBumper
//          Tx    D1    3| PD1   PC3 |26  D17 A3        
//          Int0  D2    4| PD2   PC2 |25  D16 A2        
//          Int1  D3    5| PD3   PC1 |24  D15 A1        IR sensor
//      Spk       D4    6| PD4   PC0 |23  D14 A0        Ping
//                      7| VCC   GND |22  
//                      8| GND  AREF |21  
//          Xtal        9| PB6  AVCC |20  
//          Xtal       10| PB7   PB5 |19  D13      SCK  LED      
//      M1A OC0B  D5   11| PD5   PB4 |18  D12      MISO 
//      M2A OC0A  D6   12| PD6   PB3 |17  D11 OC2A MOSI       
//      M2B       D7   13| PD7   PB2 |16  D10 OC1B      
//      M1B       D8   14| PB0   PB1 |15  D 9 OC1A      Pan servo
//                       +-----------+
//


#include <Servo.h>
#include <Speaker.h>

//Inputs/outputs
//#define Encoder_1_ChA  2 // digital pin 2    // Right Encoder
//#define Encoder_2_ChA  3 // digital pin 3    // Left Encoder

#define JumperPin     2 // digital pin 2

#define Motor_1_PWM   5 // digital pin 5    // Right Motor
#define Motor_1_Dir   8 // digital pin 8
#define Motor_2_PWM   6 // digital pin 6   // Left Motor
#define Motor_2_Dir   7 // digital pin 7

#define PingPin  14     // digital pin 14 (analog pin 0)
#define IR_Pin  15	// digital pin 15 (analog pin 1)
#define LeftBumper 18
#define RightBumper 19
#define PanPin    9     
#define SpeakerPin 4
#define LedPin 13

#define center 90

// Remote control buttons
#define btnPower   149
#define btnMute    148
#define btnPrevCh  187
#define btnDown    145
#define btnUp      144
#define btnLeft    147
#define btnRight   146
#define btnPlay    1434
#define btnStop    1432
#define btnRecord  1437
#define btnPause   1433
#define btnRew     1435
#define btnFwd     1436
#define btnInfo    186
#define btnSleep   182
#define btnInput   165
#define btnEnter   139
#define btn0       137
#define btn1       128
#define btn2       129
#define btn3       130
#define btn4       131
#define btn5       132
#define btn6       133
#define btn7       134
#define btn8       135
#define btn9       136

//Variables
byte dir=0;
byte speed1=230;
byte speed2=255;
int turn90=400;
int turn45=200;
int stopTime=200;
int USdistance=0;
int treshold=20; //20cm min distance

// remote control variables
#define start_bit  2200		// Start bit threshold (Microseconds)
#define bin_1  1000	        // Binary 1 threshold (Microseconds)
#define bin_0  400              // Binary 0 threshold (Microseconds)

Servo Pan; 
Speaker speaker = Speaker(SpeakerPin); 
//-----------------------------------------------------------------------------


void setup() { 
   
  // set motor pins as output and LOW so the motors are breaked
  pinMode(Motor_1_PWM, OUTPUT);
  pinMode(Motor_1_Dir, OUTPUT);
  pinMode(Motor_2_PWM, OUTPUT);
  pinMode(Motor_2_Dir, OUTPUT);
  Stop();

  pinMode(IR_Pin, INPUT); // uses an analog pin so it has to be declared as digital in

  pinMode(PingPin, OUTPUT); 
  digitalWrite(PingPin, LOW);
  
  pinMode(LeftBumper, INPUT); 
  digitalWrite(LeftBumper, HIGH); //turn on pull ups
  pinMode(RightBumper, INPUT); 
  digitalWrite(RightBumper, HIGH); //turn on pull ups

  Pan.attach(PanPin);
  Pan.write(center); //90
  StepDelay();

  pinMode(SpeakerPin, OUTPUT); 
  speaker.Beep();

  pinMode(LedPin, OUTPUT);      
  digitalWrite(LedPin, LOW);  

  Serial.begin (19200);
  Serial.println("start");

  if (digitalRead(JumperPin)==LOW){
    Forward();
  }
} 

void loop(){
  if (digitalRead(JumperPin)==LOW){
    Drive();
  }
  Get_IR_Command();
}

void Get_IR_Command() {
  int key = getIRKey();		    //Fetch the key
  Serial.print("Key ");
  Serial.println(key);
  switch (key) {    
    case btnLeft:
      Left();
      //delay(turn45);
      //Stop();
      break;
      
    case btnRight:    
      Right();
      //delay(turn45);
      //Stop();
      break;
      
    case btnUp:
      Forward();
      break;  
      
    case btnDown:
      Reverse();
      break;

    case btnStop:
      Stop();
      break;

    case btnMute:
      speaker.PlayMelody();
      break;
    default:
      if (digitalRead(JumperPin)==HIGH){
        Stop();
      }
  }  
  return;
}

//--------------------------
int getIRKey() {
  int data[12];
  int newdata=pulseIn(IR_Pin, LOW, 100000);
  while(newdata>0 && newdata<start_bit) { //Wait for a start bit
    newdata=pulseIn(IR_Pin, LOW, 100000);
  }
  if (newdata==0) return 0;
  for(int i=0;i<11;i++){
    data[i] = pulseIn(IR_Pin, LOW, 100000);   //Start measuring bits, we only want low pulses
  }
  //speaker.Beep();
  for(int i=0;i<11;i++) {	      //Parse them
    if(data[i] > bin_1) {	      //is it a 1?
	data[i] = 1;
    }  else {
	if(data[i] > bin_0) {	      //is it a 0?
	  data[i] = 0;
	} else {
	 data[i] = 2;		      //Flag the data as invalid; I don't know what it is!
	}
    }
  }

   for(int i=0;i<11;i++) {	      //Pre-check data for errors
    if(data[i] > 1) {
	return -1;	              //Return -1 on invalid data
    }
  }

  int result = 0;
  int seed = 1;
  for(int i=0;i<11;i++) {	      //Convert bits to integer
    if(data[i] == 1) {
	result += seed;
    }
    seed = seed * 2;
  }
  return result;		      //Return key number
}


void Drive(){
  if (digitalRead(LeftBumper)==LOW){
  digitalWrite(LedPin, HIGH);  
    Stop();
    speaker.Beep();
    StepDelay();
  digitalWrite(LedPin, LOW);  
    Reverse();
    StepDelay();
    Stop();
    StepDelay();
    Right();
    delay(turn45); //turn45
    Stop();
    StepDelay();
    Forward();
  }
  if (digitalRead(RightBumper)==LOW){
  digitalWrite(LedPin, HIGH);  
    Stop();
    speaker.Beep();
    StepDelay();
  digitalWrite(LedPin, LOW);  
    Reverse();
    StepDelay();
    Stop();
    StepDelay();
    Left();
    delay(turn45); //turn45
    Stop();
    StepDelay();
    Forward();
  }
  USdistance=Read_Ping_Sensor();
  Serial.print("USdistance ");
  Serial.println(USdistance);
  if (USdistance<10){
    Stop();
    speaker.Beep();
    StepDelay();
    TurnAround();
  }
  if (USdistance<treshold){
    Stop();
    speaker.Beep();
    StepDelay();
    Avoid();
    Forward();
  }
  delay(50);
}

void TurnAround(){
    Reverse();
    Pan.write(center);
    StepDelay();
    Stop();
    Left();
    delay(turn90);
    delay(turn90);
    Stop();
    StepDelay();
    Forward();
}

void Avoid(){
  int prev=0;
  dir=2;
  for (byte i=0; i<5; i++){
    Pan.write(i*45);
    StepDelay();
    StepDelay();
    USdistance=Read_Ping_Sensor();
    if (USdistance>prev){
      dir=i;
      prev=USdistance;
    }
  }
  Pan.write(center);
  StepDelay();
  switch (dir){
    case 0:
      Right();
      delay(turn90);
      Stop();
      speaker.Beep();
      StepDelay();
      break;
    case 1:
      Right();
      delay(turn90); //turn45
      Stop();
      speaker.Beep();
      StepDelay();
      break;
    case 2:
      Forward();
      break;
    case 3:
      Left();
      delay(turn90); //turn45
      Stop();
      speaker.Beep();
      StepDelay();
      break;
    case 4:
      Left();
      delay(turn90);
      Stop();
      speaker.Beep();
      StepDelay();
      break;
  }
  delay(500);
}  

// Read Sensors
int Read_Ping_Sensor(){
  //digitalWrite(LedPin, HIGH);  
  int cm=0;
  //trigger the sensor
  unsigned long value = 0;
  pinMode(PingPin, OUTPUT);
  digitalWrite(PingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(PingPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(PingPin, LOW);
  //receive the echo
  pinMode(PingPin, INPUT);
  digitalWrite(PingPin, HIGH); // turn on pull up resistor
  value = pulseIn(PingPin, HIGH);
  value=value/58;
  cm=int(value);
  //digitalWrite(LedPin, LOW);  
  return cm;
}

void StepDelay() {
    for (byte t=0; t<10; t++){
      //SoftwareServo::refresh();
      delay(20);
    }
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Forward(){
  digitalWrite(Motor_1_Dir, LOW); // forward
  digitalWrite(Motor_2_Dir, LOW); // forward
  analogWrite(Motor_1_PWM, speed1); // 
  analogWrite(Motor_2_PWM, speed2); //
  return;
}

void Reverse(){
  digitalWrite(Motor_1_Dir, HIGH); // reverse
  digitalWrite(Motor_2_Dir, HIGH); // reverse
  analogWrite(Motor_1_PWM, 255-speed1); // 
  analogWrite(Motor_2_PWM, 255-speed2); //
  return;
}

void Right(){
  digitalWrite(Motor_1_Dir, HIGH); // reverse
  digitalWrite(Motor_2_Dir, LOW); // forward
  analogWrite(Motor_1_PWM, 255-speed1); // 
  analogWrite(Motor_2_PWM, speed2); //
  return;
}

void Left(){
  digitalWrite(Motor_1_Dir, LOW); // forward
  digitalWrite(Motor_2_Dir, HIGH); // reverse
  analogWrite(Motor_1_PWM, speed1); // 
  analogWrite(Motor_2_PWM, 255-speed2); //
  return;
}

void Stop()
{
  digitalWrite(Motor_1_PWM, LOW);
  digitalWrite(Motor_1_Dir, LOW);
  digitalWrite(Motor_2_PWM, LOW);
  digitalWrite(Motor_2_Dir, LOW);
  return;
}  


thanks for sharing and posting :)

Great bot, but wasnt you annoyed at everyone using ultrasonic sensors when you can use IR leds ? And yet you use ultrasonic 

Well, that doesn't mean I can't use ultrasonics or IR range sensors! The problem with the IR leds and IR remote sensor is that they don't come mounted on a nice board you can fit on a servo or directly on the robot. You have to make that yourself. And this was a robot I made quickly from what I had available at the time. But if you want to take a look a a robot that I made that uses the IR leds and sensor, take a look at this one.

Is that stock gearing on the GM17s? It looks faster than I expected. I am looking to pick some up.

I have no ideea. I played with the Didel gears but I don't remember which are curently in the motors. I remember being a bit dissapointed by the torque with the fastest setup, so I might have changed them to a lower speed. 

Yeah I didn't think you would get much torque on the fastest but the middle setting looked like it might be good. Thanks anyway!