Overview

Skill Level: Intermediate

Do you know that feeling of your plants drying out only to be subsequently overwatered ? well not anymore!
This recipe explains how to monitor the soil humidity in your greenhouse or pots using a Raspberry Pi and the IBM Watson IoT Platform.

Ingredients

Hardware:

Raspberry Pi

Arduino Moist sensor

3 female-female pin-jumper cables

1 glas of water

Software:

Raspberry Pi (I used Pi 3) with Python installed, VNC,  and the IoT API (https://pypi.python.org/pypi/ibmiotf -(sudo pip install ibmiotf))

Or see the general Pi IoT configuration at https://www.ibm.com/internet-of-things/partners/raspberry-pi/

 

Login on IBM Bluemix (free at www.bluemix.net)

Step-by-step

  1. Connect cables and test the hardware

    In one end connect the 2 jump cables that came with the moist sensor from the component to the soil sensor plate (It does not matter which pin cables are connected to).

    1-arduino

     

    Then connect the 3 jump cables from the sensor D0 to the Pi Pin 11(this is the GPIO Input Output we will check on, GND to the Pis pin 9 (this is ground on the Pi check the schema later in this section and then finally connect vcc+ to the Pis pin 1 (3v). So now we have power, ground and an i/o connected. Now if we turn on the Pi we should see the PWR-LED little green light turn on (see image above).

     

    2-pi-connect

     

    Now if you drop the sensor plate tip into water you should get a connection and the second light (DO-LED)on the sensor should go on.

    3-arduino-on

    This means that now a signal is sent to our data pin on the Pi.

    Here is an overview of the pins where we are using pin 1,9 and 11

    4-pinoverview

     

     

     

  2. Prepare the script on the Pi

    Now we will prepare to read the moist level by reading the pin the moist sensor is connected to and them to send the value to the IBM IoT Platform

     

    Copy the following script to your Pi using for instance WinSCP or create a new .py file with the content:

     

    import time

    import ibmiotf.gateway

    import RPi.GPIO as GPIO # This is the GPIO library needed to read GPIO pins on the Raspberry Pi



    print "starting program"

    # Set GPIO numbering to BCM other modes are BOARD where you pick the pin by number instead of the name

    GPIO.setmode(GPIO.BCM)



    # Define the GPIO pin (pin 11=channel 17) which is where the arduino moist sensor output is connected

    # www.panu.it/raspberry/

    channel = 17

    # Set the GPIO pin to an input

    GPIO.setup(channel, GPIO.IN)

    print "pin setup done reading from pin 11"



    #here you need to add your personal gateway options

    gatewayOptions = {"org": "90xyzz", "type": "myraspberryGW", "id": "myPIGW", "auth-method": "token", "auth-token": "myT0ken"}

    print "ibmiot.gateway client with gateway options: " ,gatewayOptions

    gatewayCli = ibmiotf.gateway.Client(gatewayOptions)



    #as killing the script will leave the connection hanging its a good idea to

    #disconnect any old connections or you might risk an exception upon connection for security reasons

    print "disconnecting existing connections if any"

    try:

    gatewayCli.disconnect()

    print "disconnected existing connection"

    except ibmiotf.ConnectionException as e:

    print "Exception on disconnecting existing connections..all good"

    print(e)



    try:



    print "now connecting to iot platform..."

    gatewayCli.connect()

    print "connection successful"

    #starting endless while loop to keep the script running and sending sensor data)

    print "starting service (endless loop)"

    while True:

    moistData=""



    if GPIO.input(channel):

    print "fresh reading says dry"

    moistData = {"d" : {"moist":("dry")}}



    else:

    print "fresh reading says wet"

    moistData = {"d" : {"moist":("wet")}}



    print "sending message to IoT Platform"

    #here you need to add your own device data

    gatewayCli.publishDeviceEvent("somePitype", "somePI", "apiMoistEvent", "json", moistData, qos=1 )



    time.sleep(2)



    except ibmiotf.ConnectionException as e:

    print "Exception on connection.."

    print(e)

     

    You can not successfully run the script yet as we first need to prepare the target of the messages which will happen later.

     

    The Script explained:

    First we import the IBM IoT library to give us access to connect and publish messages on the IoT platform.

    Then we import the needed GPIO library. This allows us to read the pins on the Pi.

     

    If connection is successful we enter while loop that never ends where we read the value on the pin every 2 seconds(the wait statement that also works as a safeguard from sucking the life out of the Pi’s CPU).

    Another way to do this could be to subscribe to changes on the pin and write a callback method to handle the change so you would send a message only when the value changes. This would greatly reduce the overhead of traffic.  I choose this solution though for 2 reasons:

    1)     when the value changes there are times when the sensor is in water where it quickly changes from on/off and this results in wrong readings on subscription. So sometimes I would get a result that the channel is off even though it is in fact on and vice versa.

    2)     For debugging purposes on the IoT platform in node Red its really nice to get a constant flow of messages.

     

     

  3. Create an IoT Platform starter App to receive the messages

    In Bluemix (www.bluemix.net) after signing in click the Catalog in the top right corner.

    Search for Internet of Things and you should see the “Internet of Things Starter Platform” under the boilerplates section.

    5-boilerplate

     

    Click it and create a new app with connected services. Notice the unique App Name you give it will also be the url where you access the application:

    6-iotapp

     

    Notice also you get 2 services connected in this boilerplate. Iot platform service and a Cloudant NoSQL DB (where the Node Red stores its data).

    The plans say something about the amount of data can be sent through the IoT platfomr each month and how much data can be stored in cloudant. For our purpose the Lite and free plan is just fine.

     

  4. Define gateway and devices

    Now we will define the gateways and devices that are allowed to send messages to our IoT platform service.

    These device values must match the values in the script running on the Pi (We will update them in step 6).

    From the Apps overview click the Iot Starter Platform App Name (not the Route).

    7-appclick

     

    Click your IoTf Service

    8clickservice

     

    From the service page click “Launch”

    9-click-launch

     

    Now in the left hand side menu click “Devices”

     

    To connect to your platform service we need to note down some information.

    First of all you should write down the organization id from the top right corner:

    10-orgid

    Now we will create 2 device types. Click  the Device Types Tab and and add:

    raspberryGW (type gateway) leave the fields blank.

    raspberryPi (type device) again leave the fields blank.

    Theoretically we could also have called the device type for ArduinoMoistSensor – its just a matter of names and in our case its really the pi sending the data eventhough or sensing is coming from the arduino sensor. Whatever you pick – make sure it you take note of it.

    11-devicetypes

    Now click back to the “Browse” tab and add 2 devices forinstance myRaspberryGW and myRaspberryPi and ofcourse using the 2 types you just created.

    Make sure you use the same token for your the devices for your ease and WRITE IT DOWN. It can not be accessed later.

     

    The gateway and device will be used both for connecting and sending messages from the Python script on the Pi and for picking which messages to listen for on the Node Red flow on the iot platform.

  5. Add Weather service

    Now we will add a weather service to our app so we can add a local weather forecast to our warnings (Pretty cool huh!).

    Navigate to the overview of you Platform Starter Application (from where you can see the instances and mem allocation) -for instance click Apps in top left corner and subsequent click Dashboard in the submenu and from the apps overview click your app (again not the route).

    In the connections section click “Connect New”

    Search for “weather” and add the Weather company service:

    12-weather

     

    This is needed for the weather node we use later in Node Red. Click create. Press “Restage” when prompted to restage you application.

    Now click the “view Credentials” on the new service:

    13-weathercredentials

    Copy the entire credentials data to a separate file on your machine (this is for backup purposes as in writing moment there seems to be a bug that sometimes deletes the credentials of the free service).

    When your app is restaged it might not restart properly right away – in case this happens(if the app is still not started 5 minutes after the restage) restart the app from the app dashboard.

     

     

     

  6. Go-to node red editor and add the nodes

    Now we will add the logic that will handle the messages from the Pie. This we will do in Node Red.

    From your app in bluemix click the Route your-iot-platform-starter.bluemix.net (to find your app navigate from the Menu Apps and then Dashboard)

    This takes you to your app from where you can launch the node-red flow editor. Click the big red button:

    14-nodered

    Copy the following piece to your clipboard(ctrl-c):

     

    [{"id":"cd8f8b52.214eb8","type":"ibmiot in","z":"9ccb5ee4.3b9e5","authentication":"boundService","apiKey":"","inputType":"evt","deviceId":"senkimsPI","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"IBM IoT App In","service":"registered","allDevices":false,"allApplications":false,"allDeviceTypes":true,"allEvents":true,"allCommands":false,"allFormats":false,"qos":"0","x":85.19999694824219,"y":97.19999694824219,"wires":[["9f3016d9.c21068","1a1b2030.539848"]]},{"id":"9f3016d9.c21068","type":"function","z":"9ccb5ee4.3b9e5","name":"moist","func":"return {payload:msg.payload.d.moist};","outputs":1,"noerr":0,"x":313.20001220703125,"y":95.19999694824219,"wires":[["6c1b8164.2d48c","5d1e9ade.55ee54"]]},{"id":"6c1b8164.2d48c","type":"switch","z":"9ccb5ee4.3b9e5","name":"display warning","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"dry","vt":"str"},{"t":"eq","v":"wet","vt":"str"}],"checkall":"true","outputs":2,"x":528.2000122070312,"y":97.19999694824219,"wires":[["d6368106.ecdf4"],[]]},{"id":"1a1b2030.539848","type":"debug","z":"9ccb5ee4.3b9e5","name":"device data","active":true,"console":"false","complete":"true","x":213.1999969482422,"y":166.1999969482422,"wires":[]},{"id":"5d1e9ade.55ee54","type":"debug","z":"9ccb5ee4.3b9e5","name":"","active":true,"console":"false","complete":"false","x":421.5,"y":166.40000915527344,"wires":[]},{"id":"8ae101ec.df9fc8","type":"function","z":"9ccb5ee4.3b9e5","name":"drought message","func":"msg.payload=\"Warning - dry soil detected! \" + \"Todays (\"+msg.payload.dow+\"s) weather forecast: \"+msg.payload.narrative;\nreturn msg;","outputs":1,"noerr":0,"x":859.5000610351562,"y":497.20001220703125,"wires":[["788627c7.0c4d2","e3e80caf.68b378"]]},{"id":"27f1b4fb.7744bc","type":"e-mail","z":"9ccb5ee4.3b9e5","server":"smtp.gmail.com","port":"465","secure":true,"name":"soederhamn@gmail.com","dname":"Send email","x":968.2000122070312,"y":222.1999969482422,"wires":[]},{"id":"e3e80caf.68b378","type":"rbe","z":"9ccb5ee4.3b9e5","name":"","func":"rbe","gap":"","start":"","inout":"out","x":808.5000610351562,"y":221.6000213623047,"wires":[["5b6ef2d8.8ff794","27f1b4fb.7744bc"]]},{"id":"5a281b07.603f54","type":"weather_insights","z":"9ccb5ee4.3b9e5","name":"weather forecast","host":"","service":"/forecast/daily/10day.json","geocode":"","units":"m","language":"","x":378.5,"y":499,"wires":[["cd7be15c.3a4b88","7747a924.a468e"]]},{"id":"cd7be15c.3a4b88","type":"debug","z":"9ccb5ee4.3b9e5","name":"","active":false,"console":"false","complete":"false","x":494.5,"y":568.4000244140625,"wires":[]},{"id":"d6368106.ecdf4","type":"function","z":"9ccb5ee4.3b9e5","name":"weather request location","func":"msg.payload =\"55.76,12.48\"\nreturn msg;","outputs":1,"noerr":0,"x":202,"y":381,"wires":[["ba987543.4f55a"]]},{"id":"7747a924.a468e","type":"function","z":"9ccb5ee4.3b9e5","name":"get todays forecast","func":"msg.payload=msg.forecasts[0];\nreturn msg;","outputs":1,"noerr":0,"x":612.5,"y":498,"wires":[["8ae101ec.df9fc8","83665ab7.6559a8"]]},{"id":"788627c7.0c4d2","type":"debug","z":"9ccb5ee4.3b9e5","name":"","active":true,"console":"false","complete":"false","x":943.5000610351562,"y":564.800048828125,"wires":[]},{"id":"5b6ef2d8.8ff794","type":"debug","z":"9ccb5ee4.3b9e5","name":"","active":true,"console":"false","complete":"false","x":916.5,"y":300.800048828125,"wires":[]},{"id":"83665ab7.6559a8","type":"debug","z":"9ccb5ee4.3b9e5","name":"","active":false,"console":"false","complete":"false","x":754.5,"y":568,"wires":[]},{"id":"ba987543.4f55a","type":"delay","z":"9ccb5ee4.3b9e5","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"10","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":157.49998474121094,"y":498,"wires":[["5a281b07.603f54"]]}]

    Now in node red we will import these nodes from your clipboard. Click the menu and choose Import  from Clipboard

     

    15-import

     

    Now you should have something looking very much like this:

     

    16-noderedflow

  7. Modify the nodes in Node Red

    Modify the IBM IoT App In node (double click it) and here you must input your device id from step 4 or simply choose to subscribe to all devices.

    This is where we pick up the messages form the service to act upon them in Node Red.

     

    17-iot-in

    The next steps extracts the moist data from the JSON message received from the Pi and decides if a warning is needed. If thats the cease we forward a new message with Request Location using GPS coordinates to the Weather Applicaiton to get current forecasts for that area. You can find your own coordinates using the url of Google Maps.

    We then extract todays weather forecast from the results and build a message to send. And only if that message is differnt from the last one we will send it.

    To be able to send the message you must now modify Send Email node to match your email providers smtp and your user id/password (notice if you use gmail it will block your login for about 5 minutes then send you a notification to your email where you can unblock).

     

  8. Modify the python script on your Pi

    Now we will modify the python script on the Pi to match the device and gateway allowed to connect we created in step 4.

     

    You need to modify 2 lines. First the gateway options:

    gatewayOptions = {“org”: “90xyzz“, “type”: “raspberryGW“, “id”: “myRaspberryGW“, “auth-method”: “token”, “auth-token”: “myT0ken“}

    And then modify the

    gatewayCli.publishDeviceEvent(“raspberryPi“, “myRaspberryPI“, “apiMoistEvent”, “json”, moistData, qos=1 )

     

    Finally run the script:

    18-run-script

     

    Now nice messages should start showing up in your script (Notice you can enable and disable the nodes in Node red by clicking the little box on the debug nodes). And if you dip the moist sensor in water and extract it you should receive an email.

     

    You can follow the flow in Node Red by activating some of the debug nodes (click the little square to activate it):

    19-debug

     

    Now you can find the debug output on the right side:

    20-debug-out

Join The Discussion