Skip to content

Commit 3d75bc4

Browse files
committed
Update the Giga RPC/dual-core tutorial.
Add a section on using the RPC library with MicroPython. Signed-off-by: iabdalkader <[email protected]>
1 parent 68af80a commit 3d75bc4

File tree

1 file changed

+156
-9
lines changed
  • content/hardware/10.mega/boards/giga-r1-wifi/tutorials/giga-dual-core

1 file changed

+156
-9
lines changed

content/hardware/10.mega/boards/giga-r1-wifi/tutorials/giga-dual-core/giga-dual-core.md

+156-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ The M4 and M7 cores are programmed with separate sketches, using the same serial
2424
In this guide you will discover:
2525
- How to configure and program the M4/M7 cores and conventional approaches to do so.
2626
- How to boot the M4 core.
27-
- How to communicate between the cores via Remote Call Procedures (RPC).
27+
- How to communicate between the cores via Remote Procedure Call (RPC).
28+
- Using the RPC Library with MicroPython.
2829
- Useful examples based on the dual core & RPC features.
2930
- The `RPC` library API.
3031

@@ -194,7 +195,7 @@ void blink(int led, int delaySeconds) {
194195
- The `CM7_CPUID` flag that we compare with holds the value `0x00000003` (hexadecimal), or `3` (decimal).
195196
- It is also possible to use `CM4_CPUID` flag which holds the value `0x00000003`, or `1` (decimal).
196197

197-
## Remote Call Procedures (RPC)
198+
## Remote Procedure Call (RPC)
198199

199200
RPC is a method that allows programs to make requests to programs located elsewhere. It is based on the client-server model (also referred to as caller/callee), where the client makes a request to the server.
200201

@@ -226,7 +227,8 @@ RPC.bind("addFunction", addFunction);
226227
On the client side, it could look like this:
227228

228229
```arduino
229-
int x,y = 10;
230+
int x = 10;
231+
int y = 10;
230232
231233
RPC.call("addFunction", x, y);
232234
```
@@ -235,6 +237,103 @@ When `call()` is used, a request is sent, it is processed on the server side, an
235237

236238
![Communication between M7 and M4 core.](assets/rpc-m7-m4.png)
237239

240+
### Using the RPC Library with MicroPython
241+
242+
The `msgpackrpc` library provides the same functionality as the Arduino RPC library for MicroPython, i.e., it allows the binding of local functions or objects, starting the M4 core, and invoking remote calls from Python scripts. This library and its supporting features are enabled by default on all compatible Arduino boards starting with MicroPython release 1.23 and require no external dependencies to use. Additionally, the Arduino sketches presented in the examples section here require no changes to use with MicroPython. However, there are a few restrictions to using the RPC library with MicroPython. The first one is that MicroPython firmware always targets the main M7 core, consequently, Arduino sketches can only run on the M4 core. Additionally, only flash-based firmware, with the `1.5MB M7 + 0.5MB M4` flash partitioning scheme, is supported. While the `msgpackrpc` library does support loading firmware images to any address space, the firmware currently generated for the M4 core with an SDRAM target does not work. This issue may be fixed in future releases.
243+
244+
The following sections introduce the `msgpackrpc` library API and some use cases in detail.
245+
246+
#### The `msgpackrpc` Library
247+
248+
The `msgpackrpc` library is the RPC library's counterpart for MicroPython, and it provides the same functionality as the Arduino RPC library. The first steps to using the `msgpackrpc` library, are importing the module and creating a `MsgPackRPC` object:
249+
250+
```python
251+
import msgpackrpc
252+
253+
# Create a MsgPackRPC object.
254+
rpc = msgpackrpc.MsgPackRPC()
255+
```
256+
257+
The RPC object created above can then be used to bind Python callables, start the M4 core and invoke remote calls from MicroPython scripts.
258+
259+
#### Binding Python Functions, Callables and Objects
260+
261+
The next step is binding callables. Any Python callable (such as functions, bound methods, callable objects etc..) can be bound to a name and be made available to the remote core to call. The following example binds a function to the name `sub`:
262+
263+
```python
264+
def sub(a, b):
265+
return a - b
266+
267+
# Register a function to be called by the remote processor.
268+
rpc.bind("sub", sub)
269+
```
270+
271+
Similarly, an object's bound method can also be bound to a name. For example:
272+
273+
```python
274+
foo = Foo()
275+
rpc.bind("sub", foo.add)
276+
```
277+
278+
Both of those functions can be called in the same way from the Arduino sketch:
279+
280+
```arduino
281+
int res = RPC.call("sub", 2, 1).as<int>();
282+
```
283+
284+
Objects can also be bound to allow their methods to be called by the remote core. When an object is passed to `bind()`, all of its public methods (the ones that don't start with an `_`) are bound to their respective qualified names. For example, the following code binds the methods of an object of class `Foo`:
285+
286+
```python
287+
class Foo:
288+
def __init__(self):
289+
pass
290+
291+
def add(a, b):
292+
return a + b
293+
294+
def sub(a, b):
295+
return a - b
296+
297+
# Register an object of Foo
298+
rpc.bind("foo1", Foo())
299+
```
300+
301+
Now the object's methods can be invoked by the Arduino sketch using their fully qualified name, for example:
302+
303+
```arduino
304+
int res1 = RPC.call("foo1.add", 1, 2).as<int>();
305+
int res2 = RPC.call("foo1.sub", 2, 1).as<int>();
306+
```
307+
308+
#### Starting the M4 Core From Micropython
309+
310+
The next step is starting the M4 core by calling `MsgPackRPC.start()` with the firmware entry point as an argument:
311+
312+
```python
313+
# Start the remote processor and wait for it to be ready to communicate.
314+
rpc.start(firmware=0x08180000)
315+
```
316+
317+
This function will start the remote core (the M4), boot it from the specified firmware address, and wait for the core to be ready to communicate before it returns. The default arguments passed to this function are compatible with the Arduino RPC library and do not need to be changed for the purposes of this tutorial. Note that the firmware address used is a flash address, where the M4 firmware starts, and it's the same one used for the flash split of `1.5MB M7 + 0.5MB M4`.
318+
319+
#### Calling Remote Functions From Micropython
320+
321+
Once the M4 core is started, the `MsgPackRPC` object can be used to invoke its remote functions. Remote calls can be invoked synchronously, i.e., the call does not return until a response is received from the other side, or asynchronously. In this case, the call returns a `Future` object that must be joined at some point to read back the call's return value.
322+
323+
```python
324+
# Perform a synchronous call, which blocks until it returns.
325+
res = rpc.call("add", 1, 2)
326+
327+
# Perform an asynchronous call, which returns immediately with a Future object.
328+
f1 = rpc.call_async("add", 1, 2)
329+
330+
# The Future object returned above, must be joined at some point to get the results.
331+
print(f1.join())
332+
```
333+
334+
That covered most of the `msgpackrpc` library's API and use cases. For a complete example, see the [MicroPython RPC LED Example](#micropython-rpc-led). For more examples and applications, see the `msgpackrpc` [repository](https://github.com/arduino/arduino-lib-mpy/tree/main/lib/msgpackrpc).
335+
336+
238337
## RPC Examples
239338

240339
In this section, you will find a series of examples that is based on the `RPC` library.
@@ -251,12 +350,12 @@ The `Serial.print()` command only works on the **M7 core**. In order to print va
251350
#include <RPC.h>
252351
253352
void setup() {
254-
RPC.begin();
353+
RPC.begin();
255354
}
256355
257356
void loop() {
258-
RPC.println("Printed from M4 core");
259-
delay(1000);
357+
RPC.println("Printed from M4 core");
358+
delay(1000);
260359
}
261360
```
262361

@@ -266,8 +365,8 @@ delay(1000);
266365
#include <RPC.h>
267366
268367
void setup() {
269-
Serial.begin(9600);
270-
RPC.begin();
368+
Serial.begin(9600);
369+
RPC.begin();
271370
}
272371
273372
void loop() {
@@ -308,6 +407,7 @@ void setup() {
308407
}
309408
310409
void loop() {
410+
311411
}
312412
313413
/*
@@ -454,6 +554,53 @@ int servoMove(int angle) {
454554
}
455555
```
456556

557+
### MicroPython RPC LED
558+
559+
This example demonstrates how to use MicroPython (running on the M7 core) to remotely control an LED connected to the M4 core.
560+
561+
**M4 sketch:**
562+
563+
```arduino
564+
#include "RPC.h"
565+
566+
void led(bool on) {
567+
if (on) {
568+
digitalWrite(LEDG, LOW);
569+
} else {
570+
digitalWrite(LEDG, HIGH);
571+
}
572+
}
573+
574+
void setup() {
575+
RPC.bind("led", led);
576+
RPC.begin();
577+
}
578+
579+
void loop() {
580+
581+
}
582+
```
583+
584+
**M7 Python script:**
585+
586+
```python
587+
import time
588+
import msgpackrpc
589+
590+
# Create an RPC object
591+
rpc = msgpackrpc.MsgPackRPC()
592+
593+
# Start the remote processor.
594+
rpc.start(firmware=0x08180000)
595+
596+
# Toggle the LED every 500ms
597+
while True:
598+
rpc.call("led", True)
599+
time.sleep_ms(500)
600+
rpc.call("led", False)
601+
time.sleep_ms(500)
602+
```
603+
457604
## RPC Library API
458605

459606
The `RPC` library is based on the [rpclib](https://github.com/rpclib/rpclib) C++ library which provides a client and server implementation. In addition, it provides a method for communication between the M4 and M7 cores.
@@ -575,4 +722,4 @@ RPC.read();
575722
#### Returns
576723

577724
- The first available byte from the M4.
578-
- `-1` if there is none.
725+
- `-1` if there is none.

0 commit comments

Comments
 (0)