Merge branch 'main' of ssh://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-3/qaajeeqiinii59
This commit is contained in:
BIN
docs/assets/Node.fzz
Normal file
BIN
docs/assets/Node.fzz
Normal file
Binary file not shown.
BIN
docs/assets/Node.png
Normal file
BIN
docs/assets/Node.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 216 KiB |
@@ -62,3 +62,28 @@ A[Design Sketch] -->|Usertest| B(Fusion360 design)
|
|||||||
B -->|Not fitting| C[New fusion360 design]
|
B -->|Not fitting| C[New fusion360 design]
|
||||||
C -->|assembly| D[Finished product]
|
C -->|assembly| D[Finished product]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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:
|
@@ -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:
|
The following libraries are used in the node code:
|
||||||
|
|
||||||
- <Wire.h>
|
- Wire.h
|
||||||
- <Adafruit_SH110X.h
|
- Adafruit_SH110X.h
|
||||||
- <Adafruit_SGP30.h>
|
- Adafruit_SGP30.h
|
||||||
- <DHT.h>
|
- DHT.h
|
||||||
- <WiFiMulti.h>
|
- WiFiMulti.h
|
||||||
- <WiFi.h>
|
- WiFi.h
|
||||||
- <WebSocketsClient.>
|
- WebSocketsClient.h
|
||||||
- <nodeCodeHeader.h>
|
- nodeCodeHeader.h
|
||||||
|
|
||||||
### Code
|
### Code
|
||||||
|
|
||||||
The code is divided into the following classes:
|
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
|
### Communication
|
||||||
|
|
||||||
@@ -63,6 +66,13 @@ The wiring diagram for the node is as follows:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
### Fritsing Diagram
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Future Improvements
|
## Future Improvements
|
||||||
|
|
||||||
The node is currently working as intended, but there are some improvements that could be made:
|
The node is currently working as intended, but there are some improvements that could be made:
|
||||||
|
@@ -24,6 +24,7 @@ class GaugeGroup {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
|
<div id="graph${nodeId}></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
BIN
web/newWebsite/arrow.png
Normal file
BIN
web/newWebsite/arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@@ -28,8 +28,10 @@
|
|||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<script src="main.js"></script>
|
|
||||||
<script src="GaugGroup.js"></script>
|
<script src="GaugGroup.js"></script>
|
||||||
<script src="graph-classes.js"></script>
|
<script src="graph-classes.js"></script>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script src="liveGraph.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
85
web/newWebsite/liveGraph.js
Normal file
85
web/newWebsite/liveGraph.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -4,7 +4,7 @@ const sensorData = {};
|
|||||||
let liveGraphs = [];
|
let liveGraphs = [];
|
||||||
let nodeArray = [];
|
let nodeArray = [];
|
||||||
let nodeDict = {};
|
let nodeDict = {};
|
||||||
|
let graphArray = [];
|
||||||
// letiables
|
// letiables
|
||||||
let intervalDelay = 5000;
|
let intervalDelay = 5000;
|
||||||
let amountOfNodes = 3;
|
let amountOfNodes = 3;
|
||||||
@@ -54,18 +54,18 @@ async function handleIncomingData(data) {
|
|||||||
|
|
||||||
await nodeAdressHandler(data.node, Object.keys(data).filter(key => key !== 'node'));
|
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 temperature = data.Temp;
|
||||||
let humidity = data.Humi;
|
let humidity = data.Humi;
|
||||||
let CO2 = data.eCO2;
|
let CO2 = data.eCO2;
|
||||||
let TVOC = data.TVOC;
|
let TVOC = data.TVOC;
|
||||||
|
|
||||||
// Update the gauges with the new data
|
// Update the gauges with the new data
|
||||||
if (sensorData[nodeName]) {
|
if (sensorData[data.node]) {
|
||||||
sensorData[nodeName].updateGauge(1, temperature);
|
sensorData[data.node].updateGauge(1, temperature);
|
||||||
sensorData[nodeName].updateGauge(2, humidity);
|
sensorData[data.node].updateGauge(2, humidity);
|
||||||
sensorData[nodeName].updateGauge(3, CO2);
|
sensorData[data.node].updateGauge(3, CO2);
|
||||||
sensorData[nodeName].updateGauge(4, TVOC);
|
sensorData[data.node].updateGauge(4, TVOC);
|
||||||
} else {
|
} else {
|
||||||
console.error('No sensor data for node:', nodeName);
|
console.error('No sensor data for node:', nodeName);
|
||||||
}
|
}
|
||||||
@@ -73,11 +73,18 @@ async function handleIncomingData(data) {
|
|||||||
|
|
||||||
async function nodeAdressHandler(node, dataTypes) {
|
async function nodeAdressHandler(node, dataTypes) {
|
||||||
let nodeInfo = await getNodeInfo(node);
|
let nodeInfo = await getNodeInfo(node);
|
||||||
|
|
||||||
|
if (!nodeInfo) {
|
||||||
|
console.error('No node info found for node:', node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let nodeName = nodeInfo.name;
|
let nodeName = nodeInfo.name;
|
||||||
let nodeLocation = nodeInfo.location;
|
let nodeLocation = nodeInfo.location;
|
||||||
if (!nodeArray.includes(nodeName)) {
|
|
||||||
nodeArray.push(nodeName);
|
if (!nodeArray.includes(node)) {
|
||||||
nodeDict[node] = nodeName;
|
nodeArray.push(node);
|
||||||
|
nodeDict[node] = {name: nodeName, location: nodeLocation};
|
||||||
|
|
||||||
let maxGaugeValues = dataTypes.map(dataType => {
|
let maxGaugeValues = dataTypes.map(dataType => {
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
@@ -90,40 +97,27 @@ async function nodeAdressHandler(node, dataTypes) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let gaugeGroup = new GaugeGroup(nodeName, nodeLocation, dataTypes.length, maxGaugeValues, 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){
|
function getNodeInfo(node){
|
||||||
return fetch("http://145.92.8.114/getNodeInfo?macAdress=" + 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 => {
|
.then(data => {
|
||||||
|
if (data.length == 0) {
|
||||||
|
throw new Error('No data returned for node: ' + node);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
name: data[0].Name,
|
name: data[0].Name,
|
||||||
location: data[0].Location // Assuming the server returns a Location property
|
location: data[0].Location // Assuming the server returns a Location property
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -63,7 +63,7 @@ body {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 6vh; /* Increase bottom padding */
|
padding-top: 3vh; /* Increase bottom padding */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ body {
|
|||||||
.gaugeImage {
|
.gaugeImage {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: 200%;
|
max-height: 120%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
position: absolute; /* Make the image position absolute */
|
position: absolute; /* Make the image position absolute */
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -105,10 +105,20 @@ body {
|
|||||||
font-size: 1.4vw;
|
font-size: 1.4vw;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: absolute;
|
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 {
|
.valueContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -122,10 +132,10 @@ body {
|
|||||||
|
|
||||||
.needle {
|
.needle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -40%; /* Lower the needle to the bottom */
|
bottom: -10%; /* Lower the needle to the bottom */
|
||||||
left: 50%;
|
left: 50%;
|
||||||
width: 2px;
|
width: 2px;
|
||||||
height: 110%;
|
height: 100%;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
transform-origin: bottom;
|
transform-origin: bottom;
|
||||||
z-index: 3; /* Make the needle display above the image */
|
z-index: 3; /* Make the needle display above the image */
|
||||||
|
Reference in New Issue
Block a user