Wifi Controlled Robot is done as a mini project (Lab4) for ECE 4180 class (Georgia Tech). Robot is assembled in Sparkfun's Shadow chasis robot kit. This robot takes the command from the webpage to move forward, reverse without colliding. The distance sensor is used as a mechanism for collision detection and play a short beep sound. It turn 90 degree with magnetometer when the turn command is sent.

Dependencies:   Motordriver mbed

/media/uploads/pkoirala3/capture.jpg Showing the setup and calibration: Part1 In this part the Wifi setups and attains an IP address so we can control it through a webpage on our phone/PC. After it obtains an IP address it calibrates the IMU so we can have accurate compass headings for precise turns.

The parts that we use are as follows:

  • Mbed
  • 2 DC motors
  • H-Bridge (to drive the DC motors)
  • Speaker
  • Class D Amp (to drive the Speaker)
  • IMU (use the magnometer for compass headings)
  • Wifi card (ESP8266)

Showing webpage functionality and Robot Functionality: In this part we demonstrate the functionality of the robot and the webpage to control it.

Final Code

[Repository '/teams/Prana-Koirala/code/Wifi_controlled_Robot_ECE4180/docs/tip/main_8cpp_source.html' not found]

main.cpp

Committer:
pkoirala3
Date:
2017-03-12
Revision:
0:553fab92a347
Child:
1:bbe16318d747

File content as of revision 0:553fab92a347:

#include "mbed.h"
#include "motordriver.h"
#include "ultrasonic.h"

DigitalOut  ledFwd(p8);
DigitalOut  ledRev(p9);

Motor A(p26, p16, p25, 1); // pwm, fwd, rev, can brake
Motor B(p21, p22, p23, 1); // pwm, fwd, rev, can brake

Serial pc(USBTX, USBRX);
Serial esp(p28, p27); // tx, rx

PwmOut mySpeaker(p24);

void dist(int);
ultrasonic mu(p6, p7, .5, 2, &dist); //Check every .5 sec, timeout 1 sec,call dist when the distance changes

void dist(int distance)
{
    if(distance < 1000) { // Distance is in the unit of cm
        pc.printf("stop by sonar \r\n");
        A.stop(0.5);
        B.stop(0.5);
        ledFwd = 0;
        mySpeaker = 0.5; // Play Beep sound
    }
}

// some test values to show on web page
AnalogIn DistSensor(p20);
AnalogIn   Ain1(p18);
AnalogIn   Ain2(p19);

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;

int main()
{
    pc.baud(9600);
    esp.baud(9600);
    ledFwd = 1;
    // 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);
    startserver();
    wait(0.01);
    mu.startUpdates();   //start measuring the distance in sonar
    DataRX=0;
    count=0;
    mySpeaker.period(1.0/500.0); // 500hz period
    while(1) {
        if(DataRX==1) {
            ReadWebData();
            esp.attach(&Rx_interrupt, Serial::RxIrq);
        }
        float stateA = A.state();
        float stateB = B.state();
        if((stateA > 0 && stateA <= 1)&&(stateB > 0 && stateB <= 1)) { // obstacle within 10 cm and robot is still moving
            mu.checkDistance();
        } else if((stateA < 0 && stateA >= -1)&&(stateB < 0 && stateB >= -1)) { // If moving rev
            float currDist = DistSensor;
            pc.printf("currDist: %f",currDist);
            if(currDist <= 0.5f) {
                A.stop(0.5);
                B.stop(0.5);
                ledRev = 0;
                mySpeaker = 0.5; // Play Beep sound

            }
        }
    }
}

// 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;
    // check web data for form information
    if( strstr(webdata, "check=BotFwd") != NULL ) {
        A.speed(0.75);
        B.speed(0.75);
        ledRev = 0;
        ledFwd = 1;
    }
    if( strstr(webdata, "check=BotRev") != NULL ) {
        A.speed(-0.75);
        B.speed(-0.75);
        ledFwd = 0;
        ledRev = 1;
    }
    if( strstr(webdata, "check=BotStop") != NULL ) {
        A.stop(0.5);
        B.stop(0.5);
        ledFwd = 0;
        ledRev = 0;
    }
    if( strstr(webdata, "check=BotLeft") != NULL ) {
        B.stop(0.5);
        A.speed(0.5);

    }
    if( strstr(webdata, "check=BotRight") != NULL ) {
        A.stop(0.5);
        B.speed(0.5);
    }
    if( strstr(webdata, "POST") != NULL ) { // set update flag if POST request
        update=1;
    }
    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()
{
    pc.printf("Resetting ESP Device....\r\n");
    strcpy(cmdbuff,"node.restart()\r\n");
    SendCMD();
    wait(2);
    getreply();

    pc.printf("\nStarting Server.....\r\n> ");
    //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
    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('<form method=\"POST\"')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff, "conn:send('<p><input type=\"radio\" name=\"check\" value=\"BotFwd\"> Move Robot Forward')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff, "conn:send('<p><input type=\"radio\" name=\"check\" value=\"BotRev\"> Move Robot Reverse')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff, "conn:send('<p><input type=\"radio\" name=\"check\" value=\"BotStop\"> Stop Robot')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff, "conn:send('<p><input type=\"radio\" name=\"check\" value=\"BotLeft\"> Turn Robot Left')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff, "conn:send('<p><input type=\"radio\" name=\"check\" value=\"BotRight\"> Turn Robot Right')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff,"conn:send('<p><input type=\"submit\" value=\"Send to Robot\"></form>')\r\n");
    SendCMD();
    getreply();
    wait(0.3);
    strcpy(cmdbuff, "conn:send('<p><h2>How to use:</h2><ul><li>Select a radiobutton </li><li>Click Send to Robot button</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\nReady to go .....\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;
// 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;
    }
    return;
}

// Interupt Routine to write out data to serial port
void Tx_interrupt()
{
// 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;
    }
    return;
}