Marco Oehler / Mbed OS Lab4

Files at this revision

API Documentation at this revision

Comitter:
oehlemar
Date:
Wed Apr 15 12:53:31 2020 +0000
Commit message:
Lab4

Changed in this revision

.gitignore Show annotated file Show diff for this revision Revisions of this file
HTTPScript.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPScript.h Show annotated file Show diff for this revision Revisions of this file
HTTPScriptIMU.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPScriptIMU.h Show annotated file Show diff for this revision Revisions of this file
HTTPServer.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPServer.h Show annotated file Show diff for this revision Revisions of this file
IMU.cpp Show annotated file Show diff for this revision Revisions of this file
IMU.h Show annotated file Show diff for this revision Revisions of this file
LowpassFilter.cpp Show annotated file Show diff for this revision Revisions of this file
LowpassFilter.h Show annotated file Show diff for this revision Revisions of this file
Main.cpp Show annotated file Show diff for this revision Revisions of this file
ThreadFlag.cpp Show annotated file Show diff for this revision Revisions of this file
ThreadFlag.h Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
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(&ethernet);
+    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&sup2;]<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&sup2;]<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&sup2;]<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