Bluetooth app controlled robot

Dependencies:   ESP8266 HALLFX_ENCODER LSM9DS1_Library_cal MotorDriver PID mbed

Fork of ESP8266_pid_redbot_webserver by Bryce Williams

main.cpp

Committer:
ihl396
Date:
2016-03-16
Revision:
3:bf6e71964ceb
Parent:
2:3466e9e16c99

File content as of revision 3:bf6e71964ceb:

#include "mbed.h"
#include "PID.h"
#include "HALLFX_ENCODER.h"
#include "MotorDriver.h"
#include "LSM9DS1.h"
#include <algorithm>
#define PI 3.14159
#define DECLINATION -4.94 // Declination (degrees) in Atlanta,GA.
#define REFRESH_TIME_MS 1
#define DEBUG

Serial blue(p28,p27);
BusOut myled(LED1,LED2,LED3,LED4);
Serial pc(USBTX, USBRX);

// -------- PID Initialization -----------
float kp, ki, kd;                               // Working gain vars
float working_setpoint;                         // Used for web parsing and updating
float setpoint;                                 // This is used by PID objects, because 
                                                // encoders are not quadrature and 
                                                // do not have direction information
                                                // we use this to store the absolute
                                                // value of the working_setpoint.
                                                // Specifically setpoint is used only
                                                // for PID objects. Refer to 
                                                // pid_callback() function
float feedbackL, outputL;                       // Should these be volatile?
float feedbackR, outputR;                       // Should these be volatile? 
const float output_lower_limit = 0.0;          
const float output_upper_limit = 1.0;
const float FEEDBACK_SCALE = 1.0/384.0;         // Scale feedback to 1rev/3000cnts
                                                // this is encoder specific.
const float Ts = 0.04;                         // 25Hz Sample Freq (40ms Sample Time)
const float Ts_PID_CALLBACK = Ts/2.0;          // Update Motors and sensers twice as 
                                               // fast as PID sample rate, ensures
                                               // PID feedback is upto date every 
                                               // time PID calculations run

const float kp_init = 0.01;        // Good Kp for Speed Control; Start with this
const float ki_init= 0.015;        // Good Ki for Speed Control; Start with this
const float kd_init = 0.0001;      // Good Kd for Speed Control; Start with this

PID pidL(&setpoint, &feedbackL, &outputL,
        output_lower_limit, output_upper_limit,
        kp_init, ki_init, kd_init, Ts); 
PID pidR(&setpoint, &feedbackR, &outputR,
        output_lower_limit, output_upper_limit, 
        kp_init, ki_init, kd_init, Ts);
MotorDriver mtrR(p20, p19, p25, 10000.0, true); // in1, in2, pwm, pwmFreq, isBrakeable
MotorDriver mtrL(p18, p17, p24, 10000.0, true); // in1, in2, pwm, pwmFreq, isBrakeable
HALLFX_ENCODER encR(p21);
HALLFX_ENCODER encL(p22);
void pid_callback();            // Updates encoder feedback and motor output
        
Ticker motor;                   // Interrupt for feedback and motor updates
//global variables for main and interrupt routine

char state_num = 0, bnum =0 ;
int dirL = 0, dirR = 0;

//Interrupt routine to parse message with one new character per serial RX interrupt

float clip(float value, float lower, float upper);

LSM9DS1 imu(p9, p10, 0xD6, 0x3C);
void calculate_heading(float mx, float my, float mz);
float oldX = 0;
float oldY = 0;
float oldZ = 0;
float x= 0;
float y = 0;
float z  = 0;

float posx = 0;
float velx = 0;
float oldPosx = 0;
float oldVelx = 0;

float posy = 0;
float vely = 0;
float oldPosy = 0;
float oldVely = 0;

float posz = 0;
float velz = 0;
float oldPosz = 0;
float oldVelz = 0;
int sample_time = 0;

float accelx = 0, accely = 0, accelz = 0;
Serial esp(p13, p14);           // tx, rx
DigitalOut reset(p26);

Timer t;
void send_data(float *accelx, float *accely, float *accelz, float *velx, float *vely, float *velz);
int  count,ended,timeout;
float heading = 0.0;
char buf[1024];
char snd[1024];
char str_buf[80];
 
char ssid[32] = "4180_test";     // enter WiFi router ssid inside the quotes
char pwd [32] = "4180isawesome"; // enter WiFi router password inside the quotes
 
void SendCMD(),getreply(),ESPconfig(),ESPsetbaudrate();
int main()
{
    pc.printf("\f---------- Program Start ----------\r\n\n");
    reset=0; //hardware reset for 8266
    pc.baud(9600);  // set what you want here depending on your terminal program speed
    pc.printf("\f\n\r-------------ESP8266 Hardware Reset-------------\n\r");
    wait(0.5);
    reset=1;
    timeout=2;
    getreply();
    
    ESPconfig();        //******************  include Config to set the ESP8266 configuration  ***********************

    imu.begin();
    if (!imu.begin()) {
        pc.printf("Failed to communicate with LSM9DS1.\n");
    }
    imu.calibrate(1);
    imu.calibrateMag(0);

    working_setpoint = 0.0;
    
    encL.reset();
    encR.reset();
    feedbackL = encL.read();
    feedbackR = encR.read();
     
    // Update sensors and feedback twice as fast as PID sample time
    // this makes pid react in real-time avoiding errors due to 
    // missing counts etc. 
    
    motor.attach(&pid_callback, Ts_PID_CALLBACK);

    // Start PID sampling
    pidL.start();
    pidR.start();
    working_setpoint = 0;
    dirL = 1;
    dirR = 1;
    while(1)
    {
        if(blue.readable())
        {
            if (blue.getc()=='!')
            {
                if (blue.getc()=='B') //button data
                {
                    bnum = blue.getc(); //button number
                    if(bnum=='1') {
                        working_setpoint = 50;
                        sample_time = 1;
                        pc.printf("setpoint: %2.0f\r\n", working_setpoint);
                    }
                }
            }
        } 
        while(!imu.accelAvailable());
        imu.readAccel();
        //while(!imu.magAvailable());
        imu.readMag();
        
        if( sample_time % 800 == 0)
        {
            velx = oldVelx + REFRESH_TIME_MS * oldX/1000;     
            posx = oldPosx + REFRESH_TIME_MS * oldVelx/1000;
            
            vely = oldVely + REFRESH_TIME_MS * oldY/1000;     
            posy = oldPosy + REFRESH_TIME_MS * oldVely/1000;
                        
            accelx = imu.calcAccel(imu.ax);
            accely = imu.calcAccel(imu.ay);
            accelz = imu.calcAccel(imu.az);
            calculate_heading(imu.calcMag(imu.mx), imu.calcMag(imu.my), imu.calcMag(imu.mz)); 
         
            #ifdef DEBUG
            pc.printf("        X axis    Y axis    Z axis\n\r");
            pc.printf("accel: %2.2f %2.2f %2.2f in Gs\r\n", imu.calcAccel(imu.ax), imu.calcAccel(imu.ay), imu.calcAccel(imu.az));
            pc.printf("Veloc: %2.2f %2.2f %2.2f in G*s\r\n", velx, vely, velz);
            pc.printf("Magnetic heading: %2.2f", heading);
            #endif
           
            oldVelx = velx;
            oldPosx = posx;
            oldVely = vely;
            oldPosy = posy;
            oldX = imu.ax;
            oldY = imu.ay;
        }
        if(working_setpoint > 0)
        {
            if(sample_time % 1500 == 0)
            {   
                #ifdef DEBUG
                pc.printf("Pushing data to server\r\n");
                #endif
                
                myled[0] = !myled[0];
                working_setpoint = 0;
    
                strcpy(snd, "srv:listen(80,function(conn)\r\n");
                SendCMD();
                wait(1);
                strcpy(snd, "conn:on(\"receive\",function(conn,payload)\r\n");
                SendCMD();
                wait(1);
                strcpy(snd, "print(payload)\r\n");
                SendCMD();
                wait(1);
                strcpy(snd, "conn:send(\"<!DOCTYPE html>\")\r\n");
                SendCMD();
                wait(1);
                
                strcpy(snd, "conn:send(\"<html>\")\r\n");
                SendCMD();
                wait(1);
                sprintf(str_buf,"%2.2f",velx);
                strcpy(snd, "conn:send(\"<h1>Velocity x: ");
                strcat(snd, str_buf);
                strcat(snd, "</h1>\")\r\n");
                SendCMD();
                wait(1);
                
                sprintf(str_buf,"%2.2f",vely);
                strcpy(snd, "conn:send(\"<h1>Velocity y: ");
                strcat(snd, str_buf);
                strcat(snd, "</h1>\")\r\n");
                SendCMD();
                wait(1);
                
                sprintf(str_buf,"%2.2f",heading);
                strcpy(snd, "conn:send(\"<h1>Magnetic Heading: ");
                strcat(snd, str_buf);
                strcat(snd, "</h1>\")\r\n");
                SendCMD();
                wait(1);
                
                strcpy(snd, "conn:send(\"</html>\")\r\n");
                SendCMD();
                wait(1);
                
                strcpy(snd, "end)\r\n");
                SendCMD();
                wait(1);
                
                strcpy(snd, "conn:on(\"sent\",function(conn) conn:close() end)\r\n");
                SendCMD();
                wait(1);
                strcpy(snd, "end)\r\n");
                SendCMD();
                wait(1);
                timeout=17;
                getreply();
                pc.printf(buf);
                pc.printf("\r\nDONE");
            }
            if(sample_time > 100000)
            {
                sample_time = 1;
            } 
            sample_time++;
        }
    }
}

void ESPconfig()
{
    wait(5);
    pc.printf("\f---------- Starting ESP Config ----------\r\n\n");
    strcpy(snd,".\r\n.\r\n");
    SendCMD();
    wait(1);
    pc.printf("---------- Reset & get Firmware ----------\r\n");
    strcpy(snd,"node.restart()\r\n");
    SendCMD();
    timeout=5;
    getreply();
    pc.printf(buf);
 
    wait(2);
 
    pc.printf("\n---------- Get Version ----------\r\n");
    strcpy(snd,"print(node.info())\r\n");
    SendCMD();
    timeout=4;
    getreply();
    pc.printf(buf);
    wait(3);
 
    // set CWMODE to 1=Station,2=AP,3=BOTH, default mode 1 (Station)
    pc.printf("\n---------- Setting Mode ----------\r\n");
    strcpy(snd, "wifi.setmode(wifi.STATION)\r\n");
    SendCMD();
    timeout=4;
    getreply();
    pc.printf(buf);
 
    wait(2);

    pc.printf("\n---------- Connecting to AP ----------\r\n");
    pc.printf("ssid = %s   pwd = %s\r\n",ssid,pwd);
    strcpy(snd, "wifi.sta.config(\"");
    strcat(snd, ssid);
    strcat(snd, "\",\"");
    strcat(snd, pwd);
    strcat(snd, "\")\r\n");
    SendCMD();
    timeout=10;
    getreply();
    pc.printf(buf);
 
    wait(5);
 
    pc.printf("\n---------- Get IP's ----------\r\n");
    strcpy(snd, "print(wifi.sta.getip())\r\n");
    SendCMD();
    timeout=3;
    getreply();
    pc.printf(buf);
 
    wait(1);
 
    pc.printf("\n---------- Get Connection Status ----------\r\n");
    strcpy(snd, "print(wifi.sta.status())\r\n");
    SendCMD();
    timeout=5;
    getreply();
    pc.printf(buf);
      
    pc.printf("\n---------- Setting up http server ----------\r\n");
    strcpy(snd, "srv=net.createServer(net.TCP)\r\n");
    SendCMD();
    wait(1);
    strcpy(snd, "srv:listen(80,function(conn)\r\n");
    SendCMD();
    wait(1);
    strcpy(snd, "conn:on(\"receive\",function(conn,payload)\r\n");
    SendCMD();
    wait(1);
    strcpy(snd, "print(payload)\r\n");
    SendCMD();
    wait(1);
    strcpy(snd, "conn:send(\"<!DOCTYPE html>\")\r\n");
    SendCMD();
    wait(1);
    
    strcpy(snd, "conn:send(\"<html>\")\r\n");
    SendCMD();
    wait(1);
    
    strcpy(snd, "conn:send(\"<h1>WIFI Monitoring Program</h1>\")\r\n");
    SendCMD();
    wait(1);
    
    strcpy(snd, "conn:send(\"</html>\")\r\n");
    SendCMD();
    wait(1);
    
    strcpy(snd, "end)\r\n");
    SendCMD();
    wait(1);
    
    strcpy(snd, "conn:on(\"sent\",function(conn) conn:close() end)\r\n");
    SendCMD();
    wait(1);
    strcpy(snd, "end)\r\n");
    SendCMD();
    wait(1);
    timeout=17;
    getreply();
    pc.printf(buf);
    pc.printf("\r\nDONE");

}

void SendCMD()
{
    esp.printf("%s", snd);
}
 
void getreply()
{
    memset(buf, '\0', sizeof(buf));
    t.start();
    ended=0;
    int count=0;
    while(!ended) {
        if(esp.readable()) {
            buf[count] = esp.getc();
            count++;
        }
        if(t.read() > timeout) {
            ended = 1;
            t.stop();
            t.reset();
        }
    }
}
void calculate_heading(float mx, float my, float mz)
{

// touchy trig stuff to use arctan to get compass heading (scale is 0..360)
    mx = -mx;
    if (my == 0.0)
        heading = (mx < 0.0) ? 180.0 : 0.0;
    else
        heading = atan2(mx, my)*360.0/(2.0*PI);
    heading -= DECLINATION; //correct for geo location
    if(heading>180.0) heading = heading - 360.0;
    else if(heading<-180.0) heading = 360.0 + heading;
    else if(heading<0.0) heading = 360.0  + heading;
    
    #ifdef DEBUG
    pc.printf("Magnetic Heading: %f degress\n\r",heading);
    #endif
}

void pid_callback()
{

    // Deal with feedback and update motors
    // Motor direction based on working setpoint var

    //myled[0] = dirL;
    //myled[1] = dirR;
    // Setpoint vars used by PID objects are concerned with 
    // only SPEED not direction. 
    setpoint = abs(working_setpoint);

    float k = Ts_PID_CALLBACK;    // Discrete time, (Ts/2 because this callback is called
                                  // at interval of Ts/2... or twice as fast as pid controller)
    static int last_count_L = 0;
    static int last_count_R = 0;
    int countL = encL.read();
    int countR = encR.read();
    //pc.printf("%d\r\n", countL);
    
    // Because encoders are not quadrature we must handle the sign ourselves, 
    // i.e. explicitly make calcs based on the direction we have set the motor
    float raw_speed_L = ((countL - last_count_L)*FEEDBACK_SCALE) / k; 
    float rpm_speed_L = raw_speed_L * 60.0;     // Convert speed to RPM
    
    float raw_speed_R = ((countR - last_count_R)*FEEDBACK_SCALE) / k; 
    float rpm_speed_R = raw_speed_R * 60.0;     // Convert speed to RPM
    
    last_count_L = countL;                      // Save last count
    last_count_R = countR;
    feedbackL = rpm_speed_L;
    feedbackR = rpm_speed_R;
    
    mtrL.forceSetSpeed(outputL * dirL);
    mtrR.forceSetSpeed(outputR * dirR);
}

/*
    Clips value to lower/ uppper
    @param value    The value to clip
    @param lower    The mininum allowable value
    @param upper    The maximum allowable value
    @return         The resulting clipped value
*/
float clip(float value, float lower, float upper){
    return std::max(lower, std::min(value, upper));
}