Compare commits

..

No commits in common. "basicweb" and "main" have entirely different histories.

9 changed files with 2384 additions and 674 deletions

View File

@ -103,20 +103,4 @@ For ease of adaption, connection, and prototyping I've decided to use Cat 5 ethe
1 | auger_off | 1400
2 | pause | 5000
3 | igniter_start | 420000
4 | blower_stop | 600000
# Startup Procedure
1. Manually toggle the "I" Igniter switch ON
2. Wait 60 seconds
3. Manually toggle the "E" Exhaust switch ON
4. Using the Hestia Web Portal, set Feed Rate to 2
5. Enable the Auger
6. Wait up to three minutes for pellets to ignite
7. Manually toggle the "I" Ingniter switch OFF
8. Monitor fire and adjust feed rates as necessary
# Timings
* 60-90 seconds igniter preheat
* 120-180 seconds for pellet ignition
* 4 minutes from ignition to PoF Close
* 7:30 minutes total
4 | blower_stop | 600000

45
main.js
View File

@ -9,15 +9,9 @@ portal.start();
dbfn.run(`UPDATE timestamps SET value = ${Date.now()} WHERE key = 'process_start'`).catch(err => console.error(`Error setting process start time: ${err}`));
// Initialization, which then calls main()
fn.commands.refreshConfig().then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
config = res.config;
const shutdownNextCycleQuery = "UPDATE status SET value = 0 WHERE key = 'shutdown_next_cycle'";
dbfn.run(shutdownNextCycleQuery).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Shutdown flag reset.`);
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
// Setup for use with the Pi's GPIO pins
switch (process.env.ONPI) {
case 'true':
@ -53,45 +47,6 @@ fn.commands.refreshConfig().then(res => {
});
function main(gpio) {
// Set the Igniter
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: IGN: [${config.status.igniter}] | EXH: [${config.status.exhaust}] | AUG: [${config.status.auger}]`);
switch (config.status.igniter) {
case '0':
fn.igniter.off(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
});
break;
case '1':
fn.igniter.on(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
});
break;
default:
fn.igniter.off(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
});
break;
}
// Set the Exhaust
switch (config.status.exhaust) {
case '0':
fn.exhaust.off(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
});
break;
case '1':
fn.exhaust.on(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
});
break;
default:
fn.exhaust.off(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
});
break;
}
// If the auger is enabled
if (config.status.auger == 1) {
// Run a cycle of the auger

View File

@ -7,20 +7,15 @@
* Add actual data into the responses
*/
// Modules
const express = require('express');
const http = require('http');
const fs = require('fs');
const fn = require('./functions.js').functions;
const { dbfn } = require('./functions.js');
// Grab the current configuration settings from the database to display
var config;
fn.commands.refreshConfig().then(newConfig => {
config = newConfig.config;
});
const { dbfn } = require('./functions.js');
// Create the server
const app = express();
const server = http.createServer(app);
app.use(express.urlencoded());
@ -34,70 +29,28 @@ app.set('view engine', 'html');
// A normal load of the root page
app.get('/', (req, res) => {
// Render the page, passing the config along for the front end to use
// if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${JSON.stringify(config)}`);
res.render('index', { config: JSON.stringify(config) });
});
// A POST form submission to the root page
app.post('/', (req, response) => {
if (req.body.augerOn != undefined) {
fn.commands.augerOn();
if (req.body.start != undefined) {
fn.commands.startup();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
if (req.body.augerOff != undefined) {
fn.commands.augerOff();
if (req.body.shutdown != undefined) {
fn.commands.shutdown();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
if (req.body.igniterOn != undefined) {
fn.commands.igniterOn();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
if (req.body.igniterOff != undefined) {
fn.commands.igniterOff();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
if (req.body.exhaustOn != undefined) {
fn.commands.exhaustOn();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
if (req.body.exhaustOff != undefined) {
fn.commands.exhaustOff();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
if (req.body.reload != undefined) {
const updateAugerOffIntervalQuery = `UPDATE intervals SET value = '${2000 - req.body.feedRate}' WHERE key = 'auger_off'`;
const updateAugerOnIntervalQuery = `UPDATE intervals SET value = '${req.body.feedRate}' WHERE key = 'auger_on'`;
@ -112,24 +65,15 @@ app.post('/', (req, response) => {
});
}).catch(err => console.log(`E: ${err}`));
}).catch(err => console.log(`E: ${err}`));
return;
}
if (req.body.quit != undefined) {
fs.appendFile('quit', ".", err => {
if (err) console.error(err);
});
fn.commands.quit();
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
return;
}
fn.commands.refreshConfig().then(res => {
config = res.config;
response.render('index', { config: JSON.stringify(config) });
return;
});
});
module.exports = {

View File

@ -127,8 +127,8 @@ const createIntervalsTableQuery = "CREATE TABLE IF NOT EXISTS intervals (key var
dbfn.run(createIntervalsTableQuery).then(res => {
console.log(res.status);
const intervalsEntries = {
auger_on: 500,
auger_off: 1500,
auger_on: 600,
auger_off: 1400,
pause: 5000,
igniter_start: 420000,
blower_stop: 600000

View File

@ -2,9 +2,6 @@
// TODO: Move these to config
// Physical Pin numbers for GPIO
const augerPin = 7; // Pin for controlling the relay for the pellet auger motor.
const igniterPin = 13;
const exhaustPin = 15;
const pofPin = 16;
// Require the package for pulling version numbers
const package = require('../package.json');
@ -87,83 +84,9 @@ const functions = {
});
},
},
igniter: {
// Gets called once the Igniter Pin has been setup by rpi-gpio
ready(err) {
if (err) throw err;
console.log('Igniter GPIO Ready');
return;
},
// Turns the Igniter on (Pin 7 high)
on(gpio) {
return new Promise((resolve) => {
if (process.env.ONPI == 'true') {
gpio.write(igniterPin, true, function (err) {
if (err) throw err;
resolve('Igniter turned on.');
});
} else {
resolve('Simulated Igniter turned on.');
}
});
},
// Turns the Igniter off (pin 7 low)
off(gpio) {
return new Promise((resolve) => {
if (process.env.ONPI == 'true') {
gpio.write(igniterPin, false, function (err) {
if (err) throw err;
resolve('Igniter turned off.');
});
} else {
resolve('Simulated Igniter turned off.');
}
});
}
},
exhaust: {
// Gets called once the Exhaust Pin has been setup by rpi-gpio
ready(err) {
if (err) throw err;
console.log('Exhaust GPIO Ready');
return;
},
// Turns the Exhaust on (Pin 7 high)
on(gpio) {
return new Promise((resolve) => {
if (process.env.ONPI == 'true') {
gpio.write(exhaustPin, true, function (err) {
if (err) throw err;
resolve('Exhaust turned on.');
});
} else {
resolve('Simulated Exhaust turned on.');
}
});
},
// Turns the Exhaust off (pin 7 low)
off(gpio) {
return new Promise((resolve) => {
if (process.env.ONPI == 'true') {
gpio.write(exhaustPin, false, function (err) {
if (err) throw err;
resolve('Exhaust turned off.');
});
} else {
resolve('Simulated Exhaust turned off.');
}
});
}
},
commands: {
// Prepare the stove for starting
augerOn() { // FKA startup()
startup() {
// Basic startup just enables the auger
const enableAugerQuery = "UPDATE status SET value = 1 WHERE key = 'auger'";
dbfn.run(enableAugerQuery).then(res => {
@ -171,7 +94,7 @@ const functions = {
return;
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
},
augerOff() { // FKA shutdown()
shutdown() {
// Basic shutdown only needs to disable the auger
const disableAugerQuery = "UPDATE status SET value = 0 WHERE key = 'auger'";
dbfn.run(disableAugerQuery).then(res => {
@ -180,36 +103,6 @@ const functions = {
return;
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
},
igniterOn() {
const enableIgniterQuery = "UPDATE status SET value = 1 WHERE key = 'igniter'";
dbfn.run(enableIgniterQuery).then(res => {
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Igniter enabled.`);
return;
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
},
igniterOff() {
const disableIgniterQuery = "UPDATE status SET value = 0 WHERE key = 'igniter'";
dbfn.run(disableIgniterQuery).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Igniter disabled.`);
return;
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
},
exhaustOn() {
const enableExhaustQuery = "UPDATE status SET value = 1 WHERE key = 'blower'";
dbfn.run(enableExhaustQuery).then(res => {
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Exhaust enabled.`);
return;
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
},
exhaustOff() {
const disableExhaustQuery = "UPDATE status SET value = 0 WHERE key = 'blower'";
dbfn.run(disableExhaustQuery).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Exhaust disabled.`);
return;
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
},
// Pauses the script for the time defined in env variables
pause() {
return new Promise((resolve) => {
@ -265,7 +158,7 @@ const functions = {
let { status } = config;
let { rows } = res;
status.auger = rows.auger;
status.exhaust = rows.blower; // TODO update db to use exhaust not blower
status.blower = rows.blower;
status.igniter = rows.igniter;
status.igniterFinished = rows.igniter_finished;
status.pof = rows.proof_of_fire;
@ -277,8 +170,8 @@ const functions = {
dbfn.all(selectTimestampsQuery).then(res => {
let { timestamps } = config;
let { rows } = res;
timestamps.exhaustOff = rows.blower_off;
timestamps.exhaustOn = rows.blower_on;
timestamps.blowerOff = rows.blower_off;
timestamps.blowerOn = rows.blower_on;
timestamps.igniterOff = rows.igniter_off;
timestamps.igniterOn = rows.igniter_on;
timestamps.procStart = rows.process_start;
@ -362,25 +255,12 @@ const functions = {
gpio.setup(augerPin, gpio.DIR_OUT, (err) => {
if (err) reject(err);
if (process.env.DEBUG) console.log('== Auger pin initialized.');
this.auger.ready();
// Init the Igniter pin
gpio.setup(igniterPin, gpio.DIR_OUT, (err) => {
if (err) reject(err);
if (process.env.DEBUG) console.log('== Igniter pin initialized.');
this.igniter.ready();
// Init the Exhaust pin
gpio.setup(exhaustPin, gpio.DIR_OUT, (err) => {
if (err) reject(err);
if (process.env.DEBUG) console.log('== Exhaust pin initialized.');
this.exhaust.ready();
// Resolve the promise now that all pins have been initialized
resolve('== GPIO Initialized.');
});
});
// Resolve the promise now that all pins have been initialized
resolve('== GPIO Initialized.');
});
} else {
// Resolve the promise
resolve('== GPIO Simulated');
resolve('== GPIO Not Available');
}
});
},
@ -395,11 +275,6 @@ const functions = {
if (err) console.log('Error removing the quit file: ' + err);
config.status.shutdownNextCycle = 1;
config.status.auger = 0;
const shutdownNextCycleQuery = "UPDATE status SET value = 1 WHERE key = 'shutdown_next_cycle'";
dbfn.run(shutdownNextCycleQuery).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Shutting down next cycle.`);
}).catch(err => console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`));
resolve();
});
} else {

2692
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "hestia",
"version": "0.3.0",
"name": "pscontrolpanel",
"version": "0.2.1",
"requires": true,
"packages": {},
"dependencies": {
@ -10,6 +10,6 @@
"express": "^4.18.2",
"rpi-gpio": "^2.1.7",
"sequelize": "^6.28.0",
"sqlite3": "^5.1.6"
"sqlite3": "^5.1.4"
}
}

View File

@ -87,7 +87,7 @@ html, body {
}
table {
margin: 10;
margin: 0 auto;
color: aqua !important;
}
@ -95,12 +95,4 @@ table, th, td {
border: 1px solid;
border-collapse: collapse;
padding: 3px;
}
th, td {
width: 50%;
}
#quit {
/* visibility: hidden; */
}

View File

@ -21,28 +21,22 @@
</div>
<div id="status" class="row">
<!--
| Igniter | rows[0].cells[1]
| Exhaust | rows[1].cells[1]
| Auger | rows[2].cells[1]
| Feed Rate | rows[3].cells[1]
| Auger | rows[0].cells[1] | On Time | rows[0].cells[3] |
| Feed Rate | rows[1].cells[1] | Off Time | rows[1].cells[3] |
-->
<table id="status-table" class="table table-bordered col-sm-12 col-md-6">
<tr>
<td>Igniter</td>
<td></td>
</tr>
<tr>
<td>Exhaust</td>
<td></td>
</tr>
<tr>
<td>Auger</td>
<td></td>
<td>On Time</td>
<td></td>
</tr>
<tr>
<td>Feed Rate</td>
<td></td>
<td>Off Time</td>
<td></td>
</tr>
</table>
</div>
@ -53,27 +47,15 @@
<form action="/" method="post">
<!-- Start | Shutdown | Reload Settings -->
<div class="button-container d-flex justify-content-between">
<input class="btn btn-outline-secondary" type="submit" id="igniter-off" value="Disable Igniter" name="igniterOff">
<input class="btn btn-outline-secondary" type="submit" id="igniter-on" value="Enable Igniter" name="igniterOn">
<input class="btn btn-outline-secondary" type="submit" id="ignite" value="Enable Auger" name="start">
<input class="btn btn-outline-secondary" type="submit" id="shutdown" value="Disable Auger" name="shutdown">
</div>
<div class="button-container d-flex justify-content-between">
<input class="btn btn-outline-secondary" type="submit" id="exhaust-off" value="Disable Exhaust" name="exhaustOff">
<input class="btn btn-outline-secondary" type="submit" id="exhaust-on" value="Enable Exhaust" name="exhaustOn">
</div>
<div class="button-container d-flex justify-content-between">
<input class="btn btn-outline-secondary" type="submit" id="auger-off" value="Disable Auger" name="augerOff">
<input class="btn btn-outline-secondary" type="submit" id="auger-on" value="Enable Auger" name="augerOn">
</div>
<!-- Set feed rates -->
<div class="form-group">
<label for="feedRate">Feed Rate: </label>
<select name="feedRate" class="form-control" id="feed-rate-select">
<option value="500">Low</option>
<option value="625">Medium-Low</option>
<option value="750">Medium</option>
<option value="875">Medium-High</option>
<option value="600">Low</option>
<option value="800">Medium</option>
<option value="1000">High</option>
</select>
</div>
@ -82,12 +64,12 @@
</div>
</form>
</div>
<div class="text-center my-4">
<!-- <div class="text-center my-4">
<img src="./dancing_jesus.gif" class="img-fluid">
</div>
</div> -->
<div class="controls-container">
<form action="/" method="POST">
<input class="btn btn-danger" type="submit" id="quit" value="Quit!!" name="quit">
<input class="btn btn-danger" type="submit" id="quit" value="Quit!!" name="quit" style="visibility: hidden;">
</form>
</div>
<!-- <script src="./main.js"></script> -->
@ -131,38 +113,30 @@
// Get the elements we need to update
const statusTable = document.getElementById('status-table');
const igniterStatus = statusTable.rows[0].cells[1];
const exhaustStatus = statusTable.rows[1].cells[1];
const augerStatus = statusTable.rows[2].cells[1];
const feedRate = statusTable.rows[3].cells[1];
const augerStatus = statusTable.rows[0].cells[1];
const augerOn = statusTable.rows[0].cells[3];
const augerOff = statusTable.rows[1].cells[3];
const feedRate = statusTable.rows[1].cells[1];
const feedRateSelect = document.getElementById('feed-rate-select');
// console.log(config);
igniterStatus.innerHTML = parseStatus(config.status.igniter);
exhaustStatus.innerHTML = parseStatus(config.status.exhaust);
augerStatus.innerHTML = parseStatus(config.status.auger);
augerOn.innerHTML = config.intervals.augerOn;
augerOff.innerHTML = config.intervals.augerOff;
switch (config.intervals.augerOn) {
case '500':
case '600':
feedRate.innerHTML = 'Low';
feedRateSelect.selectedIndex = 0;
break;
case '625':
feedRate.innerHTML = 'Medium-Low';
feedRateSelect.selectedIndex = 1;
break;
case '750':
case '800':
feedRate.innerHTML = 'Medium';
feedRateSelect.selectedIndex = 2;
break;
case '875':
feedRate.innerHTML = 'Medium-High';
feedRateSelect.selectedIndex = 3;
feedRateSelect.selectedIndex = 1;
break;
case '1000':
feedRate.innerHTML = 'High';
feedRateSelect.selectedIndex = 4;
feedRateSelect.selectedIndex = 2;
break;
default:
feedRate.innerHTML = 'Unknown';