IoT Platform on Bluemix: Experimenting with IBM Watson and Gobot

by Stas TurloApril 12, 2016
In this post, we show how to turn your laptop into an IoT device and connect it to the Watson platform, controlling the system from a sample web app.

ibm-bluemix-internet-of-things-platform-catalog

The demo app described in this post consists of two main components:

  • A “sensor” that collects various information about a MacBook laptop: CPU temperature, fan speed, and laptop position using the Sudden Motion Sensor
  • A web application that visualizes data from the sensor and allows you to set fan speed and display brightness

 

Prerequisites

To follow the instructions, you need:

  • A Bluemix account
  • The Cloud Foundry CLI

The source code for the demo is available here.

 

Preparing Internet of Things Platform

  1. Clone my repository.
  2. Create a Go web application from the Bluemix dashboard.
  3. Create an instance of the Internet of Things Platform service:
    1. Click Internet of Things Platform on the Bluemix dashboard.
    2. Click Launch dashboard.
  4. ibm-bluemix-internet-of-things-dashboard

  5. Create a new device on the IoT Platform dashboard.
  6. experimenting-with-ibm-watson-iot-platform-and-gobot-on-bluemix-add-device

  7. From the sensor directory, run the go build command and then the sudo ./sensor -id id you provided when creating the device -org organization id -api_token authentication token command. The Organization ID and Authentication Token are shown after your create a device.
  8. Go to the repository directory and run the cf push application name command. See step 2.
  9. Open the application in a browser and provide the device ID.

 

Connecting a device to the IoT platform

Every registered organization has a unique endpoint that must be used when connecting MQTT clients for devices in that organization:

org_id.messaging.internetofthings.ibmcloud.com

You can find your org_id on the IoT Platform dashboard.

org_id-iot-platform

A device must authenticate using a client ID in the following format:

D:org_id:device_type:device_id

where:

  • d identifies your client as a device.
  • org_id is your unique organization ID.
  • type_id is the device type you specified when creating the device on the dashboard.
  • device_id is the ID you specified when creating the device on the dashboard.

The Watson IoT platform currently only supports token-based authentication for devices, so there is only one valid username for them—use-token-auth. Use the authentication token generated by the platform upon device creation as a password.

Connecting your Gobot sensor to the IoT platform should look similar to:

mqttAdaptor := mqtt.NewMqttAdaptorWithAuth("sensor",
    fmt.Sprintf("tcp://%s.messaging.internetofthings.ibmcloud.com:1883", *org),
    fmt.Sprintf("d:%s:gobot-sensor:%s", *org, *deviceId), username, *apiToken)

 

Publishing device events

Devices can only publish to the event topic:

iot-2/evt/event_id/fmt/format_string

where:

  • event_id is the ID of the event, for example status. The event ID can be any string permitted by MQTT. Subscriber applications must use this string in their subscription topic to receive the events published on this topic if wildcards are not used.
  • format_string is the format of the event payload, for example json. The format can be any string permitted by MQTT. Subscriber applications must use this string in their subscription topic to receive events published on this topic if wildcards are not used.

For example:

gobot.Every(1*time.Second, func() {
    data, err := json.Marshal(GatherSensorInfo())
    if err == nil {
        mqttAdaptor.Publish("iot-2/evt/status/fmt/json", data)
    }
})

 

Subscribing to commands

To receive a command, your device has to subscribe to command topics:

iot-2/cmd/command_id/fmt/format_string

where:

  • command_id is the ID of the command, for example update. The command ID can be any string permitted by MQTT. A device must use this string in its subscription topic to receive commands published on this topic if wildcards are not used.
  • format_string is the format of the event payload, for example json. The format can be any string permitted by MQTT. A device must use this string in its subscription topic to receive commands published on this topic if wildcards are not used.

For example:

mqttAdaptor.On("iot-2/cmd/set_speed/fmt/json", func(data []byte) {
    var cmd SetSpeedCmd

    if err := json.Unmarshal(data, &cmd); err == nil {
        C.SMCSetFanSpeed(C.int(cmd.Fan), C.int(cmd.Speed))
    } else {
        log.Printf("Error: %s", err)
    }
})
mqttAdaptor.On("iot-2/cmd/inc_brightness/fmt/json", func(data []byte) {
    C.IncreaseBrightness()
})
mqttAdaptor.On("iot-2/cmd/dec_brightness/fmt/json", func(data []byte) {
    C.DecreaseBrightness()
})

 

Connecting an application to the IoT platform

An application has to use the same organization endpoint as your device. The application must authenticate using a client ID in the following format:

a:org_id:app_id

where:

  • a indicates the client is an application.
  • org_id is your unique organization ID.
  • app_id is a user-defined unique string identifier for this client.

You don’t need to register the application before it is connected, so app_id can be any string that follows these rules:

  • Contains only alphanumeric characters (a-z, A-Z, 0-9) and hyphen (-), underscore (_), or dot (.)
  • Has a maximum length of 36 characters

Applications need an API key to connect to an organization. When an API key is registered, a token will be generated and it must be used with that API key.

The API key looks similar to this: a-org_id-b2roh3bptq. The token looks similar to this: p3C76R-54EPKmeKCPA.

reading-service-credentials-on-the-bluemix-website

For example:

if appEnv, err := cfenv.Current(); err != nil {
    org = "quickstart"
    port = ":8080"
} else {
    port = fmt.Sprintf(":%d", appEnv.Port)
    if iotfService, err := appEnv.Services.WithLabel("iotf-service"); err == nil {
        org = fmt.Sprintf("%s", iotfService[0].Credentials["org"])
        apiKey = fmt.Sprintf("%s", iotfService[0].Credentials["apiKey"])
        apiToken = fmt.Sprintf("%s", iotfService[0].Credentials["apiToken"].(string))
    }
}
...
mqttAdaptor := mqtt.NewMqttAdaptorWithAuth("server",
    fmt.Sprintf("tcp://%s.messaging.internetofthings.ibmcloud.com:1883", org),
    fmt.Sprintf("a:%s:%s", org, uuid.Formatter(u, uuid.CleanHyphen)),
    apiKey,
    apiToken)

 

Subscribing to device events

Subscribe to the topic:

iot-2/type/device_type/id/device_id/evt/event_id/fmt/format_string

The MQTT “any” wildcard character (+) may be used for any of the following components if you want to subscribe to more than one type of event or events from more than a single device:

  • device_type
  • device_id
  • event_id
  • format_string

For example:

mqttAdaptor.On(fmt.Sprintf("iot-2/type/+/id/%s/evt/+/fmt/json", deviceId), func(data []byte) {
    ...
})

 

Publishing device commands

Your application can publish a command to any registered device. Publish to the topic:

iot-2/type/device_type/id/device_id/cmd/command_id/fmt/format_string

For example:

cmdData, err := json.Marshal(cmd)
if err == nil {
    mqttAdaptor.Publish(fmt.Sprintf("iot-2/type/gobot-sensor/id/%s/cmd/set_speed/fmt/json", deviceId), cmdData)
} else {
    log.Printf("Error sending command: %s", err)
}

For more information about application and device communication, see IBM Watson IoT Platform Documentation.

As you can see, with MQTT support in Watson IoT Platform and Gobot, it is really easy to connect your devices and applications.

 

Related reading