Compare commits

...

4 Commits
v1.0.0 ... main

Author SHA1 Message Date
Miisa Ekholm
bc413a39d6
Merge pull request #41 from vas-dav/web
Codes cleaned and commented
2022-10-30 22:03:53 +02:00
Miisa Ekholm
34264a1913 Codes cleaned and commented 2022-10-30 21:56:31 +02:00
Vasily Davydov
7f1c12d758
Update README.md 2022-10-30 19:00:44 +02:00
Vasily Davydov
2905d1f894
Update README.md 2022-10-30 18:49:00 +02:00
7 changed files with 30821 additions and 704 deletions

View File

@ -5,3 +5,33 @@ A school project to control a simulated ventilation system based on measured CO2
## Embedded Systems Programming Ventilation Project
The objective of this project was to create a smart ventilation system that has the capability to display sensor data and let users set the ventilation fan speed manually, or let the system determine the fan speed in automatic operation by setting the target output air pressure to achieve. This documentation examines the overall system architecture in hardware and software, operation of the system, the development process and goes in detail of the technical implementation. As hardware implementation of this ventilation system is pre-determined, particular attention in this documentation is paid to the embedded software and communications implementation.
## Download latest release
### [esp-vent-main v1.0.0](https://github.com/vas-dav/ESP-Ventilation/releases) (2022-10-31)
> Source code is safely building on **MCUXpresso IDE v11.5.0 [Build 7232] [2022-01-11]**
### Current functionality:
### Modes:
##### Manual:
```
- Buttons to inc/dec fan speed(0-10V) (show in percents 10% == 1V)
- Display fan speed + pressure.
- Fan updates at the same time as UI
- BTN1 inc voltage, BTN2 dec voltage
```
##### Automatic:
```
- Buttons to inc/dec fan pressure level (0-120 Pa) (show in percents 10% == 1V)
- Pressure to inc/dec fan speed
- BTN1 inc pressure, BTN2 dec pressure
```
##### Additional:
```
- BTN3 switch between modes
- BTN4 show sensor values
```
## Latest Changes
* [Closed Pull-requests](https://github.com/vas-dav/ESP-Ventilation/pulls?q=is%3Apr+is%3Aclosed)
#### [Open issues](https://github.com/vas-dav/ESP-Ventilation/issues)

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,11 @@
<script src="main.js" defer></script>
<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script>
</head>
<body onload="checkUser(), checkMode(), getStartValues(), updateChart()">
<body onload="checkUser(), getStartValues(), updateChart()">
<header>
<form id="pre-reg" method="get" action="/register" style="margin-top: 10px; margin-right: 10px;">
<input id="reg" type="submit" value="Register new user">
</form>
<h1>ABB Ventilation Controller</h1>
</header>
<div class="logout">
@ -132,6 +135,7 @@
</div>
<div id="user-table">
<h2>User Log History</h2>
<div id="log">
<table>
<thead>
<tr>
@ -145,5 +149,6 @@
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -60,6 +60,13 @@ function getTime(){
return today;
}
const isLogged = (req, res, next) =>{
if(!req.session.userId){
res.send(`<h1 style="text-align:center; margin-top:50px;">This page is only for registered users.</h1>`);
}
else next()
}
client.on('connect', ()=>{
console.log('MQTT client connected: '+ client.connected);
});
@ -94,22 +101,10 @@ client.on('message', async (topic, message) =>{
io.emit('data', newData);
});
app.get('/', (req, res)=>{
res.sendFile(path.join(__dirname + '/index.html'));
})
app.get('/register', (req,res)=>{
res.send(`
<h1>Register</h1>
<form method='post' action='/register' />
<input type='text' name='name' placeholder='Username' required />
<input type='password' name='password' placeholder='Password' required />
<input type='submit' />
</form>
`);
});
app.post('/', async (req,res)=>{
let pwd;
const { username, password } = req.body;
@ -129,7 +124,6 @@ app.post('/', async (req,res)=>{
let y = now.slice(0,4);
let t = now.slice(11, 19);
let stamp = d + '.' + m + '.' + y + ' ' + t;
//console.log(stamp);
req.session.userId = username;
req.session.startTime = stamp;
console.log('Password is correct');
@ -145,7 +139,52 @@ app.post('/', async (req,res)=>{
console.log('Wrong password');
res.status(205);
}
})
});
app.post('/logout', async (req, res) =>{
let now = getTime();
let d = now.slice(8,10);
let m = now.slice(5,7);
let y = now.slice(0,4);
let t = now.slice(11, 19);
let stamp = d + '.' + m + '.' + y + ' ' + t;
req.session.endTime = stamp;
let sesEnd = req.session.endTime;
let log = [];
try {
log = await read('user_log.json');
} catch (e) { console.log(e); }
let newLog = {
"id": log.length +1,
"UserId": sesUser,
"Login": sesStart,
"Logout": sesEnd
}
log.unshift(newLog);
write(log, 'user_log.json');
req.session.destroy(err => {
if(err){
return res.redirect('/');
}
res.clearCookie('sid');
res.redirect('/');
});
});
app.use(isLogged);
app.get('/register', (req,res)=>{
res.send(`
<h1 style="text-align:center; margin-top:50px;">Register new user</h1>
<form method='post' action='/register' style="text-align: center;"/>
<input type='text' name='name' placeholder='Username' required /><br>
<input type='password' name='password' placeholder='Password' required /><br>
<input type='submit' id='post-reg' value="Send"/>
</form>
`);
});
app.post('/register', async (req,res) =>{
let users = [];
@ -176,50 +215,7 @@ app.post('/register', async (req,res) =>{
console.log('User exists already. No registeration done.');
}
}
res.redirect('/register')
})
app.post('/logout',async (req, res) =>{
let now = getTime();
let d = now.slice(8,10);
let m = now.slice(5,7);
let y = now.slice(0,4);
let t = now.slice(11, 19);
let stamp = d + '.' + m + '.' + y + ' ' + t;
req.session.endTime = stamp;
let sesEnd = req.session.endTime;
let log = [];
try {
log = await read('user_log.json');
} catch (e) { console.log(e); }
let newLog = {
"id": log.length +1,
"UserId": sesUser,
"Login": sesStart,
"Logout": sesEnd
}
console.log(newLog);
log.unshift(newLog);
write(log, 'user_log.json');
req.session.destroy(err => {
if(err){
return res.redirect('/');
}
res.clearCookie('sid');
res.redirect('/');
});
res.redirect('/')
});
/*
app.get('/data', async (req, res) => {
try {
const data = await read('data.json');
res.json(data);
} catch (e) {
res.status(404).send(e);
}
});
*/
server.listen(3000, () => console.log('Server listening on port 3000'));

View File

@ -18,8 +18,8 @@ const end = document.getElementById('end-time');
const reset = document.getElementById('btn_reset');
let user;
let pointerX = -1;
let pointerY = -1;
let pointX = -1;
let pointY = -1;
let lastX = 0;
let lastY = 0;
let counter = 0;
@ -56,13 +56,12 @@ socket.on('pwd', (data) =>{
if(data){
sessionStorage.setItem('reload', true);
document.location.reload();
}
});
socket.on('user', (data) =>{
user = data;
localStorage.setItem('user', data);
sessionStorage.setItem('user', data);
sessionStorage.setItem('loggedIn', 'true');
});
@ -72,9 +71,6 @@ reset.addEventListener('click', e =>{
end.value = today.replace('T', ' ') + ':00';
start_time = new Date(start.value).getTime();
end_time = new Date(end.value).getTime();
//start.value = today.replace('T', ' ') + ':00';
//end.value = today.replace('T', ' ') + ':00';
updateChart();
})
@ -122,12 +118,8 @@ automode.addEventListener('click', () =>{
manmode.addEventListener('click', () =>{
if(!sessionStorage.getItem('loggedIn')){
document.getElementById('login-form').style.display = "block";
document.getElementById('sp_div').style.display = "none";
document.getElementById('pr_div').style.display = "none";
document.getElementById('m_auto').style.display = "none";
document.getElementById('m_man').style.display = "none";
document.getElementById('auto_label').style.display = "none";
document.getElementById('man_label').style.display = "none";
document.getElementsByClassName('modes').style.display = "none";
document.getElementsByClassName('set_values').style.display = "none";
}
else{
document.getElementById('login-form').style.display = "none";
@ -154,17 +146,20 @@ document.getElementById('password').addEventListener('click', ()=>{
});
log_out.addEventListener('click', () =>{
console.log('log out clicked');
localStorage.clear();
sessionStorage.clear();
})
document.getElementById('reg').addEventListener('click', ()=>{
sessionStorage.setItem('reload', true);
})
window.addEventListener('beforeunload', (e)=>{
if(document.cookie){
log_out.click();
if(!sessionStorage.getItem('reload')){
logOutUser();
}
});
// Changes the border-color of output elements in monitor fieldset if values are outside the ranges
function circleColor(){
if(g_pressure.value > 115){
g_pressure.style.borderColor = "red";
@ -198,85 +193,64 @@ function circleColor(){
}
}
function logOutUser(){
if(document.cookie){
log_out.click();
}
}
document.onmousemove = function(event) {
pointerX = event.pageX;
pointerY = event.pageY;
pointX = event.pageX;
pointY = event.pageY;
}
setInterval(activityCheck, 1000);
// Keeps the counter if no mouse moves are detected. If the set counter limit is exceeded, the user is logged out
function activityCheck() {
if(sessionStorage.getItem('loggedIn')){
if(pointerX - lastX === 0 && pointerY - lastY === 0){
if(pointX - lastX === 0 && pointY - lastY === 0){
counter = counter + 1;
}
else{
lastX = pointerX;
lastY = pointerY;
lastX = pointX;
lastY = pointY;
counter = 0;
}
}
if(counter > 60){
if(counter > 600){
logOutUser();
}
}
// Logs out the user, click of the log out button cleares session storage with the event listener of the button
function logOutUser(){
if(sessionStorage.getItem('loggedIn')){
log_out.click();
}
}
// Session storage is used to check which elements are visible when the body of the page is loaded
function checkUser(){
if(sessionStorage.getItem('reload')){
document.getElementById('login-form').style.display = "block";
document.getElementById('pwd-warn').style.display = "block";
sessionStorage.removeItem('reload');
}
if(document.cookie && sessionStorage.getItem('loggedIn')){
if(sessionStorage.getItem('loggedIn')){
document.getElementById('login-form').style.display = "none";
document.getElementById('user').style.display = "block";
document.getElementById('user').innerHTML = 'Signed in user: ' + localStorage.getItem('user');
document.getElementById('user').innerHTML = 'Signed in user: ' + sessionStorage.getItem('user');
document.getElementById('btn_log_out').style.display = "block";
document.getElementById('user').style.display = "block";
document.getElementById('user').innerHTML = 'Signed in user: ' + localStorage.getItem('user');
document.getElementById('btn_log_out').style.display = "block";
document.getElementById('user-table').style.width = "45%";
document.getElementById('chart-cont').style.width = "50%";
if(manmode.checked = true){
s_pressure.disabled = true;
document.getElementById('pr_div').style.opacity = 0.4;
document.getElementById('sp_div').style.opacity = 1;
s_speed.disabled = false;
}
if(automode.checked = true){
s_speed.disabled = true;
document.getElementById('sp_div').style.opacity = 0.4;
s_pressure.disabled = false;
}
}
else{
document.getElementById('login-form').style.display = "block";
document.getElementById('sp_div').style.display = "none";
document.getElementById('pr_div').style.display = "none";
document.getElementById('m_auto').style.display = "none";
document.getElementById('m_man').style.display = "none";
document.getElementById('auto_label').style.display = "none";
document.getElementById('man_label').style.display = "none";
document.getElementById('user-table').style.display = "none";
document.getElementById('chart-cont').style.width = "95%";
}
}
function checkMode(){
if(document.cookie && sessionStorage.getItem('loggedIn')){
automode.checked = true;
s_speed.disabled = true;
document.getElementById('sp_div').style.opacity = 0.4;
document.getElementById('pr_div').style.opacity = 1;
s_pressure.disabled = false;
}
}
else{
document.getElementById('login-form').style.display = "block";
document.getElementById('pre-reg').style.display = "none";
document.getElementById('sp_div').style.display = "none";
document.getElementById('pr_div').style.display = "none";
document.getElementById('m_auto').style.display = "none";
@ -287,16 +261,19 @@ function checkMode(){
}
}
// Sends the selected pressure value with socket to the server
function sendPressure(){
let press = { auto: true, pressure: parseInt(s_pressure.value) }
socket.emit('setting', press);
}
// Sends the selected fan speed value with socket to the server
function sendSpeed(){
let speed = { auto: false, speed: parseInt(s_speed.value) }
socket.emit('setting', speed);
}
// Updates the data chart on the page, fetches values from json file (db) and shows the data with selected time interval
function updateChart(){
async function fetchData(){
const response = await fetch('data.json');
@ -345,6 +322,7 @@ function updateChart(){
});
};
// data and config are base settings for the data chart
const data = {
datasets: [{
label: 'CO2',
@ -433,6 +411,7 @@ const config = {
const myChart = new Chart(canvas, config);
// Fetches the data from json file (db) and updates the the user log history table
fetch('user_log.json')
.then((res)=>{
return res.json();
@ -452,6 +431,7 @@ fetch('user_log.json')
placeholder.innerHTML = out;
});
// Fetches the data from json file (db) and sets the latest values to the monitor fieldset output elements when the body of the page is loaded
async function getStartValues(){
await fetch('data.json')
.then((res) =>{

View File

@ -71,6 +71,19 @@ h4{
font-weight: bolder;
}
#pre-reg{
display: block;
position: relative;
float:right;
border-radius: 5px;
}
#reg{
border-radius: 5px;
color: black;
background-color: #CCE3DE;
}
.logout{
margin-left: 20px;
margin-top: 35px;
@ -91,6 +104,7 @@ h4{
border-radius: 5px;
background-color: #CCE3DE;
border: 0.5px solid;
color: black;
}
#user{
@ -289,11 +303,19 @@ canvas{
margin-bottom: 50px;
width: 50%;
margin: auto;
}
#log{
height: 300px;
overflow: hidden;
overflow-y: scroll;
width: 100%;
}
table{
background-color: #CCE3DE;
width: 90%;
width: 100%;
margin: auto;
border-collapse: collapse;
}
@ -302,6 +324,12 @@ table, th, td {
border: 1px solid;
}
.th.fixed{
top: 0;
z-index: 2;
position: sticky;
}
td{
text-align: center;
}

View File

@ -1,4 +1,34 @@
[
{
"id": 20,
"UserId": "Miisa",
"Login": "28.10.2022 11:48:28",
"Logout": "28.10.2022 11:55:17"
},
{
"id": 19,
"UserId": "Miisa",
"Login": "28.10.2022 11:38:52",
"Logout": "28.10.2022 11:45:02"
},
{
"id": 18,
"UserId": "Miisa",
"Login": "28.10.2022 08:28:47",
"Logout": "28.10.2022 08:29:58"
},
{
"id": 17,
"UserId": "Miisa",
"Login": "28.10.2022 08:25:47",
"Logout": "28.10.2022 08:27:40"
},
{
"id": 16,
"UserId": "Jaakko",
"Login": "28.10.2022 08:15:12",
"Logout": "28.10.2022 08:19:01"
},
{
"id": 15,
"UserId": "Miisa",