diff --git a/TODO.md b/TODO.md index c1e5710..3588a9e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,14 @@ # In Progress -1. Strip to bones +1. ~~Strip to bones~~ 2. Better commenting +2. Add startup and shutdown logic (start half implemented, not tested) -# Immediate To-Do +# Done +1. GPIO Interface 1. Duplicate and adapt `HestiaClasses` from branch `v2-front` +# Immediate To-Do +1. Connect to MQTT + # Roadmap -1. Add control logic for automatic start and stop -2. Add wiring and sensors for safeties \ No newline at end of file +1. Add wiring and sensors for safeties \ No newline at end of file diff --git a/src/custom_modules/HestiaClasses.js b/src/custom_modules/HestiaClasses.js index 7bb76f5..62b0a5e 100644 --- a/src/custom_modules/HestiaClasses.js +++ b/src/custom_modules/HestiaClasses.js @@ -1,29 +1,81 @@ export class State { constructor(config) { - config.mqtt.subscriptions.forEach(subscription => { - this[subscription.name] = { - on: false, - topic: subscription.topic, - publisher: 'back', - power: (communicator) => { - // This *should* toggle the state, asks if state is true, if it is set it false, otherwise set it true - this[subscription.name].on ? this[subscription.name].on = false : this[subscription.name].on = true; - communicator.send(subscription.name, JSON.stringify(this)); - } - }; - }); + this.igniter = { + on: false, + name: "igniter", + topic: config.mqtt.topics.igniter, + publisher: 'front', + power: (communicator) => { + // This *should* toggle the state, asks if state is true, if it is set it false, otherwise set it true + this.igniter.on ? this.igniter.on = false : this.igniter.on = true; + communicator.send(config.mqtt.topics.igniter, JSON.stringify(this.igniter)); + } + }; + + this.exhaust = { + on: false, + name: "exhaust", + topic: config.mqtt.topics.exhaust, + publisher: 'front', + power: (communicator) => { + // This *should* toggle the state, asks if state is true, if it is set it false, otherwise set it true + this.exhaust.on ? this.exhaust.on = false : this.exhaust.on = true; + communicator.send(config.mqtt.topics.exhaust, JSON.stringify(this.exhaust)); + } + }; + + this.auger = { + on: false, + name: "auger", + feedRate: 500, + topic: config.mqtt.topics.auger, + publisher: 'front', + power: (communicator) => { + // This *should* toggle the state, asks if state is true, if it is set it false, otherwise set it true + this.auger.on ? this.auger.on = false : this.auger.on = true; + communicator.send(config.mqtt.topics.auger, JSON.stringify(this.auger)); + } + }; + console.log(`State initialized.`) }; }; export class Communicator { constructor(state) { - // Connect to the MQTT Broker - this.client = mqtt.connect(config.mqtt.address); + this.publisher = state.publisher; + return this; + } - // Subscribe to status topics - config.mqtt.subscriptions.forEach(subscription => { - this.client.subscribe(subscription.topic); - state[subscription.name].topic = subscription.topic; + init(state, config) { + // Connect to the MQTT Broker + console.log(`Attempting MQTT connection to broker: ${config.mqtt.address}, with username: ${config.mqtt.username}`); + this.client = mqtt.connect(config.mqtt.address, { + username: config.mqtt.username, + password: config.mqtt.password + }); + const { client } = this; + + client.on('connect', () => { + console.log('Connected to MQTT broker'); + // Subscribe to status topics + config.states.elements.forEach(element => { + client.subscribe(state[element].topic, (err) => { + if (!err) { + console.log(`Subscribed to ${state[element].topic}`); + } + }); + }); + + }); + + // Handle when the Broker sends us a message + client.on('message', (topic, message) => { + const msgStr = message.toString(); + const msgJson = JSON.parse(msgStr); + console.log(`Message received on topic ${topic}: ${msgStr}`); + console.log(msgJson); + state[msgJson.name].on = msgJson.on; + window.refreshState(window.document, state); }); } @@ -32,9 +84,9 @@ export class Communicator { // Publish with retain flag set to true this.client.publish(topic, message, { retain: true }, (err) => { if (err) { - throw err; + console.error('Failed to publish message:', err); } else { - // TODO: Pass back a success message + console.log('Message published and retained on topic:', topic); } }); } diff --git a/src/custom_modules/config.json b/src/custom_modules/config.json index f04b4ff..d3108a2 100644 --- a/src/custom_modules/config.json +++ b/src/custom_modules/config.json @@ -50,5 +50,15 @@ "bcm": 25, "mode": "IN" } - ] + ], + "power": { + "start": { + "exhaustDelay": 30000, + "augerDelay": 60000, + "fireCheckDelay": 420000 + }, + "stop": { + "exhaustDelay": 600000 + } + } } \ No newline at end of file diff --git a/src/custom_modules/functions.js b/src/custom_modules/functions.js index a4cdd7d..0c50238 100644 --- a/src/custom_modules/functions.js +++ b/src/custom_modules/functions.js @@ -2,6 +2,11 @@ const dotenv = require('dotenv').config(); const debug = process.env.DEBUG === "TRUE"; const { pins } = require('./config.json'); const gpio = require('./VoidGPIO.js'); +const pinMap = new Map(); + +for (const pin of pins) { + pinMap.set(pin.key, pin); +} module.exports = { log(message) { @@ -13,14 +18,32 @@ module.exports = { return new Promise(resolve => setTimeout(resolve, ms)); }, gpio: { + setDefaults() { + return new Promise((resolve, reject) => { + let stateChanges = []; + for (const pin of pins) { + if (pin.mode === 'OUT') { + gpio.setPin(pin.board, pin.defaultState, (err) => { + if (err) reject(err); + stateChanges.push( `Set ${pin.key} pin to ${pin.defaultState}.`); + }) + } + } + resolve(stateChanges.join('\n')); + }); + }, // Boot up sanity check during debug mode - async debugInit() { + debugInit() { module.exports.log('Resetting all output pins.'); - pins.forEach(async (pin) => { + module.exports.gpio.setDefaults().then(changes => { + module.exports.log(changes); + }).catch(e => console.error(e)); + + pins.forEach(pin => { if (pin.mode === 'OUT') { gpio.setPin(pin.board, pin.defaultState, err => { if (err) throw err; - module.exports.log(`Set ${pin.key} pin to ${pin.defaultState}.`); + module.exports.log(); }); }; }); @@ -48,5 +71,45 @@ module.exports = { } }; } + }, + power: { + start: { + init() { + return new Promise((resolve, reject) => { + // Check pin states + + // Set pins to default states + module.exports.gpio.setDefaults().then(changes => { + module.exports.log(changes); + }).catch(e => console.error(e)); + // Power on igniter + gpio.setPin(pinMap.igniter.board, 1, (err) => { + if (err) reject(err); + }) + // Wait for igniter preheat + + // Start exhaust + + // Finish igniter preheat + + // Check for vacuum + + // Start auger + + // Wait for fire + + // Check for fire + + // Power off igniter + + // Report successful ignition + }); + } + }, + stop: { + init() { + + } + } } }