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:

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:

mbedAudio AmpSpeakerBluetoothBattery
gndpwr -gnd-
Voutpwr +Vin
p28in+RXI
p27TXO
gndCTS
out ++
out --
Vin+

The pinouts for the receiver portion of the hardware setup are connected as follows:

mbedI2S MicH-BridgeMotor 1Motor 2Battery
Vout3 VVcc / STBY-
gndgndgnd
p15BCLK
p17DOUT
p16LRCL
p5AI2
p6AI1
p7BI1
p8BI2
p22PWMB
p21PWMA
AO1red
AO2black
BO1red
BO2black
VinVM+

After setting up the chips, put your breadboard on a connected shadow chassis and it should look somewhat like the image below.

https://os.mbed.com/media/uploads/dlewis80/bot.jpg

Here is a video of the working robot:


Please log in to post comments.