Week 4.0
Tried editing elements in inspect of webpage but it wasn’t the permanent solution to our webpage.
Therefore decided to understand the java-script code line by line.
Finally data table is proper now.
Week 4.1
Working on the display chart
Week 4.2
As working on the front-end of webpage datalogger one of my friend Kunal Dhadam helped me in correcting the mistakes in code:
Week 4.3
Index.js
// convert epochtime to JavaScripte Date object
function epochToJsDate(epochTime) {
return new Date(epochTime * 1000);
}
// convert time to human-readable format YYYY/MM/DD HH:MM:SS
function epochToDateTime(epochTime) {
const epochDate = new Date(epochToJsDate(epochTime));
const dateTime =
epochDate.getFullYear() +
"/" +
("00" + (epochDate.getMonth() + 1)).slice(-2) +
"/" +
("00" + epochDate.getDate()).slice(-2) +
" " +
("00" + epochDate.getHours()).slice(-2) +
":" +
("00" + epochDate.getMinutes()).slice(-2) +
":" +
("00" + epochDate.getSeconds()).slice(-2);
return dateTime;
}
// function to plot values on charts
function plotValues(chart, timestamp, value) {
const x = epochToJsDate(timestamp).getTime();
const y = Number(value);
if (chart.series[0].data.length > 40) {
chart.series[0].addPoint([x, y], true, true, true);
} else {
chart.series[0].addPoint([x, y], true, false, true);
}
}
// DOM elements
const loginElement = document.querySelector("#login-form");
const contentElement = document.querySelector("#content-sign-in");
const userDetailsElement = document.querySelector("#user-details");
const authBarElement = document.querySelector("#authentication-bar");
const deleteButtonElement = document.getElementById("delete-button");
const deleteModalElement = document.getElementById("delete-modal");
const deleteDataFormElement = document.querySelector("#delete-data-form");
const viewDataButtonElement = document.getElementById("view-data-button");
const hideDataButtonElement = document.getElementById("hide-data-button");
const tableContainerElement = document.querySelector("#table-container");
const chartsRangeInputElement = document.getElementById("charts-range");
const loadDataButtonElement = document.getElementById("load-data");
const cardsCheckboxElement = document.querySelector(
"input[name=cards-checkbox]"
);
const gaugesCheckboxElement = document.querySelector(
"input[name=gauges-checkbox]"
);
const chartsCheckboxElement = document.querySelector(
"input[name=charts-checkbox]"
);
var downloadBtn = document.querySelector("#download");
// DOM elements for sensor readings
const cardsReadingsElement = document.querySelector("#cards-div");
const gaugesReadingsElement = document.querySelector("#gauges-div");
const chartsDivElement = document.querySelector("#charts-div");
const tempElement0 = document.getElementById("temperature0");
const tempElement1 = document.getElementById("Lux");
const tempHumidity = document.getElementById("humidity"); //n
const updateElement = document.getElementById("lastUpdate");
// MANAGE LOGIN/LOGOUT UI
const setupUI = (user) => {
if (user) {
//toggle UI elements
loginElement.style.display = "none";
contentElement.style.display = "block";
authBarElement.style.display = "block";
userDetailsElement.style.display = "block";
userDetailsElement.innerHTML = user.email;
// get user UID to get data from database
const uid = user.uid;
console.log(uid);
// Database paths (with user UID)
const dbPath = "UsersData/" + uid.toString() + "/readings";
const chartPath = "UsersData/" + uid.toString() + "/charts/range";
const namePath = "UsersData/" + uid.toString() + "/email";
// Database references
const dbRef = firebase.database().ref(dbPath);
const chartRef = firebase.database().ref(chartPath);
const nameRef = firebase.database().ref(namePath);
nameRef.set(user.email);
// CHARTS
// Number of readings to plot on charts
var chartRange = 0;
// Get number of readings to plot saved on database (runs when the page first loads and whenever there's a change in the database)
chartRef.on("value", (snapshot) => {
chartRange = Number(snapshot.val());
console.log(chartRange);
// Delete all data from charts to update with new values when a new range is selected
chartT0.destroy();
chartH.destroy();
chartT1.destroy();
// Render new charts to display new range of data
chartT0 = createDryTemperatureChart();
chartH = createHumidityChart();
chartT1 = createLuxChart(); //yet to create
// Update the charts with the new range
// Get the latest readings and plot them on charts (the number of plotted readings corresponds to the chartRange value)
dbRef
.orderByKey()
.limitToLast(chartRange)
.on("child_added", (snapshot) => {
const jsonData = snapshot.toJSON(); // example: {temperature: 25.02, humidity: 50.20, pressure: 1008.48, timestamp:1641317355}
// Save values on variables
const temperature0 = jsonData.temperature0;
const Lux = jsonData.temperature1;
const humidity = jsonData.humidity; //n
const timestamp = jsonData.timestamp;
// Plot the values on the charts
plotValues(chartT0, timestamp, temperature0);
plotValues(chartH, timestamp, humidity);
plotValues(chartT1, timestamp, Lux);
//n chart a regarder
});
});
// Update database with new range (input field)
chartsRangeInputElement.onchange = () => {
chartRef.set(chartsRangeInputElement.value);
};
//CHECKBOXES
// Checbox (cards for sensor readings)
cardsCheckboxElement.addEventListener("change", (e) => {
if (cardsCheckboxElement.checked) {
cardsReadingsElement.style.display = "block";
} else {
cardsReadingsElement.style.display = "none";
}
});
// Checbox (gauges for sensor readings)
gaugesCheckboxElement.addEventListener("change", (e) => {
if (gaugesCheckboxElement.checked) {
gaugesReadingsElement.style.display = "block";
} else {
gaugesReadingsElement.style.display = "none";
}
});
// Checbox (charta for sensor readings)
chartsCheckboxElement.addEventListener("change", (e) => {
if (chartsCheckboxElement.checked) {
chartsDivElement.style.display = "block";
} else {
chartsDivElement.style.display = "none";
}
});
// CARDS
// Get the latest readings and display on cards
dbRef
.orderByKey()
.limitToLast(1)
.on("child_added", (snapshot) => {
const jsonData = snapshot.toJSON(); // example: {temperature: 25.02, humidity: 50.20, pressure: 1008.48, timestamp:1641317355}
const temperature0 = jsonData.temperature0;
const Lux = jsonData.temperature1;
const humidity = jsonData.humidity; //n
const timestamp = jsonData.timestamp;
// Update DOM elements
tempElement0.innerHTML = temperature0;
tempElement1.innerHTML = Lux;
tempHumidity.innerHTML = humidity; //n
updateElement.innerHTML = epochToDateTime(timestamp);
});
// GAUGES
// Get the latest readings and display on gauges
dbRef
.orderByKey()
.limitToLast(1)
.on("child_added", (snapshot) => {
const jsonData = snapshot.toJSON(); // example: {temperature: 25.02, humidity: 50.20, pressure: 1008.48, timestamp:1641317355}
const temperature0 = jsonData.temperature0;
const humidity = jsonData.humidity;
const Lux = jsonData.temperature1;
const timestamp = jsonData.timestamp;
// Update DOM elements
const gaugeT0 = createDryTemperatureGauge();
const gaugeLux = createLuxGauge();
const gaugeH = createHumidityGauge();
gaugeT0.value = temperature0;
gaugeLux.value = Lux;
gaugeH.value = humidity;
updateElement.innerHTML = epochToDateTime(timestamp);
});
// DELETE DATA
// Add event listener to open modal when click on "Delete Data" button
deleteButtonElement.addEventListener("click", (e) => {
console.log("Remove data");
e.preventDefault;
deleteModalElement.style.display = "block";
});
// Add event listener when delete form is submited
deleteDataFormElement.addEventListener("submit", (e) => {
// delete data (readings)
dbRef.remove();
});
// TABLE
var lastReadingTimestamp; //saves last timestamp displayed on the table
// Function that creates the table with the first 100 readings
function createTable() {
// append all data to the table
var firstRun = true;
dbRef
.orderByKey()
.limitToLast(100)
.on("child_added", function (snapshot) {
if (snapshot.exists()) {
const jsonData = snapshot.toJSON();
console.log(jsonData);
/* var temperature = jsonData.temperature;
var humidity = jsonData.humidity;
var pressure = jsonData.pressure; */
const temperature0 = jsonData.temperature0;
const Lux = jsonData.temperature1;
const humidity = jsonData.humidity;
const timestamp = jsonData.timestamp;
var content = "";
content += "<tr>";
content += "<td>" + epochToDateTime(timestamp) + "</td>";
/* content += '<td>' + temperature + '</td>';
content += '<td>' + humidity + '</td>';
content += '<td>' + pressure + '</td>'; */
content += "<td>" + temperature0 + "</td>";
content += "<td>" + humidity + "</td>";
content += "<td>" + Lux + "</td>";
content += "</tr>";
$("#tbody").prepend(content);
// Save lastReadingTimestamp --> corresponds to the first timestamp on the returned snapshot data
if (firstRun) {
lastReadingTimestamp = timestamp;
firstRun = false;
console.log(lastReadingTimestamp);
}
}
});
}
// append readings to table (after pressing More results... button)
function appendToTable() {
const dataList = []; // saves list of readings returned by the snapshot (oldest-->newest)
const reversedList = []; // the same as previous, but reversed (newest--> oldest)
console.log("APEND");
dbRef
.orderByKey()
.limitToLast(100)
.endAt(lastReadingTimestamp)
.once("value", function (snapshot) {
// convert the snapshot to JSON
if (snapshot.exists()) {
snapshot.forEach((element) => {
const jsonData = element.toJSON();
dataList.push(jsonData); // create a list with all data
});
lastReadingTimestamp = dataList[0].timestamp; //oldest timestamp corresponds to the first on the list (oldest --> newest)
reversedList = dataList.reverse(); // reverse the order of the list (newest data --> oldest data)
var firstTime = true;
// loop through all elements of the list and append to table (newest elements first)
reversedList.forEach((element) => {
if (firstTime) {
// ignore first reading (it's already on the table from the previous query)
firstTime = false;
} else {
//var temperature = element.temperature;
const temperature0 = element.temperature0;
const Lux = element.Lux;
const humidity = element.humidity;
const timestamp = element.timestamp;
var content = "";
content += "<tr>";
content += "<td>" + epochToDateTime(timestamp) + "</td>";
content += "<td>" + temperature0 + "</td>";
content += "<td>" + Lux + "</td>";
content += "<td>" + humidity + "</td>";
content += "</tr>";
$("#tbody").append(content);
}
});
}
});
}
viewDataButtonElement.addEventListener("click", (e) => {
// Toggle DOM elements
tableContainerElement.style.display = "block";
viewDataButtonElement.style.display = "none";
hideDataButtonElement.style.display = "inline-block";
loadDataButtonElement.style.display = "inline-block";
createTable();
});
//***************DOWNLOAD BUTTON*******************
function tableToCSV() {
// Variable to store the final csv data
var csv_data = [];
csv_data.push("sep=,");
// Get each row data
const rows = document.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
// Get each column data
const cols = rows[i].querySelectorAll("td,th");
// Stores each csv row data
var csvrow = [];
for (var j = 0; j < cols.length; j++) {
// Get the text data of each cell of
// a row and push it to csvrow
csvrow.push(cols[j].innerHTML);
}
// Combine each column value with comma
csv_data.push(csvrow.join(","));
}
// combine each row data with new line character
csv_data = csv_data.join("\n");
downloadCSVFile(csv_data);
/* We will use this function later to download
the data in a csv file downloadCSVFile(csv_data);
*/
}
function downloadCSVFile(csv_data) {
// Create CSV file object and feed our csv_data into it
const CSVFile = new Blob([csv_data], {
type: "text/csv",
});
// Create to temporary link to initiate
// download process
const temp_link = document.createElement("a");
// Download csv file
temp_link.download = "measurement_history_solar_dryer.csv";
const url = window.URL.createObjectURL(CSVFile);
temp_link.href = url;
// This link should not be displayed
temp_link.style.display = "none";
document.body.appendChild(temp_link);
// Automatically click the link to trigger download
temp_link.click();
document.body.removeChild(temp_link);
}
downloadBtn.addEventListener("click", tableToCSV);
//***************DOWNLOAD BUTTON*******************
loadDataButtonElement.addEventListener("click", (e) => {
appendToTable();
});
hideDataButtonElement.addEventListener("click", (e) => {
tableContainerElement.style.display = "none";
viewDataButtonElement.style.display = "inline-block";
hideDataButtonElement.style.display = "none";
});
// IF USER IS LOGGED OUT
} else {
// toggle UI elements
loginElement.style.display = "block";
authBarElement.style.display = "none";
userDetailsElement.style.display = "none";
contentElement.style.display = "none";
}
};
important point to note in some pc code is working when variable is defined using var and in some using as const
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP Datalogging Firebase App</title>
<!-- include Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-database.js"></script>
<script>
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyD5QY8FTrFuZa6U6g4u7xTYLE1Tm4PCO_Q",
authDomain: "thn-sensor.firebaseapp.com",
databaseURL: "https://thn-sensor-default-rtdb.asia-southeast1.firebasedatabase.app",
projectId: "thn-sensor",
storageBucket: "thn-sensor.appspot.com",
messagingSenderId: "555617789996",
appId: "1:555617789996:web:83ffa25dc12f5861fbc989"
};
// Initialize firebase
firebase.initializeApp(firebaseConfig);
// Make auth and database references
const auth = firebase.auth();
const db = firebase.database();
</script>
<!-- include highchartsjs to build the charts-->
<script src="https://code.highcharts.com/highcharts.js"></script>
<!-- include to use jquery-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!--include icons from fontawesome-->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<!-- include Gauges Javascript library-->
<script src="https://cdn.rawgit.com/Mikhus/canvas-gauges/gh-pages/download/2.1.7/all/gauge.min.js"></script>
<!--reference for favicon-->
<link rel="icon" type="image/png" href="favicon.png">
<!--reference a stylesheet-->
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<!--TOP BAR-->
<div class="topnav">
<img src="img/valogo.jpg" alt="logo" />
<h1> DRYER UNIT_5<i class="fas fa-clipboard-list"></i></h1>
</div>
<!--AUTHENTICATION BAR (USER DETAILS/LOGOUT BUTTON)-->
<div id="authentication-bar" style="display: none;">
<p><span id="authentication-status">User logged in</span>
<span id="user-details">USEREMAIL</span>
<a href="/" id="logout-link">(logout)</a>
</p>
</div>
<!--LOGIN FORM-->
<form id="login-form" style="display: none;">
<div class="form-elements-container">
<label for="input-email"><b>Email</b></label>
<input type="text" placeholder="Enter Username" id="input-email" required>
<label for="input-password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" id="input-password" required>
<button type="submit" id="login-button">Login</button>
<p id="error-message" style="color:red;"></p>
</div>
</form>
<!--CONTENT (SENSOR READINGS)-->
<div class="content-sign-in" id="content-sign-in" style="display: none;">
<!--LAST UPDATE-->
<p><span class ="date-time">Last update: <span id="lastUpdate"></span></span></p>
<p>
<p><span class ="informations"> Temperature has a precision of ±1°C and humidity has a ±5% precision </span></p>
<p>
Card: <input type="checkbox" id="cards-checkbox" name="cards-checkbox" checked>
Gauge: <input type="checkbox" id="gauges-checkbox" name="gauges-checkbox" checked>
Chart: <input type="checkbox" id="charts-checkbox" name="charts-checkbox" unchecked>
</p>
<div id="cards-div">
<div class="cards">
<!--TEMPERATURE-->
<div class="card">
<p><i class="fas fa-thermometer-half" style="color:#fff200;"></i> TEMPERATURE</p>
<p><span class="reading"><span id="temperature0"></span> °C</span></p>
</div>
<!--HUMIDITY-->
<div class="card">
<p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
<p><span class="reading"><span id="humidity"></span> %</span></p>
</div>
<!--LIGHT INTENSITY-->
<div class="card">
<p><i class="fas fa-lightbulb" style="color:#ff9100;"></i> LIGHT INTENSITY</p>
<p><span class="reading"><span id="Lux"></span>lx</span></p>
</div>
</div>
</div>
<!--GAUGES-->
<div id ="gauges-div">
<div class="cards">
<!--TEMPERATURE-->
<div class="card">
<canvas id="gauge-dry-temperature"></canvas>
</div>
<!--HUMIDITY-->
<div class="card">
<canvas id="gauge-humidity"></canvas>
</div>
<!--LIGHT INTENSITY-->
<div class="card">
<canvas id="gauge-Lux"></canvas>
</div>
</div>
</div>
<!--CHARTS-->
<div id="charts-div" style="display:none">
<!--SET NUMBER OF READINGS INPUT FIELD-->
<div>
<p> Number of readings: <input type="number" id="charts-range"></p>
</div>
<!--TEMPERATURE-CHART-->
<div class="cards">
<div class="card">
<p><i class="fas fa-thermometer-half" style="color:#fff200;"></i> TEMPERATURE CHART</p>
<div id="chart-dry-temperature" class="chart-container"></div>
</div>
</div>
<!--HUMIDITY-CHART-->
<div class="cards">
<div class="card">
<p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY CHART</p>
<div id="chart-humidity" class="chart-container"></div>
</div>
</div>
<!--LUX-CHART-->
<div class="cards">
<div class="card">
<p><i class="fas fa-lightbulb" style="color:#ff9100;"></i> LUX CHART</p>
<div id="chart-lux" class="chart-container"></div>
</div>
</div>
</div>
<!--BUTTONS TO HANDLE DATA-->
<p>
<!--View data button-->
<button id="view-data-button">View all data</button>
<!--Hide data button-->
<button id="hide-data-button" style= "display:none;">Hide data</button>
<!--Delete data button-->
<button id="delete-button" class="deletebtn">Delete data</button>
</p>
<!--Modal to delete data-->
<div id="delete-modal" class="modal" sytle="display:none">
<span onclick = "document.getElementById('delete-modal').style.display='none'" class="close" title="Close Modal">×</span>
<form id= "delete-data-form" class="modal-content" action="/">
<div class="container">
<h1>Delete Data</h1>
<p>Are you sure you want to delete all data from database?</p>
<div class="clearfix">
<button type="button" onclick="document.getElementById('delete-modal').style.display='none'" class="cancelbtn">Cancel</button>
<button type="submit" onclick="document.getElementById('delete-modal').style.display='none'" class="deletebtn">Delete</button>
</div>
</div>
</form>
</div>
<!--TABLE WITH ALL DATA-->
<div class ="cards">
<div class="card" id="table-container" style= "display:none;">
<table id="readings-table">
<tr id="theader">
<th>Timestamp</th>
<th>Temperature (ºC)</th>
<th>Hum (%)</th>
<th>Light Intensity</th>
</tr>
<tbody id="tbody">
</tbody>
</table>
<p>
<button id="load-data" style= "display:none;">More results...</button>
<button id="download" >Download CSV</button>
</p>
</div>
</div>
</div>
<!--INCLUDE JS FILES-->
<script src="scripts/auth.js"></script>
<script src="scripts/charts-definition.js"></script>
<script src="scripts/gauge-defination.js"></script>
<script src="scripts/index.js"></script>
</body>
</html>
STYLE
html {
font-family: Verdana, Geneva, Tahoma, sans-serif;
display: inline-block;
text-align: center;
}
body {
margin: 0;
width: 100%;
}
.topnav {
overflow: hidden;
background-color: white;
color: blue;
font-size: 1rem;
padding: 5px;
}
.topnav img {
height: 150px;
}
#authentication-bar{
background-color:#FFD1DC;
padding-top: 10px;
padding-bottom: 10px;
}
#user-details{
color: cadetblue;
}
.content {
padding: 20px;
}
.card {
background-color: white;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding: 5%;
}
.cards {
max-width: 800px;
margin: 0 auto;
margin-bottom: 10px;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 2fr));
}
.reading {
color: #193036;
}
.date-time{
font-size: 0.8rem;
color: #1282A2;
}
.informations{
font-size: 1rem;
color: #ff0000;
}
button {
background-color: #049faa;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
border-radius: 4px;
}
button:hover {
opacity: 0.8;
}
.deletebtn{
background-color: #c52c2c;
}
.form-elements-container{
padding: 16px;
width: 250px;
margin: 0 auto;
}
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
table {
width: 100%;
text-align: center;
font-size: 0.8rem;
}
tr, td {
padding: 0.25rem;
}
tr:nth-child(even) {
background-color: #f2f2f2
}
tr:hover {
background-color: #ddd;
}
th {
position: sticky;
top: 0;
background-color: #50b8b4;
color: white;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: #474e5d;
padding-top: 50px;
}
/* Modal Content/Box */
.modal-content {
background-color: #FFFFFF;
margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
}
/* Style the horizontal ruler */
hr {
border: 1px solid #f1f1f1;
margin-bottom: 25px;
}
/* The Modal Close Button (x) */
.close {
position: absolute;
right: 35px;
top: 15px;
font-size: 40px;
font-weight: bold;
color: #f1f1f1;
}
.close:hover,
.close:focus {
color: #f44336;
cursor: pointer;
}
/* Clear floats */
.clearfix::after {
content: "";
clear: both;
display: table;
}
/* Change styles for cancel button and delete button on extra small screens */
@media screen and (max-width: 300px) {
.cancelbtn, .deletebtn {
width: 100%;
}
}
GAUGE DEFINATION
// Create DRY Temperature Gauge
function createDryTemperatureGauge() {
var gauge = new LinearGauge({
renderTo: 'gauge-dry-temperature',
width: 120,
height: 400,
units: "Temperature °C",
minValue: 0,
startAngle: 90,
ticksAngle: 180,
maxValue: 150,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueDec: 2,
valueInt: 2,
majorTicks: [
"0",
"10",
"20",
"30",
"40",
"50",
"60",
"70",
"80",
"90",
"100",
"110",
"120",
"130",
"140",
"150"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 100,
"to": 150,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#fff",
colorBarProgress: "#CC2936",
colorBarProgressEnd: "#049faa",
borderShadowWidth: 0,
borders: false,
needleType: "arrow",
needleWidth: 2,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear",
barWidth: 10,
});
return gauge;
}
// Create Humidity Gauge
function createHumidityGauge(){
var gauge = new RadialGauge({
renderTo: 'gauge-humidity',
width: 250,
height: 250,
units: "Humidity (%)",
minValue: 0,
maxValue: 100,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueInt: 2,
majorTicks: [
"0",
"20",
"40",
"60",
"80",
"100"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 0,
"to": 30,
"color": "#FF4500" //orangered
},
{
"from": 30,
"to": 60,
"color": "#FDDA0D" //cadmium yellow
},
{
"from": 60,
"to": 70,
"color": "#7CFC00" //lawngreen
},
{
"from": 70,
"to": 100,
"color": "#03C0C1"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "line",
colorNeedle: "#007F80",
colorNeedleEnd: "#007F80",
needleWidth: 2,
needleCircleSize: 3,
colorNeedleCircleOuter: "#007F80",
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
});
return gauge;
}
function createLuxGauge(){
var gauge = new RadialGauge({
renderTo: 'gauge-Lux',
width: 250,
height: 250,
units: "Lux (lx)",
minValue: 0,
maxValue: 200000,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueInt: 2,
majorTicks: [
"0",
"40000",
"80000",
"120000",
"160000",
"200000"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 0,
"to": 20000,
"color": "#FF4500" //orangered
},
{
"from": 20000,
"to": 40000,
"color": "#FDDA0D" //cadmium yellow
},
{
"from": 40000,
"to": 120000,
"color": "#7CFC00" //lawngreen
},
{
"from": 120000,
"to": 200000,
"color": "#03C0C1"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "line",
colorNeedle: "#007F80",
colorNeedleEnd: "#007F80",
needleWidth: 2,
needleCircleSize: 3,
colorNeedleCircleOuter: "#007F80",
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
});
return gauge;
}
CHART
// Create the charts when the web page loads
window.addEventListener('load', onload);
function onload(event){
chartT0 = createDryTemperatureChart();
chartH = createHumidityChart();
chartT1 = createLuxChart();
}
// Create DRY Temperature Chart
function createDryTemperatureChart() {
var chart = new Highcharts.Chart({
chart:{
renderTo:'chart-dry-temperature',
type: 'spline'
},
series: [
{
name: 'SENSOR 0'
}
],
title: {
text: undefined
},
plotOptions: {
line: {
animation: false,
dataLabels: {
enabled: true
}
}
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: {
text: 'Temperature Celsius Degrees'
}
},
credits: {
enabled: false
}
});
return chart;
}
// Create Humidity Chart
function createHumidityChart(){
var chart = new Highcharts.Chart({
chart:{
renderTo:'chart-humidity',
type: 'spline'
},
series: [{
name: 'HUMIDITY CALCULATION'
}],
title: {
text: undefined
},
plotOptions: {
line: {
animation: false,
dataLabels: {
enabled: true
}
},
series: {
color: '#50b8b4'
}
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: {
text: 'Humidity (%)'
}
},
credits: {
enabled: false
}
});
return chart;
}
function createLuxChart(){
var chart = new Highcharts.Chart({
chart:{
renderTo:'chart-lux',
type: 'spline'
},
series: [{
name: 'LUX CALCULATION'
}],
title: {
text: undefined
},
plotOptions: {
line: {
animation: false,
dataLabels: {
enabled: true
}
},
series: {
color: '#ff9100'
}
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: {
text: 'LUX (lx)'
}
},
credits: {
enabled: false
}
});
return chart;
}
ESP-32 code
#define WIFI_JIO
//#include
#if defined(ESP32)
#include
#elif defined(ESP8266)
#include
#endif
#include
#include
#include "time.h"
//AHT temperature sensors:
#include
#include
#include
BH1750 lightMeter(0x23);
#define SDA_2 33
#define SCL_2 32
TwoWire I2C_2 = TwoWire(1);
//#include
//Adafruit_AHTX0 aht; // I2C
AHTxx aht20(AHTXX_ADDRESS_X38, AHT2x_SENSOR);
//include the following for the Firebase library to work.
// Provide the token generation process info.
#include "addons/TokenHelper.h"
// Provide the RTDB payload printing info and other helper functions.
#include "addons/RTDBHelper.h"
// Insert your network credentials and make sure the define at the beginning of the document is correct
#if defined(WIFI_JIO)
//#define WIFI_SSID "JioFi_10C2184"
//#define WIFI_PASSWORD "2ycrszvn8p"
//#define WIFI_SSID "JioFi_10C2184"
//#define WIFI_PASSWORD "2ycrszvn8p"
#define WIFI_SSID "wifi name"
#define WIFI_PASSWORD "wifi password"
#define WIFI_NAME "WS"
#endif
// Insert Firebase project API Key
#define API_KEY "ENTER YOUR API KEY" //"va-black-soldier-flies" firebase project
// Insert Authorized Email and Corresponding Password
#define USER_EMAIL "ENTER EMAIL linked to firebase" //you can also choose: va_esp32_test@gmail.com
#define USER_PASSWORD "enter password"
// Insert RTDB URLefine the RTDB URL
#define DATABASE_URL "https://thn-sensor-default-rtdb.asia-southeast1.firebasedatabase.app/"
// Define Firebase objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
// Variable to save USER UID
String uid;
// Data wire is plugged TO GPIO 4
/* #define ONE_WIRE_BUS 4
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire); */
float temp=0;
float rh=0;
int hours;
int minutes;
/* // Number of temperature devices found
int numberOfDevices;*/
// We'll use this variable to store a found device address
//DeviceAddress tempDeviceAddress;
// Database main path (to be updated in setup with the user UID)
String databasePath;
// Database child nodes
String temppath = "/temperature0";
String prespath = "/temperature1";
String humPath = "/humidity";
String timePath = "/timestamp";
// Parent Node (to be updated in every loop)
String parentPath;
int timestamp;
FirebaseJson json;
const char* ntpServer = "pool.ntp.org";
float humidity;
// Timer variables (send new readings every three minutes)
unsigned long sendDataPrevMillis = 0;
unsigned long timerDelay =1000; //30 minute delay, 1 sec = 1000 for timerDelay
//variables for actuators
unsigned long delayTime;
void start_aht()
{
while (aht20.begin() != true)
{
Serial.println(F("AHT2x not connected or fail to load calibration coefficient"));
delay(2000);
}
Serial.println(F("AHT20 OK"));
}
// Initialize WiFi
void initWiFi() {
Serial.print("Connecting to ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.print("\nConnected to WiFi!\n");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println();
}
// Function that gets current epoch time
unsigned long getTime()
{
time_t now;
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
//Serial.println("Failed to obtain time");
return(0);
}
hours = timeinfo.tm_hour;
minutes = timeinfo.tm_min;
time(&now);
return now;
}
/************************************SETUP************************************/
void setup(){
//relay
// Initialize WiFi
initWiFi();
start_aht();
Serial.begin(115200);
Wire.begin();
I2C_2.begin(SDA_2, SCL_2);
if (!lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &I2C_2)) {
Serial.println("Could not find BH1750 sensor!");
while (1) delay(10);
}
Serial.println(F("BH1750 Test begin"));
bool status;
// default settings
configTime(0, 0, ntpServer);
// Assign the api key (required)
config.api_key = API_KEY;
// Assign the user sign in credentials
auth.user.email = USER_EMAIL;
auth.user.password = USER_PASSWORD;
// Assign the RTDB URL (required)
config.database_url = DATABASE_URL;
Firebase.reconnectWiFi(true);
fbdo.setResponseSize(4096);
// Assign the callback function for the long running token generation task
config.token_status_callback = tokenStatusCallback; //see addons/TokenHelper.h
// Assign the maximum retry of token generation
config.max_token_generation_retry = 5;
// Initialize the library with the Firebase authen and config
Firebase.begin(&config, &auth);
// Getting the user UID might take a few seconds
Serial.println("Getting User UID");
while ((auth.token.uid) == "") {
Serial.print('.');
delay(1000);
}
// Print user UID
uid = auth.token.uid.c_str();
Serial.print("User UID: ");
Serial.print(uid);
// Update database path
databasePath = "/UsersData/"+ uid + "/readings";
Serial.println("\n\nEnd of void setup function");
}
//************************************LOOP************************************
void loop(){
Serial.println("entering loop...");
temp = aht20.readTemperature();
Serial.print("Temperature (ºC): "); Serial.println(temp);
rh = aht20.readHumidity();
Serial.print("Humidity (%): ");
Serial.println(rh);
delay(1000);
float lux = lightMeter.readLightLevel();
Serial.print("Light: ");
Serial.print(lux);
Serial.println(" lx");
delay(1000);
timestamp = getTime();
Serial.print ("current time: ");
Serial.println (timestamp);
Serial.println (hours);Serial.println (minutes);
//Check variables and activate controls
// Send new readings to database
if (Firebase.ready() && (millis() - sendDataPrevMillis > timerDelay || sendDataPrevMillis == 0)){
sendDataPrevMillis = millis();
//Get current timestamp
timestamp = getTime();
Serial.print ("current time: ");
Serial.println (timestamp);
parentPath= databasePath + "/" + String(timestamp);
json.set(temppath.c_str(), String(temp));
json.set(prespath.c_str(), String(lux));
json.set(humPath.c_str(), String(rh));
json.set(timePath, String(timestamp));
//We can call that instruction inside a Serial.printf() command to print the results in the Serial Monitor at the same time the command runs.
Serial.printf("Set json... %s\n", Firebase.RTDB.setJSON(&fbdo, parentPath.c_str(), &json) ? "ok" : fbdo.errorReason().c_str());
}
}
All this at one place: https://drive.google.com/drive/folders/1rtPfVtiHqLQE7rHlbQTn4htS1zCm4zRh?usp=drive_link