Overview

The Connected Car starter kit for Bluemix allows you to easily model real-time traffic in a city. The kit comes with a Node.js vehicle simulator, an HTML5 visualization web app, and a control client that all communicate through the IBM Internet of Things (IoT) Foundation service for real-time messaging.

The diagram to the right provides an overview of how the kit works. Simulated vehicles are registered with IoT Foundation, and API keys are generated for each application in the kit. The authorization tokens allow for messaging between devices, applications, and services.

The starter kit can be set up and deployed in Bluemix with the following steps:

  1. Set up your IoT Foundation account: register your simulated vehicles and generate an API key for your HTML5 applications.
  2. Create a placeholder application and IoT/Geospatial services in Bluemix.
  3. Download and configure the starter kit to match your Bluemix and IoT environment.
  4. Deploy the starter kit to Bluemix using the Cloud Foundry CLI
  5. Use Node-RED (or build your own application) to extend the starter kit
(approx time: 15 minutes)

You can see a hosted starter kit in action here:


Step 1: Internet of Things (IoT) Foundation

  1. Go to http://internetofthings.ibmcloud.com and sign up for the beta program
  2. Log in to the dashboard, Manage Organizations → Create Organization. Give the organization any name you like, this is only for display purposes.
  3. You should now see your organization dashboard. First, look for the 5-character string next to the organization name (ex. 6xgna)— this is your deviceOrg, which you will use to configure the traffic simulator application.
  4. Next, you will register a device with your organization. A device is used to identify a unique connection from the traffic simulator (note: a single traffic simulator can model multiple vehicles). Click Add Device and create a new device type (ex. trafficsim)— this is your deviceType. Specify a unique deviceId (ex. ABC), then click Continue.
  5. You will now see the credentials for your device— COPY THESE DOWN. Make note of the auth-token, this will be used to configure the traffic simulator. (NOTE: copy and paste the auth-token rather than typing to avoid confusing values)

    In the example images to the right, we have:

    deviceOrg: 6xgna
    deviceType: trafficsim
    deviceId: ABC
    auth-token: gsG5JP8(Go&TyK1aP1

  6. You will now see the device in the organization’s device table. Since we haven’t yet configured the traffic simulator, there will be no data or events for this device.
  7. Next, generate an API key for applications that will interact with the traffic simulator. Select the API Keys tab on the dashboard, then click New API Key.
  8. Record the Key and Auth Token provided. The applications will use this information in the MQTT username/password fields during connection.

    appKey: a:6xgna:wf4cj1uz2n
    appToken: MtiQCvA5W+!cAPXf@+

  9. This information will be used to configure your starter kit in a later section.

Step 2: Set up Bluemix application

After configuring IoT Foundation, we next need to prepare a placeholder Bluemix application for us to set up the IoT Foundation and Geospatial Analytics services.

  1. Sign up for Bluemix at http://www.bluemix.net with the IBM ID used to create the IoT Foundation account. A basic account is free for 30 days, and provides enough compute resources to support several instance of the traffic simulator.
  2. From the dashboard, create a new application.
  3. Select a Node.js runtime. We will push the traffic simulator code to this application on top of this runtime.
  4. The next page shows details for the runtime. On the right side of the page, provide a name for the application (ex. bryan-trafficsim). This will produce a default route of http://bryan-trafficsim.mybluemix.net for the runtime. Click Create.
  5. After creating the runtime, the application dashboard will load and your application will be automatically started. The next step is to download the Cloud Foundry CLI tool to deploy the starter kit application to your Bluemix application. To do this, select View Guide, then Install the cf command line tool. Follow the instructions found on the next page to download and install the cf tools for your operating system.
  6. Once the cf tools are installed, the placeholder application will have finished staging. Next, add the Internet of Things service: from the application dashboard, click Add Service, then select the Internet of Things beta (near the bottom).
  7. On the next screen, give the service a name (ex. iot-trafficsim) and enter the API key and token you generated previously. This links the IoT service with your IoT Foundation organization, and allows Bluemix applications using this service to link to the organization.
  8. Next, add the Geospatial Analytics service. From the application dashboard, click Add Service, then select the Geospatial Analytics service (under the Big Data category). Provide a name (ex. geo-trafficsim) and select Create. The Geospatial Analytics service provides geofence alerts using the MQTT protocol. With the service, you define an input MQTT topic (for location data) and an output MQTT topic (for alert data), then add/remove geofence polygons dynamically. In the context of the starter kit, we use the telemetry data topic for simulated vehicles as the input topic, and have our HTML5 visualization app subscribe to the service alert topic to display enter/exit events.

Step 3: Download, configure, and deploy

Once the IoT Foundation and Bluemix steps are complete, the last step is to download the starter kit, configure, and deploy to Bluemix.

  1. Download the starter kit (IBM internal link), and unzip to your local machine. (external hosting on GitHub coming soon)
  2. Using your IoT Foundation device credentials and API key, update the starter kit configuration, located in trafficsimulator/public/config/settings.js. Assuming I registered three devices in total, here is what the configuration would look like:

    var config = {
    	iot_deviceType: "trafficsim",  // replace with your deviceType
    	iot_deviceOrg: "6xgna",        // replace with your IoT Foundation organization
    	iot_deviceSet: [               // replace with your registered device(s)
    		{ deviceId: "ABC", token: "gsG5JP8(Go&TyK1aP1" },   
    		{ deviceId: "DEF", token: "9290oiJEFIO9edf-s!" },
    		{ deviceId: "GHI", token: "8&_Q3alkqot7a8AP!0" }
    	],
    	iot_apiKey: "a:6xgna:wf4cj1uz2n",    // replace with the key for a generated API token
    	iot_apiToken: "MtiQCvA5W+!cAPXf@+",  // replace with the generated API token
    
    	// these topics will be used by Geospatial Analytics
    	notifyTopic: "iot-2/type/api/id/geospatial/cmd/geoAlert/fmt/json",
    	inputTopic: "iot-2/type/vehicle/id/+/evt/telemetry/fmt/json",
    };
    
  3. Next, edit the starter kit manifest file, used to manage the Bluemix deployment. In trafficsimulator/manifest.yml, update the host and name fields to match your Bluemix placeholder application, and set the number of instances equal to the number of devices you listed in the starter kit settings (ex. 3):

    applications:
    - host: bryan-trafficsim 
      disk: 128M
      name: bryan-trafficsim
      command: node app.js
      path: .
      domain: mybluemix.net
      mem: 128M
      instances: 3 
    
  4. Now, use the Cloud Foundry cf tools to deploy your starter kit to Bluemix. Navigate to the trafficsimulator/ directory, and enter the following commands:

    cf api https://api.ng.bluemix.net
    cf login
    	-- Username> your IBM ID username
    	-- Password> your IBM ID password
    	-- Select an org...> should be the same as your username
    	-- Select a space...> usually 'dev'
    cf push bryan-trafficsim   (your app name)
    
  5. If the application fails to stage, check your manifest.yml file for errors, such as tabs instead of whitespace.
  6. Once the application finishes staging, you can increase the simulation count with BLuemix app environment variable VEHICLE_COUNT (see picture at right)
  7. Next, start the Geospatial Analytics service by visiting the following URL in a browser: http://bryan-trafficsim.mybluemix.net/GeospatialService_start (replace with your app name). Wait 20-30 seconds, until your browser displays {} indicating the service has started.
  8. Now, go to http://bryan-trafficsim.mybluemix.net (replace with your app name). You will be able to view the simulated vehicles in real-time, create geofences, and view alerts.
  9. To create a geofence, click the "exclamation mark" button on the map toolbar. A polygon will appear on the middle of the screen. Click the vertices/edges of the polygon to choose a desired region, then click the black "handle" in the middle of the polygon, and either create or delete the region.
  10. To modify vehicle properties or display alerts on the map, publish an MQTT message through IoT Foundation using an MQTT client. The tester app is an example of this: http://bryan-trafficsim.mybluemix.net/tester/ (replace with your app name). Enter a vehicle ID, found by selecting a car on the map, then send a message or edit a property. Once you enter the command, the topic and payload used will appear at the bottom of the tester app. (see pictures at right)
  11. The vehicle simulator will allow you to simulate any number of customer properties: simply publish the MQTT setProperty command, and the vehicle will start modelling the property if it wasn't already before. To remove a property, publish an empty string value.

Extending the starter kit with Node-RED

Node-RED is a visual tool for wiring the Internet of Things together. Node-RED allows you to design "flows" of logic that link a variety of inputs (MQTT, TCP, HTTP) to outputs (MQTT, HTTP, Twitter, MongoDB, Cloudant) to quickly add logic to an application. Here, we provide three "recipes" for Node-RED flows that will add capabilities to the starter kit.

Deploy Node-RED in Bluemix

  • First, deploy an instance of Node-RED in Bluemix. Add a new application, selecting the Internet of Things Starter boilerplate. Give it a name and hostname (ex. bryan-trafficsimulator-nodered) and press Create.
  • When the application is running, select Bind Service and choose the iot-trafficsim service we previously used. This will allow Node-RED to be preconfigured to access your IoT Foundation organization.
  • When the application re-stages, visit http://bryan-trafficsimulator-nodered.mybluemix.net/red/ (replace with your app name) to access the Node-RED canvas.
  • For your first flow, we are going to monitor telemetry for all of our vehicles. Drag an iot-app input node onto the canvas. Double-click the node, and enter iot-2/type/trafficsim/id/+/evt/telemetry/fmt/json. Now drag a debug output node onto the canvas, and wire the nodes together. Hit Deploy in the top-right corner to launch the application, and select the debug pane. You will see telemetry data for each simulated vehicle appear in the debug pane. (see picture)
  • If your VEHICLE_COUNT environment variable is set to a value greater than 1, the telemetry data for VEHICLE_COUNT vehicles will be contained in a single array. To modify the flow to filter for a specific car, drop a function node in the middle of your flow, double-click and fill in the following code:

    var data = msg.payload;
    for (var i in data) {
    	if (data[i].id == "ABC-2") {
    		msg.payload = data[i];
    		return msg;
    	}
    }
    
  • Save your changes, wire the flow together, and click Deploy. You should now see data for only this car in your debug pane. (see picture)
  • To import this example into your Node-RED application, select Import → Clipboard and paste in the following:

    [{"id":"649f5495.9b60ac","type":"iot-app-in","name":"IoT App In","topic":"iot-2/type/trafficsim/id/ABC/evt/telemetry/fmt/json","x":95,"y":69,"z":"82444a5d.7dbbb8","wires":[["c6ff5946.3900a8"]]},{"id":"5d91ab10.a26e54","type":"debug","name":"","active":true,"console":"false","complete":"false","x":390,"y":187,"z":"82444a5d.7dbbb8","wires":[]},{"id":"c6ff5946.3900a8","type":"function","name":"filter for ABC-2","func":"var data = msg.payload;\nfor (var i in data) {\n\tif (data[i].id == \"ABC-2\") {\n\t\tmsg.payload = data[i];\n\t\treturn msg;\n\t}\n}","outputs":1,"x":241,"y":124,"z":"82444a5d.7dbbb8","wires":[["5d91ab10.a26e54"]]}]
    

Sample 1: Remote Control

  • We can use Node-RED to build a simple remote control for a vehicle. We will set up two inject nodes: one to unlock a vehicle, the other to lock it. For each operation, we will publish a setProperty command for property lockState with a value of "locked" or "unlocked", then publish an addOverlay command to display a notification on the map.
  • First, use the Tester application (http://bryan-trafficsim.mybluemix.net/tester/) (replace with your app name) to set the lockState. The Tester application will show the topic and payload you need to use to publish this command. (see picture)

    Published command!
    Topic: iot-2/type/trafficsim/id/ABC/cmd/setProperty/fmt/json
    Payload:
    {
        "id": "ABC-16",
        "property": "lockState",
        "value": "locked"
    }
    
  • Now use the Tester application to publish a "Locked!" overlay message over this car. (see picture)

    Published command!
    Topic: iot-2/type/api/id/tester/cmd/addOverlay/fmt/json
    Payload:
    {
        "id": "ABC-16",
        "text": "Locked!",
        "duration": 5000
    }
    
  • Go back to the Node-RED flow and drag two inject input nodes onto the Node-RED canvas. Double-click each node and configure a string payload of "locked" / "unlocked". This payload will be the value we send in the setProperty command.
  • Now, add a function node to the flow and wire both inject nodes to it. This function will build and publish two commands to IoT Foundation-- one for setting the vehicle state, the other for display a message on the map.

    var commandValue = msg.payload;
    
    var setPropertyMsg = {
    	topic: "iot-2/type/trafficsim/id/ABC/cmd/setProperty/fmt/json",
    	payload: JSON.stringify({
    		"id": "ABC-16",
    		"property": "lockState",
    		"value": commandValue
    	})
    }
    
    var addOverlayMsg = {
    	topic: "iot-2/type/api/id/tester/cmd/addOverlay/fmt/json",
    	payload: JSON.stringify({
    		"id": "ABC-16",
    		"text": commandValue,
    		"duration": 5000
    	})
    }
    
    return [ [setPropertyMsg, addOverlayMsg] ];
    
  • Add an iot-app output node and debug node to the output of the function. Leave the topic field on the iot-app node empty—we defined the topic already, on the messages built in the function node.
  • Deploy the application, and press the button on the inject nodes while watching the map. You will see the lockState property update for vehicle ABC-16 (or whichever you chose), and a message appear on the map. (see picture)
  • To import this example into your Node-RED application, select Import → Clipboard and paste in the following:

    [{"id":"39b55e35.c64aa2","type":"inject","name":"","topic":"","payload":"locked","payloadType":"string","repeat":"","crontab":"","once":false,"x":122,"y":61,"z":"82444a5d.7dbbb8","wires":[["15886565.ea779b"]]},{"id":"4811a619.b7ee58","type":"inject","name":"","topic":"","payload":"unlocked","payloadType":"string","repeat":"","crontab":"","once":false,"x":123,"y":118,"z":"82444a5d.7dbbb8","wires":[["15886565.ea779b"]]},{"id":"15886565.ea779b","type":"function","name":"build commands","func":"var commandValue = msg.payload;\n\nvar setPropertyMsg = {\n\ttopic: \"iot-2/type/trafficsim/id/ABC/cmd/setProperty/fmt/json\",\n\tpayload: JSON.stringify({\n\t\t\"id\": \"ABC-16\",\n    \t\"property\": \"lockState\",\n    \t\"value\": commandValue\n\t})\n}\n\nvar addOverlayMsg = {\n\ttopic: \"iot-2/type/api/id/tester/cmd/addOverlay/fmt/json\",\n\tpayload: JSON.stringify({\n\t\t\"id\": \"ABC-16\",\n    \t\"text\": commandValue,\n    \t\"duration\": 5000\n\t})\n}\n\nreturn [ [setPropertyMsg, addOverlayMsg] ];","outputs":1,"x":314,"y":87,"z":"82444a5d.7dbbb8","wires":[["f4a0c41f.0b5f38","16a019e9.e95fe6"]]},{"id":"f4a0c41f.0b5f38","type":"iot-app-out","name":"send","topic":"","x":529,"y":113,"z":"82444a5d.7dbbb8","wires":[]},{"id":"16a019e9.e95fe6","type":"debug","name":"","active":true,"console":"false","complete":"false","x":507,"y":59,"z":"82444a5d.7dbbb8","wires":[]}]
    

Sample 2: Model my Grandmother

  • In it's most basic form, the vehicle simulator is just a simulator— one that can model any properties and move along a road. To simulate a person (say, my grandmother Albertine) instead of a car, we simple change the properties of a simulated "vehicle" dynamically.
  • In the Node-RED flow, drop an inject input node onto the canvas. Give it a name of ABC-1 is a person and a string payload of ABC-1.
  • Add a function node after the inject node, and insert the following code:

    var id = msg.payload;
    var setPropertyTopic = "iot-2/type/trafficsim/id/ABC/cmd/setProperty/fmt/json";
    var addOverlayTopic = "iot-2/type/api/id/tester/cmd/addOverlay/fmt/json";
    var msgs = [];
    
    var propValues = [
    	// static properties
    	["speed", "2"],
    	["type", "person"],
    	["state", "walking"],
    	["description", "Out for a nice stroll"],
    
    	// custom properties
    	["customProp", ""],
    	["age", 87],
    	["sex", "female"],
    	["name", "Albertine"]
    ];
    
    for (var i in propValues) {
    	msgs.push({ topic: setPropertyTopic,
    		payload: JSON.stringify({
    			"id": id,
        		"property": propValues[i][0],
        		"value": propValues[i][1]
    		})
    	});	
    }
    
    // make map overlay
    msgs.push({
    	topic: addOverlayTopic,
    	payload: JSON.stringify({
    		"id": id,
        	"text": "Hello Grandmother!",
        	"duration": 5000
    	})
    });
    
    return [msgs];
    
  • Add an iot-app output node after the function, and leave the topic blank (since we defined it on each message). Press the inject node, and you will see vehicle ABC-1 now become my grandmother. (see picture)
  • Unfortunately, ABC-1 has now disappeared from the map. The map UI decides how to draw each object based on the type property, and we do not have an image defined for type person. To add in new types with custom images, modify trafficsimulator/public/js/main.js like so:
    // trafficsimulator/public/js/main.js
    var Images = {
    	car: new Image(),
    	person: new Image(),
    }
    
    ...
    
    Images.car.src = "img/redcar.png";
    Images.person.src = "img/person.png";
    
  • For now, change the Node-RED function to set a type of "circle" instead of "person", deploy the application, and press the inject node again. Now the object is drawn as a circle on the map.
  • This sample can be extended to model practically anything you want. And you don't have to use Node-RED for this— MQTT clients are available in many languages, so you could integrate property setting within an external application by connecting a client to IoT Foundation.
  • To import this example into your Node-RED application, select Import → Clipboard and paste in the following:

    [{"id":"39b55e35.c64aa2","type":"inject","name":"ABC-1 is a person","topic":"","payload":"ABC-1","payloadType":"string","repeat":"","crontab":"","once":false,"x":122,"y":61,"z":"82444a5d.7dbbb8","wires":[["15886565.ea779b"]]},{"id":"15886565.ea779b","type":"function","name":"turn into person","func":"var id = msg.payload;\nvar setPropertyTopic = \"iot-2/type/trafficsim/id/ABC/cmd/setProperty/fmt/json\";\nvar addOverlayTopic = \"iot-2/type/api/id/tester/cmd/addOverlay/fmt/json\";\nvar msgs = [];\n\nvar propValues = [\n\t// static properties\n\t[\"speed\", \"2\"],\n\t[\"type\", \"circle\"],\n\t[\"state\", \"walking\"],\n\t[\"description\", \"Out for a nice stroll\"],\n\n\t// custom properties\n\t[\"customProp\", \"\"],\n\t[\"age\", 67],\n\t[\"sex\", \"female\"],\n\t[\"name\", \"Albertine\"]\n];\n\nfor (var i in propValues) {\n\tmsgs.push({ topic: setPropertyTopic,\n\t\tpayload: JSON.stringify({\n\t\t\t\"id\": id,\n    \t\t\"property\": propValues[i][0],\n    \t\t\"value\": propValues[i][1]\n\t\t})\n\t});\t\n}\n\n// make map overlay\nmsgs.push({\n\ttopic: addOverlayTopic,\n\tpayload: JSON.stringify({\n\t\t\"id\": id,\n    \t\"text\": \"Hello Grandmother!\",\n    \t\"duration\": 5000\n\t})\n});\n\nreturn [msgs];","outputs":1,"x":314,"y":87,"z":"82444a5d.7dbbb8","wires":[["f4a0c41f.0b5f38","16a019e9.e95fe6"]]},{"id":"f4a0c41f.0b5f38","type":"iot-app-out","name":"send","topic":"","x":529,"y":113,"z":"82444a5d.7dbbb8","wires":[]},{"id":"16a019e9.e95fe6","type":"debug","name":"","active":true,"console":"false","complete":"false","x":507,"y":59,"z":"82444a5d.7dbbb8","wires":[]}]
    

Sample 3: Tweet a Geofence Alert

  • With the Geospatial Analytics service, we can create geofence polygons in our visualization map UI and receive MQTT messages when a vehicle enters or leaves a region. The map UI subscribes to this notification topic and displays messages as a notification bubble over the vehicle. These geospatial alerts can be subscribed to by multiple applications, and can be a building block used to create complex scenarios. For example: a bounding box for teenage drivers, signaling parents when their child drives outside of an "approved" zone. Or: detect when my vehicle has entered a geofence surrounding my house, and notify my house automation system to prepare for my arrival.
  • We can use Node-RED to link geofence alerts to other applications. In this sample, we will have a geofence notification message for a vehicle send out a tweet using the Twitter node.
  • First, drop an iot-app input node onto the canvas. Configure the topic to be the config notifyTopic found in trafficsimulator/public/config/settings.js in your application. If you kept the default value, it will be iot-2/type/api/id/geospatial/cmd/geoAlert/fmt/json
  • Add a function node after the iot-app input node. This function will filter the data returned from the geospatial service, and only continue execution if the vehicle for the alert matches an ID specified. Use the following code (replace the ID with a vehicle in your environment), wire the function to a debug node, and deploy:

    msg.payload = {
    	time: msg.payload.time,
    	id: msg.payload.deviceInfo.id,
    	lon: msg.payload.deviceInfo.location.longitude,
    	lat: msg.payload.deviceInfo.location.latitude,
    	eventType: msg.payload.eventType,
    	region: msg.payload.regionId
    }
    if (msg.payload.id == "HNS-19") {
    	return msg;
    }
    
  • Next, drag a Twitter output node onto the canvas, and wire the output of the function to the input of this node. The Twitter node allows you to configure an existing Twitter account using OAuth, then will tweet the contents of msg.payload. Sign in to your Twitter account, then modify the function node to "clean up" the message for Twitter:

    msg.payload = {
    	time: msg.payload.time,
    	id: msg.payload.deviceInfo.id,
    	lon: msg.payload.deviceInfo.location.longitude,
    	lat: msg.payload.deviceInfo.location.latitude,
    	eventType: msg.payload.eventType,
    	region: msg.payload.regionId
    }
    if (msg.payload.id == "HNS-19") {
    	var verb = "exited";
    	if (msg.payload.eventType == "Entry") { verb = "entered"; }
    	msg.payload = "Vehicle " + msg.payload.id + " has " + verb + " region " + msg.payload.region + "!";
    	return msg;
    }
    
  • Deploy the application, and Node-RED will tweet whenever a geofence is crossed by your vehicle (see picture)
  • Try using other output nodes (Cloudant, iot-app, HTTP, etc.) to make the scenario more interesting— log events to a Cloudant database, publish a "reduce speed" setProperty message to the vehicle on exit, call a REST API, etc.
  • To import this example into your Node-RED application, select Import → Clipboard and paste in the following:

    [{"id":"937049cc.6c8fb8","type":"iot-app-in","name":"geofence alerts","topic":"iot-2/type/api/id/geospatial/cmd/geoAlert/fmt/json","x":122,"y":113,"z":"42d6510f.bd29b","wires":[["242e86e5.dbd17a"]]},{"id":"242e86e5.dbd17a","type":"function","name":"filter for HNS-19","func":"msg.payload = {\n\ttime: msg.payload.time,\n\tid: msg.payload.deviceInfo.id,\n\tlon: msg.payload.deviceInfo.location.longitude,\n\tlat: msg.payload.deviceInfo.location.latitude,\n\teventType: msg.payload.eventType,\n\tregion: msg.payload.regionId\n}\nif (msg.payload.id == \"HNS-19\") {\n\tvar verb = \"exited\";\n\tif (msg.payload.eventType == \"Entry\") { verb = \"entered\"; }\n\tmsg.payload = \"Vehicle \" + msg.payload.id + \" has \" + verb + \" region \" + msg.payload.region + \"!\";\n\treturn msg;\n}","outputs":1,"x":317,"y":87,"z":"42d6510f.bd29b","wires":[["5fd765e.fa0289c","8205c0f8.7dfa4"]]},{"id":"5fd765e.fa0289c","type":"debug","name":"","active":true,"console":"false","complete":"false","x":511,"y":70,"z":"42d6510f.bd29b","wires":[]},{"id":"8205c0f8.7dfa4","type":"twitter out","twitter":"","name":"Tweet @bryanboyddemos","x":570,"y":128,"z":"42d6510f.bd29b","wires":[]}]
    
center>