diff --git a/docs/assets/Node.fzz b/docs/assets/Node.fzz new file mode 100644 index 0000000..98254c7 Binary files /dev/null and b/docs/assets/Node.fzz differ diff --git a/docs/assets/Node.png b/docs/assets/Node.png new file mode 100644 index 0000000..a197a40 Binary files /dev/null and b/docs/assets/Node.png differ diff --git a/docs/node-documentation/node-design.md b/docs/node-documentation/node-design.md index 01fba8e..2f2cd56 100644 --- a/docs/node-documentation/node-design.md +++ b/docs/node-documentation/node-design.md @@ -61,4 +61,29 @@ graph LR A[Design Sketch] -->|Usertest| B(Fusion360 design) B -->|Not fitting| C[New fusion360 design] C -->|assembly| D[Finished product] -``` \ No newline at end of file +``` + +## The evolved design of the nodes. +During the last sprint we had a lot of feeback about the node and mostly about tis design. hence why we had decided to remake it in several different ways. + +We had made a prototype for a wooden version, but that never made it past an first sketch, seeing as almost all fedback came back telling About how the plastic design looked better and was less distreacting. +The user-test document is here:( https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-3/qaajeeqiinii59/-/blob/main/docs/brainstorm/Feedback.md?ref_type=heads#design-questions-led-by-bram ) + +After the results came back, we decided to make progress with further designing the cover of the node. +The main body shape was certain, because all of the components needed to fit inside of the node and needed some space. +Aswell as the simple shape to save on printing costs and keep the design simple to look at. + +The only thing that was able to change was the cover. +The cover was originally made of solid plastic and was molded to fit the shape of the board and the sensors/ screen inside. + +From here we decided to brainstorm on an easyer way of covering the circurty. + +That is when it hit us, We could use acrylic as a cover! + +Acrylic can come in many different colors, is easyer to cut out ( takes less time) and gives a bit of an isight into the wiring. + +This this in mind we made a node with this new design and remade one with the old design, this again could be further used for user-tests. + +For the time being we are able to show off both of the designs and are able to read data from both, the only difference being the outer shell. + +The images: \ No newline at end of file diff --git a/docs/node-documentation/node.md b/docs/node-documentation/node.md index 3acfcfa..369c8ef 100644 --- a/docs/node-documentation/node.md +++ b/docs/node-documentation/node.md @@ -19,20 +19,23 @@ The node is programmed using the Arduino IDE. The code is written in C++ and is The following libraries are used in the node code: -- -- -- -- -- -- -- +- Wire.h +- Adafruit_SH110X.h +- Adafruit_SGP30.h +- DHT.h +- WiFiMulti.h +- WiFi.h +- WebSocketsClient.h +- nodeCodeHeader.h ### Code The code is divided into the following classes: -uitleg dano +- Node readings +- Websockets + +The two classes that are used are split into the 2 becouse the node readings handels everything about reading information from the sensors and displaying them on the screen, all the local stuff is handeled here so to speak. And into Websockets this handels every thing from connecting to the wifi to sending the data that is recorded from the sensors into json format and sending that data to the websockets so that the data can be prossed over there. ### Communication @@ -63,6 +66,13 @@ The wiring diagram for the node is as follows: ![Wiring diagram of node](docs\assets\imagesSp3\wiringDiagramNode.png) + +### Fritsing Diagram + +![fritsing diagram of node](docs\assets\Node.fzz) + +![fritsing diagram of node](.\assets\Node.png) + ## Future Improvements The node is currently working as intended, but there are some improvements that could be made: diff --git a/web/newWebsite/GaugGroup.js b/web/newWebsite/GaugGroup.js index fc214fc..31da446 100644 --- a/web/newWebsite/GaugGroup.js +++ b/web/newWebsite/GaugGroup.js @@ -24,6 +24,7 @@ class GaugeGroup { `).join('')} +
+ + + \ No newline at end of file diff --git a/web/newWebsite/liveGraph.js b/web/newWebsite/liveGraph.js new file mode 100644 index 0000000..e2aac1d --- /dev/null +++ b/web/newWebsite/liveGraph.js @@ -0,0 +1,85 @@ +class liveGraph { + // Constructor to initialize the graph + constructor(id) { + this.timeArray = []; + this.tempArray = []; + this.humiArray = []; + this.eco2Array = []; + this.tvocArray = []; + this.cnt = 0; + this.nodeId = "liveGraph" + id; + } + // Fuction to create a graph + makeGraph() { + // Create a new line for temperature + Plotly.plot(this.nodeId, [ + { + x: this.timeArray, // Use timeArray as x values + y: this.tempArray, + mode: "lines", + line: { color: "#FF0000" }, + name: "Temperature", + }, + ]); + // Create a new line for humidity + Plotly.plot(this.nodeId, [ + { + x: this.timeArray, // Use timeArray as x values + y: this.humiArray, + mode: "lines", + line: { color: "#80CAF6" }, + name: "Humidity", + }, + ]); + // Create a new line for eCO2 + Plotly.plot(this.nodeId, [ + { + x: this.timeArray, // Use timeArray as x values + y: this.eco2Array, + mode: "lines", + line: { color: "#FFA500" }, + name: "eCO2 / 10", + }, + ]); + // Create a new line for TVOC + Plotly.plot(this.nodeId, [ + { + x: this.timeArray, // Use timeArray as x values + y: this.tvocArray, + mode: "lines", + line: { color: "#000000" }, + name: "TVOC / 10", + }, + ]); + } + + // Function to update the graph with new values got from updateData function + updateGraph() { + let time = new Date(); + this.timeArray.push(new Date()); + + let update = { + x: [[this.timeArray]], + y: [[this.tempArray], [this.humiArray], [this.eco2Array], [this.tvocArray]] + }; + + let olderTime = time.setMinutes(time.getMinutes() - 1); + let futureTime = time.setMinutes(time.getMinutes() + 1); + let minuteView = { + xaxis: { + type: "date", + range: [olderTime, futureTime], + }, + }; + Plotly.relayout(this.nodeId, minuteView); + if (this.cnt === 10) clearInterval(interval); + } + // function to get the new data for graph + updateData(temperature, humidity, eCO2, TVOC) { + // Update the graph + this.tempArray.push(temperature); + this.humiArray.push(humidity); + this.eco2Array.push(eCO2 / 10); + this.tvocArray.push(TVOC / 10); + } + } \ No newline at end of file diff --git a/web/newWebsite/main.js b/web/newWebsite/main.js index d6d7e57..a62e059 100644 --- a/web/newWebsite/main.js +++ b/web/newWebsite/main.js @@ -4,7 +4,7 @@ const sensorData = {}; let liveGraphs = []; let nodeArray = []; let nodeDict = {}; - +let graphArray = []; // letiables let intervalDelay = 5000; let amountOfNodes = 3; @@ -54,18 +54,18 @@ async function handleIncomingData(data) { await nodeAdressHandler(data.node, Object.keys(data).filter(key => key !== 'node')); - let nodeName = nodeDict[data.node]; + let nodeName = nodeDict[data.node].name; let temperature = data.Temp; let humidity = data.Humi; let CO2 = data.eCO2; let TVOC = data.TVOC; // Update the gauges with the new data - if (sensorData[nodeName]) { - sensorData[nodeName].updateGauge(1, temperature); - sensorData[nodeName].updateGauge(2, humidity); - sensorData[nodeName].updateGauge(3, CO2); - sensorData[nodeName].updateGauge(4, TVOC); + if (sensorData[data.node]) { + sensorData[data.node].updateGauge(1, temperature); + sensorData[data.node].updateGauge(2, humidity); + sensorData[data.node].updateGauge(3, CO2); + sensorData[data.node].updateGauge(4, TVOC); } else { console.error('No sensor data for node:', nodeName); } @@ -73,11 +73,18 @@ async function handleIncomingData(data) { async function nodeAdressHandler(node, dataTypes) { let nodeInfo = await getNodeInfo(node); + + if (!nodeInfo) { + console.error('No node info found for node:', node); + return; + } + let nodeName = nodeInfo.name; let nodeLocation = nodeInfo.location; - if (!nodeArray.includes(nodeName)) { - nodeArray.push(nodeName); - nodeDict[node] = nodeName; + + if (!nodeArray.includes(node)) { + nodeArray.push(node); + nodeDict[node] = {name: nodeName, location: nodeLocation}; let maxGaugeValues = dataTypes.map(dataType => { switch (dataType) { @@ -90,40 +97,27 @@ async function nodeAdressHandler(node, dataTypes) { }); let gaugeGroup = new GaugeGroup(nodeName, nodeLocation, dataTypes.length, maxGaugeValues, dataTypes); - sensorData[nodeName] = gaugeGroup; + sensorData[node] = gaugeGroup; } } -function createGauge(node, dataType) { - // Create a new gauge here - let gauge = new GaugeGroup(node, dataType); // Assuming Gauge is the name of the class - sensorData[node][dataType] = gauge; // Store the gauge in the sensorData object for later use -} - - -//function for making the html elements for the following html code -function nodeData(data) { - let nodeData = document.createElement("div"); - nodeData.innerHTML = data; - // nodeData.setAttribute("id", "node" + node); - document.body.appendChild(nodeData); - // console.log("Hello World"); -} - - -function updateGauge(nodeNumber, dataType, value) { - // Update the gauge here - let gauge = sensorData[nodeNumber][dataType]; // Get the gauge from the sensorData object - gauge.update(value); // Assuming the Gauge class has an update method - } - function getNodeInfo(node){ return fetch("http://145.92.8.114/getNodeInfo?macAdress=" + node) - .then(response => response.json()) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) .then(data => { + if (data.length == 0) { + throw new Error('No data returned for node: ' + node); + } return { name: data[0].Name, location: data[0].Location // Assuming the server returns a Location property }; - }); -} \ No newline at end of file + }) + +} + diff --git a/web/newWebsite/styles/dashboard-styles.css b/web/newWebsite/styles/dashboard-styles.css index b83d7ca..2985720 100644 --- a/web/newWebsite/styles/dashboard-styles.css +++ b/web/newWebsite/styles/dashboard-styles.css @@ -63,7 +63,7 @@ body { border-radius: 10px; text-align: center; position: relative; - padding-bottom: 6vh; /* Increase bottom padding */ + padding-top: 3vh; /* Increase bottom padding */ } @@ -82,7 +82,7 @@ body { .gaugeImage { width: 100%; height: auto; - max-height: 200%; + max-height: 120%; object-fit: contain; position: absolute; /* Make the image position absolute */ top: 0; @@ -105,10 +105,20 @@ body { font-size: 1.4vw; z-index: 2; position: absolute; - bottom: -3.2vw; /* Adjust this value to move the text further down */ + top: -1.4vw; /* Adjust this value to move the text further down */ } +.arrowimg { + width: 3vh; + height: auto; + max-height: 100%; + object-fit: contain; + position: absolute; + top: 0.5vw; + right: 1.2vw; + z-index: 2; +} .valueContainer { display: flex; @@ -122,10 +132,10 @@ body { .needle { position: absolute; - bottom: -40%; /* Lower the needle to the bottom */ + bottom: -10%; /* Lower the needle to the bottom */ left: 50%; width: 2px; - height: 110%; + height: 100%; background-color: black; transform-origin: bottom; z-index: 3; /* Make the needle display above the image */