/* Copyright 2025 Thibaut Ferrand / Ulysse Cura */ #include #include #include #include #include "Ressources.hpp" #define PIN_TIRETTE 5 #define PIN_BUTTON_COLOR 6 #define PIN_MOTOR1 1 #define PIN_MOTOR2 2 #define PIN_SERVO 7 #define DANCING_ACTION_DELTA_ANGLE 2 #define GAIN_KD 50 #define ANGULAR_SPEED 120 // °/s using std::abs, std::signbit; enum class Axes { X, Y, Z }; class Motor { public: int init(int pin, int channel) { m_pin = pin; m_channel = channel; pinMode(m_pin, OUTPUT); ledcAttachChannel(m_pin, 5000, 12, m_channel); ledcWriteChannel(m_channel, 0); return 0; } void setSpeed(int speed) { if(speed > 4095) speed = 4095; ledcWriteChannel(m_channel, speed); } private: int m_pin; int m_channel; }; class IO { public: IO(CodeCell *code_cell) : m_code_cell(code_cell) {} int init() { int nb_errors {0}; pinMode(PIN_TIRETTE, INPUT_PULLUP); pinMode(PIN_BUTTON_COLOR, INPUT_PULLUP); initGyroscope(); m_initMotors(); m_initServo(); nb_errors += m_initScreen(); if(!nb_errors) { m_screen.setTextSize(1); m_screen.setTextColor(WHITE, BLACK); m_screen.setFont(&Res::Fonts::Freshman12pt7b); } return nb_errors; } void initGyroscope() { m_code_cell->Motion_RotationRead(m_init_x, m_init_y, m_init_z); } int update() { static unsigned long last_color_change_time {0}; static bool has_color_changed {true}; static bool is_color_blue {true}; if(m_is_color_blue != is_color_blue) { m_screen.clearDisplay(); m_screen.setCursor(0, 20); if(is_color_blue) { m_screen.print("Blue\nTeam"); } else { m_screen.print("Yellow\nTeam"); } last_color_change_time = millis(); has_color_changed = true; } if(millis() - last_color_change_time > 1000 && has_color_changed) { m_screen.clearDisplay(); m_screen.drawBitmap(38, 0, Res::Imgs::riombotique, 52, 64, WHITE); //m_screen.drawBitmap(75, 0, Imgs::poivron_robotique, 52, 64, WHITE); //m_screen.drawBitmap(27, 0, Res::Imgs::diable_gaga, 75, 64, WHITE); has_color_changed = false; } m_is_tirette_pulled = (digitalRead(PIN_TIRETTE) == HIGH); m_is_color_blue = is_color_blue; is_color_blue = (digitalRead(PIN_BUTTON_COLOR) == LOW); if(m_is_motor_control_activated) { m_updateMotorsControl(); } if(m_is_dancing) { m_updateDancingAction(); } m_updateScreen(); return 0; } bool isTirettePulled() { return m_is_tirette_pulled; } bool isSelectedColorBlue() { return m_is_color_blue; } void motorControlOn() { m_is_motor_control_activated = true; } void motorControlOff() { m_is_motor_control_activated = false; m_motors[0].setSpeed(0); m_motors[1].setSpeed(0); } void setDirWithAngularSpeed(float dir, float angular_speed = ANGULAR_SPEED) { static unsigned long prev_time {millis()}; float err_dir = dir - m_dir; float angular_displacement = angular_speed * static_cast(millis() - prev_time) / 1000.0f; if(abs(err_dir) < abs(angular_displacement)) { m_dir = dir; } else { m_dir += angular_displacement * ((!signbit(err_dir)) * 2 - 1); } prev_time = millis(); } void setDir(float dir) { m_dir = dir; } void setSpeed(float speed) { m_speed = speed; } float getAngle(Axes axis) { float x, y, z; float angle; m_code_cell->Motion_RotationRead(x, y, z); switch(axis) { case Axes::X: angle = x - m_init_x; if(angle < -180) angle += 360; if(angle > 180) angle -= 360; return angle; case Axes::Y: angle = y - m_init_y; if(angle < -180) angle += 360; if(angle > 180) angle -= 360; return angle; default: angle = z - m_init_z; if(angle < -180) angle += 360; if(angle > 180) angle -= 360; return angle; } } void startDancingAction(int dancing_action_delta_angle = DANCING_ACTION_DELTA_ANGLE) { m_dancing_action_delta_angle = dancing_action_delta_angle; m_is_dancing = true; } Adafruit_SSD1306 *getScreen() { return &m_screen; } Motor *getMotor(int motor) { return &(m_motors[motor]); } private: // Init Motors void m_initMotors() { m_motors[0].init(PIN_MOTOR1, 0); m_motors[1].init(PIN_MOTOR2, 1); } // Init Servo void m_initServo() { m_servo.setPeriodHertz(50); m_servo.attach(PIN_SERVO, 500, 2400); m_servo.write(87); } // Init Screen int m_initScreen() { if(!m_screen.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("SSD1306 allocation failed."); return -1; } m_screen.fillScreen(SSD1306_BLACK); return 0; } void m_updateDancingAction() { static unsigned long prev_time {millis()}; static unsigned long delta_time {0}; static int actual_angle {87}; static int8_t actual_dir {-1}; delta_time += millis() - prev_time; if(delta_time >= 10) { delta_time = 0; actual_angle += actual_dir * m_dancing_action_delta_angle; } if(actual_angle <= 43) { actual_dir = 1; } else if(actual_angle >= 130) { actual_dir = -1; } m_servo.write(actual_angle); prev_time = millis(); } void m_updateMotorsControl() { float actual_angle = getAngle(Axes::Z); float error = m_dir - actual_angle; float correction = error * GAIN_KD; int m1_speed = static_cast(m_speed + correction); int m2_speed = static_cast(m_speed - correction); if(m1_speed < 0) { m1_speed = 0; //m2_speed = static_cast(m_speed - 2 * correction); } else if(m2_speed < 0) { m2_speed = 0; //m1_speed = static_cast(m_speed + 2 * correction); } m_motors[0].setSpeed(m1_speed); m_motors[1].setSpeed(m2_speed); } void m_updateScreen() { m_screen.display(); } bool m_is_motor_control_activated {false}; bool m_is_tirette_pulled {false}; bool m_is_color_blue {false}; bool m_is_dancing {false}; Motor m_motors[2]; Servo m_servo; Adafruit_SSD1306 m_screen {128, 64, &Wire, -1}; CodeCell *m_code_cell; float m_init_x, m_init_y, m_init_z; float m_dir {0}; float m_speed {0}; int m_dancing_action_delta_angle {DANCING_ACTION_DELTA_ANGLE}; };