ECE 4180 Mbed WAG-Boy Project
Project Overview
The WAG-Boy (What A Good Boy) is a personal companion that is made using the mbed and its ecosystem. This project makes use of the mbed, robot chassis, DC motors, dual H-bridge, sonar sensor, I2C touchpad, LCD, class D amp, and speaker to create an interactive robot that can avoid objects while roaming a room, respond to physical touch, and express its needs through sight and sound. The WAG-Boy is mainly marketed towards children/young families to teach responsibility or to the elderly or anyone that needs companionship but cannot have an actual pet for whatever reason.

Bot Behavior
Roaming
- Roam around the room (default state)
- Avoid collisions by turning around in place
Petting
- Activates by touching I2C Touchpad
- Wag tail and purr
Feeding
- Activates by putting object in front of Sonar
- Wag tail and make eating noises
Whining
- Activated by no interaction after awhile
- Whimper for attention, then Roam away
Hungry
- Activates by not being fed for long time
- Bark for attention
Sleeping
- Activates when not fed for long time
- Stop all activity briefly and snore
Devices Used
- Mbed
- Robot chassis
- 2 DC motors & Dual H-bridge : Used to provide movement to the bot
| mbed | H-bridge | Motor 1 | =Motor 2 |
|---|---|---|---|
| 5V | VM | ||
| GND | GND | ||
| VOUT | VCC | ||
| A01 | + | ||
| A02 | - | ||
| p23 | PWMA | ||
| p30 | AI1 | ||
| p8 | AI2 | ||
| B01 | + | ||
| B02 | - | ||
| p24 | PWMB | ||
| p9 | BI1 | ||
| p10 | BI2 | ||
| VOUT | STBY |
- Sonar sensor : Used for collision avoidance
| mbed | Sonar |
|---|---|
| 5V | VCC |
| GND | GND |
| p11 | TRIG |
| p12 | ECHO |
- I2C touchpad : Used for tactile interactivity
| mbed | Touchpad |
|---|---|
| GND | GND |
| p28 | SDA |
| p27 | SCL |
| p26 | IRQ |
| VOUT | VCC |
- LCD screen : Used to provide visual status feedback (the "eye")
| mbed | uLCD | uLCD cable |
|---|---|---|
| 5V | 5V | 5V |
| GND | GND | GND |
| p13 | RX | TX |
| p14 | TX | RX |
| p29 | RESET | RESET |
- Speaker & Class D amp : Used to prodive aural status feedback (the "mouth")
| mbed | Class D amp | Speaker |
|---|---|---|
| GND | PWR-, IN- | |
| 5V | PWR+ | |
| p18 | IN+ | |
| OUT+ | + | |
| OUT- | - |
- SD card reader : Used to load realistic sounds for the speaker (purring, barking, etc.)
| mbed | SD Card Reader |
|---|---|
| p21 | CS |
| p5 | DI |
| VOUT | VCC |
| p7 | SCK |
| GND | GND |
| p6 | DO |
| CD |
- Servo : Used for the wagging tail
| mbed | Servo |
|---|---|
| 5V | 5V |
| GND | GND |
| p22 | PWM |
Demo Video
Code
Import programWAGBoy
Code for a robotic pet to interact with.
main.cpp
#include "mbed.h"
#include "motordriver.h"
#include "rtos.h"
#include "uLCD_4DGL.h"
#include "mpr121.h"
#include "ultrasonic.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include "Servo.h"
uLCD_4DGL uLCD(p13,p14,p29); // serial tx, serial rx, reset pin;
SDFileSystem sd(p5, p6, p7, p21, "sd"); //SD card
AnalogOut DACout(p18);
wave_player waver(&DACout);
Motor m1(p23, p30, p8, 1); // pwm, fwd, rev
Motor m2(p24, p9, p10, 1); // pwm, fwd, rev
InterruptIn interrupt(p26);
I2C i2c(p28, p27);
Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);
Servo myservo(p22);
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
enum {ROAM, STOP, SLEEP};
volatile int state;
volatile int energy;
volatile bool touched;
volatile bool fed;
volatile bool touchpad;
void fallInterrupt() {
touchpad = true;
mpr121.read(0x00);
mpr121.read(0x01);
}
void dist(int distance) {
//control wheels
if (state == ROAM) {
if(distance < 250) {
m1.stop(0);
m2.stop(0);
Thread::wait(100);
m1.speed(.6);
m2.speed(-.6);
Thread::wait(400);
} else {
m1.speed(.5);
m2.speed(.5);
}
} else { // stop for STOP and SLEEP
m1.stop(0);
m2.stop(0);
}
if (state == STOP && distance < 60) {
//increase food status
fed = true;
Thread::wait(3000); // make sure fed state lasts atleast 3 seconds
fed = false;
}
}
ultrasonic mu(p11, p12, .1, 1, &dist);
void sonar(void const *argument) {
while(1) {
mu.checkDistance();
Thread::wait(100);
}
}
void touch(void const *argument) {
while(1) {
if (touchpad) {
touched = true;
Thread::wait(5000);
touchpad = false;
} else {
touched = false;
}
Thread::wait(100); // runs 10 times a second
}
}
void screen(void const *argument) {
while(1) {
uLCD.filled_rectangle(0, 0, 127, 127, BLACK); // clear screen
switch(state) {
case ROAM: {
uLCD.circle(64, 64, 30, BLUE); // blue circle in the center
uLCD.filled_circle(64, 64, 5, BLUE); // blue circle in the center
} break;
case STOP: {
uLCD.locate(0,0);
uLCD.text_width(12);
uLCD.text_height(12);
if (fed || touched) { // touched or fed
uLCD.text_width(9);
uLCD.text_height(9);
uLCD.color(RED);
uLCD.printf("<3");
} else if (energy < 30) { // tired
uLCD.color(RED+GREEN);
uLCD.printf("0"); // yellow 0's
} else { // wondering
uLCD.color(BLUE);
uLCD.printf("?"); // blue ?'s
}
} break;
case SLEEP: {
uLCD.locate(0,0);
uLCD.text_width(12);
uLCD.text_height(12);
uLCD.color(BLUE);
uLCD.printf("Z"); // blue Z's
}
}
Thread::wait(100);
}
}
void sound(void const *argument) {
FILE *wave_file;
while(1) {
switch(state) {
case STOP: {
if (fed) { // fed
wave_file=fopen("/sd/eat.wav","r"); // 2 seconds
} else if (touched) { // touched
wave_file=fopen("/sd/purr.wav","r"); // 3 seconds
} else if (energy < 30) { // tired
wave_file=fopen("/sd/bark.wav","r"); // < 1 second
Thread::wait(500);
} else { // wondering
wave_file=fopen("/sd/curious.wav","r"); // 1 second
Thread::wait(1000);
}
waver.play(wave_file);
fclose(wave_file);
} break;
case SLEEP: {
wave_file=fopen("/sd/sleep.wav","r"); // 8 seconds
waver.play(wave_file);
fclose(wave_file);
}
}
Thread::wait(100);
}
}
void tail(void const *argument) {
while(1) {
if (state == STOP && (touched || fed)) {
for(int i=0; i<100; i++) {
myservo = i/100.0;
Thread::wait(10);
}
for(int i=100; i>0; i--) {
myservo = i/100.0;
Thread::wait(10);
}
} else {
myservo = 0.5;
}
Thread::wait(500);
}
}
int main() {
uLCD.cls();
uLCD.printf("Change baudrate.....");
uLCD.baudrate(3000000); //jack up baud rate to max
uLCD.background_color(BLACK);
uLCD.cls();
uLCD.text_bold(ON);
uLCD.text_mode(OPAQUE);
mu.startUpdates();
interrupt.fall(&fallInterrupt);
interrupt.mode(PullUp);
state = ROAM;
energy = 70;
Thread thread1(sonar);
Thread thread2(touch);
Thread thread3(screen);
Thread thread4(sound);
Thread thread5(tail);
while(1) {
// Status LEDS
led3 = touched;
led4 = fed;
// Update state
if (energy < 5) { // sleeps for 10 seconds and wake up with 50 more energy
state = SLEEP;
led1 = 1;
led2 = 1;
Thread::wait(10000);
energy += 50;
} else if (energy < 30 || touched || fed) {
state = STOP;
led1 = 0;
led2 = 1;
} else if (state == STOP) {
Thread::wait(6000); // wait wondering for 5 seconds after touched or fed
state = ROAM;
} else {
state = ROAM;
led1 = 1;
led2 = 0;
}
// Update energy
switch(state) {
case ROAM: energy -= 2;
break;
case STOP : energy -= 1;
if (fed) {
energy += 10; // +10 energy a second while fed
}
break;
}
// Make sure energy stays in 0-100
if (energy < 0) {
energy = 0;
} else if (energy > 100) {
energy = 100;
}
Thread::wait(1000); // runs once per second
}
}
Please log in to post comments.
