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.
Revision 0:3fb3c13f3cf5, committed 2020-04-15
- Comitter:
- oehlemar
- Date:
- Wed Apr 15 12:53:31 2020 +0000
- Commit message:
- Lab4
Changed in this revision
diff -r 000000000000 -r 3fb3c13f3cf5 .gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Wed Apr 15 12:53:31 2020 +0000 @@ -0,0 +1,4 @@ +.build +.mbed +projectfiles +*.py*
diff -r 000000000000 -r 3fb3c13f3cf5 HTTPScript.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPScript.cpp Wed Apr 15 12:53:31 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 "";
+}
+
diff -r 000000000000 -r 3fb3c13f3cf5 HTTPScript.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPScript.h Wed Apr 15 12:53:31 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_ */
+
diff -r 000000000000 -r 3fb3c13f3cf5 HTTPScriptIMU.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPScriptIMU.cpp Wed Apr 15 12:53:31 2020 +0000
@@ -0,0 +1,57 @@
+/*
+ * 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;
+}
+
diff -r 000000000000 -r 3fb3c13f3cf5 HTTPScriptIMU.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPScriptIMU.h Wed Apr 15 12:53:31 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_ */
+
diff -r 000000000000 -r 3fb3c13f3cf5 HTTPServer.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.cpp Wed Apr 15 12:53:31 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
+}
+
diff -r 000000000000 -r 3fb3c13f3cf5 HTTPServer.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.h Wed Apr 15 12:53:31 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_ */
+
diff -r 000000000000 -r 3fb3c13f3cf5 IMU.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IMU.cpp Wed Apr 15 12:53:31 2020 +0000
@@ -0,0 +1,348 @@
+/*
+ * IMU.cpp
+ * Copyright (c) 2020, ZHAW
+ * All rights reserved.
+ */
+#include <mbed.h>
+#include "IMU.h"
+#include "LowpassFilter.h"
+#include "stdio.h"
+#include <mbed.h>
+#include <EthernetInterface.h>
+#include "IMU.h"
+#include "HTTPServer.h"
+#include "HTTPScriptIMU.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
+const float IMU::LOWPASS_FILTER_FREQUENCY = 2.0f*M_PI; // given in [rad/s]
+
+// init lowpasses
+
+
+/**
+ * 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);
+
+ FilterX.setPeriod(PERIOD);
+ FilterX.setFrequency(LOWPASS_FILTER_FREQUENCY);
+ FilterY.setPeriod(PERIOD);
+ FilterY.setFrequency(LOWPASS_FILTER_FREQUENCY);
+
+ mxMin = 0.05;
+ mxMax = 0.1;
+ myMin = 0.05;
+ myMax = 0.1;
+
+ myX_korr = 0;
+ myY_korr = 0;
+}
+
+/**
+ * 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() {
+
+ mutex.lock();
+
+ char low = readRegister(csM, OUT_X_L_M);
+ char high = readRegister(csM, OUT_X_H_M);
+
+ short value = (short)(((unsigned short)high << 8) | (unsigned short)low);
+
+ mutex.unlock();
+
+ return (float)value/32768.0f*4.0f;
+
+
+}
+
+/**
+ * Reads the magnetic field in y-direction.
+ * @return the magnetic field in y-direction, given in [Gauss].
+ */
+float IMU::readMagnetometerY() {
+
+ mutex.lock();
+
+ char low = readRegister(csM, OUT_Y_L_M);
+ char high = readRegister(csM, OUT_Y_H_M);
+
+ short value = (short)(((unsigned short)high << 8) | (unsigned short)low);
+
+ mutex.unlock();
+
+ return (float)value/32768.0f*4.0f;
+}
+
+/**
+ * Reads the magnetic field in z-direction.
+ * @return the magnetic field in z-direction, given in [Gauss].
+ */
+float IMU::readMagnetometerZ() {
+
+ /*mutex.lock();
+
+ char low = readRegister(csM, OUT_Z_L_M);
+ char high = readRegister(csM, OUT_Z_H_M);
+
+ short value = (short)(((unsigned short)high << 8) | (unsigned short)low);
+
+ mutex.unlock();
+
+ return (float)value/32768.0f*4.0f;*/
+ return readHeading();
+}
+
+float IMU::readHeading() {
+
+ float orient = atan2(myX_korr,myY_korr);
+
+ if (orient < -M_PI){
+ orient = orient + M_PI;
+ return orient;
+ }
+ else if (orient > M_PI){
+ orient = orient - M_PI;
+ return orient;
+ }
+ else {
+ return orient;
+ }
+}
+
+/**
+ * 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);
+ float myX = FilterX.filter(readMagnetometerX());
+ float myY = FilterY.filter(readMagnetometerY());
+ // filter and process sensor data...
+ if (myX < mxMin) {
+ mxMin = myX;
+ }
+ if (myX > mxMax) {
+ mxMax = myX;
+ }
+ if (myY < myMin) {
+ myMin = myY;
+ }
+ if (myY > myMax) {
+ myMax = myY;
+ }
+
+ myX_korr = (2*(myX-mxMin)/(mxMax-mxMin)) - 1;
+ myY_korr = (2*(myY-myMin)/(myMax-myMin)) - 1;
+
+
+ }
+}
+
diff -r 000000000000 -r 3fb3c13f3cf5 IMU.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IMU.h Wed Apr 15 12:53:31 2020 +0000
@@ -0,0 +1,104 @@
+/*
+ * IMU.h
+ * Copyright (c) 2020, ZHAW
+ * All rights reserved.
+ */
+
+#ifndef IMU_H_
+#define IMU_H_
+
+#include <cstdlib>
+#include <mbed.h>
+#include "ThreadFlag.h"
+#include "LowpassFilter.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();
+ float readHeading();
+
+ 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
+ static const float PERIOD_LP;
+ static const float LOWPASS_FILTER_FREQUENCY;
+
+ float mxMin;
+ float mxMax;
+ float myMin;
+ float myMax;
+
+ float myX_korr;
+ float myY_korr;
+
+ SPI& spi;
+ DigitalOut& csAG;
+ DigitalOut& csM;
+ Mutex mutex;
+ ThreadFlag threadFlag;
+ Thread thread;
+ Ticker ticker;
+ LowpassFilter FilterX;
+ LowpassFilter FilterY;
+
+ void writeRegister(DigitalOut& cs, char address, char value);
+ char readRegister(DigitalOut& cs, char address);
+ void sendThreadFlag();
+ void run();
+};
+
+#endif /* IMU_H_ */
+
diff -r 000000000000 -r 3fb3c13f3cf5 LowpassFilter.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LowpassFilter.cpp Wed Apr 15 12:53:31 2020 +0000
@@ -0,0 +1,113 @@
+/*
+ * LowpassFilter.cpp
+ * Copyright (c) 2020, ZHAW
+ * All rights reserved.
+ */
+
+#include <cmath>
+#include "LowpassFilter.h"
+
+using namespace std;
+
+/**
+ * Creates a LowpassFilter object with a default cutoff frequency of 1000 [rad/s].
+ */
+LowpassFilter::LowpassFilter() {
+
+ period = 1.0f;
+ frequency = 1000.0f;
+
+ a11 = (1.0f+frequency*period)*exp(-frequency*period);
+ a12 = period*exp(-frequency*period);
+ a21 = -frequency*frequency*period*exp(-frequency*period);
+ a22 = (1.0f-frequency*period)*exp(-frequency*period);
+ b1 = (1.0f-(1.0f+frequency*period)*exp(-frequency*period))/frequency/frequency;
+ b2 = period*exp(-frequency*period);
+
+ x1 = 0.0f;
+ x2 = 0.0f;
+}
+
+/**
+ * Deletes this LowpassFilter object.
+ */
+LowpassFilter::~LowpassFilter() {}
+
+/**
+ * Resets the filtered value to zero.
+ */
+void LowpassFilter::reset() {
+
+ x1 = 0.0f;
+ x2 = 0.0f;
+}
+
+/**
+ * Resets the filtered value to a given value.
+ * @param value the value to reset the filter to.
+ */
+void LowpassFilter::reset(float value) {
+
+ x1 = value/frequency/frequency;
+ x2 = (x1-a11*x1-b1*value)/a12;
+}
+
+/**
+ * Sets the sampling period of the filter.
+ * This is typically the sampling period of the periodic task of a controller that uses this filter.
+ * @param the sampling period, given in [s].
+ */
+void LowpassFilter::setPeriod(float period) {
+
+ this->period = period;
+
+ a11 = (1.0f+frequency*period)*exp(-frequency*period);
+ a12 = period*exp(-frequency*period);
+ a21 = -frequency*frequency*period*exp(-frequency*period);
+ a22 = (1.0f-frequency*period)*exp(-frequency*period);
+ b1 = (1.0f-(1.0f+frequency*period)*exp(-frequency*period))/frequency/frequency;
+ b2 = period*exp(-frequency*period);
+}
+
+/**
+ * Sets the cutoff frequency of this filter.
+ * @param frequency the cutoff frequency of the filter in [rad/s].
+ */
+void LowpassFilter::setFrequency(float frequency) {
+
+ this->frequency = frequency;
+
+ a11 = (1.0f+frequency*period)*exp(-frequency*period);
+ a12 = period*exp(-frequency*period);
+ a21 = -frequency*frequency*period*exp(-frequency*period);
+ a22 = (1.0f-frequency*period)*exp(-frequency*period);
+ b1 = (1.0f-(1.0f+frequency*period)*exp(-frequency*period))/frequency/frequency;
+ b2 = period*exp(-frequency*period);
+}
+
+/**
+ * Gets the current cutoff frequency of this filter.
+ * @return the current cutoff frequency in [rad/s].
+ */
+float LowpassFilter::getFrequency() {
+
+ return frequency;
+}
+
+/**
+ * Filters a value.
+ * @param value the original unfiltered value.
+ * @return the filtered value.
+ */
+float LowpassFilter::filter(float value) {
+
+ float x1old = x1;
+ float x2old = x2;
+
+ x1 = a11*x1old+a12*x2old+b1*value;
+ x2 = a21*x1old+a22*x2old+b2*value;
+
+ return frequency*frequency*x1;
+}
+
+
diff -r 000000000000 -r 3fb3c13f3cf5 LowpassFilter.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LowpassFilter.h Wed Apr 15 12:53:31 2020 +0000
@@ -0,0 +1,40 @@
+/*
+ * LowpassFilter.h
+ * Copyright (c) 2020, ZHAW
+ * All rights reserved.
+ */
+
+#ifndef LOWPASS_FILTER_H_
+#define LOWPASS_FILTER_H_
+
+#include <cstdlib>
+
+/**
+ * This class implements a time-discrete 2nd order lowpass filter for a series of data values.
+ * This filter can typically be used within a periodic task that takes measurements that need
+ * to be filtered, like speed or position values.
+ */
+class LowpassFilter {
+
+ public:
+
+ LowpassFilter();
+ virtual ~LowpassFilter();
+ void reset();
+ void reset(float value);
+ void setPeriod(float period);
+ void setFrequency(float frequency);
+ float getFrequency();
+ float filter(float value);
+
+ private:
+
+ float period;
+ float frequency;
+ float a11, a12, a21, a22, b1, b2;
+ float x1, x2;
+};
+
+#endif /* LOWPASS_FILTER_H_ */
+
+
diff -r 000000000000 -r 3fb3c13f3cf5 Main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Main.cpp Wed Apr 15 12:53:31 2020 +0000
@@ -0,0 +1,78 @@
+/*
+ * Main.cpp
+ * Copyright (c) 2020, ZHAW
+ * All rights reserved.
+ */
+
+#include <mbed.h>
+#include <EthernetInterface.h>
+#include "IMU.h"
+#include "HTTPServer.h"
+#include "HTTPScriptIMU.h"
+
+int main() {
+
+ // initialise digital inputs and outputs
+
+ printf("Initialise digital inputs and outputs...\r\n");
+
+ DigitalIn button(USER_BUTTON);
+
+ DigitalOut ledGreen(LED1);
+ DigitalOut ledBlue(LED2);
+ DigitalOut ledRed(LED3);
+
+ // 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("169.254.20.110", "255.255.0.0", "0.0.0.0"); // 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) {
+
+ // set LEDs on microcontroller
+
+ ledGreen = 1;
+ ledBlue = 0;
+ ledRed = 0;
+
+ ThisThread::sleep_for(100);
+
+ ledGreen = 0;
+ ledBlue = 1;
+ ledRed = 0;
+
+ ThisThread::sleep_for(100);
+
+ ledGreen = 0;
+ ledBlue = 0;
+ ledRed = 1;
+
+ // print sensor data into terminal
+
+ 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());
+ printf("Orientation: %.3f\r\n", imu.readHeading());
+ }
+}
+
diff -r 000000000000 -r 3fb3c13f3cf5 ThreadFlag.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ThreadFlag.cpp Wed Apr 15 12:53:31 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();
+}
+
diff -r 000000000000 -r 3fb3c13f3cf5 ThreadFlag.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ThreadFlag.h Wed Apr 15 12:53:31 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_ */
+
diff -r 000000000000 -r 3fb3c13f3cf5 mbed-os.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Wed Apr 15 12:53:31 2020 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#cf4f12a123c05fcae83fc56d76442015cb8a39e9