jesse revised this gist 1 month ago. Go to revision
5 files changed, 920 insertions
coach_guide.md(file created)
| @@ -0,0 +1,86 @@ | |||
| 1 | + | # Botball Coach Guide | |
| 2 | + | ||
| 3 | + | ## Your Job Isn't to Write the Code | |
| 4 | + | ||
| 5 | + | 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. | |
| 6 | + | ||
| 7 | + | 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. | |
| 8 | + | ||
| 9 | + | --- | |
| 10 | + | ||
| 11 | + | ## How to Coach a Troubleshooting Session | |
| 12 | + | ||
| 13 | + | When something goes wrong, walk them through the same process every time. They'll internalize it. | |
| 14 | + | ||
| 15 | + | **1. Observe before touching anything.** | |
| 16 | + | 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. | |
| 17 | + | ||
| 18 | + | **2. Isolate the problem.** | |
| 19 | + | 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." | |
| 20 | + | ||
| 21 | + | **3. Form a hypothesis.** | |
| 22 | + | "What do you think is causing it?" Make them guess. Wrong guesses are fine — they're learning. Then test the guess. | |
| 23 | + | ||
| 24 | + | **4. Change one thing at a time.** | |
| 25 | + | Students will often change three things at once and then not know what worked. Slow them down. One change, one test, record what happened. | |
| 26 | + | ||
| 27 | + | **5. Use `printf`.** | |
| 28 | + | 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?" | |
| 29 | + | ||
| 30 | + | --- | |
| 31 | + | ||
| 32 | + | ## When They're Stuck on Code | |
| 33 | + | ||
| 34 | + | **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. | |
| 35 | + | ||
| 36 | + | **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. | |
| 37 | + | ||
| 38 | + | **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.) | |
| 39 | + | ||
| 40 | + | **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. | |
| 41 | + | ||
| 42 | + | **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. | |
| 43 | + | ||
| 44 | + | --- | |
| 45 | + | ||
| 46 | + | ## Coaching Mecanum Bots Specifically | |
| 47 | + | ||
| 48 | + | Mecanum robots are powerful but have a higher coordination cost. A few things to watch for: | |
| 49 | + | ||
| 50 | + | - **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. | |
| 51 | + | - **"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. | |
| 52 | + | - **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`. | |
| 53 | + | ||
| 54 | + | --- | |
| 55 | + | ||
| 56 | + | ## Pre-Practice Habits Worth Building | |
| 57 | + | ||
| 58 | + | - **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. | |
| 59 | + | - **Charge batteries before practice, not during.** Nothing kills debugging momentum like stopping to swap batteries mid-session. | |
| 60 | + | - **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. | |
| 61 | + | - **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. | |
| 62 | + | ||
| 63 | + | --- | |
| 64 | + | ||
| 65 | + | ## Pre-Competition Checklist | |
| 66 | + | ||
| 67 | + | Run through this with the team the day before and the morning of competition. | |
| 68 | + | ||
| 69 | + | - [ ] Self-test passes on competition robot (all motors and servos respond correctly) | |
| 70 | + | - [ ] Sensor thresholds verified on the actual competition surface and lighting | |
| 71 | + | - [ ] `wait_for_light()` is the first call in `main()`, not buried in a section function | |
| 72 | + | - [ ] `shut_down_in(119)` is called early in `main()` as a safety net | |
| 73 | + | - [ ] All `#define` port constants match the physical wiring | |
| 74 | + | - [ ] Robot starts from the correct position and orientation | |
| 75 | + | - [ ] Team knows what to do if the robot fails mid-run (stay calm, note what happened for next run) | |
| 76 | + | - [ ] Battery is fully charged | |
| 77 | + | ||
| 78 | + | --- | |
| 79 | + | ||
| 80 | + | ## Managing the Team Dynamic | |
| 81 | + | ||
| 82 | + | 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." | |
| 83 | + | ||
| 84 | + | If one student is dominating the keyboard, rotate. Even if they're the strongest coder, the others need hands-on time. | |
| 85 | + | ||
| 86 | + | 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(file created)
| @@ -0,0 +1,286 @@ | |||
| 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 | + | ||
| 79 | + | void wait_for_a() { | |
| 80 | + | printf(" [ Press A button to continue ]\n"); | |
| 81 | + | while (!a_button()) { msleep(50); } | |
| 82 | + | msleep(400); | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | void section_header(const char* title) { | |
| 86 | + | printf("\n=========================\n"); | |
| 87 | + | printf(" %s\n", title); | |
| 88 | + | printf("=========================\n"); | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | void 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 | + | ||
| 102 | + | void 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 | + | ||
| 126 | + | void 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 | + | ||
| 173 | + | void 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 | + | ||
| 198 | + | void 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 | + | ||
| 228 | + | int 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 | + | } | |
self_test_standard.c(file created)
| @@ -0,0 +1,211 @@ | |||
| 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 | + | ||
| 67 | + | void wait_for_a() { | |
| 68 | + | printf(" [ Press A button to continue ]\n"); | |
| 69 | + | while (!a_button()) { msleep(50); } | |
| 70 | + | msleep(400); | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | void 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 | + | ||
| 83 | + | void 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 | + | ||
| 107 | + | void 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 | + | ||
| 132 | + | void 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 | + | ||
| 162 | + | int 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 | + | } | |
student_coding_guide.md(file created)
| @@ -0,0 +1,186 @@ | |||
| 1 | + | # Coding Guide for Botball | |
| 2 | + | ||
| 3 | + | ## Start Every File the Same Way | |
| 4 | + | ||
| 5 | + | 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. | |
| 6 | + | ||
| 7 | + | ```c | |
| 8 | + | #include <kipr/wombat.h> | |
| 9 | + | ||
| 10 | + | // --- PORT ASSIGNMENTS --- | |
| 11 | + | #define LEFT_MOTOR 0 | |
| 12 | + | #define RIGHT_MOTOR 3 | |
| 13 | + | #define ARM_SERVO 3 | |
| 14 | + | #define CLAW_SERVO 2 | |
| 15 | + | #define LINE_SENSOR 0 | |
| 16 | + | #define BUMP_SENSOR 1 | |
| 17 | + | ||
| 18 | + | // --- THRESHOLDS --- | |
| 19 | + | #define BLACK_VALUE 3000 | |
| 20 | + | #define LINE_THRESHOLD 2000 | |
| 21 | + | ``` | |
| 22 | + | ||
| 23 | + | Then your code reads like plain English: `motor(LEFT_MOTOR, 100)` is much clearer than `motor(0, 100)`. | |
| 24 | + | ||
| 25 | + | --- | |
| 26 | + | ||
| 27 | + | ## Break Your Route into Functions | |
| 28 | + | ||
| 29 | + | 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. | |
| 30 | + | ||
| 31 | + | ```c | |
| 32 | + | void drive_to_wall() { ... } | |
| 33 | + | void grab_object() { ... } | |
| 34 | + | void return_to_start() { ... } | |
| 35 | + | ||
| 36 | + | int main() { | |
| 37 | + | wait_for_light(0); | |
| 38 | + | shut_down_in(119); | |
| 39 | + | ||
| 40 | + | drive_to_wall(); | |
| 41 | + | grab_object(); | |
| 42 | + | return_to_start(); | |
| 43 | + | ||
| 44 | + | ao(); | |
| 45 | + | return 0; | |
| 46 | + | } | |
| 47 | + | ``` | |
| 48 | + | ||
| 49 | + | Benefits: easier to test one section at a time, easier to fix bugs, easier to read. | |
| 50 | + | ||
| 51 | + | --- | |
| 52 | + | ||
| 53 | + | ## Always Stop Your Motors | |
| 54 | + | ||
| 55 | + | Every time you start motors, there needs to be a clear place where they stop. Forgetting `ao()` is one of the most common bugs. | |
| 56 | + | ||
| 57 | + | ```c | |
| 58 | + | // Good: clear start and stop | |
| 59 | + | motor(LEFT_MOTOR, 80); | |
| 60 | + | motor(RIGHT_MOTOR, 80); | |
| 61 | + | msleep(1000); | |
| 62 | + | ao(); // <-- don't forget this | |
| 63 | + | msleep(200); // brief pause after stopping | |
| 64 | + | ``` | |
| 65 | + | ||
| 66 | + | --- | |
| 67 | + | ||
| 68 | + | ## Two Ways to Control Distance | |
| 69 | + | ||
| 70 | + | **Time-based:** Simple, but affected by battery level and surface friction. Good for rough positioning. | |
| 71 | + | ```c | |
| 72 | + | motor(LEFT_MOTOR, 80); | |
| 73 | + | motor(RIGHT_MOTOR, 80); | |
| 74 | + | msleep(1500); // go for 1.5 seconds | |
| 75 | + | ao(); | |
| 76 | + | ``` | |
| 77 | + | ||
| 78 | + | **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. | |
| 79 | + | ||
| 80 | + | ```c | |
| 81 | + | cmpc(LEFT_MOTOR); // reset counter to 0 | |
| 82 | + | while (gmpc(LEFT_MOTOR) < 5000) { | |
| 83 | + | motor(LEFT_MOTOR, 80); | |
| 84 | + | motor(RIGHT_MOTOR, 80); | |
| 85 | + | msleep(10); // always include a short sleep in loops like this — | |
| 86 | + | // without it the loop runs so fast it can crash the Wombat | |
| 87 | + | } | |
| 88 | + | ao(); | |
| 89 | + | ``` | |
| 90 | + | ||
| 91 | + | When precision matters (like lining up to grab something), use encoders. When you just need to get somewhere approximately, time-based is fine. | |
| 92 | + | ||
| 93 | + | --- | |
| 94 | + | ||
| 95 | + | ## Sensor-Based Stopping | |
| 96 | + | ||
| 97 | + | Using a sensor to stop is more reliable than guessing how long something takes. | |
| 98 | + | ||
| 99 | + | ```c | |
| 100 | + | // Stop when you hit a wall | |
| 101 | + | while (digital(BUMP_SENSOR) == 0) { | |
| 102 | + | motor(LEFT_MOTOR, 60); | |
| 103 | + | motor(RIGHT_MOTOR, 60); | |
| 104 | + | msleep(10); | |
| 105 | + | } | |
| 106 | + | ao(); | |
| 107 | + | ||
| 108 | + | // Stop when you reach a black line | |
| 109 | + | while (analog(LINE_SENSOR) < BLACK_VALUE) { | |
| 110 | + | motor(LEFT_MOTOR, 60); | |
| 111 | + | motor(RIGHT_MOTOR, 60); | |
| 112 | + | msleep(10); | |
| 113 | + | } | |
| 114 | + | ao(); | |
| 115 | + | ``` | |
| 116 | + | ||
| 117 | + | If a sensor isn't working, a time-based fallback is okay for competition, but plan to fix it. | |
| 118 | + | ||
| 119 | + | --- | |
| 120 | + | ||
| 121 | + | ## Debug with printf | |
| 122 | + | ||
| 123 | + | `printf` is your best debugging tool. Print sensor values while the robot is running to see what it's actually experiencing. | |
| 124 | + | ||
| 125 | + | ```c | |
| 126 | + | // Print once each loop iteration - see what the sensor reads | |
| 127 | + | while (1) { | |
| 128 | + | printf("line sensor: %d\n", analog(LINE_SENSOR)); | |
| 129 | + | msleep(100); | |
| 130 | + | } | |
| 131 | + | ``` | |
| 132 | + | ||
| 133 | + | 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. | |
| 134 | + | ||
| 135 | + | Remove or comment out excessive `printf` calls before competition — they slow the program down. | |
| 136 | + | ||
| 137 | + | --- | |
| 138 | + | ||
| 139 | + | ## Servos: Enable, Move, and Be Patient | |
| 140 | + | ||
| 141 | + | Always call `enable_servos()` before using servos, and `disable_servos()` when done. After setting a position, give the servo time to physically reach it. | |
| 142 | + | ||
| 143 | + | ```c | |
| 144 | + | enable_servos(); | |
| 145 | + | ||
| 146 | + | set_servo_position(ARM_SERVO, 1000); // move arm | |
| 147 | + | msleep(1000); // wait for it to get there | |
| 148 | + | ||
| 149 | + | set_servo_position(CLAW_SERVO, 2047); // close claw | |
| 150 | + | msleep(500); | |
| 151 | + | ||
| 152 | + | disable_servos(); | |
| 153 | + | ``` | |
| 154 | + | ||
| 155 | + | 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. | |
| 156 | + | ||
| 157 | + | --- | |
| 158 | + | ||
| 159 | + | ## Mecanum Wheels: Motor Direction Reference | |
| 160 | + | ||
| 161 | + | | Movement | Motor 0 (LF) | Motor 1 (RF) | Motor 2 (LB) | Motor 3 (RB) | | |
| 162 | + | |---------------|:---:|:---:|:---:|:---:| | |
| 163 | + | | Forward | + | + | + | + | | |
| 164 | + | | Backward | - | - | - | - | | |
| 165 | + | | Strafe Right | + | - | - | + | | |
| 166 | + | | Strafe Left | - | + | + | - | | |
| 167 | + | | Rotate Right | + | - | + | - | | |
| 168 | + | | Rotate Left | - | + | - | + | | |
| 169 | + | ||
| 170 | + | 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. | |
| 171 | + | ||
| 172 | + | --- | |
| 173 | + | ||
| 174 | + | ## Common Mistakes | |
| 175 | + | ||
| 176 | + | **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. | |
| 177 | + | ||
| 178 | + | **Hardcoding port numbers.** Use `#define` constants. `motor(0, 100)` is hard to read and hard to fix when something changes. | |
| 179 | + | ||
| 180 | + | **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. | |
| 181 | + | ||
| 182 | + | **Trusting timing over sensors.** If the robot does the same task multiple times and the timing varies, use a sensor instead. | |
| 183 | + | ||
| 184 | + | **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. | |
| 185 | + | ||
| 186 | + | **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(file created)
| @@ -0,0 +1,151 @@ | |||
| 1 | + | # Troubleshooting Guide | |
| 2 | + | ||
| 3 | + | When something isn't working, don't just start changing code. Observe first, then figure out what's actually wrong. | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## Step 1: Run the Self-Test | |
| 8 | + | ||
| 9 | + | 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. | |
| 10 | + | ||
| 11 | + | If a motor or servo fails the self-test: | |
| 12 | + | - Check the cable connection at both ends | |
| 13 | + | - Try swapping the cable | |
| 14 | + | - Check the port number in the test file matches where it's plugged in | |
| 15 | + | ||
| 16 | + | --- | |
| 17 | + | ||
| 18 | + | ## Fix Hardware Before Code | |
| 19 | + | ||
| 20 | + | 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. | |
| 21 | + | ||
| 22 | + | --- | |
| 23 | + | ||
| 24 | + | ## My Robot Won't Move At All | |
| 25 | + | ||
| 26 | + | Work through this in order: | |
| 27 | + | ||
| 28 | + | 1. **Are the motors getting power?** Check that the Wombat is on and the battery has charge. | |
| 29 | + | 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. | |
| 30 | + | 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. | |
| 31 | + | 4. **Are you using the right port number?** Compare your `#define` constants to the physical port labels on the Wombat. | |
| 32 | + | ||
| 33 | + | --- | |
| 34 | + | ||
| 35 | + | ## Robot Curves Instead of Driving Straight | |
| 36 | + | ||
| 37 | + | If your robot drives forward but slowly curves left or right, this is very common and almost never a wiring problem. | |
| 38 | + | ||
| 39 | + | Possible causes: one motor is slightly stronger than the other (very common), uneven wheel friction, low battery, or uneven weight distribution. | |
| 40 | + | ||
| 41 | + | Fix it by adjusting motor speeds slightly until the robot tracks straight: | |
| 42 | + | ||
| 43 | + | ```c | |
| 44 | + | motor(LEFT_MOTOR, 80); | |
| 45 | + | motor(RIGHT_MOTOR, 75); // reduce the faster side in small steps (3-10 points) | |
| 46 | + | ``` | |
| 47 | + | ||
| 48 | + | Test, watch, adjust, repeat. Once it drives straight, write down the values. | |
| 49 | + | ||
| 50 | + | ## Robot Spins in Place Instead of Moving Forward | |
| 51 | + | ||
| 52 | + | If the robot spins rather than driving forward, one motor is running backwards. This is a direction problem, not a speed problem. | |
| 53 | + | ||
| 54 | + | **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. | |
| 55 | + | ||
| 56 | + | If wiring can't be changed, you can negate the speed in code: | |
| 57 | + | ```c | |
| 58 | + | // If LEFT_MOTOR spins wrong, negate its value: | |
| 59 | + | motor(LEFT_MOTOR, -80); // was 80 | |
| 60 | + | motor(RIGHT_MOTOR, 80); | |
| 61 | + | ``` | |
| 62 | + | ||
| 63 | + | ## Mecanum Bot Spins Instead of Strafing | |
| 64 | + | ||
| 65 | + | 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. | |
| 66 | + | ||
| 67 | + | --- | |
| 68 | + | ||
| 69 | + | ## Robot Drifts to One Side | |
| 70 | + | ||
| 71 | + | This is almost always a hardware issue, not a code issue. Check: | |
| 72 | + | ||
| 73 | + | - **Are both drive wheels touching the ground equally?** An uneven chassis will cause drift. | |
| 74 | + | - **Are the wheels on tight?** A loose wheel slips. | |
| 75 | + | - **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. | |
| 76 | + | ||
| 77 | + | --- | |
| 78 | + | ||
| 79 | + | ## Debug with printf — Where Does the Output Go? | |
| 80 | + | ||
| 81 | + | 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. | |
| 82 | + | ||
| 83 | + | --- | |
| 84 | + | ||
| 85 | + | ## Sensor Isn't Reading What I Expect | |
| 86 | + | ||
| 87 | + | **First: print the actual value.** Don't guess. Put this in a loop and watch the output: | |
| 88 | + | ```c | |
| 89 | + | while(1) { | |
| 90 | + | printf("sensor value: %d\n", analog(0)); | |
| 91 | + | msleep(100); | |
| 92 | + | } | |
| 93 | + | ``` | |
| 94 | + | ||
| 95 | + | Move the robot/sensor to different positions and see what values you get. Set your threshold based on actual readings, not guesses. | |
| 96 | + | ||
| 97 | + | **Digital sensor always reads 0 or always reads 1:** | |
| 98 | + | - Check the cable connection | |
| 99 | + | - Make sure you're reading the right port number | |
| 100 | + | - Check that the sensor isn't physically stuck | |
| 101 | + | ||
| 102 | + | **Analog sensor reads 0:** | |
| 103 | + | - Usually a disconnected or broken cable | |
| 104 | + | - Try a different port and update the `#define` | |
| 105 | + | ||
| 106 | + | **Line sensor doesn't detect black:** | |
| 107 | + | - The sensor may be too far from the surface. It should be close but not dragging. | |
| 108 | + | - Check what value it reads on white vs. black. Your `#define BLACK_VALUE` threshold should be somewhere between the two. | |
| 109 | + | - Lighting in the competition venue may be different from where you practiced. Re-check thresholds on the day of competition if possible. | |
| 110 | + | ||
| 111 | + | --- | |
| 112 | + | ||
| 113 | + | ## Robot Stops Too Early or Too Late | |
| 114 | + | ||
| 115 | + | **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. | |
| 116 | + | ||
| 117 | + | **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. | |
| 118 | + | ||
| 119 | + | **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. | |
| 120 | + | ||
| 121 | + | --- | |
| 122 | + | ||
| 123 | + | ## Arm / Claw Isn't Moving | |
| 124 | + | ||
| 125 | + | 1. Did you call `enable_servos()` before `set_servo_position()`? | |
| 126 | + | 2. Is the port number correct? | |
| 127 | + | 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. | |
| 128 | + | 4. Run the self-test — does the servo work at all? If not, check the cable. | |
| 129 | + | 5. Is the servo stalled against something mechanical? A servo fighting against a physical obstruction will hum, get hot, and eventually stop working. | |
| 130 | + | ||
| 131 | + | --- | |
| 132 | + | ||
| 133 | + | ## Code Compiles But Crashes or Freezes | |
| 134 | + | ||
| 135 | + | **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? | |
| 136 | + | ||
| 137 | + | **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. | |
| 138 | + | ||
| 139 | + | **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. | |
| 140 | + | ||
| 141 | + | --- | |
| 142 | + | ||
| 143 | + | ## Competition Day Problems | |
| 144 | + | ||
| 145 | + | **The route worked in practice but fails at competition:** | |
| 146 | + | - Different lighting → re-check sensor thresholds | |
| 147 | + | - Different floor surface → re-check line sensor sensitivity and timing | |
| 148 | + | - Battery not fully charged → movements will be slower than expected | |
| 149 | + | ||
| 150 | + | **Robot starts before the light:** | |
| 151 | + | - `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. | |