Remote device control using WebSocket callbacks

Remote device control using WebSocket callbacks

websocket_iconUntil now, there were two different ways of accessing Yoctopuce devices over the network: either by direct connection over a local network, or using the special HTTP callback mode which makes it possible to drive a Yoctopuce module sitting on a remote network behind a NAT filter. The HTTP callback method however had some limitations. Today, we are introducing a new connection method, named WebSocket callback, that brings all direct connection features to the callback scheme.


HTTP and WebSockets


The communication flow in a standard HTTP request is extremely simple: the client sends a request, and then listen for the server reply. It is not a real conversation, rather just a question and an answer. The HTTP client cannot respond to what the server says without restarting a new separate communication.

However there is a provision in the HTTP 1.1 standard for the client to request an upgrade of the communication protocol. One such protocol upgrade widely supported is called WebSockets, and is defined in RFC 6455. This upgrade transforms the simple text-based request/reply link into a bidirectional messaging link, that can send arbitrary data in both direction.

Upgrading an HTTP connection to WebSockets requires that both side support the upgrade. From now on, Yoctopuce networked devices, Yoctopuce VirtualHub and Yoctopuce libraries will support it. But in order to upgrade an HTTP callback connection into a WebSocket callback, you will also need to ensure that your web server supports WebSocket connection upgrade. This is well supported on Java and Node.JS. In PHP however there is currently no standard for connection upgrade.

Benefits of WebSockets


WebSockets provide access to a number of advanced features of Yoctopuce API that were unavailable in HTTP callback mode:

  • a WebSocket callback can browse and retrieve data from the sensors built-in data logger.
  • a WebSocket callback can use bidirectional communication functions with serial devices, for instance to execute MODBUS queries over a Yocto-RS485.
  • a WebSocket callback can use value change callbacks and timed report callbacks to receive real-time and averaged measures from sensors.
  • a WebSocket callback can keep long connections between device and servers and handle interactive behaviors, since API calls are made in real time.
  • A WebSocket callback can even perform a remote firmware upgrade.


Example in Java


Yoctopuce Java library leverages the standard Java API for WebSocket (JSR-356). There are a few other higher-level API for handling WebSockets, but JSR-356 is the most basic and most standard one. It is available in TomCat servers since version 7.

In order to use your the Yoctopuce Java library with WebSockets, you will need to include JavaEE in your project.

import com.yoctopuce.YoctoAPI.YAPIContext;
import com.yoctopuce.YoctoAPI.YAPI_Exception;
import com.yoctopuce.YoctoAPI.YModule;
import com.yoctopuce.YoctoAPI.YRelay;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/wscallback")
public class YServerWebsocket
{

  @OnOpen
  public void onOpen(final Session session)
  {
    // log onOpen for debug purpose
    System.out.println(session.getId() + " has open a connection");

    Thread thread = new Thread(new Runnable()
    {
      @Override
      public void run()
      {

        // since all connection use the same process create a private context
        YAPIContext yctx = new YAPIContext();
        try {
          // register the YoctoHub/VirtualHub that start the connection
          yctx.RegisterHubWebSocketCallback(session);

          // list all devices connected on this hub (only for debug propose)
          System.out.println("Device list:");
          YModule module = YModule.FirstModuleInContext(yctx);
          while (module != null) {
            System.out.println("   " + module.get_serialNumber()
              + " (" + module.get_productName() + ")");
            module = module.nextModule();
          }

          // play a bit with relay output :-)
          try {
              YRelay relay = YRelay.FirstRelayInContext(yctx);
              if (relay != null) {
                relay.set_state(YRelay.STATE_A);
                Thread.sleep(500);
                relay.set_state(YRelay.STATE_B);
                Thread.sleep(250);
                relay.set_state(YRelay.STATE_A);
                Thread.sleep(250);
                relay.set_state(YRelay.STATE_B);
                Thread.sleep(500);
                relay.set_state(YRelay.STATE_A);
                Thread.sleep(1000);
                relay.set_state(YRelay.STATE_B);
                Thread.sleep(500);
                relay.set_state(YRelay.STATE_A);
                Thread.sleep(1000);
                relay.set_state(YRelay.STATE_B);
              } else {
                  System.out.println("No Relay connected");
              }

            } catch (InterruptedException ignore) {}

        } catch (YAPI_Exception ex) {
          System.out.println(" error (" + ex.getLocalizedMessage() + ")");
          ex.printStackTrace();
        }
        // no not forget to FreeAPI to ensure that all pending operation
        // are finished and freed
        yctx.FreeAPI();
      }
    });
    thread.start();
  }
}



As you can see, there is one small difference in this code compared to the standard usage of Yoctopuce API. When enumerating connected devices, instead of calling FirstRelay we use FirstRelayInContext, passing a newly created object of class YAPIContext as parameter. This is because we can now have multiple long-lasting communications with several YoctoHubs. It is therefore useful to control which hubs we want to talk to: either we can isolate each of them in a separate context, or we can use a shared context where we can access multiple connected hubs simultaneously.

Example in Node.js


We also wrote a similar example using the new EcmaScript 2015 library introduced last week. In Node.js, we have chosen to leverage the WebSocket library named ws, because it is the most widely used and has almost no external dependency. You can use it on top of your favorite HTTP server backend. We wrote the example using a basic WebSocketServer, but ws works on top of Express as well for instance.

import { YAPI, YAPIContext, YFunction, YModule, YErrorMsg } from 'lib/yocto_api';

var WebSocketServer = System._nodeRequire('ws').Server;

async function WebSocketCallbackHandler(ws)
{
    console.log('Received a WebSocket request');

    // Code below starts the Yoctopuce library in WebSocket Callback mode
    // and interact with modules connected on the hub that made the callback
    let errmsg = new YErrorMsg();
    let yctx = new YAPIContext();
    if(await yctx.RegisterHubWebSocketCallback(ws, errmsg) != yapi.SUCCESS) {
        console.log('HTTP callback error: '+errmsg);
        yctx.FreeAPI();
        return;
    }

    // Display a list of modules on incoming hub to the Node.js console
    await yapi.UpdateDeviceList(errmsg);
    var module = YModule.FirstModuleInContext(yctx);
    while(module) {
        let msg = await module.get_serialNumber();
        msg += ' (' + (await module.get_productName()) + ')';
        console.log(msg);
        module = module.nextModule();
    }
    yctx.FreeAPI();
}

// Instantiate a simple HTTP server
var wss = new WebSocketServer({ port: 8044 });
wss.on('connection', WebSocketCallbackHandler);

console.log('Node.js WebSocket Callback server running at http://127.0.0.1:8044/');


Similarly to Java, we can now create individual YAPIContext objects for each connection. This avoids mixing hubs globally, as it was the case with our older Node.JS library. You can start this example using the following command line, executed at the root of the new library

jspm run example_nodejs/Prog-WebSocketCallback/demo



Using WebSockets for direct connections


The use of WebSockets is not limited to callbacks over a NAT filter. It is now also possible to use a WebSocket connection when doing a direct connection over a local network. For instance, you can use:

YAPI.RegisterHub("ws://192.168.1.177",errmsg);


This is of course completely transparent to the rest of the code, and all subsequent calls to the Yoctopuce API will work the same as before. The advantage of this technique is that it will use a persistent TCP connection for all queries rather than individual HTTP connections for each query. This can avoid the small latency that happens when establishing a new TCP connection over slower links such as WiFi networks, and even more when connecting over a VPN on a GSM link.

WebSockets for direct connections are currently available as preview in Java and EcmaScript, but will be released for all programming langages by the end of January.

Downloads links


You can already start experimenting with our new WebSocket API today on Java and Javascript. Here are the links to the preview code:

Don't deploy this code in production yet. We will make an official release by the end of January, when the code is more mature and properly tested. But we hope this preview will let you get ready for it !

Add a comment No comment yet Back to blog












Yoctopuce, get your stuff connected.