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:
Bram Barbieri
2024-03-14 14:30:44 +01:00
14 changed files with 343 additions and 123 deletions

13
arduino/mac_adress.ino Normal file
View File

@@ -0,0 +1,13 @@
// Complete Instructions to Get and Change ESP MAC Address: https://RandomNerdTutorials.com/get-change-esp32-esp8266-mac-address-arduino/
#include <WiFi.h>
void setup(){
Serial.begin(115200);
Serial.println();
Serial.print("ESP Board MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop(){
}

View File

@@ -0,0 +1,42 @@
// include these libraries
#include <Wire.h>
#include <Adafruit_SH110X.h>
#include <Adafruit_SGP30.h>
#include <DHT.h>
#include <WiFiMulti.h>
#include <WiFi.h>
#include <WebSocketsClient.h>
#include <nodeCodeHeader.h>
// define pins on esp32
#define MICPIN 6
#define DHTPIN 7
#define SCL 9
#define SDA 8
#define DHTTYPE DHT11
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define i2c_adress 0x3c
#define OLED_RESET -1 // QT-PY / XIAO
#define USE_SERIAL Serial
// make new objects
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
DHT dht(DHTPIN, DHTTYPE);
WiFiMulti WiFiMulti;
Adafruit_SGP30 sgp;
WebSocketsClient webSocket;
// define variables
uint16_t TVOC_base, eCO2_base;
uint16_t counter = 0;
uint16_t eCO2 = 0;
uint16_t TVOC = 0;
uint16_t interval = 5000;
float temperature = 0;
float humidity = 0;
unsigned long currentMillis;
unsigned long lastMillis;
bool errorSGP30 = false;
bool errorDHT11 = false;
bool noise = false;

View File

@@ -1,49 +1,40 @@
// Sietse Jonker & Dano van den Bosch
// 28/02/2024
// include header file into code
#include <headerFile.h>
// include these libraries
#include <Wire.h>
#include <Adafruit_SH110X.h>
#include <Adafruit_SGP30.h>
#include <DHT.h>
#include <WiFiMulti.h>
#include <WiFi.h>
#include <WebSocketsClient.h>
// setup function
void setup() {
// make serial connection at 115200 baud
Serial.begin(115200);
// define pins on esp32
#define MICPIN 6
#define DHTPIN 7
#define SCL 9
#define SDA 8
#define DHTTYPE DHT11
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define i2c_adress 0x3c
#define USE_SERIAL Serial
// tell display what settings to use
display.begin(i2c_adress, true);
display.clearDisplay();
// define node identification number
#define nodeIdentificationNumber 1
// tell sensors to start reading
dht.begin();
sgp.begin();
// make new objects
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
DHT dht(DHTPIN, DHTTYPE);
WiFiMulti WiFiMulti;
Adafruit_SGP30 sgp;
WebSocketsClient webSocket;
pinMode(MICPIN, INPUT);
pinMode(DHTPIN, INPUT);
websocketSetup();
resetValues();
}
// define variables
uint16_t TVOC_base, eCO2_base;
uint16_t counter = 0;
uint16_t eCO2 = 0;
uint16_t TVOC = 0;
uint16_t interval = 5000;
float temperature = 0;
float humidity = 0;
unsigned long currentMillis;
unsigned long lastMillis;
bool errorSGP30 = false;
bool errorDHT11 = false;
bool noise = false;
// loop function
void loop() {
// loop the websocket connection so it stays alive
webSocket.loop();
// update when interval is met
if (currentMillis - lastMillis >= interval){
lastMillis = millis();
update();
}
// update the counter
currentMillis = millis();
}
// hexdump function for websockets binary handler
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
@@ -112,7 +103,7 @@ void websocketSetup(){
webSocket.setReconnectInterval(500);
}
// fucntion to reset the values if needed
// function to reset the values if needed
void resetValues() {
TVOC_base;
eCO2_base;
@@ -153,7 +144,7 @@ void update(){
displayData();
// webSocket.sendTXT("{\"Temp\":\"" + String(temperature) + "\",\"Humi\":\"" + String(humidity) + "\",\"eCO2\":\"" + String(sgp.eCO2) + "\",\"TVOC\":\"" + String(sgp.TVOC) + "\"}");
webSocket.sendTXT("{\"node\": \"" + String(nodeIdentificationNumber) + "\", \"Temp\":\"" + String(temperature) + "\",\"Humi\":\"" + String(humidity) + "\",\"eCO2\":\"" + String(sgp.eCO2) + "\",\"TVOC\":\"" + String(sgp.TVOC) + "\"}");
webSocket.sendTXT("{\"node\": \"" + String(WiFi.macAddress()) + "\", \"Temp\":\"" + String(temperature) + "\",\"Humi\":\"" + String(humidity) + "\",\"eCO2\":\"" + String(sgp.eCO2) + "\",\"TVOC\":\"" + String(sgp.TVOC) + "\"}");
sgp.getIAQBaseline(&eCO2_base, &TVOC_base);
@@ -183,39 +174,4 @@ void displayData() {
// display the screen
display.display();
}
// setup function
void setup() {
// make serial connection at 115200 baud
Serial.begin(115200);
// tell display what settings to use
display.begin(i2c_adress, true);
display.clearDisplay();
// tell sensors to start reading
dht.begin();
sgp.begin();
pinMode(MICPIN, INPUT);
pinMode(DHTPIN, INPUT);
websocketSetup();
resetValues();
}
// loop function
void loop() {
// loop the websocket connection so it stays alive
webSocket.loop();
// update when interval is met
if (currentMillis - lastMillis >= interval){
lastMillis = millis();
update();
}
// update the counter
currentMillis = millis();
}

View File

@@ -0,0 +1,18 @@
#include "arduino.h"
#include "nodeCodeHeader.h"
nodeReadings::nodeReadings() {
}
void nodeReadings::resetValues() {
counter = 0;
eCO2 = 0;
TVOC = 0;
temperature = 0;
humidity = 0;
currentMillis = 0;
lastMillis = 0;
errorSGP30 = false;
errorDHT11 = false;
noise = false;
}

View File

@@ -0,0 +1,17 @@
#include <nodeCodeHeader.h>
nodeReadings esp32Node();
void setup()
{
// put your setup code here, to run once:
esp32Node.setup();
esp32Node.websocketSetup();
esp32Node.resetValues();
}
void loop()
{
// put your main code here, to run repeatedly:
esp32Node.loop();
}

View File

@@ -0,0 +1,63 @@
#include "arduino.h"
#include "nodeCodeHeader.h"
nodeReadings::nodeReadings() {
}
void nodeReadings::setup(){
// make serial connection at 115200 baud
Serial.begin(115200);
// tell display what settings to use
display.begin(i2c_adress, true);
display.clearDisplay();
// tell sensors to start reading
dht.begin();
sgp.begin();
pinMode(MICPIN, INPUT);
pinMode(DHTPIN, INPUT);
}
void nodeReadings::loop() {
// loop the websocket connection so it stays alive
webSocket.loop();
// update when interval is met
if (currentMillis - lastMillis >= interval){
lastMillis = millis();
update();
}
// update the counter
currentMillis = millis();
}
void nodeReadings::resetValues() {
counter = 0;
eCO2 = 0;
TVOC = 0;
temperature = 0;
humidity = 0;
currentMillis = 0;
lastMillis = 0;
errorSGP30 = false;
errorDHT11 = false;
noise = false;
}
// hexdump function for websockets binary handler
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
const uint8_t* src = (const uint8_t*) mem;
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
for(uint32_t i = 0; i < len; i++) {
if(i % cols == 0) {
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
}
USE_SERIAL.printf("%02X ", *src);
src++;
}
USE_SERIAL.printf("\n");
}

View File

@@ -1,13 +1,18 @@
#ifndef nodeClass_h
#define nodeClass_h
#ifndef nodeReading_h
#define nodeReading_h
#include "Arduino.h"
#include "headerFile.h"
class nodeReadings {
private:
public:
nodeReadings();
void readDht();
void printNode();
// class code goes here
void setup();
void loop();
void resetValues();
private:
};
#endif

View File

@@ -0,0 +1,16 @@
# Flask
## Introduction
Flask is a micro web framework written in Python. It is easy to use and has a lot of documentation. We are going to use Flask to serve our REST api.
## Cheatsheet
```python
print(f"Hello world have a nice {args}!")
```
This way you can put variables in a string. This is called f-strings.
## Rules for python
* You can only use one return statement in a function.

View File

@@ -16,7 +16,74 @@ We can only use certain ports like 113, 80, 443. These are common ports and are
A solution for this is to use a reverse proxy. (See [Reverse Proxy](./Reverse-Proxy.md))
## Classes
For the websockets we are going to use 2 classes. One for the nodes and one for the websites. The nodes are going to send data to the website and have multiple variables like temperature, humidity, eCO2, TVOC and sound. The website is going to send data to the nodes like names and settings. For this they have to use a userName and password.
``` mermaid
classDiagram
client --> User : website client
client --> Node : esp32 client
namespace raspberry pi clients {
class client {
+MacAdress
sendData()
}
class Node {
+eCO2
+Temperature
+Humidity
+TVOC
+Sound
+Settings
changeNodeName(name)
updateData(data)
}
class User {
+userName
+password
changeNodeName(data)
}
}
```
Code of the classes:
``` python
class client:
def __init__(self, macAdress):
self.macAdress = macAdress
class node(client):
def __init__(self, name, node, temperature, humidity, eCO2, TVOC, sound):
super().__init__(macAdress)
self.nodeNumber = node
self.temperature = temperature
self.humidity = humidity
self.eCO2 = eCO2
self.TVOC = TVOC
self.sound = sound
self.name = name
def updateData(self, temperature, humidity, eCO2, TVOC, sound):
self.temperature = temperature
self.humidity = humidity
self.eCO2 = eCO2
self.TVOC = TVOC
self.sound = sound
class Website(client):
def __init__(self, macAdress, user, password):
super().__init__(macAdress)
self.passWord = passWord
self.userName = userName
```
#### Sources:
* https://websockets.readthedocs.io/en/stable/index.html
**Written by Sam**
**Written by Sam & Sietse**

View File

@@ -1,5 +1,5 @@
-- MySQL Script generated by MySQL Workbench
-- Fri Mar 8 12:25:17 2024
-- Wed Mar 13 16:04:58 2024
-- Model: New Model Version: 1.0
-- MySQL Workbench Forward Engineering
@@ -44,14 +44,11 @@ ENGINE = InnoDB;
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `NodeData`.`Measurement` (
`NodeID` INT NOT NULL,
`TimeStamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`Type` VARCHAR(45) NULL,
`Value` FLOAT NULL,
`TimeStamp` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
`Node_NodeID` INT NOT NULL,
PRIMARY KEY (`NodeID`, `Node_NodeID`),
INDEX `fk_Measurement_Node1_idx` (`Node_NodeID` ASC) VISIBLE,
CONSTRAINT `fk_Measurement_Node1`
FOREIGN KEY (`Node_NodeID`)
FOREIGN KEY (`NodeID`)
REFERENCES `NodeData`.`Node` (`NodeID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)

43
server/Flask/main.py Normal file
View File

@@ -0,0 +1,43 @@
from flask import Flask, request
import mysql.connector
app = Flask(__name__)
@app.route('/')
def index():
node = request.args.get('node', default = None)
dataType = request.args.get('dataType', default = None)
return getData(node, dataType)
def getData(node, dataType):
try:
mydb = mysql.connector.connect(
host="localhost",
user="root",
password="Dingleberries69!",
database="NodeData"
)
#turn this into a switch statement
cursor = mydb.cursor()
if node:
query = f"SELECT * FROM Measurement WHERE NodeID = {node}"
elif dataType:
query = f"SELECT * FROM Measurement WHERE Type = '{dataType}'"
else:
query = "SELECT * FROM `Measurement`"
cursor.execute(query)
result = cursor.fetchall() # Fetch the results
# Convert the results to a string for display
result_str = ', '.join([str(row) for row in result])
cursor.close()
mydb.close()
return result
except mysql.connector.Error as err:
print("MySQL Error:", err)
return "MySQL Error: " + str(err)
if __name__ == '__main__':
app.run(debug=True, host='localhost')

View File

@@ -8,7 +8,6 @@
<link rel="stylesheet" href="styles.css">
<title>Node dev page</title>
<!-- <link rel="icon" type="image/x-icon" href="favicon.ico"> -->
</head>
<body>
@@ -24,11 +23,8 @@
</nav>
</header>
<div id="nodeDataLocation"></div>
<!-- Include the js file -->
<script src="classes.js"></script>
<script src="main.js"></script>

View File

@@ -2,6 +2,8 @@
// arrays and stuff
const sensorData = {};
let liveGraphs = [];
let nodeArray = [];
let nodeDict = {};
// letiables
let intervalDelay = 5000;
@@ -45,39 +47,21 @@ function openConnection() {
openConnection();
function handleIncomingData(data) {
nodeNumber = data.node;
nodeAdressHandler(data.Node);
nodeNumber = nodeDict[data.Node];
temperature = data.Temp;
humidity = data.Humi;
CO2 = data.eCO2;
TVOC = data.TVOC;
processNodeData(nodeNumber, temperature, humidity, CO2, TVOC);
updateNodeData(nodeNumber, temperature, humidity, CO2, TVOC);
}
function processNodeData(nodeNumber, temperature, humidity, CO2, TVOC) {
// Initialize the array for this node if it doesn't exist yet
if (!sensorData[nodeNumber]) {
sensorData[nodeNumber] = [];
}
// Push the new data onto the array for this node
sensorData[nodeNumber].push({
'node': nodeNumber,
'temp': temperature,
'humi': humidity,
'CO2': CO2,
'TVOC': TVOC,
});
// updateNodeData(node, temperature, humidity, lightIntensity)
updateNodeData(nodeNumber, temperature, humidity, CO2, TVOC);
// Log the array for this node
console.log(sensorData[nodeNumber]);
// If the array for this node has more than 10 elements, remove the oldest one
if (sensorData[nodeNumber].length >= 10) {
sensorData[nodeNumber].shift();
function nodeAdressHandler(node) {
if (!nodeArray.includes(node)) {
nodeArray.push(node);
nodeDict[node] = nodeArray.length;
}
}
@@ -191,8 +175,11 @@ function updateNodeData(node, temperature, humidity, eCO2, TVOC) {
document.getElementById("TVOCStatus").textContent = "Connected";
// Update the graph
liveGraphs[node - 1].updateData(temperature, humidity, eCO2, TVOC);
liveGraphs[node - 1].updateGraph();
liveGraphs[0].updateData(temperature, humidity, eCO2, TVOC);
liveGraphs[0].updateGraph();
console.log(nodeDict[node]);
console.log(nodeArray);
}
// Call the function to create the HTML structure