You are viewing an older revision! See the latest version

RPC over Websockets

Remote Procedure Call over Websockets

Information

This example does not use the official mbed networking stack

Introduction

Imagine that you have several Mbeds and you want to trigger the execution of a specific procedure or method on a specific Mbed from another one. How can you do that ?
The Remote Procedure Call (RPC) mechanism is a solution.

General Idea

To make the RPC possible, all Mbeds have to be connected on the same network. I have chosen to connect them to the Internet over a Websocket communication. If you are not familiar with Websockets, you can follow this tutorial to use them on your Mbed.

The sequence of an RPC operation: (let's suppose that you have two Mbeds: mbed_1 and mbed_2)

  • mbed_1 calls a local method with parameters for the non local one.
  • This call function packs the parameters, the name of the function to be executed,... into a message and sends it to mbed_2
  • mbed_2 receives this message, decodes it, executes the function and sends to mbed_1 a new message containing the result of the function execution.
  • mbed_1 receives the previous message and can handle the results of the non-local function.

    All exchanged messages are in JSON format.

Example of architecture

Architecture
On this diagram, you can distinguish two main elements:

  • Two different sub-networks. On each sub-networks, all Mbeds can share methods or execute a non-local one.
  • A RPC Websocket server which is responsible to manage all sub-networks and all messages exchanged. This server is written in Python and uses Tornado.

Information

You can take a look to this tutorial concerning the setting up of a such server but it's not necessary to follow this tutorial.



Method registering step

The first sub-network (at the top) explains the method registering step with the help of the register method.

Function signature

You can only register methods or procedures with the following signature:

void fn(MbedJSONValue& in, MbedJSONValue& out).

The MbedJSONValue type is explained in the next section of this document.

After this step, the client can call this method.

Call step

The second sub-network (at the bottom) is more interesting. It describes a call step. There are four different Mbeds:

  • mbed_acc where a method getAcc has been successfully registered (these method returns values from accelerometers connected to the Mbed)
  • mbed_light with a method getLight which returns the light level
  • mbed_press with a method getPress which returns the pressure value
  • mbed_client

mbed_client calls the function "call" where:

  • "getAcc" is the name of the distant function
  • "mbed_acc" is the name of the distant Mbed on the sub-network (where will be executed the method)
  • in represents the arguments to be passed to the distant method
  • out will be the results of the distant method execution

On this example, after the call step, mbed_client can handle accelerometers values returned by the method getAcc.

Connection to the Websocket server

Information

There is an existing Websocket server (on an Mbed server ;)). So you don't need to set up your own server.

ws

Type of parameters: MbedJSONValue type

In the previous section, in and out parameters are represented by a specific object: MbedJSONValue. Let's suppose that getAcc doesn't take argument but returns an array of three Integers (acceleration on x, y and z).

The code for this method can be (on mbed_acc):

    void getAcc(MbedJSONValue& in, MbedJSONValue& out) {
        
        //reading accelerometers values
        int readings[3] = {0, 0, 0};
        accelerometer.getOutput(readings);

        //store the previous values in the out parameter (the in parameter is ignored)
        out[0] = readings[0];
        out[1] = readings[1];
        out[2] = readings[2];
    }


And on mbed_client, there is something like:

        //in parameter dosn't need to be filled as getAcc ignores it
        MbedJSONValue in, out;

        //call the non-local method
        rpc.call("getAcc", "mbed_acc", in, out));

        //print accelerometers values stored in the out parameter
        printf("acc_x: %d\r\n", out[0].get<int>());
        printf("acc_y: %d\r\n", out[1].get<int>());
        printf("acc_z: %d\r\n", out[2].get<int>());

For more information on the MbedJSONValue type, please take a look to the MbedJSONValue description.

Demo

I have two Mbeds:

  • mbed_acc: One with an accelerometer and a wifi module. This Mbed will register a method getAcc.
  • mbed_client: Another with just an RJ45 Jack. This Mbed will call getAcc.

Libraries required

For this part you need to import these libraries:

mbed_acc

- Schematics:
/media/uploads/samux/_scaled_photo_wifi_schema.jpg

- Code:

#include "mbed.h"
#include "MbedJSONRpc.h"
#include "MbedJSONValue.h"
#include "Websocket.h"
#include "ADXL345.h"
#include "Wifly.h"

ADXL345 accelerometer(p5, p6, p7, p8);
Serial pc(USBTX, USBRX);

//wifi module
Wifly wifly(p9, p10, p21, "NETGEAR", "hello", true);

//websocket: configuration with sub-network = samux and mbed_id = mbed_acc 
Websocket webs("ws://sockets.mbed.org/rpc/samux/mbed_acc",&wifly);

//RPC object attached to the websocket server
MbedJSONRpc rpc(&webs);


//Acc class. The method getAcc of this class will be registered
class Acc {
public:
    Acc() {};
    void getAcc(MbedJSONValue& in, MbedJSONValue& out) {
        int readings[3] = {0, 0, 0};
        accelerometer.getOutput(readings);
        out[0] = readings[0];
        out[1] = readings[1];
        out[2] = readings[2];
    }
};

Acc acc;



int main() {
    
    RPC_TYPE t;
    
    //-------Accelerometers init---------//

    pc.printf("Starting ADXL345 test...\r\n");
    pc.printf("Device ID is: 0x%02x\r\n", accelerometer.getDevId());

    //Go into standby mode to configure the device.
    accelerometer.setPowerControl(0x00);

    //Full resolution, +/-16g, 4mg/LSB.
    accelerometer.setDataFormatControl(0x0B);

    //3.2kHz data rate.
    accelerometer.setDataRate(ADXL345_3200HZ);

    //Measurement mode.
    accelerometer.setPowerControl(0x08);

    //------------connection to the router and to the websocket server----------//
    while (1) {

        while (!wifly.Join())  //we connect to the network
            wifly.reset();

        if (!webs.connect())    //we connect to the server
            wifly.reset();
        else
            break;
    }

    //------------register getAcc---------------//
    if((t = rpc.registerMethod("getAcc", &acc, &Acc::getAcc)) == REGISTER_OK)
        printf("getAcc is registered\r\n");
    else
        printType(t);
    
    //wait for incoming CALL requests
    rpc.work();
}

This code is very simple:

  • Declare a class (here Acc) with a method having the good signature to be registered to the RPC server.
  • Connection to the router and to the RPC websocket server (with a Wifi module)
  • Register getAcc method
  • Wait for incoming CALL requests

    [Not converted]

mbed_client

- Schematics:

schematics

-Code:

#include "mbed.h"
#include "MbedJSONRpc.h"
#include "MbedJSONValue.h"
#include "Websocket.h"

Serial pc(USBTX, USBRX);

//websocket: configuration with sub-network = samux and mbed_id = mbed_acc 
Websocket webs("ws://sockets.mbed.org/rpc/samux/client");

//RPC object attached to the websocket server
MbedJSONRpc rpc(&webs);

int main() {

    RPC_TYPE t;
    
    //in: argument for the distant method (here empty)
    //out results of the distant method (accelerometers values)
    MbedJSONValue in, out;

    while (!webs.connect())
        pc.printf("cannot connect websocket, retrying\r\n");

    //CALL getAcc on mbed_acc
    if ((t = rpc.call("getAcc", "mbed_acc", in, out)) == CALL_OK) {
        printf("call success\r\n");
        printf("acc_x: %d\r\n", out[0].get<int>());
        printf("acc_y: %d\r\n", out[1].get<int>());
        printf("acc_z: %d\r\n", out[2].get<int>());
    } else
        printType(t);
}

This code is another time very simple:

  • Connection to the RPC Websocket server
  • Call getAcc on mbed_acc
  • If all is OK, print accelerometers values

    Import programRPC_mbed_client

    Example using MbedJSONRpc: call distant method

Results

/media/uploads/samux/rpc_demo.png
Et Voila, on mbed_client, we can handle accelerometers values!

Conclusion

You are now able to call every registered functions on your sub-network from an Mbed connected to the RPC Websocket server on the same sub-network.

You can control everything from everywhere !


All wikipages