RPC Over Serial Communication in Embedded Systems Lab

slide1 n.w
1 / 37
Embed
Share

Remote Procedure Call (RPC) allows programs to execute subroutines on different computers without understanding network details. RPC is used in mbed for manipulating variables and functions through a host computer. This lab explores RPC over serial communication, custom RPC functions, and controlling devices using RPC. Learn how RPC facilitates seamless interaction between client and server programs in distributed computing.

  • RPC
  • Serial Communication
  • Embedded Systems
  • Lab
  • Distributed Computing

Uploaded on | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.

E N D

Presentation Transcript


  1. Chapter 8: RPC Over Serial Communication EE2405 Embedded System Lab Prepared by: Prof. Jing-Jia Liu

  2. What is RPC? Remote Procedure Call (RPC) allows a computer program to execute subroutines on another computer. It s generally used in networks of computing devices. In the case of mbed, you can manipulate variables and execute subroutines on the mbed by simply calling the name of the variable or function on the host computer through a terminal or a browser. Variables Functions Variables Functions USB Serial/Ethernet PC mbed Terminal (serial comm) Python code using serial RPC API objects RPCFunction

  3. What is RPC? Remote Procedure Call (RPC) is a protocol that one program can use to request a service from a program located in another computer on a network without having to understand the network's details. A procedure call is also sometimes known as a function call or a subroutine RPC uses the client-server model. The requesting program is a client and the service providing program is the server. Like a regular or local procedure call, an RPC is a synchronous operation requiring the requesting program to be suspended until the results of the remote procedure are returned. However, the use of lightweight processes or threads that share the same address space allows multiple RPCs to be performed concurrently. https://searchmicroservices.techtarget.com/definition/Remote-Procedure-Call-RPC

  4. What is RPC? (Wiki) In distributed computing, a remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network), which is coded as if it was a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction. That is, the programmer writes essentially the same code whether the subroutine is local to the executing program, or remote. This is a form of client server interaction (caller is client, executor is server), typically implemented via a request response message- passing system. RPCs are a form of inter-process communication (IPC), in that different processes have different address spaces: if on the same host machine, they have distinct virtual address spaces, even though the physical address space is the same; while if they are on different hosts, the physical address space is different. Many different (often incompatible) technologies have been used to implement the concept. https://en.wikipedia.org/wiki/Remote_procedure_call

  5. Lab Items RPC over Serial Call mbed RPC (RPC objects) on host console terminal via serial (directly type in RPC command string on console) Python RPC via Serial Call mbed RPC by host Python code via serial Custom RPC Function RPCFunction to create custom RPC function LCD Text Display Using RPC Host control (by Python code) mbed TextLCD display by RPCFunction Get Values of FXOS8700Q Using RPC Host get (by Python code) mbed FXOS8700Q readings by RPCFUnction 5

  6. Checking mbed RPC commands We can use the following commands to inspect the internal of mbed objects. Command Result Enter Lists all available RPC objects Lists all available methods that can be used on the object "/<object> RPC" /<Object name>/<Method name> <Arguments separated by spaces>" Executes the method with the inputted arguments These commands are represented as character strings. For example, a command of "/myled/write 0" (the format at above third row) will call "myled.write(0);" at mbed. https://os.mbed.com/teams/mbed/code/mbed-rpc/

  7. mbed RPC calling format with serial port A RPC call is sent through serial port to mbed board as a string in above format. The string is processed by the mbed with a calling to "RPC::call()" as follows: (rpc.h) RPC::call(buf, outbuf); The "buf" is the command input. And "outbuf" is any information send back from the RPC call. https://os.mbed.com/teams/mbed/code/mbed-rpc/

  8. RPC Library Import the following library $ mbed add https://gitlab.larc-nthu.net/ee2405_2019/mbed_rpc.git #include "mbed_rpc.h" in your mbed program: #include "mbed.h" #include mbed_rpc.h RpcDigitalOut myled RpcDigitalOut myled2 RpcDigitalOut myled3 myled( (LED1, ,"myled" myled2( (LED2, ,"myled2" myled3( (LED3, ,"myled3" "myled"); ); "myled2"); ); "myled3"); ); // //DigitalOut DigitalOut RpcDigitalOut RpcDigitalOut Function Usage DigitalOut write read operator = operator int() Create a DigitalOut connected to the specified pin Set the output, specified as 0 or 1 (int) Return the output setting, represented as 0 or 1 (int) A shorthand for write (overload operator) A shorthand for read (overload operator)

  9. RPC Over Serial Start VS code to edit main.cpp /*This example program has been updated to use the RPC implementation in the new mbed libraries . This example demonstrates using RPC over serial */ #include "mbed.h" #include mbed_rpc.h //Use the RPC enabled wrapped class - see RpcClasses.h for more info RpcDigitalOut myled RpcDigitalOut myled2 RpcDigitalOut myled3 Serial pc pc( (USBTX, , USBRX); ); myled( (LED1, ,"myled" myled2( (LED2, ,"myled2" myled3( (LED3, ,"myled3" "myled"); ); "myled2"); ); "myled3"); ); // // mbed mbed (RPC) object (RPC) object myled myled 9

  10. 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) { for(int i=0; i<27; i++ { // buf size 27 characters, contain RPC cmd buf[i] = pc.putc(pc.getc()); // echo back host data } //Call the static call method on the RPC class RPC::call(buf, outbuf); // execute RPC cmd in buf and result in outbuf pc.printf("%s\r\n", outbuf); // send result back to host terminal } } On host computer, use the command $ ls /dev/ttyACM* to find the device. You will see the following information. /dev/ttyACM0 /dev/ttyACM0 10 Connect to K66F using screen with $ sudo screen /dev/ttyACM0

  11. Use the command $ ls /dev/ttyACM* to find the device. You will see the following information. /dev/ttyACM0 /dev/ttyACM0 Connect to K66F using screen with $ sudo screen /dev/ttyACM0 Enter the following command. It will call myled.write(1) at K66F. /myled/write 1 /myled/write 1 /<Object name>/<Method name> <Arguments separated by spaces>" After you enter /myled/write 1 (14 chars), please keep entering 13 white sapces (13 chars) to satisfy the buffer size(27 chars). Note that now we can send "commands" to K66F from remote end of UART with built-in RPC setup. Note also that we can only control known mbed object names. Enter the following command. It will call myled.write(0) at K66F. /myled/write 0 /myled/write 0 When you enter /myled/write 0 (14 chars), please keep entering 13 white sapces (13 chars) to satisfy the buffer size(27 chars). 11

  12. 4.3 Python RPC via Serial Using Python to remote control K66F Write a Python program to control K66F mbed led. Copy the following codes into myled_write.py import import serial serial import import time time serdev= '/dev/ttyACM0' # use the device name you get from `ls /dev/ttyACM*` s=serial.Serial( (serdev) ) s.write("/myled/write 1 ") line=s.readline() # Read an echo string from K66F terminated with '\n' print print(line) time.sleep(1) s.write("/myled/write 0 ") line=s.readline() # Read an echo string from K66F terminated with '\n' print print(line) s.close() Note that we are actually sending exactly the same commands as we have done with "screen".

  13. 4.4 Custom RPC Function RPCFunction objects behave as containers of functions, so the compiler know these functions can be called from mbed RPC. RPCFunction has two parameters, a function pointer and a string: The function pointer is the actual function to be called, and the string denotes the name of the function. A called function have a fixed prototype: two char strings as parameters (input and output). The RPCInterface library of mbed provides several templates to access or output data through these char strings (examples are shown in the following).

  14. Custom RPC Function Start VS code to edit main.cpp mbed prog. for K66F #include "mbed.h" #include mbed_rpc.h /*This example program has been updated to use the RPC implementation in the new mbed libraries . This example demonstrates using RPC over serial */ RpcDigitalOut myled1(LED1,"myled1"); RpcDigitalOut myled2(LED2,"myled2"); RpcDigitalOut myled3(LED3,"myled3"); Serial pc(USBTX, USBRX); void void LEDControl LEDControl(Arguments (Arguments *in, RPCFunction RPCFunction rpcLED rpcLED(& double x, y; *in, Reply *out); //fixed prototype Reply *out); //fixed prototype (&LEDControl LEDControl, , LEDControl LEDControl ); // ); // 14

  15. 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, 20); //Call the static call method on the RPC class RPC::call(buf, outbuf); pc.printf("%s\r\n", outbuf); } } Use the command $ ls /dev/ttyACM* to find the device. You will see the following information. /dev/ttyACM0 /dev/ttyACM0 Connect to K66F using screen with $ sudo screen /dev/ttyACM0 15

  16. //Make sure the method takes in Arguments and Reply objects void LEDControl(Arguments *in, Reply *out) { bool success = true; x = in->getArg<double>(); y = in->getArg<double>(); //Have code here to call another RPC function char buffer[256], outbuf[256]; char strings[20]; int led = x; int on = y; int n = sprintf(strings, /myled%d/write %d , led, on); strcpy(buffer, strings); RPC::call(buffer, outbuf); if (success) { out->putData(buffer);} else {out->putData( Failed to execute LED control );} } 16

  17. RPC Command (sent from host side) RPC Command Function Lists "LEDControl myled3 myled2 myled1 RPC as Reply to RPC cmd 19 spaces /LEDControl/run 1 0 Open myled1(Red) /LEDControl/run 1 1 Close myled1(Red) /LEDControl/run 2 0 Open myled2(Green) /LEDControl/run 2 1 Close myled2(Green) /LEDControl/run 3 0 Open myled3(Blue) 17 /LEDControl/run 3 1 Close myled3(Blue)

  18. Using Python to remote control K66F led import import serial serial import import time time serdev= '/dev/ttyACM0' # use the device name you get from `ls /dev/ttyACM*` s=serial.Serial( (serdev) ) s.write( ") #19 spaces line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write("/LEDControl/run 1 1") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) 18

  19. s.write("/LEDControl/run 2 1") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write("/LEDControl/run 3 1") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write("/LEDControl/run 2 0") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) 19

  20. s.write("/LEDControl/run 3 0") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write("/LEDControl/run 1 0") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) s.close() 20

  21. If it works and the following massages will shows in the terminal LEDControl myled3 myled2 myled1 RPC LEDControl myled3 myled2 myled1 RPC /myled1/write 1 /myled1/write 1 /myled2/write 1 /myled2/write 1 /myled3/write 1 /myled3/write 1 /myled2/write 0 /myled2/write 0 /myled3/write 0 /myled3/write 0 /myled1/write 0 /myled1/write 0 21

  22. 4.5 LCD text display using RPC #include "mbed.h" #include "TextLCD.h" #include "mbed_rpc.h" void void doDisplay doDisplay( (Arguments*in, , Reply*out); ); void void doLocate doLocate( (Arguments*in, , Reply*out); ); RPCFunction rpcdisplay rpcdisplay( (&doDisplay, , "doDisplay" RPCFunction rpclocate rpclocate( (&doLocate, , "doLocate" // Host PC Communication channels Serial pc(USBTX, USBRX); // tx, rx // I2C Communication I2C i2c_lcd(D14,D15); // SDA, SCL TextLCD_I2C lcd(&i2c_lcd, 0x4E, TextLCD::LCD16x2); // I2C bus, PCF8574 Slaveaddress, LCD Type "doDisplay"); ); "doLocate"); ); 22

  23. int main() { lcd.setCursor(TextLCD::CurOff_BlkOn); char buf[256], outbuf[256]; while while(1) { pc.gets(buf, 20); //Call the static call method on the RPC class RPC::call( (buf, , outbuf); ); pc.printf("%s\ \r r\ \n n", outbuf); } } 23

  24. void doDisplay(Arguments*in, Reply*out) { const const char *tmp=in->getArg<const pc.printf("[str arg]%s", tmp); for for(int i= 0; tmp[i] != 0; i++) { lcd.putc(tmp[i]); } } const char*>(); void doLocate(Arguments*in, Reply*out) { int x=in->getArg<int>(); int y=in->getArg<int>(); lcd.locate(x,y); pc.printf("locate (col,row)=(%d,%d)", x, y); } 24

  25. RPC Command RPC Command Function Lists doLocate doLocate doDisplay RPC RPC to PC host display <arg> on LCD and echo back to PC host doDisplay 19 spaces /doDisplay/run <arg> /doLocate/run x y locate the LCD cursor to (x,y) 25

  26. Using Python to remote control K66F TextLCD import import serial serial import import time time serdev= '/dev/ttyACM0' # use the device name you get from `ls /dev/ttyACM*` s=serial.Serial( (serdev) ) s.write( ") #19 spaces line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write( /doDisplay/run IMR ") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) 26

  27. s.write(" /doLocate/run 8 0 ") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write(" /doDisplay/run WELL ") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write( ") line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) time.sleep(1) 27

  28. 4.6 Get value of FXOS8700Q using RPC #include "mbed.h" #include "mbed_rpc.h" #include "fsl_port.h" #include "fsl_gpio.h" #define UINT14_MAX 16383 // FXOS8700CQ I2C address #define FXOS8700CQ_SLAVE_ADDR0 (0x1E<<1) // with pins SA0=0, SA1=0 #define FXOS8700CQ_SLAVE_ADDR1 (0x1D<<1) // with pins SA0=1, SA1=0 #define FXOS8700CQ_SLAVE_ADDR2 (0x1C<<1) // with pins SA0=0, SA1=1 #define FXOS8700CQ_SLAVE_ADDR3 (0x1F<<1) // with pins SA0=1, SA1=1 28

  29. 4.6 Get value of FXOS8700Q using RPC // FXOS8700CQ internal register addresses #define FXOS8700Q_STATUS 0x00 #define FXOS8700Q_OUT_X_MSB 0x01 #define FXOS8700Q_OUT_Y_MSB 0x03 #define FXOS8700Q_OUT_Z_MSB 0x05 #define FXOS8700Q_M_OUT_X_MSB 0x33 #define FXOS8700Q_M_OUT_Y_MSB 0x35 #define FXOS8700Q_M_OUT_Z_MSB 0x37 #define FXOS8700Q_WHOAMI 0x0D #define FXOS8700Q_XYZ_DATA_CFG 0x0E #define FXOS8700Q_CTRL_REG1 0x2A #define FXOS8700Q_M_CTRL_REG1 0x5B #define FXOS8700Q_M_CTRL_REG2 0x5C #define FXOS8700Q_WHOAMI_VAL 0xC7 29

  30. 4.6 Get value of FXOS8700Q using RPC // FXOS8700CQ internal register addresses I2C i2c( PTD9,PTD8); Serial pc(USBTX, USBRX); int m_addr=FXOS8700CQ_SLAVE_ADDR1; void FXOS8700CQ_readRegs(int addr, uint8_t *data, int len); void FXOS8700CQ_writeRegs(uint8_t *data, int len); void void getAcc getAcc( (Arguments*in, , Reply*out); ); void void getAddr getAddr( (Arguments*in, , Reply*out); ); RPCFunction rpcAcc RPCFunction rpcAddr rpcAcc( (&getAcc, , "getAcc" rpcAddr( (&getAddr, , "getAddr" "getAcc"); ); "getAddr"); ); 30

  31. int main() { // pc.baud(115200); uint8_t data[2] ; // Enable the FXOS8700Q FXOS8700CQ_readRegs( FXOS8700Q_CTRL_REG1, &data[1], 1); data[1] |= 0x01; data[0] =FXOS8700Q_CTRL_REG1; FXOS8700CQ_writeRegs(data, 2); char buf[256], outbuf[256]; while while (true) { pc.gets(buf, 20); RPC::call(buf, outbuf); pc.printf("%s\ \r r\ \n n", outbuf); } } 31

  32. void getAcc(Arguments*in, Reply*out) { int16_t acc16; float t[3]; uint8_t res[6]; FXOS8700CQ_readRegs(FXOS8700Q_OUT_X_MSB, res, 6); acc16= (res[0] << 6) | (res[1] >> 2); if if (acc16>UINT14_MAX/2) acc16-=UINT14_MAX; t[0] = ((float)acc16) / 4096.0f; acc16= (res[2] << 6) | (res[3] >> 2); I If f (acc16>UINT14_MAX/2) acc16-=UINT14_MAX; t[1] = ((float)acc16) / 4096.0f; acc16= (res[4] << 6) | (res[5] >> 2); if if (acc16>UINT14_MAX/2) acc16-=UINT14_MAX; t[2] = ((float)acc16) / 4096.0f; pc.printf("FXOS8700Q ACC: X=%1.4f(%x%x) Y=%1.4f(%x%x) Z=%1.4f(%x%x)\ \r r",\ t[0], res[0], res[1],\ t[1], res[2], res[3],\ t[2], res[4], res[5]\ ); } 32

  33. void getAddr(Arguments*in, Reply*out) { uint8_t who_am_i, data[2]; FXOS8700CQ_readRegs(FXOS8700Q_WHOAMI, &who_am_i, 1); pc.printf("Here is %x\ \r r\ \n n", who_am_i); } void FXOS8700CQ_readRegs(int addr, uint8_t *data, int len) { char t=addr; i2c.write(m_addr, &t, 1, true); i2c.read(m_addr, (char *)data, len); } void FXOS8700CQ_writeRegs(uint8_t *data, int len) { i2c.write(m_addr, (char *)data, len); } 33

  34. RPC Command RPC Command Function 19 spaces /getAddr/run Lists return address of the FXOS8700Q /getAcc/run return accelerometer value 34

  35. Using Python to remote control K66F import import serial serial import import time time serdev= '/dev/ttyACM0' # use the device name you get from `ls /dev/ttyACM*` s=serial.Serial( (serdev) ) s.write( ") #19 spaces line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) s.write( /getAcc/run line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) ") 35

  36. s.write(/getAddr/run line=s.readline() # Read an echo string from K66F terminated with '\n' print(line) time.sleep(1) ") 36

  37. Demo & Checkpoints 1. Know how to send RPC commands with serial ports. 2. Improve the RPC function for LCD to allow sending a string with length over 6 character. 3. Create custom RPC functions that blink the red led and blue led respectively. 37

More Related Content