Arduino GIGA R1 Dual-Core Guide: Run Code on the M7 and M4 Cores

Arduino GIGA R1 dual-core guide: program the 480 MHz Cortex-M7 and 240 MHz Cortex-M4 separately, partition Flash, boot the M4, exchange data with RPC, avoid peripheral conflicts and run Arduino or MicroPython across both cores.

The Arduino GIGA R1 WiFi contains two independent processor cores inside its STM32H747XI:

Unlike a normal dual-core microcontroller where one sketch automatically spreads work across both CPUs, the GIGA treats the cores much more like:

Each core can run its own sketch.

The two applications can execute in parallel and communicate through Arduino’s:

This makes it possible to place time-critical control on one core while the other handles networking, graphics, storage or application logic.

GIGA Dual-Core Architecture

The STM32H747 contains:

Core Role Maximum clock
Cortex-M7 Main processor 480 MHz
Cortex-M4 Co-processor 240 MHz

The M7 is the more powerful core and is the processor that boots first.

The M4 does not automatically start when the board powers up.

It must be started by the M7.

The Most Important Rule: M7 Boots M4

Arduino’s RPC library includes the M4 boot sequence.

The M7 sketch normally needs:

Calling:

on the M7 starts the M4 firmware.

Without that boot step, uploading a valid M4 sketch is not enough: the M4 remains disabled.

The M7 Cannot Be Disabled

The M7 is:

Arduino’s current dual-core guide does not provide a normal option to disable it and run only the M4.

You can give the M7 a minimal application, but the M7 still has to participate in the boot process.

You Need Two Sketches

For a normal dual-core application, create:

and treat them as two separate programs.

For example:

This is often easier to reason about than trying to place all application logic in one very large sketch.

Install the GIGA Board Package

Before using both cores, install the current Arduino GIGA board package in the Arduino IDE.

Once the GIGA R1 WiFi is selected, additional board menu options become available under:

including:

Flash Split Must Be Configured

The internal STM32H747 Flash is:

and Arduino lets you divide it between the two cores.

The current IDE provides three main Flash configurations.

Default

This is the normal default and effectively allocates the internal Flash to the M7.

Arduino’s guide notes that this default configuration is intended for programming the M7 only through the normal IDE dual-core workflow.

1.5 MB / 0.5 MB

This is useful when:

  • the M7 application is relatively large;
  • the M4 application is comparatively small.

1 MB / 1 MB

This is useful when both applications need similar program space.

To Upload an M4 Sketch, Allocate Flash to M4

Arduino’s current guide explicitly states that when programming the M4 through the IDE, use either:

or:

because the default does not provide the normal Flash allocation required for an IDE-uploaded M4 application.

Select the Target Core

Before uploading, open:

and choose:

The same physical USB programming connection is used for both.

Typical Upload Workflow

A practical workflow is:

After reset:

Uploading One Core Does Not Merge the Sketches

The programs remain separate.

If you upload a new M4 sketch:

while the M7 application remains its own image.

The same applies in reverse.

Identify Which Core Is Running

The RPC library provides:

which returns:

or:

This is useful if you intentionally upload a common codebase to both cores.

Example: Identify M7 or M4

In larger projects, separate M7/M4 sketches are often clearer, but runtime core identification can still be useful.

What Can the M4 Access?

Arduino currently documents M4 support for:

  • I2C;
  • SPI;
  • UART;
  • CAN;
  • DAC;
  • ADC;
  • Bluetooth LE through ArduinoBLE.

So the M4 is not merely a tiny background CPU.

It can directly control a large part of the GIGA hardware.

What the M4 Does Not Directly Support

Arduino currently lists these important limitations:

as unavailable directly from the M4 in the normal workflow.

The M7 is therefore the natural place for:

  • Wi-Fi;
  • cloud communication;
  • USB Serial Monitor output.

M4 Serial Output Must Normally Pass Through M7

If you write:

on M4 expecting the normal USB Serial Monitor, it will not behave like M7 USB serial.

The usual pattern is:

Simple M4 Serial Example

Upload this to the M4:

M7 USB Bridge Example

Upload this to the M7:

The data path becomes:

What Is RPC?

RPC means:

It allows one core to invoke a function implemented by the other core.

The basic model is:

RPC.bind()

Suppose the M4 owns a sensor.

The M4 can expose a function:

The name:

becomes callable from the other core.

RPC.call()

The M7 can request the value:

The M7 does not directly read the sensor in this architecture.

Instead:

RPC Calls Are Synchronous

This matters for real-time design.

Arduino describes a normal:

as synchronous.

The calling core waits until the remote operation returns.

So this:

can block the caller if:

  • the remote function takes a long time;
  • the other core is busy;
  • the remote code blocks on hardware.

Do Not Put Long Blocking Work Behind Every RPC Call

A better architecture is often:

For example:

This is much better than making M7 repeatedly call an RPC function for every motor-control pulse.

Stream Data Through RPC

The RPC object also behaves similarly to an Arduino Stream.

Useful methods include:

This is convenient for:

  • debug text;
  • status messages;
  • simple byte streams.

Do Not Let Both Cores Control the Same Pin

This is one of the most important GIGA dual-core rules.

The M7 and M4 share physical hardware.

If both try to drive:

for example, Arduino warns that pin priority is not guaranteed.

Do not assume:

Arduino’s documentation explicitly says access can effectively be random.

Assign Pin Ownership

A much safer architecture is:

The exact split does not matter.

What matters is that each hardware resource has one clear owner.

Do Not Use the Same Peripheral from Both Cores

The same rule applies to peripherals.

If the M7 starts a CAN application and the M4 then reconfigures the same CAN peripheral, the two programs can interfere.

Arduino explicitly warns against sharing the same:

  • pin;
  • bus;
  • peripheral;
  • hardware block;

without deliberate low-level coordination.

Peripheral Ownership Table

For a real project, create something like:

Resource Owner
Wi-Fi M7
USB Serial M7
Display M7
Motor PWM M4
Encoder M4
ADC sensors M4
CAN M4

This prevents accidental reinitialisation and timing conflicts.

A Good Dual-Core Architecture

For a machine controller:

M7

M4

RPC

This is where dual-core becomes genuinely useful rather than simply a specification.

Why Not Just Use Mbed Threads on M7?

The M7 can already run multiple threads under Mbed OS.

So dual-core is not necessary every time you want:

Use the M4 when you benefit from:

  • hardware isolation;
  • a separate timing domain;
  • keeping real-time control away from network/UI work;
  • separate firmware ownership;
  • a genuinely independent application loop.

Dual-Core Does Not Automatically Double Performance

Moving arbitrary functions to M4 does not automatically make the application:

in any useful simple sense.

Performance depends on:

  • whether tasks are parallel;
  • memory access;
  • peripheral conflicts;
  • RPC overhead;
  • synchronisation;
  • how work is divided.

Best Workloads for M4

The M4 is well suited to repetitive, isolated tasks such as:

  • reading sensors at fixed intervals;
  • PID loops;
  • motor control;
  • encoder decoding;
  • CAN processing;
  • audio/control housekeeping;
  • background monitoring.

Best Workloads for M7

The M7 is the natural home for:

  • Wi-Fi;
  • Arduino Cloud;
  • USB communication;
  • large displays;
  • graphics;
  • filesystems;
  • heavy numerical processing;
  • main application logic.

Common Mistake 1: Uploading M4 Without Changing Flash Split

If the board remains on:

the normal IDE workflow does not allocate internal Flash for an M4 sketch.

Select:

or:

Common Mistake 2: Forgetting to Change Target Core

The IDE does not magically know which sketch is intended for which processor.

Before every upload, verify:

Using filenames such as:

helps prevent mistakes.

Common Mistake 3: M4 Sketch Uploaded but Never Runs

The M4 needs to be booted by M7.

Make sure the M7 contains:

Common Mistake 4: Printing Serial Directly from M4

Normal USB Serial belongs to the M7 path.

For M4 diagnostics use:

and forward the data through M7.

Common Mistake 5: Both Cores Initialise the Same Bus

For example:

is not a clean way to share CAN.

Choose one owner and use RPC to exchange the information needed by the other core.

Common Mistake 6: Using RPC for High-Frequency Tight Loops

RPC is convenient, but it is not a substitute for local real-time execution.

Bad architecture:

Better architecture:

Run Arduino and MicroPython Together

One of GIGA’s more unusual capabilities is running:

and communicating between them through:

Current MicroPython RPC Restrictions

Arduino’s current dual-core guide documents several restrictions.

When using MicroPython on M7 with an Arduino M4 application:

and the supported Flash-based workflow uses:

Arduino also notes that the SDRAM-target M4 firmware path is not currently supported in that MicroPython RPC workflow.

MicroPython Can Boot the M4

On M7 MicroPython:

starts the M4 firmware at the documented Flash address for the 1.5 MB / 0.5 MB partition.

MicroPython Can Call M4 Functions

If the M4 binds:

MicroPython can call it through:

This creates interesting architectures where:

  • Python handles high-level experimentation;
  • M4 Arduino code handles deterministic hardware control.

RPC Can Work in Either Direction

Do not think of:

as a fixed requirement.

Either core can expose bound functions and either can initiate remote calls.

Choose the direction based on who owns the resource.

Example Architecture: Sensor Acquisition on M4

M4

M7

This cleanly keeps the measurement loop independent from network traffic.

Example Architecture: Motor Control on M4

M4 owns

M7 owns

RPC commands

This is an excellent use of the two cores because the motor loop continues even if the M7 is busy drawing graphics or handling a web request.

Example Architecture: Display on One Core

You can also move a display workload to one processor while the other handles control.

But the same ownership rule applies:

Do not let both cores initialise and update the same display controller independently.

Debugging Dual-Core Applications

Dual-core bugs can be harder than normal Arduino bugs because two programs are running simultaneously.

Useful techniques include:

  • blink a different RGB LED colour on each core during boot;
  • prefix debug messages with M7: or M4:;
  • keep a written peripheral ownership table;
  • test each core independently before enabling RPC;
  • add one RPC command at a time;
  • avoid dynamic resource ownership.

Give Each Core a Boot Signature

For example:

This immediately tells you whether:

  • both firmware images are present;
  • M7 successfully booted M4;
  • the correct core was uploaded.

Keep M4 Firmware Simple First

When starting a dual-core project, begin with:

Only after that works should you add:

  • CAN;
  • ADC;
  • motor control;
  • timers;
  • multiple threads.

Quick Setup Reference

RPC API Quick Reference

Final Recommendation

Do not use GIGA’s dual-core processor simply because two cores are available.

Use it when your project naturally separates into:

The most effective pattern is usually:

Keep one owner per hardware peripheral, avoid having both cores drive the same pin or bus, and remember that ordinary RPC calls are synchronous.

With those rules in place, the GIGA R1 WiFi behaves less like one complicated dual-core Arduino and more like two cooperating embedded controllers on the same chip.

For the full board mapping, see our Arduino GIGA R1 WiFi pinout guide. For a generational comparison with Arduino’s earlier large ARM board, see Arduino Due vs GIGA R1 WiFi.

Share your love