Salesforce IoT Explorer Edition End to End Example

Last year (2017) was one of great excitement after Salesforce Finally made the Salesforce IoT Explorer Edition Available in all Developer Sandboxes.
If you haven’t seen it yet, just navigate to setup and type IoT! Voila!!!

IoT Menu
Salesforce IoT

After Salesforce released the Explorer Edition, the first thing on my mind was to get hands-on coding and create a full cycle example, AKA, get data from a Sensor, send the data to Salesforce and after that, trigger an Action with that Data.

We all know that a sensor by itself just connected to the internet in a silo is fun to create but it really doesn’t provide much value, that is why we can use any of the Salesforce Clouds to add context and intelligence to that sensor’s data.

The goal of this post/tutorial is to get you started and excited about using Salesforce IoT Cloud and bring value to your users.

In this example, as I mentioned we will send data from a temperature and Humidity Sensor to a Salesforce Platform Event, compare that data with a Salesforce Record and execute an action based on that, It will look like this:

The Sensor Data

Let’s start with the Temperature data, in the beginning, I was thinking to work with a humidity sensor so I could trigger a water pump and water a plant, but I got a little excited and went for the temperature data from the sensor, which will turn a fan to cool down the sensor!.

What you will need:

  • Raspberry Pi 2 or 3
  • Humidity & Moisture Breakout, I am using this: One
  • 10k Resistor
  • Cable Connectors

In my case, I am using a Raspberry Pi 2 since my Pi 3 is in use with a Voice Hat project, we need to get all wired up and ready for our python code, use the following diagram to complete the wiring, I normally use an Arduino or an MCP3008 to convert the Sensor data into a digital measure, but this sensor already does that for you, so there is no need.

We will be sending the Sensor data to Salesforce using Python and Simple Salesforce, for this POC we will be using also user and password flow, therefore you notice me importing the user and password from another file, this helps when performing a demo during an event.

Here is the code we will be using:

#botanical attempt with Salesforce IoT Cloud
#Rafa Hernandez
import time
from test import pwd
from test import user
from test import token
from simple_salesforce import Salesforce
from simple_salesforce import SalesforceLogin
from simple_salesforce import SFType
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)



# Import SPI library (for hardware SPI) and MCP3008 library.
import Adafruit_GPIO.SPI as SPI
#import Adafruit_MCP3008
import Adafruit_DHT
sensor = Adafruit_DHT.DHT11


pin = 4

#connect to Salesforce
#sf = Salesforce(username=user, password=pwd, security_token=token)
print('will try to connecto to salesforce')
session_id, instance = SalesforceLogin(
    username=user,
    password=pwd,
    security_token=token)
print(session_id)
print(instance)
#We create a variable to our plataform Event, note the __e
sensorTemp = SFType('sensorEventBotanic__e',session_id,instance)

# Main program loop.
oldValue = 0
while True:
    #We try to get the humidity and Temperature
    humidity, temperature = Adafruit_DHT.read_retry(11,4)
    print(temperature)
    #I dont really see a point to send the same value over and over....
    if temperature != oldValue :
        oldValue = temperature
        data = sensorTemp.create({'sensorId__c':'109504K94Temp','humedity__c':humidity,'temperature__c':temperature})
    print(data)
    print('Temp={0:0.1f}*C  Humidity={1:0.1f}%'.format(temperature, humidity))
    # Pause for half a second.
    time.sleep(2)
GPIO.cleanup()

if you notice on

sensorTemp = SFType('sensorEventBotanic__e',session_id,instance)

We are creating a reference to a Event Object in Salesforce, therefore before we test our code, lets go and make sure we have the object ready.

Salesforce IoT Configuration

Before anything, if you haven’t done so, you need to enable the Salesforce IoT by:

  • From Setup, enter IoT in the Quick Find box, then select Get Started.
  • In the Enable Salesforce IoT box, click Enable.

    In order to store data, we will need to create a Context, a context helps us specify the source of data used by our “Orchestration” (We will talk about this later), This data can be one or more platform events and optionally a Salesforce Object. Each one of your platform events must have a key field.

    Make sure you are in the lighting experience btw

    Go to Setup, Contexts and create your context,

    at the end it should look something like this:

    If you noticed my context is referring to botanics__c which is a custom object I created and this is part of the magic of Salesforce IoT, remember that we can relate multiple platform events to a Salesforce Object, in this case, a Custom Object, that keeps track of the various sensors and holds the range of the temperature to check. You could relate the Asset, Case or any Salesforce Object to add intelligence actions to the data that comes from your sensors.

    Once you are done with that, go ahead and click Add in the Platform Events section on your Context, mine looks like this:

    As you can see the API Name of our Platform Event, matches what we are using in our python code.
    At this point, we could go and run our python code and data will start flowing but in order to see it let’s go ahead and create an Orchestration.

    An Orchestration allows us to put together all the data from the sensors and create nonlinear representations of the same while attaching business rules and actions as well.

    In our case, we will read the data from the sensor and follow as the temperature rise, at some point when the temperature gets too high, we will turn a fan, using a workflow and a Apex Class.

    Let’s create the Orchestration:

        From Setup, enter IoT in the Quick Find box, then select Orchestrations.
        Click New Orchestration.
        For Name, enter [insert name here].
        For Context, select [insert name here].
        Click Create.

    My Orchestration looks like this:

    In orchestrations you can trigger actions, in this case when the temperature gets to the High-Temperature Transition, I create a Case like this:

    Once you have created a similar Orchestration, go ahead and fire up your python code and behold the magic of IoT Cloud, In my case, I manipulate the temperature by using a lighter, as the temperature rises, events flow into the orchestration.

    It was actually very exciting to get to this point, but I wanted to take things to the next level, what else can we do with this data? You could send a text message, an email, make a phone call, in my case, I decided I want to do more IoT and decided to turn on a fan and this is how I did it.

    When the Case was created, a Process builder fires and triggers a class that makes a call out to an Arduino Yun.
    An Arduino Yun is like if a Raspberry Pi and an Arduino had a baby and that baby turned 65 right away since it seems to be retired now! but regardless, It is a pretty cool device, it basically has all the cool capabilities of an Arduino and combines it with a small distribution of Linux.

    I am using the Bride Example from the Arduino Library to Manipulate the pins on the board.

    /*
      Arduino Yún Bridge example
    
      This example for the Arduino Yún shows how to use the
      Bridge library to access the digital and analog pins
      on the board through REST calls. It demonstrates how
      you can create your own API when using REST style
      calls through the browser.
    
      Possible commands created in this shetch:
    
      "/arduino/digital/13"     -> digitalRead(13)
      "/arduino/digital/13/1"   -> digitalWrite(13, HIGH)
      "/arduino/analog/2/123"   -> analogWrite(2, 123)
      "/arduino/analog/2"       -> analogRead(2)
      "/arduino/mode/13/input"  -> pinMode(13, INPUT)
      "/arduino/mode/13/output" -> pinMode(13, OUTPUT)
    
      This example code is part of the public domain
    
      http://www.arduino.cc/en/Tutorial/Bridge
    
    */
    
    #include 
    #include 
    #include 
    
    // Listen to the default port 5555, the Yún webserver
    // will forward there all the HTTP requests you send
    BridgeServer server;
    
    void setup() {
      // Bridge startup
      pinMode(13, OUTPUT);
      digitalWrite(13, LOW);
      Bridge.begin();
      digitalWrite(13, HIGH);
    
      // Listen for incoming connection only from localhost
      // (no one from the external network could connect)
      server.listenOnLocalhost();
      server.begin();
    }
    
    void loop() {
      // Get clients coming from server
      BridgeClient client = server.accept();
    
      // There is a new client?
      if (client) {
        // Process request
        process(client);
    
        // Close connection and free resources.
        client.stop();
      }
    
      delay(50); // Poll every 50ms
    }
    
    void process(BridgeClient client) {
      // read the command
      String command = client.readStringUntil('/');
    
      // is "digital" command?
      if (command == "digital") {
        digitalCommand(client);
      }
    
      // is "analog" command?
      if (command == "analog") {
        analogCommand(client);
      }
    
      // is "mode" command?
      if (command == "mode") {
        modeCommand(client);
      }
    }
    
    void digitalCommand(BridgeClient client) {
      int pin, value;
    
      // Read pin number
      pin = client.parseInt();
    
      // If the next character is a '/' it means we have an URL
      // with a value like: "/digital/13/1"
      if (client.read() == '/') {
        value = client.parseInt();
        digitalWrite(pin, value);
      } else {
        value = digitalRead(pin);
      }
    
      // Send feedback to client
      client.print(F("Pin D"));
      client.print(pin);
      client.print(F(" set to "));
      client.println(value);
    
      // Update datastore key with the current pin value
      String key = "D";
      key += pin;
      Bridge.put(key, String(value));
    }
    
    void analogCommand(BridgeClient client) {
      int pin, value;
    
      // Read pin number
      pin = client.parseInt();
    
      // If the next character is a '/' it means we have an URL
      // with a value like: "/analog/5/120"
      if (client.read() == '/') {
        // Read value and execute command
        value = client.parseInt();
        analogWrite(pin, value);
    
        // Send feedback to client
        client.print(F("Pin D"));
        client.print(pin);
        client.print(F(" set to analog "));
        client.println(value);
    
        // Update datastore key with the current pin value
        String key = "D";
        key += pin;
        Bridge.put(key, String(value));
      } else {
        // Read analog pin
        value = analogRead(pin);
    
        // Send feedback to client
        client.print(F("Pin A"));
        client.print(pin);
        client.print(F(" reads analog "));
        client.println(value);
    
        // Update datastore key with the current pin value
        String key = "A";
        key += pin;
        Bridge.put(key, String(value));
      }
    }
    
    void modeCommand(BridgeClient client) {
      int pin;
    
      // Read pin number
      pin = client.parseInt();
    
      // If the next character is not a '/' we have a malformed URL
      if (client.read() != '/') {
        client.println(F("error"));
        return;
      }
    
      String mode = client.readStringUntil('\r');
    
      if (mode == "input") {
        pinMode(pin, INPUT);
        // Send feedback to client
        client.print(F("Pin D"));
        client.print(pin);
        client.print(F(" configured as INPUT!"));
        return;
      }
    
      if (mode == "output") {
        pinMode(pin, OUTPUT);
        // Send feedback to client
        client.print(F("Pin D"));
        client.print(pin);
        client.print(F(" configured as OUTPUT!"));
        return;
      }
    
      client.print(F("error: invalid mode "));
      client.print(mode);
    }
    
    
    

    For the fan I am using an OSEPP Fan Motor all together looks like this:

    If you are planning on using a Yun, you will need to set this one up first.
    You will need to get the Yun’s IP Address and then the pins can be manipulated like this:
    Fan on:

    http://192.168.0.29/arduino/digital/6/1

    Fan Off:

    http://192.168.0.29/arduino/digital/6/0 

    The only things we got to do is create a process builder and an invocable Apex Class that will do an HTTP Request to our Fan, Of course, you are going to need your public IP or a Service as DynIp, you will also need to configure your router to point port 80 to the fan! in this case http://192.168.0.29 Fun Fact, after I did this, I couldn’t take any Salesforce Test from home and had to reset my modem for the Sentinel to work again

    the class will look something like this:

    public class turnfan {
       
        @InvocableMethod(label='turn fan on' description='turns fan on or off')
        public static void turnfanon() {
      		callwebservise();
      	}
        @future(callout=true)
        public static void callwebservise(){
                // Instantiate a new http object
        Http h = new Http();
    	
         // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://yourpublicIP/arduino/digital/6/0');
        req.setMethod('POST');
    
        // Send the request, and return a response
        HttpResponse res = h.send(req);
       
        }
    }
    

    Then we need a process builder that executes when the Case records are created and should look something like this:

    Final Thoughts

    I had a lot of fun creating this IoT project, I put in a few hours during Thanks Giving 2017 and finished during the Holidays, It does require some advance to intermediate knowledge of IoT and Salesforce, but It gives you an idea of how much you can do with the new IoT explorer edition, I hope you enjoyed and reach out with any comments or questions, I will try to create a follow up video with a Demo of the solution.

    It is also highly recommended that you finish the trail for IoT Cloud, that way you will have no difficulty following up with all the steps.

    https://trailhead.salesforce.com/modules/iot_explorer_basics

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.