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¶
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.
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:
- MbedJSONRpc
- MbedJSONValue
- WebSocket
- Wifly
- EthernetIf. You must import the program as a library
- DNSResolver
- For the sensor: ADXL345
mbed_acc¶
- Schematics:
- 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:
-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¶
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 !
