Support » Pololu 3pi Robot User’s Guide » 10. Expansion Information »
10.b. Serial master program
A serial master program used to control the serial slave program is included with the Pololu AVR Library (see Section 6) in libpololu-avr\examples\atmegaxx8\3pi-serial-master
. The program is designed to run on an Orangutan SV-xx8, LV-168, or 3pi as a demonstration of what is possible, but you will probably want to adapt it for your own controller. To use the program, make the following connections between your master and slave:
- GND-GND
- PD0-PD1
- PD1-PD0
Turn on both master and slave. The master will display a “Connect” message followed by the signature of the slave source code (e.g. “3pi1.0”). The master will then instruct the slave to display “Connect” and play a short tune. Pressing the B botton on the master causes the slave to go through an auto-calibration routine, after which you can drive the slave around using the A and C buttons on the master, while viewing sensor data on the master’s LCD. Holding down the B button causes the slave to do PID line following.
Source code
#include <pololu/orangutan.h> #include <string.h> /* * 3pi-serial-master - An example serial master program for the Pololu * 3pi Robot. This can run on any board supported by the library; * it is intended as an example of how to use the master/slave * routines. * * http://www.pololu.com/docs/0J21 * http://www.pololu.com/docs/0J20 * http://www.poolu.com/ */ // Data for generating the characters used in load_custom_characters // and display_readings. By reading levels[] starting at various // offsets, we can generate all of the 7 extra characters needed for a // bargraph. This is also stored in program space. const char levels[] PROGMEM = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111 }; // This function loads custom characters into the LCD. Up to 8 // characters can be loaded; we use them for 6 levels of a bar graph // plus a back arrow and a musical note character. 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); clear(); // the LCD must be cleared for the characters to take effect } // 10 levels of bar graph characters const char bar_graph_characters[10] = {' ',0,0,1,2,3,3,4,5,255}; void display_levels(unsigned int *sensors) { clear(); int 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 1000, and 1000/101 is 9 // with integer math. char c = bar_graph_characters[sensors[i]/101]; // Display the bar graph characters. print_character(c); } } // set the motor speeds void slave_set_motors(int speed1, int speed2) { char message[4] = {0xC1, speed1, 0xC5, speed2}; if(speed1 < 0) { message[0] = 0xC2; // m1 backward message[1] = -speed1; } if(speed2 < 0) { message[2] = 0xC6; // m2 backward message[3] = -speed2; } serial_send_blocking(message,4); } // do calibration void slave_calibrate() { serial_send("\xB4",1); int tmp_buffer[5]; // read 10 characters (but we won't use them) serial_receive_blocking((char *)tmp_buffer, 10, 100); } // reset calibration void slave_reset_calibration() { serial_send_blocking("\xB5",1); } // calibrate (waits for a 1-byte response to indicate completion) void slave_auto_calibrate() { int tmp_buffer[1]; serial_send_blocking("\xBA",1); serial_receive_blocking((char *)tmp_buffer, 1, 10000); } // sets up the pid constants on the 3pi for line following void slave_set_pid(char max_speed, char p_num, char p_den, char d_num, char d_den) { char string[6] = "\xBB"; string[1] = max_speed; string[2] = p_num; string[3] = p_den; string[4] = d_num; string[5] = d_den; serial_send_blocking(string,6); } // stops the pid line following void slave_stop_pid() { serial_send_blocking("\xBC", 1); } // clear the slave LCD void slave_clear() { serial_send_blocking("\xB7",1); } // print to the slave LCD void slave_print(char *string) { serial_send_blocking("\xB8", 1); char length = strlen(string); serial_send_blocking(&length, 1); // send the string length serial_send_blocking(string, length); } // go to coordinates x,y on the slave LCD void slave_lcd_goto_xy(char x, char y) { serial_send_blocking("\xB9",1); serial_send_blocking(&x,1); serial_send_blocking(&y,1); } int main() { char buffer[20]; // load the bar graph load_custom_characters(); // configure serial clock for 115.2 kbaud serial_set_baud_rate(115200); // wait for the device to show up while(1) { clear(); print("Master"); delay_ms(100); serial_send("\x81",1); if(serial_receive_blocking(buffer, 6, 50)) continue; clear(); print("Connect"); lcd_goto_xy(0,1); buffer[6] = 0; print(buffer); // clear the slave's LCD and display "Connect" and "OK" on two lines // Put OK in the center to test x-y positioning slave_clear(); slave_print("Connect"); slave_lcd_goto_xy(3,1); slave_print("OK"); // play a tune char tune[] = "\xB3 l16o6gab>c"; tune[1] = sizeof(tune)-3; serial_send_blocking(tune,sizeof(tune)-1); // wait wait_for_button(ALL_BUTTONS); // reset calibration slave_reset_calibration(); time_reset(); slave_auto_calibrate(); unsigned char speed1 = 0, speed2 = 0; // read sensors in a loop while(1) { serial_send("\x87",1); // returns calibrated sensor values // read 10 characters if(serial_receive_blocking(buffer, 10, 100)) break; // get the line position serial_send("\xB6", 1); int line_position[1]; if(serial_receive_blocking((char *)line_position, 2, 100)) break; // get the battery voltage serial_send("\xB1",1); // read 2 bytes int battery_millivolts[1]; if(serial_receive_blocking((char *)battery_millivolts, 2, 100)) break; // display readings display_levels((unsigned int*)buffer); lcd_goto_xy(5,0); line_position[0] /= 4; // to get it into the range of 0-1000 if(line_position[0] == 1000) line_position[0] = 999; // keep to a max of 3 chars print_long(line_position[0]); print(" "); lcd_goto_xy(0,1); print_long(battery_millivolts[0]); print(" mV "); delay_ms(10); // if button A is pressed, increase motor1 speed if(button_is_pressed(BUTTON_A) && speed1 < 127) speed1 ++; else if(speed1 > 1) speed1 -= 2; else if(speed1 > 0) speed1 = 0; // if button C is pressed, control motor2 if(button_is_pressed(BUTTON_C) && speed2 < 127) speed2 ++; else if(speed2 > 1) speed2 -= 2; else if(speed2 > 0) speed2 = 0; // if button B is pressed, do PID control if(button_is_pressed(BUTTON_B)) slave_set_pid(40, 1, 20, 3, 2); else { slave_stop_pid(); slave_set_motors(speed1, speed2); } } } while(1); }