Difference between revisions of "Roomba"

From Floor Pi Wiki
Jump to: navigation, search
m
 
(4 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{Template:MOAR}}
+
{{Obsolete}}
  
 
First things first. The Roomba needs a name!
 
First things first. The Roomba needs a name!
Line 12: Line 12:
 
===Wallfollow===
 
===Wallfollow===
 
Moves between the lounges along the west wall of hall while playing the Imperial March. Must be started facing north.
 
Moves between the lounges along the west wall of hall while playing the Imperial March. Must be started facing north.
 +
 +
Should be compiled with the makefile and oi.h provided with the "drive" sample program.
  
 
- [[User:Bski|Bski]], [[User:Meeg|Meeg]]
 
- [[User:Bski|Bski]], [[User:Meeg|Meeg]]
Line 19: Line 21:
 
  * Designed to run on Create Command Module
 
  * Designed to run on Create Command Module
 
  *
 
  *
  * The basic architecture of this program can be re-used to easily  
+
  * The basic architecture of this program can be re-used to easily
 
  * write a wide variety of Create control programs.  All sensor values
 
  * write a wide variety of Create control programs.  All sensor values
  * are polled in the background (using the serial rx interrupt) and  
+
  * are polled in the background (using the serial rx interrupt) and
  * stored in the sensors array as long as the function  
+
  * stored in the sensors array as long as the function
 
  * delayAndUpdateSensors() is called periodically.  Users can send commands
 
  * delayAndUpdateSensors() is called periodically.  Users can send commands
  * directly a byte at a time using byteTx() or they can use the  
+
  * directly a byte at a time using byteTx() or they can use the
 
  * provided functions, such as baud() and drive().
 
  * provided functions, such as baud() and drive().
 
  */
 
  */
 
 
  
 
// Includes
 
// Includes
Line 35: Line 35:
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include "oi.h"
 
#include "oi.h"
 
  
 
// Constants
 
// Constants
 
#define RESET_SONG 0
 
#define RESET_SONG 0
#define START_SONG 1
+
#define MARCH 1 //song length > 16 notes, so this song overflows into song 2
 
#define BUMP_SONG  3
 
#define BUMP_SONG  3
 
#define END_SONG  4
 
#define END_SONG  4
 
  
 
// Global variables
 
// Global variables
Line 53: Line 51:
 
volatile int16_t distance = 0;
 
volatile int16_t distance = 0;
 
volatile int16_t angle = 0;
 
volatile int16_t angle = 0;
 
 
 
  
 
// Functions
 
// Functions
Line 68: Line 63:
 
void defineSongs(void);
 
void defineSongs(void);
  
int main (void)  
+
int main (void)
 
{
 
{
   uint8_t leds_cnt = 99;
+
   uint8_t FOLLOWING = 0;
   uint8_t leds_state = 0;
+
   uint8_t BACKING = 1;
   uint8_t leds_on = 1;
+
   uint8_t CORRECTING = 2;
   int16_t turn_angle = 0;
+
   uint8_t TURNING = 3;
   uint8_t turn_dir = 1;
+
 
   uint8_t turning = 0;
+
  uint8_t LEFT = 0;
   uint8_t backing_up = 0;
+
   uint8_t RIGHT = 1;
 +
 
 +
   uint8_t state = FOLLOWING;
 +
   uint8_t wall = LEFT;
 
    
 
    
   uint8_t follow_left = 0;
+
   uint8_t driving = 0;
  uint8_t follow_right = 1;
+
  uint8_t turning_left = 2;
+
  uint8_t turning_right = 3;
+
  uint8_t left_correct = 4;
+
  uint8_t right_correct = 5;
+
 
    
 
    
 
 
 
   // Set up Create and module
 
   // Set up Create and module
 
   initialize();
 
   initialize();
Line 104: Line 95:
 
   byteTx(RESET_SONG);
 
   byteTx(RESET_SONG);
 
   delayAndUpdateSensors(750);
 
   delayAndUpdateSensors(750);
 
  
 
   for(;;)
 
   for(;;)
Line 114: Line 104:
 
       // Play start song and wait
 
       // Play start song and wait
 
       byteTx(CmdPlay);
 
       byteTx(CmdPlay);
       byteTx(START_SONG);
+
       byteTx(MARCH);
 
       delayAndUpdateSensors(1000);
 
       delayAndUpdateSensors(1000);
 
     
 
    int state = follow_left;
 
  
 
       // Drive around until a button or unsafe condition is detected
 
       // Drive around until a button or unsafe condition is detected
 
       while(!(UserButtonPressed))
 
       while(!(UserButtonPressed))
 
       {
 
       {
 +
/*
 
         if (distance > 4000)
 
         if (distance > 4000)
 
         {
 
         {
 
             distance = 0;
 
             distance = 0;
 
             byteTx(CmdPlay);
 
             byteTx(CmdPlay);
             byteTx(START_SONG);
+
             byteTx(MARCH);
 
         }
 
         }
 +
*/
  
         if (state == follow_left)
+
         if (state == FOLLOWING)
 
         {
 
         {
            drive(500, 1000);
+
             if (wall == LEFT)
             if (sensors[SenBumpDrop] & BumpLeft)
+
              state = left_correct;
+
            if (sensors[SenBumpDrop] & BumpRight)
+
 
             {
 
             {
               byteTx(CmdPlay);
+
                if (!driving)
              byteTx(BUMP_SONG);
+
                {
               state = turning_left;
+
                  drive(500, 1000);
 +
                  driving = 1;
 +
                }
 +
                if (sensors[SenBumpDrop] & BumpLeft)
 +
                {
 +
                  state = BACKING;
 +
                  distance = 0;
 +
                  driving = 0;
 +
                }
 +
                if (sensors[SenBumpDrop] & BumpRight)
 +
               {
 +
                  byteTx(CmdPlay);
 +
                  byteTx(BUMP_SONG);
 +
                  state = TURNING;
 +
                  angle = 0;
 +
                  driving = 0;
 +
               }
 +
            }
 +
            else
 +
            {
 +
                if (!driving)
 +
                {
 +
                  drive(500, -1000);
 +
                  driving = 1;
 +
                }
 +
                if (sensors[SenBumpDrop] & BumpRight)
 +
                {
 +
                  state = BACKING;
 +
                  distance = 0;
 +
                  driving = 0;
 +
                }
 +
                if (sensors[SenBumpDrop] & BumpLeft)
 +
              {
 +
                  byteTx(CmdPlay);
 +
                  byteTx(BUMP_SONG);
 +
                  state = TURNING;
 +
                  angle = 0;
 +
                  driving = 0;
 +
              }
 
             }
 
             }
            angle = 0;
 
 
         }
 
         }
         else if (state == follow_right)
+
         else if (state == BACKING)
 
         {
 
         {
            drive(500, -1000);
+
             if (!driving)
            if (sensors[SenBumpDrop] & BumpRight)
+
              state = right_correct;
+
             if (sensors[SenBumpDrop] & BumpLeft)
+
 
             {
 
             {
               byteTx(CmdPlay);
+
               drive(-200, RadStraight);
               byteTx(BUMP_SONG);
+
               driving = 1;
               state = turning_right;
+
            }
 +
            if (distance < -30)
 +
            {
 +
               state = CORRECTING;
 +
              angle = 0;
 +
              driving = 0;
 
             }
 
             }
            angle = 0;
 
 
         }
 
         }
         else if (state == left_correct)
+
         else if (state == CORRECTING)
 
         {
 
         {
             if (angle < -7)
+
             if (wall == LEFT)
                 state = follow_left;
+
            {
             drive(200, RadCW);  
+
                if (!driving)
 +
                {
 +
                  drive(200, RadCW);
 +
                  driving = 1;
 +
                }
 +
                if (angle < -15)
 +
                 {
 +
                  state = FOLLOWING;
 +
                  driving = 0;
 +
                }
 +
             }
 +
            else
 +
            {
 +
                if (!driving)
 +
                {
 +
                  drive(200, RadCCW);
 +
                  driving = 1;
 +
                }
 +
                if (angle > 15)
 +
                {
 +
                  state = FOLLOWING;
 +
                  driving = 0;
 +
                }
 +
            }
 
         }
 
         }
         else if (state == right_correct)
+
         else if (state == TURNING)
 
         {
 
         {
             if (angle > 7)
+
             if (wall == LEFT)
                 state = follow_right;
+
            {
             drive(200, RadCCW);  
+
                if (!driving)
 +
                {
 +
                  drive(200, RadCCW);
 +
                  driving = 1;
 +
                }
 +
                if (angle > 180)
 +
                 {
 +
//                  byteTx(CmdPlay);
 +
//                  byteTx(MARCH);
 +
                  state = FOLLOWING;
 +
                  wall = RIGHT;
 +
                  driving = 0;
 +
                }
 +
             }
 +
            else
 +
            {
 +
                if (!driving)
 +
                {
 +
                  drive(200, RadCCW);
 +
                  driving = 1;
 +
                }
 +
                if (angle > 180)
 +
                {
 +
//                  byteTx(CmdPlay);
 +
//                  byteTx(MARCH);
 +
                  state = FOLLOWING;
 +
                  wall = LEFT;
 +
                  driving = 0;
 +
                }
 +
            }
 
         }
 
         }
        else if (state == turning_left)
+
 
        {
+
            if (angle > 180)
+
                state = follow_right;
+
            drive(200, RadCCW);
+
        }
+
        else if (state == turning_right)
+
        {
+
            if (angle > 180)
+
                state = follow_left;
+
            drive(200, RadCCW);   
+
        }
+
           
+
 
         // wait a little more than one robot tick for sensors to update
 
         // wait a little more than one robot tick for sensors to update
 
         delayAndUpdateSensors(20);
 
         delayAndUpdateSensors(20);
Line 193: Line 257:
 
       byteTx(END_SONG);
 
       byteTx(END_SONG);
 
       delayAndUpdateSensors(2438);
 
       delayAndUpdateSensors(2438);
 
 
     }
 
     }
 
   }
 
   }
 
}
 
}
 
 
 
 
 
 
 
  
 
// Serial receive interrupt to store sensor values
 
// Serial receive interrupt to store sensor values
Line 209: Line 265:
 
{
 
{
 
   uint8_t temp;
 
   uint8_t temp;
 
  
 
   temp = UDR0;
 
   temp = UDR0;
Line 220: Line 275:
 
   }
 
   }
 
}
 
}
 
 
 
  
 
// Timer 1 interrupt to time delays in ms
 
// Timer 1 interrupt to time delays in ms
Line 232: Line 284:
 
     timer_on = 0;
 
     timer_on = 0;
 
}
 
}
 
 
 
  
 
// Transmit a byte over the serial port
 
// Transmit a byte over the serial port
Line 242: Line 291:
 
   UDR0 = value;
 
   UDR0 = value;
 
}
 
}
 
 
 
  
 
// Delay for the specified time in ms without updating sensor values
 
// Delay for the specified time in ms without updating sensor values
Line 253: Line 299:
 
   while(timer_on) ;
 
   while(timer_on) ;
 
}
 
}
 
 
 
  
 
// Delay for the specified time in ms and update sensor values
 
// Delay for the specified time in ms and update sensor values
Line 282: Line 325:
 
   }
 
   }
 
}
 
}
 
 
 
  
 
// Initialize the Mind Control's ATmega168 microcontroller
 
// Initialize the Mind Control's ATmega168 microcontroller
Line 313: Line 353:
 
   sei();
 
   sei();
 
}
 
}
 
 
 
  
 
void powerOnRobot(void)
 
void powerOnRobot(void)
Line 333: Line 370:
 
   }
 
   }
 
}
 
}
 
 
 
  
 
// Switch the baud rate on both Create and module
 
// Switch the baud rate on both Create and module
Line 381: Line 415:
 
   }
 
   }
 
}
 
}
 
 
 
  
 
// Send Create drive commands in terms of velocity and radius
 
// Send Create drive commands in terms of velocity and radius
Line 394: Line 425:
 
   byteTx((uint8_t)(radius & 0x00FF));
 
   byteTx((uint8_t)(radius & 0x00FF));
 
}
 
}
 
 
 
  
 
// Define songs to be played later
 
// Define songs to be played later
Line 416: Line 444:
 
   // Start song
 
   // Start song
 
   byteTx(CmdSong);
 
   byteTx(CmdSong);
   byteTx(START_SONG);
+
   byteTx(MARCH);
byteTx(18); // 18 notes
+
  byteTx(18); // 18 notes
byteTx(55); // G
+
  byteTx(55); // G
byteTx(32);
+
  byteTx(32);
byteTx(55); // G
+
  byteTx(55); // G
byteTx(32);
+
  byteTx(32);
byteTx(55); // G
+
  byteTx(55); // G
byteTx(32);
+
  byteTx(32);
byteTx(51); // D#
+
  byteTx(51); // D#
byteTx(24);
+
  byteTx(24);
byteTx(58); // A#
+
  byteTx(58); // A#
byteTx(12);
+
  byteTx(12);
byteTx(55); // G
+
  byteTx(55); // G
byteTx(32);
+
  byteTx(32);
byteTx(51); // D#
+
  byteTx(51); // D#
byteTx(24);
+
  byteTx(24);
byteTx(58); // A#
+
  byteTx(58); // A#
byteTx(12);
+
  byteTx(12);
byteTx(55); // G
+
  byteTx(55); // G
byteTx(64);
+
  byteTx(64);
byteTx(62); // D
+
  byteTx(62); // D
byteTx(32);
+
  byteTx(32);
byteTx(62); // D
+
  byteTx(62); // D
byteTx(32);
+
  byteTx(32);
byteTx(62); // D
+
  byteTx(62); // D
byteTx(32);
+
  byteTx(32);
byteTx(63); // D#
+
  byteTx(63); // D#
byteTx(24);
+
  byteTx(24);
byteTx(58); // A#
+
  byteTx(58); // A#
byteTx(12);
+
  byteTx(12);
byteTx(54); // F#
+
  byteTx(54); // F#
byteTx(32);
+
  byteTx(32);
byteTx(51); // D#
+
  byteTx(51); // D#
byteTx(24);
+
  byteTx(24);
byteTx(58); // A#
+
  byteTx(58); // A#
byteTx(12);
+
  byteTx(12);
byteTx(55); // G
+
  byteTx(55); // G
byteTx(64);
+
  byteTx(64);
  
 
   // Bump song
 
   // Bump song
Line 459: Line 487:
 
   byteTx(BUMP_SONG);
 
   byteTx(BUMP_SONG);
 
   byteTx(2);
 
   byteTx(2);
byteTx(58); // A#
+
  byteTx(58); // A#
byteTx(32);
+
  byteTx(32);
byteTx(55); // G
+
  byteTx(55); // G
byteTx(12);
+
  byteTx(12);
 
+
  
 
   // End song
 
   // End song

Latest revision as of 00:03, 6 September 2010

Obsolete

This article or the information contained in it is old and quite possibly faulty. Sort of like our cruft. Please update.

Slide rule.jpg

First things first. The Roomba needs a name!

Put your suggestions on the Talk: page (everyone should learn to use this feature!).

Resources

Code

Wallfollow

Moves between the lounges along the west wall of hall while playing the Imperial March. Must be started facing north.

Should be compiled with the makefile and oi.h provided with the "drive" sample program.

- Bski, Meeg

/* drive.c
 * Designed to run on Create Command Module
 *
 * The basic architecture of this program can be re-used to easily
 * write a wide variety of Create control programs.  All sensor values
 * are polled in the background (using the serial rx interrupt) and
 * stored in the sensors array as long as the function
 * delayAndUpdateSensors() is called periodically.  Users can send commands
 * directly a byte at a time using byteTx() or they can use the
 * provided functions, such as baud() and drive().
 */

// Includes
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdlib.h>
#include "oi.h"

// Constants
#define RESET_SONG 0
#define MARCH 1 //song length > 16 notes, so this song overflows into song 2
#define BUMP_SONG  3
#define END_SONG   4

// Global variables
volatile uint16_t timer_cnt = 0;
volatile uint8_t timer_on = 0;
volatile uint8_t sensors_flag = 0;
volatile uint8_t sensors_index = 0;
volatile uint8_t sensors_in[Sen6Size];
volatile uint8_t sensors[Sen6Size];
volatile int16_t distance = 0;
volatile int16_t angle = 0;

// Functions
void byteTx(uint8_t value);
void delayMs(uint16_t time_ms);
void delayAndUpdateSensors(unsigned int time_ms);
void initialize(void);
void powerOnRobot(void);
void baud(uint8_t baud_code);
void drive(int16_t velocity, int16_t radius);
uint16_t randomAngle(void);
void defineSongs(void);

int main (void)
{
  uint8_t FOLLOWING = 0;
  uint8_t BACKING = 1;
  uint8_t CORRECTING = 2;
  uint8_t TURNING = 3;

  uint8_t LEFT = 0;
  uint8_t RIGHT = 1;

  uint8_t state = FOLLOWING;
  uint8_t wall = LEFT;
  
  uint8_t driving = 0;
  
  // Set up Create and module
  initialize();
  LEDBothOff;
  powerOnRobot();
  byteTx(CmdStart);
  baud(Baud28800);
  defineSongs();
  byteTx(CmdControl);
  byteTx(CmdFull);

  // Stop just as a precaution
  drive(0, RadStraight);

  // Play the reset song and wait while it plays
  byteTx(CmdPlay);
  byteTx(RESET_SONG);
  delayAndUpdateSensors(750);

  for(;;)
  {
    delayAndUpdateSensors(10);

    if(UserButtonPressed)
    {
      // Play start song and wait
      byteTx(CmdPlay);
      byteTx(MARCH);
      delayAndUpdateSensors(1000);

      // Drive around until a button or unsafe condition is detected
      while(!(UserButtonPressed))
      {
/*
        if (distance > 4000)
        {
            distance = 0;
            byteTx(CmdPlay);
            byteTx(MARCH);
        }
*/

        if (state == FOLLOWING)
        {
            if (wall == LEFT)
            {
                if (!driving)
                {
                   drive(500, 1000);
                   driving = 1;
                }
                if (sensors[SenBumpDrop] & BumpLeft)
                {
                   state = BACKING;
                   distance = 0;
                   driving = 0;
                }
                if (sensors[SenBumpDrop] & BumpRight)
               {
                   byteTx(CmdPlay);
                   byteTx(BUMP_SONG);
                   state = TURNING;
                   angle = 0;
                   driving = 0;
               }
            }
            else
            {
                if (!driving)
                {
                   drive(500, -1000);
                   driving = 1;
                }
                if (sensors[SenBumpDrop] & BumpRight)
                {
                   state = BACKING;
                   distance = 0;
                   driving = 0;
                }
                if (sensors[SenBumpDrop] & BumpLeft)
               {
                   byteTx(CmdPlay);
                   byteTx(BUMP_SONG);
                   state = TURNING;
                   angle = 0;
                   driving = 0;
               }
            }
        }
        else if (state == BACKING)
        {
            if (!driving)
            {
               drive(-200, RadStraight);
               driving = 1;
            }
            if (distance < -30)
            {
               state = CORRECTING;
               angle = 0;
               driving = 0;
            }
        }
        else if (state == CORRECTING)
        {
            if (wall == LEFT)
            {
                if (!driving)
                {
                   drive(200, RadCW);
                   driving = 1;
                }
                if (angle < -15)
                {
                   state = FOLLOWING;
                   driving = 0;
                }
            }
            else
            {
                if (!driving)
                {
                   drive(200, RadCCW);
                   driving = 1;
                }
                if (angle > 15)
                {
                   state = FOLLOWING;
                   driving = 0;
                }
            }
        }
        else if (state == TURNING)
        {
            if (wall == LEFT)
            {
                if (!driving)
                {
                   drive(200, RadCCW);
                   driving = 1;
                }
                if (angle > 180)
                {
//                   byteTx(CmdPlay);
//                   byteTx(MARCH);
                   state = FOLLOWING;
                   wall = RIGHT;
                   driving = 0;
                }
            }
            else
            {
                if (!driving)
                {
                   drive(200, RadCCW);
                   driving = 1;
                }
                if (angle > 180)
                {
//                   byteTx(CmdPlay);
//                   byteTx(MARCH);
                   state = FOLLOWING;
                   wall = LEFT;
                   driving = 0;
                }
            }
        }

        // wait a little more than one robot tick for sensors to update
        delayAndUpdateSensors(20);
      }

      // Stop driving
      drive(0, RadStraight);

      // Play end song and wait
      delayAndUpdateSensors(500);
      byteTx(CmdPlay);
      byteTx(END_SONG);
      delayAndUpdateSensors(2438);
    }
  }
}

// Serial receive interrupt to store sensor values
SIGNAL(SIG_USART_RECV)
{
  uint8_t temp;

  temp = UDR0;

  if(sensors_flag)
  {
    sensors_in[sensors_index++] = temp;
    if(sensors_index >= Sen6Size)
      sensors_flag = 0;
  }
}

// Timer 1 interrupt to time delays in ms
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
  if(timer_cnt)
    timer_cnt--;
  else
    timer_on = 0;
}

// Transmit a byte over the serial port
void byteTx(uint8_t value)
{
  while(!(UCSR0A & _BV(UDRE0))) ;
  UDR0 = value;
}

// Delay for the specified time in ms without updating sensor values
void delayMs(uint16_t time_ms)
{
  timer_on = 1;
  timer_cnt = time_ms;
  while(timer_on) ;
}

// Delay for the specified time in ms and update sensor values
void delayAndUpdateSensors(uint16_t time_ms)
{
  uint8_t temp;

  timer_on = 1;
  timer_cnt = time_ms;
  while(timer_on)
  {
    if(!sensors_flag)
    {
      for(temp = 0; temp < Sen6Size; temp++)
        sensors[temp] = sensors_in[temp];

      // Update running totals of distance and angle
      distance += (int)((sensors[SenDist1] << 8) | sensors[SenDist0]);
      angle += (int)((sensors[SenAng1] << 8) | sensors[SenAng0]);

      byteTx(CmdSensors);
      byteTx(6);
      sensors_index = 0;
      sensors_flag = 1;
    }
  }
}

// Initialize the Mind Control's ATmega168 microcontroller
void initialize(void)
{
  cli();

  // Set I/O pins
  DDRB = 0x10;
  PORTB = 0xCF;
  DDRC = 0x00;
  PORTC = 0xFF;
  DDRD = 0xE6;
  PORTD = 0x7D;

  // Set up timer 1 to generate an interrupt every 1 ms
  TCCR1A = 0x00;
  TCCR1B = (_BV(WGM12) | _BV(CS12));
  OCR1A = 71;
  TIMSK1 = _BV(OCIE1A);

  // Set up the serial port with rx interrupt
  UBRR0 = 19;
  UCSR0B = (_BV(RXCIE0) | _BV(TXEN0) | _BV(RXEN0));
  UCSR0C = (_BV(UCSZ00) | _BV(UCSZ01));

  // Turn on interrupts
  sei();
}

void powerOnRobot(void)
{
  // If Create's power is off, turn it on
  if(!RobotIsOn)
  {
      while(!RobotIsOn)
      {
          RobotPwrToggleLow;
          delayMs(500);  // Delay in this state
          RobotPwrToggleHigh;  // Low to high transition to toggle power
          delayMs(100);  // Delay in this state
          RobotPwrToggleLow;
      }
      delayMs(3500);  // Delay for startup
  }
}

// Switch the baud rate on both Create and module
void baud(uint8_t baud_code)
{
  if(baud_code <= 11)
  {
    byteTx(CmdBaud);
    UCSR0A |= _BV(TXC0);
    byteTx(baud_code);
    // Wait until transmit is complete
    while(!(UCSR0A & _BV(TXC0))) ;

    cli();

    // Switch the baud rate register
    if(baud_code == Baud115200)
      UBRR0 = Ubrr115200;
    else if(baud_code == Baud57600)
      UBRR0 = Ubrr57600;
    else if(baud_code == Baud38400)
      UBRR0 = Ubrr38400;
    else if(baud_code == Baud28800)
      UBRR0 = Ubrr28800;
    else if(baud_code == Baud19200)
      UBRR0 = Ubrr19200;
    else if(baud_code == Baud14400)
      UBRR0 = Ubrr14400;
    else if(baud_code == Baud9600)
      UBRR0 = Ubrr9600;
    else if(baud_code == Baud4800)
      UBRR0 = Ubrr4800;
    else if(baud_code == Baud2400)
      UBRR0 = Ubrr2400;
    else if(baud_code == Baud1200)
      UBRR0 = Ubrr1200;
    else if(baud_code == Baud600)
      UBRR0 = Ubrr600;
    else if(baud_code == Baud300)
      UBRR0 = Ubrr300;

    sei();

    delayMs(100);
  }
}

// Send Create drive commands in terms of velocity and radius
void drive(int16_t velocity, int16_t radius)
{
  byteTx(CmdDrive);
  byteTx((uint8_t)((velocity >> 8) & 0x00FF));
  byteTx((uint8_t)(velocity & 0x00FF));
  byteTx((uint8_t)((radius >> 8) & 0x00FF));
  byteTx((uint8_t)(radius & 0x00FF));
}

// Define songs to be played later
void defineSongs(void)
{
  // Reset song
  byteTx(CmdSong);
  byteTx(RESET_SONG);
  byteTx(4);
  byteTx(60);
  byteTx(6);
  byteTx(72);
  byteTx(6);
  byteTx(84);
  byteTx(6);
  byteTx(96);
  byteTx(6);

  // Start song
  byteTx(CmdSong);
  byteTx(MARCH);
  byteTx(18); // 18 notes
  byteTx(55); // G
  byteTx(32);
  byteTx(55); // G
  byteTx(32);
  byteTx(55); // G
  byteTx(32);
  byteTx(51); // D#
  byteTx(24);
  byteTx(58); // A#
  byteTx(12);
  byteTx(55); // G
  byteTx(32);
  byteTx(51); // D#
  byteTx(24);
  byteTx(58); // A#
  byteTx(12);
  byteTx(55); // G
  byteTx(64);
  byteTx(62); // D
  byteTx(32);
  byteTx(62); // D
  byteTx(32);
  byteTx(62); // D
  byteTx(32);
  byteTx(63); // D#
  byteTx(24);
  byteTx(58); // A#
  byteTx(12);
  byteTx(54); // F#
  byteTx(32);
  byteTx(51); // D#
  byteTx(24);
  byteTx(58); // A#
  byteTx(12);
  byteTx(55); // G
  byteTx(64);

  // Bump song
  byteTx(CmdSong);
  byteTx(BUMP_SONG);
  byteTx(2);
  byteTx(58); // A#
  byteTx(32);
  byteTx(55); // G
  byteTx(12);

  // End song
  byteTx(CmdSong);
  byteTx(END_SONG);
  byteTx(5);
  byteTx(77);
  byteTx(18);
  byteTx(74);
  byteTx(12);
  byteTx(72);
  byteTx(12);
  byteTx(69);
  byteTx(12);
  byteTx(65);
  byteTx(24);
}