Bluetooth app controlled robot
Dependencies: ESP8266 HALLFX_ENCODER LSM9DS1_Library_cal MotorDriver PID mbed
Fork of ESP8266_pid_redbot_webserver by
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)); }