-
-
Save rdeprey/6395b808c9b72213d8a3f298a63efaca to your computer and use it in GitHub Desktop.
{ | |
"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" | |
} | |
} |
// 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(); |
Tried my hand at this and keep getting the following error:
node:internal/fs/utils:879
throw new ERR_INVALID_ARG_TYPE(
^
TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received type number (17)
at Object.writeFileSync (node:fs:2146:5)
at exportGpio (/home/pi/irrigation/node_modules/onoff/onoff.js:18:8)
at new Gpio (/home/pi/irrigation/node_modules/onoff/onoff.js:172:36)
at Object. (/home/pi/irrigation/water-plant-script.js:8:19)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
at node:internal/main/run_main_module:17:47 {
code: 'ERR_INVALID_ARG_TYPE'
Unsure what I'm doing wrong. I am using a digital moisture sensor connected to the Pi GPIO pins themselves(21 and 23), rather than a breadboard. Otherwise, I followed everything in your guide. Can't figure out what variable I'm missing or where to start looking for the thrown error. Would be grateful for any help!
Hi @LowCulture, I can try to help you out. What does your code look like from lines 1-8? It looks like the error is triggered on line 8 specifically based on the error stack trace.
That's the thing. I copied your code verbatim and went through everything you did on your medium post. Really scratching my head with this one
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 and onoff
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 using onoff
version ^6.0.0
and Node 16.9.1 and that worked for me. Would you mind updating onoff
to at least version 6.0.0
and seeing if it works for you?
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.
Hey @Tossawon, I'm sorry for the slow reply. I just saw your message. My sensor doesn't light up, but it still reads the moisture level of the soil.
I'm thinking the error means one of two things:
SPI needs to be enabled on your Raspberry Pi. You can use these instructions to do that: https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-spi.
It might be connected to a different pin. If you use a pin other than the CH5 pin for the MCP3008, then you'll need to update line 30 so that it has the correct value, like this: