4180 Final Project (Sound Locator)
Team Members: David Kim, Kevin Wang

Overview
The idea of this project is to have a robot drive towards a sound source. The sound source is located through triangulation, and a sound map would be printed from the IoT chip to a website. The mbed first waits for a trigger to be sent from the web server. Once this occurs, the mbed starts to listen for 2 seconds. After that time, sends a location to the web server so show the origin of the sound source, and the robot then turns and moves towards the location.
Parts List
- mbed LPC1768 board
- Adafruit SPW2430 Mems Microphone (3)
| Mic | mbed |
|---|---|
| DC | p15, p16, p17 |
| Vin | 3.3V |
| Gnd | Gnd |
- Sparkfun TB6612FNG Dual Motor Driver Carrier
| HBridge | mbed /motor |
|---|---|
| Vm | p15, p16, p17 |
| Vcc | 3.3V |
| AO1 | Motor 1 + |
| AO2 | Motor 1 - |
| BO2 | Motor 2 + |
| BO1 | Motor 2 - |
| PWMA | p26 |
| AI2 | p25 |
| AI1 | p24 |
| BI1 | p23 |
| BI2 | p22 |
| PWMB | p21 |
| Gnd | Gnd |
- Sparkfun ESP8266 Wifi Chip
| Wifi Chip | mbed |
|---|---|
| RX | p28 |
| TX | p27 |
| V+ | 5V |
- Sparkfun ROB-12629 Encoder Kit (2)
| Encoder | mbed |
|---|---|
| White | p12, p13 |
| Red | 3.3V |
| Black | Gnd |
- Sparkfun ROB-1330 Shadowbot Chassis
- Sparkfun ROB-13302 DC motors (2)
- Rechargable 5V battery
Schematic

Video
Code
Final code
// ESP8266 Static page WEB server to control Mbed
#include "mbed.h"
#include "Motor.h"
#include "HALLFX_ENCODER.h"
Serial pc(USBTX, USBRX);
Serial esp(p28, p27); // tx, rx
LocalFileSystem local("local"); // Create the local filesystem under the name "local"
// Standard Mbed LED definitions
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
// some test values to show on web page
AnalogIn Ain1(p18);
AnalogIn Ain2(p19);
/*
char ssid[32] = "hsd"; // enter WiFi router ssid inside the quotes
char pwd [32] = "austin123"; // enter WiFi router password inside the quotes
*/
float temperature, AdcIn, Ht;
float R1=100000, R2=10000; // resistor values to give a 10:1 reduction of measured AnalogIn voltage
char Vcc[10];
char Temp[10];
// things for sending/receiving data over serial
volatile int tx_in=0;
volatile int tx_out=0;
volatile int rx_in=0;
volatile int rx_out=0;
const int buffer_size = 4095;
char tx_buffer[buffer_size+1];
char rx_buffer[buffer_size+1];
void Tx_interrupt();
void Rx_interrupt();
void send_line();
void read_line();
int DataRX;
int update;
int count;
char cmdbuff[1024];
char replybuff[4096];
char webdata[4096]; // This may need to be bigger depending on WEB browser used
char webbuff[4096]; // Currently using 1986 characters, Increase this if more web page data added
char timebuf[30];
void SendCMD(),getreply(),ReadWebData(),startserver();
void gettime(),setRTC(),gettemp(),getbattery();
char rx_line[1024];
int port =80; // set server port
int SERVtimeout =5; // set server timeout in seconds in case link breaks.
struct tm t;
// manual set RTC values
int minute =00; // 0-59
int hour =12; // 2-23
int dayofmonth =26; // 1-31
int month =8; // 1-12
int year =15; // last 2 digits
float mic1val;
float mic2val;
float mic3val;
float mic1max = -1000.0;
float mic2max = -1000.0;
float mic3max = -1000.0;
int mic1sample;
int mic2sample;
int mic3sample;
// MOTOR DECLARATION---------------------------------------------------------
HALLFX_ENCODER EncL(p12);
HALLFX_ENCODER EncR(p13);
Motor mL(p26, p25, p24); // pwm, fwd, rev left motor
Motor mR(p21, p22, p23); // pwm, fwd, rev right motor
// constants
float pi = 3.141592654;
//int enc_count_wheel = 384;
int enc_count_wheel = 300; // encoder count for 1 wheel revolution = 48 gear ratio * 8 poles
float d_wheel = 65.0; // wheel diameter = 65 mm
float c_wheel = d_wheel*pi; // wheel circumference =~ 204.2 mm
float d_chassis = 155.0; // axle length = 155 mm
float c_chassis = d_chassis*pi; // chassis circumference =~ 486.94
int enc_count_chassis = 1831; // encoder count for 1 chassis revolution = c_chassis/c_wheel*384 = 1831
float mm_2_enc_count = enc_count_wheel/c_wheel; // converts mm to encoder count
float degree;
float encDistToMove;
float xCoord;
float yCoord;
uint32_t mic1Timer;
uint32_t mic2Timer;
uint32_t mic3Timer;
AnalogIn mic1(p16);
AnalogIn mic2(p17);
AnalogIn mic3(p15);
//Timer tim;
Timeout triggerTimeout;
bool triggerD = false;
bool listeningForTrigger = true;
void enableTrigger() {
listeningForTrigger = true;
sprintf(cmdbuff, "count,time,analog1,analog2=0,\"%s\",\"%s\",\"%s\"\r\n","N/A","N/A","here");
SendCMD();
}
void startMics() {
triggerD = false;
printf("\n\rstart");
sprintf(cmdbuff, "count,time,analog1,analog2=0,\"%s\",\"%s\",\"%s\"\r\n","N/A","N/A","here");
SendCMD();
mic1max = -1000.0;
mic2max = -1000.0;
mic3max = -1000.0;
//tim.start();
printf("\n\r");
//printf("%d\n\r", tim.read_us());
for(int i=0; i<24000; i++) {
//read mic values
mic1val = ((mic1 * 500.0)-98.0)/1.1;
mic2val = ((mic2 * 500.0)-98.0)/1.1;
mic3val = ((mic3 * 500.0)-98.0)/1.1;
// Obtain the sample at which the maximum value occurs
if (mic1val > mic1max){
mic1max = mic1val;
mic1sample = i;
//mic1Timer = tim.read_us();
}
if (mic2val > mic2max){
mic2max = mic2val;
mic2sample = i;
//mic2Timer = tim.read_us();
}
if (mic3val > mic3max){
mic3max = mic3val;
mic3sample = i;
//mic3Timer = tim.read_us();
}
}
printf(" mic1max %f; sample %d\n\r mic2max %f; sample %d\n\r mic3max %f; sample %d\n\r",mic1max, mic1sample,mic2max, mic2sample,mic3max, mic3sample);
//printf("Mic 1 time: %f\n\r", mic1Timer / 1000000.0); // timer is in milliseconds
//printf("Mic 2 time: %f\n\r", mic2Timer / 1000000.0);
//printf("Mic 3 time: %f\n\r", mic3Timer / 1000000.0);
//printf("End time: %f\n\r", tim.read());
printf("\n\r");
float k = 4.61; // calibration constant
float diff;
// sound closest to mic1
if (mic1sample <= mic2sample && mic1sample <= mic3sample){
diff = mic2sample - mic3sample;
// if diff < 0, sound is closer to mic 2
degree = k*diff;
if (degree < -60.0){
degree = -60.0;
}
else if (degree > 60){
degree = 60.0;
}
}
// sound closest to mic2
else if (mic2sample <= mic1sample && mic2sample <= mic3sample){
diff = mic3sample - mic1sample;
// if diff < 0, sound is closer to mic 3
degree = k*diff - 120.0;
if (degree < -180.0){
degree = -180.0;
}
else if (degree > -60.0){
degree = -60.0;
}
}
// sound closest to mic3
else {
diff = mic1sample - mic2sample;
// if diff < 0, sound is closer to mic 3
degree = k*diff + 120.0;
if (degree < 60.0){
degree = 60.0;
}
else if (degree > 180.0){
degree = 180.0;
}
}
printf("sample diff: %f, degree: %f",diff,degree);
}
void turnMotors() {
// coordinates (mm)
xCoord = (500.0*cos(degree*pi/180.0));
yCoord = (500.0*sin(degree*pi/180.0));
printf("Start\n\r");
printf("start motor, moving to (%f, %f)\n\r", xCoord, yCoord);
sprintf(cmdbuff, "count,time,analog1,analog2=3,\"%f\",\"%f\",\"%s\"\r\n",xCoord/100.0,yCoord/100.0,"here");
SendCMD();
// convert coordinates to encoder counts
float mmDistToMove = sqrt(xCoord * xCoord + yCoord * yCoord);
encDistToMove = mmDistToMove * mm_2_enc_count;
printf("distance (mm): %f\n\r", mmDistToMove);
// convert coordiantes to heading
float angleRadians = atan2(yCoord, xCoord);
float angleDegrees = angleRadians*180.0/pi;
bool CW = false;
// if angle is between -180 and 0, turn CW
if (angleDegrees < 0){
angleDegrees = -angleDegrees;
CW = true;
printf("angle CW(deg): %f\n\r", angleDegrees);
}
// if angle is between 0 and 180, turn CCW
else{
printf("angle CCW(deg): %f\n\r", angleDegrees);
}
float mmDistToTurn = angleDegrees/(360.0)*c_chassis;
float encDistToTurn = mmDistToTurn * mm_2_enc_count;
wait (0.25);
// Turning
printf("turning: %f\n\r", angleDegrees);
// turn CW or CCW depending on angle
float speedR;
// CW
if (CW){
mL.speed(0.45);
mR.speed(-0.45);
speedR = -0.45;
}
// CCW
else{
mL.speed(-0.45);
mR.speed(0.45);
speedR = 0.45;
}
int leftRead = 0;
int rightRead = 0;
float newspeedR = 0;
EncL.reset();
EncR.reset();
while(leftRead + rightRead < 1.5*encDistToTurn) {
// get encoder values
leftRead = EncL.read();
rightRead = EncR.read();
// use difference to adjust motor speeds
int diff = leftRead - rightRead;
if (CW){
newspeedR = speedR - float(diff / 25.0);
}
else{
newspeedR = speedR + float(diff / 25.0);
}
if (newspeedR > 0.95){
newspeedR = 0.95;
}
printf("%i %f\n\r",diff,newspeedR);
mR.speed(newspeedR);
wait(0.1);
}
// stop motors
mL.speed(0.2);
mR.speed(0.2);
// Reset encoders
EncR.reset();
EncL.reset();
wait(0.5);
}
void moveToDest() {
// initialize speed
mL.speed(0.8);
mR.speed(0.8);
//Encoder Reset
EncR.reset();
EncL.reset();
int speedR = 0.8;
int leftRead = 0;
int rightRead = 0;
pc.printf("\n\rmoveToDest\n\r");
while(leftRead + rightRead < 2*encDistToMove) {
// get encoder values
leftRead = EncL.read();
rightRead = EncR.read();
// use difference to adjust motor speeds
int diff = leftRead - rightRead;
float newspeedR = speedR + float(diff / 35.0);
if (newspeedR > 0.95){
newspeedR = 0.95;
}
mR.speed(newspeedR);
wait(0.1);
}
// Encoder Reset
EncR.reset();
EncL.reset();
// Stop motors
mR.speed(0.2);
mL.speed(0.2);
sprintf(cmdbuff, "count,time,analog1,analog2=5,\"%s\",\"%s\",\"%s\"\r\n","N/A","N/A","here");
SendCMD();
triggerTimeout.attach(&enableTrigger, 5.0);
}
int main()
{
////////////////////////////////////////////////////
// STEP 1: Set up IOT
////////////////////////////////////////////////////
mR.speed(0.2);
mL.speed(0.2);
pc.baud(9600);
esp.baud(9600);
led1=1,led2=0,led3=0, led4=0;
// Setup a serial interrupt function to receive data
esp.attach(&Rx_interrupt, Serial::RxIrq);
// Setup a serial interrupt function to transmit data
esp.attach(&Tx_interrupt, Serial::TxIrq);
if (time(NULL) < 1420070400) {
setRTC();
}
startserver();
DataRX=0;
count=0;
triggerD = false;
led2 = 1;
while(1) {
if(DataRX==1) {
ReadWebData();
esp.attach(&Rx_interrupt, Serial::RxIrq);
}
if(update==1) // update time, hit count, and analog levels in the HUZZAH chip
{
// get new values
gettime();
gettemp();
getbattery();
count++;
// send new values
//count = rand() % 7;
//sprintf(cmdbuff, "count,time,analog1,analog2=5,\"%s\",\"%s\",\"%s\"\r\n","5","-5","here");
//SendCMD();
//getreply();
if (triggerD && listeningForTrigger) {
listeningForTrigger = false;
startMics();
sprintf(cmdbuff, "count,time,analog1,analog2=2,\"%s\",\"%s\",\"%s\"\r\n","N/A","N/A","here");
SendCMD();
turnMotors();
moveToDest();
}
update=0;
}
}
}
// Reads and processes GET and POST web data
void ReadWebData()
{
wait_ms(200);
esp.attach(NULL,Serial::RxIrq);
DataRX=0;
memset(webdata, '\0', sizeof(webdata));
strcpy(webdata, rx_buffer);
memset(rx_buffer, '\0', sizeof(rx_buffer));
rx_in = 0;
rx_out = 0;
pc.printf("WEBDATA_KEVIN____S\n\r");
pc.printf(webdata);
pc.printf("WEBDATA_KEVIN____E\n\r");
// check web data for form information
if( strstr(webdata, "check=led1v") != NULL ) {
led1=!led1;
}
if( strstr(webdata, "check=led2v") != NULL ) {
led2=!led2;
}
if( strstr(webdata, "check=led3v") != NULL ) {
led3=!led3;
}
if( strstr(webdata, "check=led4v") != NULL ) {
led4=!led4;
}
if( strstr(webdata, "POST") != NULL ) { // set update flag if POST request
update=1;
triggerD = true;
}
if( strstr(webdata, "GET") != NULL && strstr(webdata, "favicon") == NULL ) { // set update flag for GET request but do not want to update for favicon requests
update=1;
}
}
// Starts webserver
void startserver()
{
gettime();
gettemp();
getbattery();
pc.printf("++++++++++ Resetting ESP ++++++++++\r\n");
strcpy(cmdbuff,"node.restart()\r\n");
SendCMD();
wait(2);
getreply();
pc.printf("\n++++++++++ Starting Server ++++++++++\r\n> ");
// initial values
sprintf(cmdbuff, "count,time,analog1,analog2=5,\"%s\",\"%s\",\"%s\"\r\n",timebuf,Temp,Vcc);
SendCMD();
getreply();
wait(0.5);
//create server
sprintf(cmdbuff, "srv=net.createServer(net.TCP,%d)\r\n",SERVtimeout);
SendCMD();
getreply();
wait(0.5);
strcpy(cmdbuff,"srv:listen(80,function(conn)\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff,"conn:on(\"receive\",function(conn,payload) \r\n");
SendCMD();
getreply();
wait(0.3);
//print data to mbed
strcpy(cmdbuff,"print(payload)\r\n");
SendCMD();
getreply();
wait(0.2);
//web page data
pc.printf("Opening file..\n\r");
FILE *fp = fopen("/local/index.txt", "r"); // Open "out.txt" on the local file system for writing
char buffer[256];
while(fgets(buffer, 256, fp)) {
strcpy(cmdbuff,buffer);
pc.printf("cmdbuff: %s\n\r", cmdbuff);
SendCMD();
getreply();
wait(0.4);
}
fclose(fp);
pc.printf("File closed\n\r");
/*strcpy(cmdbuff,"conn:send('<!DOCTYPE html><html><body><h1>ESP8266 Mbed IoT Web Controller</h1>')\r\n");
SendCMD();
getreply();
wait(0.4);
strcpy(cmdbuff,"conn:send('Hit count: '..count..'')\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff,"conn:send('<br>Last hit (based on mbed RTC time): '..time..'<br><hr>')\r\n");
SendCMD();
getreply();
wait(0.4);
strcpy(cmdbuff,"conn:send('Analog 1: '..analog1..' V<br>Analog 2: '..analog2..' V<br><hr>')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff,"conn:send('<form method=\"POST\"')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff, "conn:send('<p><input type=\"checkbox\" name=\"check\" value=\"led1v\"> flip LED1')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff, "conn:send('<p><input type=\"text\" name=\"check\" value=\"led2v\"> flip LED2')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff, "conn:send('<p><input type=\"checkbox\" name=\"check\" value=\"led3v\"> flip LED3')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff, "conn:send('<p><input type=\"checkbox\" name=\"check\" value=\"led4v\"> flip LED4')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff,"conn:send('<p><input type=\"submit\" value=\"send-refresh\"></form>')\r\n");
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff, "conn:send('<p><h2>How to use:</h2><ul><li>Select a checkbox to flip on/off</li><li>Click Send-Refresh to send data and refresh values</li></ul></body></html>')\r\n");
SendCMD();
getreply();
wait(0.5); */
// end web page data
strcpy(cmdbuff, "conn:on(\"sent\",function(conn) conn:close() end)\r\n"); // close current connection
SendCMD();
getreply();
wait(0.3);
strcpy(cmdbuff, "end)\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff, "end)\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff, "tmr.alarm(0, 1000, 1, function()\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff, "if wifi.sta.getip() == nil then\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff, "print(\"Connecting to AP...\\n\")\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff, "else\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff, "ip, nm, gw=wifi.sta.getip()\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff,"print(\"IP Address: \",ip)\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff,"tmr.stop(0)\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff,"end\r\n");
SendCMD();
getreply();
wait(0.2);
strcpy(cmdbuff,"end)\r\n");
SendCMD();
getreply();
wait(0.2);
pc.printf("\n\n++++++++++ Ready ++++++++++\r\n\n");
}
// ESP Command data send
void SendCMD()
{
int i;
char temp_char;
bool empty;
i = 0;
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(UART1_IRQn);
empty = (tx_in == tx_out);
while ((i==0) || (cmdbuff[i-1] != '\n')) {
// Wait if buffer full
if (((tx_in + 1) % buffer_size) == tx_out) {
// End Critical Section - need to let interrupt routine empty buffer by sending
NVIC_EnableIRQ(UART1_IRQn);
while (((tx_in + 1) % buffer_size) == tx_out) {
}
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(UART1_IRQn);
}
tx_buffer[tx_in] = cmdbuff[i];
i++;
tx_in = (tx_in + 1) % buffer_size;
}
if (esp.writeable() && (empty)) {
temp_char = tx_buffer[tx_out];
tx_out = (tx_out + 1) % buffer_size;
// Send first character to start tx interrupts, if stopped
esp.putc(temp_char);
}
// End Critical Section
NVIC_EnableIRQ(UART1_IRQn);
return;
}
// Get Command and ESP status replies
void getreply()
{
read_line();
sscanf(rx_line,replybuff);
}
// Read a line from the large rx buffer from rx interrupt routine
void read_line() {
int i;
i = 0;
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(UART1_IRQn);
// Loop reading rx buffer characters until end of line character
while ((i==0) || (rx_line[i-1] != '\r')) {
// Wait if buffer empty
if (rx_in == rx_out) {
// End Critical Section - need to allow rx interrupt to get new characters for buffer
NVIC_EnableIRQ(UART1_IRQn);
while (rx_in == rx_out) {
}
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(UART1_IRQn);
}
rx_line[i] = rx_buffer[rx_out];
i++;
rx_out = (rx_out + 1) % buffer_size;
}
// End Critical Section
NVIC_EnableIRQ(UART1_IRQn);
rx_line[i-1] = 0;
return;
}
// Interupt Routine to read in data from serial port
void Rx_interrupt() {
DataRX=1;
//led3=1;
// Loop just in case more than one character is in UART's receive FIFO buffer
// Stop if buffer full
while ((esp.readable()) && (((rx_in + 1) % buffer_size) != rx_out)) {
rx_buffer[rx_in] = esp.getc();
// Uncomment to Echo to USB serial to watch data flow
pc.putc(rx_buffer[rx_in]);
rx_in = (rx_in + 1) % buffer_size;
}
//led3=0;
return;
}
// Interupt Routine to write out data to serial port
void Tx_interrupt() {
//led2=1;
// Loop to fill more than one character in UART's transmit FIFO buffer
// Stop if buffer empty
while ((esp.writeable()) && (tx_in != tx_out)) {
esp.putc(tx_buffer[tx_out]);
tx_out = (tx_out + 1) % buffer_size;
}
//led2=0;
return;
}
void gettime()
{
time_t seconds = time(NULL);
strftime(timebuf,50,"%H:%M:%S %a %d %b %y", localtime(&seconds));
}
void setRTC()
{
t.tm_sec = (0); // 0-59
t.tm_min = (minute); // 0-59
t.tm_hour = (hour); // 0-23
t.tm_mday = (dayofmonth); // 1-31
t.tm_mon = (month-1); // 0-11 "0" = Jan, -1 added for Mbed RCT clock format
t.tm_year = ((year)+100); // year since 1900, current DCF year + 100 + 1900 = correct year
set_time(mktime(&t)); // set RTC clock
}
// Analog in example
void getbattery()
{
AdcIn=Ain1.read();
Ht = (AdcIn*3.3); // set the numeric to the exact MCU analog reference voltage for greater accuracy
sprintf(Vcc,"%2.3f",Ht);
}
// Temperature example
void gettemp()
{
AdcIn=Ain2.read();
Ht = (AdcIn*3.3); // set the numeric to the exact MCU analog reference voltage for greater accuracy
sprintf(Temp,"%2.3f",Ht);
}
Have this labeled as index.txt in the mbed local file system. IoT chip should be configured to a network prior to running the code.
index.txt
conn:send('<!DOCTYPE html>')
conn:send('<html>')
conn:send('<head>')
conn:send('<title>ECE 4180 - Blind Dog</title>')
conn:send('</head>')
conn:send('<body>')
conn:send('<style>')
conn:send('h3 { visibility: hidden; }')
conn:send('</style>')
conn:send('<h1>ECE 4180 - Blind Dog</h1>')
conn:send('<h3>Status code: <p id="inStat">'..count..'</p></h3>')
conn:send('<br>X coord: <p id="x">'..time..'</p><br><hr>')
conn:send('Y coord: <p id="y">'..analog1..'</p><br><h3>Analog 2: '..analog2..' V</h3><br><hr>')
conn:send('')
conn:send('<h2>Status:</h2>')
conn:send('<h2 id="status">Waiting for trigger...</h2>')
conn:send('<form method="POST">')
conn:send('<button id="button">GO!</button>')
conn:send('</form>')
conn:send('<canvas id="myCanvas" width="600" height="400" style="border:1px solid #000000;">')
conn:send('</canvas>')
conn:send('</body>')
conn:send('<script src="http://yourjavascript.com/30312385242/script.js"></script>')
conn:send('</html>')
Please log in to post comments.
