/** RoombaPhotovore: Turns your Roomba into a light follower by controlling it via IR. * Uses a 38 KHz carrier generated by directly setting timer 2's registers. * Author: Tom Lauwers, tlauwers@birdbraintechnologies.com * Date: 1/3/2011 * * Modified: 01/24/2012 by Max Eliaser of O'Reilly Media, Inc. * Capitalize preprocessor macro names. * Add LEFT_LIGHT_PIN, RIGHT_LIGHT_PIN, LIGHT_THRESHOLD, LIGHT_DIFF_THRESHOLD, and IR_OUTPUT_PIN * macros where previously there had been hardcoded numbers. */ // The length of the raw IR signals #define SIGNAL_LENGTH 15 // The pins for the LED and photoresistors #define IR_OUTPUT_PIN 11 #define LEFT_LIGHT_PIN 0 #define RIGHT_LIGHT_PIN 1 // Twiddle this to adjust for the peculiarities of your photoresistors and the ambient light levels #define LIGHT_THRESHOLD 800 // was originally 100; I changed it to 800 to compensate for a bright room // Twiddle this to adjust how different the photoresistors should be to trigger a turn #define LIGHT_DIFF_THRESHOLD 100 // Arrays that contain the raw IR signal times in microseconds // Captured directly from IRAnalyzer unsigned int goForward[SIGNAL_LENGTH] = {3028,956,996,2948,956,2948,960,2948,960,2940,964,2948,2896,984,996}; unsigned int turnLeft[SIGNAL_LENGTH] = {2992,1008,968,2964,944,2964,944,2964,940,2972,936,2976,932,2976,2872}; unsigned int turnRight[SIGNAL_LENGTH] = {2988,1012,968,2964,936,2976,940,2968,932,2976,932,2976,2872,1012,2904}; // For storing the left and right light sensor values int leftLight; int rightLight; void setup() { // start serial port at 57600 bps: Serial.begin(57600); // PWM Magic - we directly set atmega registers to 50% duty cycle, // variable frequency mode. Currently set to 38 KHz. // Thanks http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM TCCR2A = _BV(WGM21) | _BV(WGM20); //set prescalar to 16MHz clock for now TCCR2B = _BV(WGM22) | _BV(CS20); // To get a 38KHz wave, we need OCR2A = (16000000/76000)-1 = 209 OCR2A = 209; // Set the pin the IR LED is on to output pinMode(IR_OUTPUT_PIN, OUTPUT); } void loop() { // Read in light sensors - left on pin 0, right on pin 1 leftLight = analogRead(LEFT_LIGHT_PIN); rightLight = analogRead(RIGHT_LIGHT_PIN); // Print the values for debugging purposes Serial.print("Left: "); Serial.print(leftLight); Serial.print(" Right: "); Serial.print(rightLight); Serial.print("\n"); // If both sensors see very little light, stop! if((leftLight < LIGHT_THRESHOLD) && (rightLight < LIGHT_THRESHOLD)) { // do nothing, effectively stops Roomba } // If left is a lot less than right, turn right to balance it out else if(leftLight < (rightLight - LIGHT_DIFF_THRESHOLD)) { playSignal(turnRight); } // If right is less than left, turn left else if(rightLight < (leftLight - LIGHT_DIFF_THRESHOLD)) { playSignal(turnLeft); } // else, go straight towards the light else { playSignal(goForward); } // Roomba needs a call to playSignal to repeat every 20 milliseconds delay(20); } // Plays raw signal void playSignal(unsigned int signal[]) { // Traverse the raw IR signal array, turning the LED on or off by the amount of time // specified by the array for(int i = 0; i < SIGNAL_LENGTH; i++) { // even elements of the array = IR LED is on if(i%2 == 0) { TCCR2A |= _BV(COM2A0); // turns on the IR LED to 50% duty cycle delayMicroseconds(signal[i]); } // IR LED is off for odd elements of the array else { TCCR2A &= (0xFF - _BV(COM2A0)); // disconnect pin from signal digitalWrite(IR_OUTPUT_PIN,LOW); // turn pin low in case it's high delayMicroseconds(signal[i]); } } // Turn off signal one last time TCCR2A &= (0xFF - _BV(COM2A0)); // disconnect pin from signal digitalWrite(IR_OUTPUT_PIN,LOW); // turn pin low in case it's high }