Merge branch 'main' of gitlab.fdmci.hva.nl:propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-3/qaajeeqiinii59

This commit is contained in:
Dano van den Bosch
2024-03-29 13:40:38 +01:00
22 changed files with 1049 additions and 41 deletions

View File

@@ -16,7 +16,7 @@ void DisplayText::writeText(char* text, int size, int posX, int posY, int screen
// }
tft.setCursor(posX, posY);
tft.setTextSize(size);
printWordsFull(text);
printWordsFull(text, bottom);
delay(screenTime);
}
@@ -39,38 +39,68 @@ int DisplayText::centerText(char* text) {
// }
//attempt to write the text out in full (wip)
void DisplayText::printWordsFull(char* text) {
const int screenWidth = 320; // replace with your TFT display width
const int lineHeight = 22; // replace with your text line height
char* word = strtok(text, " ");
void DisplayText::printWordsFull(char* text, bool bottom) {
const int screenWidth = 480; // replace with your TFT display width
const int lineHeight = 30; // replace with your text line height
//the double copy is needed so it doesnt alter the original text
char* newtext1 = strdup(text); // Create a copy of the string for the first strtok_r
char* newtext2 = strdup(text); // Create a second copy for the second strtok_r
char* saveptr1, *saveptr2;
char* word = strtok_r(newtext1, " ", &saveptr1);
char line[100] = "";
int lineCount = 0;
int lineWidth = 0;
while (word != NULL) {
char tempLine[100];
strcpy(tempLine, line);
strcat(tempLine, word);
strcat(tempLine, " ");
// Calculate total number of lines
int totalLines = 0;
char* tempWord = strtok_r(newtext2, " ", &saveptr2);
while (tempWord != NULL) {
int16_t x1, y1;
uint16_t w, h;
tft.getTextBounds(tempLine, 0, 0, &x1, &y1, &w, &h);
tft.getTextBounds(tempWord, 0, 0, &x1, &y1, &w, &h);
if (lineWidth + w > screenWidth && strlen(line) > 0) {
//if the line width is greater than the screen width, then we need to add a new line
totalLines++;
lineWidth = w;
//otherwise we just add the width of the word to the line width
} else {
lineWidth += w;
}
tempWord = strtok_r(NULL, " ", &saveptr2);
}
totalLines++; // Add one for the last line
if (w > screenWidth && strlen(line) > 0) {
tft.setCursor(0, lineHeight * lineCount);
// Reset variables for actual printing
strcpy(line, "");
lineWidth = 0;
while (word != NULL) {
int16_t x1, y1;
uint16_t w, h;
tft.getTextBounds(word, 0, 0, &x1, &y1, &w, &h);
if (lineWidth + w > screenWidth && strlen(line) > 0) {
int y = bottom ? tft.height() - lineHeight * (totalLines - lineCount) : lineHeight * lineCount;
tft.setCursor(0, y);
tft.println(line);
lineCount++;
strcpy(line, word);
strcat(line, " ");
lineWidth = w;
} else {
strcpy(line, tempLine);
strcat(line, word);
strcat(line, " ");
lineWidth += w;
}
word = strtok(NULL, " ");
word = strtok_r(NULL, " ", &saveptr1);
}
// print the last line
tft.setCursor(0, lineHeight * lineCount);
int y = bottom ? tft.height() - lineHeight * (totalLines - lineCount) : lineHeight * lineCount;
tft.setCursor(0, y);
tft.println(line);
free(newtext1); // Free the memory allocated by strdup
free(newtext2); // Free the memory allocated by strdup
}

View File

@@ -9,7 +9,7 @@ class DisplayText {
Adafruit_ST7796S_kbv& tft;
int centerText(char* text);
// int bottomText(char* text);
void printWordsFull(char* text);
void printWordsFull(char* text, bool bottom);
public:
DisplayText(Adafruit_ST7796S_kbv& tftDisplay);

View File

@@ -28,6 +28,7 @@ nav:
- Infrastructure: brainstorm/UML-infrastructure
- Taskflow: brainstorm/Taskflow
- Design: Sp1SchetsProject/FirstDesign
- Interview facility manager: brainstorm/bebouwBeheer.md
- 🖨️ Software:
- Dev page: brainstorm/SoftwareDocumentatie/Dev_page

View File

@@ -0,0 +1,86 @@
#include "DHT.h"
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WebSocketsClient.h>
#define DHTPIN 4
#define DHTTYPE DHT11
uint8_t h;
uint8_t t;
uint16_t interval = 5000;
unsigned long currentMillis;
unsigned long lastMillis;
WiFiMulti wifiMulti;
WebSocketsClient webSocket;
DHT dht(DHTPIN, DHTTYPE);
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
// Handle WebSocket events here if needed
}
void setup() {
Serial.begin(9600);
Serial.println(F("DHTxx test!"));
dht.begin();
wifiMulti.addAP("iotroam", "sGMINiJDcU");
wifiMulti.addAP("Ziggo6565749", "Ziggobroek1@");
Serial.println("Connecting Wifi...");
if (wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Connect to WebSocket server
webSocket.begin("145.92.8.114", 80, "/ws"); // Replace with your Raspberry Pi's IP address and port
webSocket.onEvent(webSocketEvent);
webSocket.setReconnectInterval(500);
}
}
void loop() {
// update when interval is met
if (currentMillis - lastMillis >= interval){
lastMillis = millis();
update();
}
// update the counter
currentMillis = millis();
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
// Compute heat index in Celsius (isFahrenheit = false)
float hic = dht.computeHeatIndex(t, h, false);
Serial.print(F("Humidity: "));
Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.print(F("°C "));
Serial.print(hic);
Serial.print(F("°C "));
// Ensure WebSocket communication
webSocket.loop();
}
void update(){
webSocket.sendTXT("{\"node\": \"" + String(WiFi.macAddress()) + "\", \"Temp\":\"" + String(t) + "\",\"Humi\":\"" + String(h) + "\"}");
}

View File

@@ -0,0 +1,53 @@
### Connecting With The websocket.
#### The reason.
During our project we needed more esp's to connect to the websocket, so i was given the task to make a connection to wifi, connect to the websocket, and then send information to it.
#### Before the setup.
I used library's for: connecting to multiple wifi's, connecting to the websocket and being able to read infromation form a DHT11.
These were called uppon in the first few lines.
Together with this a few variables are named to be used later in the code.
#### The Setup
Firstoff, start with identifying which serial port will be used and work from there.
It Then tells the DHT to send a test message and start it up.
Then the several wifi connections get told here and it looks for the one that connects.
(This is made bij giving the wifi name and pasword)
In this sequence a print is made, showing that there is an atempt to connect to a wifi.
*This was used because the node should be able to connect to several wifi's and not be stuck t one in perticulair.
After the wifi is connected it sends a print to indicate a connection and pings the ip adress of the used wifi.
The websocket connection which was then made is made using the uri, port, and type of connection.
In case of websocket events it gets sent.
In case of connection failiure, try this.
#### The loop
We start with setting a timer because a "delay" function would break the connection with the websocket resulting in an error.
This relates back to some some variables that were made in the beginning.
We make variables for the different results we are gathering, so these can be used later.
Then the variables get printed for overview.
To ensure the websocket connection , the process gets looped.
#### Update File.
Here the text thathas te be sent to the websocket gets sent.
### The fysical product.
it is shown here:
![fysical1](<../documentatie/assets/DHT11 state 1.jpg>)
It also turns on:
![fysical2](<../documentatie/assets/DHT11 state 2.jpg>)
Here are my fritzing and wireframe, the components used are shown but The DHT11 is replaced by a DHT22.
![alt text](../documentatie/assets/DHT11_by_button_fritzing.png)
![alt text](../documentatie/assets/DHT11_by_button_wireframe.png)
### The code.
Here the c++ code is shown:
https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-3/qaajeeqiinii59/-/blob/main/docs/LearningProcessBram/ArduinoExperience/NodeWithWebConnection.ino?ref_type=heads

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

View File

@@ -43,34 +43,52 @@ The Plastic: less eco-friendly, takes a longer time, looks objectively better.
since we can't decide on what we should use, I (Bram) went out to asks the public for their opinion.
To do this i took the front of the plastic design, and the front of the wooden design.
They lloked like this:
They looked like this:
![2Covers](../LearningProcessBram/documentatie/assets/2Covers.jpg)
1. which design is more likable
/Sebas : plastic looks better and goes straight to the point.
/Skip : plastic looks more clean.
/Dano : plastic is more smooth.
/Sietse : plastic is better.
/Ishak : Wood is more ecofriendly.
/Ishak : Wood is more ecofriendly.
/Nailah : PLastic looks cooler
2. which design is more distracting.
/Sebas : wood is more distracting and more obvious.
/Skip : wood, because the school walls are more darker themed.
/Dano : wood looks off.
/Sietse : wood would be out of place.
/Ishak : Wood, but in a good way.
/Nailah : plastic looks more interesting.
3. Any further comments about the design?
/Sebas : Don't do wood, plastic is more sleek.
/Skip : plastic looks more professional.
/Dano : no.
/Sietse: no.
/Ishak : in the wood you can burn in your logo.
/Nailah : The wood can look better inside the school.
In conclusion, Even though the wood would atract more attention, the plastic looks better according to the students.
So from this information we will continue to make plastic cases for the nodes.

View File

@@ -1,12 +0,0 @@
# Talk with building management
## Questions for building management
1. Design of page? (current page, new design or own idea)
2. What do they expect of a page made for building management?
3. Do they think they can work with the incomming feedback from the enquete?
4. Design of the node? (plastic or wood)
## Feedback:
building management had some good feedback points about the page itself, about the idea, and some good pointers for the execution. They would also look into making the data they are already measuring accessible for us. The things they had to say about the website are: they found the first design a bit unorganized and said that they would rather see the second design where you can select the node you want to see, maybe also a search function to specify the node that is displayed. What also was said was to try to make it idiot-proof because not all of the building management people are technically active, to put it that way. They had some things to say about the node design, like maybe make the color white to make it better with blending into the white walls of the total area. They also asked the question of does it matter at what height the node is placed. One other thing they said was to write something onto the node to make it clear to the people in the area what it was doing, something like ReaderNode™. And the last thing they said was if we thought about how to make it vandalizing students proof.

View File

@@ -0,0 +1,24 @@
# Talk with building management
## Introduction
We interviewed building management so we can clarify what they expect from the project and what they think of the state the project is in right now and if there need to be some adjustments. We also asked them some questions about the website and the node itself and the website. We primaly wanted clarity on what they thought on the website design because they are the ones that are going to use it the most.
## Questions for building management
1. Design of page? (current page, new design or own idea)
2. What do they expect of a page made for building management?
3. Do they think they can work with the incomming feedback from the enquete?
4. Design of the node? (plastic or wood)
## Answers
1. The current page is a bit too techincal and a bit unorganized. you couldnt tell that there is a new node added to the page. They also said that we needed to point out that there is another node connected for example start with half the ui of that specific node at the bottom of the screen.
2. They expect a page that is easy to use and dummy proof.
3. They think they can work with the incomming feedback from the enquete. And they thought it was a good idea to measure things that sensors cant measure for example peoples opinions.
4. plastic is better because it is easier to clean and it is more durable. It also looks nicer on the wall, it blends in better.
## Feedback:
Building management had some good feedback points about the page itself, about the idea and some good pointers for the execution. They would also look into making the data they are already measuring accessible for us so we can compare it to our own sensors. The things they had to say about the website are: they found the first design a bit unorganized and said that they would rather see the second design we had in figma where you can select the node you want to see, maybe also a search function to specify the node that is displayed. What also was said was to try to make it idiot-proof because not all of the building management people are very technical. They had some things to say about the node design, like maybe make the color white to make it better with blending into the white walls of the total area. They also asked the question of does it matter at what height the node is placed. One other thing they said was to write something onto the node to make it clear to the people in the area what it was doing, something like ReaderNode™. And the last thing they said was if we thought about how to make sure it doesnt get vandalized.
## Conclusion
Building management thought it was a good and interesting project. They wanna actively help us with the data they are already measuring and they had some good feedback points about the website and the node itself. They also had some good questions about the node itself that we need to look into. For example how are we are going to make sure it doesnt get vandalized. Furthermore they had good feedback on the website and they preffered our figma design over our current design and we needed to make the website dummy proof so everyone can use it even without technical knowledge.

View File

@@ -99,6 +99,12 @@ class Adafruit_ST7796S_kbv{
}
```
## Why did we choose for this screen?
We chose the screen too fast without going over all the functions and researching what we actually needed. For example we aren't using the touchscreen function of the screen so we could've gotten a different screen without touchscreen. We've attempted to use a screen for the raspberry pi with some pin headers on it but we couldn't get it to work with the esp32. We wanted to have a bigger screen than the small oled screens we already have because it isn't nice to read from and you need to get up close to see what it displays. With a bigger screen thats less of a issue. After the purchase we did some more research for screens but the bottomline was this was the best one we could get because there aren't screens that are this big without touchscreen.
## Sources
* https://www.tinytronics.nl/en/displays/tft/4-inch-tft-display-320*480-pixels-with-touchscreen-spi-st7796s Source for Driver
* https://github.com/prenticedavid/Adafruit_ST7796S_kbv Download link for the library

View File

@@ -1,32 +1,40 @@
# Original Version (by bram)
## Python code + explaination
We wanted to make a working connection between our websocket wich runs all the data gatherd by our nodes and a live feed to our database.
So we set out to make this connection using python.
At first we needed to import the folowing librarys:
```js
// everything is running async so the code can run together and not interfere with eachother.
import asyncio
import websockets
// a library to connect to the database.
import mysql.connector
import mysql.connector
import json
```
Then we began the process of connecting with both the websocket and the database.
First-off, we began making a connection to the database by using a mysql library in wich we gave the log in in order to connect to our ow database.
```js
//the data that has to be pushed needs to be
//the data that has to be pushed needs to be
async def process_data(data):
try:
mydb = mysql.connector.connect(
host="localhost",
user="*****",
password="*********",
database="*******"
database="*******"
)
```
Then after this we code in the infromation we want to put inside of the database.
The data collected from the websocket is json data, so this has to be changed.
```js
//Making a variable for the database connection
cursor = mydb.cursor()
@@ -49,7 +57,7 @@ The data collected from the websocket is json data, so this has to be changed.
//fetch the data from the database an[d put it in an array.
MACDataFetching = MACDataReading.fetchall()
MACArray = list(MACDataFetching)
//see if the fetched data is not in the gotten array.
//otehrwise insert it into the database directly.
if MACTuple not in MACArray:
@@ -70,9 +78,11 @@ The data collected from the websocket is json data, so this has to be changed.
cursor.close()
mydb.close()
```
After fully connecting t othe database, making statements of what to put there and telling the code what to do, we ofcourse need to write the code to connect to the weebsocket.
We begin by telling our websocket id and what type of port we are using.
Then we will collect live data from the conected websocket, store it in a variable, and then in the previous code
Then we will collect live data from the conected websocket, store it in a variable, and then in the previous code
```js
//here the connection to the websocked is made
async def receive_data():
@@ -94,4 +104,165 @@ async def main():
await receive_data()
asyncio.run(main())
```
```
# New Version (by sietse)
## Changes made
The original code was a good start, but it had some issues. The code could only handle the data from the sensorNodes and didn't include the nodeID for measurements.
Since we have 2 kind of nodes (sensorNodes and enqueteNodes) we needed to make another function to commit the enqueteData in the database. I have also made a filter to know which data is from the sensorNodes and which data is from the enqueteNodes. This way we can commit the data to the right table in the database.
I have also added a function to get the nodeID from the MAC address. This way we can commit the data to the right node in the database.
## The new "filter" code
### Function to get a list with macAdresses from the sensorNodes and enqueteNodes
To filter i have made 2 lists, one with all the mac adresses of the sensorNodes and the other with the mac adresses of the enqueteNodes.
The function that handles that and updates the list is the following:
```python
async def getNodeInfo(type):
global sensorNodeArray
global enqueteNodeArray
nodeInfoArray = []
id = (type,)
mydb = dbLogin()
cursor = mydb.cursor()
cursor.execute("""SELECT MAC FROM Node WHERE Type = %s""", id)
nodeInfo = cursor.fetchall()
for tuples in nodeInfo:
for item in tuples:
nodeInfoArray.append(item)
print(nodeInfoArray)
cursor.close()
mydb.close()
if type == 'sensor':
sensorNodeArray = nodeInfoArray
print(sensorNodeArray)
return sensorNodeArray
elif type == 'enquete':
enqueteNodeArray = nodeInfoArray
return enqueteNodeArray
```
As you can it works like this:
1. It gets the MAC adresses from the database with the type of node you want to get the data from. (sensor or enquete)
2. It executes the command and puts the data in a list.
3. It uses a nested for loop to get the data out of the tuples and puts it in the nodeInfoArray.
4. It updates, depending on what type, the sensorNodeArray or the enqueteNodeArray with the new data (NodeInfoArray).
5. It returns the array with the data.
### The filter code
Now that we have the data we can filter the data from the websocket.
```python
data = await websocket.recv()
processedData = json.loads(data)
macAdress = processedData['node']
if "Temp" in processedData:
type = 'sensor'
else:
type = 'enquete'
await getNodeInfo('sensor')
await getNodeInfo('enquete')
if macAdress in sensorNodeArray:
nodeID = await getNodeID(macAdress)
await processSensorNodeData(data, nodeID)
elif macAdress in enqueteNodeArray:
nodeID = await getNodeID(macAdress)
await processEnqueteNodeData(data, nodeID)
else:
await newNode(macAdress, type)
```
As you can see its alot of code to explain. So to make it easier i made a mermaid diagram to show how the code works / what it does.
```mermaid
graph TD
A[Get data from websocket] --> B{Is it sensor data or enquete data?}
B -->|sensor| C[Get sensorNodeArray]
B -->|enquete| D[Get enqueteNodeArray]
B -->|New node| E[Add new node to database]
C -->|data| G[Process sensorNodeData]
D -->|data| H[Process enqueteNodeData]
```
## The function to get the nodeID
This function is used to get the nodeID from the MAC adress. This way we can commit the data with the right id in the database.
The function to get the nodeID from the MAC adress is the following:
```python
async def getNodeID(macAdress):
id = (macAdress,)
mydb = dbLogin()
cursor = mydb.cursor()
cursor.execute("""SELECT nodeID FROM Node WHERE MAC = %s""", id)
data = cursor.fetchall()
for tuples in data:
for item in tuples:
nodeID = item
return nodeID
```
1. It gets the nodeID from the database with the MAC adress.
2. It executes the command and puts the data in a list.
3. It uses a nested for loop to get the data out of the tuples and puts it in the nodeID.
4. It returns the nodeID.
## The function to commit the data from the sensorNodes
This function is alot like the original one, with the only 2 changes being that it now also commits the nodeID and that the code to make a new node is now in a different function.
[Link to code](server\data.py)
## The function to commit the data from the enqueteNodes
This function is alot like the sensorNode function. It just commits the data to the enqueteData table in the database. And it has another data.
[Link to code](server\data.py)
## The function to add a new node to the database
This function is used to add a new node to the database. This is used when a new node is connected to the websocket, but not yet in the database.
The function to add a new node to the database is the following:
```python
async def newNode(mac, type):
id = (mac, type)
mydb = dbLogin()
cursor = mydb.cursor()
cursor.execute("INSERT INTO `Node` (MAC, Type) VALUES (%s, %s)", id)
print("new node assigned")
mydb.commit()
```
1. It gets the MAC adress and the type of node from the arguments.
2. It executes the command to add the new node to the database.
3. It prints that a new node is assigned.
4. It commits the data to the database.

View File

@@ -12,7 +12,7 @@ async def send_data(uri):
data = {
"node": "69:42:08:F5:00:00",
"Response": str(round(random.uniform(0, 2))),
"QuestionID": str(round(random.uniform(0, 90))),
"QuestionID": str(round(random.uniform(1, 2))),
}
await websocket.send(json.dumps(data))
print("Data sent")

View File

@@ -0,0 +1,47 @@
class GaugeGroup {
constructor(nodeId, Location, gaugesCount, maxGaugeValues, dataTypes) {
this.nodeId = nodeId;
this.gaugesCount = gaugesCount;
this.maxGaugeValues = maxGaugeValues; // Maximum value the gauge can display
this.dataTypes = dataTypes; // Array of data type names for each gauge
this.location = Location;
// Create a new div element
this.element = document.createElement("div");
this.element.className = "gaugeGroup";
// Set the HTML of the new div
this.element.innerHTML = `
<h2 class="groupTitle">${this.nodeId} - ${this.location}</h2>
<div class="Node">
${Array(this.gaugesCount).fill().map((_, i) => `
<div class="Sensorvalues">
<div id="gaugeContainer${this.nodeId}_${i+1}" class="gaugeContainer">
<img src="gauge.png" class="gaugeImage">
<div id="gaugeValue${this.nodeId}_${i+1}" class="gaugeValue"></div>
<div id="needle${this.nodeId}_${i+1}" class="needle"></div>
<div id="gaugeText${this.nodeId}_${i+1}" class="gaugeText">0</div>
<div class="gaugeCover"></div>
</div>
</div>
`).join('')}
</div>
`;
// Append the new div to the body
document.body.appendChild(this.element);
}
updateGauge(gaugeId, value) {
if (!this.maxGaugeValues || gaugeId - 1 < 0 || gaugeId - 1 >= this.maxGaugeValues.length) {
console.error('Invalid gaugeId or maxGaugeValues:', gaugeId, this.maxGaugeValues);
return;
}
const needle = document.getElementById(`needle${this.nodeId}_${gaugeId}`);
const gaugeText = document.getElementById(`gaugeText${this.nodeId}_${gaugeId}`);
const maxGaugeValue = this.maxGaugeValues[gaugeId - 1]; // Get the maximum value for this gauge
const rotationDegree = ((value / maxGaugeValue) * 180) - 90; // Convert value to degree (-90 to 90)
needle.style.transform = `rotate(${rotationDegree}deg)`;
gaugeText.textContent = `${this.dataTypes[gaugeId - 1]}: ${value}`; // Update the text with data type
}
}

BIN
web/newWebsite/gauge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles/graph-styles.css">
<title>Graphs</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
</head>
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item">
<a href="index.html" class="nav-link">Dashboard</a>
</li>
<li class="nav-item">
<a href="graphs.html" class="nav-link">Graphs</a>
</li>
<li class="nav-item">
<a href="settings.html" class="nav-link">Settings</a>
</li>
</ul>
</nav>
<body>
<script src="graph-main.js"></script>
<script src="graph-classes.js"></script>
</body>
</html>

33
web/newWebsite/index.html Normal file
View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles/dashboard-styles.css">
<title>Gauges</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
</head>
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item">
<a href="index.html" class="nav-link">Dashboard</a>
</li>
<li class="nav-item">
<a href="graphs.html" class="nav-link">Graphs</a>
</li>
<li class="nav-item">
<a href="settings.html" class="nav-link">Settings</a>
</li>
</ul>
</nav>
<body>
<script src="main.js"></script>
<script src="GaugGroup.js"></script>
</body>
</html>

145
web/newWebsite/main.js Normal file
View File

@@ -0,0 +1,145 @@
// Description: Main JavaScript file for the web application.
// arrays and stuff
const sensorData = {};
let liveGraphs = [];
let nodeArray = [];
let nodeDict = {};
// letiables
let intervalDelay = 5000;
let amountOfNodes = 3;
const socket = new WebSocket("ws://145.92.8.114/ws");
function openConnection() {
// Open connection
socket.addEventListener("open", (event) => {
console.log("Connected to the WebSocket server");
});
// Error handling
socket.addEventListener('error', (event) => {
console.error('WebSocket error:', event);
// Attempt to reconnect
setTimeout(openConnection, 1000); // Retry after 1 second
});
// Message handling
socket.addEventListener("message", (event) => {
try {
const jsonData = JSON.parse(event.data);
// Use the parsed JSON data as needed
handleIncomingData(jsonData);
} catch (error) {
console.error("Error parsing JSON:", error);
}
});
// Close handling
socket.addEventListener('close', (event) => {
console.log('Connection closed');
// Attempt to reconnect
setTimeout(openConnection, 1000); // Retry after 1 second
});
console.log("Connected to the WebSocket server");
}
openConnection();
async function handleIncomingData(data) {
if (!data.node || !data.Temp || !data.Humi || !data.eCO2 || !data.TVOC) {
console.error('Invalid data received:', data);
return;
}
await nodeAdressHandler(data.node, Object.keys(data).filter(key => key !== '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[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);
}
}
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(node)) {
nodeArray.push(node);
nodeDict[node] = {name: nodeName, location: nodeLocation};
let maxGaugeValues = dataTypes.map(dataType => {
switch (dataType) {
case 'Temp': return 50;
case 'Humi': return 100;
case 'eCO2': return 3000;
case 'TVOC': return 2200;
default: return 100;
}
});
let gaugeGroup = new GaugeGroup(nodeName, nodeLocation, dataTypes.length, maxGaugeValues, dataTypes);
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 => {
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
};
})
}

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles/settings-styles.css">
<title>Gauges</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
</head>
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item">
<a href="index.html" class="nav-link">Dashboard</a>
</li>
<li class="nav-item">
<a href="graphs.html" class="nav-link">Graphs</a>
</li>
<li class="nav-item">
<a href="settings.html" class="nav-link">Settings</a>
</li>
</ul>
</nav>
<body>
<script src="main.js"></script>
<script src="GaugGroup.js"></script>
</body>
</html>

View File

@@ -0,0 +1,172 @@
* {
box-sizing: border-box;
font-family: "Roboto", sans-serif;
}
body {
padding-top: 5vw;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
flex-direction: column;
background-color: #afafaf;
}
.gaugeGroup {
width: 98vw;
height: 20vh;
display: flex;
flex-direction: column; /* Keep as column */
justify-content: flex-start;
background-color: #333;
color: #fff;
padding: 10px;
border-radius: 50px;
border: 2px solid #333;
clear: both;
margin-bottom: 10px;
position: relative;
float: top;
}
.groupTitle {
width: 100%;
text-align: center;
font-size: 24px;
}
.Node {
display: flex;
justify-content: space-around;
align-items: center;
width: 100%;
height: 100%;
}
.Sensorvalues {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
flex-wrap: wrap;
width: 15%;
height: 110%;
background-color: #ddd;
color: #333;
padding: 10px;
margin: 10px;
border-radius: 10px;
text-align: center;
position: relative;
padding-top: 3vh; /* Increase bottom padding */
}
.gaugeContainer {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 80%; /* Increase the height from 70% to 80% */
position: relative;
overflow: visible;
}
.gaugeImage {
width: 100%;
height: auto;
max-height: 120%;
object-fit: contain;
position: absolute; /* Make the image position absolute */
top: 0;
left: 0;
z-index: 1; /* Make the image display below the needle */
bottom: 0;
margin-bottom: 10px;
}
.gaugeValue, .gaugeText {
width: 100%;
text-align: center;
font-size: 24px;
z-index: 2;
}
.gaugeText {
width: 100%;
text-align: center;
font-size: 1.4vw;
z-index: 2;
position: absolute;
top: -1.4vw; /* Adjust this value to move the text further down */
}
.valueContainer {
display: flex;
justify-content: center;
margin-top: 10px;
}
#valueText {
font-size: 20px;
}
.needle {
position: absolute;
bottom: -10%; /* Lower the needle to the bottom */
left: 50%;
width: 2px;
height: 100%;
background-color: black;
transform-origin: bottom;
z-index: 3; /* Make the needle display above the image */
transition: transform 0.1s ease-out;
}
.contentContainer {
display: flex;
flex-direction: row; /* Layout children side by side */
width: 100%;
height: 100%;
}
.navbar {
background-color: #333;
height: 60px;
display: flex;
align-items: center;
padding: 0 20px;
position: fixed; /* Fix the navbar at the top of the page */
top: 0; /* Position it at the very top */
width: 100%; /* Make it span the full width of the page */
z-index: 1000; /* Make sure it's above all other elements */
}
.navbar-nav {
list-style: none;
display: flex;
align-items: center;
justify-content: center; /* Center the links horizontally */
height: 100%;
width: 100%; /* Make it span the full width of the navbar */
}
.nav-item {
margin-right: 20px;
}
.nav-link {
color: #fff;
text-decoration: none;
font-size: 18px;
}

View File

@@ -0,0 +1,168 @@
* {
box-sizing: border-box;
font-family: "Roboto", sans-serif;
}
body {
padding-top: 5vw;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
flex-direction: column;
background-color: #afafaf;
}
.gaugeGroup {
width: 98vw;
height: 20vh;
display: flex;
flex-direction: column; /* Keep as column */
justify-content: flex-start;
background-color: #333;
color: #fff;
padding: 10px;
border-radius: 50px;
border: 2px solid #333;
clear: both;
margin-bottom: 10px;
position: relative;
float: top;
}
.groupTitle {
width: 100%;
text-align: center;
font-size: 24px;
}
.Node {
display: flex;
justify-content: space-around;
align-items: center;
width: 100%;
height: 100%;
}
.Sensorvalues {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
flex-wrap: wrap;
width: 15%;
height: 110%;
background-color: #ddd;
color: #333;
padding: 10px;
margin: 10px;
border-radius: 10px;
text-align: center;
position: relative;
padding-bottom: 6vh; /* Increase bottom padding */
}
.gaugeContainer {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 80%; /* Increase the height from 70% to 80% */
position: relative;
overflow: visible;
}
.gaugeImage {
width: 100%;
height: auto;
max-height: 200%;
object-fit: contain;
position: absolute; /* Make the image position absolute */
top: 0;
left: 0;
z-index: 1; /* Make the image display below the needle */
bottom: 0;
margin-bottom: 10px;
}
.gaugeValue, .gaugeText {
width: 100%;
text-align: center;
font-size: 24px;
z-index: 2;
}
.gaugeText {
width: 100%;
text-align: center;
font-size: 1.4vw;
z-index: 2;
position: absolute;
bottom: -3.2vw; /* Adjust this value to move the text further down */
}
.valueContainer {
display: flex;
justify-content: center;
margin-top: 10px;
}
#valueText {
font-size: 20px;
}
.needle {
position: absolute;
bottom: -40%; /* Lower the needle to the bottom */
left: 50%;
width: 2px;
height: 110%;
background-color: black;
transform-origin: bottom;
z-index: 3; /* Make the needle display above the image */
}
.contentContainer {
display: flex;
flex-direction: row; /* Layout children side by side */
width: 100%;
height: 100%;
}
.navbar {
background-color: #333;
height: 60px;
display: flex;
align-items: center;
padding: 0 20px;
position: fixed; /* Fix the navbar at the top of the page */
top: 0; /* Position it at the very top */
width: 100%; /* Make it span the full width of the page */
z-index: 1000; /* Make sure it's above all other elements */
}
.navbar-nav {
list-style: none;
display: flex;
align-items: center;
justify-content: center; /* Center the links horizontally */
height: 100%;
width: 100%; /* Make it span the full width of the navbar */
}
.nav-item {
margin-right: 20px;
}
.nav-link {
color: #fff;
text-decoration: none;
font-size: 18px;
}