Paul Langdon
Published © CC BY-NC-SA

Intel Edison IoT Hydroponic Controller

An IoT enabled Hydroponics Controller using the Intel Edison during the Boston IoT Hackathon

IntermediateWork in progress3,348
Intel Edison IoT Hydroponic Controller

Things used in this project

Story

Read more

Schematics

Wire

Code

Timers

JavaScript
function setSecondsTimer(waitTime)
{
    var endTime;
    var d = new Date();

   endTime = d.getTime() + (waitTime * 1000);  // convert back to milliseconds from seconds
   return endTime;
  
} // setSecondsTimer

function setMinutesTimer(waitTime)
{
    var endTime = 0;
    var d = new Date();

    endTime = d.getTime() + (waitTime * 60000);  // convert back to milliseconds from minutes
    return endTime;
  
} // setMinutesTimer

function checkTimer(timer) {
    var d = new Date();
//    console.log('current time ' + d.getTime() + ' timer =' + timer);
    if (d.getTime() > timer) {return true;} 
    else {return false;}

} // checkTimer


// Main Routine
function periodicActivity()
{
    if (checkTimer(weatherTimer)) {getWeather(); weatherTimer = setMinutesTimer(240);} // every 4 hours
    growLights();
    tempControl();
    checkECDoser();
    waterEC();
    if (checkTimer(waterLeverTimer)) {waterCirculation();  waterLeverTimer = setSecondsTimer(1);}
    if (checkTimer(lcdDisplayTimer)) {lcdDisplay(); lcdDisplayTimer = setSecondsTimer(1);}
    if (checkTimer(logTimer)) {sendToCloud(); logTimer = setMinutesTimer(1);}
    setTimeout(periodicActivity,500); //call the indicated function after 1 second (1000 milliseconds)
}

EC Sensor

JavaScript
// #EC Sensor 
var ecSensor = new mraa.Aio(1);
var EC_reading = 0;
var ecDoser = new mraa.Gpio(6); 
ecDoser.dir(mraa.DIR_OUT); //set the gpio direction to output
var ecDoserState = 0;
var ecDoserTimer = 0;
var ecDoserActivate = false;
var ecSampleTimer = 0;
var EC_DOSER_INTERVAL = 2; // seconds to run doser
var EC_SAMPLE_INTERVAL = 1;  // minutes to wait before using EC reading to determine to dose
var EC_LIMIT = 1500;        // dose when below this value minus EC_Band until it reaches this level
var EC_BAND = 200;
var EC_MS = 0;    // EC micro/S

function waterEC() //Read Water ED
{
 EC_reading = ecSensor.read();    // read the analog input voltage
 // 0 to 1024 = 0 to 5v
 //  204 micro/S per volt
 EC_MS = (EC_reading * 4.88).toFixed();    //   5000 micro/S / 1024 = 4.88
 if (EC_MS < (EC_LIMIT - EC_BAND)) {         // outside of acceptible range
 if (checkTimer(ecSampleTimer)) {        // time to re-dose
 ecSampleTimer = 0;
 //           console.log('--ecDoserState ' + ecDoserState);
 if (ecDoserState != 1) {     // if doser not ON then turn it ON
 ecDoserActivate = true; 
 ecSampleTimer = setMinutesTimer(EC_SAMPLE_INTERVAL);  // reset time until next sample - dose
 console.log('dose for EC');
 }
 }
//        console.log('lower than ' + (EC_LIMIT - EC_BAND));
 }
//    console.log('ecSampleTimer ' + ecSampleTimer);
//    console.log('EC = ' + EC_MS);

} // waterEC()

function checkECDoser()
{ 
 if (ecDoserState == 0 && ecDoserActivate == true)  // if not already on and needs to be on
 {
 ecDoserTimer = setSecondsTimer(EC_DOSER_INTERVAL); 
 ecDoserState = 1;
 ecDoser.write(ecDoserState); 
 console.log('EC Doser is ' + ecDoserState + ' for ' + EC_DOSER_INTERVAL + ' seconds');
 }
 if (checkTimer(ecDoserTimer)) {          // timer went off
 ecDoserActivate = false;
 ecDoserState = 0;                            // turn doser off 
 ecDoser.write(ecDoserState); 
//        console.log('EC Doser Timer Fired');
 }
//    console.log('ecDoserTimer ' + ecDoserTimer);
} // checkECDoser

Temperature Sensor

JavaScript
// Temperature
var tempSensor = new groveSensor.GroveTemp(3);
var tempBase = 23; // goal temperature
var tempTheshold = 25; // try to cool down
var tempAlarm = 27; // too hot sound alarm
var louverOpened = 160;
var louverClosed = 0;
var tempValue = 0;
function tempControl()
{
 // when temp to high; turn on fans, open louvers
 // if temp above theshold, stay on until lowered to base temp
 // turn on buzzer at alarm temp
 tempValue = tempSensor.value();
//    console.log('temp is=' + tempValue);
 if (tempValue > tempTheshold) {
 fanCoolingState = 1;
 servo.setAngle(louverOpened);
 //       console.log('temp hot');
 } else if (tempValue <= tempBase) {
 fanCoolingState = 0;
 servo.setAngle(louverClosed);
 //       console.log('temp normal');
 }
 fanCooling.write(fanCoolingState);
 
 
 if (tempValue >= tempAlarm) {
 alarmState = alarmState ? 0:1;
 alarm.write(alarmState);
 } else {
 alarm.write(0);
 alarmState = 0;
 }
 //  console.log('alarm is=' + alarmState);

} // tempControl()

Water Level Sensor

JavaScript
var waterSensor = require('jsupm_grovewater');
var waterLevel = new waterSensor.GroveWater(2);
var waterLeverTimer = 0;
var waterLevelValue = 0;

Light Level Sensor

JavaScript
var digitalLightSensor = require("jsupm_tsl2561");
var lightSensor = new digitalLightSensor.TSL2561();
var lightLevel = 0;
var lightLowCnt = 0;
var lightTimeRemaining = 960; // minutes in 16 hours
var lightDay = 1440; // minutes in 24 hour day
var lightsState = 0;

Circulation Pump

JavaScript
var circulationPump = new mraa.Gpio(4); 
circulationPump.dir(mraa.DIR_OUT); //set the gpio direction to output
var circulationPumpState = 0;
var circulationPumpTimer = 0;
var CIRCULATION_PUMP_TIME_ON = 1;  // pump time on
var CIRCULATION_PUMP_TIME_OFF = 1;  // pump time off
function waterCirculation()
{
 waterLevelValue = waterLevel.isWet();
 if (waterLevelValue == true)
 {
 if (checkTimer(circulationPumpTimer)) {
 if (circulationPumpState == 1) {
 circulationPumpState = 0;
 circulationPumpTimer = setMinutesTimer(CIRCULATION_PUMP_TIME_OFF);
 } else {
 circulationPumpState = 1;
 circulationPumpTimer = setMinutesTimer(CIRCULATION_PUMP_TIME_ON);
 }
 circulationPump.write(circulationPumpState);
 }
 } else {
 console.log('low water');
 circulationPumpTimer = 0;
 circulationPumpState = 0;
 circulationPump.write(circulationPumpState);
 }
//    console.log('pump = ' + circulationPumpState);

} // waterCirculation

Servo Controller (for louvers)

JavaScript
//Instantiate Servo module on digital port 5
var servo = new servoModule.Servo(5);
servo.setMinPulseWidth(600);
servo.setMaxPulseWidth(2200);

Fan Controller

JavaScript
var fanCooling = new mraa.Gpio(7); 
fanCooling.dir(mraa.DIR_OUT); //set the gpio direction to output
var fanCoolingState = 0;

Temperature Logic

JavaScript
function tempControl()
{
 // when temp to high; turn on fans, open louvers
 // if temp above theshold, stay on until lowered to base temp
 // turn on buzzer at alarm temp
 tempValue = tempSensor.value();
//    console.log('temp is=' + tempValue);
 if (tempValue > tempTheshold) {
 fanCoolingState = 1;
 servo.setAngle(louverOpened);
 //       console.log('temp hot');
 } else if (tempValue <= tempBase) {
 fanCoolingState = 0;
 servo.setAngle(louverClosed);
 //       console.log('temp normal');
 }
 fanCooling.write(fanCoolingState);
 
 
 if (tempValue >= tempAlarm) {
 alarmState = alarmState ? 0:1;
 alarm.write(alarmState);
 } else {
 alarm.write(0);
 alarmState = 0;
 }
 //  console.log('alarm is=' + alarmState);
} // tempControl()

Grow Lights

JavaScript
function growLights() // called every minute
{ 
 lightLevel = lightSensor.getLux();
 if (lightLevel < 100 && lightTimeRemaining > 0) {
 if (lightLowCnt < 2) {lightLowCnt++;} else {lightsState = 1; lightLowCnt = 0;}
 } else {
 lightsState = 0;
 lightLowCnt = 0;
 }
 lights.write(lightsState);
 lightTimeRemaining--; 
 lightDay--;
 if (lightDay < 1) {lightDay = 1440; lightRemaining = 640;} // new day 24 hr day with 16 hours of light
 if (lightLowCnt > 0) console.log('lightLow: ' + lightLowCnt); 
 console.log('LightLevel: ' + lightLevel);
} // end growLights

LCD Output

JavaScript
function lcdDisplay()
{
 var lums = 0;
 myLcd.clear();
 myLcd.setCursor(0,0);
 myLcd.write('Cond: ' + localWeather);
 
 myLcd.setCursor(1,0);
 myLcd.write('EC:' + EC_MS);
 
 myLcd.setCursor(1,8);
 if (lightLevel < 100) {lums = 'L';} else if (lightLevel < 130) {lums = 'M';} else {lums = 'H';}
 myLcd.write('L:' + lums);
 
 myLcd.setCursor(1,12); 
 myLcd.write('T:' + tempSensor.value());
} // lcdDisplay

Publishing to the cloud

JavaScript
// Define your sensors for cloud publish
var data = [{
 sensorName : "light",
 sensorType: "light.v1.0"
},{
 sensorName : "temperature",     // air temp
 sensorType: "temperature.v1.0"
}];
//Run Once to Init/Register sensors
data.forEach(function(item) {
 registerNewSensor(item.sensorName, item.sensorType, function () { 
 });
});

function registerNewSensor(name, type, callback){
 var msg = JSON.stringify({
 n: name,
 t: type
 });

 var sentMsg = new Buffer(msg);
 console.log("Registering sensor: " + sentMsg);
 client.send(sentMsg, 0, sentMsg.length, options.port, options.host, callback);
};

function sendObservation(name, value, on){
 var msg = JSON.stringify({
 n: name,
 v: value,
 on: on
 });

 var sentMsg = new Buffer(msg);
 console.log("Sending observation: " + sentMsg);
 client.send(sentMsg, 0, sentMsg.length, options.port, options.host);
 
};

function sendToCloud(){
 sendObservation("light", lightLevel, new Date().getTime());
 sendObservation("temperature", tempValue, new Date().getTime());
} //sendToCloud

Fetching weather observations

JavaScript
var localWeather = 'Waiting';
var url = 'http://api.wunderground.com/api/df5bd75178df2c09/conditions/q/MA/Boston.json';
var weatherTimer = 1;  // wait 1 minute before first call
function getWeather()
{
 request({
 url: url,
 json: false
 }, function (error, response, body) {
 if (!error && response.statusCode === 200) {
 var weatherReport = response.body;
 localWeather = weatherReport.substr(weatherReport.indexOf('"weather":"') + 11,10);
 }
 });
} // getWeather

Credits

Paul Langdon

Paul Langdon

49 projects • 317 followers
Working as a cloud architect for an IoT hardware company

Comments