11 years ago.

Any working examples for he new mbed-rpc (or forks of it)?

Hi All,

I've previously used the old/original RPC code on LPC1768 & LPC11U24 without problems but now want to use the newer mbed library...if only to allow it to work on the new FRDM board as well.

This then means picking up the new mbed-rpc library (or a fork of it) - am I right?

If so, has anyone got this working yet & if so would they be prepared to share a very simple example (similar to the source code still shown on the original RPC page http://mbed.org/cookbook/Interfacing-Using-RPC )?

Thanks

Jez

Hey, what is your question exactly? I have been working a bit on mbed RPC. It should be working.

posted by Sarah Marsh 30 Jun 2015

Hi Sarah,

When I first posted this question, the RPC code had gone through some major changes & wasn't supported by the (previously working) examples, hence the question.

I've seen that you have been working on this a lot recently but I have not yet had chance to try your latest developments.

I will try to do so over the next few days & let you know how I get on!

It's good to see someone picking this up & driving it forward.

Best regards,

Jez

posted by Jez Cawley 01 Jul 2015

Great! Sorry to pick up an old thread, it just showed up on the feed as "updated." So, I wasn't sure if you had changed the question a bit.

Thanks!

Sarah

posted by Sarah Marsh 01 Jul 2015

5 Answers

8 years, 9 months ago.

Can anyone explain how RPC:call(); function works here. And the above code run in PC or in Target?

  1. /DigitalOut/new LED1 myled
  2. /myled/write 1

Commands provided for reference in the explanation.

RPC::call() will parse your string input by the "/" seperator. It splits it into an object(or class) name, method, and any extra arguments. It does this by looking for what is between the first "/"s and stores that as the name (potentially class or object). It then looks for what is between "/" and " " and store that as the method. Then it will parse the rest of the arguments by spaces, and those are put into an array of arguments, maximum size of 16.

All RPC classes, like RPCDigitalOut make a call to RPC() with the name of the object as an argument, *as well as* create objects of the type they represent - IE constructing an RPCDigitalOut also constructs a generic DigitalOut object called "o". As each call to RPC is made, a linked list of RPC objects is expanded within rpc.cpp, with the newest object as the head. Call will iterate over that list and see if it can find a match for the object name you are trying to operate on and one in the linked list. If it does, it will fetch a list of methods supported by that particular object, you can see these methods in RPCClasses.h. If it finds a matching method for the one you have passed, that RPC object will call the super function of the class it represents. IE RPCDigitialOut call to write will call DigitalOut's write, it will do this using that "o" object we talked about earlier.

Now, if in our previous search for object name, we DIDN'T find anything in the linked list by that name, we will see if the "name" is actually a class, like in the instance of command #1 above. If a class name is a supported RPC class, we will fetch the static methods that can operate on that class, in this instance, pretty much just "new".

In either case, if you don't actually *provide* a method, the reply generated by call will contain all possible methods for that class or object. If you provide an invalid object name, class, or method, call will return false.

If it succeeds in executing on some RPC object, call will return true, and the reply message will be populated with the method that was executed, IE, the reply would hold "new" for #1 and "write" for #2.

Hopefully this makes sense.

posted by Sarah Marsh 30 Jun 2015
8 years, 9 months ago.

Something interesting about the example program is that it waits until you have pressed the enter key to print out what you have written. If you use CoolTerm, I would suggest using the Send String functionality so that you can send everything at once and see what you are typing. I am working on an example that will print something back character by character, but it is a little finnicky.

Anyway, that example should work for now. Just try it by typing your command and pressing enter. You won't see anything on the terminal until you have pressed enter.

Jez Cawley
poster
11 years ago.

Is anyone using the new RPC classes out there?

I'd be grateful for any example using the new mbed RPC library, no matter how simple...or ideally something allowing the RPC over serial operation to be used.

If some of the mbed developers who updated the RPC library could even post something they used to test the new library with, that would be a good start...

Help please - my C++ is too rusty to work it out from the library source code without a little help.

Cheers,

Jez

Hi Jez, I'm in the same position. I'd like to use mbed RPC library with the FRDM-KL25Z device, but can't get any of the example code to work!

Stephen

posted by Stephen Marlowe 02 Apr 2013

Hi Stephen,

I've only had a little bit of time/success to go further with this...

Following seems to work - though the echo onto the terminal is not great...

You can still enter things like "/led/write 1" & "/led/write 0" to drive the LED/output state...

HTH

Jez

#include "mbed.h"
#include "mbed_rpc.h"

Serial pc(USBTX, USBRX);

char buf[RPC_MAX_STRING], outbuf[RPC_MAX_STRING];

void test(const char *input, const char *expected) {
    printf("> %s\n", input);
    bool result = RPC::call(input, outbuf);
    
    if (!result) {
        printf("[ERROR] The remote procedure call failed\n");
    } else if (strcmp(outbuf, expected) != 0) {
        printf("[ERROR] \"%s\" != \"%s\"\n", outbuf, expected);
    } else {
        printf("{%s}\n", outbuf);
    }
}

int main() {

    RPC::add_rpc_class<RpcDigitalOut>();

    RpcDigitalOut rpc_led(LED_RED, "led");

    // Some test code for now...
    test("/led/write 1", "");
    test("/led/read", "1");
    wait (1.0);
    test("/led/write 0", "");
    test("/led/read", "0");
    wait (1.0);
    test("/led/write 1", "");
    test("/led/read", "1");
    wait(0.5);

    // receive commands, and send back the responses
    while(1) {
        pc.gets(buf, 256);
        RPC::call(buf, outbuf); 
        pc.printf("%s\n", outbuf);
    }
}
posted by Jez Cawley 02 Apr 2013

Hi Jez,

Thank you for your prompt response and the code sample.

I can now receive your test message on my terminal. Well done!

It certainly makes more sense when you realize that they are using classes rather than function calls. It would have helped if they could have told us beforehand!

The only thing I can't get to work is controllong the LED from my terminal.

If I send "/led/write 1" (without quotes and a CR/LF at the end), the gree "comms" light flashes, but the LED doesn't turn-on. Am I doing something stupid?

Kind regards,

Stephen

posted by Stephen Marlowe 02 Apr 2013

Hi Stephen,

Driving the red LED via TeraTerm is working ok for me - but it's the /led/write 0 that turns the LED on & /led/write 1 that turns it off.

This does seem the other way round than on the other modules (1768 & 11U24).

Does this work for you?

I'd love to be able to get the creation of new objects working through the terminal as well...

Cheers,

Jez

posted by Jez Cawley 03 Apr 2013

Quote:

Driving the red LED via TeraTerm is working ok for me - but it's the /led/write 0 that turns the LED on & /led/write 1 that turns it off.

This does seem the other way round than on the other modules (1768 & 11U24).

This behaviour is as expected. The LEDs are connected differently on the KL25Z vs the 1768 & 11U24. Check the schematics for details.

posted by Wim Huiskamp 03 Apr 2013

Hello Wim,

Thanks for the info!

I've not yet had chance to look at the detailed hardware differences between the Freedom board & the NXP-based parts - but I'm hoping that we'll soon get support for the new Freedom board with the Cortex-M4 (K20).

Cheers,

Jez

posted by Jez Cawley 03 Apr 2013
7 years, 12 months ago.

I've been working hard to get the ADS1115 library to call I2C over RPC and to control the RPC via python with the KLM25Z. The implementation 'works', but it's kind of cludgy. One thing I've noticed is that every other RPC call returns a null value. I'm not sure what's causing this.

In this case, I had to modify the ADS1115 Hello World headers to instantiate at the different addresses for four adc breakouts and cloned the ADS1115 class to represent the four different addresses.

MBED_RPC_SERIAL and ADS1115 HELLO WORLD

#include "mbed.h"
#include "mbed.h"
#include "mbed_rpc.h"
#include "Adafruit_ADS1015.h"

RpcDigitalOut Blue(LED4,"Blue");
RpcDigitalOut Red(LED1, "Red");
RpcDigitalOut Green(LED2,"Green");
I2C i2c(PTE0, PTE1);
Serial pc(USBTX, USBRX);
int reading;
RPCVariable<int> rpcreading(&reading, "reading");
Adafruit_ADS1115GND adsgnd(&i2c);
Adafruit_ADS1115SCL adsscl(&i2c);
Adafruit_ADS1115VDD adsvdd(&i2c);
Adafruit_ADS1115SDA adssda(&i2c);

//Read Ground address
void ADSREAD23GND(Arguments *input, Reply *output);
RPCFunction rpcReadGND23(&ADSREAD23GND, "ADSREAD23GND");
void ADSREAD23GND(Arguments *input, Reply *output)   {
    
    reading = adsgnd.readADC_Differential_2_3();
    pc.printf("diff a2, a3: %d\r\n", reading);
}
void ADSREAD01GND(Arguments *input, Reply *output);
RPCFunction rpcReadGND01(&ADSREAD01GND, "ADSREAD01GND");
void ADSREAD01GND(Arguments *input, Reply *output)   {
    
    reading = adsgnd.readADC_Differential_0_1();
}

//Read SCL address
void ADSREAD23SCL(Arguments *input, Reply *output);
RPCFunction rpcReadSCL23(&ADSREAD23SCL, "ADSREAD23SCL");
void ADSREAD23SCL(Arguments *input, Reply *output)   {
    
   reading = adsscl.readADC_Differential_2_3();
}
void ADSREAD01SCL(Arguments *input, Reply *output);
RPCFunction rpcReadSCL01(&ADSREAD01SCL, "ADSREAD01SCL");
void ADSREAD01SCL(Arguments *input, Reply *output)   {
    
   reading = adsscl.readADC_Differential_0_1();
}

//Read VDD address
void ADSREAD23VDD(Arguments *input, Reply *output);
RPCFunction rpcReadVDD23(&ADSREAD23VDD, "ADSREAD23VDD");
void ADSREAD23VDD(Arguments *input, Reply *output)   {
    
   reading = adsvdd.readADC_Differential_2_3();
}
void ADSREAD01VDD(Arguments *input, Reply *output);
RPCFunction rpcReadVDD01(&ADSREAD01VDD, "ADSREAD01VDD");
void ADSREAD01VDD(Arguments *input, Reply *output)   {
    
   reading = adsvdd.readADC_Differential_0_1();
}
//Read SDA address
void ADSREAD23SDA(Arguments *input, Reply *output);
RPCFunction rpcReadSDA23(&ADSREAD23SDA, "ADSREAD23SDA");
void ADSREAD23SDA(Arguments *input, Reply *output)   {
    
    adssda.readADC_Differential_2_3();
}
void ADSREAD01SDA(Arguments *input, Reply *output);
RPCFunction rpcReadSDA01(&ADSREAD01SDA, "ADSREAD01SDA");
void ADSREAD01SDA(Arguments *input, Reply *output)   {
    
    reading = adssda.readADC_Differential_0_1();
}

      
int main() {
    //The mbed RPC classes are now wrapped to create an RPC enabled version - see RpcClasses.h so don't add to base class
    
    // receive commands, and send back the responses
    char buf[256], outbuf[256];
    while(1) {
        pc.gets(buf, 256);
        //Call the static call method on the RPC class
        RPC::call(buf, outbuf); 
        pc.printf("%s\n", outbuf);
    }
}

I'm using the mbedRPY.py script provided on this website. Make sure to chmod the /dev/ttyACM* to user before playing

RPC via interactive Python console

from mbedRPC import *
from time import sleep
import numpy as np

serdev = '/dev/ttyACM0'
mbed = SerialRPC(serdev, 9600)
x = DigitalOut(mbed, "Green")
y = DigitalOut(mbed, "Red")
z = DigitalOut(mbed, "Blue")
b = RPCFunction(mbed, "ADSREAD23GND")
c = RPCVariable(mbed,"reading")
d = []
# one may now run the desired adc function via b.run('').  One may save that value to a list with d.append(c.read())

#a quick test is in order - blink the lights in random colors and collect thermocouple data:
while True:
     x.write(np.random.randint(0,2))
     y.write(np.random.randint(0,2))
     z.write(np.random.randint(0,2))
     b.run('')
     d.append(c.read())
     print(d)
     sleep(np.random.randint(1,10))


that said, I end up with every other read/write cycle being null. Here's the output of 'd':

truncated output of above python while

'diff a2, a3: -4', '', 'diff a2, a3: -4', '', 'diff a2, a3: 0', '', 'diff a2, a3: 2', '', 'diff a2, a3: 0', '', 'diff a2, a3: -2', '', 'diff a2, a3: -2', ''']

So I'm not sure what's going on here. Is the RPC channel that slow or are my pull-up resistors too weak (2.5k)? If you have any advice, I sincerely appreciate it.

Photos of the setup follow: /media/uploads/klobas/2016-03-29_18.58.13.jpg /media/uploads/klobas/2016-03-29_18.54.49.jpg /media/uploads/klobas/2016-03-29_20.20.14.jpg

Please post this as a new question.

posted by Jan Jongboom 30 Mar 2016

well, I solved the issue, but I'll post this as a new question anyway with the answer, as it might help other users who aren't as adept with C++ out there. (it seems like 90% of the issues here are C++ issues anyway).

posted by Kloba Kloba 30 Mar 2016
8 years, 3 months ago.

Did anyone try to create a DigitalOut object dynamically from the Terminal? The Cookbook seems to suggest that by entering the following two lines in Terminal, one can create a new DigitalOut object and interact with it. " /DigitalOut/new LED1 myled1 /myled1/write 1 " I tried it with the FRDM KL25Z, but the LED1 didn't change at all.

By the way, I was able to use "myled/write 0 (or 1)" to turn on and off LED4 if main.h already has the line "RpcDigitalOut myled(LED4,"myled");".

Got that to work: Need to add the line "RPC::add_rpc_class<RpcDigitalOut>();" to RPC - Serial code in main to get the wiki page DigitalOut/new example to work, so that new DigitalOuts can be created. The basic mbed APIs were all setup in the old version of the library, but each one will need this line to use them in the new RPC library code.

posted by jim hamblen 15 Mar 2016