Lesson 14: Basic miniAuto Programming
Programming your robot for autonomous behavior
Important: Code Compatibility Notice
Some code examples in this lesson reference #include "miniAuto.h" and miniAuto.* functions. This is a custom library that may not be available.
✅ Use the working code from Section 1 - it uses standard Arduino libraries and will compile successfully. The other examples are for conceptual understanding only.
Learning Objectives
By the end of this lesson, students will:
- Use Arduino functions for robot control effectively
- Create autonomous navigation programs
- Implement sensor-based decision making
- Debug and optimize robot behavior
Skills Developed:
- Robotics library usage
- Autonomous system design
- Real-time debugging techniques
Lesson Content
1 miniAuto Library Functions (20 minutes)
Essential miniAuto Functions:
Movement Functions:
- •
moveForward(speed) - •
moveBackward(speed) - •
turnLeft(speed) - •
turnRight(speed) - •
stop()
Sensor Functions:
- •
getDistance() - •
getLineSensor() - •
getBatteryVoltage() - •
getEncoderCount() - •
isButtonPressed()
/**
* @file miniAuto_obstacle_avoidance.ino
* @brief Basic Obstacle Avoidance Program using Standard Arduino Libraries
* @author miniAuto Team / Hiwonder
* @version V1.0
* @date 2024-04-22
*/
#include <Arduino.h>
// Motor control pins
const static uint8_t motorpwmPin[4] = {10, 9, 6, 11}; // Motor speed control pins
const static uint8_t motordirectionPin[4] = {12, 8, 7, 13}; // Motor direction control pins
// Sensor pins
const int trigPin = 2;
const int echoPin = 3;
const int ledPin = 13;
// Function declarations
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);
float getUltrasonicDistance();
void setStatusLED(bool state);
void setup() {
Serial.begin(9600);
Serial.println("🤖 miniAuto Obstacle Avoidance Program");
// Initialize motors
Motor_Init();
// Initialize ultrasonic sensor
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// Initialize LED
pinMode(ledPin, OUTPUT);
// Startup sequence
Serial.println("System initialized - Starting obstacle avoidance...");
setStatusLED(true);
delay(1000);
setStatusLED(false);
}
void loop() {
// Get distance reading
float distance = getUltrasonicDistance();
// Display status
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
// Simple obstacle avoidance behavior
if (distance > 30) {
// Clear path - move forward
Serial.println("→ Moving forward");
Velocity_Controller(0, 60, 0, 0); // Move forward at 60% speed
setStatusLED(false); // LED off = good
} else if (distance > 15) {
// Obstacle detected - slow down
Serial.println("⚠️ Slowing down");
Velocity_Controller(0, 30, 0, 0); // Move forward at 30% speed
setStatusLED(true); // LED on = caution
} else {
// Obstacle too close - stop and turn
Serial.println("🚫 Obstacle! Turning right");
Velocity_Controller(0, 0, 0, 0); // Stop
delay(200);
Velocity_Controller(0, 0, -50, 0); // Turn right in place
delay(800);
setStatusLED(false);
}
delay(100); // Small delay for stability
}
// Motor initialization function
void Motor_Init(void) {
for(uint8_t i = 0; i < 4; i++) {
pinMode(motordirectionPin[i], OUTPUT);
}
Velocity_Controller(0, 0, 0, 0); // Start with motors stopped
}
/**
* @brief Speed control function for omnidirectional movement
* @param angle Controls robot motion direction (0-359°, front=0°, counterclockwise positive)
* @param velocity Controls robot speed (0-100)
* @param rot Controls rotation speed (-100 to 100, positive=counterclockwise)
* @param drift Enables drift mode (0=normal, 1=drift)
*/
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);
}
/**
* @brief PWM and motor direction setting function
*/
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];
else direction[i] = direction[i];
if(motors[i] == 0) pwm_set[i] = 0;
else pwm_set[i] = map(abs(motors[i]), 0, 100, 2, 255);
digitalWrite(motordirectionPin[i], direction[i]);
analogWrite(motorpwmPin[i], pwm_set[i]);
}
}
/**
* @brief Get distance reading from ultrasonic sensor
*/
float getUltrasonicDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
float distance = duration * 0.034 / 2; // Convert to centimeters
return distance;
}
/**
* @brief Control status LED
*/
void setStatusLED(bool state) {
digitalWrite(ledPin, state);
} 2 Autonomous Navigation Algorithms (25 minutes)
Navigation Strategies:
- • Reactive Navigation: Respond to immediate sensor inputs
- • Behavior-Based: Combine multiple simple behaviors
- • State Machine: Different modes for different situations
- • Goal-Oriented: Navigate toward specific objectives
// Advanced navigation with state machine
enum NavigationState {
EXPLORING,
AVOIDING_OBSTACLE,
FOLLOWING_WALL,
SEARCHING,
RETURNING_HOME
};
NavigationState currentState = EXPLORING;
unsigned long stateStartTime = 0;
int explorationDirection = 1; // 1 = right, -1 = left
void autonomousNavigation() {
float frontDistance = miniAuto.getDistance();
unsigned long currentTime = millis();
switch (currentState) {
case EXPLORING:
if (frontDistance < 20) {
changeState(AVOIDING_OBSTACLE);
} else {
miniAuto.moveForward(180);
miniAuto.setLED(GREEN);
// Random direction changes
if (currentTime - stateStartTime > 5000) {
if (random(0, 100) < 20) { // 20% chance
changeState(SEARCHING);
}
}
}
break;
case AVOIDING_OBSTACLE:
miniAuto.setLED(RED);
miniAuto.stop();
delay(300);
// Turn away from obstacle
if (explorationDirection > 0) {
miniAuto.turnRight(150);
} else {
miniAuto.turnLeft(150);
}
delay(800);
changeState(FOLLOWING_WALL);
break;
case FOLLOWING_WALL:
miniAuto.setLED(YELLOW);
// Follow wall for a while
if (frontDistance > 40) {
changeState(EXPLORING);
} else {
miniAuto.moveForward(120);
}
if (currentTime - stateStartTime > 3000) {
changeState(EXPLORING);
}
break;
case SEARCHING:
miniAuto.setLED(BLUE);
// Scanning behavior
miniAuto.turnRight(100);
if (currentTime - stateStartTime > 2000) {
explorationDirection *= -1; // Change direction
changeState(EXPLORING);
}
break;
}
}
void changeState(NavigationState newState) {
currentState = newState;
stateStartTime = millis();
Serial.print("State changed to: ");
Serial.println(newState);
} 3 Multi-Sensor Decision Making (15 minutes)
Sensor Fusion Concepts:
- • Combine multiple sensor inputs for better decisions
- • Handle conflicting sensor information
- • Prioritize sensors based on reliability and context
- • Create robust behaviors that work in various environments
// Multi-sensor decision making
struct SensorData {
float distance;
int lineSensorLeft;
int lineSensorRight;
float batteryVoltage;
bool buttonPressed;
unsigned long timestamp;
};
SensorData readAllSensors() {
SensorData data;
data.distance = miniAuto.getDistance();
data.lineSensorLeft = miniAuto.getLineSensor(LEFT);
data.lineSensorRight = miniAuto.getLineSensor(RIGHT);
data.batteryVoltage = miniAuto.getBatteryVoltage();
data.buttonPressed = miniAuto.isButtonPressed();
data.timestamp = millis();
return data;
}
void intelligentBehavior() {
SensorData sensors = readAllSensors();
// Priority 1: Safety (battery and obstacles)
if (sensors.batteryVoltage < 6.5) {
miniAuto.setLED(RED);
miniAuto.playTone(880, 100);
Serial.println("🔋 Low battery - returning to base");
returnToBase();
return;
}
// Priority 2: Emergency stop
if (sensors.buttonPressed) {
miniAuto.stop();
miniAuto.setLED(RED);
Serial.println("🛑 Emergency stop activated");
while (miniAuto.isButtonPressed()) delay(100);
return;
}
// Priority 3: Line following (if line detected)
if (sensors.lineSensorLeft < 500 || sensors.lineSensorRight < 500) {
followLine(sensors);
return;
}
// Priority 4: Obstacle avoidance
if (sensors.distance < 25) {
avoidObstacle(sensors);
return;
}
// Default: Explore
miniAuto.moveForward(150);
miniAuto.setLED(GREEN);
}
4 Debugging and Performance Optimization (15 minutes)
Debugging Techniques:
- • Use Serial monitor for real-time debugging
- • LED indicators for visual status feedback
- • Sound cues for different robot states
- • Systematic testing of individual components
// Debugging and monitoring system
bool debugMode = true;
unsigned long lastDebugOutput = 0;
void debugOutput(String message, float value = -999) {
if (!debugMode) return;
Serial.print("[");
Serial.print(millis());
Serial.print("] ");
Serial.print(message);
if (value != -999) {
Serial.print(": ");
Serial.print(value);
}
Serial.println();
}
void performanceMonitor() {
if (millis() - lastDebugOutput > 1000) {
debugOutput("Distance", miniAuto.getDistance());
debugOutput("Battery", miniAuto.getBatteryVoltage());
debugOutput("State", currentState);
debugOutput("Free RAM", ESP.getFreeHeap());
lastDebugOutput = millis();
}
}
// Performance optimization
void optimizedLoop() {
// Reduce sensor reading frequency
static unsigned long lastSensorRead = 0;
static float cachedDistance = 0;
if (millis() - lastSensorRead > 50) { // 20Hz
cachedDistance = miniAuto.getDistance();
lastSensorRead = millis();
}
// Use cached value for decisions
makeDecision(cachedDistance);
// Monitor performance
performanceMonitor();
} Hands-On Activity (25 minutes)
Project: Intelligent Explorer Robot
Students will create a sophisticated autonomous robot that demonstrates intelligent behavior using multiple sensors and decision-making algorithms.
Program Requirements:
- • Implement state machine navigation
- • Use multiple sensors for decision making
- • Include safety features (battery monitoring, emergency stop)
- • Add visual and audio feedback for different states
- • Create debugging output for monitoring
- • Optimize performance for smooth operation
Behavior Goals:
- • Explore environment autonomously for 5+ minutes
- • Avoid all obstacles without getting stuck
- • Respond appropriately to different situations
- • Provide clear status information to operator
Advanced Challenge:
Add a "learning" component where the robot remembers successful navigation strategies and adapts its behavior based on past experiences.
Assessment & Homework
Quick Check (In Class):
- • Can student use miniAuto library functions effectively?
- • Do they implement proper state machine logic?
- • Are they integrating multiple sensors correctly?
- • Can they debug and optimize their programs?
Homework Assignment:
Personal Robot Assistant
Create a robot that acts as a personal assistant - it can follow you around, respond to button presses with different behaviors, monitor your environment, and provide useful feedback through lights and sounds. Include at least 5 different "assistant modes".