I’ve been struggling to convert the 3pi demo program to run under Arduino 1.0, and without the bootloader. The pololu_3pi_init(2000) function and IR_EMITTERS_ON are “not declared in this scope”. After a bit of searching, I find that #include <pololu/3pi.h> is no where to be found in the libraries. I reloaded 3pi libraries and found #include <pololu/Pololu3pi.h>. However, according to the AVR Library Command Reference (doc 0J13, section19) one needs to use “#include <pololu/3pi.h> // use this line for C”.
Has someone ever converted the demo program to Arduino (C, not C++)? If so, please share. If not, any ideas on how to make it work, or where to find the pololu/3pi.h library?
Here’s the current code
/*
* 3pi-demo-program - demo code for the Pololu 3pi Robot
*
* https://www.pololu.com/docs/0J20
* https://www.pololu.com
* https://forum.pololu.com
*
* You may freely modify and share this code, as long as you keep this
* notice intact (including the three links above). Licensed under the
* Creative Commons BY-SA 3.0 license:
* http://creativecommons.org/licenses/by-sa/3.0/
* Disclaimer: To the extent permitted by law, Pololu provides this work
* without any warranty. It might be defective, in which case you agree
* to be responsible for all resulting costs and damages.
*/
// ***** Includes *********************************************************
#include <pololu/3pi.h>
//#include <pololu/Pololu3pi.h>
#include <OrangutanLCD.h>
#include <OrangutanLEDs.h>
#include <OrangutanAnalog.h>
#include <OrangutanMotors.h>
#include <OrangutanBuzzer.h>
//#include <PololuQTRSensors.h>
#include <OrangutanPushbuttons.h>
#include <avr/pgmspace.h>
// **** Introductory messages, stored in program space **********************
const char welcome_line1[] PROGMEM = " Pololu";
const char welcome_line2[] PROGMEM = "3\xf7 Robot";
const char demo_name_line1[] PROGMEM = "Demo";
const char demo_name_line2[] PROGMEM = "Program";
const char instructions_line1[] PROGMEM = "Use B to";
const char instructions_line2[] PROGMEM = "select.";
const char instructions_line3[] PROGMEM = "Press B";
const char instructions_line4[] PROGMEM = "-try it!";
const char thank_you_line1[] PROGMEM = " Thank ";
const char thank_you_line2[] PROGMEM = " you!";
const char main_menu_intro_line1[] PROGMEM = " Main";
const char main_menu_intro_line2[] PROGMEM = " Menu";
const char menu_bat_test[] PROGMEM = "Battery";
const char menu_led_test[] PROGMEM = "LEDs";
const char menu_lcd_test[] PROGMEM = "LCD";
const char menu_ir_test[] PROGMEM = "Sensors";
const char menu_motor_test[] PROGMEM = "Motors";
const char menu_music_test[] PROGMEM = "Music";
const char menu_pot_test[] PROGMEM = "Trimpot";
const char menu_time_test[] PROGMEM = "Timer";
const char menu_line2[] PROGMEM = "\x7f" "A \xa5" "B C\x7e";
const char back_line2[] PROGMEM = "\6B";
// **** Functions for the Main Menu **************************************
void bat_test();
void led_test();
void lcd_test();
void ir_test();
void motor_test();
void music_test();
void time_test();
void pot_test();
typedef void (*function)();
const function main_menu_functions[] = { bat_test, led_test, pot_test, ir_test, motor_test, music_test, time_test };
const char *main_menu_options[] = { menu_bat_test, menu_led_test, menu_pot_test, menu_ir_test, menu_motor_test, menu_music_test, menu_time_test };
const char main_menu_length = sizeof(main_menu_options)/sizeof(main_menu_options[0]);
// **** Tunes, stored in program space **********************************
const char welcome[] PROGMEM = ">g32>>c32";
const char thank_you_music[] PROGMEM = ">>c32>g32";
const char beep_button_a[] PROGMEM = "!c32";
const char beep_button_b[] PROGMEM = "!e32";
const char beep_button_c[] PROGMEM = "!g32";
const char timer_tick[] PROGMEM = "!v8>>c32";
// **** Character Data **************************************************
const char levels[] PROGMEM = {
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111
};
const prog_char note[] PROGMEM = {
0b00100,
0b00110,
0b00101,
0b00101,
0b00100,
0b11100,
0b11100,
0b00000,
};
const prog_char back_arrow[] PROGMEM = {
0b00000,
0b00010,
0b00001,
0b00101,
0b01001,
0b11110,
0b01000,
0b00100,
};
// **** Loads the custom characters into the LCD ***********************
void load_custom_characters()
{
lcd_load_custom_character(levels+0,0); // no offset, e.g. one bar
lcd_load_custom_character(levels+1,1); // two bars
lcd_load_custom_character(levels+2,2); // etc...
lcd_load_custom_character(levels+4,3); // skip level 3
lcd_load_custom_character(levels+5,4);
lcd_load_custom_character(levels+6,5);
lcd_load_custom_character(back_arrow,6);
lcd_load_custom_character(note,7);
clear(); // clear LCD for the characters to take effect
}
// **** Bar graph character array **************************************
const char bar_graph_characters[10] = {' ',0,0,1,2,3,3,4,5,255};
// **** Displays the battery voltage ***********************************
void bat_test()
{
int bat = read_battery_millivolts_3pi();
print_long(bat);
print(" mV");
delay(100);
}
// **** Button debounce? ************************************************
char wait_for_250_ms_or_button_b()
{
int i;
for(i = 0; i < 25; i++)
{
delay(10);
if(button_is_pressed(BUTTON_B))
return 1;
}
return 0;
}
// **** Blinks the LEDs ************************************************
void led_test()
{
play("c32");
print("Red ");
red_led(1);
if(wait_for_250_ms_or_button_b())
return;
red_led(0);
if(wait_for_250_ms_or_button_b())
return;
play(">c32");
lcd_goto_xy(0,0);
print("Green");
green_led(1);
if(wait_for_250_ms_or_button_b())
return;
green_led(0);
if(wait_for_250_ms_or_button_b())
return;
}
// **** Line sensor test ************************************************
void ir_test()
{
unsigned int sensors[5]; // an array to hold sensor values
if(button_is_pressed(BUTTON_C))
read_line(sensors, IR_EMITTERS_OFF);
else
read_line(sensors, IR_EMITTERS_ON);
char i;
for(i = 0; i < 5; i++)
{ // Initialize the array of characters that we will use for the
// graph. Using the space, an extra copy of the one-bar
// character, and character 255 (a full black box), we get 10
// characters in the array.
// The variable c will have values from 0 to 9, since
// values are in the range of 0 to 2000, and 2000/201 is 9
// with integer math.
char c = bar_graph_characters[sensors[i]/201];
// Display the bar graph characters.
print_character(c);
}
// Display an indicator of whether IR is on or off
if(button_is_pressed(BUTTON_C))
print("IR-");
else
print(" C");
delay(100);
}
// **** Motor test ****************************************************
int m1_speed = 0;
int m2_speed = 0;
void motor_test()
{
char m1_back = 0, m2_back = 0;
char m1_char, m2_char;
if(button_is_pressed(BUTTON_A))
{
if(m1_speed == 0)
{
delay(200);
// If the button is pressed quickly when the motor is off, reverse direction.
if(!button_is_pressed(BUTTON_A))
m1_back = !m1_back;
}
m1_speed += 10;
}
else
m1_speed -= 20;
if(button_is_pressed(BUTTON_C))
{
if(m2_speed == 0)
{
delay(200);
// If the button is pressed quickly when the motor is off, reverse direction.
if(!button_is_pressed(BUTTON_C))
m2_back = !m2_back;
}
m2_speed += 10;
}
else
m2_speed -= 20;
if(m1_speed < 0)
m1_speed = 0;
if(m1_speed > 255)
m1_speed = 255;
if(m2_speed < 0)
m2_speed = 0;
if(m2_speed > 255)
m2_speed = 255;
// 255/26 = 9, so this gives values in the range of 0 to 9
m1_char = bar_graph_characters[m1_speed / 26];
m2_char = bar_graph_characters[m2_speed / 26];
print_character(m1_char);
print_character(m1_back ? 'a' : 'A');
print_character(m1_char);
lcd_goto_xy(5,0);
print_character(m2_char);
print_character(m2_back ? 'c' : 'C');
print_character(m2_char);
set_motors(m1_speed * (m1_back ? -1 : 1), m2_speed * (m2_back ? -1 : 1));
delay(50);
}
// **** Music test **********************************************************
const char fugue[] PROGMEM =
"! T120O5L16agafaea dac+adaea fa<aa<bac#a dac#adaea f"
"O6dcd<b-d<ad<g d<f+d<gd<ad<b- d<dd<ed<f+d<g d<f+d<gd<ad"
"L8MS<b-d<b-d MLe-<ge-<g MSc<ac<a MLd<fd<f O5MSb-gb-g"
"ML>c#e>c#e MS afaf ML gc#gc# MS fdfd ML e<b-e<b-"
"O6L16ragafaea dac#adaea fa<aa<bac#a dac#adaea faeadaca"
"<b-acadg<b-g egdgcg<b-g <ag<b-gcf<af dfcf<b-f<af"
"<gf<af<b-e<ge c#e<b-e<ae<ge <fe<ge<ad<fd"
"O5e>ee>ef>df>d b->c#b->c#a>df>d e>ee>ef>df>d"
"e>d>c#>db>d>c#b >c#agaegfe fO6dc#dfdc#<b c#4";
const char fugue_title[] PROGMEM = " \7 Fugue in D Minor - by J.S. Bach \7 ";
void music_test()
{
char fugue_title_pos = 0;
long last_shift = 0;
char c,i;
if(millis() - last_shift > 250)
{
for(i=0;i<8;i++)
{
c = pgm_read_byte(fugue_title + fugue_title_pos + i);
print_character(c);
}
last_shift = millis();
fugue_title_pos ++;
if(fugue_title_pos + 8 >= sizeof(fugue_title))
fugue_title_pos = 0;
}
if(!is_playing())
{
play_from_program_space(fugue);
}
delay(100);
}
// **** Potentiometer test ******************************************
void pot_test()
{
long start = millis();
char elapsed_ms;
int value;
set_analog_mode(MODE_10_BIT);
print_long(read_trimpot());
print(" "); // to clear the display
while((elapsed_ms = millis() - start) < 100)
{
value = read_trimpot();
play_frequency(value, 200, 15);
if(value < elapsed_ms * 10)
{
red_led(0);
green_led(1);
}
else
{
red_led(1);
green_led(0);
}
}
}
// **** Timer test ****************************************************
void time_test()
{
long elapsed_time = 0;
long last_read = 0;
long is_ticking = 0;
char a_is_pressed = 0;
char c_is_pressed = 0;
char last_seconds = 0;
long current_time = millis();
if(is_ticking)
elapsed_time += current_time - last_read;
last_read = current_time;
if(button_is_pressed(BUTTON_A) && !a_is_pressed)
{
// reset
a_is_pressed = 1;
is_ticking = 0;
elapsed_time = 0;
if(!is_playing()) // only play once
play_from_program_space(beep_button_a);
}
// find the end of the button press without stopping
if(!button_is_pressed(BUTTON_A))
a_is_pressed = 0;
if(button_is_pressed(BUTTON_C) && !c_is_pressed)
{
// start/stop
c_is_pressed = 1;
is_ticking = !is_ticking;
play_from_program_space(beep_button_c);
}
// find the end of the button press without stopping
if(!button_is_pressed(BUTTON_C))
c_is_pressed = 0;
print_long((elapsed_time/1000/60/10)%10); // tens of minutes
print_long((elapsed_time/1000/60)%10); // minutes
print_character(':');
print_long((elapsed_time/1000)%60/10); // tens of seconds
char seconds = ((elapsed_time/1000)%60)%10;
print_long(seconds); // seconds
print_character('.');
print_long((elapsed_time/100)%10); // tenths of seconds
print_long((elapsed_time/10)%10); // hundredths of seconds
// beep every second
if(seconds != last_seconds && elapsed_time != 0 && !is_playing())
play_from_program_space(timer_tick);
last_seconds = seconds;
}
// **** Menu overhead ***********************************************
void print_two_lines_delay_1s(const char *line1, const char *line2)
{
// Play welcome music and display a message
clear();
print_from_program_space(line1);
lcd_goto_xy(0,1);
print_from_program_space(line2);
delay(1000);
}
// waits for a button, plays the appropriate beep, and returns the button(s) that were pressed
char wait_for_button_and_beep()
{
char button = wait_for_button_press(ANY_BUTTON);
if(button & BUTTON_A)
play_from_program_space(beep_button_a);
else if(button & BUTTON_B)
play_from_program_space(beep_button_b);
else
play_from_program_space(beep_button_c);
wait_for_button_release(button);
return button;
}
// **** Start up, Initialization *******************************************
// Initializes the 3pi, displays a welcome message, calibrates, and plays the initial music.
void initialize()
{
// This must be called at the beginning of 3pi code, to set up the
// sensors. We use a value of 2000 for the timeout, which
// corresponds to 2000*0.4 us = 0.8 ms on our 20 MHz processor.
pololu_3pi_init(2000);
load_custom_characters(); // load the custom characters
play_from_program_space(welcome);
print_two_lines_delay_1s(welcome_line1,welcome_line2);
print_two_lines_delay_1s(demo_name_line1,demo_name_line2);
print_two_lines_delay_1s(instructions_line1,instructions_line2);
clear();
print_from_program_space(instructions_line3);
lcd_goto_xy(0,1);
print_from_program_space(instructions_line4);
while(!(wait_for_button_and_beep() & BUTTON_B));
play_from_program_space(thank_you_music);
print_two_lines_delay_1s(thank_you_line1,thank_you_line2);
}
// **** Main Menu selection ***************************************************
void menu_select()
{
int menu_index = 0;
print_two_lines_delay_1s(main_menu_intro_line1,main_menu_intro_line2);
while(1)
{
clear();
lcd_goto_xy(0,1);
print_from_program_space(menu_line2);
lcd_goto_xy(0,0);
print_from_program_space(main_menu_options[menu_index]);
lcd_show_cursor(CURSOR_BLINKING);
// the cursor will be blinking at the end of the option name
// wait for all buttons to be released, then a press
while(button_is_pressed(ANY_BUTTON));
char button = wait_for_button_press(ANY_BUTTON);
if(button & BUTTON_A)
{
play_from_program_space(beep_button_a);
menu_index --;
}
else if(button & BUTTON_B)
{
lcd_hide_cursor();
clear();
play_from_program_space(beep_button_b);
wait_for_button_release(button);
while(!button_is_pressed(BUTTON_B))
{
lcd_goto_xy(0,1);
print_from_program_space(back_line2);
lcd_goto_xy(0,0);
main_menu_functions[menu_index]();
}
set_motors(0,0);
stop_playing();
m1_speed = 0;
m2_speed = 0;
red_led(0);
green_led(0);
play_from_program_space(beep_button_b);
return;
}
else if(button & BUTTON_C)
{
play_from_program_space(beep_button_c);
menu_index ++;
}
if(menu_index < 0)
menu_index = main_menu_length-1;
if(menu_index >= main_menu_length)
menu_index = 0;
}
}
void setup() {
initialize(); // set up the 3pi
}
void loop() {
menu_select(); // the main menu
}