Introduction: Intel Edison Hydroponics Controller

Creating a IoT enabled Hydroponics Controller using the Intel Edison during the Boston IoT Hackathon

Our Goal:

Within a 36 hour hackathon to construct a controller to use with our previous instructable (Vertical Hydroponic Farm). The controller will leverage the sensors found in the Grove Starter Kit Plus - Intel® IoT Edition, some additional sensors we provided (pH,ec) and an Envioronmental Kit Intel had on site for the event.

Step 1: Sensors

What's Inside the Grove Starter Kit Plus - Intel® IoT Edition:

Enviornmental kit - provided by Intel Roadshow

Additional Sensors/Parts

  • RoboMesh analog pH sensor
  • Conductivity Sensor (EC)
  • 12V DC Peristaltic Dosing Pump

Step 2: The Plan

We wanted to implement the base functionality to add automation to our previous instructable (Vertical Hydroponic Farm) which would include;

  • water cycles
    • by timers
  • light
    • by timers
    • by measurement of natural light and augmenting to maximize throughput
    • by fetching current observed weather from weatherunderground.com and augmenting
  • temperature
    • by measurement and corrective action via a fan and louver
  • nutrition
    • by measurement and corrective action via nutrient dosing pump
  • water quality (pH)
    • by measurement and corrective action via nutrient dosing pump
  • IoT Integration
    • data collection
      • all measurements and actions recorded to the cloud for analytics
    • alerting
      • all measurement data is available to create alerting rules from the cloud
    • corrective actions
      • all measurement data is available to create rules from device specific corrective actions the cloud

Step 3: The Build

We utilized Intel's XDK IoT Edition. It allows you to write your code in JavaScript using Node.js libraries.

The software can be found at Intel XDK IoT Edition, there are many documents regarding how to get started with the IDE.

Coding:

Timers

function setSecondsTimer(waitTime)<br>{<br>    var endTime;<br>    var d = new Date();<br><br>   endTime = d.getTime() + (waitTime * 1000);  // convert back to milliseconds from seconds<br>   return endTime;<br>  <br>} // setSecondsTimer<br><br>function setMinutesTimer(waitTime)<br>{<br>    var endTime = 0;<br>    var d = new Date();<br><br>    endTime = d.getTime() + (waitTime * 60000);  // convert back to milliseconds from minutes<br>    return endTime;<br>  <br>} // setMinutesTimer<br><br>function checkTimer(timer) {<br>    var d = new Date();<br>//    console.log('current time ' + d.getTime() + ' timer =' + timer);<br>    if (d.getTime() > timer) {return true;} <br>    else {return false;}<br><br>} // checkTimer<br><br><br>// Main Routine<br>function periodicActivity()<br>{<br>    if (checkTimer(weatherTimer)) {getWeather(); weatherTimer = setMinutesTimer(240);} // every 4 hours<br>    growLights();<br>    tempControl();<br>    checkECDoser();<br>    waterEC();<br>    if (checkTimer(waterLeverTimer)) {waterCirculation();  waterLeverTimer = setSecondsTimer(1);}<br>    if (checkTimer(lcdDisplayTimer)) {lcdDisplay(); lcdDisplayTimer = setSecondsTimer(1);}<br>    if (checkTimer(logTimer)) {sendToCloud(); logTimer = setMinutesTimer(1);}<br>    setTimeout(periodicActivity,500); //call the indicated function after 1 second (1000 milliseconds)<br>}<br><br><br>

Sensor reading

EC Sensor

// #EC Sensor <br><br>var ecSensor = new mraa.Aio(1);<br>var EC_reading = 0;<br>var ecDoser = new mraa.Gpio(6); <br>ecDoser.dir(mraa.DIR_OUT); //set the gpio direction to output<br>var ecDoserState = 0;<br>var ecDoserTimer = 0;<br>var ecDoserActivate = false;<br>var ecSampleTimer = 0;<br>var EC_DOSER_INTERVAL = 2; // seconds to run doser<br>var EC_SAMPLE_INTERVAL = 1;  // minutes to wait before using EC reading to determine to dose<br>var EC_LIMIT = 1500;        // dose when below this value minus EC_Band until it reaches this level<br>var EC_BAND = 200;<br>var EC_MS = 0;    // EC micro/S<br><br><br>function waterEC() //Read Water ED<br>{<br>    EC_reading = ecSensor.read();    // read the analog input voltage<br>    // 0 to 1024 = 0 to 5v<br>    //  204 micro/S per volt<br>    EC_MS = (EC_reading * 4.88).toFixed();    //   5000 micro/S / 1024 = 4.88<br>    if (EC_MS < (EC_LIMIT - EC_BAND)) {         // outside of acceptible range<br>        if (checkTimer(ecSampleTimer)) {        // time to re-dose<br>            ecSampleTimer = 0;<br> //           console.log('--ecDoserState ' + ecDoserState);<br>            if (ecDoserState != 1) {     // if doser not ON then turn it ON<br>                ecDoserActivate = true;  <br>                ecSampleTimer = setMinutesTimer(EC_SAMPLE_INTERVAL);  // reset time until next sample - dose<br>                console.log('dose for EC');<br>            }<br>        }<br>//        console.log('lower than ' + (EC_LIMIT - EC_BAND));<br>    }<br>//    console.log('ecSampleTimer ' + ecSampleTimer);<br>//    console.log('EC = ' + EC_MS);<br><br>} // waterEC()<br><br>function checkECDoser()<br>{  <br>    if (ecDoserState == 0 && ecDoserActivate == true)  // if not already on and needs to be on<br>    {<br>        ecDoserTimer = setSecondsTimer(EC_DOSER_INTERVAL);  <br>        ecDoserState = 1;<br>        ecDoser.write(ecDoserState);  <br>        console.log('EC Doser is ' + ecDoserState + ' for ' + EC_DOSER_INTERVAL + ' seconds');<br>    }<br>    if (checkTimer(ecDoserTimer)) {          // timer went off<br>        ecDoserActivate = false;<br>        ecDoserState = 0;                            // turn doser off    <br>        ecDoser.write(ecDoserState);  <br>//        console.log('EC Doser Timer Fired');<br>    }<br>//    console.log('ecDoserTimer ' + ecDoserTimer);<br>} // checkECDoser<br>

Temperature

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

Water Level Sensor

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

Light Level Sensor

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

Controls and Control Logic

Circulation Pump

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

Servo Controller (for louvers)

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

Fan Controller

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

Temperature Logic

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

Grow Lights

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

LCD Output

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

Publishing to the cloud

// Define your sensors for cloud publish<br>var data = [{<br>    sensorName : "light",<br>    sensorType: "light.v1.0"<br>},{<br>    sensorName : "temperature",     // air temp<br>    sensorType: "temperature.v1.0"<br>}];<br><br>//Run Once to Init/Register sensors<br>data.forEach(function(item) {<br>    registerNewSensor(item.sensorName, item.sensorType, function () {  <br>    });<br>});<br><br><br><br>function registerNewSensor(name, type, callback){<br>    var msg = JSON.stringify({<br>        n: name,<br>        t: type<br>    });<br><br>    var sentMsg = new Buffer(msg);<br>    console.log("Registering sensor: " + sentMsg);<br>    client.send(sentMsg, 0, sentMsg.length, options.port, options.host, callback);<br>};<br><br><br>function sendObservation(name, value, on){<br>    var msg = JSON.stringify({<br>        n: name,<br>        v: value,<br>        on: on<br>    });<br><br>    var sentMsg = new Buffer(msg);<br>    console.log("Sending observation: " + sentMsg);<br>    client.send(sentMsg, 0, sentMsg.length, options.port, options.host);<br>    <br>};<br><br><br>function sendToCloud(){<br>    sendObservation("light", lightLevel, new Date().getTime());<br>    sendObservation("temperature", tempValue, new Date().getTime());<br>} //sendToCloud<br><br>

Fetching weather observations

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

Step 4: Intel IoT Analytics Dashboard

Using Intel's IoT Analytics Dashboard

There are some great tutorials on getting set up on the dashboard here on instructables. I found Intel-IoT-Analytics-Dashboard by 10DotMatrix very helpful. Follow the steps outined there to get your account set up.

Once you get your account set up as described in the instructable by 10DotMatrix you can define the rules for notification and controls.

Building Rules in Intel's IoT Analytics Dashboard

  1. Go to the "Rules" menu item in the left hand nav
  2. My Rules Page
    1. Click the "Add a Rule" button
  3. My Rules - Detail Page
    1. Give your rule a name, select a priority and pick the type of action to take:
      • Email - sends an email
      • HTTP Endpoint - Call a webservice of your choice
      • Actuation - This will integrate with your Edison to trigger an action you can code to
  4. My Rules - Devices Page
    1. Pick the device/devices you want to monitor
  5. My Rules - Conditions Page
    1. Monitor Measured - Select the control from the list of registered controls that you want to construct your rule against.
    2. Trigger When - Pick your trigger type
    3. Pick your operation type: equal to, greater than, etc...
    4. Enter your test value

Step 5: The Results

We are happy to announce we won 3rd place in the Intel IoT Hackathon - Boston!!!

A fun event and it was amazing to see all the great projects that came out of the event.

Explore Science Contest

Participated in the
Explore Science Contest

Automation Contest

Participated in the
Automation Contest