Lesson 12: Basic miniAuto Programming

Programming your assembled miniAuto for basic movements and behaviors

🤖 From assembly to programming - bringing your robot to life

📚 Learning Objectives

By the end of this lesson, you will:

  • Program basic miniAuto movement functions
  • Control omnidirectional movement patterns
  • Implement speed and direction control
  • Create autonomous movement sequences

Key Concepts:

  • Mecanum wheel kinematics and control
  • Motor driver PWM and direction control
  • Velocity vectors and angle calculations
  • Autonomous behavior programming

🤖 Section 1: miniAuto Movement Basics

Understanding Mecanum Wheels

The miniAuto uses Mecanum wheels that allow omnidirectional movement. Each wheel has rollers at 45° angles that enable the robot to move forward, backward, sideways, and rotate without changing orientation.

Motor Control Pins:

  • Front Left Motor: PWM Pin 5, Direction Pin 4
  • Front Right Motor: PWM Pin 6, Direction Pin 7
  • Rear Left Motor: PWM Pin 9, Direction Pin 8
  • Rear Right Motor: PWM Pin 10, Direction Pin 11

🤖 Complete miniAuto Movement Control Program

Full Working Program

This complete program includes all movement functions and demonstrates the full capabilities of your miniAuto robot:

// Complete miniAuto Movement Control Program
// Lesson 12: Basic miniAuto Programming

// Motor pin definitions (using official Hiwonder pinout)
const static uint8_t motorpwmPin[4] = {10, 9, 6, 11}; // Motor PWM pins
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13}; // Motor direction pins
const static uint8_t pwm_min = 50; // Minimum PWM for motor movement

// LED pin for status indication
const int statusLED = 2;

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(500);
  
  // Initialize motor pins
  Motor_Init();
  
  // Initialize status LED
  pinMode(statusLED, OUTPUT);
  digitalWrite(statusLED, HIGH); // Turn on LED to show ready
  
  Serial.println("=== miniAuto Movement Control System ===");
  Serial.println("Robot initialized and ready for movement!");
  Serial.println("Starting comprehensive movement demonstration...");
  
  // Flash LED to indicate start
  for(int i = 0; i < 3; i++) {
    digitalWrite(statusLED, LOW);
    delay(200);
    digitalWrite(statusLED, HIGH);
    delay(200);
  }
  
  delay(2000); // Wait 2 seconds before starting
}

void loop() {
  // Comprehensive movement demonstration sequence
  demonstrateAllMovements();
  
  // Wait 5 seconds before repeating
  Serial.println("\nWaiting 5 seconds before next demonstration...");
  delay(5000);
}

void demonstrateAllMovements() {
  Serial.println("\n--- Starting Movement Demonstration ---");
  
  // 1. Forward Movement
  Serial.println("1. Moving Forward");
  digitalWrite(statusLED, HIGH);
  Velocity_Controller(0, 80, 0, false); // Forward at 80% speed
  delay(2000);
  stopRobot();
  delay(1000);
  
  // 2. Backward Movement
  Serial.println("2. Moving Backward");
  Velocity_Controller(180, 80, 0, false); // Backward at 80% speed
  delay(2000);
  stopRobot();
  delay(1000);
  
  // 3. Left Strafe
  Serial.println("3. Strafing Left");
  Velocity_Controller(270, 80, 0, false); // Left strafe
  delay(2000);
  stopRobot();
  delay(1000);
  
  // 4. Right Strafe
  Serial.println("4. Strafing Right");
  Velocity_Controller(90, 80, 0, false); // Right strafe
  delay(2000);
  stopRobot();
  delay(1000);
  
  // 5. Clockwise Rotation
  Serial.println("5. Rotating Clockwise");
  digitalWrite(statusLED, LOW); // Different LED state for rotation
  Velocity_Controller(0, 0, -60, false); // Clockwise rotation
  delay(2000);
  stopRobot();
  delay(1000);
  
  // 6. Counter-Clockwise Rotation
  Serial.println("6. Rotating Counter-Clockwise");
  Velocity_Controller(0, 0, 60, false); // Counter-clockwise rotation
  delay(2000);
  stopRobot();
  delay(1000);
  
  // 7. Diagonal Movements
  Serial.println("7. Diagonal Forward-Right");
  digitalWrite(statusLED, HIGH);
  Velocity_Controller(45, 70, 0, false); // 45-degree angle
  delay(1500);
  stopRobot();
  delay(1000);
  
  Serial.println("8. Diagonal Backward-Left");
  Velocity_Controller(225, 70, 0, false); // 225-degree angle
  delay(1500);
  stopRobot();
  delay(1000);
  
  // 8. Advanced: Drift Movement
  Serial.println("9. Drift Movement (Right + Rotation)");
  flashLED(3); // Special indication for advanced move
  Velocity_Controller(90, 60, 40, true); // Right movement with rotation and drift
  delay(2500);
  stopRobot();
  delay(1000);
  
  // 9. Figure-8 Pattern (Advanced)
  Serial.println("10. Figure-8 Pattern");
  performFigure8();
  
  Serial.println("--- Movement Demonstration Complete ---");
  digitalWrite(statusLED, HIGH);
}

void performFigure8() {
  // Create a figure-8 pattern using curved movements
  Serial.println("   Starting Figure-8 pattern...");
  
  // First loop of the 8
  for(int i = 0; i < 8; i++) {
    Velocity_Controller(0, 50, 30, false); // Forward with left turn
    delay(300);
  }
  
  // Transition
  Velocity_Controller(0, 40, 0, false);
  delay(200);
  
  // Second loop of the 8 (opposite direction)
  for(int i = 0; i < 8; i++) {
    Velocity_Controller(0, 50, -30, false); // Forward with right turn
    delay(300);
  }
  
  stopRobot();
  Serial.println("   Figure-8 pattern complete!");
  delay(1000);
}

void Motor_Init(void) {
  // Initialize all motor direction pins as outputs
  for(uint8_t i = 0; i < 4; i++) {
    pinMode(motordirectionPin[i], OUTPUT);
  }
  // Stop all motors initially
  Velocity_Controller(0, 0, 0, false);
}

void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift) {
  // Advanced velocity controller using Hiwonder's algorithm
  int8_t velocity_0, velocity_1, velocity_2, velocity_3;
  float speed = 1;
  angle += 90; // Adjust for coordinate system
  float rad = angle * PI / 180;
  
  // Adjust speed based on rotation
  if (rot == 0) speed = 1;
  else speed = 0.5;
  
  velocity /= sqrt(2);
  
  // Calculate individual motor velocities
  if (drift) {
    // Drift mode - special algorithm for drifting movements
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed * 2;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed * 2;
  } else {
    // Normal mode
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed + rot * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed - rot * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed;
  }
  
  // Apply velocities to motors
  Motors_Set(velocity_0, velocity_1, velocity_2, velocity_3);
}

void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3) {
  // Set individual motor speeds and directions
  int8_t pwm_set[4];
  int8_t motors[4] = {Motor_0, Motor_1, Motor_2, Motor_3};
  bool direction[4] = {1, 0, 0, 1}; // Default forward directions
  
  for(uint8_t i = 0; i < 4; ++i) {
    // Handle direction based on motor value sign
    if(motors[i] < 0) direction[i] = !direction[i];
    
    // Set PWM value
    if(motors[i] == 0) pwm_set[i] = 0;
    else pwm_set[i] = map(abs(motors[i]), 0, 100, pwm_min, 255);
    
    // Apply to hardware
    digitalWrite(motordirectionPin[i], direction[i]);
    analogWrite(motorpwmPin[i], pwm_set[i]);
  }
}

void stopRobot() {
  // Stop all motors immediately
  Velocity_Controller(0, 0, 0, false);
  Serial.println("   Robot stopped.");
}

void flashLED(int times) {
  // Flash status LED specified number of times
  for(int i = 0; i < times; i++) {
    digitalWrite(statusLED, LOW);
    delay(150);
    digitalWrite(statusLED, HIGH);
    delay(150);
  }
}

🔧 Section 4: Advanced Motor Testing Programs

The following programs are based on the official Hiwonder miniAuto documentation and provide comprehensive motor testing capabilities using the manufacturer's advanced control functions.

5.2 Forward Movement & Turning Test

Tests basic forward movement and in-place rotation using the Hiwonder velocity controller system. The robot moves forward for 1 second, turns left for 1 second, then turns right for 1 second.

// 5.2 Forward Movement & Turning Test
#include <Arduino.h>

const static uint8_t pwm_min = 50;
const static uint8_t motorpwmPin[4] = {10, 9, 6, 11}; // Motor PWM pins
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13}; // Motor direction pins

void Motor_Init(void);
void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift);
void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3);

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(500);
  Motor_Init();
  
  Velocity_Controller(0, 100, 0, 0);  // Move forward
  delay(1000);
  Velocity_Controller(0, 0, 100, 0);  // Turn left in place
  delay(1000);
  Velocity_Controller(0, 0, -100, 0); // Turn right in place
  delay(1000);
  Velocity_Controller(0, 0, 0, 0);    // Stop
}

void loop() {
  // Empty - single test sequence
}

void Motor_Init(void) {
  for(uint8_t i = 0; i < 4; i++) {
    pinMode(motordirectionPin[i], OUTPUT);
  }
  Velocity_Controller(0, 0, 0, 0);
}

void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift) {
  int8_t velocity_0, velocity_1, velocity_2, velocity_3;
  float speed = 1;
  angle += 90;
  float rad = angle * PI / 180;
  
  if (rot == 0) speed = 1;
  else speed = 0.5;
  
  velocity /= sqrt(2);
  
  if (drift) {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed * 2;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed * 2;
  } else {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed + rot * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed - rot * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed;
  }
  
  Motors_Set(velocity_0, velocity_1, velocity_2, velocity_3);
}

void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3) {
  int8_t pwm_set[4];
  int8_t motors[4] = {Motor_0, Motor_1, Motor_2, Motor_3};
  bool direction[4] = {1, 0, 0, 1}; // Forward direction defaults
  
  for(uint8_t i = 0; i < 4; ++i) {
    if(motors[i] < 0) direction[i] = !direction[i];
    
    if(motors[i] == 0) pwm_set[i] = 0;
    else pwm_set[i] = map(abs(motors[i]), 0, 100, pwm_min, 255);
    
    digitalWrite(motordirectionPin[i], direction[i]);
    analogWrite(motorpwmPin[i], pwm_set[i]);
  }
}

🎥 Expected Behavior Demo

When you upload and run this code, your miniAuto should perform the following sequence:

  1. Forward Movement (1 second): Robot moves straight forward
  2. Left Turn (1 second): Robot rotates counter-clockwise in place
  3. Right Turn (1 second): Robot rotates clockwise in place
  4. Stop: All motors stop, robot remains stationary

💡 Troubleshooting: If the robot moves in unexpected directions, check your wiring connections and ensure the Bluetooth module is removed during upload.

🎬 Watch Official Demo: View Hiwonder GIF demonstration of this movement sequence.

5.3 Four-Direction Movement Test

Tests all four basic movement directions: forward, backward, left strafe, and right strafe. Each movement lasts 1 second, demonstrating the full omnidirectional capabilities of mecanum wheels.

// 5.3 Four-Direction Movement Test
#include <Arduino.h>

const static uint8_t pwm_min = 50;
const static uint8_t motorpwmPin[4] = {10, 9, 6, 11}; // Motor PWM pins
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13}; // Motor direction pins

void Motor_Init(void);
void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift);
void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3);

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(500);
  Motor_Init();
  
  Serial.println("Starting 4-direction movement test...");
  
  // Forward movement
  Serial.println("Moving Forward");
  Velocity_Controller(0, 100, 0, 0);
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(500);
  
  // Backward movement  
  Serial.println("Moving Backward");
  Velocity_Controller(180, 100, 0, 0);
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(500);
  
  // Left strafe
  Serial.println("Moving Left");
  Velocity_Controller(270, 100, 0, 0);
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(500);
  
  // Right strafe
  Serial.println("Moving Right");
  Velocity_Controller(90, 100, 0, 0);
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  
  Serial.println("4-direction test complete!");
}

void loop() {
  // Empty - single test sequence
}

void Motor_Init(void) {
  for(uint8_t i = 0; i < 4; i++) {
    pinMode(motordirectionPin[i], OUTPUT);
  }
  Velocity_Controller(0, 0, 0, 0);
}

void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift) {
  int8_t velocity_0, velocity_1, velocity_2, velocity_3;
  float speed = 1;
  angle += 90;
  float rad = angle * PI / 180;
  
  if (rot == 0) speed = 1;
  else speed = 0.5;
  
  velocity /= sqrt(2);
  
  if (drift) {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed * 2;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed * 2;
  } else {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed + rot * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed - rot * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed;
  }
  
  Motors_Set(velocity_0, velocity_1, velocity_2, velocity_3);
}

void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3) {
  int8_t pwm_set[4];
  int8_t motors[4] = {Motor_0, Motor_1, Motor_2, Motor_3};
  bool direction[4] = {1, 0, 0, 1}; // Forward direction defaults
  
  for(uint8_t i = 0; i < 4; ++i) {
    if(motors[i] < 0) direction[i] = !direction[i];
    
    if(motors[i] == 0) pwm_set[i] = 0;
    else pwm_set[i] = map(abs(motors[i]), 0, 100, pwm_min, 255);
    
    digitalWrite(motordirectionPin[i], direction[i]);
    analogWrite(motorpwmPin[i], pwm_set[i]);
  }
}

🎥 Expected Behavior Demo

When you upload and run this code, your miniAuto should demonstrate omnidirectional movement:

  1. Forward Movement (1 second): Robot moves straight forward
  2. Backward Movement (1 second): Robot moves straight backward
  3. Left Strafe (1 second): Robot slides sideways to the left without rotating
  4. Right Strafe (1 second): Robot slides sideways to the right without rotating

🔍 Key Observation: Notice how the robot maintains its orientation while moving in all four directions - this is the power of mecanum wheels!

🎬 Watch Official Demo: View Hiwonder GIF demonstration of four-direction movement.

5.4 Diagonal Movement Test

Tests diagonal movements by controlling specific wheel combinations. Demonstrates advanced mecanum wheel kinematics: forward-left, backward-right, forward-right, and backward-left movements.

// 5.4 Diagonal Movement Test
#include <Arduino.h>

const static uint8_t pwm_min = 50;
const static uint8_t motorpwmPin[4] = {10, 9, 6, 11}; // Motor PWM pins
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13}; // Motor direction pins

void Motor_Init(void);
void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift);
void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3);

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(500);
  Motor_Init();
  
  Serial.println("Starting diagonal movement test...");
  
  // Forward-Left diagonal
  Serial.println("Moving Forward-Left");
  Velocity_Controller(315, 100, 0, 0); // 45° angle for diagonal
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(500);
  
  // Backward-Right diagonal
  Serial.println("Moving Backward-Right");
  Velocity_Controller(135, 100, 0, 0); // 135° angle
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(500);
  
  // Forward-Right diagonal
  Serial.println("Moving Forward-Right");
  Velocity_Controller(45, 100, 0, 0); // 45° angle
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(500);
  
  // Backward-Left diagonal
  Serial.println("Moving Backward-Left");
  Velocity_Controller(225, 100, 0, 0); // 225° angle
  delay(1000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  
  Serial.println("Diagonal movement test complete!");
}

void loop() {
  // Empty - single test sequence
}

void Motor_Init(void) {
  for(uint8_t i = 0; i < 4; i++) {
    pinMode(motordirectionPin[i], OUTPUT);
  }
  Velocity_Controller(0, 0, 0, 0);
}

void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift) {
  int8_t velocity_0, velocity_1, velocity_2, velocity_3;
  float speed = 1;
  angle += 90;
  float rad = angle * PI / 180;
  
  if (rot == 0) speed = 1;
  else speed = 0.5;
  
  velocity /= sqrt(2);
  
  if (drift) {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed * 2;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed * 2;
  } else {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed + rot * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed - rot * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed;
  }
  
  Motors_Set(velocity_0, velocity_1, velocity_2, velocity_3);
}

void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3) {
  int8_t pwm_set[4];
  int8_t motors[4] = {Motor_0, Motor_1, Motor_2, Motor_3};
  bool direction[4] = {1, 0, 0, 1}; // Forward direction defaults
  
  for(uint8_t i = 0; i < 4; ++i) {
    if(motors[i] < 0) direction[i] = !direction[i];
    
    if(motors[i] == 0) pwm_set[i] = 0;
    else pwm_set[i] = map(abs(motors[i]), 0, 100, pwm_min, 255);
    
    digitalWrite(motordirectionPin[i], direction[i]);
    analogWrite(motorpwmPin[i], pwm_set[i]);
  }
}

🎥 Expected Behavior Demo

When you upload and run this code, your miniAuto should demonstrate advanced diagonal movements:

  1. Forward-Left Diagonal (1 second): Robot moves at 45° angle forward and left
  2. Backward-Right Diagonal (1 second): Robot moves at 45° angle backward and right
  3. Forward-Right Diagonal (1 second): Robot moves at 45° angle forward and right
  4. Backward-Left Diagonal (1 second): Robot moves at 45° angle backward and left

⚡ Advanced Feature: These diagonal movements showcase the sophisticated kinematics possible with mecanum wheels - impossible with regular wheels!

🎬 Watch Official Demo: View Hiwonder GIF demonstration of diagonal movements.

5.5 Drifting Movement Test

Tests advanced drifting capabilities where the robot moves sideways while rotating. This demonstrates the drift parameter in the velocity controller, creating car-like drifting motion.

// 5.5 Drifting Movement Test
#include <Arduino.h>

const static uint8_t pwm_min = 50;
const static uint8_t motorpwmPin[4] = {10, 9, 6, 11}; // Motor PWM pins
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13}; // Motor direction pins

void Motor_Init(void);
void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift);
void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3);

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(500);
  Motor_Init();
  
  Serial.println("Starting drift movement test...");
  
  // Clockwise drift
  Serial.println("Drifting Clockwise");
  Velocity_Controller(90, 80, 50, true); // Right movement with rotation and drift enabled
  delay(2000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(1000);
  
  // Counter-clockwise drift
  Serial.println("Drifting Counter-Clockwise");
  Velocity_Controller(270, 80, -50, true); // Left movement with opposite rotation and drift
  delay(2000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  delay(1000);
  
  // Forward drift with rotation
  Serial.println("Forward Drift with Rotation");
  Velocity_Controller(0, 60, 40, true); // Forward with rotation and drift
  delay(2000);
  Velocity_Controller(0, 0, 0, 0); // Stop
  
  Serial.println("Drift test complete!");
}

void loop() {
  // Empty - single test sequence
}

void Motor_Init(void) {
  for(uint8_t i = 0; i < 4; i++) {
    pinMode(motordirectionPin[i], OUTPUT);
  }
  Velocity_Controller(0, 0, 0, 0);
}

void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift) {
  int8_t velocity_0, velocity_1, velocity_2, velocity_3;
  float speed = 1;
  angle += 90;
  float rad = angle * PI / 180;
  
  if (rot == 0) speed = 1;
  else speed = 0.5;
  
  velocity /= sqrt(2);
  
  if (drift) {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed * 2;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed * 2;
  } else {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed + rot * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed - rot * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed;
  }
  
  Motors_Set(velocity_0, velocity_1, velocity_2, velocity_3);
}

void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3) {
  int8_t pwm_set[4];
  int8_t motors[4] = {Motor_0, Motor_1, Motor_2, Motor_3};
  bool direction[4] = {1, 0, 0, 1}; // Forward direction defaults
  
  for(uint8_t i = 0; i < 4; ++i) {
    if(motors[i] < 0) direction[i] = !direction[i];
    
    if(motors[i] == 0) pwm_set[i] = 0;
    else pwm_set[i] = map(abs(motors[i]), 0, 100, pwm_min, 255);
    
    digitalWrite(motordirectionPin[i], direction[i]);
    analogWrite(motorpwmPin[i], pwm_set[i]);
  }
}

🎥 Expected Behavior Demo

When you upload and run this code, your miniAuto should demonstrate spectacular drifting movements:

  1. Clockwise Drift (2 seconds): Robot moves right while rotating clockwise - like a car drifting around a corner
  2. Counter-Clockwise Drift (2 seconds): Robot moves left while rotating counter-clockwise
  3. Forward Drift with Rotation (2 seconds): Robot moves forward while spinning - creating a spiral motion

🏎️ Cool Factor: This is the most impressive test - your robot will look like it's performing automotive-style drifts! Perfect for demonstrations.

🎬 Watch Official Demo: View Hiwonder GIF demonstration of drifting movements.

📋 Testing Notes:

  • Remove Bluetooth module before uploading to avoid serial port conflicts
  • Speed range is -100 to 100 for all parameters
  • Positive rotation values = counter-clockwise, negative = clockwise
  • Drift mode changes the motor control algorithm for special effects
  • Test on a clear, flat surface with adequate space

🛠️ Hands-On Activity: Standalone Movement Demos

💡 Why Separate Files?

Each demo is a separate program you can upload and run independently. Upload the code, disconnect the USB cable, and watch your robot perform the movement pattern without being tethered to your computer!

Demo 1: Basic Movement Test

Tests all basic movements: forward, backward, left, right, and rotations. Perfect for verifying your robot works correctly.

// Demo 1: Basic Movement Test
// Upload this code, disconnect USB, and watch your robot move!

const static uint8_t motorpwmPin[4] = {10, 9, 6, 11};
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13};
const static uint8_t pwm_min = 50;

void setup() {
  Motor_Init();
  delay(3000); // Wait 3 seconds after power on
}

void loop() {
  // Forward movement
  Velocity_Controller(0, 80, 0, false);
  delay(2000);
  stopRobot();
  delay(1000);
  
  // Backward movement
  Velocity_Controller(180, 80, 0, false);
  delay(2000);
  stopRobot();
  delay(1000);
  
  // Left strafe
  Velocity_Controller(270, 80, 0, false);
  delay(2000);
  stopRobot();
  delay(1000);
  
  // Right strafe
  Velocity_Controller(90, 80, 0, false);
  delay(2000);
  stopRobot();
  delay(1000);
  
  // Clockwise rotation
  Velocity_Controller(0, 0, -60, false);
  delay(2000);
  stopRobot();
  delay(1000);
  
  // Counter-clockwise rotation
  Velocity_Controller(0, 0, 60, false);
  delay(2000);
  stopRobot();
  delay(5000); // Wait 5 seconds before repeating
}

// Motor control functions (include in all demos)
void Motor_Init(void) {
  for(uint8_t i = 0; i < 4; i++) {
    pinMode(motordirectionPin[i], OUTPUT);
  }
  Velocity_Controller(0, 0, 0, false);
}

void Velocity_Controller(uint16_t angle, uint8_t velocity, int8_t rot, bool drift) {
  int8_t velocity_0, velocity_1, velocity_2, velocity_3;
  float speed = 1;
  angle += 90;
  float rad = angle * PI / 180;
  
  if (rot == 0) speed = 1;
  else speed = 0.5;
  
  velocity /= sqrt(2);
  
  if (drift) {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed * 2;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed * 2;
  } else {
    velocity_0 = (velocity * sin(rad) - velocity * cos(rad)) * speed + rot * speed;
    velocity_1 = (velocity * sin(rad) + velocity * cos(rad)) * speed - rot * speed;
    velocity_2 = (velocity * sin(rad) - velocity * cos(rad)) * speed - rot * speed;
    velocity_3 = (velocity * sin(rad) + velocity * cos(rad)) * speed + rot * speed;
  }
  
  Motors_Set(velocity_0, velocity_1, velocity_2, velocity_3);
}

void Motors_Set(int8_t Motor_0, int8_t Motor_1, int8_t Motor_2, int8_t Motor_3) {
  int8_t pwm_set[4];
  int8_t motors[4] = {Motor_0, Motor_1, Motor_2, Motor_3};
  bool direction[4] = {1, 0, 0, 1};
  
  for(uint8_t i = 0; i < 4; ++i) {
    if(motors[i] < 0) direction[i] = !direction[i];
    
    if(motors[i] == 0) pwm_set[i] = 0;
    else pwm_set[i] = map(abs(motors[i]), 0, 100, pwm_min, 255);
    
    digitalWrite(motordirectionPin[i], direction[i]);
    analogWrite(motorpwmPin[i], pwm_set[i]);
  }
}

void stopRobot() {
  Velocity_Controller(0, 0, 0, false);
}

Demo 2: Square Pattern

Makes your robot draw a perfect square using forward movement and 90-degree turns. Note: Turn timing has been calibrated for accurate 90° rotations.

// Demo 2: Square Pattern
// Upload, disconnect, and watch your robot draw a square!

const static uint8_t motorpwmPin[4] = {10, 9, 6, 11};
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13};
const static uint8_t pwm_min = 50;

void setup() {
  Motor_Init();
  delay(3000); // Wait 3 seconds after power on
}

void loop() {
  // Draw a square (4 sides with 4 turns)
  for(int i = 0; i < 4; i++) {
    // Move forward for one side
    Velocity_Controller(0, 70, 0, false);
    delay(2500);
    
    // Stop briefly
    stopRobot();
    delay(500);
    
    // Turn right 90 degrees
    Velocity_Controller(0, 0, -50, false);
    delay(1100);
    
    // Stop briefly
    stopRobot();
    delay(500);
  }
  
  // Wait 10 seconds before drawing another square
  delay(10000);
}

// Include motor control functions here (same as Demo 1)
// [Motor_Init, Velocity_Controller, Motors_Set, stopRobot functions]

🔧 Calibration Tip:

The delay(1100) value has been tested on ceramic tile floors for accurate 90° turns:
Smooth surfaces (tile, hardwood): Use delay(1100)
Carpet or rough surfaces: May need delay(1200-1300)
Turns too much? Reduce to delay(1000)
Turns too little? Increase to delay(1200)
Battery level affects rotation speed - fresh batteries may need less time

🚀 Advanced: Sensor-Based Navigation

For more accurate navigation, robots can use sensors instead of timing:
IMU/Gyroscope: Measures actual rotation angles (±1° accuracy)
Magnetometer/Compass: Provides absolute directional heading
Encoders: Count wheel rotations for precise distance measurement
Future lesson: We'll explore adding these sensors to miniAuto!

Demo 3: Circle Pattern

Creates smooth circular motion by combining forward movement with continuous rotation.

// Demo 3: Circle Pattern
// Upload, disconnect, and watch your robot draw circles!

const static uint8_t motorpwmPin[4] = {10, 9, 6, 11};
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13};
const static uint8_t pwm_min = 50;

void setup() {
  Motor_Init();
  delay(3000); // Wait 3 seconds after power on
}

void loop() {
  // Draw a circle using forward movement + rotation
  for(int i = 0; i < 25; i++) {
    Velocity_Controller(0, 60, 30, false); // Forward with left turn
    delay(400);
  }
  
  // Stop and wait
  stopRobot();
  delay(3000);
  
  // Draw circle in opposite direction
  for(int i = 0; i < 25; i++) {
    Velocity_Controller(0, 60, -30, false); // Forward with right turn
    delay(400);
  }
  
  // Stop and wait before repeating
  stopRobot();
  delay(5000);
}

// Include motor control functions here (same as Demo 1)
// [Motor_Init, Velocity_Controller, Motors_Set, stopRobot functions]

Demo 4: Drift Showcase

Shows off the advanced drift capabilities - the most impressive demo for audiences!

// Demo 4: Drift Showcase
// Upload, disconnect, and watch impressive drift movements!

const static uint8_t motorpwmPin[4] = {10, 9, 6, 11};
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13};
const static uint8_t pwm_min = 50;

void setup() {
  Motor_Init();
  delay(3000); // Wait 3 seconds after power on
}

void loop() {
  // Right drift
  Velocity_Controller(90, 70, 50, true);
  delay(2500);
  stopRobot();
  delay(1000);
  
  // Left drift
  Velocity_Controller(270, 70, -50, true);
  delay(2500);
  stopRobot();
  delay(1000);
  
  // Forward spiral drift
  Velocity_Controller(0, 60, 60, true);
  delay(3000);
  stopRobot();
  delay(1000);
  
  // Backward spiral drift
  Velocity_Controller(180, 60, -60, true);
  delay(3000);
  stopRobot();
  delay(5000); // Wait before repeating
}

// Include motor control functions here (same as Demo 1)
// [Motor_Init, Velocity_Controller, Motors_Set, stopRobot functions]

How to Use These Demos

  1. Choose a demo - Copy one of the complete programs above
  2. Upload to miniAuto - Paste into Arduino IDE and upload
  3. Disconnect USB cable - No need to stay connected!
  4. Power on and watch - Robot runs the demo automatically
  5. Try different demos - Upload a new one to see different patterns
  6. Modify and experiment - Change speeds, delays, and patterns

✅ Benefits of Standalone Demos:

  • No USB tether - Robot moves freely without cable constraints
  • Perfect for demonstrations - Show friends and family without a laptop
  • Easy to modify - Each demo is self-contained and simple to understand
  • Safe testing - Each pattern is predictable and repeatable

Extension Challenges

  1. Create new patterns: Triangle, pentagon, or figure-8 movements
  2. Add LED indicators: Different colors for different movement types
  3. Speed variations: Gradually change speed during patterns
  4. Obstacle course: Program a sequence to navigate around objects
  5. Combine patterns: Chain multiple demos together in one program

📝 Assessment & Homework

Quick Check Questions

  1. What makes Mecanum wheels different from regular wheels?
  2. How many motors does the miniAuto have and what pins control them?
  3. What wheel pattern creates sideways movement to the left?
  4. How do you make the robot rotate in place?
  5. What's the difference between PWM and direction pins?
← Previous: Lesson 11
Course Overview
Next: Lesson 13 →