Welcome to ESP32 WiFi Camera Lite repository
Robot car
MSPM0G3507/MSPM0G3519 Competition Robot Car Tutorial — Steel Ball Control + Four-Channel Line Tracking
MSPM0G3507/MSPM0G3519 Competition Robot Car Tutorial — Steel Ball Control + Four-Channel Line Tracking1. Hardware Platform2. Software Environment3. Structural Overview3.1 Component List and Materials3.2 Physical Appearance3.3 Assembly Instructions4. Working Principle4.1 Wiring Pinout4.2 Wiring Diagram4.3 Control Principle5. Project File Structure6. Key Parameter Configuration6.1 Servo Ball Control PID (vision_servo_test.c / esp_ball_control.c)6.2 Line-Tracking PID (Four_linewalking_debug.c)6.3 Per-Task Line-Tracking Speeds (questions.c)6.4 Task Timeout Settings (questions.c)7. Task Descriptions7.1 State Machine Structure (questions.h)7.2 Task Details8. Key Code Explanation8.1 ESP32 Ball Control — ESP_Ball_Control_Update() (esp_ball_control.c)8.2 Line Tracking — Debug_LineWalking_Run() (Four_linewalking_debug.c)8.3 Combined Tracking + Ball Control — Debug_LineWalking_WithBall() (Four_linewalking_debug.c)8.4 Task 2 Steel Ball Reciprocation (questions.c)9. Operating Procedure
1. Hardware Platform
| Component | Model / Description |
|---|---|
| Main controller | MSPM0G3507/MSPM0G3519 development board |
| Camera | ESP32 vision recognition module (steel ball coordinate detection) |
| Line tracking | Four-channel IR tracking module (digital output) |
| Display | OLED 128×32 (I2C) |
| Servo | 9G servo (steel ball tilt control) |
| Motor | Two-wheel L-type 520 encoder motors (I2C driver board) |
| Button | Expansion board K1 single-button control |
2. Software Environment
IDE: Keil MDK V5 (ARM Compiler V6.7)
SDK: TI MSPM0 SDK 2.02.00.05
3. Structural Overview
3.1 Component List and Materials
Chassis: Black metal-etched base plate, drive motors, wheels. The chassis features pre-drilled screw holes and cable routing holes.
Mechanical arm structure: Blue/black metal servo brackets, metal link metal L-shaped / straight connectors, servo, metal flange plate.
Horizontal scale sliding platform: Transparent acrylic enclosure, hand-written digit scale and zero reference line, internal drive rail / lead screw.
Top vision bracket: Brushed metal strip (aluminum alloy / stainless steel), transparent acrylic L-shaped bracket, ESP32 vision module.
Hardware accessories: M2/M3 combination screws, nuts, washers, black cable ties, heat-shrink tubing.
Note: In addition to the accessories included in the v2 chassis kit, the 9G servo, ESP32 vision module, and tracking module can be purchased from our store. Other connecting rods, scales, water tubes, and hardware accessories must be sourced separately.
3.2 Physical Appearance






3.3 Assembly Instructions
Step 1: Install the Mobile Chassis and Motors
Mount the two drive motors (with wheels) on opposite sides of the black metal base plate. Ensure the wheels are oriented consistently and make full contact with the ground.
Install the MSPM0G3507/MSPM0G3519 main control board using screws in the designated position on the chassis. Be careful to avoid the wheel and motor rotation clearance.
Step 2: Build the Horizontal Scale and Sliding Platform
Refer to Figure 5: Assemble the transparent acrylic enclosure and insert the paperboard with the printed scale into it. This structure is mounted on the front of the chassis.
Ensure the rail or lead screw inside the sliding platform remains level, as this serves as the critical reference datum for subsequent mechanical arm coordinate calibration.
Step 3: Assemble the Mechanical Arm Joints
Refer to Figures 3 and 7: Press the green servo into the black metal bracket and secure it with the provided screws.
Install the servo arm and connect the blue/black metal linkages.
Refer to Figure 7 for the end effector assembly (connector / gripper). In the photos, this component uses a significant amount of transparent acrylic and hot-melt glue / resin for reinforcement. For actual assembly, 3D-printed or metal parts are recommended to ensure sufficient structural strength.
Mount the servo base to the rear of the chassis (refer to the layout in Figure 7).
Step 4: Install the Vertical Metal Bracket
Refer to Figures 1, 4, and 6: Take the brushed metal strip and mount it vertically in the center or front-side mounting slot of the chassis.
When securing with screws, ensure the metal strip is perpendicular to the chassis plane and does not wobble. In Figure 6, the nut is passed through and tightened from the back.
Step 5: Install the Top Vision Sensor
Refer to Figure 35: Position the ESP32 module with its back facing upward. Use a bracket and M3 screws to mount the module at the top of the vertical metal bracket.
Adjust the sensor's orientation to ensure it can see the track and scale in front of the robot.
4. Working Principle
4.1 Wiring Pinout
Motor Wiring:
| Motor | Driver Board |
|---|---|
| M2 | M- |
| V | 3V3 |
| A | H1B |
| B | H1A |
| G | GND |
| M1 | M+ |
Four-Channel Motor Driver Board:
| Driver Board | MSPM0G3507/MSPM0G3519 |
|---|---|
| 5V | 5V |
| SCL | PA15 |
| SDA | PA16 |
| GND | GND |
Four-Channel Tracking Module:
| Tracking Module | MSPM0G3507/MSPM0G3519 |
|---|---|
| 5V | 5V |
| X1 | PA24 |
| X2 | PA25 |
| X3 | PA26 |
| X4 | PA27 |
| GND | GND |
OLED:
| OLED | MSPM0G3507/MSPM0G3519 |
|---|---|
| VCC | 5V |
| GND | GND |
| SDA | PA28 |
| SCL | PA31 |
ESP32 Camera:
| ESP32 Vision Module | MSPM0G3507/MSPM0G3519 |
|---|---|
| 5V | 5V |
| GND | GND |
| TXD | PA13 (UART3_RX) |
| RXD | PA14 (UART3_TX) |
| Baud rate | 115200 |
4.2 Wiring Diagram

4.3 Control Principle
Robot Car Line-Tracking Control Flow:
Four-channel IR sensors read the black line position -> compute deviation
PID controller computes turning correction -> controls differential steering
ESP32 camera detects steel ball position -> sends coordinates via UART3 -> servo PID controls swing arm angle
Timer expiry or waypoint detection -> stop / state transition
PID Control Parameter Descriptions:
| PID Parameter | Function | Adjustment Direction |
|---|---|---|
| P (Proportional) | Determines response speed; large output for large errors | Too large: oscillation / serpentine motion; Too small: weak turns |
| I (Integral) | Eliminates steady-state error; corrects accumulated error | Too large: overshoot; Too small: drifts to one side and does not return |
| D (Derivative) | Suppresses oscillation; decelerates in advance | Too large: jittering; Too small: cannot brake |
5. Project File Structure
Steel_ball_control_4walking/├── main.c # Main program: peripheral initialization, main loop, state machine dispatch├── APP/│ ├── app_k230.c/h # Legacy K230 serial protocol and ball control code (not used in current main flow)│ ├── app_pid.c/h # PID controller (positional + incremental)│ ├── app_kalman.c/h # One-dimensional Kalman filter│ └── app_servo.c/h # Servo PWM driver├── BSP/│ ├── Four_Tracking/│ │ ├── Four_linewalking_debug.c/h # Four-channel line tracking + line-tracking PID + combined task (tracking + ball control)│ │ └── Four_linewalking.c/h# Four-channel tracking module interface definitions│ ├── Motor/│ │ ├── app_motor.c/h # Car motion control (Motion_Car_Control)│ │ └── bsp_motor_iic.c/h# Motor driver board I2C protocol│ ├── OLED/│ │ ├── oled.c/h # OLED driver + display function OLED_time()│ │ └── oledfont.h # Font data│ ├── Key/│ │ └── key.c/h # Button driver + mode selection state machine│ ├── Questions/│ │ └── questions.c/h # Competition task state machine (Task 1~5)│ ├── Task/│ │ └── task.c/h # Timer-based task scheduler (Scheduler_Run)│ ├── Timer/│ │ └── timer.c/h # 1 ms system clock Get_Time()│ └── usart.c/h # Serial initialization (UART0 debug, UART1 K230)│ ├── bsp_camera_usart.c/h # ESP32 UART3 receive, checksum and coordinate parsing (current vision pipeline)│ └── esp_ball_control.c/h # ESP32 coordinate-to-servo control wrapper└── ti_msp_dl_config.c/h # TI driver library configuration (SysConfig generated)
6. Key Parameter Configuration
6.1 Servo Ball Control PID (vision_servo_test.c / esp_ball_control.c)
x/* vision_servo_test.h: Parameters displayed in debug output. */
/* ESP32 image width (px) *//* X coordinate when the ball is centered on the arm (px) *//* cm/px for right side *//* cm/px for left side */
/* Servo CC center value (1500 us) *//* Servo CC minimum *//* Servo CC maximum *//* PID output to CC scaling factor */
/* Target-reached threshold *//* Per-frame CC change rate limit *//* Vision frame interval timeout threshold *//* ESP32 coordinate filter: process noise (increase for faster response) *//* ESP32 coordinate filter: measurement noise (increase for smoother output) */Note: PID_KP/KI/KD are currently only used for startup debug information; the actual runtime parameters in pid_calc() are defined within the function body with base values of kp=0.058, ki=0.008, kd=0.720. When approaching the target, kp is dynamically reduced. Tuning should modify the runtime parameters inside pid_calc(), not just the print macros in the header file.
Target position conversion uses independent calibration for left and right sides:
xxxxxxxxxxfloat cm_px = (target_cm >= 0.0f) ? INPUT_CM_PX_POS : INPUT_CM_PX_NEG;float target_x = INPUT_CENTER_X + target_cm / cm_px;float err_px = target_x - x_flt;PID Tuning Guide:
| Symptom | Adjust | Direction | Description |
|---|---|---|---|
| Ball oscillates near center | Increase kd or tighten D limit | Increase damping | Also check RATE_LIMIT |
| Ball stops before reaching target | Increase kp or add standstill boost | Increase thrust | First confirm coordinate calibration is correct |
| Ball overshoots and bounces far | Decrease kp or increase kd | Reduce thrust, increase damping | |
| Ball settles off-center and does not return | Increase ki | Strengthen integral | Watch out for integral saturation |
| Servo output changes too fast | Decrease RATE_LIMIT | Limit per-frame CC change |
6.2 Line-Tracking PID (Four_linewalking_debug.c)
xxxxxxxxxxLine-Tracking Tuning Guide:
| Symptom | Adjust | Direction |
|---|---|---|
| Insufficient turning / line straddling | Increase KP | Increase |
| Serpentine motion | Increase KD or decrease KP | Damping / reduce thrust |
| Constant drift to one side | Increase KI | Strengthen integral |
Inertia Compensation (used in the combined tracking + ball control task):
xxxxxxxxxx// Forward inertia compensation (cm); larger values push the ball forward// Turning inertia compensation (cm)// Start-up compensation (cm); pushes the ball forward when the car accelerates and the ball lags behind// Stopping compensation (cm); pulls the ball back when the car stops and the ball overshoots// Start-up ramp time (ms); larger values mean gentler acceleration6.3 Per-Task Line-Tracking Speeds (questions.c)
xxxxxxxxxx// Task 1 pure line-following speed// Task 3 A->B + ball control speed// Task 4 one lap + ball control speed// Task 5 specified position speed// Start-up ramp time6.4 Task Timeout Settings (questions.c)
xxxxxxxxxx// Task 1 pure line-following 20s timeout// Task 2 steel ball reciprocation 5s timeout// Task 3 A->B + ball control 8s timeout// Task 4 one lap + ball control 30s timeout// Task 5 specified position 30s timeout7. Task Descriptions
7.1 State Machine Structure (questions.h)
Main States (Main_State):
| Macro | Value | Task Description |
|---|---|---|
| STOP_STATE | 0 | Stop |
| TASK_LINE_LAP | 1 | Task 1: Pure line-following one lap back to A, ≤ 20 s |
| TASK_BALL_CTRL | 2 | Task 2: Steel ball O → +5 → 0 → -5 (stop), < 5 s |
| TASK_AB_STAB | 3 | Task 3: A → B + ball stabilized at O, ≤ 8 s |
| TASK_LAP_STAB | 4 | Task 4: One lap back to A + ball stabilized at O, ≤ 30 s |
| TASK_LAP_POS | 5 | Task 5: One lap back to A + ball at specified position, ≤ 30 s |
Sub-States (Sub_State):
| Macro | Value | Description |
|---|---|---|
| SUB_IDLE | 0 | Idle |
| SUB_INIT | 1 | Initialization (set speed, start timer) |
| SUB_RUNNING | 2 | Running |
| SUB_DONE | 3 | Completed (car stops but ball control continues; also entered on timeout) |
7.2 Task Details
Task 1 (Pure Line-Following):
Line-following only; no ball control
Speed: TASK1_SPEED = 316 mm/s
Passes through 4 waypoints (A/B/C/D) and stops at A
Auto-stop after 20 s timeout
Task 2 (Steel Ball Control):
Car is stationary; only servo control
Sequence: O → +5 cm (settle) → 0 cm (settle) → -5 cm (settle)
Each phase waits for the ball to approach the target (≤ 0.5 cm) for 30 consecutive frames before transitioning
5 s timeout
After completion, the ball is held at the -5 cm position
Task 3 (A → B + Ball Control):
Line-following and servo ball control simultaneously
Speed: TASK3_SPEED = 130 mm/s
Departs from A, passes through 1 waypoint, arrives at B
Ball is stabilized at center O during travel
8 s timeout; car stops but ball control continues
Task 4 (One Lap + Ball Control):
Line-following and servo ball control simultaneously
Speed: TASK4_SPEED = 222 mm/s
Clockwise one lap, passing through 4 waypoints, returning to A
Ball is stabilized at center O during travel
30 s timeout; car stops but ball control continues
Task 5 (Specified Position + Ball Control):
Same as Task 4, but the ball target position is configurable (Target_Position)
Speed: TASK5_SPEED = 222 mm/s
30 s timeout; car stops but ball control continues
8. Key Code Explanation
8.1 ESP32 Ball Control — ESP_Ball_Control_Update() (esp_ball_control.c)
The ESP32 vision pipeline consists of three steps:
Camera_Vision_Process()parses the 8-byte frameAA 55 FLAGS SEQ X_L X_H WIDTH CHECKSUMfrom the UART3 ring buffer and updates the globalvisionstructure.ESP_Ball_Control_Update()is simply a wrapper aroundVision_Servo_Test_Update(). The actual filtering, error calculation, and servo control are performed invision_servo_test.c.The controller only processes new valid frames; when no new frame is available, it returns
VISION_SERVO_NO_FRAMEand does not drive the servo again.
The calling sequence in the main loop is:
xxxxxxxxxx/* main.c */Camera_Vision_Process();ESP_Ball_Control_Run();The wrapper function implementation is as follows:
xxxxxxxxxx/* esp_ball_control.c */VisionServoResult ESP_Ball_Control_Update(float target_cm, uint8_t hold){ /* hold is currently reserved as an interface parameter and is not used by Vision_Servo_Test_Update. */ return Vision_Servo_Test_Update(target_cm, hold);}The core control flow is shown below. The calibration values in the code must be based on actual device calibration results:
xxxxxxxxxx/* vision_servo_test.c */VisionServoResult Vision_Servo_Test_Update(float target_cm, uint8_t hold){ uint32_t frame = vision.valid_n; if (frame == s_frame) { return VISION_SERVO_NO_FRAME; }
s_frame = frame;
/* Clear the integral and reset the filter on initialization, excessive vision interval, or target change. */ uint8_t reset = 0U; if (!s_inited) { s_kf.first = 1U; s_pid.integral = 0.0f; s_pid.target_cm = target_cm; s_inited = 1U; reset = 1U; } else if ((s_valid_ms != 0U) && ((vision.valid_ms - s_valid_ms) > HISTORY_TIMEOUT_MS)) { s_kf.first = 1U; s_pid.integral = 0.0f; reset = 1U; } else if (fabsf(target_cm - s_pid.target_cm) > 0.1f) { s_pid.integral = 0.0f; s_pid.target_cm = target_cm; reset = 1U; } s_valid_ms = vision.valid_ms;
/* Apply one-dimensional Kalman filtering to the ESP32 X coordinate output. */ float x_flt = kalman_filter(&s_kf, (float)vision.x);
/* Convert the steel ball position and target error using independent left/right calibration coefficients. */ float cm_px = (x_flt >= INPUT_CENTER_X) ? INPUT_CM_PX_POS : INPUT_CM_PX_NEG; float pos_cm = (x_flt - INPUT_CENTER_X) * cm_px; float err_px = vision_to_error_px(x_flt, target_cm); uint8_t reached = (fabsf(err_px) < REACH_ERR_PX);
/* pid_calc returns the incremental servo CC value, which is then clamped and rate-limited. */ float cc_req = PWM_CC_CENTER + pid_calc(&s_pid, err_px, pos_cm, reset); set_servo(cc_req);
return reached ? VISION_SERVO_REACHED : VISION_SERVO_MOVING;}The pid_calc() computation includes: reducing Kp for small errors, accumulating and clamping the integral within a moderate error band, limiting the derivative term based on error magnitude, and adding a small boost when the ball has been stationary for an extended period. The final output is:
xxxxxxxxxxoutput = (p + i + d) * PWM_CC_SCALE + boost;The base parameters are kp=0.058, ki=0.008, kd=0.720. The servo output center is CC=60, range is 50~70, and the per-frame rate limit is 4. The code also computes a velocity_brake debug variable but does not add it to the final output, so the tutorial should not describe it as an active velocity brake.
8.2 Line Tracking — Debug_LineWalking_Run() (Four_linewalking_debug.c)
xxxxxxxxxx/* Standalone four-channel line-following function. */void Debug_LineWalking_Run(void){ extern volatile uint32_t g_task_start_ms; uint32_t now = Get_Time();
if ((s_debug_inited == 0U) || (g_task_start_ms != s_debug_last_task_start_ms)) { s_debug_last_task_start_ms = g_task_start_ms; Debug_Line_Reset(); s_debug_inited = 1U; }
if ((now - s_debug_last_run_ms) < DEBUG_LINE_PERIOD_MS) { return; } s_debug_last_run_ms = now;
float raw_error = Debug_Line_GetError(); float line_error = raw_error; float turn_cmd = 0.0f;
if (s_debug_line_lost != 0U) { if (s_debug_lost_start_ms == 0U) { s_debug_lost_start_ms = now; } uint32_t lost_ms = now - s_debug_lost_start_ms;
if (lost_ms >= DEBUG_LINE_LOST_STOP_MS) { control_speed(0, 0, 0, 0); return; }
if (lost_ms >= DEBUG_LINE_LOST_DEBOUNCE_MS) { float lost_turn = (lost_ms >= DEBUG_LINE_LOST_ESCALATE_MS) ? DEBUG_LINE_LOST_TURN_HARD : DEBUG_LINE_LOST_TURN_SOFT; float lost_dir = 0.0f; if (s_debug_last_direction > 0.0f) { lost_dir = 1.0f; } else if (s_debug_last_direction < 0.0f) { lost_dir = -1.0f; } else if (s_debug_last_turn_dir > 0.0f) { lost_dir = 1.0f; } else if (s_debug_last_turn_dir < 0.0f) { lost_dir = -1.0f; } else { lost_dir = 1.0f; } turn_cmd = lost_dir * lost_turn; s_debug_err_last = raw_error; } else { turn_cmd = Debug_Line_PID(raw_error); } } else { s_debug_lost_start_ms = 0U;
line_error = Kalman_Filter(&s_debug_line_kalman, raw_error);
turn_cmd = Debug_Line_PID(line_error);
if (turn_cmd > 1.0f) { s_debug_last_turn_dir = 1.0f; } else if (turn_cmd < -1.0f){ s_debug_last_turn_dir = -1.0f; } }
/* Convert the current debug turning quantity to the 5000 scale for ball inertia compensation. */ pid_output_IRR = (int)(turn_cmd * (BALL_INERTIA_PID_MAX / DEBUG_LINE_OUT_MAX));
float base_speed = (float)g_line_speed;
if (base_speed <= 0.0f) { base_speed = DEBUG_LINE_BASE_SPEED; }
float right_cmd = base_speed + turn_cmd; float left_cmd = base_speed - turn_cmd;
right_cmd = Debug_Clamp(right_cmd, DEBUG_LINE_MIN_MOTOR_CMD, DEBUG_LINE_MAX_MOTOR_CMD); left_cmd = Debug_Clamp(left_cmd, DEBUG_LINE_MIN_MOTOR_CMD, DEBUG_LINE_MAX_MOTOR_CMD);
control_speed(0, (int16_t)right_cmd, 0, (int16_t)left_cmd);
if ((now - s_debug_last_print_ms) >= DEBUG_LINE_PRINT_PERIOD_MS) { uint32_t ps = DL_GPIO_readPins(Sensor_four_PORT, Sensor_four_X1_PIN | Sensor_four_X2_PIN | Sensor_four_X3_PIN | Sensor_four_X4_PIN); int s1 = (ps & Sensor_four_X1_PIN) ? 1 : 0; int s2 = (ps & Sensor_four_X2_PIN) ? 1 : 0; int s3 = (ps & Sensor_four_X3_PIN) ? 1 : 0; int s4 = (ps & Sensor_four_X4_PIN) ? 1 : 0;
printf("IR=%d%d%d%d E=%.2f T=%.2f L=%d R=%d LOST=%d\r\n", s1, s2, s3, s4, (double)line_error, (double)turn_cmd, (int)left_cmd, (int)right_cmd, (int)s_debug_line_lost);
s_debug_last_print_ms = now; }}8.3 Combined Tracking + Ball Control — Debug_LineWalking_WithBall() (Four_linewalking_debug.c)
xxxxxxxxxx/* Four-channel line tracking with steel ball inertia compensation. */void Debug_LineWalking_WithBall(float ball_target_cm){ static uint32_t last_task_start_ms = 0xFFFFFFFFU; static uint8_t ramp_ready = 0; static uint8_t stop_ready = 0; static uint32_t stop_start_ms = 0; static int saved_spd = 0; static int stop_spd = 0; extern volatile uint32_t g_task_start_ms; float start_comp = 0.0f; float stop_comp = 0.0f;
/* Record the cruising speed and reset ramp / stop states when the task restarts. */ if (g_task_start_ms != last_task_start_ms) { last_task_start_ms = g_task_start_ms; saved_spd = g_line_speed; ramp_ready = 1; stop_ready = 0; stop_start_ms = 0; stop_spd = 0; }
/* Stop phase: linearly decelerate from the current speed to 0 over BALL_STOP_TIME_MS. */ if (g_slow_stop) { if (!stop_ready) { stop_start_ms = Get_Time(); stop_spd = g_line_speed; stop_ready = 1; }
uint32_t st = Get_Time() - stop_start_ms; if (st >= BALL_STOP_TIME_MS) { g_line_speed = 0; control_pwm(0, 0, 0, 0); ESP_Ball_Control_Update(ball_target_cm, 1U); return; }
float stop_ratio = 1.0f - (float)st / (float)BALL_STOP_TIME_MS; if (stop_ratio < 0.0f) stop_ratio = 0.0f; if (stop_ratio > 1.0f) stop_ratio = 1.0f; stop_comp = BALL_STOP_COMP * stop_ratio; g_line_speed = (int)((float)stop_spd * stop_ratio); } else { stop_ready = 0; stop_start_ms = 0; stop_spd = 0;
/* Start-up phase: linearly accelerate from minimum speed to cruising speed. */ if (ramp_ready) { uint32_t et = Get_Time() - last_task_start_ms; if (et < BALL_START_COMP_TIME_MS) start_comp = BALL_START_COMP * (1.0f - (float)et / (float)BALL_START_COMP_TIME_MS); if (et < BALL_RAMP_TIME_MS) { float ramp = (float)et / (float)BALL_RAMP_TIME_MS; if (ramp < BALL_RAMP_MIN_RATIO) ramp = BALL_RAMP_MIN_RATIO; if (ramp > 1.0f) ramp = 1.0f; g_line_speed = (int)((float)saved_spd * ramp); } else { g_line_speed = saved_spd; } } }
/* Run line tracking first, then process ESP32 data and send the compensated servo target. */ Debug_LineWalking_Run(); Camera_Vision_Process();
float compensated = ball_target_cm; compensated -= BALL_INERTIA_FWD; if (pid_output_IRR > 0) compensated -= BALL_INERTIA_TURN * (float)pid_output_IRR / BALL_INERTIA_PID_MAX; else if (pid_output_IRR < 0) compensated += BALL_INERTIA_TURN * (float)(-pid_output_IRR) / BALL_INERTIA_PID_MAX; compensated += start_comp + stop_comp;
ESP_Ball_Control_Update(compensated, 1U);}
8.4 Task 2 Steel Ball Reciprocation (questions.c)
xxxxxxxxxx// Three phases: +5 cm (1.8 s) -> 0 cm (1.8 s) -> -5 cm (1.8 s) -> DONE (hold at -5)case SUB_RUNNING: { static int phase = 0; static uint32_t t0 = 0; if (t0 == 0) t0 = Get_Time();
float target_cm; if (phase == 0) target_cm = +5.0f; else if (phase == 1) target_cm = 0.0f; else target_cm = -5.0f;
ESP_Ball_Control_Update(target_cm, 1U);
if (Get_Time() - t0 >= 1800) { // 1.8 seconds per phase t0 = Get_Time(); if (phase == 0) phase = 1; else if (phase == 1) phase = 2; else { phase = 0; Sub_State = SUB_DONE; } } break;}case SUB_DONE: ESP_Ball_Control_Update(-5.0f, 1U); // Hold ball at the -5 cm position break;9. Operating Procedure
After wiring, power on. The ESP32 UART3 vision pipeline initializes and waits for 4 seconds.
The mode selection screen appears on the OLED showing "Mode:1".
Long press K1 to cycle through modes (1~5).
Short press K1 to confirm and enter the selected mode.
The robot car starts executing the task; the OLED displays the task name and timer.
Short press K1 during task execution: Pause; Long press K1: Exit back to mode selection.
After task completion or timeout, the car stops but ball control continues (the ball is held at the target position).
Long press K1 to exit back to mode selection.