Acoustic Vehicle Robot
Authors: Daniel Lewis and Quentin Truncale
Introduction
4180 Final Project for "Acoustic Vehicle Robot". The purpose of this project was to prototype an acoustic modem and use it to control a robotic vehicle. The idea is to send a signal at a certain frequency via a speaker. This signal will then sampled by a microphone and processed by the Goertzel algorithm. After detecting the correct frequency, the microcontroller will then send out a signal to motors in order to drive the car. The frequencies we used for this project were 800 Hz and 1 kHz.
Parts List
The parts required for this project are:
- 2 Mbed NXP LPC1768 microcontrollers
- 2 Small Geared DC Motors
- ROB-14450 (Dual H-Bridge Driver)
- Speaker (PCB Mount, Sparkfun)
- SPH0645 (I2S MEMS Microphone)
- Adafruit Bluefruit LE UART Friend
- Class D Audio Amplifier
- 4 AA Batteries
- 2 Wheels
- Shadow Chassis
Code
There are two sets of code to complete this project. The first set is for the transmitter, which effectively sends out a signal at either 800 Hz or 1 kHz depending on what button is hit on the Adafruit Bluefruit LE Connect App. Hitting '3' on this app interface sends an 800 Hz signal on the speaker which interacts with the left wheel. The '4' button sends a 1 kHz signal out from the speaker which will interact with the right wheel.
Import programTransmitter_publish
This is the transmitter file.
#include "mbed.h"
class Speaker
{
public:
Speaker(PinName pin) : _pin(pin) {
// _pin(pin) means pass pin to the Speaker Constructor
}
// class method to play a note based on PwmOut class
void PlayNote(float frequency, float duration, float volume) {
_pin.period(1.0/frequency);
_pin = volume/2.0;
wait(duration);
_pin = 0.0;
}
private:
// sets up specified pin for PWM using PwmOut class
PwmOut _pin;
};
BusOut myled(LED1,LED2,LED3,LED4);
Serial blue(p28,p27);
Speaker mySpeaker(p21);
int main()
{
mySpeaker.PlayNote(400.0,0.05,0.1);
char bnum=0;
char bhit=0;
while(1) {
if (blue.getc()=='!') {
if (blue.getc()=='B') { //button data packet
bnum = blue.getc(); //button number
bhit = blue.getc(); //1=hit, 0=release
if (blue.getc()==char(~('!' + 'B' + bnum + bhit))) { //checksum OK?
myled = bnum - '0'; //current button number will appear on LEDs
switch (bnum) {
case '1': //number button 1
if (bhit=='1') {
mySpeaker.PlayNote(400.0,0.01,0.5);
}
break;
case '2': //number button 2
if (bhit=='1') {
mySpeaker.PlayNote(600.0,0.01,0.5);
}
break;
case '3': //number button 3
if (bhit=='1') {
mySpeaker.PlayNote(800.0,0.01,0.5);
}
break;
case '4': //number button 4
if (bhit=='1') {
mySpeaker.PlayNote(1000.0,0.01,0.5);
}
break;
case '5': //button 5 up arrow
if (bhit=='1') {
//add hit code here
} else {
//add release code here
}
break;
case '6': //button 6 down arrow
if (bhit=='1') {
//add hit code here
} else {
//add release code here
}
break;
case '7': //button 7 left arrow
if (bhit=='1') {
//add hit code here
} else {
//add release code here
}
break;
case '8': //button 8 right arrow
if (bhit=='1') {
//add hit code here
} else {
//add release code here
}
break;
default:
break;
}
}
}
}
}
}
The second set of code is the receiver. This file samples input from the microphone using interrupts. Once a full block of samples is ready, this data is run through the Goertzel algorithm in order to detect two frequencies. Once the algorithm calculation is complete, it signals to the sampling process that it is ready for another block. Also, depending on the detected frequencies, the motors will turn on and off.
Import programReceiver
This is the receiver code for the 4180 project.
#include "mbed.h"
#include "I2S.h"
#include "Motor.h"
#define sample_freq 8000.0
#define M_PI 3.14159265358979323846
//108 is good bin size
// Globals
I2S i2srx(I2S_RECEIVE, p17, p16, p15);
DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalOut myled4(LED4);
Motor Lmotor(p21,p6,p5);
Motor Rmotor(p22,p7,p8);
volatile int updating_data[108] ={0}; //i2s data
volatile int i = 0; // counter
volatile bool dv = 0; // data valid flag
volatile bool gr = 1; // goertzel ready flag
volatile float data[108] = {0.0}; //complete data block
// Interrupt to read microphone
void i2s_isr()
{
if (i<108){
updating_data[i] = (i2srx.read()>>7);//read value using I2S input
dv = 0;
i++;
}
else{
if (gr){
i = 0;
for (i=0; i<108; i++)
data[i] = float(updating_data[i]); //shift data to constant register
dv = 1;
}
i = 0;
updating_data[i] = i2srx.read();
}
}
// Goertzal function
float goertzel_mag(int numSamples,int TARGET_FREQUENCY,int SAMPLING_RATE, volatile float* data)
{
float realW,imagW,d1,d2,y,resultr,resulti,magnitude;
realW = 2.0*cos(2.0*M_PI*TARGET_FREQUENCY/numSamples);
imagW = sin(2.0*M_PI*TARGET_FREQUENCY/numSamples);
d1=0;
d2=0;
for (int n=0; n<numSamples; n++)
{
y = data[n] + realW*d1 - d2;
d2=d1;
d1=y;
}
resultr = 0.5*realW*d1 - d2;
resulti = imagW*d1;
magnitude = sqrtf(resultr*resultr + resulti*resulti);
return magnitude;
}
int main()
{
//Goertzel function initialize
int numSamples = 108; //block size
int TARGET_FREQUENCY = 800; //frequency to detect
int SAMPLING_RATE = sample_freq; //I2S sampling frequency
float mag1 = 0.0;
float mag2 = 0.0;
//Motor Initialize
bool lm_moving = 0;
bool rm_moving = 0;
//i2s microphone initialize
i2srx.stereomono(I2S_MONO);//mono not stereo mode
i2srx.masterslave(I2S_MASTER);//master drives clocks
i2srx.frequency(sample_freq);//set sample freq
i2srx.set_interrupt_fifo_level(1);//interrupt on one sample
i2srx.attach(&i2s_isr);//set I2S ISR
i2srx.start();//start I2S and interrupts
while(1) {
if(dv && gr){
gr = 0;
mag1 = goertzel_mag(numSamples, TARGET_FREQUENCY, SAMPLING_RATE, data);
mag2 = goertzel_mag(108.0,1000.0,SAMPLING_RATE,data);
gr = 1;
myled4 = !myled4;
}
//3000000
if (mag1>3000000)
{
myled1=1;
rm_moving = !rm_moving;
}
else
myled1=0;
if (mag2>3000000)
{
myled2=1;
lm_moving = !lm_moving;
}
else
myled2=0;
if (lm_moving)
Lmotor.speed(-0.5);
else
Lmotor.speed(0);
if (rm_moving)
Rmotor.speed(0.5);
else
Rmotor.speed(0);
}
}
Hardware Setup
The pinouts for the transmitter portion of the hardware setup are connected as follows:
| mbed | Audio Amp | Speaker | Bluetooth | Battery |
|---|---|---|---|---|
| gnd | pwr - | gnd | - | |
| Vout | pwr + | Vin | ||
| p28 | in+ | RXI | ||
| p27 | TXO | |||
| gnd | CTS | |||
| out + | + | |||
| out - | - | |||
| Vin | + |
The pinouts for the receiver portion of the hardware setup are connected as follows:
| mbed | I2S Mic | H-Bridge | Motor 1 | Motor 2 | Battery |
|---|---|---|---|---|---|
| Vout | 3 V | Vcc / STBY | - | ||
| gnd | gnd | gnd | |||
| p15 | BCLK | ||||
| p17 | DOUT | ||||
| p16 | LRCL | ||||
| p5 | AI2 | ||||
| p6 | AI1 | ||||
| p7 | BI1 | ||||
| p8 | BI2 | ||||
| p22 | PWMB | ||||
| p21 | PWMA | ||||
| AO1 | red | ||||
| AO2 | black | ||||
| BO1 | red | ||||
| BO2 | black | ||||
| Vin | VM | + |
After setting up the chips, put your breadboard on a connected shadow chassis and it should look somewhat like the image below.

Here is a video of the working robot:
Please log in to post comments.
