From 096d1739ac99f8e786dc7a09869a031f9a681025 Mon Sep 17 00:00:00 2001 From: Skylar Grant Date: Mon, 19 Dec 2022 21:25:17 -0500 Subject: [PATCH 1/2] struggles --- .DS_Store | Bin 0 -> 6148 bytes .vscode/launch.json | 17 ++++ config.json | 7 +- functions.js | 236 ++++++++++++++++++++++++++++---------------- main.js | 47 ++++----- 5 files changed, 193 insertions(+), 114 deletions(-) create mode 100644 .DS_Store create mode 100644 .vscode/launch.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dd0e71df3ec55db8163875938bb14924f58a621d GIT binary patch literal 6148 zcmeHK-AcnS6i(c98AIrW!Y%{e4&2-n#G6v*3s}($mD$pv#oCOuvlnB~YkeV~#OLvx zBn5}P7IEi{XP1-b`+seWa^{CN5EjJlA4r5Jy43E(fHG NfF^`GV&E4T_yQabNxc97 literal 0 HcmV?d00001 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c52eeff --- /dev/null +++ b/.vscode/launch.json @@ -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": [ + "/**" + ], + "program": "${workspaceFolder}/main.js" + } + ] +} \ No newline at end of file diff --git a/config.json b/config.json index 22467b4..78483ef 100644 --- a/config.json +++ b/config.json @@ -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 } } \ No newline at end of file diff --git a/functions.js b/functions.js index 293ba60..df7f9f9 100644 --- a/functions.js +++ b/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 diff --git a/main.js b/main.js index ad71f8f..b24d0da 100644 --- a/main.js +++ b/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(); - }); - } } \ No newline at end of file From 3e0a78b905b1910fc7a80461d383640f2b3720c9 Mon Sep 17 00:00:00 2001 From: Skylar Grant Date: Tue, 20 Dec 2022 15:04:37 -0500 Subject: [PATCH 2/2] Safeties, Startup, Shutdown --- config.json | 6 +++--- functions.js | 13 ++++++------- main.js | 22 ++++++++++++++++------ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/config.json b/config.json index 78483ef..fdb8676 100644 --- a/config.json +++ b/config.json @@ -20,8 +20,8 @@ "intervals": { "augerOn": 500, "augerOff": 1500, - "pause": 10000, - "igniterStart": 10000, - "blowerStop": 10000 + "pause": 3000, + "igniterStart": 30000, + "blowerStop": 30000 } } \ No newline at end of file diff --git a/functions.js b/functions.js index df7f9f9..cce97ae 100644 --- a/functions.js +++ b/functions.js @@ -224,6 +224,7 @@ const functions = { // If the auger is enabled, disable it if (config.status.auger == 1) { config.status.auger = 0; + if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Auger disabled.`); } // If the igniter is on, shut it off. if (config.status.igniter == 1) { @@ -235,20 +236,17 @@ const functions = { 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 { - // blower.canShutdown() returns true only if the blower shutdown has + // blower.blocksShutdown() returns false only if the blower shutdown has // been initiated AND the specified cooldown time has passed - if(functions.blower.blocksShutdown()) { + 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 => { + functions.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(); + functions.commands.quit(); }); } else { return "A shutdown has already been initiated and the blower is preventing shutdown."; @@ -379,6 +377,7 @@ const functions = { off(gpio) { return new Promise((resolve, reject) => { config.timestamps.igniterOff = Date.now(); + config.status.igniterFinished = true; if (process.env.ONPI == 'true') { gpio.write(igniterPin, false, (err) => { if (err) reject(err); diff --git a/main.js b/main.js index b24d0da..7f3cf25 100644 --- a/main.js +++ b/main.js @@ -70,7 +70,8 @@ async function main(fn, gpio) { break; case "quit": // Quit the script - fn.commands.shutdown(gpio); + console.log(fn.commands.shutdown(gpio)); + statusCheck(fn, gpio); break; case "ignite": // Start the ignite sequence @@ -121,9 +122,16 @@ 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}`); - }); + if (config.status.shutdown == 1) { + console.log(fn.commands.shutdown(gpio) || 'Shutting down...'); + main(fn, gpio); + return; + } + if (config.status.igniter == 1) { + fn.tests.igniter(gpio).then((res) => { + if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`); + }); + } // Check the vacuum switch, if the test returns true, the vacuum is sensed // if it returns false, we will initiate a shutdown @@ -131,14 +139,16 @@ function statusCheck(fn, gpio) { fn.tests.vacuum(gpio).then(vacStatus => { if (!vacStatus) { console.error('No vacuum detected, beginning shutdown procedure.'); - fn.commands.shutdown(gpio); + console.log(fn.commands.shutdown(gpio)); + main(fn, 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); + console.log(fn.commands.shutdown(gpio)); + main(fn, gpio); } else { if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Vacuum and Proof of Fire OK.`); main(fn, gpio);