SumoORE Software

=Preliminary Software Library notes=

There are many different ways to write code. The code contained herein was written by a hardware guy, so excuse any crazy stuff that might make a a software person cringe. ;-)

The SumoORE libraries and demo program are written using the Microchip C18 Compiler currently. A JAL version is under development and we are looking at writing a version using the free Swordfish SE basic compiler if there is interest.

Installing the IDE and C18 compiler
The first step is to download and install MPLAB and then download and install the C18 Compiler. This will get the development environment up and running on your PC. The links above are for the windows version of MPLAB and C18. Microchip has a new environment under development called MPLAB X which runs on Linux and OSX. It is possible to use this new environment to develop on the SumoORE robots, but we will not discuss that here yet.

Getting the SumoORE C Library
The next step is to download the source library for this design. The current version is 1.2. This library contains a MPLAB C18 project which contains all the base level functions of the robot that are required (A/D readings, motor control, etc...). Because the base level software is already written, the user can begin coding the higher level functions of the robot immediately. Extract the archive to somewhere convenient (C:\work\xxx or something along those lines). The SumoORE.mcp file is the project file that MPLAB will load. This tells MPLAB which files are associated with the project. The .c and .h files are the source code that will be compiled for the robot.

=Overview of the C software library files=

Here is a quick description of the various files which comes with the software library. At the start, you simply need to start putting your code in the usercode.c file in the simplesumo function. This is the function that is called when the robot is first powered up and the first menu item (SumoSeek) is selected.


 * main.c : contains main{}, which is the first function called by a C program. In this case, main{} called the initialization code and then calls the user code. A few useful global variables are also potentially initialized here.
 * main.h : contains hardware configuration of PIC chip and defines some convenient terminology for the program to make every a little more readable
 * init.c : contains initialization code for the I/O pins, timers, interrupts, UARTs, etc.. Called at the start of the program and likely never again
 * interrupts.c : contains code that gets called by the interrupts. This includes ADC and timers primarily. The code here happens behind the scenes essentially so you don't have to take care of some low level things like generating servo pulses, reading ADCs, etc..
 * hardware.c : contains code that abstracts the hardware so that you can just use the hardware hopefully without worrying about the low level details
 * lcd.c : contains code which handles all the low level details of the LCD so that you can simply have characters show up without having to manually wiggle the LCD lines
 * testcode.c : code for demonstrating that various hardware and software features.
 * usercode.c : This is where all the user code is intended to go. When the program first starts up, it will configure the robot and then call one of the functions in usercode.c (from the main{} function in main.c). You can place your code in the function called simplesumo, spinsumo or create a new function with your own name.

=C Commands and Useful Variables=

What follows is a list of functions and variables that are part of the SumoORE software library. This functions have been written to handle the low level functions of the robot so that you can focus on the strategies and brains that your robot will need in order to win minisumo matches.

LCD Functions

 * lcd_print(...);
 * Prints strings to the 2x8 character LCD screen. You can treat this like the printf function, passing variables to display numbers, etc... Does not support floating point. '\n' moves to the next line (from line 1 to 2, or line 2 to 1). '\f' clears the screen and moves to line 1.


 * Example:
 * lcd_print("\f"); // clear the screen and leave it blank
 * lcd_print("\fLine 1\nLine2"); // Clear screen, print line 1 on first line and line 2 on second line
 * unsigned char val=10; lcd_print("val=%i",val); // display 'val=10' on the screen


 * lcd_gotoxy(line,position);
 * Moves the cursor to the given line and position. The next lcd_print or lcd_putc will be displayed at that location


 * Example:
 * lcd_gotoxy(1,2); lcd_print("*");
 * lcd_gotoxy(1,4); lcd_print("*");
 * lcd_gotoxy(1,6); lcd_print("*");
 * lcd_gotoxy(1,8); lcd_print("*"); // display ' * * * *' on the first line of the LCD


 * lcd_putc(character);
 * Prints a single character to the LCD, instead of a full string. Mostly useful when using custom characters.


 * Example:
 * lcd_putc('\f'); // clear the screen
 * lcd_putc('a'); // print the character "a" to the screen
 * lcd_putc('\1'); // print custom character 1 to the screen


 * display_batt;
 * Displays the current battery voltage at the current cursor location. Converts from the raw A/D value held in BATT_VOLT variable and displays the battery voltage in a X.X format.


 * Example:
 * lcd_gotoxy(2,5); display_batt; // displays current battery voltage at LCD location 2,5


 * lcd_init;
 * Initializes the LCD. Called by the main function when the board first powers up. Does not need to be called at any other point.


 * lcd_definechar(num,data[]);
 * used to program one of 8 custom characters to the LCD. [num] can be 0..7. [data[]] is a pointer to a set of 8 values which will define the custom character. Custom character can be printed to the LCD using special character \0, \1, ..., \7. Special characters 1 to 7 can be printed either using lcd_print or lcd_putc. Special character \0 can only be printed using lcd_putc.


 * Example:
 * // blink a custom bell character on and off
 * char custom[8]={0x04,0x0E,0x0E,0x0E,0x1F,0x00,0x04,0x00}; // custom bell character
 * lcd_definechar(1,custom); // call the routine to program this character
 * lcd_print("\fBell-->\1"); // clear the LCD and print 'Bell-->' on the first line
 * for{
 * lcd_gotoxy(1,8); // go to the last character in the first line
 * lcd_putc(1);    // display bell character
 * delay_ms(250);  // wait 250 milliseconds
 * lcd_gotoxy(1,8); // go to the last character in the first line
 * lcd_putc(' ');  // clear this location on the LCD
 * delay_ms(250);  //
 * } // loop forever, blinking bell character on and off

Motor Functions

 * set_servo(servo,direction,speed);
 * Used to set the servo speeds. [servo] can be LEFT, RIGHT or BOTH. [direction] can be FWD, BWD, FORWARD, or REVERSE. [Speed] ranges from 0 (stopped) to 100 (full speed). FWD/FORWARD and BWD/REVERSE are the same things, both are defined in case you prefer one terminology over the other. Setting the speed to STOP means that direction is ignored of course. Servo speeds do not change instantly if the variable flags.motorRampEnabled is TRUE. Instead, they will accelerate/decelerate to the new speed setting. See the Motor Acceleration heading below.


 * Example:
 * set_servo(BOTH,FORWARD,0); // stop both motors (set servo pulse to 1500us essentially)
 * set_servo(LEFT,REVERSE,20); // set only the left motor in reverse. Right motor continues doing whatever it already was


 * set_servo_raw(servo,pulse_width);
 * Used to set the exact servo pulse required. [servo] can be LEFT, RIGHT or BOTH. Pulse_width is given in microseconds and should be between 1000 to 2000 (1 to 2 milliseconds) for a typical servo. This is not normally needed and the set_servo function should be used in almost all cases.


 * Example:
 * set_servo_raw(LEFT,1500); // set left servo to middle position = stopped
 * set_servo_raw(BOTH,2000); // set both servo pulses to 2000... full speed. Note that the servos will move in the opposite directions on your robot with this command!


 * Enable/Disable Motors
 * The functions enableMotors and disableMotors are used to enable or disable the servo motors. This can be used to conserve power when the servos are not needed to be moving, or used during test code when the motors are not required. By default the motors are disabled, so you need to call the enableMotors function before the robot will move.


 * Example:
 * enableMotors; // allow the motors to start moving
 * disableMotors; // do not allow the motors to move


 * Motor Acceleration
 * Changing motors speeds instantaneously can be hard on the motors as well as cause the robot to make unintended movements. For instance: if you are moving backwards at full speed and then tell the robot to start moving forward at full speed, the robot may lift the front of the robot off the ground giving the opponent robot an opportunity to flip you. To counter this, when the speed of the robot is changed, it will accelerate/decelerate to the new speed when the variable flags.motorRampEnabled is TRUE. The speed of change is set by the variables servo_laccel (for left servo) and servo_raccel (for right servo). Higher values here allow for more quick speed changes, lower values will result in more gentle changes in speed. Speed ramping is enabled by default and the accelerations are set to 10.


 * Example:
 * // Robot will very slowly speed up to full speed foward, then quickly reverse direction
 * // to reverse at full speed, and then will switch direction to forward again instantly,
 * // causing it to briefly wheelie
 * flags.motorRampEnabled = TRUE; // Speed ramping is on
 * servo_laccel = 1;              // very slow servo acceleration
 * servo_raccel = 1;              // very slow servo acceleration
 * enableMotors;                // allow the motors to start moving
 * set_servo(BOTH,FORWARD,100);   // make both motors move forward
 * delay_ms(4000);                // let the robot move forward for 4 seconds
 * servo_laccel = 10;             // default servo acceleration
 * servo_raccel = 10;             // default servo acceleration
 * set_servo(BOTH,BACKWARD,100);  // make both motors move backwards
 * delay_ms(4000);                // let the robot move backwards for 4 seconds
 * flags.motorRampEnabled = FALSE; // Speed ramping is off
 * set_servo(BOTH,FORWARD,100);   // make both motors move forward.. Wheelie!

IR Range Sensor Functions

 * '''read_range(sensor);
 * Returns a value from the given sensor (LEFT, RIGHT, BACK), typically ranging from 0 (nothing detected) to 80 (close object detected). This function spends roughly 500us looking at the reflections, counting up how many reflections (roughly) are seen. Higher values indicate that a strong reflection was seen. This can be used as a roughly indicator of how close an object is or simply as an binary enemy detector. You should wait several milliseconds between calling this function as calling it too often will cause the IR detections to stop functioning briefly.


 * Example:
 * if (read_range(LEFT)) > 70 {
 * // object detected on our left!
 * .. do something ..
 * }

Input and Output Functions

 * ANALOG
 * The analog values are constantly being refreshed by an interrupt routine, so you simply need to use the variable which holds the current value and not worry about setting up the ADC, triggering ADC sampling, etc...


 * BATT_VOLT
 * The variable holds the current battery voltage in raw A/D values. Roughly, a value of 185 = full (7.2V), 169 = nominal (6.6V), and 156 = empty (6.0V). You can use the display_batt for displaying this in a human readable format.


 * Example:
 * // if battery voltage is less than 6V, display an error on LCD
 * if (BATT_VOLT<156) {
 * lcd_print("\fLOW BATT");
 * }


 * LINE_R2, LINE_R1, LINE_R0, LINE_C, LINE_L0, LINE_L1, LINE_L2
 * These variables hold the current line sensor readings, from rightmost sensor to leftmost sensor. Only LINE_R2 and LINE_L2 are installed be default on your robot. Values greater than LINE_THRESHOLD (set to 0x40 by default) indicate white. Values lower than this threshold indicate black.


 * Example:
 * if (LINE_R2>LINE_THRESHOLD) {
 * // White line detected on right sensor
 * .. do something ..
 * }


 * BUTTONS:
 * You can read the state of the two buttons using the BTN1 and BTN2 variables. A '1' indicates that the button is not pressed and a '0' indicated that the button is pressed.


 * Example:
 * if (BTN1=0) {.. do something ..} // do something if button 1is pressed
 * while (BTN2==1); // halt code execution until button 2 is pressed (until BTN2==0)


 * LEDS:
 * You can turn on and off the LEDS using the variables LED1 and LED2. By default the heartbeat counter is blinking LED1, so LED2 is the safer one to use unless you've disabled the heartbeat. '1' turns the led on and '0' turns the led off.


 * Example:
 * LED2=1; // turn on led 2
 * delay_ms(200); // wait 200ms
 * LED2=0; // turn off led 2


 * HeartBeat LED
 * By default, LED1 is set to blink on and off every second. You can enable and disable this using the flags.enableHeartbeat variable. Set to TRUE to enable, set to FALSE to disable the heartbeat. If you turn on or off the LED1 while the heartbeat is enabled, the heartbeat routine will over write your LED setting on the next heartbeat cycle. You can change the heartbeat rate in the main.h file by setting the HEARTBEAT_RATE to a different value (measured in milliseconds). Default is 1000 (1000ms or 1 second)


 * Example:
 * flags.enableHeartbeat=TRUE; // turn heartbeat function on
 * flags.enableHeartbeat=FALSE; // turn heartbeat function off


 * Alarm LED
 * LED2 can be configured to blink every 200ms as an alarm condition. You can enable and disable this using the flags.enableAlarm variable. Set to TRUE to enable, set to FALSE to disable the alarm. This is disabled by default. If you turn on or off LED2 while the alarm is enabled, the alarm routine will over write your LED setting on the next heartbeat cycle. You can change the alarm rate in the main.h file by setting the ALARM_RATE to a different value (measured in milliseconds). Default is 200 (200ms or 0.2 seconds)


 * Example:
 * flags.enableAlarm=TRUE; // turn alarm function on
 * flags.enableAlarm=FALSE; // turn alarm function off

Speaker Functions

 * beep(freq,duration);
 * Makes a tone from the speaker. This is uncalibrated right now. Frequency is the time between high/low pulses and duration is the number of pulses to make. This means that you will need to increase duration for lower values of freq if you want to play anything resembling music.


 * Example:
 * beep( 50, 200); // play a high pitched tone
 * beep(200, 50); // play a low pitched tone

Other Functions

 * delay_ms(milliseconds);
 * Delays code execution for the given time (in milliseconds). Depending on what background tasks are in progress (ADC sampling, etc..), the exact delay may vary slightly but can be used for rough delays. Any value between 1 and 65535 is valid.


 * Example:
 * delay_ms(500); // delay 0.5 seconds


 * delay_us(microseconds);
 * Delays code execution for the given time (in microseconds). Used for short delays. Depending on what background tasks are in progress (ADC sampling, etc..), the exact delay may vary slightly but can be used for rough delays +-2us. Any value between 1 and 255 is valid.


 * Example:
 * delay_us(30); // delay 30 microseconds roughly


 * delay_us_long(microseconds);
 * Delays code execution for the given time (in microseconds). Used for shorter delays. Depending on what background tasks are in progress (ADC sampling, etc..), the exact delay may vary slightly but can be used for rough delays +-10us. Any value between 1 and 65535 is valid. Use this delay instead of delay_us when you need greater than 255us of delay and higher resolution than delay_ms provides.


 * Example:
 * delay_us_long(425); // delay 425 microseconds roughly


 * random
 * This 8-bit variable holds a constantly changing value between 0 and 255 which can be used to make random decisions if you need. This can be used to ensure that the robot doesn't get stuck in a code loop. Sometimes it can be useful to add a random nature to your robot.


 * Example:
 * if (random>128) { ..do something..} // execute some particular code if random is greater than 128... basically a 50/50 chance of that code executing.


 * systemTime
 * This 32-bit variable increments roughly every millisecond. You can use this to measure time between events for instance.


 * Example:
 * unsigned long wait_until; // declare 32-bit variable wait_until
 * wait_until=systemTime+4000; // set wait_until to a value which is 4000ms larger than current systemTime
 * while (systemTime<wait_until){
 * // do the following code for 4 seconds
 * ...do something...
 * }

=Robot Demonstration Mode=

As of version 1.2 of the software libraries, the robot contains a set of test routines that the user can select from and run in the robot. The robot starts up with a menu, allowing you to choose one of 3 robot behaviours (SumoSeek, SumoSpin, LineRobo) as well as entering a test routine (TestMenu). When in the menus, BTN1 moves to the next menu selection and BTN2 activates the current selection. If you choose TestMenu, the robot will jump to a function in testcode.c called test_menu. This function allows the user to navigate a short menu, calling various bits of test routines.


 * fIR TEST
 * Front IR sensor test. The LCD will display the current range on the front left and right IR distance sensors, denoted by L and R. Two values are display for each sensor, instantaneous reading (the first value) and an averaged reading over 10 readings (the seconds value). You must reset to exit this routine.


 * bIR TEST
 * Back IR sensor test. The LCD will display the current range on the back IR sensors, denoted by L and R. Two values are display for the sensor, instantaneous reading (the first value) and an averaged reading over 10 readings (the seconds value). You must reset to exit this routine.


 * MOT TEST
 * Motor Test. BTN1 decreases the speed of the motors and BTN2 increases the speed of the motors. Speed 0 should be stopped. You can use this to adjust the servos. It can also be used to determine what various speed values in code relate to in actual robot movement. You must reset to exit this routine.


 * LINETEST
 * Line sensor test. Reads the two outside front ground sensors and report back the raw A/D readings. The left number is the left sensor reading and the right number is the right sensor reading. You must reset to exit this routine.


 * SPK TEST
 * Speaker Test. Plays various annoying noises to demonstrate how the speaker works. You must reset to exit this routine.


 * LED TEST
 * LED Test. Blinks the two LEDs at varying rates. You must reset to exit this routine.


 * BAT TEST
 * Battery test. Reads the current voltage of the battery and displays the raw A/D value on the first LCD line and the voltage converted to a real voltage level (roughly) on the second line. You must reset to exit this routine.


 * LCDTEST1
 * LCD Test 1. Moves an asterix around the screen to show how lcd_gotoxy and lcd_putc work. You must reset to exit this routine.


 * LCDTEST2
 * LCD Test 2. Programs several custom characters (a monkey named Apu and a bell) into the LCD and displays them. You must reset to exit this routine.

=Compiling Code=

You can now start MPLAB. From the project menu, choose 'Load..." and choose the SumoORE.mcp file that we extract. You can arrange the source code and project windows to your liking. Double-clicking the usercode.c file in the project window will open that source code. Here we can see the example line follower code that is going to run on our robot. Scroll down to the "void simplesumo(void)" line. This is the procedure that will be called after the robot has booted up and is ready to run.

This function is sample code for a simple sumo robot. It is just using the outer line sensors (LINE_R2 and LINE_L2) avoid the edge of the ring and the IR distance sensors to track the enemy. This is the code that you can build on to make your robot smarter and faster.

For now, lets compile the code. Pressing F10 (or clicking the Make or Build All buttons at the top of the screen) will compile the code. Assuming that there are no errors, the compiler will chug away and finally end with "BUILD SUCCEEDED". It will have created a "SumoORE.hex" file in the same directory where our source code is. This is the file that we will use to load onto the robot.

=Bootloader=

SumoORE is using the opensource Microchip USB HID bootloader from the Microchip Application Libraries. We've configured the bootloader for the SumoORE PIC18F4550 processor and preprogrammed it into the SumoORE robots. You can download the SumoORE bootloader .hex file and Bootloader application here. This bootloader application is only supported by windows currently.

Press and hold BTN1 on the SumoORE robot and then power it on. Once the robot is powered, you can stop holding the button. Pressing BTN1 during powerup triggers the bootloader built into the SumoORE.

Plug in the USB cable. If this is the first time, windows may ask for drivers. The drivers are contained in the /driver/ directory in the bootloader zip file that you downloaded above. Once the drivers have been installed, you will see the robot flashing its two leds on and off in sequence. This signals that the robot is ready to receive new code. Start the Microchip bootloader program, select the hex file and press the program button. Once it is finished uploaded, press the reset button in the GUI and disconnect the USB cable. The robot will now be running your code.

=Tutorial : Getting Started with C and the SumoORE=

We've added a getting-started tutorial to add a new behaviour to your sumo robot while learning C! You can find that here : SumoORE C Tutorial