PID Motor Speed & Position Control Over WiFi using ESP8266 WiFi module, US Digital E4P-100-079 Quadrature Encoder, HN-GH12-1634T 30:1 200 RPM DC Motor, and LMD18200 H-Bridge Breakout

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