Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed
Revision 1:bba0ec7e075a, committed 2020-04-08
- Comitter:
- stollpa1
- Date:
- Wed Apr 08 09:13:33 2020 +0000
- Parent:
- 0:0a667cdbf4c1
- Commit message:
- P4 init;
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPScript.cpp Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,29 @@ +/* + * HTTPScript.cpp + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#include "HTTPScript.h" + +using namespace std; + +HTTPScript::HTTPScript() {} + +HTTPScript::~HTTPScript() {} + +/** + * This method must be implemented by derived classes. + * It gets called by the http server, when an object of this class is + * registered with the server, and the corresponding script is called + * by an http client. + * @param names a vector of the names of arguments passed to the server by + * the client with a URL. + * @param values a vector of the corresponding values of arguments passed + * to the server. + */ +string HTTPScript::call(vector<string> names, vector<string> values) { + + return ""; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPScript.h Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,28 @@ +/* + * HTTPScript.h + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#ifndef HTTP_SCRIPT_H_ +#define HTTP_SCRIPT_H_ + +#include <string> +#include <vector> + +/** + * This is the abstract http script superclass that needs to be derived + * by application specific http scripts. + * @see HTTPServer + */ +class HTTPScript { + + public: + + HTTPScript(); + virtual ~HTTPScript(); + virtual std::string call(std::vector<std::string> names, std::vector<std::string> values); +}; + +#endif /* HTTP_SCRIPT_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPScriptIMU.cpp Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,56 @@ +/* + * HTTPScriptIMU.cpp + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#include "HTTPScriptIMU.h" + +using namespace std; + +inline string float2String(float f) { + + char buffer[32]; + sprintf(buffer, "%.3f", f); + + return string(buffer); +} + +/** + * Create and initialize this http script. + * @param imu a reference to the imu to read data from. + */ +HTTPScriptIMU::HTTPScriptIMU(IMU& imu) : imu(imu) {} + +HTTPScriptIMU::~HTTPScriptIMU() {} + +/** + * This method gets called by the http server, when an object of this class is + * registered with the server, and the corresponding script is called + * by an http client. + */ +string HTTPScriptIMU::call(vector<string> names, vector<string> values) { + + string response; + + response += " <imu>\r\n"; + response += " <acceleration>\r\n"; + response += " <x><float>"+float2String(imu.readAccelerationX())+"</float></x>\r\n"; + response += " <y><float>"+float2String(imu.readAccelerationY())+"</float></y>\r\n"; + response += " <z><float>"+float2String(imu.readAccelerationZ())+"</float></z>\r\n"; + response += " </acceleration>\r\n"; + response += " <gyro>\r\n"; + response += " <x><float>"+float2String(imu.readGyroX())+"</float></x>\r\n"; + response += " <y><float>"+float2String(imu.readGyroY())+"</float></y>\r\n"; + response += " <z><float>"+float2String(imu.readGyroZ())+"</float></z>\r\n"; + response += " </gyro>\r\n"; + response += " <magnetometer>\r\n"; + response += " <x><float>"+float2String(imu.readMagnetometerX())+"</float></x>\r\n"; + response += " <y><float>"+float2String(imu.readMagnetometerY())+"</float></y>\r\n"; + response += " <z><float>"+float2String(imu.readMagnetometerZ())+"</float></z>\r\n"; + response += " </magnetometer>\r\n"; + response += " </imu>\r\n"; + + return response; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPScriptIMU.h Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,33 @@ +/* + * HTTPScriptIMU.h + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#ifndef HTTP_SCRIPT_IMU_H_ +#define HTTP_SCRIPT_IMU_H_ + +#include <string> +#include <vector> +#include "HTTPScript.h" +#include "IMU.h" + +/** + * This is a specific http script to read sensor data from an imu. + * @see HTTPServer + */ +class HTTPScriptIMU : public HTTPScript { + + public: + + HTTPScriptIMU(IMU& imu); + virtual ~HTTPScriptIMU(); + virtual std::string call(std::vector<std::string> names, std::vector<std::string> values); + + private: + + IMU& imu; +}; + +#endif /* HTTP_SCRIPT_IMU_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.cpp Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,433 @@ +/* + * HTTPServer.cpp + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#include <algorithm> +#include "HTTPScript.h" +#include "HTTPServer.h" + +using namespace std; + +inline string int2String(int i) { + + char buffer[32]; + sprintf(buffer, "%d", i); + + return string(buffer); +} + +const unsigned int HTTPServer::INPUT_BUFFER_SIZE = 1024; // size of receive buffer, given in [bytes] +const int HTTPServer::SOCKET_TIMEOUT = 1000; // timeout of socket, given in [ms] + +/** + * Create and initialize an http server object. + * @param ethernet a reference to the embedded TCP/IP stack to use. + */ +HTTPServer::HTTPServer(EthernetInterface& ethernet) : ethernet(ethernet), thread(osPriorityNormal, STACK_SIZE) { + + // start thread + + thread.start(callback(this, &HTTPServer::run)); +} + +/** + * Delete the http server object. + */ +HTTPServer::~HTTPServer() {} + +/** + * Registers the given script with the http server. + * This allows to call a method of this script object + * through virtual cgi-bin requests from a remote system. + */ +void HTTPServer::add(string name, HTTPScript* httpScript) { + + httpScriptNames.push_back(name); + httpScripts.push_back(httpScript); +} + +/** + * Decodes a given URL string into a standard text string. + */ +string HTTPServer::urlDecoder(string url) { + + size_t pos = 0; + while ((pos = url.find("+")) != string::npos) url = url.substr(0, pos)+" "+url.substr(pos+1); + while ((pos = url.find("%08")) != string::npos) url = url.substr(0, pos)+"\b"+url.substr(pos+3); + while ((pos = url.find("%09")) != string::npos) url = url.substr(0, pos)+"\t"+url.substr(pos+3); + while ((pos = url.find("%0A")) != string::npos) url = url.substr(0, pos)+"\n"+url.substr(pos+3); + while ((pos = url.find("%0D")) != string::npos) url = url.substr(0, pos)+"\r"+url.substr(pos+3); + while ((pos = url.find("%20")) != string::npos) url = url.substr(0, pos)+" "+url.substr(pos+3); + while ((pos = url.find("%22")) != string::npos) url = url.substr(0, pos)+"\""+url.substr(pos+3); + while ((pos = url.find("%23")) != string::npos) url = url.substr(0, pos)+"#"+url.substr(pos+3); + while ((pos = url.find("%24")) != string::npos) url = url.substr(0, pos)+"$"+url.substr(pos+3); + while ((pos = url.find("%25")) != string::npos) url = url.substr(0, pos)+"%"+url.substr(pos+3); + while ((pos = url.find("%26")) != string::npos) url = url.substr(0, pos)+"&"+url.substr(pos+3); + while ((pos = url.find("%2B")) != string::npos) url = url.substr(0, pos)+"+"+url.substr(pos+3); + while ((pos = url.find("%2C")) != string::npos) url = url.substr(0, pos)+","+url.substr(pos+3); + while ((pos = url.find("%2F")) != string::npos) url = url.substr(0, pos)+"/"+url.substr(pos+3); + while ((pos = url.find("%3A")) != string::npos) url = url.substr(0, pos)+":"+url.substr(pos+3); + while ((pos = url.find("%3B")) != string::npos) url = url.substr(0, pos)+";"+url.substr(pos+3); + while ((pos = url.find("%3C")) != string::npos) url = url.substr(0, pos)+"<"+url.substr(pos+3); + while ((pos = url.find("%3D")) != string::npos) url = url.substr(0, pos)+"="+url.substr(pos+3); + while ((pos = url.find("%3E")) != string::npos) url = url.substr(0, pos)+">"+url.substr(pos+3); + while ((pos = url.find("%3F")) != string::npos) url = url.substr(0, pos)+"?"+url.substr(pos+3); + while ((pos = url.find("%40")) != string::npos) url = url.substr(0, pos)+"@"+url.substr(pos+3); + + return url; +} + +/** + * This <code>run()</code> method binds the TCP/IP server to a given port number + * and enters an infinite loop that waits for http requests and then processes + * these requests and returns a response. + */ +void HTTPServer::run() { + + // bind the server to a given port number + + server.open(ðernet); + server.bind(PORT_NUMBER); + server.listen(); + + // enter infinite loop + + while (true) { + + TCPSocket* client = server.accept(); + if (client != NULL) { + + client->set_blocking(true); + client->set_timeout(SOCKET_TIMEOUT); // set timeout of socket + + // read input + + char buffer[INPUT_BUFFER_SIZE]; + int size = client->recv(buffer, sizeof(buffer)); + + if (size > 0) { + + string input(buffer, size); + string header; + string output; + + // parse input + + if ((input.find("GET") == 0) || (input.find("HEAD") == 0)) { + + if (input.find("cgi-bin") != string::npos) { + + // process script request with arguments + + string script = input.substr(input.find("cgi-bin/")+8, input.find(" ", input.find("cgi-bin/")+8)-input.find("cgi-bin/")-8); + string name; + vector<string> names; + vector<string> values; + + if (script.find("?") != string::npos) { + + name = script.substr(0, script.find("?")); + script = script.substr(script.find("?")+1); + + vector<string> arguments; + while (script.find("&") != string::npos) { + arguments.push_back(script.substr(0, script.find("&"))); + script = script.substr(script.find("&")+1); + } + arguments.push_back(script); + + for (int i = 0; i < arguments.size(); i++) { + + if (arguments[i].find("=") != string::npos) { + + names.push_back(arguments[i].substr(0, arguments[i].find("="))); + values.push_back(urlDecoder(arguments[i].substr(arguments[i].find("=")+1))); + + } else { + + names.push_back(arguments[i]); + values.push_back(""); + } + } + + } else { + + name = script; + } + + // look for corresponding script + + for (int i = 0; i < min(httpScriptNames.size(), httpScripts.size()); i++) { + + if (httpScriptNames[i].compare(name) == 0) { + + output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"; + output += "<!DOCTYPE html>\r\n"; + output += "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\r\n"; + output += "<body>\r\n"; + output += httpScripts[i]->call(names, values); + output += "</body>\r\n"; + output += "</html>\r\n"; + + header = "HTTP/1.1 200 OK\r\n"; + header += "Content-Length: "+int2String(output.size())+"\r\n"; + header += "Content-Type: text/xml\r\n"; + header += "Expires: 0\r\n"; + header += "\r\n"; + + output = header+output; + } + } + + // requested script was not found on this server + + if ((output).size() == 0) { + + output = "<!DOCTYPE html>\r\n"; + output += "<html lang=\"en\">\r\n"; + output += "<head>\r\n"; + output += " <title>404 Not Found</title>\r\n"; + output += " <style type=\"text/css\">\r\n"; + output += " h2 {font-family:Helvetica,Arial,sans-serif; font-size: 24; color:#FFFFFF;}\r\n"; + output += " p {font-family:Helvetica,Arial,sans-serif; font-size: 14; color:#444444;}\r\n"; + output += " </style>\r\n"; + output += "</head>\r\n"; + output += "<body leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\r\n"; + output += " <table width=\"100%\" height=\"100%\" border=\"0\" frame=\"void\" cellspacing=\"0\" cellpadding=\"20\">\r\n"; + output += " <tr>\r\n"; + output += " <th width=\"100%\" height=\"30\" bgcolor=\"#0064A6\"><h2>400 Bad Request</h2></th>\r\n"; + output += " </tr>\r\n"; + output += " <tr>\r\n"; + output += " <td valign=\"top\">\r\n"; + output += " <p>The requested script could not be found on this server!</p>\r\n"; + output += " </td>\r\n"; + output += " </tr>\r\n"; + output += " </table>\r\n"; + output += "</body>\r\n"; + output += "</html>\r\n"; + + header = "HTTP/1.1 404 Not Found\r\n"; + header += "Content-Length: "+int2String(output.size())+"\r\n"; + header += "Content-Type: text/html\r\n"; + header += "\r\n"; + + output = header+output; + } + + // write output + + void* address = (void*)(output).c_str(); + int offset = 0; + while (offset < (output).size()) offset += client->send((void*)(static_cast<int>(reinterpret_cast<intptr_t>(address))+offset), (output).size()-offset); + + } else { + + // transmit static file + + output = "<!DOCTYPE html>\r\n"; + output += "<html lang=\"en\">\r\n"; + output += "<head>\r\n"; + output += " <meta charset=\"utf-8\"/>\r\n"; + output += " <title>IMU Sensor Data</title>\r\n"; + output += " <style type=\"text/css\">\r\n"; + output += " html {background-color: #223344;}\r\n"; + output += " h2 {font-family:Helvetica,Arial,sans-serif; font-size: 24; color:#FFFFFF;}\r\n"; + output += " p, td {font-family:Helvetica,Arial,sans-serif; font-size: 16; color:#EEEEEE;}\r\n"; + output += " </style>\r\n"; + output += "</head>\r\n"; + output += "<body leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\r\n"; + output += " <script type=\"text/javascript\">\r\n"; + output += " var ax = [];\r\n"; + output += " var ay = [];\r\n"; + output += " var az = [];\r\n"; + output += " var gx = [];\r\n"; + output += " var gy = [];\r\n"; + output += " var gz = [];\r\n"; + output += " var mx = [];\r\n"; + output += " var my = [];\r\n"; + output += " var mz = [];\r\n"; + output += " var xmlhttp = null;\r\n"; + output += " var task = window.setInterval(\"update()\", 50);\r\n"; + output += " function update() {\r\n"; + output += " if (window.XMLHttpRequest) {\r\n"; + output += " xmlhttp = new XMLHttpRequest();\r\n"; + output += " } else if (window.ActiveXObject) {\r\n"; + output += " try {\r\n"; + output += " xmlhttp = new ActiveXObject(\"Msxml2.XMLHTTP\");\r\n"; + output += " } catch (exception) {\r\n"; + output += " try {\r\n"; + output += " xmlhttp = new ActiveXObject(\"Microsoft.XMLHTTP\");\r\n"; + output += " } catch (exception) {}\r\n"; + output += " }\r\n"; + output += " }\r\n"; + output += " xmlhttp.onreadystatechange = refresh;\r\n"; + output += " xmlhttp.open(\"GET\", \"/cgi-bin/imu\");\r\n"; + output += " xmlhttp.send(null);\r\n"; + output += " }\r\n"; + output += " function refresh() {\r\n"; + output += " if (xmlhttp.readyState == 4) {\r\n"; + output += " var xml = xmlhttp.responseXML;\r\n"; + output += " var floatValues = xml.getElementsByTagName(\"float\");\r\n"; + output += " ax.push(floatValues[0].childNodes[0].nodeValue); if (ax.length > 200) ax.shift();\r\n"; + output += " ay.push(floatValues[1].childNodes[0].nodeValue); if (ay.length > 200) ay.shift();\r\n"; + output += " az.push(floatValues[2].childNodes[0].nodeValue); if (az.length > 200) az.shift();\r\n"; + output += " gx.push(floatValues[3].childNodes[0].nodeValue); if (gx.length > 200) gx.shift();\r\n"; + output += " gy.push(floatValues[4].childNodes[0].nodeValue); if (gy.length > 200) gy.shift();\r\n"; + output += " gz.push(floatValues[5].childNodes[0].nodeValue); if (gz.length > 200) gz.shift();\r\n"; + output += " mx.push(floatValues[6].childNodes[0].nodeValue); if (mx.length > 200) mx.shift();\r\n"; + output += " my.push(floatValues[7].childNodes[0].nodeValue); if (my.length > 200) my.shift();\r\n"; + output += " mz.push(floatValues[8].childNodes[0].nodeValue); if (mz.length > 200) mz.shift();\r\n"; + output += " drawPlot(\"ax\", 300, 200, ax, \"m/s2\");\r\n"; + output += " drawPlot(\"ay\", 300, 200, ay, \"m/s2\");\r\n"; + output += " drawPlot(\"az\", 300, 200, az, \"m/s2\");\r\n"; + output += " drawPlot(\"gx\", 300, 200, gx, \"rad/s\");\r\n"; + output += " drawPlot(\"gy\", 300, 200, gy, \"rad/s\");\r\n"; + output += " drawPlot(\"gz\", 300, 200, gz, \"rad/s\");\r\n"; + output += " drawPlot(\"mx\", 300, 200, mx, \"gauss\");\r\n"; + output += " drawPlot(\"my\", 300, 200, my, \"gauss\");\r\n"; + output += " drawPlot(\"mz\", 300, 200, mz, \"gauss\");\r\n"; + output += " }\r\n"; + output += " }\r\n"; + output += " function drawPlot(id, width, height, value, valueUnit) {\r\n"; + output += " var canvas = document.getElementById(id);\r\n"; + output += " canvas.width = 2*width;\r\n"; + output += " canvas.height = 2*height;\r\n"; + output += " canvas.style.width = width+\"px\";\r\n"; + output += " canvas.style.height = height+\"px\";\r\n"; + output += " var ctx = canvas.getContext(\"2d\");\r\n"; + output += " ctx.scale(2,2);\r\n"; + output += " ctx.fillStyle = \"#FFFFFF11\";\r\n"; + output += " ctx.fillRect(0.5, 0.5, width-1, height-1);\r\n"; + output += " var valueMin = value[0];\r\n"; + output += " var valueMax = value[0];\r\n"; + output += " for (i = 0; i < value.length; i++) {\r\n"; + output += " valueMin = Math.min(valueMin, value[i]);\r\n"; + output += " valueMax = Math.max(valueMax, value[i]);\r\n"; + output += " }\r\n"; + output += " if (valueMax-valueMin < 2.0/Math.pow(10.0, 3)) {\r\n"; + output += " var valueMean = (valueMin+valueMax)/2.0;\r\n"; + output += " valueMin = valueMean-1.0/Math.pow(10.0, 3);\r\n"; + output += " valueMax = valueMean+1.0/Math.pow(10.0, 3);\r\n"; + output += " }\r\n"; + output += " var valueStep = (valueMax-valueMin)/(height/100); if (valueStep < 1.0/Math.pow(10.0, 3)) valueStep = 1.0/Math.pow(10.0, 3);\r\n"; + output += " var valueGain = Math.pow(10.0, Math.floor(Math.log(valueStep)/Math.log(10.0)));\r\n"; + output += " valueStep = valueStep/valueGain;\r\n"; + output += " if (valueStep > 5.0) valueStep = 5.0*valueGain; else if (valueStep > 2.0) valueStep = 2.0*valueGain; else valueStep = valueGain;\r\n"; + output += " valueMin = Math.floor(valueMin/valueStep-0.25)*valueStep;\r\n"; + output += " valueMax = Math.ceil(valueMax/valueStep+0.25)*valueStep;\r\n"; + output += " ctx.strokeStyle = \"#EEEEEE\";\r\n"; + output += " ctx.lineWidth = 1;\r\n"; + output += " ctx.beginPath();\r\n"; + output += " for (valueCurrent = valueMin+valueStep; valueCurrent < valueMax-valueStep/2.0; valueCurrent += valueStep) {\r\n"; + output += " ctx.moveTo(0, (valueMax-valueCurrent)/(valueMax-valueMin)*height+0.5);\r\n"; + output += " ctx.lineTo(width, (valueMax-valueCurrent)/(valueMax-valueMin)*height+0.5);\r\n"; + output += " }\r\n"; + output += " ctx.stroke();\r\n"; + output += " ctx.font = \"normal 14px sans-serif\";\r\n"; + output += " ctx.textBaseline = \"bottom\";\r\n"; + output += " ctx.fillStyle = \"white\";\r\n"; + output += " for (valueCurrent = valueMin+valueStep; valueCurrent < valueMax-valueStep/2.0; valueCurrent += valueStep) {\r\n"; + output += " ctx.fillText(valueCurrent.toFixed(2), 10.0, (valueMax-valueCurrent)/(valueMax-valueMin)*height-5.0);\r\n"; + output += " }\r\n"; + output += " ctx.fillText((value[value.length-1]*1.0).toFixed(3)+\" [\"+valueUnit+\"]\", width/2.0, (valueMax-valueCurrent+valueStep)/(valueMax-valueMin)*height-5.0);\r\n"; + output += " ctx.strokeStyle = \"#FF0000\";\r\n"; + output += " ctx.lineWidth = 1;\r\n"; + output += " ctx.beginPath();\r\n"; + output += " ctx.moveTo(0, (valueMax-value[0])/(valueMax-valueMin)*height+0.5);\r\n"; + output += " for (i = 1; i < value.length; i++) {\r\n"; + output += " ctx.lineTo(i/(value.length-1)*width, (valueMax-value[i])/(valueMax-valueMin)*height+0.5);\r\n"; + output += " }\r\n"; + output += " ctx.stroke();\r\n"; + output += " ctx.strokeStyle = \"white\";\r\n"; + output += " ctx.lineWidth = 1;\r\n"; + output += " ctx.strokeRect(0.5, 0.5, width-1, height-1);\r\n"; + output += " }\r\n"; + output += " </script>"; + output += " <table width=\"100%\" height=\"100%\" border=\"0\" frame=\"void\" cellspacing=\"20\" cellpadding=\"0\">\r\n"; + output += " <tr>\r\n"; + output += " <th colspan=\"3\" width=\"100%\" height=\"20\"><h2>IMU Sensor Data</h2></th>\r\n"; + output += " </tr>\r\n"; + output += " <tr>\r\n"; + output += " <td>Acceleration X [m/s²]<br/><br/><canvas id=\"ax\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " <td>Gyro X [rad/s]<br/><br/><canvas id=\"gx\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " <td>Magnetometer X [gauss]<br/><br/><canvas id=\"mx\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " </tr>\r\n"; + output += " <tr>\r\n"; + output += " <td>Acceleration Y [m/s²]<br/><br/><canvas id=\"ay\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " <td>Gyro Y [rad/s]<br/><br/><canvas id=\"gy\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " <td>Magnetometer Y [gauss]<br/><br/><canvas id=\"my\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " </tr>\r\n"; + output += " <tr>\r\n"; + output += " <td>Acceleration Z [m/s²]<br/><br/><canvas id=\"az\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " <td>Gyro Z [rad/s]<br/><br/><canvas id=\"gz\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " <td>Magnetometer Z [gauss]<br/><br/><canvas id=\"mz\" width=\"300\" height=\"200\"></canvas></td>\r\n"; + output += " </tr>\r\n"; + output += " </table>\r\n"; + output += "</body>\r\n"; + output += "</html>\r\n"; + + header = "HTTP/1.1 404 Not Found\r\n"; + header += "Content-Length: "+int2String(output.size())+"\r\n"; + header += "Content-Type: text/html\r\n"; + header += "\r\n"; + + output = header+output; + + // write output + + void* address = (void*)output.c_str(); + int offset = 0; + while (offset < output.size()) offset += client->send((void*)(static_cast<int>(reinterpret_cast<intptr_t>(address))+offset), output.size()-offset); + } + + } else { + + // the http method is not known + + output = "<!DOCTYPE html>\r\n"; + output += "<html lang=\"en\">\r\n"; + output += "<head>\r\n"; + output += " <title>400 Bad Request</title>\r\n"; + output += " <style type=\"text/css\">\r\n"; + output += " h2 {font-family:Helvetica,Arial,sans-serif; font-size: 24; color:#FFFFFF;}\r\n"; + output += " p {font-family:Helvetica,Arial,sans-serif; font-size: 14; color:#444444;}\r\n"; + output += " </style>\r\n"; + output += "</head>\r\n"; + output += "<body leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\r\n"; + output += " <table width=\"100%\" height=\"100%\" border=\"0\" frame=\"void\" cellspacing=\"0\" cellpadding=\"20\">\r\n"; + output += " <tr>\r\n"; + output += " <th width=\"100%\" height=\"30\" bgcolor=\"#0064A6\"><h2>400 Bad Request</h2></th>\r\n"; + output += " </tr>\r\n"; + output += " <tr>\r\n"; + output += " <td valign=\"top\">\r\n"; + output += " <p>The requested method is not supported by this server!</p>\r\n"; + output += " </td>\r\n"; + output += " </tr>\r\n"; + output += " </table>\r\n"; + output += "</body>\r\n"; + output += "</html>\r\n"; + + header = "HTTP/1.1 400 Bad Request\r\n"; + header += "Content-Length: "+int2String(output.size())+"\r\n"; + header += "Content-Type: text/html\r\n"; + header += "\r\n"; + + output = header+output; + + // write output + + void* address = (void*)output.c_str(); + int offset = 0; + while (offset < output.size()) offset += client->send((void*)(static_cast<int>(reinterpret_cast<intptr_t>(address))+offset), output.size()-offset); + } + } + + client->close(); + + } // client != NULL + + } // infinite while loop +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.h Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,105 @@ +/* + * HTTPServer.h + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#ifndef HTTP_SERVER_H_ +#define HTTP_SERVER_H_ + +#include <string> +#include <vector> +#include <mbed.h> +#include <EthernetInterface.h> + +class HTTPScript; + +/** + * The <code>HTTPServer</code> class implements a simple webserver that is able to + * transmit files over an ethernet connection and allows to call scripts that are + * registered with the server. + * <br/> + * An http server can be created and started as follows: + * <pre><code> + * EthernetInterface* ethernet = new EthernetInterface(); <span style="color:#008000">// init the TCP/IP stack</span> + * ethernet->set_network("192.168.0.10", "255.255.255.0", "192.168.0.1"); + * ethernet->connect(); + * + * HTTPServer* httpServer = new HTTPServer(ethernet); <span style="color:#008000">// creates an http server</span> + * ... + * </code></pre> + * This http server allows to execute application specific code implemented as http + * scripts. These scripts are objects derived from the <code>HTTPScript</code> superclass. + * <br/> + * An example of an application specific script is given below: + * <pre><code> + * class MyHTTPScript : public HTTPScript { + * public: + * MyHTTPScript(); + * virtual ~MyHTTPScript(); + * string call(vector<string> names, vector<string> values); + * }; + * + * string MyHTTPScript::call(vector<string> names, vector<string> values) { + * + * string response; + * + * response += " <h2>"; + * for (uint32_t i = 0; i < min(names.size(), values.size()); i++) { + * response += " <p>"+names[i]+"="+values[i]+"</p>"; + * } + * response += " </h2>"; + * + * return response; + * } + * </code></pre> + * This script returns the parameters that were passed to it by the http server. + * <br/> + * Before this script can be used, it needs to be registered with the http server + * with the <code>add()</code> method as follows: + * <pre><code> + * httpServer->add("myScript", new MyHTTPScript()); + * </code></pre> + * When the <code>call()</code> method of the script is called by the http server, + * it receives two string vectors: a vector with the names of the arguments passed + * in the URL, and a vector with the corresponding values of the arguments. + * <br/> + * An example of an http request calling this script is as follows: + * <pre><code> + * http://192.168.1.10/cgi-bin/myScript?x=0.5&y=-0.1&z=0.2 + * </code></pre> + * The vectors of arguments passed to the <code>call()</code> method are then + * {'x', 'y', 'z'} for the names and {'0.5', '-0.1', '0.2'} for the values. + * <br/> + * The response of the <code>call()</code> method is a <code>string</code> object + * which is placed within an xhtml page, which in turn is returned by the http + * server to the requesting http client. + * @see HTTPScript + */ +class HTTPServer { + + public: + + HTTPServer(EthernetInterface& ethernet); + virtual ~HTTPServer(); + void add(std::string name, HTTPScript* httpScript); + + private: + + static const unsigned int STACK_SIZE = 16384; // stack size of thread, given in [bytes] + static const int PORT_NUMBER = 80; // port number of server to use + static const unsigned int INPUT_BUFFER_SIZE; // size of receive buffer, given in [bytes] + static const int SOCKET_TIMEOUT; // timeout of socket, given in [ms] + + EthernetInterface& ethernet; + TCPSocket server; + std::vector<std::string> httpScriptNames; + std::vector<HTTPScript*> httpScripts; + Thread thread; + + string urlDecoder(std::string url); + void run(); +}; + +#endif /* HTTP_SERVER_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IMU.cpp Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,267 @@ +/* + * IMU.cpp + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#include "IMU.h" + +using namespace std; + +const float IMU::PERIOD = 0.002f; // the period of the timer interrupt, given in [s] +const float IMU::M_PI = 3.14159265358979323846f; // the mathematical constant PI + +/** + * Creates an IMU object. + * @param spi a reference to an spi controller to use. + * @param csAG the chip select output for the accelerometer and the gyro sensor. + * @param csM the chip select output for the magnetometer. + */ +IMU::IMU(SPI& spi, DigitalOut& csAG, DigitalOut& csM) : spi(spi), csAG(csAG), csM(csM), thread(osPriorityHigh, STACK_SIZE) { + + // initialize SPI interface + + spi.format(8, 3); + spi.frequency(1000000); + + // reset chip select lines to logical high + + csAG = 1; + csM = 1; + + // initialize accelerometer and gyro + + writeRegister(csAG, CTRL_REG1_G, 0xC3); // ODR 952 Hz, full scale 245 deg/s + writeRegister(csAG, CTRL_REG2_G, 0x00); // disable interrupt generation + writeRegister(csAG, CTRL_REG3_G, 0x00); // disable low power mode, disable high pass filter, high pass cutoff frequency 57 Hz + writeRegister(csAG, CTRL_REG4, 0x38); // enable gyro in all 3 axis + writeRegister(csAG, CTRL_REG5_XL, 0x38); // no decimation, enable accelerometer in all 3 axis + writeRegister(csAG, CTRL_REG6_XL, 0xC0); // ODR 952 Hz, full scale 2g + writeRegister(csAG, CTRL_REG7_XL, 0x00); // high res mode disabled, filter bypassed + writeRegister(csAG, CTRL_REG8, 0x00); // 4-wire SPI interface, LSB at lower address + writeRegister(csAG, CTRL_REG9, 0x04); // disable gyro sleep mode, disable I2C interface, disable FIFO + writeRegister(csAG, CTRL_REG10, 0x00); // self test disabled + + // initialize magnetometer + + writeRegister(csM, CTRL_REG1_M, 0x10); // temperature not compensated, low power mode for x & y axis, data rate 10 Hz + writeRegister(csM, CTRL_REG2_M, 0x00); // full scale 4 gauss + writeRegister(csM, CTRL_REG3_M, 0x80); // disable I2C interface, low power mode, SPI write only, continuous conversion mode + writeRegister(csM, CTRL_REG4_M, 0x00); // low power mode for z axis, LSB at lower address + writeRegister(csM, CTRL_REG5_M, 0x00); // fast read disabled + + // start thread and timer interrupt + + thread.start(callback(this, &IMU::run)); + ticker.attach(callback(this, &IMU::sendThreadFlag), PERIOD); +} + +/** + * Deletes the IMU object. + */ +IMU::~IMU() { + + ticker.detach(); +} + +/** + * This private method allows to write a register value. + * @param cs the chip select output to use, either csAG or csM. + * @param address the 7 bit address of the register. + * @param value the value to write into the register. + */ +void IMU::writeRegister(DigitalOut& cs, char address, char value) { + + cs = 0; + + spi.write(0x7F & address); + spi.write(value & 0xFF); + + cs = 1; +} + +/** + * This private method allows to read a register value. + * @param cs the chip select output to use, either csAG or csM. + * @param address the 7 bit address of the register. + * @return the value read from the register. + */ +char IMU::readRegister(DigitalOut& cs, char address) { + + cs = 0; + + spi.write(0x80 | address); + int value = spi.write(0xFF); + + cs = 1; + + return (char)(value & 0xFF); +} + +/** + * Reads the acceleration in x-direction. + * @return the acceleration in x-direction, given in [m/s2]. + */ +float IMU::readAccelerationX() { + + mutex.lock(); + + char low = readRegister(csAG, OUT_X_L_XL); + char high = readRegister(csAG, OUT_X_H_XL); + + short value = (short)(((unsigned short)high << 8) | (unsigned short)low); + + mutex.unlock(); + + return (float)value/32768.0f*2.0f*9.81f; +} + +/** + * Reads the acceleration in y-direction. + * @return the acceleration in y-direction, given in [m/s2]. + */ +float IMU::readAccelerationY() { + + mutex.lock(); + + char low = readRegister(csAG, OUT_Y_L_XL); + char high = readRegister(csAG, OUT_Y_H_XL); + + short value = (short)(((unsigned short)high << 8) | (unsigned short)low); + + mutex.unlock(); + + return (float)value/32768.0f*2.0f*9.81f; +} + +/** + * Reads the acceleration in z-direction. + * @return the acceleration in z-direction, given in [m/s2]. + */ +float IMU::readAccelerationZ() { + + mutex.lock(); + + char low = readRegister(csAG, OUT_Z_L_XL); + char high = readRegister(csAG, OUT_Z_H_XL); + + short value = (short)(((unsigned short)high << 8) | (unsigned short)low); + + mutex.unlock(); + + return (float)value/32768.0f*2.0f*9.81f; +} + +/** + * Reads the gyroscope about the x-axis. + * @return the rotational speed about the x-axis given in [rad/s]. + */ +float IMU::readGyroX() { + + mutex.lock(); + + char low = readRegister(csAG, OUT_X_L_G); + char high = readRegister(csAG, OUT_X_H_G); + + short value = (short)(((unsigned short)high << 8) | (unsigned short)low); + + mutex.unlock(); + + return (float)value/32768.0f*245.0f*M_PI/180.0f; +} + +/** + * Reads the gyroscope about the y-axis. + * @return the rotational speed about the y-axis given in [rad/s]. + */ +float IMU::readGyroY() { + + mutex.lock(); + + char low = readRegister(csAG, OUT_Y_L_G); + char high = readRegister(csAG, OUT_Y_H_G); + + short value = (short)(((unsigned short)high << 8) | (unsigned short)low); + + mutex.unlock(); + + return (float)value/32768.0f*245.0f*M_PI/180.0f; +} + +/** + * Reads the gyroscope about the z-axis. + * @return the rotational speed about the z-axis given in [rad/s]. + */ +float IMU::readGyroZ() { + + mutex.lock(); + + char low = readRegister(csAG, OUT_Z_L_G); + char high = readRegister(csAG, OUT_Z_H_G); + + short value = (short)(((unsigned short)high << 8) | (unsigned short)low); + + mutex.unlock(); + + return (float)value/32768.0f*245.0f*M_PI/180.0f; +} + +/** + * Reads the magnetic field in x-direction. + * @return the magnetic field in x-direction, given in [Gauss]. + */ +float IMU::readMagnetometerX() { + + // bitte implementieren! + + return 0.0f; +} + +/** + * Reads the magnetic field in y-direction. + * @return the magnetic field in y-direction, given in [Gauss]. + */ +float IMU::readMagnetometerY() { + + // bitte implementieren! + + return 0.0f; +} + +/** + * Reads the magnetic field in z-direction. + * @return the magnetic field in z-direction, given in [Gauss]. + */ +float IMU::readMagnetometerZ() { + + // bitte implementieren! + + return 0.0f; +} + +/** + * This method is called by the ticker timer interrupt service routine. + * It sends a flag to the thread to make it run again. + */ +void IMU::sendThreadFlag() { + + thread.flags_set(threadFlag); +} + +/** + * This <code>run()</code> method contains an infinite loop with the run logic. + */ +void IMU::run() { + + while (true) { + + // wait for the periodic thread flag + + ThisThread::flags_wait_any(threadFlag); + + // filter and process sensor data... + + + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IMU.h Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,91 @@ +/* + * IMU.h + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#ifndef IMU_H_ +#define IMU_H_ + +#include <cstdlib> +#include <mbed.h> +#include "ThreadFlag.h" + +/** + * This is a device driver class for the ST LSM9DS1 inertial measurement unit. + */ +class IMU { + + public: + + IMU(SPI& spi, DigitalOut& csAG, DigitalOut& csM); + virtual ~IMU(); + float readAccelerationX(); + float readAccelerationY(); + float readAccelerationZ(); + float readGyroX(); + float readGyroY(); + float readGyroZ(); + float readMagnetometerX(); + float readMagnetometerY(); + float readMagnetometerZ(); + + private: + + static const char WHO_AM_I = 0x0F; + static const char CTRL_REG1_G = 0x10; + static const char CTRL_REG2_G = 0x11; + static const char CTRL_REG3_G = 0x12; + static const char OUT_X_L_G = 0x18; + static const char OUT_X_H_G = 0x19; + static const char OUT_Y_L_G = 0x1A; + static const char OUT_Y_H_G = 0x1B; + static const char OUT_Z_L_G = 0x1C; + static const char OUT_Z_H_G = 0x1D; + static const char CTRL_REG4 = 0x1E; + static const char CTRL_REG5_XL = 0x1F; + static const char CTRL_REG6_XL = 0x20; + static const char CTRL_REG7_XL = 0x21; + static const char CTRL_REG8 = 0x22; + static const char CTRL_REG9 = 0x23; + static const char CTRL_REG10 = 0x24; + static const char OUT_X_L_XL = 0x28; + static const char OUT_X_H_XL = 0x29; + static const char OUT_Y_L_XL = 0x2A; + static const char OUT_Y_H_XL = 0x2B; + static const char OUT_Z_L_XL = 0x2C; + static const char OUT_Z_H_XL = 0x2D; + + static const char WHO_AM_I_M = 0x0F; + static const char CTRL_REG1_M = 0x20; + static const char CTRL_REG2_M = 0x21; + static const char CTRL_REG3_M = 0x22; + static const char CTRL_REG4_M = 0x23; + static const char CTRL_REG5_M = 0x24; + static const char OUT_X_L_M = 0x28; + static const char OUT_X_H_M = 0x29; + static const char OUT_Y_L_M = 0x2A; + static const char OUT_Y_H_M = 0x2B; + static const char OUT_Z_L_M = 0x2C; + static const char OUT_Z_H_M = 0x2D; + + static const unsigned int STACK_SIZE = 4096; // stack size of thread, given in [bytes] + static const float PERIOD; // the period of the timer interrupt, given in [s] + static const float M_PI; // the mathematical constant PI + + SPI& spi; + DigitalOut& csAG; + DigitalOut& csM; + Mutex mutex; + ThreadFlag threadFlag; + Thread thread; + Ticker ticker; + + void writeRegister(DigitalOut& cs, char address, char value); + char readRegister(DigitalOut& cs, char address); + void sendThreadFlag(); + void run(); +}; + +#endif /* IMU_H_ */ +
--- a/Main.cpp Wed Mar 25 12:22:47 2020 +0000 +++ b/Main.cpp Wed Apr 08 09:13:33 2020 +0000 @@ -5,15 +5,23 @@ */ #include <mbed.h> +#include <EthernetInterface.h> +#include "IMU.h" +#include "HTTPServer.h" +#include "HTTPScriptIMU.h" #include "IRSensor.h" #include "EncoderCounter.h" #include "Controller.h" #include "StateMachine.h" +#include "ThreadFlag.h" int main() { + // initialise digital inputs and outputs + printf("Initialise digital inputs and outputs...\r\n"); + DigitalIn button(USER_BUTTON); DigitalOut ledGreen(LED1); @@ -26,71 +34,93 @@ DigitalOut led3(PD_2); DigitalOut led4(PD_7); DigitalOut led5(PD_5); - + // create distance sensor objects - + AnalogIn distance(PA_0); DigitalOut enable(PG_1); DigitalOut bit0(PF_0); DigitalOut bit1(PF_1); DigitalOut bit2(PF_2); - + enable = 1; - + IRSensor irSensor0(distance, bit0, bit1, bit2, 0); IRSensor irSensor1(distance, bit0, bit1, bit2, 1); IRSensor irSensor2(distance, bit0, bit1, bit2, 2); IRSensor irSensor3(distance, bit0, bit1, bit2, 3); IRSensor irSensor4(distance, bit0, bit1, bit2, 4); IRSensor irSensor5(distance, bit0, bit1, bit2, 5); - + // create motor controller objects - - DigitalOut enableMotorDriver(PG_0); + + DigitalOut enableMotorDriver(PG_0); DigitalIn motorDriverFault(PD_1); DigitalIn motorDriverWarning(PD_0); - + PwmOut pwmLeft(PF_9); PwmOut pwmRight(PF_8); - + // create encoder counter objects - + EncoderCounter counterLeft(PD_12, PD_13); EncoderCounter counterRight(PB_4, PC_7); - + // create robot controller objects - + Controller controller(pwmLeft, pwmRight, counterLeft, counterRight); StateMachine stateMachine(controller, enableMotorDriver, led0, led1, led2, led3, led4, led5, button, irSensor0, irSensor1, irSensor2, irSensor3, irSensor4, irSensor5); + // create inertial measurement unit object + + printf("Create inertial measurement unit object...\r\n"); + + SPI spi(PC_12, PC_11, PC_10); + DigitalOut csAG(PC_8); + DigitalOut csM(PC_9); + + IMU imu(spi, csAG, csM); + + // create ethernet interface and webserver + + printf("Create ethernet interface and webserver (please wait!)...\r\n"); + + EthernetInterface* ethernet = new EthernetInterface(); + ethernet->set_network("192.168.0.10", "255.255.255.0", "192.168.0.1"); // configure IP address, netmask and gateway address + ethernet->connect(); + + HTTPServer* httpServer = new HTTPServer(*ethernet); + httpServer->add("imu", new HTTPScriptIMU(imu)); + // enter main loop + printf("Enter main loop...\r\n"); + while (true) { - if (stateMachine.getState() == StateMachine::MOVE_FORWARD) { - - ledGreen = 1; - ledBlue = 0; - ledRed = 0; - - } else if ((stateMachine.getState() == StateMachine::TURN_LEFT) || (stateMachine.getState() == StateMachine::TURN_RIGHT)) { - - ledGreen = 1; - ledBlue = 1; - ledRed = 0; - - } else { - - ledGreen = 0; - ledBlue = 0; - ledRed = 1; - } + // set LEDs on microcontroller + + ledGreen = 1; + ledBlue = 0; + ledRed = 0; + + ThisThread::sleep_for(100); + + ledGreen = 0; + ledBlue = 1; + ledRed = 0; - // print robot pose into terminal + ThisThread::sleep_for(100); + + ledGreen = 0; + ledBlue = 0; + ledRed = 1; - printf("%.3f %.3f %.3f\r\n", controller.getX(), controller.getY(), controller.getAlpha()); + // print sensor data into terminal - wait(0.1); + printf("Gyro: %.3f / %.3f / %.3f\r\n", imu.readGyroX(), imu.readGyroY(), imu.readGyroZ()); + printf("Acceleration: %.3f / %.3f / %.3f\r\n", imu.readAccelerationX(), imu.readAccelerationY(), imu.readAccelerationZ()); + printf("Magnetometer: %.3f / %.3f / %.3f\r\n", imu.readMagnetometerX(), imu.readMagnetometerY(), imu.readMagnetometerZ()); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThreadFlag.cpp Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,54 @@ +/* + * ThreadFlag.cpp + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#include "ThreadFlag.h" + +using namespace std; + +unsigned int ThreadFlag::threadFlags = 0; + +/** + * Creates a signal object and assignes a unique flag. + */ +ThreadFlag::ThreadFlag() { + + mutex.lock(); + + unsigned int n = 0; + while ((((1 << n) & threadFlags) > 0) && (n < 30)) n++; + threadFlag = (1 << n); + + mutex.unlock(); +} + +/** + * Deletes the signal object and releases the assigned flag. + */ +ThreadFlag::~ThreadFlag() { + + mutex.lock(); + + threadFlags &= ~threadFlag; + + mutex.unlock(); +} + +/** + * Gets the assigned thread flag. + */ +unsigned int ThreadFlag::read() { + + return threadFlag; +} + +/** + * The empty operator is a shorthand notation of the <code>read()</code> method. + */ +ThreadFlag::operator unsigned int() { + + return read(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThreadFlag.h Wed Apr 08 09:13:33 2020 +0000 @@ -0,0 +1,33 @@ +/* + * ThreadFlag.h + * Copyright (c) 2020, ZHAW + * All rights reserved. + */ + +#ifndef THREAD_FLAG_H_ +#define THREAD_FLAG_H_ + +#include <cstdlib> +#include <mbed.h> + +/** + * This class manages the handling of unique thread flags to trigger rtos threads. + */ +class ThreadFlag { + + public: + + ThreadFlag(); + virtual ~ThreadFlag(); + virtual unsigned int read(); + operator unsigned int(); + + private: + + static unsigned int threadFlags; // variable that holds all assigned thread flags + unsigned int threadFlag; // thread flag of this object + Mutex mutex; // mutex to lock critical sections +}; + +#endif /* THREAD_FLAG_H_ */ +