From 6acc6786672c046474fabc194025fb6db5365e30 Mon Sep 17 00:00:00 2001 From: jpalmeidaalves Date: Tue, 28 Jan 2025 22:43:40 +0000 Subject: [PATCH] fix: classes improved and docs car movement and joystick --- docs/carmotion.md | 149 +++++++++++++++++++++++++++ docs/joystick.md | 177 +++++++++++++++++++++++++++++++++ examples/baseCar/main.cpp | 33 +----- platform/carMove/carMove.cpp | 6 -- platform/carMove/carMove.hpp | 12 ++- platform/joystick/joystick.cpp | 47 ++++++--- platform/joystick/joystick.hpp | 5 +- 7 files changed, 371 insertions(+), 58 deletions(-) create mode 100644 docs/carmotion.md create mode 100644 docs/joystick.md diff --git a/docs/carmotion.md b/docs/carmotion.md new file mode 100644 index 00000000..827b3d63 --- /dev/null +++ b/docs/carmotion.md @@ -0,0 +1,149 @@ +# Car Movement System Documentation + +## Overview +The Car Movement System is a C++ implementation for controlling a robotic car's movement through servo motors and motor drivers using I2C communication. The system provides control over steering angle and motor speed, integrating with the pigpio library for GPIO control on Raspberry Pi. + +## Dependencies +- pigpio library for GPIO control +- I2C enabled on the Raspberry Pi +- Standard C++ libraries (algorithm, cmath, stdexcept) + +## Hardware Configuration +### Servo Configuration +- I2C Address: 0x40 +- PWM Frequency: ~50Hz +- Steering Channel: 0 +- PWM Values: + - Left Maximum (45°): 205 + - Center (0°): 307 + - Right Maximum (45°): 410 + +### Motor Configuration +- I2C Address: 0x60 +- PWM Frequency: 60Hz +- Channels: + - ENA: Channel 0 + - IN1: Channel 1 + - IN2: Channel 2 + - IN3: Channel 5 + - IN4: Channel 6 + - ENB: Channel 7 + +## Class Reference + +### carMove Class + +#### Constructor +```cpp +carMove(); +``` +Initializes the car movement system by: +1. Initializing the pigpio library +2. Setting up I2C communication for both servo and motor controllers +3. Configuring PWM frequencies and modes +4. Throws `std::runtime_error` if initialization fails + +#### Destructor +```cpp +~carMove(); +``` +Performs cleanup by: +1. Closing I2C connections +2. Terminating the pigpio library + +#### Public Methods + +##### setServoAngle +```cpp +void setServoAngle(int angle); +``` +Sets the steering angle of the car. +- Parameters: + - `angle`: Integer value between -45 and 45 degrees + - Negative values: Turn left + - Positive values: Turn right + - Zero: Center position +- Automatically clamps values to valid range + +##### setMotorSpeed +```cpp +void setMotorSpeed(int speed); +``` +Controls the car's motor speed and direction +- Parameters: + - `speed`: Integer value between -100 and 100 + - Positive values: Forward motion + - Negative values: Backward motion + - Zero: Stop +- Automatically clamps values to valid range + +##### sequence +```cpp +void sequence(); +``` +Demonstrates basic movement capabilities: +1. Drives forward at full speed (2 seconds) +2. Drives backward at full speed (2 seconds) +3. Turns wheels fully left (1 second) +4. Turns wheels fully right (1 second) +5. Returns to neutral position + +#### Private Methods + +##### setPWM +```cpp +static void setPWM(int device_handle, int channel, int on_value, int off_value); +``` +Low-level PWM control for both servo and motor +- Parameters: + - `device_handle`: I2C device handle + - `channel`: PWM channel number + - `on_value`: PWM signal start time + - `off_value`: PWM signal stop time + +## Constants + +```cpp +static const int SERVO_ADDR = 0x40; // Servo controller I2C address +static const int MOTOR_ADDR = 0x60; // Motor controller I2C address +static const int STEERING_CHANNEL = 0; // Servo channel for steering +static constexpr int MAX_ANGLE = 45; // Maximum steering angle +static const int SERVO_LEFT_PWM = 205; // PWM value for maximum left turn +static const int SERVO_CENTER_PWM = 307; // PWM value for center position +static const int SERVO_RIGHT_PWM = 410; // PWM value for maximum right turn +``` + +## Usage Example + +```cpp +try { + carMove car; + + // Basic movement + car.setMotorSpeed(50); // Move forward at half speed + + sleep(2); // Continue for 2 seconds + + car.setServoAngle(-30); // Turn left at 30 degrees + + sleep(1); + + car.setMotorSpeed(0); // Stop + car.setServoAngle(0); // Center wheels + + // Run demo sequence + // car.sequence(); + +} catch (const std::runtime_error& e) { + std::cerr << "Error: " << e.what() << std::endl; +} +``` + +## Error Handling +The class uses exception handling to manage errors: +- Throws `std::runtime_error` if: + - pigpio initialization fails + - I2C device initialization fails +- All motor and servo commands are bounds-checked to prevent invalid values + + diff --git a/docs/joystick.md b/docs/joystick.md new file mode 100644 index 00000000..00c90141 --- /dev/null +++ b/docs/joystick.md @@ -0,0 +1,177 @@ +# Joystick Controller Documentation + +## Overview +The Joystick Controller is a C++ implementation for handling game controller input using SDL2's GameController API. It provides a flexible interface for mapping controller buttons and axes to car control functions, integrating with the carMove system for controlling a robotic car. + +## Dependencies +- SDL2 library with GameController support +- carMove class +- Standard C++ libraries (array, functional, iostream, map, utility) + +## Class Reference + +### joystick Class + +#### Constructor +```cpp +joystick(carMove& car); +``` +Initializes the joystick controller system: +1. Initializes SDL2 GameController subsystem +2. Scans for available game controllers +3. Opens the first compatible controller found +4. Sets up default axis mappings for the car +5. Throws `std::runtime_error` if: + - SDL2 initialization fails + - No compatible controller is found + +#### Destructor +```cpp +~joystick(); +``` +Performs cleanup: +1. Closes the game controller connection +2. Shuts down SDL2 + +#### Public Methods + +##### setButtonAction +```cpp +void setButtonAction(int button, Actions actions); +``` +Maps a controller button to specific press and release actions +- Parameters: + - `button`: Button identifier (SDL_GameControllerButton value) + - `actions`: Structure containing: + - `onPress`: Function to execute when button is pressed + - `onRelease`: Function to execute when button is released + +##### setAxisAction +```cpp +void setAxisAction(int axis, std::function action); +``` +Maps a controller axis to a specific action +- Parameters: + - `axis`: Axis identifier (SDL_GameControllerAxis value) + - `action`: Function taking an integer value (-32768 to 32767) + +##### setAxisMapping +```cpp +void setAxisMapping(carMove& car); +``` +Sets up default axis mappings for car control: +1. Left stick X-axis (1): Controls steering angle (-45° to 45°) +2. Right stick Y-axis (5): Controls motor speed (-100% to 100%) + +##### listen +```cpp +void listen(); +``` +Enters the main event processing loop: +1. Continuously polls for controller events +2. Processes button presses and axis movements +3. Exits when specific button combination is pressed (buttons 4 and 6) +4. Handles controller disconnection events + +##### printButtonStates +```cpp +void printButtonStates(); +``` +- When a button is pressed, print its number in the console +- Use this method to map the control buttons + +#### Private Methods + +##### processEvent +```cpp +void processEvent(const SDL_Event& event); +``` +Internal event processing method: +- Handles: + - Button press/release events + - Axis motion events + - Controller disconnection events +- Updates button states +- Executes mapped actions +- Logs event information to console + +## Data Structures + +### Actions Structure +```cpp +struct Actions { + std::function onPress; // Function called on button press + std::function onRelease; // Function called on button release +}; +``` + +### Class Members +```cpp +SDL_GameController* gameController; // Pointer to the active controller +std::map buttonActions; // Maps buttons to their actions +std::map> axisActions; // Maps axes to their actions +std::array buttonStates; // Current button states +``` + +## Usage Example + +```cpp +try { + carMove car; + joystick controller(car); + + // Custom button mapping example + controller.setButtonAction(SDL_CONTROLLER_BUTTON_A, + Actions{ + []() { std::cout << "A pressed\n"; }, + []() { std::cout << "A released\n"; } + }); + + // Custom axis mapping example + controller.setAxisAction(SDL_CONTROLLER_AXIS_LEFTX, + [](int value) { + std::cout << "Left stick X: " << value << "\n"; + }); + + // Start processing controller input + controller.listen(); + +} catch (const std::runtime_error& e) { + std::cerr << "Error: " << e.what() << std::endl; +} +``` + +## Event Handling +The class handles several types of SDL events: +1. SDL_CONTROLLERBUTTONDOWN: Button press events +2. SDL_CONTROLLERBUTTONUP: Button release events +3. SDL_CONTROLLERAXISMOTION: Axis movement events +4. SDL_CONTROLLERDEVICEREMOVED: Controller disconnection events + +## Default Mappings +- Left Stick X-Axis: + - Range: -32768 to 32767 + - Mapped to steering angle: -45° to 45° +- Right Stick Y-Axis: + - Range: -32768 to 32767 + - Mapped to motor speed: -100% to 100% + +## Error Handling +- Throws `std::runtime_error` for initialization failures +- Handles controller disconnection gracefully +- Validates button and axis indices before processing +- Logs events and errors to standard output + +## Safety Considerations +1. Always check for null controller pointer before operations +2. Handle controller disconnection appropriately +3. Implement emergency stop functionality +4. Validate axis values before applying to car controls +5. Consider implementing dead zones for axis inputs + +## Maintenance Notes +- Regularly check SDL2 events for controller status +- Monitor controller connection stability +- Consider implementing controller reconnection handling +- Add support for multiple controller types/layouts +- Implement axis calibration functionality if needed diff --git a/examples/baseCar/main.cpp b/examples/baseCar/main.cpp index 6ed93256..f37c2e13 100644 --- a/examples/baseCar/main.cpp +++ b/examples/baseCar/main.cpp @@ -1,41 +1,12 @@ -#include - #include "platform/joystick/joystick.hpp" -carMove car; - -// Function to handle left stick horizontal axis movements (steering) -void moveLeftandRight(int value) { - float fvalue = value * 1.0; // Raw value - std::cout << "Before normalization: " << fvalue << '\n'; - - // Normalize to a 45-degree range - fvalue = fvalue / 32000.0 * 45; - std::cout << "Servo angle moved to: " << fvalue << " degrees" << '\n'; - - car.setServoAngle(fvalue); -} - -// Function to handle right stick vertical axis movements (engine control) -void moveForwardandBackward(int value) { - value -= 16319; // Adjust for neutral position - value = (value / 165) * -1; // Normalize and invert for correct direction - - std::cout << "Engine speed moved to: " << value << '\n'; - car.setMotorSpeed(value); -} - auto main() -> int { try { + carMove car; joystick controller(car); - // Map left stick horizontal axis to control the servo (steering) - controller.setAxisAction(SDL_CONTROLLER_AXIS_LEFTX, moveLeftandRight); - - // Map right stick vertical axis to control the engine (forward/reverse) - controller.setAxisAction(SDL_CONTROLLER_AXIS_RIGHTY, moveForwardandBackward); + // car.sequence(); // Uncomment to test the car sequence - // Start listening for events controller.listen(); } catch (const std::runtime_error &e) { diff --git a/platform/carMove/carMove.cpp b/platform/carMove/carMove.cpp index 50cf05d6..ae9578b6 100644 --- a/platform/carMove/carMove.cpp +++ b/platform/carMove/carMove.cpp @@ -1,11 +1,5 @@ #include "carMove.hpp" -#include - -#include // For std::max, std::min -#include // For std::abs, std::floor, etc. -#include // For std::runtime_error - void carMove::setPWM(int device_handle, int channel, int on_value, int off_value) { int reg_base = 0x06 + (channel * 4); i2cWriteByteData(device_handle, reg_base, on_value & 0xFF); diff --git a/platform/carMove/carMove.hpp b/platform/carMove/carMove.hpp index d61dbda3..ba8ca29b 100644 --- a/platform/carMove/carMove.hpp +++ b/platform/carMove/carMove.hpp @@ -2,7 +2,9 @@ #define CAR_MOVE_HPP #include +#include +#include #include #include #include @@ -22,17 +24,17 @@ class carMove { static void setPWM(int device_handle, int channel, int on_value, int off_value); - // Constantes + // Constants static const int SERVO_ADDR = 0x40; static const int MOTOR_ADDR = 0x60; static const int STEERING_CHANNEL = 0; - // A constante MAX_ANGLE é 'constexpr' para permitir uso direto em tempo de compilação - static constexpr int MAX_ANGLE = 45; + // The constant MAX_ANGLE is 'constexpr' to allow direct use at compile time + static constexpr int MAX_ANGLE = 80; - static const int SERVO_LEFT_PWM = 205; // Adjust as needed >> TEST!! + static const int SERVO_LEFT_PWM = 180; // Adjust as needed >> TEST!! static const int SERVO_CENTER_PWM = 307; - static const int SERVO_RIGHT_PWM = 410; + static const int SERVO_RIGHT_PWM = 435; }; #endif diff --git a/platform/joystick/joystick.cpp b/platform/joystick/joystick.cpp index f57c819d..a402be87 100644 --- a/platform/joystick/joystick.cpp +++ b/platform/joystick/joystick.cpp @@ -1,21 +1,19 @@ #include "joystick.hpp" -#include - joystick::joystick(carMove& car) : gameController(nullptr) { if (SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) { throw std::runtime_error("Failed to initialize SDL2 GameController: " + std::string(SDL_GetError())); } - // Abrir o primeiro controlador de jogo disponível for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i) != 0U) { gameController = SDL_GameControllerOpen(i); if (gameController != nullptr) { std::cout << "GameController connected: " << SDL_GameControllerName(gameController) << '\n'; - setPS4AxisMapping(car); + setAxisMapping(car); + printButtonStates(); break; } } @@ -41,20 +39,22 @@ void joystick::setAxisAction(int axis, std::function action) { axisActions[axis] = std::move(action); } -void joystick::setPS4AxisMapping(carMove& car) { - std::cout << "Mapping PS4 controller axes to car actions." << '\n'; +void joystick::setAxisMapping(carMove& car) { // Left stick X-axis controls the servo (steering) - setAxisAction(1, [&car](int value) { - // Normalize SDL axis value (-32768 to 32767) to angle range (-45 to 45 degrees) - int angle = static_cast(value / 32767.0 * 45); - car.setServoAngle(angle); + setAxisAction(0, [&car](int value) { + float fvalue = value * 1.0; // Raw value + + // Normalize to a 45-degree range + fvalue = fvalue / 32000.0 * 55; + car.setServoAngle(fvalue); }); // Right stick Y-axis controls the motor speed setAxisAction(5, [&car](int value) { // Normalize SDL axis value (-32768 to 32767) to speed percentage (-100% to 100%) - int speed = static_cast(value / 32767.0 * 100); - car.setMotorSpeed(speed); // Invert to match joystick forward/backward direction + value -= 16319; // Adjust for neutral position + value = (value / 165) * -1; + car.setMotorSpeed(value); // Invert to match joystick forward/backward direction }); } @@ -67,10 +67,8 @@ void joystick::processEvent(const SDL_Event& event) { buttonStates[button] = is_pressed; if (buttonActions.find(button) != buttonActions.end()) { if (is_pressed) { - std::cout << "Button " << button << " pressed." << '\n'; buttonActions[button].onPress(); } else { - std::cout << "Button " << button << " released." << '\n'; buttonActions[button].onRelease(); } } @@ -79,7 +77,6 @@ void joystick::processEvent(const SDL_Event& event) { int axis = event.caxis.axis; int value = event.caxis.value; - std::cout << "Axis " << axis << " moved to " << value << "." << '\n'; if (axisActions.find(axis) != axisActions.end()) { axisActions[axis](value); } @@ -120,3 +117,23 @@ void joystick::listen() { SDL_Delay(10); } } + +void joystick::printButtonStates() { + setButtonAction(0, Actions{[]() {}, []() {}}); + setButtonAction(1, Actions{[]() {}, []() {}}); + setButtonAction(2, Actions{[]() {}, []() {}}); + setButtonAction(3, Actions{[]() {}, []() {}}); + setButtonAction(4, Actions{[]() {}, []() {}}); + setButtonAction(5, Actions{[]() {}, []() {}}); + setButtonAction(6, Actions{[]() {}, []() {}}); + setButtonAction(7, Actions{[]() {}, []() {}}); + setButtonAction(8, Actions{[]() {}, []() {}}); + setButtonAction(9, Actions{[]() {}, []() {}}); + setButtonAction(10, Actions{[]() {}, []() {}}); + setButtonAction(11, Actions{[]() {}, []() {}}); + setButtonAction(12, Actions{[]() {}, []() {}}); + setButtonAction(13, Actions{[]() {}, []() {}}); + setButtonAction(14, Actions{[]() {}, []() {}}); + setButtonAction(15, Actions{[]() {}, []() {}}); + setButtonAction(16, Actions{[]() {}, []() {}}); +} diff --git a/platform/joystick/joystick.hpp b/platform/joystick/joystick.hpp index fb09a44a..715acde1 100644 --- a/platform/joystick/joystick.hpp +++ b/platform/joystick/joystick.hpp @@ -1,11 +1,13 @@ #pragma once #include +#include #include #include #include #include +#include #include "platform/carMove/carMove.hpp" @@ -20,8 +22,9 @@ class joystick { ~joystick(); void setButtonAction(int button, Actions actions); void setAxisAction(int axis, std::function action); - void setPS4AxisMapping(carMove& car); + void setAxisMapping(carMove& car); void listen(); + void printButtonStates(); private: SDL_GameController* gameController;