Browser to Device LED Control using SimpleMQ

In this tutorial, we will show you how to use the SMQ protocol for designing a web based control management user interface for controlling Light Emitting Diodes (LEDS) in one or multiple devices. The application will consist of a browser based control user interface and device code implemented in C and designed for embedded devices. We also provide a simulated version that may run in a terminal window both on Windows and Linux.

The device will publish its given capability as a JSON object to the named topic "/m2m/led/device". All browsers are subscribed to this topic and will be alerted to any new device(s) that may join the network.

Publish to Device

Figure 1: new devices joining the network send a list of capabilities to connected browsers.

The JSON object sent to the "/m2m/led/device" topic includes the number of LEDs in the device, the LED colors, and an ID (number) for each LED. The browser will use the information provided to add a new tab per connected device and then dynamically create HTML based on the capabilities presented by the device in the JSON object.

New browsers joining the network will send a message to allow all devices to subscribe. The browser starts by publishing a message to the named topic "/m2m/led/display", thus all existing devices will receive the message as illustrated in the figure below.

Publish to Display (Browser)

Figure 2: new browsers joining the network send a "hello" message to the named topic "/m2m/led/display".

Ephemeral topic ID

We introduced Topic IDs, Sub Topic IDs, and Ephemeral Topic IDs in the Chat Client Tutorial. At this point, you may want to review either the chat client tutorial for an introduction to these features, or read the SMQ documentation, which introduces these unique IoT features as used within SMQ.

The device will respond to the browser publication by sending a message back to the "sender" of the message. The sender of the message is known as the publisher's ephemeral topic ID (ptid). The ptid that was introduced in the chat client is an ephemeral ID that uniquely identifies a specific client connected to the broker. The message sent to the ephemeral ID is the same JSON data as sent to the "/m2m/led/device" topic (figure 1) and enables the browser to dynamically build a user interface for the connected device(s).

The following code snippet shows how the JavaScript code in the browser subscribes to the topic "/m2m/led/device" and to the topic name "self", which means that the browser subscribes to messages sent to the client's own ephemeral ID. Notice that the message callback function "devInfo" is the same for the two subscribe calls below. One is for receiving JSON messages sent to the topic "/m2m/led/device", and the other is for receiving messages received on the ephemeral topic ID.

smq.subscribe("/m2m/led/device", "devinfo", {"datatype":"json", "onmsg":devInfo});
smq.subscribe("self", "devinfo", {"datatype":"json", "onmsg":devInfo});

Example 1: subscribing to topic "/m2m/led/device" and to the device's own ephemeral topic ID.

The "devInfo" callback function receives incoming messages as parsed JSON objects. The "datatype" parameter instructs the SMQ client stack to parse the incoming message as JSON. The "devInfo" callback function is called when a device publishes to the named topic "/m2m/led/device" or to the client's ephemeral topic ID ("self"). Notice that we are further refining the subscription by setting a secondary topic name[w1]. Both subscribe requests above include the secondary sub topic name "devinfo". Secondary sub topic names, aka sub-topics, are optional in the SMQ protocol. We use them in this example for differentiating sub-messages sent on a particular topic.

The "devInfo" callback function, not shown here, dynamically builds the HTML user interface based on the device's capabilities as presented in the JSON object. See the source code for details on this function.

When a user clicks an LED for one of the devices connected, we must first be able to uniquely identify the device and then uniquely identify the LED in the device. We can do this by embedding the publisher's ephemeral topic ID (ptid) and LED ID in the dynamically generated HTML.

id="switch-'+ptid+'-'+ledId+'";

Example 2: creating an HTML id that uniquely identifies an LED in a device by combining ptid and LED ID.

A click event callback function is installed on the HTML element and this function gets called when a user clicks on the LED button's on/off switch.

function clickCallback() {
    var id = $(this).prop('id').match(/(\d+)-(\d+)/);
    var ptid = parseInt(id[1]);
    var ledId = parseInt(id[2]);
    var data = new Uint8Array(2);
    data[0] = ledId;
    data[1] = this.checked ? 1 : 0;
    smq.publish(data,ptid);
}

Example 3: extract ptid and LED ID from the HTML element on user click events.

Line 1: We use regular expressions to extract the ptid and LED ID from the HTML element's 'id'. The regular expression matches the format we used in example 2 where we created the HTML element's id.

Line 3 - 4: The ptid and LED ID extracted from the regular expression are string values. These must be converted to number values.

Line 5- 7: The LED state change is sent as a two byte binary array to the device, where byte one is the LED ID, and byte two is "one" for LED on and "zero" for LED off.

Line 8: The two byte binary packet is sent to the device's ephemeral topic ID. We get the ephemeral topic ID when the device sends its capability list as a JSON object. We can now use this number to send a message directly to the device. The following figure illustrates how a browser sets an LED in a specific device by publishing to the device's ephemeral topic ID.

Set LED by publishing to device's ephemeral ID

Figure 3: device receives set LED command from browser, updates LED, and publishes LED status to all browsers.

The set LED command is published to the device's ephemeral topic ID on line 8 in example 3. When the device receives this command, the device sets the LED and sends a LED state change command to all connected browsers by publishing to the named topic "/m2m/led/device" and sub-topic "led". All browsers subscribe to this topic, thus clicking an LED in one browser will automatically set the LED in all the other connected browsers.

function led(data, ptid) {
    var ledId = data[0]; 
    var checked = data[1];
    //Update user interface
};
//When a device publishes LED state change.
smq.subscribe("/m2m/led/device", "led", {"onmsg":led});

Example 4: subscribing to topic "/m2m/led/device" and sub-topic "led".

Example 4 illustrates how the browser handles the LED state change event received from the device.

The above provides an introduction to the LED browser based management code. There is more code involved than explained above such as creating the HTML for the LEDs, managing the tabs for each connected device, and so on. The JavaScript code embedded in the LED management HTML page is fully documented, and we recommend that you study this code for the details.

Device C code

The device C code is split into two sections:

  • Generic code that implements the LED logic and code that interfaces to the SMQ C client stack.
  • Device specific code that must be connected to LEDs in a particular device. The C code includes a section that should only be enabled when running the device code in simulation mode on Windows or Linux and when you have no LEDs to control. The interface functions required for LED device control are defined in ledctrl.h.

The device specific code designed to interface to the physical LED(s) is straightforward, so we will not go into how this code works in this tutorial. Please see the code for details.

A device should at startup call the function "mainTask", which is a function that will not return unless the SMQ stack gets a forced disconnect request from the broker or if an unrecoverable error occurs.

void mainTask(const char* uuid, int uuidLen, const char* devInfo);

The first two parameters are for a an ID required by the SMQ protocol. This ID must be unique, but should preferably not change when the device restarts. The unique ID is typically generated in a device from the 6 byte MAC address associated with the Ethernet interface. The devInfo is an optional string that is sent to the broker when the client initially connects. The string may be used for identification purposes.

Connecting to a broker and establishing a persistent SMQ connection is a two step process in C code. The following code snippet from the example shows the connect sequence in the example code (error checking is removed).

SMQ_init(smq, SIMPLEMQ_URL, 0)
SMQ_connect(smq,
            uuid, uuidLen,
            0, 0, /* credentials */
            devInfo, strlen(devInfo));

Example 5: the two step sequence for establishing a persistent SMQ connection.

Function SMQ_init sends the initial HTTP connect command to the broker URL. The macro SIMPLEMQ_URL is set to our online demo broker http://simplemq.com/smq.lsp. You can change this URL and set it to your own broker.

SMQ_connect establishes the persistent SMQ connection. We are not using credentials in our example code so these two parameters are set to NULL.

Topic ID versus named topics

In the JavaScript example, we installed callbacks for receiving messages on named topics. We also published directly to named topics. The SMQ stack for C code provides a more bare-bone API and does not allow us to install callbacks. All messages are received by calling function SMQ_getMessage. The following example illustrates how the C code waits for messages from the broker.

int len =  SMQ_getMessage(smq, &msg);
if(len < 0) /* We received a control message or an error code */
{
   /* manage control messages and error codes */
}
else if(len > 0)
{
   /* Manage received message for a topic we subscribe to or manage
    * messages sent to our ephemeral ID. smq->tid is the topic ID for
    * the received message.
    */
}

Example 6: distinguishing between received control messages, error codes, and payload data.

The SMQ protocol translates all topic names to topic IDs. A topic ID is a unique ID generated by the server when we either subscribe to a topic or request access to publish to a message. The topic IDs are partly abstracted away in the JavaScript code and we can work with named topics. However, the C code can only work with topic IDs. See the documentation on how the topic names are translated to topic IDs for details. The following code snippet from the LED source code shows how we request this information from the broker.

SMQ_create(smq, "/m2m/led/device"); /*So we can publish to /m2m/led/device*/
SMQ_createsub(smq, "devinfo");
SMQ_createsub(smq, "led");
SMQ_subscribe(smq, "/m2m/led/display");

Example 7: requests broker to create one topic ID, two sub-topic IDs, and subscribe to a topic.

Control Messages

The broker sends four ACK messages for the above four calls. The ACK messages have a negative value so they can easily be distinguished from standard published messages. The following code snippet shows how to distinguish the various control messages received:

len =  SMQ_getMessage(smq, &msg);
if(len < 0) /* We received a control message or an error code */
{
   switch(len)
   { 
      case SMQ_CREATEACK:  /* ACK: "/m2m/led/device" */
      case SMQ_CREATESUBACK: /*We get two suback messages (devinfo and led)*/
      case SMQ_SUBACK: /* ACK: "/m2m/led/display" */

Example 8: shows how to distinguish the various control messages from the requests sent in example 7.

Receiving payload data (messages published to a topic)

We have received a message published to a topic we have subscribed to when function SMQ_getMessage returns a value greater than zero. Note: you are also automatically subscribed to messages published to your own ephemeral topic ID. The return value from SMQ_getMessage is either the complete frame payload data or the amount of data the SMQ client can buffer. You set the buffer and size when calling SMQ_constructor. We will not go into how to receive messages in fragments in this example since the messages we receive are small and will not be fragmented.

len =  SMQ_getMessage(smq, &msg);
if(len > 0)
{
   if(smq->tid == displayTid) /* topic "/m2m/led/display" */
   {
      // Send device info to the new display unit
   }
   else if(smq->tid == smq->clientTid) /* sent to our ephemeral tid */
   {
      // 1: Set LED with ID ledId to on or off.
      // 2: Publish to "/m2m/led/device", sub-topic "led"
   }
}

Example 9: receiving a message published to a subscribed topic.

SMQ::tid is set to the topic ID by the SMQ client stack. We can check this number and compare it with the topic IDs we are subscribed to. SMQ::clientTid is set to our own ephemeral topic ID; thus, we can find out if a peer published to our own ephemeral topic ID by comparing SMQ::tid and SMQ::clientTid. The browser(s) sends a message to the device's ephemeral topic ID when the browser wants the device to change an LED state to on or off. The device code will then set the LED to on or off and publish a message to the topic "/m2m/led/device". All browsers are subscribed to this topic and will update the user interface accordingly. You can test this feature by opening two browser windows. Clicking the LED in one browser window automatically updates the LED in the other browser window.

And this completes our introduction to the M2M LED SMQ version. You can download the complete C code as a ZIP file. Download, unpack, and study the source code file m2m-led.c.

Secure SMQ IoT Demo

The server side code and the web application is included in the Mako Server Tutorials. The C code can be downloaded from the SMQ source page. See the online SMQ documentation for more information.

Posted in SimpleMQ