WiFi Webserver Robot with PID Motor Control

Dependencies:   HALLFX_ENCODER MotorDriver PID SDFileSystem mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002     Uses the ESP8266 WiFi Chip to set up a WiFi Webserver used to control 
00003     the speed of a Sparkfun Redbot Rotbot. USE FIREFOX 
00004     Web Browser
00005 
00006     NOTES: 
00007     1. Webpage Handling in this program is specific to a CUSTOM 
00008     WEBPAGE. Program must be modified to handle specfically a new 
00009     webpage. A copy of the webpage for this program can be found at 
00010     the end of this program page. Simply copy and past text into a
00011     html file and save as the given name.
00012     
00013     2. Developed and tested with FireFox 42.0 Web Browser. Does not seem to work
00014     well with Google Chrome or Internet Explorer for some reason... they seem 
00015     to generate two post requests which messes with the user input values. 
00016     
00017     3. There are a bunch of printf statements in the code that can be
00018     uncommented for debugging in a serial terminal progrom. 
00019 
00020     
00021     TODO: ESP8366 has a max packet send size. Make sure we implement
00022     a method to send webpages that exceed this value. The max size is
00023     listed in the official ESP8266 AT Commands Documentation, I think
00024     it is 2048 bytes/chars
00025     
00026     TODO: CREATE CONFIG FUNCTION TO SET SSID, PASSWORD, BAUDRATE ETC.
00027     Perhaps have a serial terminal method to take user input, and 
00028     put the function call into a #ifdef WiFiConfig statement, so 
00029     that the user can enable it to config Wifi module then turn 
00030     it off once Wifi module is configed so that this program can 
00031     run in a "stand alone" mode. 
00032     
00033     Reference/ Sources: This code is based on 
00034     https://developer.mbed.org/users/4180_1/notebook/using-the-esp8266-with-the-mbed-lpc1768/
00035 */
00036 
00037 #include "mbed.h"
00038 #include "SDFileSystem.h"
00039 #include "PID.h"
00040 #include "HALLFX_ENCODER.h"
00041 #include "MotorDriver.h"
00042 #include <algorithm>
00043 
00044 //#define DEBUG   // Uncomment for serial terminal print debugging
00045                 // Comment out to turn prints off for normal op
00046 
00047 /*********PID CONTROLLER SPECIFIC DECLARATIONS********************************/
00048 /*****************************************************************************/
00049 float kp, ki, kd;                               // Working gain vars
00050 float working_setpoint;                         // Used for web parsing and updating
00051 float setpoint;                                 // This is used by PID objects, because 
00052                                                 // encoders are not quadrature and 
00053                                                 // do not have direction information
00054                                                 // we use this to store the absolute
00055                                                 // value of the working_setpoint.
00056                                                 // Specifically setpoint is used only
00057                                                 // for PID objects. Refer to 
00058                                                 // pid_callback() function
00059 float feedbackL, outputL;                       // Should these be volatile?
00060 float feedbackR, outputR;                       // Should these be volatile? 
00061 const float output_lower_limit = 0.0;          
00062 const float output_upper_limit = 1.0;
00063 const float FEEDBACK_SCALE = 1.0/384.0;         // Scale feedback to 1rev/3000cnts
00064                                                 // this is encoder specific.
00065 enum CONTROL_MODE{PID_OFF = 0, PID_ON = 1};
00066 int control_mode = PID_ON;
00067 const float Ts = 0.04;                         // 25Hz Sample Freq (40ms Sample Time)
00068 const float Ts_PID_CALLBACK = Ts/2.0;          // Update Motors and sensers twice as 
00069                                                // fast as PID sample rate, ensures
00070                                                // PID feedback is upto date every 
00071                                                // time PID calculations run
00072 
00073 const float kp_init = 0.01;        // Good Kp for Speed Control; Start with this
00074 const float ki_init= 0.015;        // Good Ki for Speed Control; Start with this
00075 const float kd_init = 0.0001;      // Good Kd for Speed Control; Start with this
00076 
00077 PID pidL(&setpoint, &feedbackL, &outputL,
00078         output_lower_limit, output_upper_limit,
00079         kp_init, ki_init, kd_init, Ts); 
00080 PID pidR(&setpoint, &feedbackR, &outputR,
00081         output_lower_limit, output_upper_limit, 
00082         kp_init, ki_init, kd_init, Ts);
00083 MotorDriver mtrR(p20, p19, p25, 10000.0, true); // in1, in2, pwm, pwmFreq, isBrakeable
00084 MotorDriver mtrL(p18, p17, p24, 10000.0, true); // in1, in2, pwm, pwmFreq, isBrakeable
00085 HALLFX_ENCODER encR(p21);
00086 HALLFX_ENCODER encL(p22);
00087 void pid_callback();            // Updates encoder feedback and motor output
00088 Ticker motor;                   // Interrupt for feedback and motor updates
00089 /*****************************************************************************/
00090 /*****************************************************************************/                
00091 
00092 /**********WEB SERVER SPECIFIC DECLARTATIONS**********************************/
00093 /*****************************************************************************/
00094 SDFileSystem sd(p5,p6,p7,p8,"sd");  // MOSI, MISO, SCLK, CS, 
00095                                     // Virtual File System Name
00096 Serial esp(p13, p14);           // tx, rx
00097 DigitalOut  espRstPin(p26);     // ESP Reset
00098 DigitalOut led(LED4);
00099 
00100 Timer t1;
00101 Timer t2;
00102 
00103 void init(char* buffer, int size);
00104 void getreply(int timeout_ms, char* buffer, int size, int numBytes);
00105 void startserver(char* buffer, int size);
00106 void update_webpage(char* webpage, float working_setpoint, float kp, float ki, float kd);
00107 void parse_input(char* webpage_user_data, int* control_mode, float* working_setpoint, float* kp, float* ki, float* kd);
00108 int port        =80;         // set server port
00109 int serverTimeout_secs =5;   // set server timeout in seconds in case 
00110                              // link breaks.
00111 /*****************************************************************************/
00112 /*****************************************************************************/
00113 
00114 // Common Application Declarations
00115 Serial pc(USBTX, USBRX);
00116 float clip(float value, float lower, float upper);
00117 
00118 int main()
00119 {
00120     printf("Starting\n");
00121     
00122     /****************** Load Webpage from SD Card***************************************/
00123     /***********************************************************************************/
00124     char file[] = "/sd/pid_bot.html";
00125     
00126     // Get file size so we can dynamically allocate buffer size 
00127     int num_chars = 0;
00128     FILE *fp = fopen(file, "r");
00129     while(!feof(fp)){
00130         fgetc(fp);
00131         num_chars++;
00132     } 
00133     rewind(fp);                     // Go to beginning of file
00134 
00135     #ifdef DEBUG
00136     printf("Webpage Data Size: %d byte\r\n", num_chars);
00137     #endif
00138 
00139     const int WEBPAGE_SIZE = num_chars;
00140     char webpage[WEBPAGE_SIZE];                   
00141     webpage[0] = NULL;               // Init our array so that element zero contains a null
00142                                     // This is important, ensures strings are placed into
00143                                     // buffer starting at element 0... not some random 
00144                                     // elment                                           
00145     // Read in and buffer file to memory
00146     if(fp == NULL){
00147          printf("Error: No Such File or something :(");
00148          return 1;
00149     }
00150     else{
00151         while(!feof(fp)){
00152             fgets(webpage + strlen(webpage), WEBPAGE_SIZE, fp);  // Get a string from stream, add to buffer
00153         }
00154     }
00155     fclose(fp);
00156     printf("Webpage Buffer Size: %d bytes\r\n", sizeof(webpage));
00157     update_webpage(webpage, working_setpoint, kp_init, ki_init, kd_init);  // Update Webpage for 
00158                                                                    // Position Mode   
00159     /***********************************************************************************/
00160     /***********************************************************************************/
00161     
00162     /***************BRING UP SERVER*****************************************************/
00163     /***********************************************************************************/
00164     char buff[5000];                // Working buffer
00165     init(buff, sizeof(buff));       // Init ESP8266
00166     
00167     esp.baud(115200);               // ESP8266 baudrate. Maximum on KLxx' is 115200, 230400 works on K20 and K22F
00168   
00169     startserver(buff, sizeof(buff));      // Configure the ESP8266 and Setup as Server
00170 
00171     printf(buff);                   // If start successful buff contains IP address...
00172                                     // if not if contains an error.
00173     /***********************************************************************************/
00174     /***********************************************************************************/
00175     
00176     /************Initialize the PID*****************************************************/
00177     /***********************************************************************************/
00178     working_setpoint = 0.0;
00179     encL.reset();
00180     encR.reset();
00181     feedbackL = encL.read();
00182     feedbackR = encR.read();
00183      
00184     // Update sensors and feedback twice as fast as PID sample time
00185     // this makes pid react in real-time avoiding errors due to 
00186     // missing counts etc. 
00187     motor.attach(&pid_callback, Ts_PID_CALLBACK);
00188     
00189     // Start PID sampling
00190     pidL.start();
00191     pidR.start();
00192     
00193     /***********************************************************************************/
00194     /***********************************************************************************/
00195     
00196     while(1){
00197         /**************SERVICE WEBPAGE******************************************************/
00198         /***********************************************************************************/
00199         if(esp.readable()){
00200             getreply(500, buff, sizeof(buff), sizeof(buff) -1); // Get full buff, leave last element for null char
00201             #ifdef DEBUG
00202             printf("\r\n*************WORKING BUFFER******************************\r\n");
00203             printf(buff); printf("\n");
00204             printf("\r\n**************END WORKING BUFFER**************************\r\n");
00205             #endif            
00206             // If Recieved Data get ID, Length, and Data
00207             char* rqstPnt = strstr(buff, "+IPD");
00208             if(rqstPnt != NULL){
00209                 int id, len;
00210                 char type[10]; memset(type, '\0', sizeof(type)); // Create and null out data buff
00211                 sscanf(rqstPnt, "+IPD,%d,%d:%s ", &id, &len, type);
00212                 #ifdef DEBUG
00213                 printf("ID: %i\nLen: %i\nType: %s\n", id, len, type);
00214                 #endif                
00215                 // If GET or POST request "type" parse and update user input then send webpage
00216                 if(strstr(type, "GET") != NULL || strstr(type, "POST") != NULL){
00217                     #ifdef DEBUG
00218                     printf("I got web request\n");
00219                     #endif                   
00220                     /* Read Webpage <Form> data sent using "method=POST"...
00221                        Note: Input elements in the <Form> need a set name attribute to 
00222                        appear in the returned HTML body. Thus to "POST" data ensure:
00223                        <Form method="POST"> <input type="xxx" name="xxx" value="xxx"> 
00224                        <input type="xxx" value="xxx"> </Form>
00225                        Only the input with name="xxx" will appear in body of HTML
00226                     */
00227                     #ifdef DEBUG
00228                     printf("\r\n*************USER INPUT**********************************\r\n");
00229                     #endif
00230                     
00231                     parse_input(buff, &control_mode, &working_setpoint, &kp, &ki, &kd);
00232                     working_setpoint = clip(working_setpoint, -90.0, 90.0); // Redbot motors are ~90 RPM; max html field size for setpoint is 7 (to accomodate for a "-" sign
00233                     kp = clip(kp, 0.00, 999.99); // 999.99 is max size that can be updated to webpage, i.e. field is 6 digits (see html)
00234                     ki = clip(ki, 0.00, 999.99); // 999.99 is max size that can be updated to webpage, i.e. field is 6 digits (see html)
00235                     kd = clip(kd, 0.00, 999.99); // 999.99 is max size that can be updated to webpage, i.e. field is 6 digits (see html)
00236                     
00237                     #ifdef DEBUG
00238                     printf("User Entered: \ncontrol_mode: %i\nSetpoint: %7.4f\nKp: %6.4f\nKi: %6.4f\nKd: %6.4f\n", 
00239                             control_mode, working_setpoint, kp, ki, kd);
00240                     #endif                    
00241                      
00242                     pidL.set_parameters(kp, ki, kd, Ts);    // Updata PID params 
00243                     pidR.set_parameters(kp, ki, kd, Ts);    // Updata PID params 
00244                     
00245                     #ifdef DEBUG
00246                     printf("Updated to Kp: %1.4f Ki: %1.4f Kd: %1.4f Ts: %1.4f\r\n",
00247                             pidL.getKp(), pidL.getKi(), pidL.getKd(), pidL.getTs());
00248                     printf("Setpoint: %1.4f\r\n", working_setpoint);
00249                     printf("\r\n*************END USER INPUT******************************\r\n");
00250                     #endif
00251                                         
00252                     // Update Webpage to reflect new values POSTED by client
00253                     static bool isFirstRequest = true;
00254                     if(!isFirstRequest) update_webpage(webpage, working_setpoint, kp, ki, kd);
00255                     else isFirstRequest = false; // First Request just send page with initial values
00256                     
00257                     #ifdef DEBUG
00258                     printf(webpage); 
00259                     #endif
00260                     
00261                     // Command TCP/IP Data Tx
00262                     esp.printf("AT+CIPSEND=%d,%d\r\n", id, strlen(webpage));
00263                     getreply(200, buff, sizeof(buff), 15); /*TODO: Wait for "OK\r\n>"*/
00264                     
00265                     #ifdef DEBUG
00266                     printf(buff); printf("\n");
00267                     #endif
00268                     
00269                     // Send webpage
00270 //                    while(!esp.writeable());         // Wait until esp ready to send data
00271                     int idx = 0;
00272                     while(webpage[idx] != '\0'){
00273                         esp.putc(webpage[idx]);
00274                         idx++;
00275                     }
00276                    
00277                     // Check status - Success: close channel and update PID controller, Error: reconnect 
00278                     bool weberror = true;
00279                     t2.reset(); t2.start();
00280                     while(weberror ==1 && t2.read_ms() < 5000){
00281                         getreply(500, buff, sizeof(buff), 24);
00282                         if(strstr(buff, "SEND OK") != NULL) weberror = false;
00283                     }
00284                     if(weberror){
00285                         esp.printf("AT+CIPMUX=1\r\n");
00286                         getreply(500, buff, sizeof(buff), 10);
00287                         #ifdef DEBUG
00288                         printf(buff); printf("\n");
00289                         #endif
00290                         esp.printf("AT+CIPSERVER=1,%d\r\n", port);
00291                         getreply(500, buff, sizeof(buff), 10);
00292                         #ifdef DEBUG
00293                         printf(buff); printf("\n");
00294                         #endif
00295                     }
00296                     else{
00297                         esp.printf("AT+CIPCLOSE=%d\r\n", id);  // Notice id is an int formatted to string
00298                         getreply(500, buff, sizeof(buff), 24);
00299                         #ifdef DEBUG
00300                         printf(buff); printf("\n");
00301                         #endif
00302                     }
00303                 }       
00304             }
00305         }
00306         /*********************************************************************/
00307         /*********************************************************************/    
00308     }                      
00309 }
00310 
00311 // Initialize ESP8266
00312 void init(char* buffer, int size){
00313     // Hardware Reset ESP 
00314     espRstPin=0;
00315     wait(0.5);
00316     espRstPin=1;
00317     // Get start up junk from ESP8266
00318     getreply(6000, buffer, size, 500);
00319 }
00320 
00321 // Get Command and ESP status replies
00322 void getreply(int timeout_ms, char* buffer, int size, int numBytes)
00323 {
00324     memset(buffer, '\0', size);     // Null out buffer
00325     t1.reset();
00326     t1.start();
00327     int idx = 0;
00328     while(t1.read_ms()< timeout_ms && idx < numBytes) {
00329         if(esp.readable()) {
00330             buffer[idx] = esp.getc();
00331             idx++;
00332         }
00333     }
00334     t1.stop();
00335 }
00336 
00337 // Starts and restarts webserver if errors detected.
00338 void startserver(char* buffer, int size)
00339 {
00340     esp.printf("AT+RST\r\n");       // BWW: Reset the ESP8266          
00341     getreply(8000, buffer, size, 1000);                                            
00342 
00343     if (strstr(buffer, "OK") != NULL) {
00344         // BWW: Set ESP8266 for multiple connections
00345         esp.printf("AT+CIPMUX=1\r\n");
00346         getreply(500, buffer, size, 20);
00347         
00348         // BWW: Set ESP8266 as Server on given port
00349         esp.printf("AT+CIPSERVER=1,%d\r\n", port);
00350         getreply(500, buffer, size, 20);   // BWW: Wait for reply
00351                 
00352         // BWW: Set ESP8266 Server Timeout
00353         esp.printf("AT+CIPSTO=%d\r\n", serverTimeout_secs);
00354         getreply(500, buffer, size, 50);    // BWW: Wait for reply
00355         
00356         // BWW: Request IP Address from router for ESP8266 
00357         int weberror = 0;
00358         while(weberror==0) {
00359             esp.printf("AT+CIFSR\r\n");
00360             getreply(2500, buffer, size, 200);
00361             if(strstr(buffer, "0.0.0.0") == NULL) {
00362                 weberror=1;   // wait for valid IP
00363             }
00364         }
00365     } 
00366     // else ESP8266 did not reply "OK" something is messed up
00367     else {
00368         strcpy(buffer, "ESP8266 Error\n");
00369     }
00370 }
00371 
00372 /*
00373     update_webpage() updates output fields based on webpage user inputs "POSTED"
00374     Preconditions: webpage[] must have the following elements
00375         "kp_output"  value="xxx.xx"
00376         "ki_output"  value="xxx.xx"
00377         "kp_output"  value="xxx.xx"
00378     @param webpage  Pointer to webpage char[]
00379     @param kp       New kp value posted by user
00380     @param ki       New ki value posted by user
00381     @param kd       New kd value posted by user
00382     
00383     NOTE: THIS IS WEBPAGE SPECIFIC!!!! CHANGE THE CODE IN HERE TO SUITE THE 
00384     SPECIFIC APPLICATION WEBPAGE!!! ALSO USED TO REFLECT THE CUSTOM 
00385     IMPLEMENTATION OF THE parse_intput() function. MAKE SURE THESE TWO FUNCTIONS 
00386     INTEGRATE PROPERLY!!!
00387 */
00388 void update_webpage(char* webpage, float working_setpoint, float kp, float ki, float kd){
00389     // Change output value to reflect new control mode, setpoint, kp, ki, kd values
00390     char* begin; 
00391     char temp[8];
00392     int idx;
00393     
00394     // Update Control Mode Radio Buttons
00395     memset(temp, '\0', sizeof(temp));
00396     idx = 0;
00397     begin = strstr(webpage, "name=\"control_mode\" value=\"") +     
00398             sizeof("name=\"control_mode\" value=\"0");        // Update Control Mode Position Radio Button
00399     if(control_mode == PID_OFF) sprintf(temp, "%s", "checked");// If PID OFF active "check" it
00400     else sprintf(temp, "%s", "       ");                         // else "clear" it
00401     while(temp[idx] !=  '\0'){                                  // Write "checked"/"        " to field
00402         begin[idx] = temp[idx];
00403         idx++;
00404     }
00405     memset(temp, '\0', sizeof(temp));
00406     idx = 0;
00407     begin = strstr(webpage, "name=\"control_mode\" value=\"") +     
00408             sizeof("name=\"control_mode\" value=\"0\"");        // Nav to first Control Mode Radio Button (Position)
00409     begin = strstr(begin, "name=\"control_mode\" value=\"") +     
00410             sizeof("name=\"control_mode\" value=\"1");        // Nav to second Control Mode Radio Button (Speed) 
00411     if(control_mode == PID_ON) sprintf(temp, "%s", "checked"); // If PID ON active "check" it
00412     else sprintf(temp, "%s", "       ");                         // else "clear" it
00413     while(temp[idx] !=  '\0'){                                  // Write "checked"/"        " to field
00414         begin[idx] = temp[idx];
00415         idx++;
00416     }
00417     
00418     // Update Kp Paramater Field
00419     memset(temp, '\0', sizeof(temp));
00420     idx = 0;
00421     begin = strstr(webpage, "name=\"kp_input\" value=\"") + 
00422             sizeof("name=\"kp_input\" value="); // Points to start of kp_output field
00423     // Determine precision of float such temp string has no empty spaces; 
00424     // i.e. each space must have a value or a decimal point, other wise webbrowser may not recognize value
00425     if(kp >= 100) sprintf(temp, "%6.2f", kp);                             // xxx.00
00426     else if(10 <= kp && kp < 100) sprintf(temp, "%6.3f", kp);             // xx.000
00427     else sprintf(temp, "%6.4f", kp);                                      // x.0000
00428     while(temp[idx] !=  '\0'){                                            // Overwrite old digits with new digits
00429         begin[idx] = temp[idx];
00430         idx++;
00431     }
00432     
00433     // Update Ki Parameter Field
00434     memset(temp, '\0', sizeof(temp));
00435     idx = 0;
00436     begin = strstr(webpage, "name=\"ki_input\" value=\"") + 
00437             sizeof("name=\"ki_input\" value="); // Points to start of ki_output field
00438     // Determine precision of float such temp string has no empty spaces; 
00439     // i.e. each space must have a value or a decimal point, other wise webbrowser may not recognize value 
00440     if(ki >= 100) sprintf(temp, "%6.2f", ki);                             // xxx.00
00441     else if(10 <= ki && ki < 100) sprintf(temp, "%6.3f", ki);             // xx.000
00442     else sprintf(temp, "%6.4f", ki);                                      // x.0000     
00443     while(temp[idx] !=  '\0'){                                            // Overwrite old digits with new digits
00444         begin[idx] = temp[idx];
00445         idx++;
00446     }
00447     
00448     // Update Kd Parameter Field
00449     memset(temp, '\0', sizeof(temp));
00450     idx = 0;
00451     begin = strstr(webpage, "name=\"kd_input\" value=\"")+
00452             sizeof("name=\"kd_input\" value="); // Points to start of kd_output field
00453     // Determine precision of float such temp string has no empty spaces; 
00454     // i.e. each space must have a value or a decimal point, other wise webbrowser may not recognize value
00455     if(kd >= 100) sprintf(temp, "%6.2f", kd);                             // xxx.00
00456     else if(10 <= kd && kd < 100) sprintf(temp, "%6.3f", kd);             // xx.000
00457     else sprintf(temp, "%6.4f", kd);                                      // x.0000
00458     while(temp[idx] !=  '\0'){                                            // Overwrite old digits with new digits
00459         begin[idx] = temp[idx];
00460         idx++;
00461     }
00462     
00463     // Update Setpoint Parameter Field
00464     // Determine precision of float such temp string has no empty spaces; 
00465     // i.e. each space must have a value or a decimal point or neg sign, 
00466     // other wise webbrowser may not recognize value
00467     memset(temp, '\0', sizeof(temp));
00468     idx = 0;
00469     begin = strstr(webpage, "name=\"setpoint_input\" value=\"")+
00470             sizeof("name=\"setpoint_input\" value="); // Points to start of kp_output field
00471     // Determine precision of float such temp string has no empty spaces; 
00472     // i.e. each space must have a value or a decimal point, other wise webbrowser may not recognize value
00473     if(working_setpoint >= 0.00){
00474         if(working_setpoint >= 100) sprintf(temp, "%6.3f", working_setpoint);                           // xxx.000
00475         else if(10 <= working_setpoint && working_setpoint < 100) sprintf(temp, "%7.4f", working_setpoint);     // xx.0000
00476         else sprintf(temp, "%6.5f", working_setpoint);                                          // x.00000
00477     }
00478     else{
00479         if(working_setpoint <= -100) sprintf(temp, "%6.2f", working_setpoint);                          // -xxx.00
00480         else if(-100 < working_setpoint && working_setpoint <= -10) sprintf(temp, "%6.3f", working_setpoint);   // -xx.000
00481         else sprintf(temp, "%6.4f", working_setpoint);                                          // -x.0000
00482     }
00483     while(temp[idx] !=  '\0'){                                            // Overwrite old digits with new digits
00484         begin[idx] = temp[idx];
00485         idx++;
00486     }
00487 }
00488 
00489 /*
00490     parse_input() take a char*, in particular a pointer to Webpage User 
00491     Input Data, for example: 
00492     char str[] = "+IPD,0,44:kp_input=0.12&ki_input=14.25&kd_input=125.42";
00493     
00494     and parses out the Setpoint Kp, Ki, Kd values that the user entered
00495     and posted in the webpage. Values are converted to floats and 
00496     assigned to the given argurments.
00497     
00498     NOTE: THIS IS WEBPAGE SPECIFIC!!!! CHANGE THE CODE IN HERE TO SUITE THE 
00499     SPECIFIC APPLICATION WEBPAGE!!! THESE EXTRACTED VALUES WILL BE USED IN 
00500     THE update_webpage() function. MAKE SURE THESE TWO FUNCTIONS INTEGRATE
00501     PROPERLY!!!
00502 */
00503 void parse_input(char* webpage_user_data, int* control_mode, float *working_setpoint, float* kp, float* ki, float* kd){
00504     char keys[] = {'&', '\0'};
00505       
00506     // Parse out user input values
00507     char input_buff[50]; 
00508     char* begin;
00509     char* end;
00510     
00511     // Parse and Update Control Mode Value
00512     memset(input_buff, '\0', sizeof(input_buff));           // Null out input buff
00513     begin = strstr(webpage_user_data, "control_mode=") +
00514             sizeof("control_mode");                         // Points to start of setpoint_input value
00515     end = begin + strcspn(begin, keys);                     // Points to end of setpoint_input value
00516     for(long i = 0; i < end - begin; i++){                  // Parse out the value one char at a time
00517         input_buff[i] = begin[i];        
00518     }
00519     *control_mode = atoi(input_buff);
00520     
00521     // Parse and Update Setpoint Value
00522     memset(input_buff, '\0', sizeof(input_buff));           // Null out input buff
00523     begin = strstr(webpage_user_data, "setpoint_input=") +
00524             sizeof("setpoint_input");                       // Points to start of setpoint_input value
00525     end = begin + strcspn(begin, keys);                     // Points to end of setpoint_input value
00526     for(long i = 0; i < end - begin; i++){                  // Parse out the value one char at a time
00527         input_buff[i] = begin[i];        
00528     }
00529     *working_setpoint = atof(input_buff);
00530     
00531     // Parse and Update Kp Value
00532     memset(input_buff, '\0', sizeof(input_buff));           // Null out input buff
00533     begin = strstr(webpage_user_data, "kp_input=") + 
00534             sizeof("kp_input");                             // Points to start of kp_input value
00535     end = begin + strcspn(begin, keys);                     // Points to end of kp_input value
00536     for(long i = 0; i < end - begin; i++){                  // Parse out the value one char at a time
00537         input_buff[i] = begin[i];
00538     }
00539     *kp = atof(input_buff);
00540     
00541     // Parse and Update Ki Value
00542     memset(input_buff, '\0', sizeof(input_buff));           // Null out input buff
00543     begin = strstr(webpage_user_data, "ki_input=") + 
00544             sizeof("ki_input");                             // Points to start of ki_input value
00545     end = begin + strcspn(begin, keys);                     // Points to end of ki_input value
00546     for(long i = 0; i < end - begin; i++){                  // Parse out the value one char at a time
00547         input_buff[i] = begin[i];
00548     }
00549     *ki = atof(input_buff);
00550     
00551     // Parse and Update Kd Value
00552     memset(input_buff, '\0', sizeof(input_buff));           // Null out input buff
00553     begin = strstr(webpage_user_data, "kd_input=") + 
00554             sizeof("kd_input");                             // Points to start of kd_input value
00555     end = begin + strcspn(begin, keys);                     // Points to end of kd_input value
00556     for(long i = 0; i < end - begin; i++){                  // Parse out the value one char at a time
00557         input_buff[i] = begin[i];  
00558     }
00559     *kd = atof(input_buff);
00560     
00561     // Parse for stop button press; we only have to see if "STOP" exists in the input buffer 
00562     // because it is just a button and will not be included unless it is pressed!... Makes 
00563     // our job easy!... if stop was pressed then set setpoint to zero!
00564     if(strstr(webpage_user_data, "STOP") != NULL) *working_setpoint = 0;
00565 }
00566 
00567 void pid_callback(){
00568     static int lastMode = control_mode;
00569     
00570     // If control_mode is PID_OFF turn off PID controllers and run motors in open loop
00571     if(control_mode == PID_OFF){
00572         // Mode changed; turn off PID controllers
00573         if(lastMode != control_mode){
00574             pidL.stop(); pidR.stop();
00575             lastMode = PID_OFF;
00576         }
00577         // Set motor speed in open loop mode
00578         // Motor direction based on working setpoint var
00579         int dirL, dirR;
00580         if(working_setpoint < 0.0){
00581              dirL = -1; dirR = -1;
00582         }
00583         else{
00584             dirL = 1; dirR = 1;
00585         }
00586         float speed = abs(working_setpoint) / 90.0; // Normalize based on 90 RPM
00587         mtrL.forceSetSpeed(speed * dirL);
00588         mtrR.forceSetSpeed(speed * dirR);
00589     }
00590     // else control_mode is PID_ON, turn on PID controllers and run motors in closed loop
00591     else{
00592         // Mode changed; turn on PID controllers
00593         if(lastMode != control_mode){
00594             pidL.start(); pidR.start();
00595             lastMode = PID_ON;
00596         }
00597         // Deal with feedback and update motors
00598         // Motor direction based on working setpoint var
00599         int dirL, dirR;
00600         if(working_setpoint < 0.0){
00601              dirL = -1; dirR = -1;
00602         }
00603         else{
00604             dirL = 1; dirR = 1;
00605         }
00606         
00607         // Setpoint vars used by PID objects are concerned with 
00608         // only SPEED not direction. 
00609         setpoint = abs(working_setpoint);
00610     
00611         float k = Ts_PID_CALLBACK;    // Discrete time, (Ts/2 because this callback is called
00612                                       // at interval of Ts/2... or twice as fast as pid controller)
00613         static int last_count_L = 0;
00614         static int last_count_R = 0;
00615         int countL = encL.read();
00616         int countR = encR.read();
00617         
00618         // Because encoders are not quadrature we must handle the sign outselves, 
00619         // i.e. explicitly make calcs based on the direction we have set the motor
00620         float raw_speed_L = ((countL - last_count_L)*FEEDBACK_SCALE) / k; 
00621         float rpm_speed_L = raw_speed_L * 60.0;     // Convert speed to RPM
00622         
00623         float raw_speed_R = ((countR - last_count_R)*FEEDBACK_SCALE) / k; 
00624         float rpm_speed_R = raw_speed_R * 60.0;     // Convert speed to RPM
00625         
00626         last_count_L = countL;                      // Save last count
00627         last_count_R = countR;
00628         feedbackL = rpm_speed_L;
00629         feedbackR = rpm_speed_R;
00630         
00631         mtrL.forceSetSpeed(outputL * dirL);
00632         mtrR.forceSetSpeed(outputR * dirR);
00633     }
00634 }
00635 
00636 /*
00637     Clips value to lower/ uppper
00638     @param value    The value to clip
00639     @param lower    The mininum allowable value
00640     @param upper    The maximum allowable value
00641     @return         The resulting clipped value
00642 */
00643 float clip(float value, float lower, float upper){
00644     return std::max(lower, std::min(value, upper));
00645 }
00646 
00647 /**************************WEB PAGE TEXT**************************************/
00648 /*****************************************************************************
00649 Copy and past text below into a html file and save as the given file name to 
00650 your SD card.
00651 
00652 file name: pid_bot.html
00653 
00654 html text:
00655 
00656 <!DOCTYPE html>
00657 <html>
00658 <head>
00659 <title>PID RedBot Control</title>
00660 </head>
00661 <body>
00662 <h1>PID Motor Control</h1>
00663 <h2>PID Status</h2>
00664 <form title="User Input" method="post">
00665 PID Controls: <br>
00666 <input type="radio" name="control_mode" value="0"checked>PID OFF
00667 <br>
00668 <input type="radio" name="control_mode" value="1"       >PID ON
00669 <br>
00670 Setpoint:<br> 
00671 <input type="number" name="setpoint_input" value="0000.00" step="0.0000001" size="6"  /><br>
00672 Proportional Gain: (Good Starting Value: 0.01)<br>
00673 <input type="number" name="kp_input" value="000.01" step="0.0000001" size="6"  /><br>
00674 Integral Gain: (Good Starting Value: 0.015)<br>
00675 <input type="number" name="ki_input" value="00.015" step="0.0000001" size="6"  /><br>
00676 Derivative Gain: (Good Starting Value: 0.0001)<br>
00677 <input type="number" name="kd_input" value="0.0001" step="0.0000001" size="6"  /><br>
00678 <br>
00679 <input type="submit" value="Update" />
00680 <input type="submit" name="STOP" value="STOP!" />
00681 </form>
00682 </body>
00683 </html>
00684 
00685 *****************************************************************************/
00686 /*****************************************************************************/