Last active 1 month ago

Coaching guide, student coding reference, troubleshooting guide, and self-test programs (standard + mecanum) for middle school Botball teams.

coach_guide.md Raw

Botball Coach Guide

Your Job Isn't to Write the Code

The goal is for the students to understand what their robot is doing and why. When they're stuck, your instinct will be to fix it for them — resist that. Ask questions instead. "What do you think is happening?" is more valuable than pointing at the bug.

That said, don't let them spin indefinitely. A good rule: if students have tried three different ideas and nothing has changed, step in and suggest a new direction. Three attempts is enough to learn from; more than that without progress is just frustrating.


How to Coach a Troubleshooting Session

When something goes wrong, walk them through the same process every time. They'll internalize it.

1. Observe before touching anything. Watch the robot fail. Ask: "What did you expect to happen? What actually happened?" If they can't articulate the difference, they're not ready to fix it.

2. Isolate the problem. Is it mechanical (motor, servo, wiring), sensor-related, or code logic? Have them run the self-test program first if they're unsure. "Let's rule things out one at a time."

3. Form a hypothesis. "What do you think is causing it?" Make them guess. Wrong guesses are fine — they're learning. Then test the guess.

4. Change one thing at a time. Students will often change three things at once and then not know what worked. Slow them down. One change, one test, record what happened.

5. Use printf. If they can't see what the robot is thinking, have them print sensor values and motor states. "What does the sensor actually read when the robot is in that position?"


When They're Stuck on Code

They don't know where to start: Help them break it down. "What's the very first thing the robot needs to do in this section?" Get them writing one function at a time.

The code doesn't compile: Read the error with them, out loud. Compiler errors are intimidating but usually specific. Most common issues: missing semicolons, mismatched braces, calling a function before it's defined.

The robot does something unexpected: Ask them to walk you through the code line by line, out loud, and describe what each line should do. They'll often find the bug themselves mid-explanation. (This is called rubber duck debugging — it works.)

Timing is off (robot stops too early or too late): Remind them that msleep timings depend on battery level and surface conditions. If they're relying heavily on time-based movement, encourage them to add encoder or sensor checks.

The sensor threshold seems wrong: Have them print the sensor value in a loop while moving the robot to the exact position where the behavior should change. Then set the threshold to that value, with a small buffer.


Coaching Mecanum Bots Specifically

Mecanum robots are powerful but have a higher coordination cost. A few things to watch for:

  • Motor directions matter more. Each wheel needs to spin the right way for strafing and diagonal moves to work. If the bot is spinning instead of strafing, at least one motor direction is flipped. Run the self-test to check each motor in isolation.
  • "Squaring up" is tricky. Mecanum bots can strafe into walls to align, but students often just use time-based strafing. Encourage them to think about whether a sensor could give them a more reliable stop condition.
  • Inconsistent strafing. Friction and floor surface variation affect mecanum movement more than regular drive. If a strafe distance is inconsistent, use encoder ticks (cmpc/gmpc) instead of msleep.

Pre-Practice Habits Worth Building

  • Always run the self-test first. Before writing new code or debugging a route, verify the hardware is working. A loose motor wire will look like a code bug.
  • Charge batteries before practice, not during. Nothing kills debugging momentum like stopping to swap batteries mid-session.
  • Back up code before making changes. Encourage them to copy working code to a commented-out block or a separate file before experimenting. They'll thank you when they break something.
  • Write port assignments as #define constants at the top of the file. If a wire gets moved to a different port, changing one line is far better than hunting through code.

Pre-Competition Checklist

Run through this with the team the day before and the morning of competition.

  • Self-test passes on competition robot (all motors and servos respond correctly)
  • Sensor thresholds verified on the actual competition surface and lighting
  • wait_for_light() is the first call in main(), not buried in a section function
  • shut_down_in(119) is called early in main() as a safety net
  • All #define port constants match the physical wiring
  • Robot starts from the correct position and orientation
  • Team knows what to do if the robot fails mid-run (stay calm, note what happened for next run)
  • Battery is fully charged

Managing the Team Dynamic

Middle schoolers will disagree about the code. That's good — let them argue it out to a point, then have them test the disagreement rather than just debate it. "Both ideas sound reasonable. Let's try yours first and see what happens."

If one student is dominating the keyboard, rotate. Even if they're the strongest coder, the others need hands-on time.

Celebrate working things. A section that runs correctly in practice is worth noting, even if the full route isn't done yet.

self_test_mecanum.c Raw
1// ============================================================
2// SELF-TEST: Mecanum Robot
3// Run this before every practice and competition to verify
4// all hardware is working correctly.
5//
6// HOW TO USE:
7// 1. Update the port #defines below to match your robot
8// 2. Set TEST_MOTORS / TEST_SERVOS / TEST_SENSORS to 0
9// if you want to skip that section
10// 3. Set any unused port to -1 to skip it
11// 4. Run the program — press the A button to advance
12// between tests so you can watch each component
13//
14// OUTPUT: Open the Console window in the Wombat software
15// to see all printed messages.
16//
17// MECANUM MOTOR LAYOUT (top view):
18//
19// FRONT
20//
21// LF (0) RF (1)
22//
23// LB (2) RB (3)
24//
25// BACK
26//
27// ============================================================
28
29#include <kipr/wombat.h>
30
31// ============================================================
32// --- WHAT TO TEST (set to 0 to skip a whole section) ---
33// ============================================================
34#define TEST_MOTORS 1
35#define TEST_SERVOS 1
36#define TEST_SENSORS 1
37
38// ============================================================
39// --- PORT ASSIGNMENTS ---
40// Use -1 if you DON'T have something plugged in.
41// ============================================================
42
43// Drive motors — update these if yours are wired differently
44#define MOTOR_LF 0 // Left Front
45#define MOTOR_RF 1 // Right Front
46#define MOTOR_LB 2 // Left Back
47#define MOTOR_RB 3 // Right Back
48
49// Extra motors (set to -1 to skip)
50#define EXTRA_MOTOR_A -1
51#define EXTRA_MOTOR_B -1
52
53// Servos (set to -1 to skip)
54#define SERVO_0 0
55#define SERVO_1 1
56#define SERVO_2 2
57#define SERVO_3 3
58
59// Analog sensors (set to -1 to skip)
60#define ANALOG_0 0
61#define ANALOG_1 1
62#define ANALOG_2 2
63#define ANALOG_3 -1
64
65// Digital sensors (set to -1 to skip)
66#define DIGITAL_0 0
67#define DIGITAL_1 1
68#define DIGITAL_2 -1
69#define DIGITAL_3 -1
70
71// Speed for motor tests — keep low so robot doesn't travel far
72#define TEST_SPEED 60
73#define TEST_TIME 1200 // ms per direction
74
75// ============================================================
76// --- HELPERS ---
77// ============================================================
78
79void wait_for_a() {
80 printf(" [ Press A button to continue ]\n");
81 while (!a_button()) { msleep(50); }
82 msleep(400);
83}
84
85void section_header(const char* title) {
86 printf("\n=========================\n");
87 printf(" %s\n", title);
88 printf("=========================\n");
89}
90
91void set_drive(int lf, int rf, int lb, int rb) {
92 motor(MOTOR_LF, lf);
93 motor(MOTOR_RF, rf);
94 motor(MOTOR_LB, lb);
95 motor(MOTOR_RB, rb);
96}
97
98// ============================================================
99// --- TEST FUNCTIONS ---
100// ============================================================
101
102void test_single_motor(int port, const char* name) {
103 if (port < 0) return;
104
105 printf("\nMotor: %s (port %d)\n", name, port);
106 printf(" EXPECTED: Only this wheel spins.\n");
107 wait_for_a();
108
109 printf(" Forward (%d)...\n", TEST_SPEED);
110 printf(" EXPECTED: Wheel spins forward.\n");
111 motor(port, TEST_SPEED);
112 msleep(TEST_TIME);
113 ao();
114 msleep(300);
115
116 printf(" Backward (-%d)...\n", TEST_SPEED);
117 printf(" EXPECTED: Wheel spins backward.\n");
118 motor(port, -TEST_SPEED);
119 msleep(TEST_TIME);
120 ao();
121 msleep(300);
122
123 printf(" Done with %s.\n", name);
124}
125
126void test_drive_directions() {
127 int s = TEST_SPEED;
128
129 printf("\nForward\n");
130 printf(" EXPECTED: Robot moves straight forward.\n");
131 wait_for_a();
132 set_drive(s, s, s, s);
133 msleep(TEST_TIME);
134 ao(); msleep(500);
135
136 printf("\nBackward\n");
137 printf(" EXPECTED: Robot moves straight backward.\n");
138 wait_for_a();
139 set_drive(-s, -s, -s, -s);
140 msleep(TEST_TIME);
141 ao(); msleep(500);
142
143 printf("\nStrafe Right\n");
144 printf(" EXPECTED: Robot slides right with NO rotation.\n");
145 printf(" If it spins, a motor direction is wrong - note which ones.\n");
146 wait_for_a();
147 set_drive(s, -s, -s, s);
148 msleep(TEST_TIME);
149 ao(); msleep(500);
150
151 printf("\nStrafe Left\n");
152 printf(" EXPECTED: Robot slides left with NO rotation.\n");
153 wait_for_a();
154 set_drive(-s, s, s, -s);
155 msleep(TEST_TIME);
156 ao(); msleep(500);
157
158 printf("\nRotate Right\n");
159 printf(" EXPECTED: Robot spins clockwise in place.\n");
160 wait_for_a();
161 set_drive(s, -s, s, -s);
162 msleep(TEST_TIME);
163 ao(); msleep(500);
164
165 printf("\nRotate Left\n");
166 printf(" EXPECTED: Robot spins counter-clockwise in place.\n");
167 wait_for_a();
168 set_drive(-s, s, -s, s);
169 msleep(TEST_TIME);
170 ao(); msleep(500);
171}
172
173void test_servo(int port, const char* name) {
174 if (port < 0) return;
175
176 printf("\nServo: %s (port %d)\n", name, port);
177 printf(" EXPECTED: Servo sweeps from one end to the other and back to center.\n");
178 wait_for_a();
179
180 enable_servo(port);
181
182 printf(" Moving to 0...\n");
183 set_servo_position(port, 0);
184 msleep(1500);
185
186 printf(" Moving to 2047...\n");
187 set_servo_position(port, 2047);
188 msleep(1500);
189
190 printf(" Moving to center (1024)...\n");
191 set_servo_position(port, 1024);
192 msleep(1000);
193
194 disable_servo(port);
195 printf(" Done with %s.\n", name);
196}
197
198void test_sensors() {
199 printf("\nAnalog sensor readings (range 0-4095):\n");
200 printf(" EXPECTED: Values change when you cover or move the sensor.\n");
201 if (ANALOG_0 >= 0) printf(" Analog port %d: %4d\n", ANALOG_0, analog(ANALOG_0));
202 if (ANALOG_1 >= 0) printf(" Analog port %d: %4d\n", ANALOG_1, analog(ANALOG_1));
203 if (ANALOG_2 >= 0) printf(" Analog port %d: %4d\n", ANALOG_2, analog(ANALOG_2));
204 if (ANALOG_3 >= 0) printf(" Analog port %d: %4d\n", ANALOG_3, analog(ANALOG_3));
205
206 printf("\nDigital sensor readings (0 or 1):\n");
207 printf(" EXPECTED: Value flips when you press/activate the sensor.\n");
208 if (DIGITAL_0 >= 0) printf(" Digital port %d: %d\n", DIGITAL_0, digital(DIGITAL_0));
209 if (DIGITAL_1 >= 0) printf(" Digital port %d: %d\n", DIGITAL_1, digital(DIGITAL_1));
210 if (DIGITAL_2 >= 0) printf(" Digital port %d: %d\n", DIGITAL_2, digital(DIGITAL_2));
211 if (DIGITAL_3 >= 0) printf(" Digital port %d: %d\n", DIGITAL_3, digital(DIGITAL_3));
212
213 printf("\nWatching digital sensors for 5 seconds - press them now!\n");
214 int i;
215 for (i = 0; i < 50; i++) {
216 if (DIGITAL_0 >= 0 && digital(DIGITAL_0)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_0);
217 if (DIGITAL_1 >= 0 && digital(DIGITAL_1)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_1);
218 if (DIGITAL_2 >= 0 && digital(DIGITAL_2)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_2);
219 if (DIGITAL_3 >= 0 && digital(DIGITAL_3)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_3);
220 msleep(100);
221 }
222}
223
224// ============================================================
225// --- MAIN ---
226// ============================================================
227
228int main() {
229 enable_servos();
230
231 printf("========================================\n");
232 printf(" BOTBALL SELF-TEST: Mecanum Robot\n");
233 printf("========================================\n");
234 printf("Put the robot on the floor with room\n");
235 printf("to move in all directions.\n");
236 printf("Press A button to step through each test.\n");
237 printf("Check the Console window for output.\n");
238
239 msleep(1000);
240
241#if TEST_MOTORS
242 section_header("INDIVIDUAL MOTOR TESTS");
243 printf("Each wheel tested alone.\n");
244 printf("Watch which wheel spins - it should match the label.\n");
245 test_single_motor(MOTOR_LF, "Left Front (port 0)");
246 test_single_motor(MOTOR_RF, "Right Front (port 1)");
247 test_single_motor(MOTOR_LB, "Left Back (port 2)");
248 test_single_motor(MOTOR_RB, "Right Back (port 3)");
249 if (EXTRA_MOTOR_A >= 0) test_single_motor(EXTRA_MOTOR_A, "Extra Motor A");
250 if (EXTRA_MOTOR_B >= 0) test_single_motor(EXTRA_MOTOR_B, "Extra Motor B");
251
252 section_header("DRIVE DIRECTION TESTS");
253 printf("If strafe produces spinning, a motor direction is wrong.\n");
254 printf("Use the individual tests above to find which motor.\n");
255 test_drive_directions();
256#endif
257
258#if TEST_SERVOS
259 section_header("SERVO TESTS");
260 test_servo(SERVO_0, "Servo 0");
261 test_servo(SERVO_1, "Servo 1");
262 test_servo(SERVO_2, "Servo 2");
263 test_servo(SERVO_3, "Servo 3");
264#endif
265
266#if TEST_SENSORS
267 section_header("SENSOR TESTS");
268 test_sensors();
269#endif
270
271 disable_servos();
272
273 printf("\n========================================\n");
274 printf(" SELF-TEST COMPLETE\n");
275 printf("\n");
276 printf(" If everything moved correctly,\n");
277 printf(" your hardware is ready.\n");
278 printf("\n");
279 printf(" Strafe wrong? Check motor directions\n");
280 printf(" in the student coding guide.\n");
281 printf(" Fix wiring before fixing in code.\n");
282 printf("========================================\n");
283
284 ao();
285 return 0;
286}
287
self_test_standard.c Raw
1// ============================================================
2// SELF-TEST: Standard (Non-Mecanum) Robot
3// Run this before every practice and competition to verify
4// all hardware is working correctly.
5//
6// HOW TO USE:
7// 1. Update the port #defines below to match your robot
8// 2. Set TEST_MOTORS / TEST_SERVOS / TEST_SENSORS to 0
9// if you want to skip that section
10// 3. Set any unused port to -1 to skip it
11// 4. Run the program — press the A button to advance
12// between tests so you can watch each component
13//
14// OUTPUT: Open the Console window in the Wombat software
15// to see all printed messages.
16// ============================================================
17
18#include <kipr/wombat.h>
19
20// ============================================================
21// --- WHAT TO TEST (set to 0 to skip a whole section) ---
22// ============================================================
23#define TEST_MOTORS 1
24#define TEST_SERVOS 1
25#define TEST_SENSORS 1
26
27// ============================================================
28// --- PORT ASSIGNMENTS ---
29// Use -1 if you DON'T have something plugged in.
30// Example: only one extra motor? Set EXTRA_MOTOR_B to -1.
31// ============================================================
32
33// Drive motors
34#define LEFT_MOTOR 3
35#define RIGHT_MOTOR 0
36
37// Extra motors, e.g. arm motor, pulley (set to -1 to skip)
38#define EXTRA_MOTOR_A 1
39#define EXTRA_MOTOR_B -1
40
41// Servos (set to -1 to skip)
42#define SERVO_0 0
43#define SERVO_1 1
44#define SERVO_2 -1
45#define SERVO_3 -1
46
47// Analog sensors (set to -1 to skip)
48#define ANALOG_0 0
49#define ANALOG_1 1
50#define ANALOG_2 2
51#define ANALOG_3 -1
52
53// Digital sensors (set to -1 to skip)
54#define DIGITAL_0 0
55#define DIGITAL_1 1
56#define DIGITAL_2 -1
57#define DIGITAL_3 -1
58
59// Speed for motor tests — keep low so robot doesn't travel far
60#define TEST_SPEED 60
61#define TEST_TIME 1500 // ms per direction
62
63// ============================================================
64// --- HELPERS ---
65// ============================================================
66
67void wait_for_a() {
68 printf(" [ Press A button to continue ]\n");
69 while (!a_button()) { msleep(50); }
70 msleep(400);
71}
72
73void section_header(const char* title) {
74 printf("\n=========================\n");
75 printf(" %s\n", title);
76 printf("=========================\n");
77}
78
79// ============================================================
80// --- TEST FUNCTIONS ---
81// ============================================================
82
83void test_motor(int port, const char* name) {
84 if (port < 0) return;
85
86 printf("\nMotor: %s (port %d)\n", name, port);
87 printf(" EXPECTED: Only this motor spins, not the whole robot.\n");
88 wait_for_a();
89
90 printf(" Forward (%d)...\n", TEST_SPEED);
91 printf(" EXPECTED: Motor spins forward.\n");
92 motor(port, TEST_SPEED);
93 msleep(TEST_TIME);
94 ao();
95 msleep(300);
96
97 printf(" Backward (-%d)...\n", TEST_SPEED);
98 printf(" EXPECTED: Motor spins backward.\n");
99 motor(port, -TEST_SPEED);
100 msleep(TEST_TIME);
101 ao();
102 msleep(300);
103
104 printf(" Done with %s.\n", name);
105}
106
107void test_servo(int port, const char* name) {
108 if (port < 0) return;
109
110 printf("\nServo: %s (port %d)\n", name, port);
111 printf(" EXPECTED: Servo sweeps from one end to the other and back to center.\n");
112 wait_for_a();
113
114 enable_servo(port);
115
116 printf(" Moving to 0...\n");
117 set_servo_position(port, 0);
118 msleep(1500);
119
120 printf(" Moving to 2047...\n");
121 set_servo_position(port, 2047);
122 msleep(1500);
123
124 printf(" Moving to center (1024)...\n");
125 set_servo_position(port, 1024);
126 msleep(1000);
127
128 disable_servo(port);
129 printf(" Done with %s.\n", name);
130}
131
132void test_sensors() {
133 printf("\nAnalog sensor readings (range 0-4095):\n");
134 printf(" EXPECTED: Values change when you cover or move the sensor.\n");
135 if (ANALOG_0 >= 0) printf(" Analog port %d: %4d\n", ANALOG_0, analog(ANALOG_0));
136 if (ANALOG_1 >= 0) printf(" Analog port %d: %4d\n", ANALOG_1, analog(ANALOG_1));
137 if (ANALOG_2 >= 0) printf(" Analog port %d: %4d\n", ANALOG_2, analog(ANALOG_2));
138 if (ANALOG_3 >= 0) printf(" Analog port %d: %4d\n", ANALOG_3, analog(ANALOG_3));
139
140 printf("\nDigital sensor readings (0 or 1):\n");
141 printf(" EXPECTED: Value flips when you press/activate the sensor.\n");
142 if (DIGITAL_0 >= 0) printf(" Digital port %d: %d\n", DIGITAL_0, digital(DIGITAL_0));
143 if (DIGITAL_1 >= 0) printf(" Digital port %d: %d\n", DIGITAL_1, digital(DIGITAL_1));
144 if (DIGITAL_2 >= 0) printf(" Digital port %d: %d\n", DIGITAL_2, digital(DIGITAL_2));
145 if (DIGITAL_3 >= 0) printf(" Digital port %d: %d\n", DIGITAL_3, digital(DIGITAL_3));
146
147 printf("\nWatching digital sensors for 5 seconds - press them now!\n");
148 int i;
149 for (i = 0; i < 50; i++) {
150 if (DIGITAL_0 >= 0 && digital(DIGITAL_0)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_0);
151 if (DIGITAL_1 >= 0 && digital(DIGITAL_1)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_1);
152 if (DIGITAL_2 >= 0 && digital(DIGITAL_2)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_2);
153 if (DIGITAL_3 >= 0 && digital(DIGITAL_3)) printf(" >> Digital port %d TRIGGERED\n", DIGITAL_3);
154 msleep(100);
155 }
156}
157
158// ============================================================
159// --- MAIN ---
160// ============================================================
161
162int main() {
163 enable_servos();
164
165 printf("========================================\n");
166 printf(" BOTBALL SELF-TEST: Standard Robot\n");
167 printf("========================================\n");
168 printf("Keep the robot clear of obstacles.\n");
169 printf("Watch each component as it's tested.\n");
170 printf("Press A button to step through each test.\n");
171 printf("Check the Console window for output.\n");
172
173 msleep(1000);
174
175#if TEST_MOTORS
176 section_header("MOTOR TESTS");
177 printf("Each motor tested individually.\n");
178 test_motor(LEFT_MOTOR, "Left Motor");
179 test_motor(RIGHT_MOTOR, "Right Motor");
180 test_motor(EXTRA_MOTOR_A, "Extra Motor A");
181 test_motor(EXTRA_MOTOR_B, "Extra Motor B");
182#endif
183
184#if TEST_SERVOS
185 section_header("SERVO TESTS");
186 test_servo(SERVO_0, "Servo 0");
187 test_servo(SERVO_1, "Servo 1");
188 test_servo(SERVO_2, "Servo 2");
189 test_servo(SERVO_3, "Servo 3");
190#endif
191
192#if TEST_SENSORS
193 section_header("SENSOR TESTS");
194 test_sensors();
195#endif
196
197 disable_servos();
198
199 printf("\n========================================\n");
200 printf(" SELF-TEST COMPLETE\n");
201 printf("\n");
202 printf(" If everything moved correctly,\n");
203 printf(" your hardware is ready.\n");
204 printf("\n");
205 printf(" Something wrong? Check the cable\n");
206 printf(" and port number for that component.\n");
207 printf("========================================\n");
208
209 ao();
210 return 0;
211}
212
student_coding_guide.md Raw

Coding Guide for Botball

Start Every File the Same Way

Define your ports at the top using #define. This way if a wire gets moved, you only change one line instead of hunting through the whole program.

#include <kipr/wombat.h>

// --- PORT ASSIGNMENTS ---
#define LEFT_MOTOR    0
#define RIGHT_MOTOR   3
#define ARM_SERVO     3
#define CLAW_SERVO    2
#define LINE_SENSOR   0
#define BUMP_SENSOR   1

// --- THRESHOLDS ---
#define BLACK_VALUE   3000
#define LINE_THRESHOLD 2000

Then your code reads like plain English: motor(LEFT_MOTOR, 100) is much clearer than motor(0, 100).


Break Your Route into Functions

Don't write one giant main(). Write one function per task. Each function should do one thing and have a name that describes what it does.

void drive_to_wall() { ... }
void grab_object()   { ... }
void return_to_start() { ... }

int main() {
    wait_for_light(0);
    shut_down_in(119);

    drive_to_wall();
    grab_object();
    return_to_start();

    ao();
    return 0;
}

Benefits: easier to test one section at a time, easier to fix bugs, easier to read.


Always Stop Your Motors

Every time you start motors, there needs to be a clear place where they stop. Forgetting ao() is one of the most common bugs.

// Good: clear start and stop
motor(LEFT_MOTOR, 80);
motor(RIGHT_MOTOR, 80);
msleep(1000);
ao();            // <-- don't forget this
msleep(200);     // brief pause after stopping

Two Ways to Control Distance

Time-based: Simple, but affected by battery level and surface friction. Good for rough positioning.

motor(LEFT_MOTOR, 80);
motor(RIGHT_MOTOR, 80);
msleep(1500);   // go for 1.5 seconds
ao();

Encoder-based: More reliable for consistent distances. Encoders count wheel "ticks" as the wheel turns — but how many ticks per full rotation depends on your motor and gearing. Common values are around 1440 ticks/rotation for Tetrix motors, but ask your coach before relying on exact numbers. The tick counts in your route code are found by testing, not calculated.

cmpc(LEFT_MOTOR);   // reset counter to 0
while (gmpc(LEFT_MOTOR) < 5000) {
    motor(LEFT_MOTOR, 80);
    motor(RIGHT_MOTOR, 80);
    msleep(10);     // always include a short sleep in loops like this —
                    // without it the loop runs so fast it can crash the Wombat
}
ao();

When precision matters (like lining up to grab something), use encoders. When you just need to get somewhere approximately, time-based is fine.


Sensor-Based Stopping

Using a sensor to stop is more reliable than guessing how long something takes.

// Stop when you hit a wall
while (digital(BUMP_SENSOR) == 0) {
    motor(LEFT_MOTOR, 60);
    motor(RIGHT_MOTOR, 60);
    msleep(10);
}
ao();

// Stop when you reach a black line
while (analog(LINE_SENSOR) < BLACK_VALUE) {
    motor(LEFT_MOTOR, 60);
    motor(RIGHT_MOTOR, 60);
    msleep(10);
}
ao();

If a sensor isn't working, a time-based fallback is okay for competition, but plan to fix it.


Debug with printf

printf is your best debugging tool. Print sensor values while the robot is running to see what it's actually experiencing.

// Print once each loop iteration - see what the sensor reads
while (1) {
    printf("line sensor: %d\n", analog(LINE_SENSOR));
    msleep(100);
}

Watch the output in the Wombat's terminal as the robot moves. This will tell you the right threshold values to use. When you find a value that works, set it as a #define constant.

Remove or comment out excessive printf calls before competition — they slow the program down.


Servos: Enable, Move, and Be Patient

Always call enable_servos() before using servos, and disable_servos() when done. After setting a position, give the servo time to physically reach it.

enable_servos();

set_servo_position(ARM_SERVO, 1000);   // move arm
msleep(1000);                           // wait for it to get there

set_servo_position(CLAW_SERVO, 2047);  // close claw
msleep(500);

disable_servos();

Servo positions range from 0 to 2047. You'll need to experiment to find the right positions for your specific arm — there's no universal answer.


Mecanum Wheels: Motor Direction Reference

Movement Motor 0 (LF) Motor 1 (RF) Motor 2 (LB) Motor 3 (RB)
Forward + + + +
Backward - - - -
Strafe Right + - - +
Strafe Left - + + -
Rotate Right + - + -
Rotate Left - + - +

If something isn't moving the right direction, check whether one or more motors is spinning backwards. The self-test program will help you verify each motor individually.


Common Mistakes

Changing too many things at once. If the robot isn't working, change one thing and test it before changing anything else. Otherwise you won't know what fixed it.

Hardcoding port numbers. Use #define constants. motor(0, 100) is hard to read and hard to fix when something changes.

No pause between movements. Add a short ao(); msleep(200); between sections. It gives the robot time to stop fully before the next movement starts.

Trusting timing over sensors. If the robot does the same task multiple times and the timing varies, use a sensor instead.

Not testing sections separately. If you're debugging section 3, comment out sections 1 and 2 so you don't have to wait through them every test run.

One giant main(). If your main() is longer than about 30 lines, you probably need more functions. Long main() functions are hard to debug and hard to test piece by piece.

student_troubleshooting_guide.md Raw

Troubleshooting Guide

When something isn't working, don't just start changing code. Observe first, then figure out what's actually wrong.


Step 1: Run the Self-Test

Before assuming it's a code problem, run self_test.c. It will test each motor and servo one at a time. This rules out hardware problems quickly.

If a motor or servo fails the self-test:

  • Check the cable connection at both ends
  • Try swapping the cable
  • Check the port number in the test file matches where it's plugged in

Fix Hardware Before Code

If something moves the wrong direction, fix the wiring first before changing code. Hardware fixes work everywhere. Code fixes only work in one program, and future code becomes confusing to write.


My Robot Won't Move At All

Work through this in order:

  1. Are the motors getting power? Check that the Wombat is on and the battery has charge.
  2. Does the self-test work? If a motor works in the self-test but not in your code, the bug is in your code.
  3. Are you calling ao() before you move? If there's an ao() right before your motor call, that's fine. But if you have ao() inside a loop where you want movement, that's stopping the motor every iteration.
  4. Are you using the right port number? Compare your #define constants to the physical port labels on the Wombat.

Robot Curves Instead of Driving Straight

If your robot drives forward but slowly curves left or right, this is very common and almost never a wiring problem.

Possible causes: one motor is slightly stronger than the other (very common), uneven wheel friction, low battery, or uneven weight distribution.

Fix it by adjusting motor speeds slightly until the robot tracks straight:

motor(LEFT_MOTOR, 80);
motor(RIGHT_MOTOR, 75);  // reduce the faster side in small steps (3-10 points)

Test, watch, adjust, repeat. Once it drives straight, write down the values.

Robot Spins in Place Instead of Moving Forward

If the robot spins rather than driving forward, one motor is running backwards. This is a direction problem, not a speed problem.

Fix this by swapping the two wires on that motor — don't fix it in code if you can avoid it. Code fixes for direction make future programs confusing.

If wiring can't be changed, you can negate the speed in code:

// If LEFT_MOTOR spins wrong, negate its value:
motor(LEFT_MOTOR, -80);   // was 80
motor(RIGHT_MOTOR, 80);

Mecanum Bot Spins Instead of Strafing

One or more wheels is going the wrong direction. Run the self-test to check each motor individually, then compare against the motor direction table in the coding guide. Fix in wiring if possible.


Robot Drifts to One Side

This is almost always a hardware issue, not a code issue. Check:

  • Are both drive wheels touching the ground equally? An uneven chassis will cause drift.
  • Are the wheels on tight? A loose wheel slips.
  • Is one motor slightly slower than the other? You can compensate in code by lowering the faster motor's speed slightly (e.g., motor(LEFT_MOTOR, 95) instead of 100) until it drives straight. Use printf to print encoder values of both motors while driving — they should be roughly equal.

Debug with printf — Where Does the Output Go?

Open the Console window in the Wombat software. That's where printf messages appear. If you don't see the console, look for a "Console" or "Output" tab in the interface.


Sensor Isn't Reading What I Expect

First: print the actual value. Don't guess. Put this in a loop and watch the output:

while(1) {
    printf("sensor value: %d\n", analog(0));
    msleep(100);
}

Move the robot/sensor to different positions and see what values you get. Set your threshold based on actual readings, not guesses.

Digital sensor always reads 0 or always reads 1:

  • Check the cable connection
  • Make sure you're reading the right port number
  • Check that the sensor isn't physically stuck

Analog sensor reads 0:

  • Usually a disconnected or broken cable
  • Try a different port and update the #define

Line sensor doesn't detect black:

  • The sensor may be too far from the surface. It should be close but not dragging.
  • Check what value it reads on white vs. black. Your #define BLACK_VALUE threshold should be somewhere between the two.
  • Lighting in the competition venue may be different from where you practiced. Re-check thresholds on the day of competition if possible.

Robot Stops Too Early or Too Late

Time-based movement: msleep timing depends on battery charge and floor friction. A lower battery makes the robot slower, so it travels less distance in the same time. If you're using time-based movement for anything precise, consider switching to encoder-based.

Encoder-based movement: Print the encoder value at the moment it stops to make sure it's reaching the target. If it stops too soon, the target tick count is too low. Add printf("ticks: %d\n", gmpc(0)) inside your loop.

Sensor-based stopping: If the robot doesn't stop at a line, the threshold is probably wrong. Print sensor values and adjust. If the robot stops immediately, the sensor is already reading above the threshold before it starts moving — check your starting position.


Arm / Claw Isn't Moving

  1. Did you call enable_servos() before set_servo_position()?
  2. Is the port number correct?
  3. Are you giving it enough time to move? set_servo_position tells the servo where to go, but msleep(1000) gives it time to actually get there.
  4. Run the self-test — does the servo work at all? If not, check the cable.
  5. Is the servo stalled against something mechanical? A servo fighting against a physical obstruction will hum, get hot, and eventually stop working.

Code Compiles But Crashes or Freezes

Infinite loop: You probably have a while loop with a condition that never becomes false. Add a printf inside the loop to confirm it's actually running. Ask: what would need to change for the loop to end? Is that thing actually changing?

Robot freezes partway through the route: Add printf("section X starting\n") at the beginning of each function. This tells you exactly where execution stopped.

Timing out: If your route takes more than 119 seconds, shut_down_in(119) will cut power to the motors. If the robot stops unexpectedly near the end of a long run, this might be why.


Competition Day Problems

The route worked in practice but fails at competition:

  • Different lighting → re-check sensor thresholds
  • Different floor surface → re-check line sensor sensitivity and timing
  • Battery not fully charged → movements will be slower than expected

Robot starts before the light:

  • wait_for_light() can be triggered by ambient light changes. If the venue is bright, it may fire early. Test wait_for_light() in the actual competition lighting before your run.

Comments (1)

GalileoBotball 1 month ago

Yay! Thank you, this is amazing and I am sure this will help our team! -Roman R.

Log in to leave a comment.