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.