-
Notifications
You must be signed in to change notification settings - Fork 0
ESP32 ‐ WROOM
This section focuses on receiving and processing commands over the I2C interface. The commands are received as strings, parsed, and converted into structured data for further processing.
Each command follows the structure:
<NUMBER> <NUMBER> <NUMBER>\n
-
<NUMBER>
: An integer (can be negative). -
Spaces
: Separators between numbers. -
\n
: Newline character marking the end of a command.
Where everything is processed inside the main:
The i2c_slave.read_buffer()
function reads data from the I2C bus.
If no data is received (received_data == NULL)
, the loop continues without processing.
Because commands can accumulate in the buffer divideI2CPacket(received_data, &command_count)
splits the received data into individual commands based on the newline character \n
.
It returns an array of command strings (commands) and updates command_count
with the number of valid commands.
Iterates over each command string.
Uses parseServoCommand(command)
to convert the string into a RpiDataPacket
structure containing the command and its parameters.
If parsing fails (numbers == NULL
), it skips to the next command.
After parsing the commands into RpiDataPacket
structures, the system uses a switch
statement to handle different command types based on the command
field of the RpiDataPacket
.
- Purpose: Adjusts the angle of a specified servo.
-
Validation:
- Checks if the servo ID (
param1
) is valid. - Ensures that auto-aim is not enabled, as manual control is only allowed when auto-aim is disabled.
- Checks if the servo ID (
-
Action:
- Calls the
set_angle
method on the specified servo with the desired angle (param2
). - Resets both the X and Y PID controllers to prevent any residual control effects from previous operations.
- Calls the
- Purpose: Updates the minimum pulse width setting for a specified servo.
-
Validation:
- Checks for a valid servo ID.
- Ensures auto-aim is disabled to allow manual configuration.
-
Action:
- Calls
set_min_pulse_width
on the specified servo with the new minimum pulse width value.
- Calls
- Purpose: Updates the maximum pulse width setting for a specified servo.
-
Validation:
- Checks for a valid servo ID.
- Ensures auto-aim is disabled.
-
Action:
- Calls
set_max_pulse_width
on the specified servo with the new maximum pulse width value.
- Calls
- Purpose: Adjusts servo positions based on error values from a tracking system, primarily used when auto-aim is enabled.
-
Validation:
- Ensures that auto-aim is not in manual mode; the command is ignored if it is.
-
Process:
- Creates an
error
coordinate withx
andy
values fromparam1
andparam2
. - If both error values are zero:
- Checks if a "reseter" task is already running.
- If not, creates a "reseter" task to reset the controllers after a delay.
- Skips further processing to avoid unnecessary calculations.
- If the error is non-zero and a "reseter" task exists:
- Deletes the "reseter" task to prevent it from resetting the controllers prematurely.
- Creates an
-
Action:
- Calculates control outputs using the X and Y PID controllers based on the error values.
- Logs the error and control values for debugging purposes.
- Adjusts the servos by adding the control outputs to their current pulse widths using the
add_pulse_width
method.
- Purpose: Updates specific parameters of the PID controllers or resets them.
-
Process:
- Casts
param1
to aControllerParameters
enum to identify which parameter to update. - Converts
param2
to a floating-point value (dividing by 1000.0) for precision.
- Casts
-
Action:
- Depending on the parameter specified (
KP
,KI
,KD
,KS
, orRESET
), it updates the corresponding gains of both X and Y PID controllers or resets them.
- Depending on the parameter specified (
-
Error Handling:
- Logs an error message if an invalid controller parameter is specified.
- Purpose: Changes the state of the auto-aim feature.
-
Validation:
- Checks if the new auto-aim state (
param2
) is within the valid range.
- Checks if the new auto-aim state (
-
Action:
- Updates the
auto_aim_state
variable by castingparam2
to theAutoAimState
enum. - Logs the new auto-aim state for confirmation.
- Updates the
- Purpose: Handles any unrecognized or invalid commands.
-
Action:
- Logs an error message indicating that an invalid command was received.
The PIDController
class implements a PID control loop to calculate the control output based on the error between a desired setpoint and the current process variable. It adjusts the control signal to minimize this error over time, providing smooth and precise control for systems such as servo motors.
The core functionality lies in the calculateControl(double error)
method, which computes the control output using the PID formula:
-
Proportional Term (
up
):- Calculated as
up = kp * error
. - Provides an immediate response proportional to the current error.
- A higher proportional gain (
kp
) increases the responsiveness but can cause overshoot.
- Calculated as
-
Integral Term (
ui
):- Updated as
ui = prev_ui + kp * ki * prev_error
. - Accumulates past errors to eliminate steady-state offset.
- The integral gain (
ki
) determines the contribution of the accumulated error. -
prev_ui
stores the previous integral term for continuous accumulation.
- Updated as
-
Derivative Term (
ud
):- Calculated as
ud = kp * kd * (error - prev_error)
. - Predicts future error based on the rate of change.
- The derivative gain (
kd
) dampens the system response, reducing overshoot and oscillations.
- Calculated as
-
Control Output (
u
):- Computed as
u = ks * (up + ui + ud)
. - The scaling factor (
ks
) adjusts the overall control output magnitude. - Summing the three terms provides a balanced control action considering present, past, and future errors.
- Computed as
After calculating u
, the method updates the internal state:
-
prev_error
is set to the currenterror
for use in the next iteration. -
prev_ui
is updated to the currentui
, preserving the integral term's history.
The class includes methods to update controller parameters at runtime:
-
updateKp(double kp)
: Updates the proportional gain. -
updateKi(double ki)
: Updates the integral gain. -
updateKd(double kd)
: Updates the derivative gain. -
updateKs(double ks)
: Updates the scaling factor.
These methods allow dynamic tuning of the controller to adapt to changing system conditions or to optimize performance.
The reset()
method reinitializes the controller's internal state:
- Resets
prev_ui
andprev_error
to zero. - Useful when the system needs to restart control from a known state, eliminating accumulated errors.
The Servo
class encapsulates the functionality required to interface with servo motors via the ESP32's LEDC (LED Control) PWM hardware. It initializes the necessary timers and channels, and provides methods to set servo angles, adjust pulse widths, and update servo positions accordingly.
When a Servo
object is created, it performs the following steps:
-
LEDC Timer Configuration:
- Sets up a PWM timer with a frequency of 50 Hz, which is standard for servo control.
- Uses a 13-bit duty resolution, providing fine control over the pulse width.
- Selects the appropriate LEDC mode (high-speed or low-speed) based on the hardware capabilities.
-
LEDC Channel Configuration:
- Assigns the specified GPIO pin to the LEDC channel.
- Initializes the channel with zero duty cycle initially.
- Associates the channel with the configured timer.
-
Default Pulse Width Range:
- Sets default minimum and maximum pulse widths (typically 1000 µs to 2000 µs).
- These values correspond to the servo's physical limits (0 to 180 degrees).
The set_angle(int angle)
method allows setting the servo to a specific angle within its operating range:
-
Input Validation:
- Clamps the input angle between 0 and the maximum degree (usually 180 degrees) to prevent invalid positions.
-
Pulse Width Calculation:
- Calculates the required pulse width corresponding to the desired angle.
- Uses linear interpolation between the minimum and maximum pulse widths based on the angle:
pulse_width = min_pulse_width + (angle) * ((max_pulse_width - min_pulse_width) / SERVO_MAX_DEGREE)
- This ensures that the full range of motion is mapped proportionally to the pulse width range.
-
Updating the Servo:
- Calls the
update()
method to apply the new pulse width to the servo.
- Calls the
The add_pulse_width(uint32_t change)
method adjusts the current pulse width by a specified amount:
-
Change Application:
- Adds the
change
value to the current pulse width. - Can be positive (increase pulse width) or negative (decrease pulse width).
- Adds the
-
Boundary Enforcement:
- Ensures that the new pulse width stays within the defined minimum and maximum limits.
- Prevents the servo from moving beyond its physical capabilities.
-
Updating the Servo:
- Calls the
update()
method to apply the adjusted pulse width.
- Calls the
The update()
method applies the current pulse width to the servo hardware:
-
Duty Cycle Calculation:
- Converts the pulse width (in microseconds) to a duty cycle value compatible with the LEDC hardware.
- The formula used is:
duty = (pulse_width * max_duty) / period
-
max_duty
is determined by the duty resolution (e.g., 2^13 for 13-bit resolution). -
period
is the PWM period in microseconds (e.g., 20,000 µs for 50 Hz).
-
-
Applying Duty Cycle:
- Sets the duty cycle on the LEDC channel.
- Updates the duty cycle to take effect immediately.
The class provides methods to configure the pulse width range:
-
set_min_pulse_width(uint32_t min_pulse_width)
:- Updates the minimum pulse width.
- Adjusts the current pulse width to the new minimum and updates the servo.
-
set_max_pulse_width(uint32_t max_pulse_width)
:- Updates the maximum pulse width.
- Adjusts the current pulse width to the new maximum and updates the servo.
These methods allow calibration of the servo's range to match specific hardware characteristics.