Red Rover... Red Rover...

Science, Technology, Engineering, (Art) and Math (STEM / STEAM) curriculum is gaining momentum in K-12 education. It's exciting to see so many new makers and engineers learning how fun it is to make! I wanted to design a low cost robot that anyone could build if they have access to a 3D printer.

So... here it is - an autonomous micro rover based on Trinket.
It's inexpensive and fun, and looks adorable!

Tools / Materials

When I started designing the rover, I ran into a significant obstacle trying to find inexpensive tracks / treads. I had some success 3D printing ones with flexible filament but the total cost was too high.

That's when I stumbled into these... 'chain bracelets' from Oriental Trading. You can buy a dozen for less than ten dollars; which will make six rovers.

The pattern is slightly different on the inside and outside; rounded (left) and flat (right). The rounded side fits in the wheel / sprocket perfectly.
This guide builds on three others...

Introducing Trinket

Modifying Servos for Continuous Rotation

Trinket Servo Control


Ready? Here is a list of materials you will need.
For distance measurement, you can use a Parallax Ping))) Sensor, Grove Ultrasonic Ranger, or Maxbotix Ultrasonic Rangefinder.

Let's get fantastic with some plastic!

3D Printing

The rover is comprised of seven plastic parts... the stache' is not optional!
The model archive includes a small and large chassis and three different sonar mounts.
You should be able to squeeze all of the parts into a single print.

Source files are available on Github. Or, you could try a re-mix with some elements of designs on Thingiverse.

Assembly

You might need to tap the axle holes... better safe than sorry.
Or, you could warm / soften the plastic with a hand torch and thread the hole with the screw.
Make sure that the wheel on the front doesn't bind; you might need to sand around the axle a bit.
The mounting hole in the wheel is purposefully small to allow for the widest possible screw size. Widen the hole with a drill bit that is slightly larger than the threads of your screw.

Insert your M3 hex nut...
And screw the sonar mount to the chassis.
Pilot holes are needed to mount the servo to the chassis. A 1/16 inch bit will do nicely.
Take it slow... if you hear any kind of cracking you might want to warm / soften the plastic a bit before proceeding.
Let's get to work on the rear wheels.

Servo horns are typically white or black... grab one, and trim it to fit.

I tried using model cement this time... which works, but it will take an hour or so before the wheels are solid enough for final assembly.

5 minute epoxy might be a better alternative.
Repeat these steps for the other wheel.

Electronics

You'll have to decide on what combination of distance sensor + microcontroller you want to use. I've tried them all, and found the following configurations to work well.

Of all the sensors that I tried, the Parallax Ping))) was the easiest to use with Trinket. All you need is GND, VCC, and 1-pin for measurement.

Snap the sonar into the mount.
The servos wires end in female connectors, which you will need to convert to male.
You can do this two ways, replace them with male ones.
Or pull out the female connectors...
Extract a few pins from standard headers.
Insert the pin into the female receptacle.
And solder in-place.
Sweet.
Here is the circuit diagram to get all the wires in the right place. With the servos, remember that ground is often brown / signal is orange.

Source Code

"To control servos with the tiny microcontroller on the Trinket, we'll need a Servo library. The default Arduino Servo library is really only good for Uno/Leonardo/Due and similar beefy processors that can drive servos 'standalone'. Sadly, the Attiny85 can't quite do that as it does not have 16bit timers."

If you haven't installed the Adafruit Software Servo Library yet, read through this guide and install the library.

Good to go?

The following sketch uses the Parallax Ping))) Distance Sensor and is a starting point; ultra-bare bones. And this, is where all the fun begins!


#include <Adafruit_SoftServo.h>

#define SERVO1PIN 0   // Servo control line (orange) on Trinket Pin #0
#define SERVO2PIN 1   // Servo control line (orange) on Trinket Pin #1

Adafruit_SoftServo servo_left, servo_rght;

// Connect the sonar signal pin to this pin on the Trinket.
const int sonar = 2;

// Moderate speed forward for both servos. Given the orientation of the servos
// one will be going forward, and the other backward. You may need to adjust 
// these slightly to get the rover to move straight forward.
const int left_speed = 75;
const int rght_speed = 90;

// Number in cm when the rover will reverse and try to navigate around.
const int obstacle = 8;

// Multiplier used to determine how far the rover will back-up.
const int back_track = 100;

// Duration of a ping, distance in inches, distance converted to cm.
long duration, inches, cm;

void setup() 
{  
  // Attach servos... and off we go!
  servo_left.attach(SERVO1PIN);
  servo_rght.attach(SERVO2PIN);
} 

void loop() 
{ 
  // Setting servos in forward motion.
  servo_left.write(left_speed - cm);
  servo_left.refresh();
  servo_rght.write(rght_speed + cm);
  servo_rght.refresh();
  delay(15);  

  // establish variables for duration of the ping, and the distance 
  // result in inches and centimeters.
  duration = 0;
  inches = 0;
  cm = 0;

  // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  pinMode(sonar, OUTPUT);
  digitalWrite(sonar, LOW);
  delayMicroseconds(2);
  digitalWrite(sonar, HIGH);
  delayMicroseconds(5);
  digitalWrite(sonar, LOW);

  // The same pin is used to read the signal from the PING))): a HIGH
  // pulse whose duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  pinMode(sonar, INPUT);
  duration = pulseIn(sonar, HIGH);

  // convert the time into a distance.
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);  

  // Long distances will cause the servos to misbehave... cap at 50 cm.
  if ( cm > 50 ) {
    cm = 50;
  }

  if ( cm < obstacle ) {
    // back_track * delay(15) = distance the rover will back-up during 
    // obstacle avoidance.
    for (int i = 0; i < back_track; i++) {
      // Magic numbers... will always backup the same direction. Can you 
      // think of a better way to navigate obstacles?
      servo_left.write(150);
      servo_left.refresh();
      servo_rght.write(50);
      servo_rght.refresh();  
      delay(15);
    }
  }
}

long microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;
}