struggles
This commit is contained in:
parent
2a19a8499d
commit
096d1739ac
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}/main.js"
|
||||
}
|
||||
]
|
||||
}
|
@ -6,10 +6,13 @@
|
||||
"auger": 0,
|
||||
"igniterFinished": false,
|
||||
"seenFire": false,
|
||||
"shutdown": 0
|
||||
"shutdown": 0,
|
||||
"vacuum": 1,
|
||||
"pof": 0
|
||||
},
|
||||
"timestamps": {
|
||||
"procStart": 0,
|
||||
"blowerOn": 0,
|
||||
"blowerOff": 0,
|
||||
"igniterOn": 0,
|
||||
"igniterOff": 0
|
||||
@ -18,7 +21,7 @@
|
||||
"augerOn": 500,
|
||||
"augerOff": 1500,
|
||||
"pause": 10000,
|
||||
"igniterStart": 30000,
|
||||
"igniterStart": 10000,
|
||||
"blowerStop": 10000
|
||||
}
|
||||
}
|
236
functions.js
236
functions.js
@ -1,5 +1,6 @@
|
||||
// TODOs: Add tests for PoF and Vacuum switches, add delays for shutting down blower, test logic for igniter
|
||||
|
||||
// TODO: Move these to config
|
||||
// Physical Pin numbers for GPIO
|
||||
const augerPin = 26; // Pin for controlling the relay for the pellet auger motor.
|
||||
const igniterPin = 13; // Pin for controlling the relay for the igniter.
|
||||
@ -17,8 +18,7 @@ const config = require('./config.json');
|
||||
const dotenv = require('dotenv').config();
|
||||
// Module for working with files
|
||||
const fs = require('fs');
|
||||
// Promises I think?
|
||||
const { resolve } = require('path');
|
||||
const { time } = require('console');
|
||||
|
||||
|
||||
// The functions we'll export to be used in other files
|
||||
@ -66,21 +66,21 @@ const functions = {
|
||||
// Turn the auger on
|
||||
this.on(gpio).then((res) => {
|
||||
// Log action if in debug mode
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// Sleep for the time set in env variables
|
||||
functions.sleep(config.intervals.augerOn).then((res) => {
|
||||
// Log action if in debug mode
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// Turn the auger off
|
||||
this.off(gpio).then((res) => {
|
||||
// Log action if in debug mode
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// Sleep for the time set in env variables
|
||||
functions.sleep(config.intervals.augerOff).then((res) => {
|
||||
// Log action if in debug mode
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// Resolve the promise, letting the main script know the cycle is complete
|
||||
resolve("Cycle complete.");
|
||||
resolve("Auger cycled.");
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -89,15 +89,13 @@ const functions = {
|
||||
},
|
||||
},
|
||||
blower: {
|
||||
canShutdown() {
|
||||
// If the blowerOff timestamp hasn't been set, return false as the blower hasn't been asked to turn off yet
|
||||
if (config.timestamps.blowerOff == 0) return false;
|
||||
// If the current time is past the blowerOff timestamp, we can turn off the blower
|
||||
if (Date.now() > config.timestamps.blowerOff) {
|
||||
return true;
|
||||
// Otherwise, return false because we're not ready to
|
||||
} else {
|
||||
blocksShutdown() {
|
||||
// If the current time is past the blowerOff timestamp, we can turn finish shutting down the stove
|
||||
if ((config.timestamps.blowerOff > 0) && (Date.now() > config.timestamps.blowerOff)) {
|
||||
return false;
|
||||
// Otherwise, return true because we're not ready to shutdown yet
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -197,29 +195,23 @@ const functions = {
|
||||
process.exit();
|
||||
},
|
||||
ignite(gpio) {
|
||||
// Enable the auger
|
||||
config.status.auger = 1;
|
||||
// Set the timestamp when the igniter turned on
|
||||
config.timestamps.igniterOn = Date.now();
|
||||
// Set the timestamp for when the igniter will turn off
|
||||
config.timestamps.igniterOff = config.timestamps.igniterOn + config.intervals.igniterStart; // 7 Minutes, 420,000ms
|
||||
return new Promise((resolve, reject) => {
|
||||
// Check if we got here from a file, then delete it.
|
||||
if (fs.existsSync('./ignite')) fs.unlink('./ignite', (err) => { if (err) throw err; });
|
||||
// Run the first block if this is being run on a Raspberry Pi
|
||||
if (process.env.ONPI == 'true') {
|
||||
// Power the blower on
|
||||
functions.power.blower.on(gpio).then(res => {
|
||||
// Turn on the igniter
|
||||
functions.power.igniter.on(gpio).then(res => {
|
||||
resolve('Auger enabled, combustion blower and igniter turned on.');
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
functions.power.blower.on(gpio).then(res => {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// Turn on the igniter
|
||||
functions.power.igniter.on(gpio).then(res => {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
// Enable the auger
|
||||
config.status.auger = 1;
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Auger enabled.`);
|
||||
|
||||
resolve('Ignition sequence started successfully.');
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
resolve('Simulated igniter turned on.');
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
shutdown(gpio) {
|
||||
@ -236,75 +228,123 @@ const functions = {
|
||||
// If the igniter is on, shut it off.
|
||||
if (config.status.igniter == 1) {
|
||||
functions.power.igniter.off(gpio).then(res => {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Shut off igniter.`);
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
}); // TODO catch an error here
|
||||
}
|
||||
// TODO Change this so it gives a delay after shutting down so smoke doesn't enter the house
|
||||
if (config.status.blower == 1) {
|
||||
// Set the timestamp to turn the blower off at
|
||||
config.timestamps.blowerOff = Date.now() + config.intervals.blowerStop;
|
||||
functions.power.blower.off(gpio).then(res => {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
});
|
||||
}
|
||||
return "Shutdown has been initiated.";
|
||||
} else {
|
||||
return "A shutdown has already been initiated.";
|
||||
// blower.canShutdown() returns true only if the blower shutdown has
|
||||
// been initiated AND the specified cooldown time has passed
|
||||
if(functions.blower.blocksShutdown()) {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Blower can be turned off.`);
|
||||
fn.power.blower.off(gpio).then(res => {
|
||||
// Since the blower shutting off is the last step in the shutdown, we can quit.
|
||||
// TODO eventually we don't want to ever quit the program, so it can be restarted remotely
|
||||
fn.commands.quit();
|
||||
});
|
||||
} else {
|
||||
return "A shutdown has already been initiated and the blower is preventing shutdown.";
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
},
|
||||
tests: {
|
||||
vacuum(gpio) {
|
||||
return new Promise((resolve, reject) => {
|
||||
gpio.read(vacuumPin, (err, status) => {
|
||||
if (err) reject(err);
|
||||
resolve(status);
|
||||
});
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.read(vacuumPin, (err, status) => {
|
||||
if (err) reject(err);
|
||||
resolve(status);
|
||||
});
|
||||
} else {
|
||||
switch (config.status.vacuum) {
|
||||
case 0:
|
||||
resolve(false);
|
||||
break;
|
||||
case 1:
|
||||
resolve(true);
|
||||
break;
|
||||
default:
|
||||
reject('Unable to determine vacuum status.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
pof(gpio) {
|
||||
return new Promise((resolve, reject) => {
|
||||
gpio.read(pofPin, (err, status) => {
|
||||
if (err) reject(err);
|
||||
resolve(status);
|
||||
});
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.read(pofPin, (err, status) => {
|
||||
if (err) reject(err);
|
||||
resolve(status);
|
||||
});
|
||||
} else {
|
||||
switch (config.status.pof) {
|
||||
case 0:
|
||||
resolve(false);
|
||||
break;
|
||||
case 1:
|
||||
resolve(true);
|
||||
break;
|
||||
default:
|
||||
reject('Unable to determine proof of fire status.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
igniter(gpio) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Create a blank string to store the status message in as we build it
|
||||
var statusMsg = "";
|
||||
// Determine if the igniter is on
|
||||
if (config.status.igniter == 1) {
|
||||
statusMsg += "The igniter is on.\n";
|
||||
statusMsg += "The igniter is on. ";
|
||||
} else if (config.status.igniter == 0) {
|
||||
statusMsg += "The igniter is off.\n";
|
||||
statusMsg += "The igniter is off. ";
|
||||
} else {
|
||||
reject("E: Unable to determine igniter status.");
|
||||
reject("Unable to determine igniter status.");
|
||||
}
|
||||
// Run this if the igniter has been turned on
|
||||
if (config.timestamps.igniterOn > 0) {
|
||||
const humanStartTime = new Date(config.timestamps.igniterOn).toISOString();
|
||||
const humanEndTime = new Date(config.timestamps.igniterOff).toISOString();
|
||||
if (Date.now() < config.timestamps.igniterOff && config.status.igniter == 1) {
|
||||
statusMsg += `Igniter started: ${humanStartTime}.\n`;
|
||||
statusMsg += `Igniter scheduled to stop: ${humanEndTime}.\n`;
|
||||
statusMsg += `Started: ${functions.time(config.timestamps.igniterOn)}. `;
|
||||
statusMsg += `Stopping: ${functions.time(config.timestamps.igniterOff)}. `;
|
||||
}
|
||||
// Shut the igniter off if it's past the waiting period
|
||||
if ((Date.now() > config.timestamps.igniterOff) && (config.status.igniter == 1)) {
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.write(igniterPin, false, (err) => {
|
||||
if (err) throw(err);
|
||||
config.status.igniter = 0;
|
||||
statusMsg += `${new Date().toISOString()} I: Turned off igniter.`;
|
||||
functions.tests.pof(gpio).then(res => {
|
||||
if (res) {
|
||||
config.status.seenFire = true;
|
||||
} else {
|
||||
reject(`E: No Proof of Fire after igniter shut off.`);
|
||||
}
|
||||
}).catch(rej => {
|
||||
// if (process.env.ONPI == 'true') {
|
||||
// gpio.write(igniterPin, false, (err) => {
|
||||
// if (err) throw(err);
|
||||
// config.status.igniter = 0;
|
||||
// statusMsg += `${new Date().toISOString()} I: Turned off igniter.`;
|
||||
// functions.tests.pof(gpio).then(res => {
|
||||
// if (res) {
|
||||
// config.status.seenFire = true;
|
||||
// } else {
|
||||
// reject(`E: No Proof of Fire after igniter shut off.`);
|
||||
// }
|
||||
// }).catch(rej => {
|
||||
|
||||
});
|
||||
});
|
||||
} else {
|
||||
config.status.igniter = 0;
|
||||
statusMsg += `${new Date().toISOString()} I: Simulated igniter turned off.`;
|
||||
}
|
||||
// });
|
||||
// });
|
||||
// } else {
|
||||
// config.status.igniter = 0;
|
||||
// statusMsg += `${new Date().toISOString()} I: Simulated igniter turned off.`;
|
||||
// }
|
||||
// TODO I think this needs to be moved elsewhere, it doesn't finish resolving before the resolve call on line 354 is called (344+10=354)
|
||||
functions.power.igniter.off(gpio).then(res => {
|
||||
statusMsg += res;
|
||||
});
|
||||
} else if ((Date.now() > config.timestamps.igniterOff) && (config.status.igniter == 0)) {
|
||||
statusMsg += `The igniter was turned off at ${new Date(config.timestamps.igniterOff).toISOString()}.`;
|
||||
}
|
||||
@ -321,45 +361,66 @@ const functions = {
|
||||
power: {
|
||||
igniter: {
|
||||
on(gpio) {
|
||||
// TODO
|
||||
return new Promise((resolve, reject) => {
|
||||
gpio.write(igniterPin, true, (err) => {
|
||||
if (err) reject(err);
|
||||
config.timestamps.igniterOn = Date.now();
|
||||
config.timestamps.igniterOff = Date.now() + config.intervals.igniterStart;
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.write(igniterPin, true, (err) => {
|
||||
if (err) reject(err);
|
||||
config.status.igniter = 1;
|
||||
resolve('Igniter turned on.');
|
||||
});
|
||||
} else {
|
||||
config.status.igniter = 1;
|
||||
resolve('Igniter turned on.');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
off(gpio) {
|
||||
// TODO
|
||||
return new Promise((resolve, reject) => {
|
||||
gpio.write(igniterPin, false, (err) => {
|
||||
if (err) reject(err);
|
||||
config.timestamps.igniterOff = Date.now();
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.write(igniterPin, false, (err) => {
|
||||
if (err) reject(err);
|
||||
config.status.igniter = 0;
|
||||
resolve('Igniter turned off.');
|
||||
});
|
||||
} else {
|
||||
config.status.igniter = 0;
|
||||
resolve('Igniter turned off.');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
blower: {
|
||||
on(gpio) {
|
||||
// TODO
|
||||
return new Promise((resolve, reject) => {
|
||||
gpio.write(blowerPin, true, (err) => {
|
||||
if (err) reject(err);
|
||||
config.timestamps.blowerOn = Date.now();
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.write(blowerPin, true, (err) => {
|
||||
if (err) reject(err);
|
||||
config.status.blower = 1;
|
||||
resolve('Blower turned on.');
|
||||
});
|
||||
} else {
|
||||
config.status.blower = 1;
|
||||
resolve('Blower turned on.');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
off(gpio) {
|
||||
// TODO
|
||||
config.timestamps.blowerOff = Date.now();
|
||||
return new Promise((resolve, reject) => {
|
||||
gpio.write(blowerPin, false, (err) => {
|
||||
if (err) reject(err);
|
||||
if (process.env.ONPI == 'true') {
|
||||
gpio.write(blowerPin, false, (err) => {
|
||||
if (err) reject(err);
|
||||
config.status.blower = 0;
|
||||
resolve('Blower turned off.');
|
||||
});
|
||||
} else {
|
||||
config.status.blower = 0;
|
||||
resolve('Blower turned off.');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
@ -379,6 +440,7 @@ const functions = {
|
||||
},
|
||||
// Initializes rpi-gpio, or resolves if not on a raspberry pi
|
||||
init(gpio) {
|
||||
// TODO this boot splash needs updating
|
||||
return new Promise((resolve, reject) => {
|
||||
// Boot/About/Info
|
||||
console.log(`== Lennox Winslow PS40
|
||||
@ -435,6 +497,10 @@ const functions = {
|
||||
}
|
||||
});
|
||||
},
|
||||
time(stamp) {
|
||||
const time = new Date(stamp);
|
||||
return `${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Export the above object, functions, as a module
|
||||
|
47
main.js
47
main.js
@ -49,7 +49,7 @@ async function main(fn, gpio) {
|
||||
// Check for the existence of certain files
|
||||
fn.files.check().then((res,rej) => {
|
||||
// Log the result of the check if in debug mode
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: File Check: ${res}`);
|
||||
// if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: File Check: ${res}`);
|
||||
// Choose what to do depending on the result of the check
|
||||
switch (res) {
|
||||
case "pause":
|
||||
@ -75,16 +75,12 @@ async function main(fn, gpio) {
|
||||
case "ignite":
|
||||
// Start the ignite sequence
|
||||
fn.commands.ignite(gpio).then(res => {
|
||||
if (config.debugMode) console.log(res);
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
|
||||
statusCheck(fn, gpio);
|
||||
}).catch(rej => {
|
||||
console.error(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${rej}`);
|
||||
fn.commands.shutdown(gpio).then(res => {
|
||||
fn.commands.quit();
|
||||
}).catch(rej => {
|
||||
console.error(rej);
|
||||
fn.commands.quit();
|
||||
});
|
||||
fn.commands.shutdown(gpio);
|
||||
});
|
||||
break;
|
||||
case "start":
|
||||
@ -127,30 +123,27 @@ async function main(fn, gpio) {
|
||||
function statusCheck(fn, gpio) {
|
||||
fn.tests.igniter(gpio).then((res) => {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
|
||||
main(fn, gpio);
|
||||
});
|
||||
|
||||
// Check the vacuum switch, if the test returns true, the vacuum is sensed
|
||||
// if it returns false, we will initiate a shutdown
|
||||
fn.tests.vacuum(gpio).then(status => {
|
||||
if (!status) {
|
||||
// TODO this is messed up
|
||||
fn.tests.vacuum(gpio).then(vacStatus => {
|
||||
if (!vacStatus) {
|
||||
console.error('No vacuum detected, beginning shutdown procedure.');
|
||||
fn.commands.shutdown(gpio);
|
||||
} else {
|
||||
// Check the Proof of Fire Switch
|
||||
fn.tests.pof(gpio).then(pofStatus => {
|
||||
// If the igniter has finished running and no proof of fire is seen, shutdown the stove
|
||||
if (config.status.igniterFinished && (!pofStatus)) {
|
||||
console.error('No Proof of Fire after the igniter shut off, beginning shutdown procedure.');
|
||||
fn.commands.shutdown(gpio);
|
||||
} else {
|
||||
if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Vacuum and Proof of Fire OK.`);
|
||||
main(fn, gpio);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Check the Proof of Fire Switch
|
||||
fn.tests.pof(gpio).then(status => {
|
||||
// If the igniter has finished running and no proof of fire is seen, shutdown the stove
|
||||
if (config.status.igniterFinished && (!status)) fn.commands.shutdown(gpio);
|
||||
});
|
||||
|
||||
// blower.canShutdown() returns true only if the blower shutdown has
|
||||
// been initiated AND the specified cooldown time has passed
|
||||
if(fn.blower.canShutdown()) {
|
||||
fn.power.blower.off(gpio).then(res => {
|
||||
// Since the blower shutting off is the last step in the shutdown, we can quit.
|
||||
// TODO eventually we don't want to ever quit the program, so it can be restarted remotely
|
||||
fn.commands.quit();
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user