Wifi Webserver to control the Speed of a motor using PID

Dependencies:   4DGL-uLCD-SE PID QEI SDFileSystem mbed

Committer:
electromotivated
Date:
Sat Nov 28 03:15:54 2015 +0000
Revision:
4:05e4ebafbcaf
Parent:
3:2151f4b584b5
Added comments;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
electromotivated 0:7478eabf00eb 1 /*
electromotivated 0:7478eabf00eb 2 Uses the ESP8266 WiFi Chip to set up a WiFi Webserver used to control
electromotivated 0:7478eabf00eb 3 the speed of a motor using a PID controller. USE FIREFOX Web
electromotivated 0:7478eabf00eb 4 Browser
electromotivated 0:7478eabf00eb 5
electromotivated 0:7478eabf00eb 6 NOTES:
electromotivated 0:7478eabf00eb 7 1. Webpage Handling in this program is specific to a CUSTOM
electromotivated 0:7478eabf00eb 8 WEBPAGE. Program must be modified to handle specfically a new
electromotivated 0:7478eabf00eb 9 webpage. A copy of the webpage for this program can be found at
electromotivated 0:7478eabf00eb 10 the end of this program page. Simply copy and past text into a
electromotivated 0:7478eabf00eb 11 html file and save as the given name.
electromotivated 0:7478eabf00eb 12
electromotivated 0:7478eabf00eb 13 2. Developed and tested with FireFox 42.0 Web Browser. Does not seem to work
electromotivated 0:7478eabf00eb 14 well with Google Chrome or Internet Explorer for some reason... they seem
electromotivated 0:7478eabf00eb 15 to generate two post requests which messes with the user input values.
electromotivated 0:7478eabf00eb 16
electromotivated 0:7478eabf00eb 17 3. There are a bunch of printf statements in the code that can be
electromotivated 0:7478eabf00eb 18 uncommented for debugging in a serial terminal progrom.
electromotivated 0:7478eabf00eb 19
electromotivated 0:7478eabf00eb 20
electromotivated 0:7478eabf00eb 21 TODO: ESP8366 has a max packet send size. Make sure we implement
electromotivated 0:7478eabf00eb 22 a method to send webpages that exceed this value. The max size is
electromotivated 0:7478eabf00eb 23 listed in the official ESP8266 AT Commands Documentation, I think
electromotivated 0:7478eabf00eb 24 it is 2048 bytes/chars
electromotivated 0:7478eabf00eb 25
electromotivated 0:7478eabf00eb 26 TODO: CREATE CONFIG FUNCTION TO SET SSID, PASSWORD, BAUDRATE ETC.
electromotivated 0:7478eabf00eb 27 Perhaps have a serial terminal method to take user input, and
electromotivated 0:7478eabf00eb 28 put the function call into a #ifdef WiFiConfig statement, so
electromotivated 0:7478eabf00eb 29 that the user can enable it to config Wifi module then turn
electromotivated 0:7478eabf00eb 30 it off once Wifi module is configed so that this program can
electromotivated 0:7478eabf00eb 31 run in a "stand alone" mode.
electromotivated 0:7478eabf00eb 32
electromotivated 0:7478eabf00eb 33 TODO: Move debugging printf statements inside of #ifdef DEBUG
electromotivated 0:7478eabf00eb 34 statements, so that serial terminal print statements can be
electromotivated 0:7478eabf00eb 35 turned on and off easily for debugging.
electromotivated 1:1490beafb4f7 36
electromotivated 1:1490beafb4f7 37 TODO: Implement stop button in webpage
electromotivated 0:7478eabf00eb 38 */
electromotivated 0:7478eabf00eb 39
electromotivated 0:7478eabf00eb 40
electromotivated 0:7478eabf00eb 41 #include "mbed.h"
electromotivated 0:7478eabf00eb 42 #include "SDFileSystem.h"
electromotivated 0:7478eabf00eb 43 #include "PID.h"
electromotivated 0:7478eabf00eb 44 #include "QEI.h"
electromotivated 0:7478eabf00eb 45 #include <algorithm>
electromotivated 0:7478eabf00eb 46
electromotivated 0:7478eabf00eb 47 /**********WEB SERVER SPECIFIC DECLARTATIONS**********************************/
electromotivated 0:7478eabf00eb 48 /*****************************************************************************/
electromotivated 0:7478eabf00eb 49 SDFileSystem sd(p5,p6,p7,p8,"sd"); // MOSI, MISO, SCLK, CS,
electromotivated 0:7478eabf00eb 50 // Virtual File System Name
electromotivated 0:7478eabf00eb 51 Serial esp(p13, p14); // tx, rx
electromotivated 0:7478eabf00eb 52 DigitalOut espRstPin(p26); // ESP Reset
electromotivated 0:7478eabf00eb 53 DigitalOut led(LED4);
electromotivated 0:7478eabf00eb 54
electromotivated 0:7478eabf00eb 55 Timer t1;
electromotivated 0:7478eabf00eb 56 Timer t2;
electromotivated 0:7478eabf00eb 57
electromotivated 0:7478eabf00eb 58 void init(char* buffer, int size);
electromotivated 0:7478eabf00eb 59 void getreply(int timeout_ms, char* buffer, int size, int numBytes);
electromotivated 0:7478eabf00eb 60 void startserver(char* buffer, int size);
electromotivated 0:7478eabf00eb 61 void update_webpage(char* webpage, float setpoint, float kp, float ki, float kd);
electromotivated 0:7478eabf00eb 62 void parse_input(char* webpage_user_data, float* setpoint, float* kp, float* ki, float* kd);
electromotivated 0:7478eabf00eb 63 int port =80; // set server port
electromotivated 0:7478eabf00eb 64 int serverTimeout_secs =5; // set server timeout in seconds in case
electromotivated 0:7478eabf00eb 65 // link breaks.
electromotivated 0:7478eabf00eb 66 /*****************************************************************************/
electromotivated 0:7478eabf00eb 67 /*****************************************************************************/
electromotivated 0:7478eabf00eb 68
electromotivated 0:7478eabf00eb 69 /*********PID CONTROLLER SPECIFIC DECLARATIONS********************************/
electromotivated 0:7478eabf00eb 70 /*****************************************************************************/
electromotivated 2:f579e98b82de 71 float setpoint, feedback, output;
electromotivated 0:7478eabf00eb 72 const float output_lower_limit = -1.0;
electromotivated 0:7478eabf00eb 73 const float output_upper_limit = 1.0;
electromotivated 0:7478eabf00eb 74 const float FEEDBACK_SCALE = 1.0/3000.0; // Scale feedback to 1rev/3000cnts
electromotivated 0:7478eabf00eb 75 // this is encoder specific.
electromotivated 1:1490beafb4f7 76 // Init Webpage to these values
electromotivated 0:7478eabf00eb 77 const float setpoint_init = 0.0;
electromotivated 0:7478eabf00eb 78 const float kp_init = 0.01;
electromotivated 1:1490beafb4f7 79 const float ki_init = 0.015;
electromotivated 1:1490beafb4f7 80 const float kd_init = 0.0001;
electromotivated 3:2151f4b584b5 81 const float Ts_init = 0.04; // 25Hz Sample Freq (40ms Sample Time)
electromotivated 3:2151f4b584b5 82 const float Ts_PID_CALLBACK = Ts_init/2.0; // Update Motors and sensers twice as
electromotivated 3:2151f4b584b5 83 // fast as PID sample rate, ensures
electromotivated 3:2151f4b584b5 84 // PID feedback is upto date every
electromotivated 3:2151f4b584b5 85 // time PID calculations run
electromotivated 0:7478eabf00eb 86
electromotivated 0:7478eabf00eb 87 PID pid(&setpoint, &feedback, &output, output_lower_limit, output_upper_limit,
electromotivated 0:7478eabf00eb 88 kp_init, ki_init, kd_init, Ts_init);
electromotivated 0:7478eabf00eb 89 QEI encoder(p15, p16);
electromotivated 0:7478eabf00eb 90 PwmOut mtr_pwm(p25);
electromotivated 0:7478eabf00eb 91 DigitalOut mtr_dir(p24);
electromotivated 0:7478eabf00eb 92 void pid_callback(); // Updates encoder feedback and motor output
electromotivated 0:7478eabf00eb 93 Ticker motor;
electromotivated 0:7478eabf00eb 94 /*****************************************************************************/
electromotivated 0:7478eabf00eb 95 /*****************************************************************************/
electromotivated 0:7478eabf00eb 96
electromotivated 0:7478eabf00eb 97 // Common Application Declarations
electromotivated 0:7478eabf00eb 98 Serial pc(USBTX, USBRX);
electromotivated 0:7478eabf00eb 99 float clip(float value, float lower, float upper);
electromotivated 0:7478eabf00eb 100
electromotivated 0:7478eabf00eb 101 int main()
electromotivated 0:7478eabf00eb 102 {
electromotivated 0:7478eabf00eb 103 printf("Starting\n");
electromotivated 0:7478eabf00eb 104
electromotivated 0:7478eabf00eb 105 /****************** Load Webpage from SD Card***************************************/
electromotivated 0:7478eabf00eb 106 /***********************************************************************************/
electromotivated 1:1490beafb4f7 107 char file[] = "/sd/pid_spd.html";
electromotivated 0:7478eabf00eb 108
electromotivated 0:7478eabf00eb 109 // Get file size so we can dynamically allocate buffer size
electromotivated 0:7478eabf00eb 110 int num_chars = 0;
electromotivated 0:7478eabf00eb 111 FILE *fp = fopen(file, "r");
electromotivated 0:7478eabf00eb 112 while(!feof(fp)){
electromotivated 0:7478eabf00eb 113 fgetc(fp);
electromotivated 0:7478eabf00eb 114 num_chars++;
electromotivated 0:7478eabf00eb 115 }
electromotivated 0:7478eabf00eb 116 rewind(fp); // Go to beginning of file
electromotivated 0:7478eabf00eb 117
electromotivated 0:7478eabf00eb 118 // printf("Webpage Data Size: %d byte\r\n", num_chars);
electromotivated 0:7478eabf00eb 119
electromotivated 0:7478eabf00eb 120 const int WEBPAGE_SIZE = num_chars;
electromotivated 0:7478eabf00eb 121 char webpage[WEBPAGE_SIZE];
electromotivated 0:7478eabf00eb 122 webpage[0] = NULL; // Init our array so that element zero contains a null
electromotivated 0:7478eabf00eb 123 // This is important, ensures strings are placed into
electromotivated 0:7478eabf00eb 124 // buffer starting at element 0... not some random
electromotivated 0:7478eabf00eb 125 // elment
electromotivated 0:7478eabf00eb 126 // Read in and buffer file to memory
electromotivated 0:7478eabf00eb 127 if(fp == NULL){
electromotivated 0:7478eabf00eb 128 printf("Error: No Such File or something :(");
electromotivated 0:7478eabf00eb 129 return 1;
electromotivated 0:7478eabf00eb 130 }
electromotivated 0:7478eabf00eb 131 else{
electromotivated 0:7478eabf00eb 132 while(!feof(fp)){
electromotivated 0:7478eabf00eb 133 fgets(webpage + strlen(webpage), WEBPAGE_SIZE, fp); // Get a string from stream, add to buffer
electromotivated 0:7478eabf00eb 134 }
electromotivated 0:7478eabf00eb 135 }
electromotivated 0:7478eabf00eb 136 fclose(fp);
electromotivated 0:7478eabf00eb 137 printf("Webpage Buffer Size: %d bytes\r\n", sizeof(webpage));
electromotivated 0:7478eabf00eb 138 update_webpage(webpage, setpoint, kp_init, ki_init, kd_init);
electromotivated 0:7478eabf00eb 139 /***********************************************************************************/
electromotivated 0:7478eabf00eb 140 /***********************************************************************************/
electromotivated 0:7478eabf00eb 141
electromotivated 0:7478eabf00eb 142 /***************BRING UP SERVER*****************************************************/
electromotivated 0:7478eabf00eb 143 /***********************************************************************************/
electromotivated 0:7478eabf00eb 144 char buff[5000]; // Working buffer
electromotivated 0:7478eabf00eb 145 init(buff, sizeof(buff)); // Init ESP8266
electromotivated 0:7478eabf00eb 146
electromotivated 0:7478eabf00eb 147 esp.baud(115200); // ESP8266 baudrate. Maximum on KLxx' is 115200, 230400 works on K20 and K22F
electromotivated 0:7478eabf00eb 148
electromotivated 0:7478eabf00eb 149 startserver(buff, sizeof(buff)); // Configure the ESP8266 and Setup as Server
electromotivated 0:7478eabf00eb 150
electromotivated 0:7478eabf00eb 151 printf(buff); // If start successful buff contains IP address...
electromotivated 0:7478eabf00eb 152 // if not if contains an error.
electromotivated 0:7478eabf00eb 153 led =1;
electromotivated 0:7478eabf00eb 154 /***********************************************************************************/
electromotivated 0:7478eabf00eb 155 /***********************************************************************************/
electromotivated 0:7478eabf00eb 156
electromotivated 0:7478eabf00eb 157 /************Initialize the PID*****************************************************/
electromotivated 0:7478eabf00eb 158 /***********************************************************************************/
electromotivated 0:7478eabf00eb 159 // Working paramater variables
electromotivated 0:7478eabf00eb 160 setpoint = setpoint_init;
electromotivated 0:7478eabf00eb 161 encoder.reset();
electromotivated 0:7478eabf00eb 162 feedback = encoder.read();
electromotivated 0:7478eabf00eb 163 float kp = kp_init;
electromotivated 0:7478eabf00eb 164 float ki = ki_init;
electromotivated 0:7478eabf00eb 165 float kd = kd_init;
electromotivated 0:7478eabf00eb 166 pid.set_parameters(kp, ki, kd, Ts_init);
electromotivated 0:7478eabf00eb 167
electromotivated 0:7478eabf00eb 168 // Init the motor
electromotivated 0:7478eabf00eb 169 mtr_dir = 0; // Can be 0 or 1, sets the direction
electromotivated 0:7478eabf00eb 170 mtr_pwm = 0.0;
electromotivated 0:7478eabf00eb 171
electromotivated 0:7478eabf00eb 172 // Clear encoder count
electromotivated 0:7478eabf00eb 173 encoder.reset();
electromotivated 0:7478eabf00eb 174
electromotivated 0:7478eabf00eb 175 // Update sensors and feedback twice as fast as PID sample time
electromotivated 0:7478eabf00eb 176 // this makes pid react in real-time avoiding errors due to
electromotivated 0:7478eabf00eb 177 // missing counts etc.
electromotivated 3:2151f4b584b5 178 motor.attach(&pid_callback, Ts_PID_CALLBACK);
electromotivated 0:7478eabf00eb 179
electromotivated 0:7478eabf00eb 180 // Start PID sampling
electromotivated 0:7478eabf00eb 181 pid.start();
electromotivated 0:7478eabf00eb 182
electromotivated 0:7478eabf00eb 183 /***********************************************************************************/
electromotivated 0:7478eabf00eb 184 /***********************************************************************************/
electromotivated 0:7478eabf00eb 185
electromotivated 0:7478eabf00eb 186 while(1){
electromotivated 0:7478eabf00eb 187 /**************SERVICE WEBPAGE******************************************************/
electromotivated 0:7478eabf00eb 188 /***********************************************************************************/
electromotivated 0:7478eabf00eb 189 if(esp.readable()){
electromotivated 0:7478eabf00eb 190 getreply(500, buff, sizeof(buff), sizeof(buff) -1); // Get full buff, leave last element for null char
electromotivated 0:7478eabf00eb 191 // printf("\r\n*************WORKING BUFFER******************************\r\n");
electromotivated 0:7478eabf00eb 192 // printf(buff); printf("\n");
electromotivated 0:7478eabf00eb 193 // printf("\r\n**************END WORKING BUFFER**************************\r\n");
electromotivated 0:7478eabf00eb 194
electromotivated 0:7478eabf00eb 195 // If Recieved Data get ID, Length, and Data
electromotivated 0:7478eabf00eb 196 char* rqstPnt = strstr(buff, "+IPD");
electromotivated 0:7478eabf00eb 197 if(rqstPnt != NULL){
electromotivated 0:7478eabf00eb 198 int id, len;
electromotivated 0:7478eabf00eb 199 char type[10]; memset(type, '\0', sizeof(type)); // Create and null out data buff
electromotivated 0:7478eabf00eb 200 sscanf(rqstPnt, "+IPD,%d,%d:%s ", &id, &len, type);
electromotivated 0:7478eabf00eb 201 // printf("ID: %i\nLen: %i\nType: %s\n", id, len, type);
electromotivated 0:7478eabf00eb 202
electromotivated 0:7478eabf00eb 203 // If GET or POST request "type" parse and update user input then send webpage
electromotivated 0:7478eabf00eb 204 if(strstr(type, "GET") != NULL || strstr(type, "POST") != NULL){
electromotivated 0:7478eabf00eb 205 // printf("I got web request\n");
electromotivated 0:7478eabf00eb 206
electromotivated 0:7478eabf00eb 207 /* Read Webpage <Form> data sent using "method=POST"...
electromotivated 0:7478eabf00eb 208 Note: Input elements in the <Form> need a set name attribute to
electromotivated 0:7478eabf00eb 209 appear in the returned HTML body. Thus to "POST" data ensure:
electromotivated 0:7478eabf00eb 210 <Form method="POST"> <input type="xxx" name="xxx" value="xxx">
electromotivated 0:7478eabf00eb 211 <input type="xxx" value="xxx"> </Form>
electromotivated 0:7478eabf00eb 212 Only the input with name="xxx" will appear in body of HTML
electromotivated 0:7478eabf00eb 213 */
electromotivated 0:7478eabf00eb 214 // printf("\r\n*************USER INPUT**********************************\r\n");
electromotivated 0:7478eabf00eb 215 parse_input(buff, &setpoint, &kp, &ki, &kd);
electromotivated 4:05e4ebafbcaf 216 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)
electromotivated 4:05e4ebafbcaf 217 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)
electromotivated 4:05e4ebafbcaf 218 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)
electromotivated 4:05e4ebafbcaf 219 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)
electromotivated 0:7478eabf00eb 220 // printf("User Entered: \nSetpoint: %7.2f\nKp: %6.2f\nKi: %6.2f\nKd: %6.2f\n", setpoint, kp, ki, kd);
electromotivated 0:7478eabf00eb 221 pid.set_parameters(kp, ki, kd, Ts_init); // Updata PID params
electromotivated 0:7478eabf00eb 222 // printf("Updated to Kp: %1.2f Ki: %1.2f Kd: %1.2f Ts: %1.2f\r\n",
electromotivated 0:7478eabf00eb 223 // pid.getKp(), pid.getKi(), pid.getKd(), pid.getTs());
electromotivated 0:7478eabf00eb 224 // printf("Setpoint: %1.2f\r\n", setpoint);
electromotivated 0:7478eabf00eb 225 // printf("Output: %1.2f\r\n", output);
electromotivated 0:7478eabf00eb 226 // printf("\r\n*************END USER INPUT******************************\r\n");
electromotivated 0:7478eabf00eb 227
electromotivated 0:7478eabf00eb 228 // Update Webpage to reflect new values POSTED by client
electromotivated 0:7478eabf00eb 229 static bool isFirstRequest = true;
electromotivated 0:7478eabf00eb 230 if(!isFirstRequest) update_webpage(webpage, setpoint, kp, ki, kd);
electromotivated 0:7478eabf00eb 231 else isFirstRequest = false; // First Request just send page with initial values
electromotivated 0:7478eabf00eb 232 // printf(webpage); // DEBUGGING ONLY!!! REMOVE FOR RELEASE!!!
electromotivated 0:7478eabf00eb 233
electromotivated 0:7478eabf00eb 234 // Command TCP/IP Data Tx
electromotivated 0:7478eabf00eb 235 esp.printf("AT+CIPSEND=%d,%d\r\n", id, strlen(webpage));
electromotivated 0:7478eabf00eb 236 getreply(200, buff, sizeof(buff), 15); /*TODO: Wait for "OK\r\n>"*/
electromotivated 0:7478eabf00eb 237 // printf(buff); printf("\n");
electromotivated 0:7478eabf00eb 238
electromotivated 0:7478eabf00eb 239 // Send webpage
electromotivated 0:7478eabf00eb 240 // while(!esp.writeable()); // Wait until esp ready to send data
electromotivated 0:7478eabf00eb 241 int idx = 0;
electromotivated 0:7478eabf00eb 242 while(webpage[idx] != '\0'){
electromotivated 0:7478eabf00eb 243 esp.putc(webpage[idx]);
electromotivated 0:7478eabf00eb 244 idx++;
electromotivated 0:7478eabf00eb 245 }
electromotivated 0:7478eabf00eb 246
electromotivated 0:7478eabf00eb 247 // Check status - Success: close channel and update PID controller, Error: reconnect
electromotivated 0:7478eabf00eb 248 bool weberror = true;
electromotivated 0:7478eabf00eb 249 t2.reset(); t2.start();
electromotivated 0:7478eabf00eb 250 while(weberror ==1 && t2.read_ms() < 5000){
electromotivated 0:7478eabf00eb 251 getreply(500, buff, sizeof(buff), 24);
electromotivated 0:7478eabf00eb 252 if(strstr(buff, "SEND OK") != NULL) weberror = false;
electromotivated 0:7478eabf00eb 253 }
electromotivated 0:7478eabf00eb 254 if(weberror){
electromotivated 0:7478eabf00eb 255 esp.printf("AT+CIPMUX=1\r\n");
electromotivated 0:7478eabf00eb 256 getreply(500, buff, sizeof(buff), 10);
electromotivated 0:7478eabf00eb 257 // printf(buff); printf("\n");
electromotivated 0:7478eabf00eb 258
electromotivated 0:7478eabf00eb 259 esp.printf("AT+CIPSERVER=1,%d\r\n", port);
electromotivated 0:7478eabf00eb 260 getreply(500, buff, sizeof(buff), 10);
electromotivated 0:7478eabf00eb 261 // printf(buff); printf("\n");
electromotivated 0:7478eabf00eb 262 }
electromotivated 0:7478eabf00eb 263 else{
electromotivated 0:7478eabf00eb 264 esp.printf("AT+CIPCLOSE=%d\r\n", id); // Notice id is an int formatted to string
electromotivated 0:7478eabf00eb 265 getreply(500, buff, sizeof(buff), 24);
electromotivated 0:7478eabf00eb 266 // printf(buff); printf("\n");
electromotivated 0:7478eabf00eb 267 }
electromotivated 0:7478eabf00eb 268 }
electromotivated 0:7478eabf00eb 269 }
electromotivated 0:7478eabf00eb 270 }
electromotivated 0:7478eabf00eb 271 /*********************************************************************/
electromotivated 0:7478eabf00eb 272 /*********************************************************************/
electromotivated 0:7478eabf00eb 273
electromotivated 0:7478eabf00eb 274
electromotivated 0:7478eabf00eb 275 }
electromotivated 0:7478eabf00eb 276 }
electromotivated 0:7478eabf00eb 277
electromotivated 0:7478eabf00eb 278 // Initialize ESP8266
electromotivated 0:7478eabf00eb 279 void init(char* buffer, int size){
electromotivated 0:7478eabf00eb 280 // Hardware Reset ESP
electromotivated 0:7478eabf00eb 281 espRstPin=0;
electromotivated 0:7478eabf00eb 282 wait(0.5);
electromotivated 0:7478eabf00eb 283 espRstPin=1;
electromotivated 0:7478eabf00eb 284 // Get start up junk from ESP8266
electromotivated 0:7478eabf00eb 285 getreply(6000, buffer, size, 500);
electromotivated 0:7478eabf00eb 286 }
electromotivated 0:7478eabf00eb 287
electromotivated 0:7478eabf00eb 288 // Get Command and ESP status replies
electromotivated 0:7478eabf00eb 289 void getreply(int timeout_ms, char* buffer, int size, int numBytes)
electromotivated 0:7478eabf00eb 290 {
electromotivated 0:7478eabf00eb 291 memset(buffer, '\0', size); // Null out buffer
electromotivated 0:7478eabf00eb 292 t1.reset();
electromotivated 0:7478eabf00eb 293 t1.start();
electromotivated 0:7478eabf00eb 294 int idx = 0;
electromotivated 0:7478eabf00eb 295 while(t1.read_ms()< timeout_ms && idx < numBytes) {
electromotivated 0:7478eabf00eb 296 if(esp.readable()) {
electromotivated 0:7478eabf00eb 297 buffer[idx] = esp.getc();
electromotivated 0:7478eabf00eb 298 idx++;
electromotivated 0:7478eabf00eb 299 }
electromotivated 0:7478eabf00eb 300 }
electromotivated 0:7478eabf00eb 301 t1.stop();
electromotivated 0:7478eabf00eb 302 }
electromotivated 0:7478eabf00eb 303
electromotivated 0:7478eabf00eb 304 // Starts and restarts webserver if errors detected.
electromotivated 0:7478eabf00eb 305 void startserver(char* buffer, int size)
electromotivated 0:7478eabf00eb 306 {
electromotivated 0:7478eabf00eb 307 esp.printf("AT+RST\r\n"); // BWW: Reset the ESP8266
electromotivated 0:7478eabf00eb 308 getreply(8000, buffer, size, 1000);
electromotivated 0:7478eabf00eb 309
electromotivated 0:7478eabf00eb 310 if (strstr(buffer, "OK") != NULL) {
electromotivated 0:7478eabf00eb 311 // BWW: Set ESP8266 for multiple connections
electromotivated 0:7478eabf00eb 312 esp.printf("AT+CIPMUX=1\r\n");
electromotivated 0:7478eabf00eb 313 getreply(500, buffer, size, 20);
electromotivated 0:7478eabf00eb 314
electromotivated 0:7478eabf00eb 315 // BWW: Set ESP8266 as Server on given port
electromotivated 0:7478eabf00eb 316 esp.printf("AT+CIPSERVER=1,%d\r\n", port);
electromotivated 0:7478eabf00eb 317 getreply(500, buffer, size, 20); // BWW: Wait for reply
electromotivated 0:7478eabf00eb 318
electromotivated 0:7478eabf00eb 319 // wait(1);
electromotivated 0:7478eabf00eb 320
electromotivated 0:7478eabf00eb 321 // BWW: Set ESP8266 Server Timeout
electromotivated 0:7478eabf00eb 322 esp.printf("AT+CIPSTO=%d\r\n", serverTimeout_secs);
electromotivated 0:7478eabf00eb 323 getreply(500, buffer, size, 50); // BWW: Wait for reply
electromotivated 0:7478eabf00eb 324
electromotivated 0:7478eabf00eb 325 // wait(5);
electromotivated 0:7478eabf00eb 326
electromotivated 0:7478eabf00eb 327 // BWW: Request IP Address from router for ESP8266
electromotivated 0:7478eabf00eb 328 int weberror = 0;
electromotivated 0:7478eabf00eb 329 while(weberror==0) {
electromotivated 0:7478eabf00eb 330 esp.printf("AT+CIFSR\r\n");
electromotivated 0:7478eabf00eb 331 getreply(2500, buffer, size, 200);
electromotivated 0:7478eabf00eb 332 if(strstr(buffer, "0.0.0.0") == NULL) {
electromotivated 0:7478eabf00eb 333 weberror=1; // wait for valid IP
electromotivated 0:7478eabf00eb 334 }
electromotivated 0:7478eabf00eb 335 }
electromotivated 0:7478eabf00eb 336 }
electromotivated 0:7478eabf00eb 337 // else ESP8266 did not reply "OK" something is messed up
electromotivated 0:7478eabf00eb 338 else {
electromotivated 0:7478eabf00eb 339 strcpy(buffer, "ESP8266 Error\n");
electromotivated 0:7478eabf00eb 340 }
electromotivated 0:7478eabf00eb 341 }
electromotivated 0:7478eabf00eb 342
electromotivated 0:7478eabf00eb 343 /*
electromotivated 0:7478eabf00eb 344 update_webpage() updates output fields based on webpage user inputs "POSTED"
electromotivated 0:7478eabf00eb 345 Preconditions: webpage[] must have the following elements
electromotivated 0:7478eabf00eb 346 "kp_output" value="xxx.xx"
electromotivated 0:7478eabf00eb 347 "ki_output" value="xxx.xx"
electromotivated 0:7478eabf00eb 348 "kp_output" value="xxx.xx"
electromotivated 0:7478eabf00eb 349 @param webpage Pointer to webpage char[]
electromotivated 0:7478eabf00eb 350 @param kp New kp value posted by user
electromotivated 0:7478eabf00eb 351 @param ki New ki value posted by user
electromotivated 0:7478eabf00eb 352 @param kd New kd value posted by user
electromotivated 0:7478eabf00eb 353
electromotivated 0:7478eabf00eb 354 NOTE: THIS IS WEBPAGE SPECIFIC!!!! CHANGE THE CODE IN HERE TO SUITE THE
electromotivated 0:7478eabf00eb 355 SPECIFIC APPLICATION WEBPAGE!!! ALSO USED TO REFLECT THE CUSTOM
electromotivated 0:7478eabf00eb 356 IMPLEMENTATION OF THE parse_intput() function. MAKE SURE THESE TWO FUNCTIONS
electromotivated 0:7478eabf00eb 357 INTEGRATE PROPERLY!!!
electromotivated 0:7478eabf00eb 358 */
electromotivated 0:7478eabf00eb 359 void update_webpage(char* webpage, float setpoint, float kp, float ki, float kd){
electromotivated 0:7478eabf00eb 360 // Change output value to reflect new setpoint kp, ki, kd values
electromotivated 0:7478eabf00eb 361 char* begin;
electromotivated 0:7478eabf00eb 362 // char* end;
electromotivated 0:7478eabf00eb 363 char temp[8];
electromotivated 0:7478eabf00eb 364 int idx;
electromotivated 0:7478eabf00eb 365
electromotivated 0:7478eabf00eb 366 memset(temp, '\0', sizeof(temp));
electromotivated 0:7478eabf00eb 367 idx = 0;
electromotivated 0:7478eabf00eb 368 begin = strstr(webpage, "name=\"kp_input\" value=\"") +
electromotivated 0:7478eabf00eb 369 sizeof("name=\"kp_input\" value="); // Points to start of kp_output field
electromotivated 0:7478eabf00eb 370 // end = begin + 5; // Points to end of kp_output value
electromotivated 0:7478eabf00eb 371 // Determine precision of float such temp string has no empty spaces;
electromotivated 0:7478eabf00eb 372 // i.e. each space must have a value or a decimal point, other wise webbrowser may not recognize value
electromotivated 0:7478eabf00eb 373 if(kp >= 100) sprintf(temp, "%6.2f", kp); // xxx.00
electromotivated 0:7478eabf00eb 374 else if(10 <= kp && kp < 100) sprintf(temp, "%6.3f", kp); // xx.000
electromotivated 0:7478eabf00eb 375 else sprintf(temp, "%6.4f", kp); // x.0000
electromotivated 0:7478eabf00eb 376 while(temp[idx] != '\0'){ // Overwrite old digits with new digits
electromotivated 0:7478eabf00eb 377 begin[idx] = temp[idx];
electromotivated 0:7478eabf00eb 378 idx++;
electromotivated 0:7478eabf00eb 379 }
electromotivated 0:7478eabf00eb 380
electromotivated 0:7478eabf00eb 381 memset(temp, '\0', sizeof(temp));
electromotivated 0:7478eabf00eb 382 idx = 0;
electromotivated 0:7478eabf00eb 383 begin = strstr(webpage, "name=\"ki_input\" value=\"") +
electromotivated 0:7478eabf00eb 384 sizeof("name=\"ki_input\" value="); // Points to start of ki_output field
electromotivated 0:7478eabf00eb 385 // end = begin + 5; // Points to end of ki_output value
electromotivated 0:7478eabf00eb 386 if(ki >= 100) sprintf(temp, "%6.2f", ki); // xxx.00
electromotivated 0:7478eabf00eb 387 else if(10 <= ki && ki < 100) sprintf(temp, "%6.3f", ki); // xx.000
electromotivated 0:7478eabf00eb 388 else sprintf(temp, "%6.4f", ki); // x.0000
electromotivated 0:7478eabf00eb 389 while(temp[idx] != '\0'){ // Overwrite old digits with new digits
electromotivated 0:7478eabf00eb 390 begin[idx] = temp[idx];
electromotivated 0:7478eabf00eb 391 idx++;
electromotivated 0:7478eabf00eb 392 }
electromotivated 0:7478eabf00eb 393
electromotivated 0:7478eabf00eb 394 memset(temp, '\0', sizeof(temp));
electromotivated 0:7478eabf00eb 395 idx = 0;
electromotivated 0:7478eabf00eb 396 begin = strstr(webpage, "name=\"kd_input\" value=\"")+
electromotivated 0:7478eabf00eb 397 sizeof("name=\"kd_input\" value="); // Points to start of kd_output field
electromotivated 0:7478eabf00eb 398 // end = begin + 5; // Points to end of kd_output value
electromotivated 0:7478eabf00eb 399 if(kd >= 100) sprintf(temp, "%6.2f", kd); // xxx.00
electromotivated 0:7478eabf00eb 400 else if(10 <= kd && kd < 100) sprintf(temp, "%6.3f", kd); // xx.000
electromotivated 0:7478eabf00eb 401 else sprintf(temp, "%6.4f", kd); // x.0000
electromotivated 0:7478eabf00eb 402 while(temp[idx] != '\0'){ // Overwrite old digits with new digits
electromotivated 0:7478eabf00eb 403 begin[idx] = temp[idx];
electromotivated 0:7478eabf00eb 404 idx++;
electromotivated 0:7478eabf00eb 405 }
electromotivated 0:7478eabf00eb 406
electromotivated 0:7478eabf00eb 407 // Determine precision of float such temp string has no empty spaces;
electromotivated 0:7478eabf00eb 408 // i.e. each space must have a value or a decimal point or neg sign,
electromotivated 0:7478eabf00eb 409 // other wise webbrowser may not recognize value
electromotivated 0:7478eabf00eb 410 memset(temp, '\0', sizeof(temp));
electromotivated 0:7478eabf00eb 411 idx = 0;
electromotivated 0:7478eabf00eb 412 begin = strstr(webpage, "name=\"setpoint_input\" value=\"")+
electromotivated 0:7478eabf00eb 413 sizeof("name=\"setpoint_input\" value="); // Points to start of kp_output field
electromotivated 0:7478eabf00eb 414 // end = begin + 6; // Points to end of kp_output value. +6 to accomadate negative sign
electromotivated 0:7478eabf00eb 415 if(setpoint >= 0.00){
electromotivated 0:7478eabf00eb 416 if(setpoint >= 100) sprintf(temp, "%6.3f", setpoint); // xxx.000
electromotivated 0:7478eabf00eb 417 else if(10 <= setpoint && setpoint < 100) sprintf(temp, "%7.4f", setpoint); // xx.0000
electromotivated 0:7478eabf00eb 418 else sprintf(temp, "%6.5f", setpoint); // x.00000
electromotivated 0:7478eabf00eb 419 }
electromotivated 0:7478eabf00eb 420 else{
electromotivated 0:7478eabf00eb 421 if(setpoint <= -100) sprintf(temp, "%6.2f", setpoint); // -xxx.00
electromotivated 0:7478eabf00eb 422 else if(-100 < setpoint && setpoint <= -10) sprintf(temp, "%6.3f", setpoint); // -xx.000
electromotivated 0:7478eabf00eb 423 else sprintf(temp, "%6.4f", setpoint); // -x.0000
electromotivated 0:7478eabf00eb 424 }
electromotivated 0:7478eabf00eb 425 while(temp[idx] != '\0'){ // Overwrite old digits with new digits
electromotivated 0:7478eabf00eb 426 begin[idx] = temp[idx];
electromotivated 0:7478eabf00eb 427 idx++;
electromotivated 0:7478eabf00eb 428 }
electromotivated 0:7478eabf00eb 429 }
electromotivated 0:7478eabf00eb 430
electromotivated 0:7478eabf00eb 431 /*
electromotivated 0:7478eabf00eb 432 parse_input() take a char*, in particular a pointer to Webpage User
electromotivated 0:7478eabf00eb 433 Input Data, for example:
electromotivated 0:7478eabf00eb 434 char str[] = "+IPD,0,44:kp_input=0.12&ki_input=14.25&kd_input=125.42";
electromotivated 0:7478eabf00eb 435
electromotivated 0:7478eabf00eb 436 and parses out the Setpoint Kp, Ki, Kd values that the user entered
electromotivated 0:7478eabf00eb 437 and posted in the webpage. Values are converted to floats and
electromotivated 0:7478eabf00eb 438 assigned to the given argurments.
electromotivated 0:7478eabf00eb 439
electromotivated 0:7478eabf00eb 440 NOTE: THIS IS WEBPAGE SPECIFIC!!!! CHANGE THE CODE IN HERE TO SUITE THE
electromotivated 0:7478eabf00eb 441 SPECIFIC APPLICATION WEBPAGE!!! THESE EXTRACTED VALUES WILL BE USED IN
electromotivated 0:7478eabf00eb 442 THE update_webpage() function. MAKE SURE THESE TWO FUNCTIONS INTEGRATE
electromotivated 0:7478eabf00eb 443 PROPERLY!!!
electromotivated 0:7478eabf00eb 444 */
electromotivated 0:7478eabf00eb 445 void parse_input(char* webpage_user_data,float *setpoint, float* kp, float* ki, float* kd){
electromotivated 0:7478eabf00eb 446 char keys[] = {'&', '\0'};
electromotivated 0:7478eabf00eb 447
electromotivated 0:7478eabf00eb 448 // Parse out user input values
electromotivated 0:7478eabf00eb 449 char input_buff[50];
electromotivated 0:7478eabf00eb 450 char* begin;
electromotivated 0:7478eabf00eb 451 char* end;
electromotivated 0:7478eabf00eb 452 // printf("**************Parsing**************\n");
electromotivated 0:7478eabf00eb 453 memset(input_buff, '\0', sizeof(input_buff)); // Null out input buff
electromotivated 0:7478eabf00eb 454 begin = strstr(webpage_user_data, "setpoint_input=") +
electromotivated 0:7478eabf00eb 455 sizeof("setpoint_input"); // Points to start of setpoint_input value
electromotivated 0:7478eabf00eb 456 end = begin + strcspn(begin, keys); // Points to end of setpoint_input value
electromotivated 0:7478eabf00eb 457 for(long i = 0; i < end - begin; i++){ // Parse out the value one char at a time
electromotivated 0:7478eabf00eb 458 input_buff[i] = begin[i];
electromotivated 0:7478eabf00eb 459 }
electromotivated 0:7478eabf00eb 460 // printf("Setpoint Parsed Data: %s\n", input_buff);
electromotivated 0:7478eabf00eb 461 *setpoint = atof(input_buff);
electromotivated 0:7478eabf00eb 462
electromotivated 0:7478eabf00eb 463 memset(input_buff, '\0', sizeof(input_buff)); // Null out input buff
electromotivated 0:7478eabf00eb 464 begin = strstr(webpage_user_data, "kp_input=") +
electromotivated 0:7478eabf00eb 465 sizeof("kp_input"); // Points to start of kp_input value
electromotivated 0:7478eabf00eb 466 end = begin + strcspn(begin, keys); // Points to end of kp_input value
electromotivated 0:7478eabf00eb 467 for(long i = 0; i < end - begin; i++){ // Parse out the value one char at a time
electromotivated 0:7478eabf00eb 468 input_buff[i] = begin[i];
electromotivated 0:7478eabf00eb 469 }
electromotivated 0:7478eabf00eb 470 // printf("Kp Parsed Data: %s\n", input_buff);
electromotivated 0:7478eabf00eb 471 *kp = atof(input_buff);
electromotivated 0:7478eabf00eb 472
electromotivated 0:7478eabf00eb 473 memset(input_buff, '\0', sizeof(input_buff)); // Null out input buff
electromotivated 0:7478eabf00eb 474 begin = strstr(webpage_user_data, "ki_input=") +
electromotivated 0:7478eabf00eb 475 sizeof("ki_input"); // Points to start of ki_input value
electromotivated 0:7478eabf00eb 476 end = begin + strcspn(begin, keys); // Points to end of ki_input value
electromotivated 0:7478eabf00eb 477 for(long i = 0; i < end - begin; i++){ // Parse out the value one char at a time
electromotivated 0:7478eabf00eb 478 input_buff[i] = begin[i];
electromotivated 0:7478eabf00eb 479 }
electromotivated 0:7478eabf00eb 480 // printf("Ki Parsed Data: %s\n", input_buff);
electromotivated 0:7478eabf00eb 481 *ki = atof(input_buff);
electromotivated 0:7478eabf00eb 482
electromotivated 0:7478eabf00eb 483 memset(input_buff, '\0', sizeof(input_buff)); // Null out input buff
electromotivated 0:7478eabf00eb 484 begin = strstr(webpage_user_data, "kd_input=") +
electromotivated 0:7478eabf00eb 485 sizeof("kd_input"); // Points to start of kd_input value
electromotivated 0:7478eabf00eb 486 end = begin + strcspn(begin, keys); // Points to end of kd_input value
electromotivated 0:7478eabf00eb 487 for(long i = 0; i < end - begin; i++){ // Parse out the value one char at a time
electromotivated 0:7478eabf00eb 488 input_buff[i] = begin[i];
electromotivated 0:7478eabf00eb 489 }
electromotivated 0:7478eabf00eb 490 // printf("Kd Parsed Data: %s\n", input_buff);
electromotivated 0:7478eabf00eb 491 *kd = atof(input_buff);
electromotivated 0:7478eabf00eb 492 // printf("**********End Parsing***************\n");
electromotivated 0:7478eabf00eb 493 }
electromotivated 0:7478eabf00eb 494
electromotivated 0:7478eabf00eb 495 void pid_callback(){
electromotivated 0:7478eabf00eb 496 // Update motor
electromotivated 0:7478eabf00eb 497 if(setpoint >= 0.0) mtr_dir = 1; // Set motor direction based on setpoint
electromotivated 0:7478eabf00eb 498 else mtr_dir = 0;
electromotivated 0:7478eabf00eb 499 if(-0.001 < setpoint && setpoint < 0.001){
electromotivated 0:7478eabf00eb 500 /* Setpoint = 0 is a special case, we allow output to control speed AND
electromotivated 0:7478eabf00eb 501 direction to fight intertia and/or downhill roll. */
electromotivated 0:7478eabf00eb 502 if(output >= 0.0) mtr_dir = 1;
electromotivated 0:7478eabf00eb 503 else mtr_dir = 0;
electromotivated 0:7478eabf00eb 504 mtr_pwm = abs(output);
electromotivated 0:7478eabf00eb 505 }
electromotivated 0:7478eabf00eb 506 else{
electromotivated 0:7478eabf00eb 507 if(mtr_dir == 1){ // If CW then apply positive outputs
electromotivated 0:7478eabf00eb 508 if(output >= 0.0) mtr_pwm = output;
electromotivated 0:7478eabf00eb 509 else mtr_pwm = 0.0;
electromotivated 0:7478eabf00eb 510 }
electromotivated 0:7478eabf00eb 511 else{ // If CCW then apply negative outputs
electromotivated 0:7478eabf00eb 512 if(output <= 0.0) mtr_pwm = abs(output);
electromotivated 0:7478eabf00eb 513 else mtr_pwm = 0.0;
electromotivated 0:7478eabf00eb 514 }
electromotivated 0:7478eabf00eb 515 }
electromotivated 1:1490beafb4f7 516 float k = Ts_init/2.0; // Discrete time, (Ts/2 because this callback is called
electromotivated 0:7478eabf00eb 517 // at interval of Ts/2... or twice as fast as pid controller)
electromotivated 0:7478eabf00eb 518
electromotivated 0:7478eabf00eb 519 /* TODO: Implement a "rolling"/"moving" average */
electromotivated 0:7478eabf00eb 520 static int last_count = 0;
electromotivated 0:7478eabf00eb 521 int count = encoder.read();
electromotivated 0:7478eabf00eb 522 float raw_speed = ((count - last_count)*FEEDBACK_SCALE) / k;
electromotivated 0:7478eabf00eb 523 float rpm_speed = raw_speed * 60.0; // Convert speed to RPM
electromotivated 0:7478eabf00eb 524
electromotivated 0:7478eabf00eb 525 last_count = count; // Save last count
electromotivated 0:7478eabf00eb 526 feedback = rpm_speed;
electromotivated 0:7478eabf00eb 527 }
electromotivated 0:7478eabf00eb 528
electromotivated 0:7478eabf00eb 529 /*
electromotivated 0:7478eabf00eb 530 Clips value to lower/ uppper
electromotivated 0:7478eabf00eb 531 @param value The value to clip
electromotivated 0:7478eabf00eb 532 @param lower The mininum allowable value
electromotivated 0:7478eabf00eb 533 @param upper The maximum allowable value
electromotivated 0:7478eabf00eb 534 @return The resulting clipped value
electromotivated 0:7478eabf00eb 535 */
electromotivated 0:7478eabf00eb 536 float clip(float value, float lower, float upper){
electromotivated 0:7478eabf00eb 537 return std::max(lower, std::min(value, upper));
electromotivated 0:7478eabf00eb 538 }
electromotivated 0:7478eabf00eb 539
electromotivated 0:7478eabf00eb 540 /**************************WEB PAGE TEXT**************************************/
electromotivated 0:7478eabf00eb 541 /*****************************************************************************
electromotivated 0:7478eabf00eb 542 Copy and past text below into a html file and save as the given file name to
electromotivated 0:7478eabf00eb 543 your SD card.
electromotivated 0:7478eabf00eb 544
electromotivated 1:1490beafb4f7 545 file name: pid_spd.html
electromotivated 0:7478eabf00eb 546
electromotivated 0:7478eabf00eb 547 html text:
electromotivated 0:7478eabf00eb 548
electromotivated 1:1490beafb4f7 549 <!DOCTYPE html>
electromotivated 1:1490beafb4f7 550 <html>
electromotivated 1:1490beafb4f7 551 <head>
electromotivated 1:1490beafb4f7 552 <title>PID Motor Speed Control</title>
electromotivated 1:1490beafb4f7 553 </head>
electromotivated 1:1490beafb4f7 554 <body>
electromotivated 1:1490beafb4f7 555 <h1>PID Motor Speed Control</h1>
electromotivated 1:1490beafb4f7 556 <h2>Motor Status</h2>
electromotivated 1:1490beafb4f7 557 <p>
electromotivated 1:1490beafb4f7 558 <form title="Motor Status">
electromotivated 1:1490beafb4f7 559 <input type="text" value="Some user information" size="25" readonly /><br>
electromotivated 1:1490beafb4f7 560 Current Setpoint:
electromotivated 1:1490beafb4f7 561 <input type="number" name="current_setpoint" value="0000.00" readonly /><br>
electromotivated 1:1490beafb4f7 562 Current Position:
electromotivated 1:1490beafb4f7 563 <input type="number" name="current_position" value="0000.00" readonly /><br>
electromotivated 1:1490beafb4f7 564 </form>
electromotivated 1:1490beafb4f7 565 </p>
electromotivated 1:1490beafb4f7 566 <h2>PID Status</h2>
electromotivated 1:1490beafb4f7 567 <form title="User Input" method="post">
electromotivated 1:1490beafb4f7 568 PID Controls: <br>
electromotivated 1:1490beafb4f7 569 Setpoint (RPM):
electromotivated 1:1490beafb4f7 570 <input type="number" name="setpoint_input" value="0000.00" step="0.01" size="6" /><br>
electromotivated 1:1490beafb4f7 571 Proportional Gain:
electromotivated 1:1490beafb4f7 572 <input type="number" name="kp_input" value="000.01" step="0.0001" size="6" /><br>
electromotivated 1:1490beafb4f7 573 Integral Gain:
electromotivated 1:1490beafb4f7 574 <input type="number" name="ki_input" value="000.01" step="0.0001" size="6" /><br>
electromotivated 1:1490beafb4f7 575 Derivative Gain:
electromotivated 1:1490beafb4f7 576 <input type="number" name="kd_input" value="000.00" step="0.0001" size="6" /><br>
electromotivated 1:1490beafb4f7 577 <br>
electromotivated 1:1490beafb4f7 578 <input type="submit" value="Submit" />
electromotivated 1:1490beafb4f7 579 <input type="submit" name="update" value="Update">
electromotivated 1:1490beafb4f7 580 <input type="submit" name="estop" value="STOP">
electromotivated 1:1490beafb4f7 581 </form>
electromotivated 1:1490beafb4f7 582 </body>
electromotivated 1:1490beafb4f7 583 </html>
electromotivated 1:1490beafb4f7 584
electromotivated 0:7478eabf00eb 585
electromotivated 0:7478eabf00eb 586 *****************************************************************************/
electromotivated 0:7478eabf00eb 587 /*****************************************************************************/