Last active
November 9, 2021 16:36
-
-
Save rdeprey/6395b808c9b72213d8a3f298a63efaca to your computer and use it in GitHub Desktop.
Automatic Plant Waterer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "plant-waterer", | |
"version": "0.0.1", | |
"private": true, | |
"scripts": { | |
"start": "node ./bin/www" | |
}, | |
"dependencies": { | |
"node-schedule": "^1.3.2", | |
"onoff": "^6.0.0", | |
"mcp-spi-adc": "^3.1.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Make sure that you're using Node v16.x and up | |
// Setup the sensor input | |
const mcpadc = require('mcp-spi-adc'); | |
const Gpio = require('onoff').Gpio; | |
const schedule = require('node-schedule'); | |
const completelyWet = 395; | |
const completelyDry = 780; | |
const pumpRelay = new Gpio(17, 'high'); // IMPORTANT: Use 'high' if relay uses low level trigger | |
function getSensorReadings(sensor) { | |
return new Promise((resolve, reject) => { | |
sensor.read((readError, reading) => { | |
if (readError) { | |
return reject(new Error(`There was an error getting the sensor reading: | |
${readError}`)); | |
} | |
return resolve(reading); | |
}); | |
}); | |
}; | |
function getMoistureLevel() { | |
const readingPromises = []; | |
let readings = {}; | |
readings.rawValues = []; | |
readings.values = []; | |
return new Promise((resolve, reject) => { | |
const sensor = mcpadc.open(5, {speedHz: 20000}, (error) => { | |
if (error) { | |
return reject(new Error(`There was an error accessing the sensor: ${error}`)); | |
} | |
let iterator = 50; // Just need a large number of readings to try for better accuracy | |
while (iterator >= 0) { | |
readingPromises.push(getSensorReadings(sensor) | |
.then(reading => { | |
readings.rawValues.push(reading.rawValue); | |
readings.values.push(reading.value); | |
}).catch(error => { | |
return reject(error); | |
}) | |
); | |
iterator--; | |
} | |
return Promise.all(readingPromises).then(() => { | |
const averageRawValue = readings.rawValues.reduce((a, b) => a + b, 0) / 50; | |
const averageValue = readings.values.reduce((a, b) => a + b, 0) / 50; | |
// Set the value to a percentage based on the max reading | |
return resolve({ | |
rawValue: averageRawValue, | |
value: averageValue, | |
soilDrynessPercentage: averageRawValue > 0 ? ((averageRawValue / completelyWet) * 100).toFixed(0) : 0, | |
}); | |
}); | |
}); | |
}); | |
}; | |
function shouldWater(moistureLevel) { | |
// Adjust this value based on your sensor and the needs of your plant | |
// Value represents a percentage | |
if (moistureLevel <= 45) { | |
return true; | |
} | |
return false; | |
}; | |
function waterThePlant() { | |
return new Promise((resolve, reject) => { | |
pumpRelay.read(async (error, status) => { | |
if (error) { | |
return reject(new Error(`There was an error getting the pump relay status: ${error}`)); | |
} | |
const moistureLevel = await getMoistureLevel(); | |
const needsWater = shouldWater(moistureLevel.soilDrynessPercentage); | |
if (status !== 0 && needsWater) { | |
pumpRelay.writeSync(0); // closes the circuit and starts the pump | |
} | |
return resolve({ | |
status: `The plant is being watered.`, | |
}); | |
}); | |
}); | |
}; | |
function stopWateringPlant() { | |
return new Promise((resolve, reject) => { | |
pumpRelay.read((error, status) => { | |
if (error) { | |
return reject(new Error(`There was an error getting the pump relay status: ${error}`)); | |
} | |
if (status !== 1) { | |
pumpRelay.writeSync(1); // opens the circuit and stops the pump | |
} | |
return resolve({ | |
status: `The plant is not being watered.`, | |
}); | |
}); | |
}); | |
}; | |
const shouldWaterPlant = () => { | |
// Run every day at 7 a.m. | |
return schedule.scheduleJob('0 7 * * *', async () => { | |
if (shouldWater) { | |
// Water the plant for three seconds | |
setTimeout(() => { | |
waterThePlant(); | |
setTimeout(() => { | |
stopWateringPlant(); | |
}, 3000); | |
}, 3000); | |
} | |
}); | |
}; | |
shouldWaterPlant(); |
That did it, thank you for your help with this!
I'm glad it's working now. :) Thanks for bringing this up - I'm going to update the post for the latest Node so that others don't run into this.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @LowCulture, I think that I found the issue. I think it's related to newer versions of Node and the
onoff
package. I didn't run into this with Node 13.x, but I could replicate it with Node 16.x andonoff
version^5.0.0
.I came across this exact error reported to the
onoff
package here, fivdi/onoff#170, and they mentioned that they fixed it in version 6. I tested usingonoff
version^6.0.0
and Node 16.9.1 and that worked for me. Would you mind updatingonoff
to at least version6.0.0
and seeing if it works for you?