Portenta Mid Carrier User Manual

Learn about the hardware and software features of the Arduino® Portenta Mid Carrier.

Overview

The user manual comprehensively explains the Arduino Portenta Mid Carrier, compiling all its features in one place for easy accessibility. It includes instructions for setting up, testing, and using its various onboard functions.

The Portenta Mid Carrier & Pro 4G Module performing Speed Test
The Portenta Mid Carrier & Pro 4G Module performing Speed Test

This manual can help users deploy the Portenta Mid Carrier industrial prototyping, swift development of machine vision applications, control systems for active mechanical designs, and many more applications.

Hardware and Software Requirements

Hardware Requirements

The Portenta Mid Carrier requires one of the SOM boards from the Portenta Family for operation:

The following accessories are needed:

  • USB-C® cable (either USB-C® to USB-A or USB-C® to USB-C®) (x1)
  • Wi-Fi® Access Point or Ethernet with Internet access (x1)
  • Compatible antennas like the Arduino Pro 4G Module Antennas Kit (x1)
  • Power cables: Wires with a cross-sectional area ranging from 0.82 mm² to 1.3 mm², corresponding to AWG sizes 18 to 16

Software Requirements

To use the Portenta Mid Carrier with a Portenta X8, please follow these guidelines:

For the smooth functioning of the Portenta Mid Carrier with the Portenta X8, it is crucial to have at least Linux image version > 746 on the Portenta X8. To update your board to the latest image, use the Portenta X8 Out-of-the-box method or manually flash it, downloading the most recent version from this link.

To enter Flashing Mode with the Portenta X8 and the Portenta Mid Carrier, please consult the BOOT DIP switch configuration instructions within the present user manual.

For using the Portenta Mid Carrier with a Portenta H7/C33:

Product Overview

Portenta Mid Carrier Overview
Portenta Mid Carrier Overview

The Portenta Mid Carrier enhances the functionality of the Portenta family boards by providing a user-friendly platform for rapid prototyping. Designed for versatility, the carrier is fully compatible with all the Portenta SOM, making it a robust choice for a wide array of development and prototyping applications. It conveniently brings all high-density signals within reach through dedicated headers.

Portenta Mid Carrier Stacked Form
Portenta Mid Carrier Stacked Form

This carrier provides access to various peripherals. It highlights a Mini PCIe connector for Cat.4 cellular modules, suitable for solutions requiring extensive network coverage. It also includes two CAN lines, Ethernet, microSD, USB, camera connectors for DVP and MIPI interfaces, and a Display Serial Interface (DSI) compatible with GIGA Display Shield. Additionally, it features dedicated debug pins and an RTC battery backup, further enhancing the development experience by simplifying processes.

Compatible Portenta boards
Compatible Portenta boards

Carrier Architecture Overview

The Portenta Mid Carrier is designed for the Portenta family of SOM boards and offers a versatile range of power supply options:

  • Power input of 5.0 V via the screw terminal
  • Power through USB-C® connection on Portenta SOM

Its extensive connectivity features include a USB-A port for peripheral devices, 1000 Mbit Base-T Ethernet, and various interfaces like SPI, I2C, I2S, and UART accessible through a dedicated male header. It also supports MIPI and ArduCam® camera connections. The MIPI connector is specifically designed for the Portenta X8.

The carrier integrates a microSD slot for data logging purposes. It has dedicated headers for diverse interface options, JTAG pins for debugging purposes, and a mini PCIe connector for Cat.4 cellular modules. The CAN bus can be used either via an onboard or external transceiver.

Portenta Mid Carrier board overview
Portenta Mid Carrier board overview

The Portenta Mid Carrier has the following characteristics:

  • Compatible SOM boards: The carrier is compatible with the Portenta X8 (ABX00049), Portenta H7 (ABX00042/ABX00045/ABX00046) and Portenta C33 (ABX00074).

  • Power management: The board can be powered up from different sources. The onboard screw terminal block allows a 5.0 V power supply to power the Portenta board, the carrier, and a mini PCIe module, if available.

    The USB-C® interface of the Portenta X8, H7, and C33 can supply the needed power to the board when a 4G Module is not connected. It will require a 5.0 V external power source when operating mPCIe modules to guarantee a consistent power supply for the Portenta SOM and its carrier.

The 5.0 V pin from the carrier's breakout male header can also power the board. The carrier can deliver a maximum current of 2.0 A and therefore wires of at least 0.82 mm2 / 18 AWG are recommended.

  • USB connectivity: A USB-A female connector is used for data logging and supports 2.0 standard rates.

  • Communication: The carrier supports Ethernet interface (X1) via RJ45 connector 1000 MBase-T connected to High-Density pins.

    The SPI (X2), I2C (x3), I2S (x1), and UART (x4) are accessible via dedicated breakout male header connectors (J14 - J15).

  • Two CAN bus ports are available, and CAN1 has an onboard transceiver controlled via CAN1 DIP switch (SW2). The CAN0 bus can be used with an external transceiver. CAN0 and CAN1 are available via the breakout pins header.

  • The MIPI interface (high-speed differential serial interface) is compatible only with the Portenta X8. Examples of supported devices include the OmniVision OV5647 and the Sony IMX219 sensors. For Portenta MCU SOM, the ArduCam® connector is also available, providing a Display Video Port (DVP) parallel interface.

  • Storage: The board has a microSD card slot and a USB-A female connector for data logging operation purposes.

  • Ethernet connectivity: The carrier offers a Gigabit Ethernet interface through an RJ45 connector 1000 MBase-T. The Ethernet properties can be configured with a dedicated 4-way DIP switch. More information can be found in the DIP switch configuration section.

  • Mini PCIe Connector: The carrier has a mini PCIe (Peripheral Component Interconnect Express) connector, i.e., an expansion card interface for modules compatible with its standard, such as Cat.4 modems as Arduino Pro 4G Modules.

  • PCIe Breakout Header: Besides the standard breakout header, the carrier also features a specific PCIe breakout header. This header provides access to each pin, enabling manual probing or the creation of external extensions for prototyping purposes.

  • GIGA Display Shield Connector: The Portenta Mid Carrier features a Display Serial Interface (DSI), which matches the Mobile Industry Processor Interface (MIPI) specifications, allowing it to connect displays that support MIPI DSI signals through its MIPI/DSI connector.

    The GIGA Display Shield is compatible with the Portenta Mid Carrier. It is connected through the MIPI/DSI connector found on the carrier. This setup supports widely used frameworks like LVGL and GFX, enhancing its utility for various display applications.

  • Breakout Header: The carrier eases access to all the high-density pins on the Portenta boards. It makes all pins associated with communication protocols, buses, and GPIOs easily accessible for various applications.

  • Screw terminal block: The terminal block allows power supply line feed for the carrier and bus ports. It consists of VIN 5.0 VDC (x1), VCC 3.3 V (x1), CANH (CAN1) (x1), CANL (CAN1) (x1), and GND (x2).

  • Debug interface: The carrier features an onboard 10x pin 1.27mm JTAG connector.

  • DIP switch: The carrier features different DIP switches, allowing for different profiles for dedicated features or functionalities. The DIP switches are ETH CENTER TAP, BOOT, and CAN1. More information can be found in the DIP switch configuration section.

Carrier Topology

Portenta Mid Carrier Topology
Portenta Mid Carrier Topology

ItemOnboard modules
J1 - J2High-Density connectors for Portenta boards
J3JTAG Interface
J4Screw Terminal Block with Onboard CAN Bus (CAN1) Transceiver
J5RTC Li-Po battery slot
J8Mini PCIe (Peripheral Component Interconnect Express) connector - (CELL Modules)
J9Mini PCI Express Power breakout header
J10MIPI camera connector - exclusive for Portenta X8
J11ArduCam® connector
J12MicroSD slot for data logging operations
J13USB-A female connector
J14 - J15Breakout headers
J16Mini PCI Express Breakout Header
J17Sub-series of Mini PCI Express Breakout Header
J18Gigabit Ethernet connector - RJ45
J19GIGA Display Shield connector (DSI)
SIM1Dedicated PCIe SIM Slot
SW1BOOT DIP Switch
SW2CAN Bus - CAN1 - DIP Switch
SW3ETH CENTER TAP DIP Switch

Portenta Mid Carrier Block Diagram
Portenta Mid Carrier Block Diagram

Pinout

Portenta Mid Carrier Pinout
Portenta Mid Carrier Pinout

Datasheet

The complete datasheet is available and downloadable as PDF from the link below:

Schematics

The complete schematics are available and downloadable as PDF from the link below:

STEP Files

The complete STEP files are available and downloadable from the link below:

Mechanical Information

In this section, you can find mechanical information about the Portenta Mid Carrier. The board's dimensions are all specified here, within top and bottom views, including the placements of the components onboard.

Suppose you desire to design and manufacture a custom mounting device or create a custom enclosure for your carrier. In that case, the following image shows the dimensions for the mounting holes and general board layout. The given dimensions are all in millimeters [mm].

You can also access the STEP files, available here.

Portenta Mid Carrier Dimension Outline
Portenta Mid Carrier Dimension Outline

For reference, the following diagram outlines the dimensions measured between the connectors.

Portenta Mid Carrier Connectors Dimension Outline
Portenta Mid Carrier Connectors Dimension Outline

First Use Of Your Portenta Mid Carrier

Stack The Carrier

The design of the Portenta Mid Carrier enables effortless stacking of your preferred Portenta board.

Portenta Mid Carrier Stack Orientation

The illustration below demonstrates the pairing of the Portenta Mid Carrier with Portenta boards via High-Density connectors.

Portenta Mid Carrier Stack Orientation
Portenta Mid Carrier Stack Orientation

Once the Portenta is mounted on the carrier, you can power the carrier and begin prototyping.

Power The Board

The Portenta Mid Carrier offers several options for power supply:

  • The most recommended method is using an external 5.0 V power supply connected to the VIN pin on the screw terminal block of the board. This approach ensures that the Portenta Mid Carrier, the SOM (System on Module), and any connected PCIe modules are adequately powered.

    To ensure the power demands are met, especially for the PMIC modules' external power, we recommend using cables that conform to appropriate electrical standards, such as ASTM B 258 standard, and can carry currents up to 2.0 A. Cables with a cross-sectional area ranging from 0.82 mm² to 1.3 mm², corresponding to AWG 18-16, should be adequate to manage 2.0 A of current.

    For detailed information about connection points, please refer to the board pinout section in the user manual. It is essential to ensure that the current provided meets the requirements of all components, as specified in the operating conditions table provided later.

  • An external 5.0 V power supply can also be used, connected to the 5.0 V pin on the dedicated breakout header.

    This method can power the Portenta Mid Carrier, the SOM, and any connected PCIe modules.

    For further information on this type of connection, consult the board pinout section in the user manual. Ensure the power supply’s maximum current adheres to the specifications of all components.

  • Using a USB-C® cable (not included) connected to the Portenta core board of your choice (like the Portenta X8, H7, or C33) also powers the selected core board and the Portenta Mid Carrier. When a mPCIe module is attached, a 5.0 V external power source will be needed to ensure all connected boards receive a steady power supply.

The Portenta Mid Carrier can deliver a maximum current of 2.0 A.

The following diagram provides an overview of the power connection options available on the Portenta Mid Carrier, illustrating the different ways it can be powered:

Portenta Mid Carrier Power Connection Overview
Portenta Mid Carrier Power Connection Overview

Please use a 5.0 V external power source when using an Arduino Pro 4G Module (EMEA / GNSS Global) or any other mPCIe modules due to their high power consumption. This is important for maintaining a stable power supply to the Portenta SOM and the carrier, particularly for extended periods of use.

The image below focuses on the terminal block area of the Portenta Mid Carrier. This close-up view highlights the specific position for connecting the 5 V power sources within the terminal block:

Portenta Mid Carrier Power Source within Terminal Block
Portenta Mid Carrier Power Source within Terminal Block

Refer to the power tree diagram to understand how power is distributed within the Portenta Mid Carrier. This diagram visually represents the allocation of power resources within the carrier:

Portenta Mid Carrier Power Tree Diagram
Portenta Mid Carrier Power Tree Diagram

To ensure the safety and longevity of the board, it is important to be aware of the Portenta Mid Carrier's operating conditions. The table provided below outlines the recommended operating conditions for the carrier:

ParameterMinTypMaxUnit
5.0 V from onboard screw terminal* of the Carrier-5.0-V
USB-C® input from the connected Portenta family board-5.0-V
+5 VDC from the carrier's onboard Breakout header-5.0-V
Current supplied by the carrier--2.0A
Ambient operating temperature-40-85°C

The onboard screw terminal powers both the carrier and the connected Portenta board.

To ensure the power demands are met, and connectivity is reliable, especially for the PMIC modules' external power, we recommend using cables that conform to the appropriate electrical standards, such as ASTM B 258 standard and can carry currents up to 2.0 A. Cables with a cross-sectional area ranging from 0.82 mm² to 1.3 mm², corresponding to AWG 18-16, should be adequate to manage 2.0 A of current.

Carrier Characteristics Highlight

The Portenta Mid Carrier's functionality varies depending on which Portenta family board it is paired with. The table below outlines these differences:

FeaturesPortenta X8Portenta H7Portenta C33
MIPI CameraCompatible--
ArduCam®-AvailableAvailable
GIGA Display Shield-Compatible (Touchscreen Enabled w/ External Pins)-
Ethernet1 Gbit10/100 Mbit10/100 Mbit
JTAG DebugAvailableAvailableAvailable
Storage ExpansionMicroSD slotMicroSD slotMicroSD slot
Mini PCIe SlotAvailableAvailableAvailable
CAN FD (CAN1 - Onboard Transceiver)AvailableAvailableAvailable
CAN FD (CAN0 - External Transceiver)Available-Available
USB-A SupportAvailableAvailableAvailable

This table provides a quick overview of the Portenta Mid Carrier's performance when paired with different Portenta boards. Each feature will be detailed in the subsequent section, and a guide on properly connecting the Portenta boards will be provided.

Hello World Carrier

Hello World Using Linux

Using Portenta X8 with Linux

To effectively use the Portenta Mid Carrier with the Portenta X8, align the High-Density connectors and the USB-C® port facing outside the carrier. The accompanying diagrams illustrate how to stack the board onto the carrier.

Portenta Mid Carrier with X8
Portenta Mid Carrier with X8

Portenta Mid Carrier with X8
Portenta Mid Carrier with X8

For optimal operation of the Portenta Mid Carrier with the Portenta X8, ensure you have at least version > 746 of the Linux image on the Portenta X8. The latest version can be downloaded [here]](https://downloads.arduino.cc/portentax8image/image-latest.tar.gz).

Hello World With Portenta X8 Shell

To verify the correct functioning of the Portenta Mid Carrier with the Portenta X8, we will use several Hello World examples with different approaches. These examples use Linux commands, Python® scripts, and the Arduino IDE to demonstrate various debugging methods and provide a basic introduction.

Starting with a Hello World example using Linux commands, we control GPIO through the Portenta X8's shell. Instructions for connecting to the Portenta X8 shell can be found here.

The commands below will help you set and control GPIO3, which can be linked to an LED or similar accessory.

Access the Portenta X8's shell using:

1adb shell
2sudo su -

Upon executing the

sudo su -
command, you will be prompted for a password:

The default password is

fio

These grants root user access and the corresponding environment settings like

$HOME
and
$PATH.

These commands provide elevated access to the Portenta X8's shell, enabling system configuration changes that require administrative rights.

Use the following command to activate the GPIO device in

/sys/class/
. Here, GPIO3 is identified as GPIO 163.

1echo 163 > /sys/class/gpio/export

Check available GPIO elements with:

1ls /sys/class/gpio

This lists all initialized GPIOs. To view details of GPIO 163 (or GPIO3), use:

1ls /sys/class/gpio/gpio163

Once the GPIO3 elements are exported, configure the GPIO using this command to set the I/O state of the pin. It can be set as Input (

in
) or Output (
out
).

1echo <I/O> >/sys/class/gpio/gpio163/direction

For our example, we set the pin as Output:

1echo out >/sys/class/gpio/gpio163/direction

To confirm the pin's setting, use:

1cat /sys/class/gpio/gpio163/direction

After setting the GPIO as an output, control its state by setting it to High or Low.

To set the pin to HIGH:

1echo 1 >/sys/class/gpio/gpio163/value

To set the pin to LOW:

1echo 0 >/sys/class/gpio/gpio163/value

To stop controlling the GPIO, you can use the following command to unexport it, ensuring it no longer appears in the userspace:

1echo 163 >/sys/class/gpio/unexport

To confirm that the specified GPIO has been properly unexported, you can use the following command:

1ls /sys/class/gpio

This step helps you to prevent unintentional modifications to the GPIO element configuration.

Hello World Using Linux and Python® Scripts

Instead of manually toggling the GPIO3 on the Portenta X8 via the command line, we can use a Python® script for automation and extended control logic.

Here is a compatible script for the ADB shell on the Portenta X8:

1#!/usr/bin/env python3
2import time
3
4class GPIOController:
5 def __init__(self, gpio_number):
6 self.gpio_number = gpio_number
7 self.gpio_path = f"/sys/class/gpio/gpio{gpio_number}/"
8
9 def export(self):
10 with open("/sys/class/gpio/export", "w") as f:
11 f.write(str(self.gpio_number))
12
13 def unexport(self):
14 with open("/sys/class/gpio/unexport", "w") as f:
15 f.write(str(self.gpio_number))
16
17 def set_direction(self, direction):
18 with open(f"{self.gpio_path}direction", "w") as f:
19 f.write(direction)
20
21 def read_direction(self):
22 with open(f"{self.gpio_path}direction", "r") as f:
23 return f.read().strip()
24
25 def set_value(self, value):
26 with open(f"{self.gpio_path}value", "w") as f:
27 f.write(str(value))
28
29 def read_value(self):
30 with open(f"{self.gpio_path}value", "r") as f:
31 return int(f.read().strip())
32
33def main():
34 print("============================================")
35 print("Hello World!")
36 print("============================================")
37
38 gpio = GPIOController(163)
39
40 # Export GPIO
41 gpio.export()
42
43 # Set as output
44 gpio.set_direction("out")
45 if gpio.read_direction() == "out":
46 print("GPIO set as output.")
47 print("GPIO3 blinking 20 times")
48
49
50 # Turn on (set to 1) and then off (set to 0)
51 for i in range(1,20,1):
52 gpio.set_value(1)
53 time.sleep(1)
54 gpio.set_value(0)
55 time.sleep(1)
56
57
58 print("GPIO Unexport")
59 gpio.unexport()
60 print("End of the program")
61 exit()
62
63if __name__ == "__main__":
64 main()

The script can be named

hello_world_python.py
, for example. To transfer it to the Portenta X8, use the following command on your computer's terminal:

1adb push hello_world_python.py /home/fio

This uploads the file to the

/home/fio
directory. Navigate to the directory via the ADB shell:

1cd /home/fio

Use the following command to run the script:

1python3 hello_world_python.py

When the script runs, the Portenta Mid Carrier's GPIO3 will begin to toggle its state.

For more information on how the board operates and to maximize its use with the Portenta Mid Carrier, refer to the Portenta X8 user manual.

The Portenta X8 works in a Linux environment based on the Yocto Linux distribution. It is recommendable to read how the Portenta X8 works in terms of Linux environment here.

Hello World Using Arduino

Using Portenta X8 / H7 / C33 with Arduino

The Portenta X8 can also operate within the Arduino environment and retains the same hardware setup as explained here. It can become a handy tool when opting for the Remote Procedure Call (RPC) mechanism within development. You can find more information on RPC in the "Data Exchange Between Python® on Linux & Arduino Sketch" tutorial.

The Portenta H7 and C33 boards have hardware setups identical to the Portenta X8. To mount them on the Mid Carrier, please align the High-Density connectors along with the USB-C® port orientation facing outside the carrier.

The diagrams below show how the Portenta H7 and C33 stack on the carrier:

  • Portenta H7

Portenta Mid Carrier with H7
Portenta Mid Carrier with H7

Portenta Mid Carrier with H7
Portenta Mid Carrier with H7

  • Portenta C33

Portenta Mid Carrier with C33
Portenta Mid Carrier with C33

Portenta Mid Carrier with C33
Portenta Mid Carrier with C33

Hello World With Arduino

In this section, you will learn how to use the Portenta X8, Portenta H7, or Portenta C33 with the Portenta Mid Carrier. You will interact with the GPIO3 pin within the Arduino environment.

Upon connecting a compatible Portenta board to the Portenta Mid Carrier, launch the Arduino IDE and set up the following code:

1// the setup function runs once when you press reset or power the board
2void setup() {
3 // Initialize the digital pin of the chosen SOM as an output
4 pinMode(<DIGITAL_PIN>, OUTPUT);
5}
6
7// the loop function runs over and over again forever
8void loop() {
9 digitalWrite(<DIGITAL_PIN>, HIGH); // turn the select pin on (HIGH is the voltage level)
10 delay(1000); // wait for a second
11 digitalWrite(<DIGITAL_PIN>, LOW); // turn the select pin off by making the voltage LOW
12 delay(1000); // wait for a second
13}

Make sure to substitute

<DIGITAL_PIN>
with the specific value corresponding to the GPIO3 for your Portenta board:

  • Portenta X8: PF_4
  • Portenta H7: PD_5
  • Portenta C33: 30

For instance, with the Portenta X8, the script will be:

1// the setup function runs once when you press reset or power the board
2void setup() {
3 // Initialize the digital pin of the chosen SOM as an output
4 pinMode(PF_4, OUTPUT);
5}
6
7// the loop function runs over and over again forever
8void loop() {
9 digitalWrite(PF_4, HIGH); // turn the select pin on (HIGH is the voltage level)
10 delay(1000); // wait for a second
11 digitalWrite(PF_4, LOW); // turn the select pin off by making the voltage LOW
12 delay(1000); // wait for a second
13}

Upon uploading the sketch to the board, GPIO3 will be driven to switch its state. The image below shows the anticipated outcome if an oscilloscope probe is connected to GPIO3, exemplifying a blinking LED or any actuator that imitates signal switching.

Portenta Mid Carrier Hello World Pin Switch
Portenta Mid Carrier Hello World Pin Switch

Please check out the following documentation below to better understand each board and optimize its use with the Portenta Mid Carrier:

Carrier Features and Interfaces

The carrier presents various features and interfaces to meet needs and applications. This section summarizes the key hardware interfaces, storage alternatives, and configuration methods incorporated into the carrier.

The following sub-sections offer a detailed examination of each feature's specifications, providing thorough information for the most effective use of the carrier.

Hardware Interfaces

The present sub-section delves into the fundamental hardware connection points and interfaces on the Portenta Mid Carrier. It covers a variety of components, from the mini PCIe interface to the connectors and camera interfaces.

High-Density Connectors

The Portenta X8, H7, and C33 models expand their capabilities using High-Density connectors. To fully grasp the functionality and layout of these connectors, it is recommended that you consult the detailed pinout documentation available for each Portenta model.

This documentation provides an in-depth view of the connectors, ensuring a comprehensive understanding of how they enhance the functionality of these devices.

Mini PCI Express Interface (J8)

Mini PCIe, short for Mini Peripheral Component Interconnect Express, is a compact version of the PCIe interface, predominantly used in laptops and small devices for adding functionalities like Wi-Fi®, Bluetooth®, and cellular modems.

These cards are significantly smaller than standard PCIe cards, typically measuring around 30 mm x 50.95 mm, and are designed to fit into the limited spaces of compact systems. They connect to a motherboard via a dedicated Mini PCIe slot, supporting PCIe and USB 2.0 interfaces. They are available in full-size and half-size variants.

In its portfolio, Arduino has two mini PCIe modules compatible with Portenta Mid Carrier, the Arduino Pro 4G Module, a Cat.4 modem mini PCIe card available in two variants: EMEA and GNSS Global.

Portenta Mid Carrier & Pro 4G Module
Portenta Mid Carrier & Pro 4G Module

The onboard Mini PCIe slot of the Portenta Mid Carrier has the following pin layout:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density PinPin Detail
1N/AConnected to pin 23 of J16 connector
2N/A+3V3 PCIE (Out)From PCIE dedicated high current 3V3 power supply
3N/AConnected to pin 21 of J16 connector
4N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
5N/AConnected to pin 19 of J16 connector
6N/AConnected to pin 17 of J16 connector
7N/AGPIO_1J2-48
8N/AConnected to pin C1 of SIM1 slot
9N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
10N/AConnected to pin C7 of SIM1 slot
11N/APCIE_CK_NJ2-19
12N/AConnected to pin C3 of SIM1 slot
13N/APCIE_CK_PJ2-17
14N/AConnected to pin C2 of SIM1 slot
15N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
16N/AConnected to pin 15 of J16 connector
17N/AConnected to pin 13 of J16 connector
18N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
19N/AConnected to pin 11 of J16 connector
20N/AGPIO_2J2-50
21N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54 J2-24, J2-33, J2-44, J2-57, J2-70
22N/APCIE_RSTJ2-21Connected to pin 9 of J16 connector
23N/APCIE_RX_NJ2-15
24N/A+3V3 PCIE (Out)From PCIE dedicated high current 3V3 power supply
25N/APCIE_RX_PJ2-13
26N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
27N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
28N/AConnected to pin 22 of J16 connector
29N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
30N/AI2C0_SCLJ1-46Connected to pin 28 of J15 connector
31N/APCIE_TX_NJ2-11
32N/AI2C0_SDAJ1-44Connected to pin 26 of J15 connector
33N/APCIE_TX_PJ2-9
34N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
35N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
36N/AUSB0_D_NJ1-28Connected to USB-A connector J13
37N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
38N/AUSB0_D_PJ1-26Connected to USB-A connector J13
39N/A+3V3 PCIE (Out)From PCIE dedicated high current 3V3 power supply
40N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
41N/A+3V3 PCIE (Out)From PCIE dedicated high current 3V3 power supply
42N/AConnected to pin 20 of J16 connector
43N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
44N/AConnected to pin 18 of J16 connector
45N/AConnected to pin 12 of J16 connector
46N/AConnected to pin 16 of J16 connector
47N/AConnected to pin 10 of J16 connector
48N/AConnected to pin 14 of J16 connector
49N/AConnected to pin 8 of J16 connector
50N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
51N/AConnected to pin 6 of J16 connector
52N/A+3V3 PCIE (Out)From PCIE dedicated high current 3V3 power supply

Mini PCIe Power Breakout Header (J9)

The Mini PCIe slot of the Portenta Mid Carrier has a dedicated breakout pin to control the power distribution of its interface.

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
13V3 PCIE+3V3 PCIE (Out)
2VCC+3V3 Portenta (Out)
33V3 BUCK+3V3 Buck (Out)
4PCIE ENABLEPCIE_ENGPIO_5J2-56
5NCNC
6GNDGround

To accommodate the power requirements and ensure reliable connectivity, jumper cables with appropriate electrical standards, such as ASTM B 258 standard, should be used to support a current of up to 2A. Jumper cables with a cross-sectional area of 0.82 mm² to 1.3 mm² (approximately equivalent to AWG 18-16) should support 2.0 A of current.

This precaution is necessary to prevent wire overheating and ensure reliable power transmission for the connected Mini PCIe-compatible module, such as Cat.4 modems. A minimum requirement to set the mini PCIe interface with the Portenta Mid Carrier consists of:

  • 3V3 PCIE pin connected to 3V3 BUCK pin
  • Properly inserted mini PCIe module, e.g., Pro 4G GNSS Module Global / Pro 4G EMEA Module

Please use a 5.0 V external power source when using an Arduino Pro 4G Module (EMEA / GNSS Global) or any other mPCIe modules due to their high power consumption. This is important for maintaining a stable power supply to the Portenta SOM and the carrier, particularly for extended periods of use.

Portenta Mid Carrier Mini PCIe Configuration
Portenta Mid Carrier Mini PCIe Configuration

The following animation shows the assembly process using a mini PCIe slot compatible with the Pro 4G module.

Portenta Mid Carrier & Pro 4G Module Assembly Animation

To immediately jump to the Pro 4G Module integration with the Portenta Mid Carrier, you can go to the Cat.4 Modem (Cellular Connectivity) subsection within the Network Connectivity section.

Accessing Mini PCIe Interface

It is possible to know if the compatible mini PCIe module has been correctly set up and detected using the Portenta X8. The Portenta Mid Carrier's mini PCIe lanes contain USB lines, and the Pro 4G Module is recognized as a USB module.

Thus, to verify that the Pro 4G Module has been correctly powered up and recognized by the Portenta X8 with the Portenta Mid Carrier, the following command is used instead of the

lspci
command:

1lsusb

The above command will list the devices that the Portenta X8 has recognized. The following image shows similar results if the Pro 4G Module is detected.

Portenta Mid Carrier Mini PCIe Module Listing
Portenta Mid Carrier Mini PCIe Module Listing

To learn about implementing the Pro 4G Module with the Portenta Mid Carrier immediately, you can jump to the Cat.4 Modem (Cellular Connectivity) section under the Network Connectivity section.

MIPI Camera Connector(J10)

The Portenta Mid Carrier integrates a dedicated camera connector, enabling the Portenta X8 to interface with MIPI cameras. However, it is important to note that the out-of-the-box Alpine shell does not support specific commands directly through the ADB shell.

In contrast, the Portenta H7 and C33 models do not support the MIPI interface and thus cannot use the camera connector.

Portenta Mid Carrier MIPI Camera
Portenta Mid Carrier MIPI Camera

The MIPI connector's pin distribution is as follows:

Pin numberPower NetPortenta HD Standard PinHigh-Density PinInterface
1GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
2CAM_D0_D0_NJ2-16
3CAM_D1_D0_PJ2-14
4GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
5CAM_D2_D1_NJ2-12
6CAM_D3_D1_PJ2-10
7GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
8CAM_CK_CK_NJ2-20
9CAM_VS_CK_PJ2-18
10GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
11GPIO_5J2-56
12NCNC
13I2C1_SCLJ1-45I2C 1 SCL
14I2C1_SDAJ1-43I2C 1 SDA
15+3V3_PORTENTAVCCJ2-23, J2-34, J2-43, J2-69

The Portenta Mid Carrier supports MIPI cameras when paired with the Portenta X8, allowing for a flexible connection to compatible cameras via a flex cable.

Portenta Mid Carrier MIPI Camera Connection Close-Up
Portenta Mid Carrier MIPI Camera Connection Close-Up

The following camera devices are compatible:

  • OmniVision OV5647 sensor (Raspberry Pi® Camera Module 1)
  • Sony IMX219 sensor (Raspberry Pi® Camera Module 2)

Portenta Mid Carrier MIPI Camera Mount

Using Linux

To initialize the Raspberry Pi Camera v1.3, based on the OV5647 CMOS sensor, follow these steps using the Portenta X8 environment, which will set environment variables and specify camera and board overlays:

1fw_setenv carrier_custom 1
1fw_setenv is_on_carrier yes
1fw_setenv carrier_name mid
1fw_setenv overlays 'ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_breakout_usbfs ov_carrier_mid_pcie_mini ov_carrier_mid_ov5647_camera_mipi'

Then, you can consider proceeding to start the camera to capture beginning with the runtime directory definition for

Wayland
and load the OV5647 camera module:

1export XDG_RUNTIME_DIR=/run # location of wayland-0 socket
2modprobe ov5647_mipi

Check the supported formats and controls of the connected video device:

1v4l2-ctl --list-formats-ext --device /dev/video0
2v4l2-ctl -d /dev/video0 --list-ctrls

Capture a single frame in JPEG format using GStreamer:

1export GST_DEBUG=3
2gst-top-1.0 gst-launch-1.0 -v v4l2src device=/dev/video0 num-buffers=1 ! "video/x-bayer, format=bggr, width=640, height=480, bpp=8, framerate=30/1" ! bayer2rgbneon reduce-bpp=t ! jpegenc ! filesink location=/tmp/test.jpg

This command allows to capture one frame and save the file as

/tmp/test.jpg
. To stream video at 30FPS for approximately 10 seconds using
GStreamer
, the following command is used:

1gst-top-1.0 gst-launch-1.0 -v v4l2src device=/dev/video0 num-buffers=300 ! "video/x-bayer, format=bggr, width=640, height=480, bpp=8, framerate=30/1" ! bayer2rgbneon reduce-bpp=t ! queue ! waylandsink

Following these instructions, you can capture and stream video from the Raspberry Pi Camera v1.3 with the OV5647 sensor.

For improved image quality, consider using a MIPI camera module with an integrated Image Signal Processor (ISP).

ArduCam® Compatibility Via Camera Socket (J11)

The Portenta Mid Carrier is also characterized for compatibility with the ArduCam® via the Digital Video Port (DVP) interface. Its compatibility can go with HM01B0, HM0360, GC2145, or OV7675 modules.

Portenta Mid Carrier ArduCam Connector
Portenta Mid Carrier ArduCam Connector

The connection is accomplished via the Camera socket (J11), and it is specified as follows:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1VCC+3V3 Portenta (Out)VCCJ2-23, J2-34, J2-43, J2-69
2GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
3SCL0I2C0_SCLJ1-46
4SDA0I2C0_SDAJ1-44
5VSYNCCAM_VS_CK_PJ2-18
6HREFCAM_HSJ2-22
7PCLKCAM_CK_CK_NJ2-20
8XCLKPWM_0J2-59
9DOUT7CAM_D7_D3_PJ2-2
10DOUT6CAM_D6_D3_NJ2-4
11DOUT5CAM_D5_D2_PJ2-6
12DOUT4CAM_D4_D2_NJ2-8
13DOUT3CAM_D3_D1_PJ2-10
14DOUT2CAM_D2_D1_NJ2-12
15DOUT1CAM_D1_D0_PJ2-14
16DOUT0CAM_D0_D0_NJ2-16
17PWRENABLEGPIO_3J2-52
18PWDNGPIO_4J2-54
19PWRENABLEGPIO_3J2-52
20PWDNGPIO_4J2-54

The following image shows the camera socket close-up of the Portenta Mid Carrier with an ArduCam® module that may be used to stack on the carrier.

Portenta Mid Carrier ArduCam Connection Close-Up
Portenta Mid Carrier ArduCam Connection Close-Up

As well as the following clip shows Arducam-F Shield V2 Camera module shield with OV2640, which is compatible with the Portenta Mid Carrier when paired with the Portenta H7 and C33.

Portenta Mid Carrier ArduCam Connection Close-Up

Using Arduino

The arducam_dvp library provides an example that supports three types of camera models: OV7670, Himax HM0360, Himax HM01B0, and GC2145. It captures pixel data and stores it in a frame buffer. These stored frames can subsequently be accessed continuously for further processing. It is compatible with the Portenta H7, and the CameraCaptureRawBytes example looks as follows:

1#include "arducam_dvp.h"
2
3#define ARDUCAM_CAMERA_HM01B0
4
5#ifdef ARDUCAM_CAMERA_HM01B0
6 #include "Himax_HM01B0/himax.h"
7 HM01B0 himax;
8 Camera cam(himax);
9 #define IMAGE_MODE CAMERA_GRAYSCALE
10#elif defined(ARDUCAM_CAMERA_HM0360)
11 #include "Himax_HM0360/hm0360.h"
12 HM0360 himax;
13 Camera cam(himax);
14 #define IMAGE_MODE CAMERA_GRAYSCALE
15#elif defined(ARDUCAM_CAMERA_OV767X)
16 #include "OV7670/ov767x.h"
17 // OV7670 ov767x;
18 OV7675 ov767x;
19 Camera cam(ov767x);
20 #define IMAGE_MODE CAMERA_RGB565
21#elif defined(ARDUCAM_CAMERA_GC2145)
22 #include "GC2145/gc2145.h"
23 GC2145 galaxyCore;
24 Camera cam(galaxyCore);
25 #define IMAGE_MODE CAMERA_RGB565
26#endif
27
28FrameBuffer fb;
29
30void blinkLED(uint32_t count = 0xFFFFFFFF,uint32_t t_delay = 50)
31{
32 while (count--)
33 {
34 digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level)
35 delay(t_delay); // wait for a second
36 digitalWrite(LED_BUILTIN, HIGH); // turn the LED off by making the voltage LOW
37 delay(t_delay); // wait for a second
38 }
39}
40
41
42void setup()
43{
44 pinMode(LED_BUILTIN, OUTPUT);
45 // Init the cam QVGA, 30FPS
46 if (!cam.begin(CAMERA_R320x240, IMAGE_MODE, 30))
47 {
48 blinkLED();
49 }
50 blinkLED(5);
51 Serial.begin(921600);
52}
53
54void loop()
55{
56 if(Serial.read() != 0x01)
57 {
58 blinkLED(1,10);
59 return;
60 }
61 // Grab frame and write to serial
62 if (cam.grabFrame(fb, 3000) == 0)
63 {
64 Serial.write(fb.getBuffer(), cam.frameSize());
65 }
66 else
67 {
68 blinkLED(20,100);
69 delay(1000);
70 }
71}

For the Portenta C33, given it has a different architecture, it is possible to navigate using the official ArduCam® source known as Arducam_Mega library.

GIGA Display Shield Connector (J19)

The Arduino GIGA Display Shield is an add-on that can be used with the Portenta Mid Carrier, offering an 800x480 display with touch support. The display shield can be connected via MIPI DSI (Display Serial Interface).

Portenta Mid Carrier GIGA Display Connector
Portenta Mid Carrier GIGA Display Connector

The shield also features a camera connector, built-in IMU, microphone, and an RGB pixel. The video display connector (J19) of the carrier is specified in the following table:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1DSI D1NDSI_D1_NJ1-12
2DSI D1PDSI_D1_PJ1-10
3GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
4GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
5DSI CKNDSI_CK_NJ1-20
6DSI CKPDSI_CK_PJ1-18
7GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
8GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
9DSI D0NDSI_D0_NJ1-16
10DSI D0PDSI_D0_PJ1-14
11GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
12GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
13DSI D2NDSI_D2_NJ1-8
14DSI D2PDSI_D2_PJ1-6
15GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
16GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
17DSI D3NDSI_D3_NJ1-4
18DSI D3PDSI_D3_PJ1-2
19NCNCNC
20NCNCNC
21VIN+5V (In/Out)VINJ1-21, J1-32, J1-41, J1-48
22GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
23VCC+3V3 Portenta (Out)VCCJ2-23, J2-34, J2-43, J2-69
24VIN+5V (In/Out)VINJ1-21, J1-32, J1-41, J1-48

The MIPI DSI (Mobile Industry Processor Interface Display Serial Interface) connector provides a high-speed interface that supports complex display functionalities like high resolutions and color depths. Its design is suitable for compact and mobile devices, making it ideal for applications where space and power efficiency are critical.

The following image shows the orientation of the connection between the GIGA Display Shield and the Portenta Mid Carrier.

Portenta Mid Carrier GIGA Display Shield Connection Close-Up
Portenta Mid Carrier GIGA Display Shield Connection Close-Up

For a more detailed connection orientation with a brief pinout designation, the following diagram might help you understand how to stack the GIGA Display Shield to the Portenta Mid Carrier.

Portenta Mid Carrier GIGA Display Shield Connection Close-Up
Portenta Mid Carrier GIGA Display Shield Connection Close-Up

Once you have aligned and connected the Portenta Mid Carrier with the GIGA Display Shield, you should have a similar setup as in the following image:

Portenta Mid Carrier & GIGA Display Shield Connected
Portenta Mid Carrier & GIGA Display Shield Connected

Using Linux

To review the current device tree overlay configurations, which are crucial for hardware feature management and system customization, to interface with the GIGA Display Shield, use the following command:

1fw_printenv overlays

This command invokes the

fw_printenv
utility to display overlays from the U-Boot firmware settings. Overlays are instrumental in defining modifications or additions to the device's hardware configuration without altering the base device tree.

This can include enabling additional peripherals, configuring pin mappings, or activating specific hardware features, providing a flexible approach to hardware customization. From this, the following overlay should be available:

1ov_carrier_mid_dsi_panel

If it is not present, the following steps will help you to set the needed overlays to use the GIGA Display Shield. Access the docker container named x8-devel with the following command:

1docker exec -it x8-devel sh

This command uses docker exec to begin a new shell session inside the running x8-devel container. The

-it
flags ensure an interactive terminal session, providing direct command execution within the container's environment. This is particularly useful for development purposes, enabling code editing, process monitoring, or debugging tasks within the isolated context of the container.

We need to search for tenta_runner Python script, and the following command can help to locate the script:

1find / -name *.py

Starting from the root directory, this command recursively searches for files ending in

.py
, indicating Python scripts. This search aids in locating Python-based utilities, scripts, or applications scattered across the system.

Having located the tenta_runner.py, navigate to the directory using the following command:

1cd /root/examples/tenta-config

Run the tenta_runner.py script using the following command:

1python tenta_runner.py

The script will bring up a GUI within the

tenta
framework. When the tenta-config window is up, please select Portenta Mid Carrier.

Portenta Mid Carrier GIGA Display Configuration - Main
Portenta Mid Carrier GIGA Display Configuration - Main

It will then show a list of available options that can be executed within the Portenta Mid Carrier platform. Here, the Enable alternative overlays option will be selected.

Portenta Mid Carrier GIGA Display Configuration - Overlays
Portenta Mid Carrier GIGA Display Configuration - Overlays

Because the GIGA Display Shield will be used, Removes video output on USB-C enables video output on DSI connector configuration will be selected. This configuration will disable video output via the USB-C connector and switch the video output to be available to the DSI connector.

Portenta Mid Carrier GIGA Display Configuration - DSI Settings
Portenta Mid Carrier GIGA Display Configuration - DSI Settings

Once selected, it will prompt a message showing differences that overlays will have after the change.

Portenta Mid Carrier GIGA Display Configuration - Applied Changes
Portenta Mid Carrier GIGA Display Configuration - Applied Changes

Proceed to accept with Ok, and the device will now be ready with the overlays to enable video output on the DSI connector.

Portenta Mid Carrier GIGA Display Configuration - Changes Set
Portenta Mid Carrier GIGA Display Configuration - Changes Set

We can now observe if the GIGA Display Shield is powered on and ready for use, having the shield connected to the Portenta Mid Carrier.

Using Arduino IDEs

With the Portenta H7, it is possible to use the following example:

1/*
2 ArduinoLogo
3
4 created 17 Apr 2023
5 by Leonardo Cavagnis
6*/
7
8#include "Arduino_H7_Video.h"
9#include "ArduinoGraphics.h"
10
11#include "img_arduinologo.h"
12// Alternatively, any raw RGB565 image can be included on demand using this macro
13// Online image converter: https://lvgl.io/tools/imageconverter (Output format: Binary RGB565)
14/*
15#define INCBIN_PREFIX
16#include "incbin.h"
17INCBIN(test, "/home/user/Downloads/test.bin");
18*/
19
20Arduino_H7_Video Display(800, 480, GigaDisplayShield);
21//Arduino_H7_Video Display(1024, 768, USBCVideo);
22
23Image img_arduinologo(ENCODING_RGB16, (uint8_t *) texture_raw, 300, 300);
24
25void setup() {
26 Display.begin();
27
28 Display.beginDraw();
29 Display.image(img_arduinologo, (Display.width() - img_arduinologo.width())/2, (Display.height() - img_arduinologo.height())/2);
30 Display.endDraw();
31}
32
33void loop() { }

It will require the ArduinoGraphics library installed to compile the code without any issues.

The example can be found within the Arduino IDE, and it is located under File -> Examples -> Portenta_H7_Video. The name of the example is ArduinoLogo.

Once the example has been compiled and uploaded to the Portenta H7, you will be able to see the Arduino logo drawn on the GIGA Display Shield.

CAN FD (Onboard Transceiver)

The Portenta Mid Carrier is equipped with a CAN bus port that connects to a screw terminal block. This bus incorporates the TJA1049 module. This high-speed CAN FD transceiver is integral to the Portenta Mid Carrier.

The TJA1049 module complies with various standards, including ISO 11898-2:2016, SAE J2284-1, and SAE J2284-5. These standards pertain to the CAN physical layer, ensuring reliable communication, especially during the fast phase of CAN FD.

Portenta Mid Carrier CAN Interface
Portenta Mid Carrier CAN Interface

Since CAN FD is accessible within the screw terminal block, we have highlighted the CAN bus ports within the screw terminal block pinout for reference. It is important to know that the onboard transceiver is attached for CAN1. The CAN bus port is controlled via the DIP switch for CANH and CANL lines. The following table shows the Screw Terminal Block (J4) with CAN1 Bus that is connected to the onboard transceiver of the Portenta Mid Carrier:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1VIN+5V (In/Out)VINJ1-21, J1-32, J1-41, J1-48
2GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
3VCC+3V3 Portenta (Out)VCCJ2-23, J2-34, J2-43, J2-69
4GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
5CANHJ1-49 (Through U2)
6CANLJ1-51 (Through U2)

For stable CAN bus communication, it is recommended to install a 120 Ω termination resistor between CANH and CANL lines.

For more information on implementing the CAN Bus protocol, you can go directly to the CAN Bus section in the Communication section.

CAN FD (External Transceiver)

In addition to the onboard TJA1049 transceiver, the Portenta Mid Carrier allows for the integration of external CAN FD transceivers. This flexibility is crucial for applications requiring multiple CAN networks or specific transceiver characteristics not provided by the onboard module.

Integrating an external CAN FD transceiver involves connecting it to the Portenta Mid Carrier's I/O ports. Ensuring the external transceiver is compatible with the CAN FD protocol and adheres to relevant standards such as ISO 11898-2:2016.

The connection typically involves linking the transceiver's CANH and CANL lines to the respective pins (CAN0 TX/RX) on the Mid Carrier. Power and ground connections must be established according to the transceiver's specifications.

Some technical considerations must be taken into account as well:

  • Compatibility: Ensure the external transceiver is compatible with the Portenta Mid Carrier's voltage levels and pin configurations.

  • Termination Resistors: Like the onboard transceiver, it is recommended to use a 120 Ω termination resistor between the CANH and CANL lines of the external transceiver to ensure signal integrity.

  • Software Configuration: Depending on the external transceiver used, software configuration might be necessary to establish communication between the Portenta Mid Carrier and the external module.

    This could involve setting up the correct CAN baud rate, configuring the CAN controller, and defining message filters.

There are some advantages to using an external transceiver:

  • Customization: Users can select a transceiver with specific features such as higher fault tolerance, different voltage levels, or advanced diagnostics capabilities.

  • Enhanced Performance: Some external transceivers might offer better performance regarding data rates or reliability in specific environmental conditions.

  • Redundancy and Multiple Networks: External transceivers provide an effective solution for systems that require redundancy or need to connect to multiple CAN networks simultaneously.

Using an external CAN FD transceiver with the Portenta Mid Carrier makes it possible to customize the CAN network setup to specific requirements, ensuring both flexibility and robust application performance.

For detailed information about the CAN Bus protocol implementation, please refer to the CAN Bus section in the Communication section.

Storage Options

The Portenta Mid Carrier enhances its capabilities by offering storage options for data logging operations, allowing for effective data storage management. This sub-section provides insights into the onboard storage options and how to access and use them.

MicroSD Storage (J12)

The carrier features a microSD card slot, offering the option to expand storage capacity significantly. This is particularly useful for handling large volumes of data, such as sensor logs or onboard computer registries.

Portenta Mid Carrier microSD Expansion Slot
Portenta Mid Carrier microSD Expansion Slot

The detailed connector designation for the microSD slot is as follows:

Pin numberSilkscreenPower NetPortenta HD Standard PinHigh-Density Pin
1N/ASDC_D2J1-63
2N/ASDC_D3J1-65
3N/ASDC_CMDJ1-57
4N/AVDD_SDCARDVSDJ1-72
5N/ASDC_CLKJ1-55
6N/AGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
7N/ASDC_D0J1-59
8N/ASDC_D1J1-61
CD1N/ASDC_CDJ1-67
CD2N/AGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70

Using Linux

To use a microSD card with the Portenta X8, run the following command to fetch a Docker container. This container eases the setup of elements essential for the microSD card interaction:

1docker run -it --cap-add SYS_ADMIN --device /dev/mmcblk1p1 debian:stable-slim bash

The command launches the image immediately after successfully downloading the container image. You will be directed inside the container once it is operational.

First, locate the partition scheme of the microSD card. If it lacks a partition table, create partitions using the

fdisk
command.

To check if the Portenta X8 has detected the microSD card, enter either of these commands:

1lsblk
2
3# or
4fdisk -l

The microSD card typically appears as

/dev/mmcblk0
or
/dev/sdX
, where X varies based on other connected storage devices.

Before accessing the microSD card's contents, mount it. Create a directory to use as a mount point:

1mkdir -p /tmp/sdcard

Mount the microSD card to the previously defined directory with the correct partition number (e.g.,

p1
for the first partition):

1mount /dev/mmcblk1p1 /tmp/sdcard

You can navigate to the mount point and view the contents of the microSD card:

1cd /tmp/sdcard
2ls

The' echo' command is useful for writing data on the microSD card. For instance, to make a hello.txt file with

"Hello World Carrier!"
text:

1echo "Hello World Carrier!" > hello.txt

To read the contents of the file you have just created:

1cat hello.txt

This displays the saved content in hello.txt on your present shell.

Portenta Mid Carrier MicroSD - Content Reading
Portenta Mid Carrier MicroSD - Content Reading

Once you are done with the operations related to the microSD card, proceed to unmount its partition properly:

1umount /tmp/sdcard

If you need to format the SD card to the ext4 filesystem, use the following command. Please be cautious, as this will remove all data on the microSD card.

1# Please be aware that this command will erase all data on the microSD card.
2mkfs.ext4 /dev/mmcblk1p1

With this, you could write and read a text file on a microSD card using the Portenta X8 and Mid Carrier.

Using Arduino IDE

To learn how the microSD card slot works for enhanced storage with the Arduino IDE, please follow this guide.

For Portenta H7, you can use the following Arduino IDE script to test the mounted SD card within the Portenta Mid Carrier:

1#include "SDMMCBlockDevice.h"
2#include "FATFileSystem.h"
3
4SDMMCBlockDevice block_device;
5mbed::FATFileSystem fs("fs");
6
7void setup() {
8 Serial.begin(9600);
9 while (!Serial);
10
11 Serial.println("Mounting SDCARD...");
12 int err = fs.mount(&block_device);
13 if (err) {
14 // Reformat if we can't mount the filesystem
15 // this should only happen on the first boot
16 Serial.println("No filesystem found, formatting... ");
17 err = fs.reformat(&block_device);
18 }
19 if (err) {
20 Serial.println("Error formatting SDCARD ");
21 while(1);
22 }
23
24 DIR *dir;
25 struct dirent *ent;
26 int dirIndex = 0;
27
28 Serial.println("List SDCARD content: ");
29 if ((dir = opendir("/fs")) != NULL) {
30 // Print all the files and directories within directory (not recursively)
31 while ((ent = readdir (dir)) != NULL) {
32 Serial.println(ent->d_name);
33 dirIndex++;
34 }
35 closedir (dir);
36 } else {
37 // Could not open directory
38 Serial.println("Error opening SDCARD\n");
39 while(1);
40 }
41 if(dirIndex == 0) {
42 Serial.println("Empty SDCARD");
43 }
44}
45
46void loop() {
47 // Empty
48}

If the micro SD card has been successfully detected and mounted, you will be able to see the list of the files printed on the Arduino IDE's Serial Monitor:

Portenta Mid Carrier MicroSD - Content Reading with Portenta H7
Portenta Mid Carrier MicroSD - Content Reading with Portenta H7

For Portenta C33, consider the following script for testing a mounted SD card.

1#include <vector>
2#include <string>
3#include "SDCardBlockDevice.h"
4#include "FATFileSystem.h"
5
6#define TEST_FS_NAME "fs"
7#define TEST_FOLDER_NAME "TEST_FOLDER"
8#define TEST_FILE "test.txt"
9#define DELETE_FILE_DIMENSION 150
10
11
12SDCardBlockDevice block_device(PIN_SDHI_CLK, PIN_SDHI_CMD, PIN_SDHI_D0, PIN_SDHI_D1, PIN_SDHI_D2, PIN_SDHI_D3, PIN_SDHI_CD, PIN_SDHI_WP);
13FATFileSystem fs(TEST_FS_NAME);
14
15std::string root_folder = std::string("/") + std::string(TEST_FS_NAME);
16std::string folder_test_name = root_folder + std::string("/") + std::string(TEST_FOLDER_NAME);
17std::string file_test_name = folder_test_name + std::string("/") + std::string(TEST_FILE);
18
19void setup() {
20 /*
21 * SERIAL INITIALIZATION
22 */
23 Serial.begin(9600);
24 while(!Serial) {
25
26 }
27
28 /* list to store all directory in the root */
29 std::vector<std::string> dir_list;
30
31 Serial.println();
32 Serial.println("##### TEST SD CARD with FAT FS");
33 Serial.println();
34
35 /*
36 * MOUNTING SDCARD AS FATFS filesystem
37 */
38 Serial.println("Mounting SDCARD...");
39 int err = fs.mount(&block_device);
40 if (err) {
41 // Reformat if we can't mount the filesystem
42 // this should only happen on the first boot
43 Serial.println("No filesystem found, formatting... ");
44 err = fs.reformat(&block_device);
45 }
46 if (err) {
47 Serial.println("Error formatting SDCARD ");
48 while(1);
49 }
50
51 /*
52 * READING root folder
53 */
54
55 DIR *dir;
56 struct dirent *ent;
57 int dirIndex = 0;
58
59 Serial.println("*** List SD CARD content: ");
60 if ((dir = opendir(root_folder.c_str())) != NULL) {
61 while ((ent = readdir (dir)) != NULL) {
62
63 if(ent->d_type == DT_REG) {
64 Serial.print("- [File]: ");
65 }
66
67 else if(ent->d_type == DT_DIR) {
68 Serial.print("- [Fold]: ");
69 dir_list.push_back(ent->d_name);
70 }
71 Serial.println(ent->d_name);
72 dirIndex++;
73 }
74 closedir (dir);
75 }
76 else {
77 // Could not open directory
78 Serial.println("Error opening SDCARD\n");
79 while(1);
80 }
81
82 if(dirIndex == 0) {
83 Serial.println("Empty SDCARD");
84 }
85
86 bool found_test_folder = false;
87
88 /*
89 * LISTING CONTENT of the first level folders (the one immediately present in root folder)
90 */
91
92 if(dir_list.size()) {
93 Serial.println();
94 Serial.println("Listing content of folders in root: ");
95 }
96 for(unsigned int i = 0; i < dir_list.size(); i++) {
97 if(dir_list[i] == TEST_FOLDER_NAME) {
98 found_test_folder = true;
99 }
100 Serial.print("- ");
101 Serial.print(dir_list[i].c_str());
102 Serial.println(":");
103
104 std::string d = root_folder + std::string("/") + dir_list[i];
105 if ((dir = opendir(d.c_str())) != NULL) {
106 while ((ent = readdir (dir)) != NULL) {
107 if(ent->d_type == DT_REG) {
108 Serial.print(" - [File]: ");
109 }
110 else if(ent->d_type == DT_DIR) {
111 Serial.print(" - [Fold]: ");
112 }
113 Serial.println(ent->d_name);
114 }
115 closedir (dir);
116 }
117 else {
118 Serial.print("ERROR OPENING SUB-FOLDER ");
119 Serial.println(d.c_str());
120 }
121 }
122
123 /*
124 * CREATING TEST FOLDER (if does not exist already)
125 */
126
127 err = 0;
128 if(!found_test_folder) {
129 Serial.println("TEST FOLDER NOT FOUND... creating folder test");
130 err = mkdir(folder_test_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
131 if(err != 0) {
132 Serial.print("FAILED folder creation with error ");
133 Serial.println(err);
134 }
135 }
136
137 /*
138 * READING TEST FILE CONTENT
139 */
140
141 if(err == 0) {
142 int file_dimension = 0;
143 FILE* fp = fopen(file_test_name.c_str(), "r");
144 if(fp != NULL) {
145 Serial.print("Opened file: ");
146 Serial.print(file_test_name.c_str());
147 Serial.println(" for reading");
148
149 fseek(fp, 0L, SEEK_END);
150 int numbytes = ftell(fp);
151 fseek(fp, 0L, SEEK_SET);
152
153 Serial.print("Bytes in the file: ");
154 Serial.println(numbytes);
155 file_dimension = numbytes;
156
157 if(numbytes > 0) {
158 Serial.println();
159 Serial.println("-------------------- START FILE CONTENT --------------------");
160 }
161
162 for(int i = 0; i < numbytes; i++) {
163 char ch;
164 fread(&ch, sizeof(char), 1, fp);
165 Serial.print(ch);
166 }
167
168 if(numbytes > 0) {
169 Serial.println("--------------------- END FILE CONTENT ---------------------");
170 Serial.println();
171 }
172 else {
173 Serial.println("File is EMPTY!");
174 Serial.println();
175 }
176
177 fclose(fp);
178 }
179 else {
180 Serial.print("FAILED open file ");
181 Serial.println(file_test_name.c_str());
182 }
183
184 /*
185 * DELETE FILE IF THE File dimension is greater than 150 bytes
186 */
187
188 if(file_dimension > DELETE_FILE_DIMENSION) {
189 Serial.println("Test file reached the delete dimension... deleting it!");
190 if(remove(file_test_name.c_str()) == 0) {
191 Serial.println("TEST FILE HAS BEEN DELETED!");
192 }
193 }
194
195 /*
196 * APPENDING SOMETHING TO FILE
197 */
198
199 fp = fopen(file_test_name.c_str(), "a");
200 if(fp != NULL) {
201 Serial.print("Opened file: ");
202 Serial.print(file_test_name.c_str());
203 Serial.println(" for writing (append)");
204 char text[] = "This line has been appended to file!\n";
205 fwrite(text, sizeof(char), strlen(text), fp);
206 fclose(fp);
207 }
208 else {
209 Serial.print("FAILED open file for appending ");
210 Serial.println(file_test_name.c_str());
211 }
212
213 /*
214 * READING AGAIN FILE CONTENT
215 */
216
217 fp = fopen(file_test_name.c_str(), "r");
218 if(fp != NULL) {
219 Serial.print("Opened file: ");
220 Serial.print(file_test_name.c_str());
221 Serial.println(" for reading");
222
223 fseek(fp, 0L, SEEK_END);
224 int numbytes = ftell(fp);
225 fseek(fp, 0L, SEEK_SET);
226
227 Serial.print("Bytes in the file: ");
228 Serial.println(numbytes);
229
230 if(numbytes > 0) {
231 Serial.println();
232 Serial.println("-------------------- START FILE CONTENT --------------------");
233 }
234
235 for(int i = 0; i < numbytes; i++) {
236 char ch;
237 fread(&ch, sizeof(char), 1, fp);
238 Serial.print(ch);
239 }
240
241 if(numbytes > 0) {
242 Serial.println("--------------------- END FILE CONTENT ---------------------");
243 Serial.println();
244 }
245 else {
246 Serial.println("File is EMPTY!");
247 Serial.println();
248 }
249
250 fclose(fp);
251
252 }
253 else {
254 Serial.print("FAILED open file for appending ");
255 Serial.println(file_test_name.c_str());
256 }
257 }
258
259}
260
261void loop() {
262 // Empty
263}

Once the micro SD card is recognized and accessed, its files will be shown in the Arduino IDE's Serial Monitor:

Portenta Mid Carrier MicroSD - Content Reading with Portenta C33
Portenta Mid Carrier MicroSD - Content Reading with Portenta C33

USB Interface (J13)

The Portenta Mid Carrier is equipped with a USB interface, which is USB 2.0, designed for data logging purposes.

Portenta Mid Carrier USB-A Port
Portenta Mid Carrier USB-A Port

For those interested in the specifics of the USB-A port's pinout, the table below provides detailed information on how the connections are distributed:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1N/A+5V (In/Out)VINJ1-21, J1-32, J1-41, J1-48
2N/AUSB0_D_NJ1-28
3N/AUSB0_D_PJ1-26
4N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70

This USB-A interface can accommodate devices such as storage drives for data logging.

Using Linux

To demonstrate a write operation on a USB memory drive using the Portenta X8's shell, follow this sequence of commands:

1sudo su -

First, switch to root mode to obtain necessary permissions for mounting and unmounting peripherals, such as a USB memory drive.

1lsblk

The

lsblk
command displays all available block devices like hard drives and USB drives. This helps identify the device name, for instance,
/dev/sda1
, which is likely your USB drive's partition.

A helpful tip to pinpoint the newly connected USB drive is to run

lsblk
before and after connecting the USB, then compare the outputs. The
lsusb
command can provide additional details about the connected USB drive.

1mkdir -p /mnt/USBmount

Here,

mkdir -p
is used to create the directory
/mnt/USBmount
, which will serve as the mount point for the USB drive.

1mount -t vfat /dev/sda1 /mnt/USBmount

This command mounts the USB drive (presumed to have a FAT filesystem, vfat) at

/dev/sda1
to the
/mnt/USBmount
directory. After mounting, you can access the USB drive's contents from the
/mnt/USBmount
directory:

1cd /mnt/USBmount

An

ls
command will display the contents of the connected USB drive.

1ls

To create a simple text file with the message

Hello, World!
on the USB drive, use:

1dd if=<(echo -n "Hello, World!") of=/mnt/USBmount/helloworld.txt

This command uses

dd
and process substitution. It redirects the output of the
echo
command, which generates
Hello, World!
as an input to
dd
. The message is then written to helloworld.txt in the
/mnt/USBmount
directory.

To read and display the file's contents in the shell:

1cat helloworld.txt

The

cat
command will show the content of helloworld.txt, in this case,
Hello, World!
.

Portenta Mid Carrier USB Storage - Content Reading
Portenta Mid Carrier USB Storage - Content Reading

With these steps, you can effectively manage external storage devices like USB sticks or hard drives, expanding the storage capabilities of your solution with the Portenta Mid Carrier.

Using Arduino IDE

The following example illustrates the process of using the USB interface on the Portenta Mid Carrier with the Portenta C33 to interface with a Mass Storage Device (MSD).

By implementing this example, you can connect with and access data on a USB storage device, thus simplifying managing external storage through the USB interface.

1#include <vector>
2#include <string>
3#include "UsbHostMsd.h"
4#include "FATFileSystem.h"
5
6#define TEST_FS_NAME "usb"
7#define TEST_FOLDER_NAME "TEST_FOLDER"
8#define TEST_FILE "test.txt"
9#define DELETE_FILE_DIMENSION 150
10
11
12USBHostMSD block_device;
13FATFileSystem fs(TEST_FS_NAME);
14
15std::string root_folder = std::string("/") + std::string(TEST_FS_NAME);
16std::string folder_test_name = root_folder + std::string("/") + std::string(TEST_FOLDER_NAME);
17std::string file_test_name = folder_test_name + std::string("/") + std::string(TEST_FILE);
18
19/* this callback will be called when a Mass Storage Device is plugged in */
20void device_attached_callback(void) {
21 Serial.println();
22 Serial.println("++++ Mass Storage Device detected ++++");
23 Serial.println();
24}
25
26void setup() {
27 /*
28 * SERIAL INITIALIZATION
29 */
30 Serial.begin(9600);
31 while(!Serial) {
32
33 }
34
35 Serial.println();
36 Serial.println("*** USB HOST Mass Storage Device example ***");
37 Serial.println();
38
39 /* attached the callback so that when the device is inserted the device_attached_callback
40 will be automatically called */
41 block_device.attach_detected_callback(device_attached_callback);
42 /* list to store all directory in the root */
43 std::vector<std::string> dir_list;
44
45 /*
46 * Check for device to be connected
47 */
48
49 int count = 0;
50 while (!block_device.connect()) {
51 if(count == 0) {
52 Serial.println("Waiting for Mass Storage Device");
53 }
54 else {
55 Serial.print(".");
56 if(count % 30 == 0) {
57 Serial.println();
58 }
59 }
60 count++;
61 delay(1000);
62 }
63
64 Serial.println("Mass Storage Device connected.");
65
66 /*
67 * MOUNTIN SDCARD AS FATFS filesystem
68 */
69
70 Serial.println("Mounting Mass Storage Device...");
71 int err = fs.mount(&block_device);
72 if (err) {
73 // Reformat if we can't mount the filesystem
74 // this should only happen on the first boot
75 Serial.println("No filesystem found, formatting... ");
76 err = fs.reformat(&block_device);
77 }
78
79 if (err) {
80 Serial.println("Error formatting USB Mass Storage Device");
81 while(1);
82 }
83
84 /*
85 * READING root folder
86 */
87
88 DIR *dir;
89 struct dirent *ent;
90 int dirIndex = 0;
91
92 Serial.println("*** List USB Mass Storage Device content: ");
93 if ((dir = opendir(root_folder.c_str())) != NULL) {
94 while ((ent = readdir (dir)) != NULL) {
95 if(ent->d_type == DT_REG) {
96 Serial.print("- [File]: ");
97 }
98 else if(ent->d_type == DT_DIR) {
99 Serial.print("- [Fold]: ");
100 if(ent->d_name[0] != '.') { /* avoid hidden folders (.Trash might contain a lot of files) */
101 dir_list.push_back(ent->d_name);
102 }
103 }
104 Serial.println(ent->d_name);
105 dirIndex++;
106 }
107 closedir (dir);
108 }
109 else {
110 // Could not open directory
111 Serial.println("Error opening USB Mass Storage Device\n");
112 while(1);
113 }
114
115 if(dirIndex == 0) {
116 Serial.println("Empty SDCARD");
117 }
118
119 bool found_test_folder = false;
120
121 /*
122 * LISTING CONTENT of the first level folders (the one immediately present in root folder)
123 */
124
125 if(dir_list.size()) {
126 Serial.println();
127 Serial.println("Listing content of folders in root: ");
128 }
129 for(unsigned int i = 0; i < dir_list.size(); i++) {
130 if(dir_list[i] == TEST_FOLDER_NAME) {
131 found_test_folder = true;
132 }
133 Serial.print("- ");
134 Serial.print(dir_list[i].c_str());
135 Serial.println(":");
136
137 std::string d = root_folder + std::string("/") + dir_list[i];
138 if ((dir = opendir(d.c_str())) != NULL) {
139 while ((ent = readdir (dir)) != NULL) {
140 if(ent->d_type == DT_REG) {
141 Serial.print(" - [File]: ");
142 }
143 else if(ent->d_type == DT_DIR) {
144 Serial.print(" - [Fold]: ");
145 }
146 Serial.println(ent->d_name);
147 }
148 closedir (dir);
149 }
150 else {
151 Serial.print("ERROR OPENING SUB-FOLDER ");
152 Serial.println(d.c_str());
153 }
154 }
155
156 /*
157 * CREATING TEST FOLDER (if does not exist already)
158 */
159
160 err = 0;
161 if(!found_test_folder) {
162 Serial.println("TEST FOLDER NOT FOUND... creating folder test");
163 err = mkdir(folder_test_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
164 if(err != 0) {
165 Serial.print("FAILED folder creation with error ");
166 Serial.println(err);
167 }
168 }
169
170 /*
171 * READING TEST FILE CONTENT
172 */
173
174 if(err == 0) {
175 int file_dimension = 0;
176 FILE* fp = fopen(file_test_name.c_str(), "r");
177 if(fp != NULL) {
178 Serial.print("Opened file: ");
179 Serial.print(file_test_name.c_str());
180 Serial.println(" for reading");
181
182 fseek(fp, 0L, SEEK_END);
183 int numbytes = ftell(fp);
184 fseek(fp, 0L, SEEK_SET);
185
186 Serial.print("Bytes in the file: ");
187 Serial.println(numbytes);
188 file_dimension = numbytes;
189
190 if(numbytes > 0) {
191 Serial.println();
192 Serial.println("-------------------- START FILE CONTENT --------------------");
193 }
194
195 for(int i = 0; i < numbytes; i++) {
196 char ch;
197 fread(&ch, sizeof(char), 1, fp);
198 Serial.print(ch);
199 }
200
201 if(numbytes > 0) {
202 Serial.println("--------------------- END FILE CONTENT ---------------------");
203 Serial.println();
204 }
205 else {
206 Serial.println("File is EMPTY!");
207 Serial.println();
208 }
209
210 fclose(fp);
211 }
212 else {
213 Serial.print("FAILED open file ");
214 Serial.println(file_test_name.c_str());
215 }
216
217 /*
218 * DELETE FILE IF THE File dimension is greater than 150 bytes
219 */
220
221 if(file_dimension > DELETE_FILE_DIMENSION) {
222 Serial.println("Test file reached the delete dimension... deleting it!");
223 if(remove(file_test_name.c_str()) == 0) {
224 Serial.println("TEST FILE HAS BEEN DELETED!");
225 }
226 }
227
228 /*
229 * APPENDING SOMETHING TO FILE
230 */
231
232 fp = fopen(file_test_name.c_str(), "a");
233 if(fp != NULL) {
234 Serial.print("Opened file: ");
235 Serial.print(file_test_name.c_str());
236 Serial.println(" for writing (append)");
237 char text[] = "This line has been appended to file!\n";
238 fwrite(text, sizeof(char), strlen(text), fp);
239 fclose(fp);
240 }
241 else {
242 Serial.print("FAILED open file for appending ");
243 Serial.println(file_test_name.c_str());
244 }
245
246 /*
247 * READING AGAIN FILE CONTENT
248 */
249
250 fp = fopen(file_test_name.c_str(), "r");
251 if(fp != NULL) {
252 Serial.print("Opened file: ");
253 Serial.print(file_test_name.c_str());
254 Serial.println(" for reading");
255
256 fseek(fp, 0L, SEEK_END);
257 int numbytes = ftell(fp);
258 fseek(fp, 0L, SEEK_SET);
259
260 Serial.print("Bytes in the file: ");
261 Serial.println(numbytes);
262
263 if(numbytes > 0) {
264 Serial.println();
265 Serial.println("-------------------- START FILE CONTENT --------------------");
266 }
267
268 for(int i = 0; i < numbytes; i++) {
269 char ch;
270 fread(&ch, sizeof(char), 1, fp);
271 Serial.print(ch);
272 }
273
274 if(numbytes > 0) {
275 Serial.println("--------------------- END FILE CONTENT ---------------------");
276 Serial.println();
277 }
278 else {
279 Serial.println("File is EMPTY!");
280 Serial.println();
281 }
282
283 fclose(fp);
284
285 }
286 else {
287 Serial.print("FAILED open file for appending ");
288 Serial.println(file_test_name.c_str());
289 }
290 }
291
292}
293
294void loop() {
295 // Empty
296}

The image below shows a list of the files contained within the mounted USB storage device:

Portenta Mid Carrier USB Storage - Content Reading with Portenta C33
Portenta Mid Carrier USB Storage - Content Reading with Portenta C33

Configuration and Control

Configuration and control features allow users to customize the device's behavior for specific needs. If you want to learn how to set up network connectivity or adjust switch configurations, follow the section below.

DIP Switch Configuration

The Portenta Mid Carrier features DIP switches, enabling users to customize the board's behavior. These switches are designated for specific functions:

  • Flashing mode
  • Ethernet behavior
  • CAN FD (CAN1)

Portenta Mid Carrier DIP switch
Portenta Mid Carrier DIP switch

BOOT DIP Switch

For setting Flashing mode, the BOOT DIP switch is used. Adjustments to this switch enable the following behaviors:

DIP Switch DesignationPosition: ONPosition: OFF
BOOTFlashing ModeNormal Mode
BOOT SELFlashing ModeNormal Mode

Setting the BOOT SEL and BOOT DIP switch to the

ON
position puts the board into Flashing Mode, which allows for the Portenta X8's OS Image update using
uuu
tool
.

For more information on how to flash an OS image of the Portenta X8, please check this tutorial.

ETH CENTER TAP DIP Switch

The configuration for Ethernet is managed using the ETH CENTER TAP DIP switch. Such switch adjustments provide the following behaviors:

DIP Switch DesignationPosition: ONPosition: OFF
1Ethernet DisabledEthernet Enabled
2Ethernet DisabledEthernet Enabled
3-Ethernet Enabled
4-Ethernet Enabled

This configuration applies to the Portenta X8. The Portenta H7 and C33, on the other hand, remain unaffected regardless of the switch positions.

Ethernet connection speeds differ based on the associated Portenta board:

  • Portenta X8: 1 Gbit Ethernet
  • Portenta H7 or C33: 10/100 Mbit Ethernet

CAN1 DIP Switch Configuration

The CAN FD (CAN1) configuration relies on the CAN1 SWITCH. Adjustments to this switch control the following behaviors:

DIP Switch DesignationPosition: ONPosition: OFF
TXOnboard CAN Transceiver Active (CAN1)Onboard CAN Transceiver Inactive (CAN1)
RXOnboard CAN Transceiver Active (CAN1)Onboard CAN Transceiver Inactive (CAN1)

For optimal performance, it is recommended to use a 120 Ω termination resistor between the CANH and CANL lines.

Network Connectivity

The Portenta Mid Carrier enhances the networking capabilities of the Portenta family devices with its integrated Ethernet port and Cat.4 Module compatibility support via a Mini PCIe interface. This addition complements the existing Wi-Fi® and Bluetooth® features of the Portenta devices designed for use with this carrier.

This integration fully leverages wired and wireless communication methods in project developments. The Portenta devices' built-in wireless capabilities and adaptable protocol options, including the Mini PCIe interface for Cat.4 Module compatibility, create a comprehensive suite of communication solutions.

These features make the carrier ideal for a broad spectrum of applications with long-range coverage.

Cat.4 Modem (Cellular Connectivity)

The Cat.4 modem, designed for mPCIe interfaces, uses LTE (Long Term Evolution) standards. These modems are crucial for achieving high-speed data transmission in various electronic devices.

Cat.4 modems can deliver robust data speeds, with peak download rates of up to 150 Mbps and upload rates reaching 50 Mbps. This performance level is well-suited to various online activities, including high-definition video streaming and expedient large file transfers.

The mPCIe form factor of these modems ensures compatibility with compact devices, including laptops, tablets, and IoT (Internet of Things) systems. Furthermore, these modems maintain backward compatibility with 3G and 2G networks, offering comprehensive network connectivity in diverse locations.

The Portenta Mid Carrier takes advantage of this modem via an onboard mini PCIe interface and provides reliable 4G connectivity with backward compatibility for 3G and 2G networks. The Arduino Pro 4G Module (EMEA / GNSS Global), a Cat.4 modem mini PCIe card using PCI Express Mini Card 1.2 Standard Interface, will be the main protagonist of this section.

The following image represents the Arduino Pro 4G Module:

Arduino Pro 4G Module
Arduino Pro 4G Module

It is available in two variants, EMEA and Global (covering the US). This module can be integrated with various Portenta boards to create expansive smart cities/buildings and implement remote maintenance and fleet management systems.

Arduino Pro 4G GNSS Module Global / Module EMEA

Make sure to attach external antennas to the Pro 4G Module to work correctly with wireless signals. There are three external antenna connectors: a main antenna connector, an Rx-diversity antenna connector, and a GNSS antenna connector. The impedance of the antenna connectors is 50 Ω.

Setting Up Via Out-Of-The-Box Experience

You can easily set up the modem through the Out-Of-The-Box process using the Portenta X8 and the Portenta Mid Carrier.

Please ensure the mini PCIe power configuration is set as outlined in the Mini PCIe Power Breakout Header section. The Portenta X8 needs the PCIE Enable (GPIO5) pin to be connected to a VCC (3V3) pin.

Please use a 5.0 V external power source when using an Arduino Pro 4G Module (EMEA / GNSS Global) or any other mPCIe modules due to their high power consumption. This is important for maintaining a stable power supply to the Portenta SOM and the carrier, particularly for extended periods of use.

Below is an image illustrating the expected setup, showing the Portenta X8 and Pro 4G Module combined with the Portenta Mid Carrier and mini PCIe power configuration:

Portenta Mid Carrier Mini PCIe & Portenta X8 Setup
Portenta Mid Carrier Mini PCIe & Portenta X8 Setup

If you are not familiar with the Out-Of-The-Box experience of the Portenta X8, it is recommended to read the Out-Of-The-Box Experience section of the Portenta X8 User Manual to gain a better understanding before proceeding.

Navigate to the Out-Of-The-Box dashboard of the Portenta X8.

Pro 4G Module OOTB Activation - Main Page
Pro 4G Module OOTB Activation - Main Page

In this dashboard, you will find the Settings option. Please click on this option to proceed to the next step.

Pro 4G Module OOTB Activation - Settings Option
Pro 4G Module OOTB Activation - Settings Option

You will see three setup options in the Settings section. Select the LTE/4G Sim option to begin the modem setup.

Make sure the Pro 4G Module is connected to the mini PCIe slot at this stage, with the 3V3_PCIE pin of the Mini PCIe power breakout connected to the 3V3_BUCK of the same breakout header.

Pro 4G Module OOTB Activation - Modem Parameters
Pro 4G Module OOTB Activation - Modem Parameters

In the LTE/4G Sim setting, it will require you to provide the following:

  • APN
  • PIN (if available)

Additionally, it is necessary to select an Authentication Protocol, which could be either:

  • PAP/CHAP
  • NONE

Briefly, PAP (Password Authentication Protocol) sends credentials in plain text, suitable for low-security or legacy environments. At the same time, CHAP (Challenge-Handshake Authentication Protocol) improves security with three-way handshake and hash functions, protecting against replay attacks and providing a more secure option than PAP.

Make sure to provide these details according to your SIM card settings. After completing these steps, the bottom left of the Out-Of-The-Box interface will display the 4G/LTE Network connection status.

Pro 4G Module OOTB Activation - Network Status
Pro 4G Module OOTB Activation - Network Status

A notification will also appear briefly to inform you that the Portenta X8 has successfully established a network connection if it finds an available network. By selecting SYSTEM INFO, you can view detailed information about the established network connection.

Pro 4G Module OOTB Activation - System Information
Pro 4G Module OOTB Activation - System Information

With this, you now have the Portenta X8 connected to a 4G/LTE network using the Portenta Mid Carrier through the Pro 4G Module.

Using Linux

The Pro 4G Module can be managed via ADB shell on the Portenta X8's Linux environment.

Make sure the mini PCIe power configuration is configured as described in the Mini PCIe Power Breakout Header section. The Portenta X8 requires the PCIE Enable (GPIO5) pin to be connected to a VCC (3V3) pin.

Please use a 5.0 V external power source when using an Arduino Pro 4G Module (EMEA / GNSS Global) or any other mPCIe modules due to their high power consumption. This is important for maintaining a stable power supply to the Portenta SOM and the carrier, particularly for extended periods of use.

The image below shows the anticipated configuration, featuring the Portenta X8 and Pro 4G Module integrated with the Portenta Mid Carrier, including the mini PCIe power configuration:

Portenta Mid Carrier Mini PCIe & Portenta X8 Setup
Portenta Mid Carrier Mini PCIe & Portenta X8 Setup

Once the setup is ready, the following sequence of commands is used to set the overlays required for the Portenta X8 and the Portenta Mid Carrier:

1fw_setenv carrier_custom 1
1fw_setenv is_on_carrier yes
1fw_setenv carrier_name mid
1fw_setenv overlays 'ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_breakout_usbfs ov_carrier_mid_pcie_mini'

Alternatively, it is possible to use the tenta-config process implemented in the GIGA Display Connector's Linux Setup section to apply the overlays to enable mini PCIe for the Portenta Mid Carrier with the Portenta X8.

Please check out the guidelines in the GIGA Display Connector's Linux Setup section for detailed information on how the tenta-config works if you have yet to become familiar with the usage.

Access the docker container named x8-devel with the following command:

1docker exec -it x8-devel sh

Navigate to the directory using the following command:

1cd /root/examples/tenta-config

Run the tenta_runner.py script using the following command:

1python tenta_runner.py

The script will activate a user interface within the

tenta
framework. Once the tenta-config window appears, please choose Portenta Mid Carrier. Next, opt for the Enable alternative overlays option.

It will open a new window displaying the Adds Mini-PCIE support option. Please select the Adds Mini-PCIE support option.

Portenta Mid Carrier mPCIe Overlay Configuration - Mini PCIe
Portenta Mid Carrier mPCIe Overlay Configuration - Mini PCIe

It will prompt a message showing a new set of overlays that will be applied once modified.

Portenta Mid Carrier mPCIe Overlay Configuration - Applied Changes
Portenta Mid Carrier mPCIe Overlay Configuration - Applied Changes

Select Ok to confirm, and the device will be configured with the overlays for mini PCIe support.

The module must be enabled, and this can be accomplished either by putting the GPIO 5 (iMX8 Pin 165) manually via the 3.3V line or by command as follows:

1echo 165 > /sys/class/gpio/export
1echo out > /sys/class/gpio/gpio165/direction
1echo 1 > /sys/class/gpio/gpio165/value

Afterward, the setup process involves bringing down the

wwan0
interface, setting it to raw IP mode, and then bringing it back up:

1ip link set dev wwan0 down
1echo Y > /sys/class/net/wwan0/qmi/raw_ip
1ip link set dev wwan0 up

The following steps include using

qmicli
commands to check the card status and start a network connection:

1qmicli --device=/dev/cdc-wdm0 --device-open-proxy --uim-get-card-status

Pro 4G Module - Card Status
Pro 4G Module - Card Status

1qmicli --device=/dev/cdc-wdm0 --device-open-proxy --wds-start-network="ip-type=4,apn=iot.1nce.net" --client-no-release-cid

Pro 4G Module - Network Initialization
Pro 4G Module - Network Initialization

After establishing the network connection, you can use

udhcpc
to manage dynamic IP configuration:

1udhcpc -q -f -n -i wwan0

Pro 4G Module - Dynamic IP Configuration
Pro 4G Module - Dynamic IP Configuration

A speed test can be performed to test the speed and performance of the connection. It involves downloading the

speedtest-cli
script, converting it to an executable, and running it inside a Docker container:

1wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
1chmod +x speedtest-cli
1docker run python:3.8-slim-buster
1docker run -it --mount type=bind,source="$(pwd)",target=/app python:3.8-slim-buster /bin/bash
1/app/speedtest-cli

Once the speed test concludes, you can see similar behavior to the following image.

Arduino Pro 4G Module - Speed Test
Arduino Pro 4G Module - Speed Test

The download and upload speed may vary depending on the region.

This comprehensive setup shows how Mini PCIe cards can be integrated and used in compact systems as carriers, leveraging their ability to provide additional functionalities while maintaining a small footprint.

To compress into a single line of command, the following format is also suggested:

1nmcli c add type gsm ifname cdc-wdm0 con-name wwan0 apn hologram connection.autoconnect yes

In case the SIM card requires a PIN number, the format is modified slightly as follows:

1nmcli c add type gsm ifname cdc-wdm0 con-name wwan0 apn mobile.vodafone.it gsm.pin <PIN>

Using Arduino IDE

The Portenta H7 and C33 are compatible with the Mini PCIe interface and can leverage the Pro 4G Module's capability. Enable and using the modem with the Portenta H7 or C33 will require a library named Arduino_4GModem.

The Arduino_4GModem library packs two sources:

  • Arduino_4G_Module
  • ArduinoProModem

Each source is included for the correct compilation and operation of the modem with either Portenta H7 or C33. The library manager within the Arduino IDE can access the library by navigating to Sketch -> Include Library -> Manage Libraries.

The library provides functionality for setting up a 4G connection, handling GPS/A-GPS location services, and uninterrupted SMS transactions.

Please ensure the mini PCIe power configuration is set as outlined in the Mini PCIe Power Breakout Header section. The Portenta H7 or C33 requires SERIAL1 Breakout pins to be connected to designated PCIe Breakout pins :

SERIAL1 Breakout Pins (17)PCIe Breakout Pins (16)
SERIAL1 RXmPCIe_CK_P
SERIAL1 TXmPCIe_CK_N
SERIAL1 RTSmPCIe_RX_N
SERIAL1 CTSmPCIe_RX_P
mPCIE_GPIO_RST (GPIO6)mPCIe_RST

Please use a 5.0 V external power source when using an Arduino Pro 4G Module (EMEA / GNSS Global) or any other mPCIe modules due to their high power consumption. This is important for maintaining a stable power supply to the Portenta SOM and the carrier, particularly for extended periods of use.

The image below shows the setup, featuring the Portenta H7 and Pro 4G Module connected to the Portenta Mid Carrier along with a mini PCIe power configuration:

Portenta Mid Carrier Mini PCIe & Portenta H7/C33 Setup
Portenta Mid Carrier Mini PCIe & Portenta H7/C33 Setup

The Portenta H7 can be replaced with the Portenta C33, maintaining the same setup.

Once the setup is ready, we can use the following example from the library called HTTPClient:

1#define TINY_GSM_DEBUG
2#define LOGGING
3#define DEBUGSERIAL Serial
4
5#include "ArduinoProModem.h"
6
7const char apn[] = "APN";
8const char gprsUser[] = "GPRSUSER";
9const char gprsPass[] = "GPRSPASS";
10
11const char server[] = "vsh.pp.ua";
12const char resource[] = "/TinyGSM/logo.txt";
13const int port = 80;
14
15ArduinoCellularModem fourgee = ArduinoCellularModem();
16HttpClient http = fourgee.getHTTPClient(server, port);
17
18//HttpClient http = HttpClient(&fourgee.getNetworkClient(), server, port);
19
20void setup(){
21 Serial.begin(115200);
22 while (!Serial);
23 fourgee.begin();
24 fourgee.connect(apn, gprsUser, gprsPass);
25}
26
27void loop(){
28
29 Serial.print(F("Performing HTTP GET request... "));
30 int err = http.get(resource);
31 if (err != 0) {
32 Serial.println(F("failed to connect"));
33 delay(10000);
34 return;
35 }
36
37 int status = http.responseStatusCode();
38 Serial.print(F("Response status code: "));
39 Serial.println(status);
40 if (!status) {
41 delay(10000);
42 return;
43 }
44
45 Serial.println(F("Response Headers:"));
46 while (http.headerAvailable()) {
47 String headerName = http.readHeaderName();
48 String headerValue = http.readHeaderValue();
49 Serial.println(" " + headerName + " : " + headerValue);
50 }
51
52 int length = http.contentLength();
53 if (length >= 0) {
54 Serial.print(F("Content length is: "));
55 Serial.println(length);
56 }
57 if (http.isResponseChunked()) {
58 Serial.println(F("The response is chunked"));
59 }
60
61 String body = http.responseBody();
62 Serial.println(F("Response:"));
63 Serial.println(body);
64
65 Serial.print(F("Body length is: "));
66 Serial.println(body.length());
67
68 // Shutdown
69
70 http.stop();
71 Serial.println(F("Server disconnected"));
72
73}

The example above connects to the web and fetches resources via HTTP. The script will require defining the following parameter fields:

  • APN
  • GPRS User
  • GPRS Password

These three parameters will always be required to be defined to use the SIM functionalities within the modem. The image below shows an anticipated initial result of the modem detected and connecting to a 4G network:

Portenta H7 & Pro 4G Module - HTTPClient Example Initialized
Portenta H7 & Pro 4G Module - HTTPClient Example Initialized

You may find additional examples as well within the library, each dedicated to different purposes as follows:

  • MQTTClient: Stream sensor information via MQTT
  • SMSReceive: Send and receive SMS messages
  • TimeAndLocation: Get time and location using GPS and GSM as a fallback process for the EU version

Ethernet

The Portenta Mid Carrier features an Ethernet port with an RJ45 connector model TRJK7003A97NL with integrated magnetics. These magnetics are crucial for voltage isolation, noise suppression, signal quality maintenance, and rejecting common mode noise, ensuring adherence to waveform standards.

The connector supports the 1000MBASE-T standard, complying with IEEE 802.3ab, guaranteeing high-speed, reliable network connections for data-intensive industrial applications.

Portenta Mid Carrier Ethernet Port
Portenta Mid Carrier Ethernet Port

The following table shows an in-depth connector (J18) designation:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
2ETH_C_NJ1-11
3N/AETH_C_PJ1-9
4N/AETH_B_PJ1-5
5N/AETH_B_NJ1-7
8N/AETH_D_PJ1-13
9N/AETH_D_NJ1-15
10N/AETH_A_NJ1-3
11N/AETH_A_PJ1-1
13N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
14N/AETH_LED2J1-19
15N/AETH_LED1J1-17
16N/AVCC
17NCNCNC

Ethernet connection speeds differ based on the associated Portenta board:

  • With the Portenta X8: The system supports 1 Gbit Ethernet.
  • When combined with the Portenta H7 or C33: The performance is limited to 100 Mbit Ethernet.

To configure the Ethernet settings, depending on the paired Portenta board, one must use the provided DIP switch on the Portenta Mid Carrier. The following table shows the specific DIP switch configuration needed to enable Ethernet on the carrier:

DIP Switch DesignationPosition: ONPosition: OFF
1Ethernet DisabledEthernet Enabled
2Ethernet DisabledEthernet Enabled
3-Ethernet Enabled
4-Ethernet Enabled

For an in-depth understanding of the DIP switch, kindly refer to this section.

Connecting the Portenta X8 via the Portenta Mid Carrier to a DHCP-enabled device like a network router is recommended. This setup automatically assigns an IP address to the Portenta X8, simplifying communication with other network devices without needing manual IP configuration. Using DHCP eases device management, supports dynamic reconfiguration, and benefits applications involving numerous devices.

Suppose you manually assign an IP to your device or establish a direct network between your computer and the board. Depending on your network devices and operating system, various methods are available.

Using Linux

Using the Portenta X8 in combination with the Mid Carrier allows you to evaluate the Ethernet speed between your device and your computer in your network. First, ensure that the Portenta X8 is mounted on the Mid Carrier, and then connect them using an RJ45 LAN cable to your local network.

Ensure that your computer and your devices are connected to the same network, on the same IP range, and can see each other.

Subsequently, open a terminal to access the shell of the Portenta X8 with admin (root) privileges.

1adb shell
2sudo su -

When prompted, enter the password

fio
. We will use the iperf3 tool to measure the bandwidth, which is available here.

To use the iperf3 tool, we will set the Portenta X8 and Mid Carrier as the Server and the controlling computer as the Client. The commands will measure the bandwidth between the Portenta Mid Carrier with Portenta X8 and the computer. For a deeper understanding of iperf3, refer to its official documentation.

Begin by setting up the Portenta Mid Carrier with Portenta X8 as the Server. For the configuration of the necessary files to establish iperf3 on the device, follow the steps for Linux and Cygwin under General Build Instructions available here. In this case, we need the aarch64 / arm64 version. Thus, we need to execute the following commands:

1mkdir -p ~/bin && source ~/.profile
1wget -qO ~/bin/iperf3 https://github.com/userdocs/iperf3-static/releases/latest/download/iperf3-arm64v8
1chmod 700 ~/bin/iperf3

Once installed, iperf3 will be ready on your device. To ensure it operates without issues, run:

1chmod +x iperf3

By following the provided instructions, the tool should be located in the Linux shell at:

1# ~bin/

Check the tool's version using the following command within the

bin
directory where iperf3 is located:

1./iperf3 -v

Portenta Mid Carrier Ethernet - iperf3 Version
Portenta Mid Carrier Ethernet - iperf3 Version

It should display the version information for the iperf3 tool.

Activate the Portenta Mid Carrier with Portenta X8 as a Server using the command:

1./iperf3 -s

This will set the Server to wait for connections via its IP address. It listens on port

5201
by default. To use an alternative port, append
-p
and your chosen port number:

1./iperf3 -s -p <Port Number>

To identify the IP address of the Portenta X8, you can use either of the following commands and search for

eth0
, which provides the network information related to the Ethernet connection:

1ifconfig
2
3# Or
4ip addr show

Next, set up your computer as a Client. In this shared repository, select and download a version suitable for your system, like Windows x64.

Once you extract the content, you will notice the iperf3 file structure as follows:

1iperf3
2 |___bin
3 |___include
4 |___lib
5 |___share

Navigate to bin and launch a terminal to prepare to use the tool. You can now begin a simple speed test using the following command.

1# For Linux shell
2iperf3 -c <Server IP>
3
4# For Windows
5.\iperf3.exe -c <Server IP>

This will set the computer as a Client and connect to the configured IP address. If a specific Port needs to be assigned, the following command will allow you to make such a configuration:

1.\iperf3.exe -c <Server IP> -p <Port Number>

Upon starting the test, you will see the amount of data transferred and the corresponding bandwidth rate. With this, you can verify the Ethernet connection and its performance.

Portenta Mid Carrier Ethernet - Connection & Performance Test
Portenta Mid Carrier Ethernet - Connection & Performance Test

Going forward, we can use the following examples to test Ethernet connectivity.

If you desire to use Portenta X8 paired with Portenta Mid Carrier, please consider following Python® scripts. These scripts use the

socket
library to create the socket and establish a computer network.

The below script would be used for Server side (TCP/IP) operations:

1#!/usr/bin/env python3
2
3import socket
4
5def start_server():
6 HOST = '127.0.0.1' # Localhost
7 PORT = 65432 # Port to listen on
8
9 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
10 s.bind((HOST, PORT))
11 s.listen()
12 print('Server is listening...')
13 conn, addr = s.accept()
14 with conn:
15 print('Connected by', addr)
16 while True:
17 data = conn.recv(1024)
18 if not data:
19 break
20 conn.sendall(data)
21
22if __name__ == "__main__":
23 start_server()

The Server-side script is set to wait for incoming connections on

127.0.0.1
(localhost) at port
65432
. These two properties can be modified later at your preference. When a Client connects, the server waits for incoming data. It simply sends back whatever it receives, behaving as an echo server.

The script below will be used for Client side (TCP/IP) operations:

1#!/usr/bin/env python3
2
3import socket
4
5def start_client():
6 HOST = '127.0.0.1' # The server's hostname or IP address
7 PORT = 65432 # The port used by the server
8
9 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
10 s.connect((HOST, PORT))
11 s.sendall(b'Hello, server!')
12 data = s.recv(1024)
13
14 print('Received', repr(data))
15
16if __name__ == "__main__":
17 start_client()

The Client-side script connects to the server specified by the

HOST
and
PORT
. These are properties that you change to your preferences. Once connected, it sends a message
"Hello, server!"
and waits for a response.

Suppose you would like to have a single script running both instances. In that case, the following script can perform the task using Python®'s built-in

threading
component.

1import socket
2import threading
3
4def server_function():
5 HOST = '127.0.0.1'
6 PORT = 65432
7
8 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
9 s.bind((HOST, PORT))
10 s.listen()
11 print('Server is listening...')
12 conn, addr = s.accept()
13 with conn:
14 print('Connected by', addr)
15 data = conn.recv(1024)
16 if data:
17 print('Server received:', repr(data))
18 conn.sendall(data)
19
20def client_function():
21 HOST = '127.0.0.1'
22 PORT = 65432
23
24 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
25 s.connect((HOST, PORT))
26 s.sendall(b'Hello, server!')
27 data = s.recv(1024)
28 print('Client received:', repr(data))
29
30if __name__ == "__main__":
31 # Start server thread
32 server_thread = threading.Thread(target=server_function)
33 server_thread.start()
34
35 # Wait a bit to ensure server is up before starting client
36 threading.Event().wait(1)
37
38 # Start client function
39 client_function()
40
41 # Join server thread
42 server_thread.join()

The script makes the server start in a separate thread, adding a brief pause using

threading.Event().wait(1)
to confirm it successfully started. It ensures the server is ready to accept connections before the client attempts to connect and send any data.

The client runs on the main thread. Using

server_thread.join()
, the main script waits for the server thread to finish its tasks before exiting.

Using Arduino IDE

The following is a WebClient example that can be used to test Ethernet connectivity with Portenta H7:

1#include <PortentaEthernet.h>
2#include <Ethernet.h>
3#include <SPI.h>
4
5// Enter a MAC address for your controller below.
6// Newer Ethernet shields have a MAC address printed on a sticker on the shield
7// byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
8
9// if you don't want to use DNS (and reduce your sketch size)
10// use the numeric IP instead of the name for the server:
11//IPAddress server(74,125,232,128); // numeric IP for Google (no DNS)
12char server[] = "www.google.com"; // name address for Google (using DNS)
13
14// Set the static IP address to use if the DHCP fails to assign
15IPAddress ip(192, 168, 2, 177);
16IPAddress myDns(192, 168, 2, 1);
17
18// Initialize the Ethernet client library
19// with the IP address and port of the server
20// that you want to connect to (port 80 is default for HTTP):
21EthernetClient client;
22
23// Variables to measure the speed
24unsigned long beginMicros, endMicros;
25unsigned long byteCount = 0;
26bool printWebData = true; // set to false for better speed measurement
27
28void setup()
29{
30
31 // Open serial communications and wait for port to open:
32 Serial.begin(9600);
33 while (!Serial) {
34 ; // wait for serial port to connect. Needed for native USB port only
35 }
36
37 // start the Ethernet connection:
38 Serial.println("Initialize Ethernet with DHCP:");
39 if (Ethernet.begin() == 0) {
40 Serial.println("Failed to configure Ethernet using DHCP");
41 // Check for Ethernet hardware present
42 if (Ethernet.hardwareStatus() == EthernetNoHardware) {
43 Serial.println("Ethernet shield was not found. Sorry, can't run without hardware");
44 while (true) {
45 delay(1); // do nothing, no point running without Ethernet hardware
46 }
47 }
48 if (Ethernet.linkStatus() == LinkOFF) {
49 Serial.println("Ethernet cable is not connected.");
50 }
51 // try to configure using IP address instead of DHCP:
52 Ethernet.begin(ip, myDns);
53 } else {
54 Serial.print(" DHCP assigned IP ");
55 Serial.println(Ethernet.localIP());
56 }
57 // give the Ethernet shield a second to initialize:
58 delay(1000);
59 Serial.print("connecting to ");
60 Serial.print(server);
61 Serial.println("...");
62
63 // if you get a connection, report back via serial:
64 if (client.connect(server, 80)) {
65 Serial.print("connected to ");
66 Serial.println(client.remoteIP());
67 // Make a HTTP request:
68 client.println("GET /search?q=arduino HTTP/1.1");
69 client.println("Host: www.google.com");
70 client.println("Connection: close");
71 client.println();
72 } else {
73 // if you didn't get a connection to the server:
74 Serial.println("connection failed");
75 }
76 beginMicros = micros();
77}
78
79void loop()
80{
81 // if there are incoming bytes available
82 // from the server, read them and print them:
83 int len = client.available();
84 if (len > 0) {
85 byte buffer[80];
86 if (len > 80)
87 len = 80;
88 client.read(buffer, len);
89 if (printWebData) {
90 Serial.write(buffer, len); // show in the serial monitor (slows some boards)
91 }
92 byteCount = byteCount + len;
93 }
94
95 // if the server's disconnected, stop the client:
96 if (!client.connected()) {
97 endMicros = micros();
98 Serial.println();
99 Serial.println("disconnecting.");
100 client.stop();
101 Serial.print("Received ");
102 Serial.print(byteCount);
103 Serial.print(" bytes in ");
104 float seconds = (float)(endMicros - beginMicros) / 1000000.0;
105 Serial.print(seconds, 4);
106 float rate = (float)byteCount / seconds / 1000.0;
107 Serial.print(", rate = ");
108 Serial.print(rate);
109 Serial.print(" kbytes/second");
110 Serial.println();
111
112 // do nothing forevermore:
113 while (true) {
114 delay(1);
115 }
116 }
117}

Once the connection has been established, you will be able to see similar results shown in the Serial Monitor in the image below:

Portenta Mid Carrier Ethernet - Connection Test with Portenta H7
Portenta Mid Carrier Ethernet - Connection Test with Portenta H7

The WebClient example below can be used to test Ethernet connectivity for Portenta C33:

1#include <EthernetC33.h>
2
3// if you don't want to use DNS (and reduce your sketch size)
4// use the numeric IP instead of the name for the server:
5//IPAddress server(74,125,232,128); // numeric IP for Google (no DNS)
6char server[] = "www.google.com"; // name address for Google (using DNS)
7
8// Set the static IP address to use if the DHCP fails to assign
9IPAddress ip(10, 130, 22, 84);
10
11// Initialize the Ethernet client library
12// with the IP address and port of the server
13// that you want to connect to (port 80 is default for HTTP):
14EthernetClient client;
15
16void setup() {
17 // Open serial communications and wait for port to open:
18 Serial.begin(115200);
19
20 while (!Serial) {
21 ; // wait for serial port to connect. Needed for native USB port only
22 }
23
24 bool use_dns = true;
25
26 // start the Ethernet connection:
27 if (Ethernet.begin() == 0) {
28 Serial.println("Failed to configure Ethernet using DHCP");
29 // try to configure using IP address instead of DHCP:
30 // IN THAT CASE YOU SHOULD CONFIGURE manually THE DNS or USE the IPAddress Server variable above
31 // that is what is automatically done here...
32 Ethernet.begin(ip);
33 use_dns = false;
34 }
35 // give the Ethernet shield a second to initialize:
36 delay(2000);
37 Serial.println("connecting...");
38
39 Serial.print("Your DNS server is: ");
40 Serial.println(Ethernet.dnsServerIP());
41
42 bool connect_result = false;
43
44 if(use_dns) {
45 connect_result = client.connect(server, 80);
46 }
47 else {
48 connect_result = client.connect(IPAddress(74,125,232,128), 80);
49 }
50
51 // if you get a connection, report back via serial:
52 if (client.connect(server, 80)) {
53 Serial.println("connected");
54 // Make a HTTP request:
55 client.println("GET /search?q=arduino HTTP/1.1");
56 client.println("Host: www.google.com");
57 client.println("Connection: close");
58 client.println();
59 } else {
60 // if you didn't get a connection to the server:
61 Serial.println("connection failed");
62 }
63}
64
65/* just wrap the received data up to 80 columns in the serial print*/
66void read_request() {
67 uint32_t received_data_num = 0;
68 while (client.available()) {
69 /* actual data reception */
70 char c = client.read();
71 /* print data to serial port */
72 Serial.print(c);
73 /* wrap data to 80 columns*/
74 received_data_num++;
75 if(received_data_num % 80 == 0) {
76 Serial.println();
77 }
78 }
79}
80
81void loop() {
82
83 read_request();
84
85 // if the server's disconnected, stop the client:
86 if (!client.connected()) {
87 Serial.println();
88 Serial.println("disconnecting.");
89 client.stop();
90
91 // do nothing forevermore:
92 while (true);
93 }
94}

After establishing the connection, similar outcomes will be shown in the Serial Monitor, as depicted in the following image:

Portenta Mid Carrier Ethernet - Connection Test with Portenta C33
Portenta Mid Carrier Ethernet - Connection Test with Portenta C33

Wi-Fi® & Bluetooth®

The Portenta Mid Carrier can be complemented with the wireless capabilities of Portenta models like the X8, H7, or C33. It enhances the use of Wi-Fi® and Bluetooth® technologies integrated into these boards. Activating these wireless features allows them to synergize with the carrier's core functionalities, creating a more versatile and rich solution for various projects.

This integration expands the range of possible applications for the Portenta Mid Carrier, allowing developers to use wireless communications in their projects. Combining the carrier's onboard capabilities with these wireless technologies makes the Portenta Mid Carrier a valuable asset for developers seeking flexible and powerful connectivity options.

For detailed information on the connectivity features of each Portenta model, please refer to their specific documentation:

Pins

The Portenta Mid Carrier is a versatile platform, and a significant feature of this carrier is its extensive pin availability. These pins provide a range of functionalities, including power, I/Os, communication, and more.

Portenta Mid Carrier Back Side
Portenta Mid Carrier Back Side

This section will examine the Breakout and PCIe breakout header of the Portenta Mid Carrier. These headers are integral to the carrier's interfacing capabilities, providing diverse connectivity options for various applications.

Portenta Mid Carrier Breakout Header (J14 - J15)

Portenta Mid Carrier Breakout Headers
Portenta Mid Carrier Breakout Headers

The following table constitutes details for the J14 Breakout Header:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
2GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
3RTS0SERIAL0_RTSJ1-38
4RTS1SERIAL1_RTSJ1-37
5NCNC
6NCNC
7RX0SERIAL0_RXJ1-36
8RX1SERIAL1_RXJ1-35
9TX0SERIAL0_TXJ1-34
10TX1SERIAL1_TXJ1-33
11CTS0SERIAL0_CTSJ1-40
12CTS1SERIAL1_CTSJ1-39
13GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
14GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
15RTS2SERIAL2_RTSJ2-30
16RTS3SERIAL3_RTSJ2-29
17NCNC
18NCNC
19RX2SERIAL2_RXJ2-28
20RX3SERIAL3_RXJ2-27
21TX2SERIAL2_TXJ2-26
22TX3SERIAL3_TXJ2-25
23CTS2SERIAL2_CTSJ2-32
24CTS3SERIAL3_CTSJ2-31
25I2S CLKI2S_CKJ1-56
26CAN0 TXCAN0_TXJ1-50
27I2S WSI2S_WSJ1-58
28CAN0 RXCAN0_RXJ1-52
29I2S SDII2S_SDIJ1-60
30CAN1 TXCAN1_TXJ1-49
31I2S SDOI2S_SDOJ1-62
32CAN1 RXCAN1_RXJ1-51
33SPDIF TXSPDIF_TXJ1-74
34PDM CLKPDM_CKJ1-66
35SPDIF RXSPDIF_RXJ1-76
36PDM D0PDM_D0J1-68
37GPIO0GPIO_0J2-46
38PDM D1PDM_D1J1-70
39GPIO1GPIO_1J2-48
40GPIO6GPIO_6J2-58
41GPIO2GPIO_2J2-50
42GPIO5GPIO_5J2-56
43GPIO3GPIO_3J2-52
44GPIO4GPIO_4J2-54

The following table constitutes details for the J15 Breakout Header:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
2GNDGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
3VCC+3V3 Portenta (Out)VCCJ2-23, J2-34, J2-43, J2-69
4VCC+3V3 Portenta (Out)VCCJ2-23, J2-34, J2-43, J2-69
5VIN+5V (In/Out)VINJ1-21, J1-32, J1-41, J1-48
6VIN+5V (In/Out)VINJ1-21, J1-32, J1-41, J1-48
7VREFPANALOG_VREF_PJ2-71
8VREFNANALOG_VREF_NJ2-72
9A0ANALOG_A0J2-73
10SPI0 CSSPI0_CSJ2-53
11A1ANALOG_A1J2-75
12SPI0 SCLKSPI0_CKJ2-37
13A2ANALOG_A2J2-77
14SPI0 CIPOSPI0_MISOJ2-39
15A3ANALOG_A3J2-79
16SPI0 COPISPI0_MOSIJ2-41
17A4ANALOG_A4J2-74
18SPI1 CSSPI1_CSJ2-36
19A5ANALOG_A5J2-76
20SPI1 SCLKSPI1_CKJ2-38
21A6ANALOG_A6J2-78
22SPI1 CIPOSPI1_MISOJ2-40
23A7ANALOG_A7J2-80
24SPI1 COPISPI1_MOSIJ2-42
25PWM0PWM_0J2-59
26I2C0 SDAI2C0_SDAJ1-44
27PWM1PWM_1J2-61
28I2C0 SCLI2C0_SCLJ1-46
29PWM2PWM_2J2-63
30I2C1 SDAI2C1_SDAJ1-43
31PWM3PWM_3J2-65
32I2C1 SCLI2C1_SCLJ1-45
33PWM4PWM_4J2-67
34I2C2 SDAI2C2_SDAJ2-45
35PWM5PWM_5J2-60
36I2C2 SCLI2C2_SCLJ2-47
37PWM6PWM_6J2-62
38SAI CLKSAI_CKJ2-49
39PWM7PWM_7J2-64
40SAI FSSAI_FSJ2-51
41PWM8PWM_8J2-66
42SAI D0SAI_D0J2-53
43PWM9PWM_9J2-68
44SAI D1SAI_D1J2-55

PCIe Breakout Header (J16)

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density PinPin Detail
1N/APCIE_CK_PJ2-17
2N/APCIE_TX_PJ2-9
3N/APCIE_CK_NJ2-19
4N/APCIE_TX_NJ2-11
5N/APCIE_RX_NJ2-15
6N/AConnected to pin 51 of mPCIE J8 connector
7N/APCIE_RX_PJ2-13
8N/AConnected to pin 49 of mPCIE J8 connector
9N/APCIE_RSTJ2-21Connected to pin 22 of mPCIE J8 connector
10N/AConnected to pin 47 of mPCIE J8 connector
11N/AConnected to pin 19 of mPCIE J8 connector
12N/AConnected to pin 45 of mPCIE J8 connector
13N/AConnected to pin 17 of mPCIE J8 connector
14N/AConnected to pin 48 of mPCIE J8 connector
15N/AConnected to pin 16 of mPCIE J8 connector
16N/AConnected to pin 46 of mPCIE J8 connector
17N/AConnected to pin 6 of mPCIE J8 connector
18N/AConnected to pin 44 of mPCIE J8 connector
19N/AConnected to pin 5 of mPCIE J8 connector
20N/AConnected to pin 42 of mPCIE J8 connector
21N/AConnected to pin 3 of mPCIE J8 connector
22N/AConnected to pin 28 of mPCIE J8 connector
23N/AConnected to pin 1 of mPCIE J8 connector
24N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54 J2-24, J2-33, J2-44, J2-57, J2-70
25N/AI2C0_SCLJ1-46
26N/AConnected to pin 30 of mPCIE J8 connector
27N/AI2C0_SDAJ1-44
28N/AConnected to pin 32 of mPCIE J8 connector

Portenta Mid Carrier Mini PCIe Breakout Headers
Portenta Mid Carrier Mini PCIe Breakout Headers

The mini PCIe breakout header also has sub-series pins (J17) detailed in the following table:

Pin NumberSilkscreen PinPortenta Standard PinHigh-Density PinPin Detail
1N/ASERIAL1_RXJ1-35Connected to pin 8 of J14 connector
2N/ASERIAL1_TXJ1-33Connected to pin 10 of J14 connector
3N/ASERIAL1_RTSJ1-37Connected to pin 4 of J14 connector
4N/ASERIAL1_CTSJ1-39Connected to pin 12 of J14 connector
5N/AGPIO_6J2-58Connected to pin 40 of J14 connector

GPIO Pins

Understanding and managing your device's General-Purpose Input/Output (GPIO) pins can be crucial for many applications. The following script is designed to display all the GPIOs available on the breakout connector of the Portenta Mid Carrier paired with Portenta X8.

The Portenta.GPIO library, officially supported and compatible with the Portenta Mid Carrier and Portenta X8, can be found here.

The GPIO configuration register for the STM32 microcontroller is structured with various fields that control different aspects of GPIO functionality:

87654 32 1 0
PEHYSPUEODEFSELDSE
  • PE (Pull Resistors Enable): Controls the use of pull resistors. 0 disables them, while 1 enables them.
  • HYS (Hysteresis Enable Field): Sets the input type. 0 selects CMOS input, and 1 selects Schmitt input.
  • PUE (Control IO ports PS): Determines the type of pull resistors used. 0 selects pull-down resistors, and 1 selects pull-up resistors.
  • ODE (Open Drain Enable Field): Configures the pin for open-drain mode. 0 turns off, 1 enables.
  • FSEL (Slew Rate Field): Controls the slew rate. 0X is slow, 1X is fast.
  • DSE (Drive Strength Field): Adjusts the drive strength. Options range from X1 to X6, with varying levels of strength.

To control a desired GPIO within the Linux environment of the Portenta X8, the following GPIO chip formula can help get the required number designation:

1[(<GPIO group> -1) * 32] + <GPIO number>

For example, PA0 is one available GPIO pin from Port A within i.MX8M Mini found with the Portenta X8. Its GPIO designation is defined as

GPIO1_IO07
. Such port relationship can be found within the provided schematics of the Portenta X8, that can be exposed to be used with the Portenta Mid Carrier.

Applying the formula to the GPIO designation, the formula field is seen as follows:

1# Illustrative form
2[(GPIO1 - 1) * 32] + IO07
3
4# Numeric form
5[(1 - 1) * 32] + 7 = 7

With this, it is possible to recognize that PA0, known as

GPIO1_IO07
, can be accessed as
7
, representing a numeric designation for this specific GPIO.

Each GPIO chip manages a specific range of GPIO numbers, facilitating organized and efficient access to the GPIO functionality of the STM32 microcontroller. The GPIO groups for the Portenta X8 are segmented in the following manner:

GPIO ChipCorresponding GPIO Number
gpiochip0GPIOs 0-31
gpiochip1GPIOs 32-63
gpiochip2GPIOs 64-95
gpiochip3GPIOs 96-127
gpiochip4GPIOs 128-159
gpiochip5GPIOs 160-193 (H7 GPIOs)

The STM32 microcontroller includes various GPIO ports, each with a specific set of pins. The enumeration of these GPIO ports is as follows:

  • Port A: PA6, PA8, PA9, PA10, PA11, PA12
  • Port B: PB1, PB5, PB6, PB10
  • Port C: PC4, PC6, PC7, PC8
  • Port D: PD0, PD1, PD3, PD4, PD5, PD6, PD15
  • Port E: PE10, PE11
  • Port F: PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF11, PF12, PF13

Each pin is identified by its port and a unique port number. The following table shows the numeric designations of the GPIOs handled by the STM32 microcontroller.

PortPort NumberPort NameFunction / Role
Port A
168PA6ADC_CH1 (A1)
189PA8PWM_6
184PA9PWM_1
185PA10PWM_2
187PA11PWM_4
191PA12PWM_8
Port B
170PB1ADC_CH3 (A3)
178PB5FDCAN2_RX
177PB6FDCAN2_TX
186PB10PWM_3
Port C
171PC4ADC_CH4 (A4)
190PC6PWM_7
183PC7PWM_0
192PC8PWM_9
Port D
176PD0FDCAN1_RX
175PD1FDCAN1_TX
182PD3USART2_CTS
181PD4USART2_RTS
189PD5USART2_TX
180PD6USART2_RX
188PD15
Port E
165PE10GPIO_5
166PE11GPIO_6
Port F
162PF3GPIO_2
163PF4GPIO_3
174PF5ADC_CH7 (A7)
161PF6GPIO_1
172PF7ADC_CH5 (A5)
160PF8GPIO_0
173PF9ADC_CH6 (A6)
167PF11ADC_CH0 (A0)
164PF12GPIO_4
169PF13ADC_CH2 (A2)

The breakout header offers access directly to GPIOs:

Pin NumberSilkscreen PinPortenta Standard PinHigh-Density Pin
37GPIO0GPIO_0J2-46
39GPIO1GPIO_1J2-48
40GPIO6GPIO_6J2-58
41GPIO2GPIO_2J2-50
42GPIO5GPIO_5J2-56
43GPIO3GPIO_3J2-52
44GPIO4GPIO_4J2-54

Portenta Mid Carrier GPIOs
Portenta Mid Carrier GPIOs

Using Linux With Shell

The General Purpose Input/Output (GPIO) pins of the Portenta X8 can be managed using the ADB shell. Initially, you need to gain administrative access to the Portenta X8's shell. This is a crucial step to modify settings that require higher privileges.

Once you have administrative access, use the following command to make a specific GPIO available. Here, we are using GPIO3 as an example, which corresponds to 163:

1echo 163 > /sys/class/gpio/export

To check which GPIOs are ready for use, execute this command:

1ls /sys/class/gpio

It lists all the GPIOs that the system has initialized. For more details about GPIO3 (or 163), which you just made available, run:

1ls /sys/class/gpio/gpio163

After exporting GPIO3, you can set it up as an Input or Output. The command below is used to define the pin's mode. Replace

<I/O>
with either
in
for input or
out
for output:

1echo <I/O> >/sys/class/gpio/gpio163/direction

For this example, the

<I/O>
field will use
out
:

1echo out >/sys/class/gpio/gpio163/direction

The

cat
command can confirm the pin's configuration. If the configuration is correct, the command will display the set value.

1cat /sys/class/gpio/gpio163/direction

Once the GPIO is set as an output, you can change its state. To set the pin to High (active), use

1
; to set it to Low (inactive), use
0
. The following command allows you to change the pin's state.

To set to High:

1echo 1 >/sys/class/gpio/gpio163/value

To set to Low:

1echo 0 >/sys/class/gpio/gpio163/value

After using the GPIO, it is an excellent practice to unexport it, which removes it from the userspace and prevents accidental changes. The following command allows making such change:

1echo 163 >/sys/class/gpio/unexport

To ensure the GPIO has been unexported properly and is no longer available for use, run:

1ls /sys/class/gpio

By following these steps, you can manage the GPIO settings on the Portenta X8, from gaining administrative access to safely unexporting the GPIO after use with the carrier.

Using Linux With Library

The General-Purpose Input/Output (GPIO) features of the Portenta X8 can be effectively controlled using the python-periphery. The library offers a user-friendly way to manage GPIOs, which is especially useful when integrating GPIO control into Python® scripts.

The Portenta.GPIO library will be available soon to offer dedicated yet simplified processes to control all available GPIOs within the Portenta Mid Carrier.

The example below demonstrates how to check the status of all GPIOs on a Portenta X8 mounted on the Portenta Mid Carrier. This script is part of the

x8-devel
container, where the necessary environment is already set up.

The following conditions are recommended to ensure the hardware is correctly configured for testing GPIO controls:

  1. Attach the Portenta-X8 securely to the Portenta Mid Carrier, ensuring the High-Density connectors are correctly connected.

  2. Each GPIO on the Portenta Mid Carrier is versatile, safely accommodating input voltages from 0.0 V to 3.3 V. This range ensures compatibility with various sensors and devices.

  3. To maintain stable and predictable behavior, internal pull-ups are enabled by default on all input pins, meaning they will read as high (3.3 V) unless actively driven low.

When all conditions are set and in place, access the Portenta X8's shell with administrative privileges:

1adb shell
2sudo su -

Enter the password

fio
when prompted. Then, enter the x8-devel Docker container:

1docker exec -it x8-devel sh

Navigate to the directory with the

gpios.py
script:

1cd root/examples/

Before running the example script, and to ensure the container environment is adequately configured, please use the following command:

1export $(grep -v '^#' /run/arduino_hw_info.env | xargs)

Run the

gpios.py
script to check the status of all GPIOs on the breakout header. The command would be as follows:

1python gpios.py

The code below is compatible with the Portenta Mid carrier, as the Portenta X8 is the core used to control the GPIOs over High-Density pins:

1#!/usr/bin/env python3
2
3# Read all gpios
4
5# A simple script to print all the gpios on
6# the corresponding connector of the Breakout carrier
7
8# Circuit:
9# * Place Portenta-X8 on a Breakout carrier
10# * All gpios accepts 0.0v - 3.3v input range
11# * Internal pull up are enabled by default on all input pins
12
13# created 18 March 2022
14# by Massimo Pennazio
15
16import os
17from periphery import GPIO
18
19if os.environ['CARRIER_NAME'] != "breakout":
20 print("This script requires Breakout carrier")
21 exit(1)
22
23gpio0 = GPIO("/dev/gpiochip5", 0, "in")
24gpio1 = GPIO("/dev/gpiochip5", 1, "in")
25gpio2 = GPIO("/dev/gpiochip5", 2, "in")
26gpio3 = GPIO("/dev/gpiochip5", 3, "in")
27gpio4 = GPIO("/dev/gpiochip5", 4, "in")
28gpio5 = GPIO("/dev/gpiochip5", 5, "in")
29gpio6 = GPIO("/dev/gpiochip5", 6, "in")
30
31value0 = int(gpio0.read())
32value1 = int(gpio1.read())
33value2 = int(gpio2.read())
34value3 = int(gpio3.read())
35value4 = int(gpio4.read())
36value5 = int(gpio5.read())
37value6 = int(gpio6.read())
38
39print("GPIO0 = %d" % value0)
40print("GPIO1 = %d" % value1)
41print("GPIO2 = %d" % value2)
42print("GPIO3 = %d" % value3)
43print("GPIO4 = %d" % value4)
44print("GPIO5 = %d" % value5)
45print("GPIO6 = %d" % value6)

This script consolidates and displays the status of all GPIOs, saving time and reducing error risk.

Portenta Mid Carrier GPIOs Reading
Portenta Mid Carrier GPIOs Reading

It is beneficial for debugging, prototyping, or setting up new projects on the Portenta Mid Carrier. Such considerations involve:

  • Avoid manually checking each pin by having a consolidated overview of all GPIOs' statuses.

  • By staying within the specified voltage range, you ensure the longevity of your device and prevent potential damages.

  • With the default pull-ups, you can be confident in your readings, knowing unintentional fluctuations are minimized.

The Portenta.GPIO library will soon be released to add compatibility to the Portenta Mid Carrier, providing a dedicated and streamlined approach to managing all GPIOs on the carrier.

For a quick overview of available GPIOs on the Portenta X8, this shell command can be used:

1cat /sys/kernel/debug/gpio

This command provides a convenient way to view the GPIO setup and status directly from the shell.

Analog Pins

The Portenta Mid Carrier's breakout header connector includes analog channels, providing access to the analog pins

A0
,
A1
,
A2
,
A3
,
A4
,
A5
,
A6
, and
A7
. These pins are accessible through the breakout header.

Portenta Mid Carrier Analog Pins
Portenta Mid Carrier Analog Pins

The table below details the pin assignments for these analog channels:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
7VREFPANALOG_VREF_PJ2-71
8VREFNANALOG_VREF_NJ2-72
9A0ANALOG_A0J2-73
11A1ANALOG_A1J2-75
13A2ANALOG_A2J2-77
15A3ANALOG_A3J2-79
17A4ANALOG_A4J2-74
19A5ANALOG_A5J2-76
21A6ANALOG_A6J2-78
23A7ANALOG_A7J2-80

To interact with these analog input pins, you can use the built-in functionalities of the Arduino programming language, precisely the

analogRead()
function, available in the Arduino IDE.

To locate these analog pins on the board, please consult the board pinout section of the user manual.

Using Linux

When using the Portenta X8, you can measure voltage within a range of 0 to 65535, which correlates to a real-world voltage range of 0 to 3.3 V. To obtain this voltage reading, you can use the following command within the ADB shell:

1cat /sys/bus/iio/devices/iio\:device0/in_voltage<adc_pin>_raw

Here,

<adc_pin>
should be replaced with the specific number of the analog pin you wish to read. For instance, to read from pin
A0
, the command would be:

1cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw

For those working with Python®, you can incorporate this command into your script as demonstrated in the example script below:

1def read_adc_value(adc_pin):
2 try:
3 with open(f'/sys/bus/iio/devices/iio:device0/in_voltage{adc_pin}_raw', 'r') as file:
4 return int(file.read().strip())
5 except FileNotFoundError:
6 print(f"ADC pin {adc_pin} not found!")
7 return None
8
9if __name__ == "__main__":
10 adc_pin = input("Enter ADC pin number: ")
11 value = read_adc_value(adc_pin)
12
13 if value is not None:
14 print(f"Value from ADC pin {adc_pin}: {value}")
15
16 # Mapping between 0-3.3 V
17 new_value = (float) (value/65535)*3.3
18 print(f"Value mapped between 0-3.3 V: {new_value}")

Using Arduino IDE

The example code below, designed for the Portenta H7, exemplifies the voltage readings obtained from a potentiometer connected to

A0
. The obtained values will subsequently be displayed on the Serial Monitor within the Arduino IDE.

1// Define the potentiometer pin and variable to store its value
2int potentiometerPin = A0;
3int potentiometerValue = 0;
4
5void setup() {
6 // Initialize Serial communication
7 Serial.begin(9600);
8}
9
10void loop() {
11 // Read the voltage value from the potentiometer
12 potentiometerValue = analogRead(potentiometerPin);
13
14 // Print the potentiometer voltage value to the Serial Monitor
15 Serial.print("- Potentiometer voltage value: ");
16 Serial.println(potentiometerValue);
17
18 // Wait for 1000 milliseconds
19 delay(1000);
20}

The following example can be used with the Portenta C33:

1#include "analogWave.h" // Include the library for analog waveform generation
2
3analogWave wave(DAC); // Create an instance of the analogWave class, using the DAC pin
4
5int freq = 10; // in hertz, change accordingly
6
7void setup() {
8 Serial.begin(115200); // Initialize serial communication at a baud rate of 115200
9 wave.sine(freq); // Generate a sine wave with the initial frequency
10}
11
12void loop() {
13 // Read an analog value from pin XX and map it to a frequency range
14 freq = map(analogRead(XX), 0, 1024, 0, 10000);
15
16 // Print the updated frequency to the serial monitor
17 Serial.println("Frequency is now " + String(freq) + " hz");
18
19 wave.freq(freq); // Set the frequency of the waveform generator to the updated value
20 delay(1000); // Delay for one second before repeating
21}

PWM Pins

The Portenta Mid Carrier has ten digital pins with PWM functionality, mapped as follows:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
25PWM0-PWM_0J2-59
27PWM1-PWM_1J2-61
29PWM2-PWM_2J2-63
31PWM3-PWM_3J2-65
33PWM4-PWM_4J2-67
35PWM5-PWM_5J2-60
37PWM6-PWM_6J2-62
39PWM7-PWM_7J2-64
41PWM8-PWM_8J2-66
43PWM9-PWM_9J2-68

Portenta Mid Carrier PWM Pins
Portenta Mid Carrier PWM Pins

Please refer to the board pinout section of the user manual to find them on the board. All these pins must be configured on the corresponding Portenta.

Using Linux

The following Python® script is designed to control the brightness of a device, such as an LED, by varying the duty cycle of a PWM signal in a Linux environment on Portenta X8.

The script sets up the PWM channel, defines its period, and then, within a loop, modulates the brightness by adjusting the duty cycle. Consider the script below as an example:

1#!/usr/bin/env python3
2
3import time
4import subprocess
5
6# Define the PWM chip, channel, and other parameters
7PWM_CHIP = 0
8PWM_CHANNEL = 9 # Replace with the correct channel if necessary
9BASE_PATH = f"/sys/class/pwm/pwmchip{PWM_CHIP}/pwm{PWM_CHANNEL}"
10PERIOD = 1000000000 # 1 second in nanoseconds
11FADE_DURATION = 0.03 # 30 milliseconds
12
13# Define brightness and fade amount variables
14brightness = 0
15fadeAmount = 5 * (PERIOD // 255) # Scale fadeAmount for our period
16
17def setup_pwm():
18 subprocess.run(f"echo {PWM_CHANNEL} | sudo tee /sys/class/pwm/pwmchip{PWM_CHIP}/export", shell=True)
19 subprocess.run(f"echo {PERIOD} | sudo tee {BASE_PATH}/period", shell=True)
20 subprocess.run(f"echo 0 | sudo tee {BASE_PATH}/duty_cycle", shell=True)
21 subprocess.run(f"echo 1 | sudo tee {BASE_PATH}/enable", shell=True)
22
23def set_pwm_brightness(brightness_value):
24 duty_cycle = brightness_value * (PERIOD // 255)
25 subprocess.run(f"echo {duty_cycle} | sudo tee {BASE_PATH}/duty_cycle", shell=True)
26
27if __name__ == "__main__":
28 setup_pwm()
29 try:
30 while True:
31 set_pwm_brightness(brightness)
32 brightness += fadeAmount
33 if brightness <= 0 or brightness >= 255:
34 fadeAmount = -fadeAmount
35 time.sleep(FADE_DURATION)
36 except KeyboardInterrupt:
37 print("Exiting")

Using Arduino IDE

The [

analogWrite()
function, a feature of the Arduino programming language, allows interaction with PWM-compatible pins.

In the example code below, a PWM-capable pin is used to adjust the luminosity of an LED that is attached to it:

1const int ledPin = <PWM_X>; // Use a pin that supports PWM
2
3void setup() {
4 pinMode(ledPin, OUTPUT); // Configure the pin as OUTPUT
5}
6
7void loop() {
8 // Increase brightness
9 for (int brightness = 0; brightness <= 255; brightness++) {
10 analogWrite(ledPin, brightness);
11 delay(10);
12 }
13
14 // Decrease brightness
15 for (int brightness = 255; brightness >= 0; brightness--) {
16 analogWrite(ledPin, brightness);
17 delay(10);
18 }
19}

JTAG Pins (J3)

The Portenta Mid Carrier features a built-in JTAG interface to get detailed insight and log the elaborate development details. It provides hardware debugging, offering real-time observation. Through the JTAG pins, users can debug and program, guaranteeing accurate and optimal device performance.

Portenta Mid Carrier onboard JTAG pin
Portenta Mid Carrier onboard JTAG pin

The pins used for the JTAG debug port on the Portenta Mid Carrier are the following:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
1N/A+3V3 Portenta (Out)VCCJ2-23, J2-34, J2-43, J2-69
2N/AJTAG_SWDJ1-75
3N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
4N/AJTAG_SCKJ1-77
5N/AGroundGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
6N/AJTAG_SWOJ1-79
7NCNCNC
8N/AJTAG_TDIJ1-78
9N/AJTAG_TRSTJ1-80
10N/AJTAG_RSTJ1-73

For further guidance on managing and using the debugging process, the following resources may be of assistance:

Understanding Device Tree Blobs (DTB) Overlays

Device Tree Blobs (DTB) And DTB Overlays

In embedded systems, a key concept used by U-boot and the Linux kernel is the Device Tree Blobs (DTB). This is a method for detailing the hardware configuration of a board. The idea is to have a standard format that works across various board types, providing consistency and compatibility.

Consider these board platforms, such as carriers, to which you can attach different peripherals, like sensors or accelerometers. DTB makes it easier to manage these connections, offering flexibility for adding or removing components.

DTB overlays are an extension of this concept. They allow the hardware configuration to be modular, breaking it into smaller files. Each file represents a specific peripheral or feature. When the system boots, these overlays are combined into a single DTB in the memory, enabling customizable configurations. However, modifying the hardware setup requires a system reboot for stability.

Handling DTB Overlays

The following steps may help you adapt the DTB overlays for your Portenta X8 to support diverse hardware and devices.

Custom DTB Overlays

There may be situations where the existing DTB overlays do not meet your specific needs. In such cases, you can create custom DTB overlays. They are derived from source files called DTS files. With the respective experience, you can modify these DTS files and compile them into unique overlays customized to particular requirements.

Automated Load And Carrier Detection

U-boot can automatically select appropriate DTB overlays based on the detected carrier board, like the Portenta Mid Carrier. This detection can be hardware-based or involve reading an ID from an EEPROM.

For example, with a Portenta-X8 on a Portenta Mid Carrier, specific settings can be observed and modified through shell commands. U-boot sets these during the boot process in a step known as auto carrier detection. These settings are temporary and reset after a reboot unless you use the

carrier_custom
setting configured to
1
.

1fw_printenv overlays
2overlays=ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_xxxx
3
4fw_printenv carrier_name
5carrier_name=xxxx
6
7fw_printenv is_on_carrier
8is_on_carrier=yes
9
10fw_setenv carrier_custom 1

This serves as an escape mechanism to enable user-based configurations.

Hardware Configuration Layers

Hardware configuration is organized into layers:

  • Layer 0: System on Module (SoM), prefixed with
    ov_som_
    .
  • Layer 1: Carrier boards, prefixed with
    ov_carrier_
    .
  • Layer 2: Cameras, for example, usually concatenate the carrier name and the element name or functionality.

EEPROMs storing identification IDs are usually found on Layer 1 and communicate via I2C1. Some add-ons may include EEPROMs following the respective standard accessible via a compatible protocol.

Overlays add specific functionalities. For instance:

  • ov_som_lbee5kl1dx
    : Adds Wi-Fi®
  • ov_som_x8h7
    : Adds the H7 external microcontroller

Without a recognized carrier, with the Portenta X8 as the main board, the system defaults to the first two overlays.

Distinction Between System And Hardware Configuration

It is vital to understand the difference between system configuration (like user setup and Wi-Fi® passwords) and hardware configuration, defined via the device tree. Custom device tree overlays are restricted to ensure system integrity and security in a production environment.

Communication

The Portenta Mid Carrier amplifies the Portenta core board's connectivity options by providing support for multiple communication standards, including Serial Peripheral Interface (SPI), Inter-Integrated Circuit (I2C), Universal Asynchronous Receiver-Transmitter (UART), JTAG interface, and MIPI, which is specifically adapted for use with the Portenta X8 camera. Detailed information on these communication protocols is provided in this section.

Dedicated connectors for each communication protocol are available on the Portenta Mid Carrier for ease of use. These connectors are readily accessible through the breakout male header, easing connection with various devices, peripherals, and sensors.

SPI

The Portenta Mid Carrier offers two SPI communication ports, designated SPI0 and SPI1. These ports are linked via High-Density connectors to the breakout male header.

This configuration helps transfer data between the board and other devices compatible with SPI. The Portenta Mid Carrier uses specific pins for SPI protocol, which are outlined in the table below:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
10SPI0 CSSPI0_CSJ2-53
12SPI0 SCLKSPI0_CKJ2-37
14SPI0 CIPOSPI0_MISOJ2-39
16SPI0 COPISPI0_MOSIJ2-41
18SPI1 CSSPI1_CSJ2-36
20SPI1 SCLKSPI1_CKJ2-38
22SPI1 CIPOSPI1_MISOJ2-40
24SPI1 COPISPI1_MOSIJ2-42

Portenta Mid Carrier SPI Pins
Portenta Mid Carrier SPI Pins

Please refer to the board pinout section of the user manual to find them on the board.

Using Linux

With root privileges, enter the following commands for the Portenta X8. To enable the SPI device interface on the Portenta X8, load the spidev module using the following:

1sudo modprobe spidev

Enable the

spidev
module within the system's module configuration and reboot to activate the changes:

1echo "spidev" | sudo tee > /etc/modules-load.d/spidev.conf
2sudo systemctl reboot

To configure a service named

my_spi_service
that uses the SPI device at
/dev/spidev0.0
, include the following in your service configuration:

1services:
2 my_spi_service:
3 devices:
4 - '/dev/spidev0.0'

Such service configuration, for example, is made within the docker-compose.yml file.

I2S

I2S stands for Inter-IC Sound and is an electrical serial bus interface standard for connecting digital audio devices.

The interface operates with three main signals:

  • SCK (Serial Clock) or BCLK: This clock signal synchronizes the data transmission.
  • WS (Word Select) or FS (Frame Select): This signal indicates whether the data is for the Right or Left audio channel.
  • SD (Serial Data): This line carries the transmitted audio data.

In an I2S system, one device is a Controller, generating the

SCK
and
WS
signals. The frequency of these signals depends on the product of the Sample Rate, Bits Per Channel, and the Number of Channels.

While one device functions as a Controller, others are set to Peripheral mode. The audio data can range from 4 to 32 bits per sample, with the understanding that higher sample rates and bit depths can yield better audio quality.

For the I2S communication on the Portenta Mid Carrier, the following pins are designated:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
25I2S CLKI2S_CKJ1-56
27I2S WSI2S_WSJ1-58
29I2S SDII2S_SDIJ1-60
31I2S SDOI2S_SDOJ1-62

Portenta Mid Carrier I2S Pins
Portenta Mid Carrier I2S Pins

SAI - Serial Audio Interface

Serial Audio Interface (SAI) is a protocol for transmitting audio data between digital devices. It is more flexible than the I2S standard, allowing for different audio data formats and configurations. The following are the data lines that the carrier utilizes for SAI:

  • D0: The main data line for sending or receiving audio data.
  • CK (BCLK): The Bit Clock line controls the timing for sending or receiving each bit of audio data.
  • FS: Frame Sync signal indicates the start of an audio data frame and is often used to switch between channels in stereo audio streams.

The SAI protocol can work synchronously and asynchronously, making it versatile enough to handle complex audio applications, such as systems requiring multiple audio channels or specific audio formats.

SAI's adaptability makes it suitable for elaborate audio processing tasks and use in systems requiring more complex configurations.

For the SAI communication on the Portenta Mid Carrier, the assigned pins are:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
38SAI CLKSAI_CKJ2-49
40SAI FSSAI_FSJ2-51
42SAI D0SAI_D0J2-53
44SAI D1SAI_D1J2-55

Portenta Mid Carrier SAI Pins
Portenta Mid Carrier SAI Pins

I2C

The Portenta Mid Carrier provides I2C communication, allowing data exchange with I2C-compatible devices. It uses specific pins for I2C protocol, detailed below:

Pin NumberSilkscreen PinPower NetPortenta Standard PinHigh-Density Pin
26I2C0 SDAI2C0_SDAJ1-44
28I2C0 SCLI2C0_SCLJ1-46
30I2C1 SDAI2C1_SDAJ1-43
32I2C1 SCLI2C1_SCLJ1-45
34I2C2 SDAI2C2_SDAJ2-45
36I2C2 SCLI2C2_SCLJ2-47

Portenta Mid Carrier I2C Pins
Portenta Mid Carrier I2C Pins

Refer to the user manual's pinout section for pin locations.

Using Linux

With administrative (root) privileges on the Portenta X8, the following shell commands are applicable:

1sudo modprobe i2c-dev

The following command sequence activates the I2C device interface on the Portenta X8. To integrate the

i2c-dev
module into the system's configuration, a reboot is required:

1echo "i2c-dev" | sudo tee > /etc/modules-load.d/i2c-dev.conf
2sudo systemctl reboot

These commands ensure the I2C device interface is enabled and properly set up in the system. The following section sets up I2C services by defining under

my_i2c_service
within docker-compose.yml file:

1services:
2 my_i2c_service:
3 devices:
4 - `/dev/i2c-0`
5 - `/dev/i2c-1`
6 - `/dev/i2c-2`
7 - `/dev/i2c-3`

All I2C services can be added from 0 to 3 or as specific services if desired.

The Portenta Mid Carrier fearures three I2C ports accessible via ADB shell. The following list shows the corresponding I2C bus for I2C port connected:

Physical I2C PortI2C Bus Designation
I2C02
I2C11
I2C23

Within the Portenta X8 shell, you can use specific commands to test I2C communication with compatible devices quickly. The command below lists all the connected I2C devices:

1i2cdetect -y <I2C bus>

To interface with a specific I2C device and retrieve information, the command format is:

1i2cget -y <I2C bus> <device address> <register address>

An example command format would be as follows:

1# i2cdetect -y <I2C bus>
2i2cdetect -y 2
3
4# i2cget -y <I2C bus> <device address> <register address>
5i2cget -y 2 0x77 0xD0

Suppose you have a BME280 sensor connected to the I2C0 and used the above commands. In that case, the Portenta X8 should return similar results as in the image below:

Portenta Mid Carrier I2C - BME280 Detection with Portenta X8
Portenta Mid Carrier I2C - BME280 Detection with Portenta X8

Below are simple examples of implementing I2C with the Portenta X8 and Portenta Mid Carrier. It is shown as an example of implementation within Python®.

Here, the SMBus (System Management Bus) communication, with SMBus-compatible libraries, is established with the device on

/dev/i2c-3
. A data byte is read from the device at address 80 and offset 0, then printed.

1from smbus2 import SMBus
2
3# Connect to /dev/i2c-3
4bus = SMBus(3)
5b = bus.read_byte_data(80, 0)
6print(b)

The following code initializes the I2C bus using the smbus2 library and reads multiple bytes from the device. The

read_i2c_block_data
function reads a block of bytes from the I2C device at a given address.

1from smbus2 import SMBus
2
3# Initialize the I2C bus
4bus = SMBus(3) # 3 indicates /dev/i2c-3
5
6device_address = 0x1
7num_bytes = 2
8
9# Read from the I2C device
10data = bus.read_i2c_block_data(device_address, 0, num_bytes) # Starting address is 0 to read from
11
12# Now data is a list of bytes
13for byte in data:
14 print(byte)

The following code shows how to write data to an I2C device using the smbus2 library. A data byte (

value
) is written to a specific address (
device_address
) with a given instruction.

1from smbus2 import SMBus
2
3# Initialize the I2C bus
4bus = SMBus(3) # 3 indicates /dev/i2c-3
5
6device_address = 0x1
7instruction = 0x00
8value = 0xFF
9
10# Write to the I2C device
11bus.write_byte_data(device_address, instruction, value)

In the following code, the python-periphery library interacts with the I2C device. This is useful if a broad spectrum of protocols are required within the same script. The

I2C.transfer()
method performs write and read operations on the I2C bus.

A byte is read from the EEPROM at address

0x50
and offset
0x100
, then printed.

1from periphery import I2C
2
3# Open i2c-0 controller
4i2c = I2C("/dev/i2c-3")
5
6# Read byte at address 0x100 of EEPROM at 0x50
7msgs = [I2C.Message([0x01, 0x00]), I2C.Message([0x00], read=True)]
8i2c.transfer(0x50, msgs)
9print("0x100: 0x{:02x}".format(msgs[1].data[0]))
10
11i2c.close()

To go a step further, setting up a Docker container for capturing data from the BME280 sensor through the I2C0 channel is one implementation example. This configuration uses SMBus-compatible libraries and the RPi.bme280 library, which conveniently includes the necessary sensor calibration routines. The connection setup between the BME280 sensor and the Portenta Mid Carrier is outlined as follows:

Portenta Mid Carrier w/ Portenta X8BME280
VCCVIN
GNDGND
I2C0 SDASDI
I2C0 SCLSCK

For instructions on deploying a custom Docker container on the Portenta board, please see the detailed guide in the "Deploy a Custom Container with Portenta X8 Manager" tutorial.

Next, download the docker files from here and extract them to a directory of your choice within the Portenta X8. To build the Docker image required for establishing a connection and reading data from the BME280 sensor, use the following command:

1sudo docker build . -t bme28

After the build is complete, you can run the container. Use the command below to start the container, which will then display results similar to the image shown:

1sudo docker compose up

Portenta Mid Carrier I2C - BME280 Reading with Portenta X8
Portenta Mid Carrier I2C - BME280 Reading with Portenta X8

Using Arduino IDE

To enable I2C communication using the Portenta H7 or C33, begin by including the

Wire
library at the top of your sketch. This library offers a range of functions tailored for I2C communication:

1#include <Wire.h>

In the

setup()
function, initialize the I2C library:

1// Initialize the I2C communication
2Wire.begin();

The following commands are available to transmit or write with an I2C-compatible device.

1// Replace with the target device's I2C address
2byte deviceAddress = 0x1;
3
4// Replace with the appropriate instruction byte
5byte instruction = 0x00;
6
7// Replace with the value to send
8byte value = 0xFF;
9
10// Begin transmission to the target device
11Wire.beginTransmission(deviceAddress);
12
13// Send the instruction byte
14Wire.write(instruction);
15
16// Send the value
17Wire.write(value);
18
19// End transmission
20Wire.endTransmission();

The following commands are available to request or read with an I2C-compatible device.

1// The target device's I2C address
2byte deviceAddress = 0x1;
3
4// The number of bytes to read
5int numBytes = 2;
6
7// Request data from the target device
8Wire.requestFrom(deviceAddress, numBytes);
9
10// Read while there is data available
11while (Wire.available()) {
12 byte data = Wire.read();
13}

For example, just as we have implemented with the Portenta X8, this setup can be replicated with the Portenta H7 and C33 models. In this context, the Adafruit BME280 library, which works in conjunction with the

Wire
library, is suitable for similar objectives of already implementing calibration functions.

This library is accessible through the Library Manager in the Arduino IDE, and the

bme280test
example from it will be used to obtain readings from the BME280 sensor.

After uploading the example onto the Portenta H7 paired with the Portenta Mid Carrier, you will notice the following behavior:

Portenta Mid Carrier I2C - BME280 Reading with Portenta H7
Portenta Mid Carrier I2C - BME280 Reading with Portenta H7

A similar outcome is expected when the setup involves the Portenta C33:

Portenta Mid Carrier I2C - BME280 Reading with Portenta C33
Portenta Mid Carrier I2C - BME280 Reading with Portenta C33

CAN Bus

The CAN bus, an acronym for Controller Area Network bus, is a robust communication system initially developed by Bosch® in the 1980s primarily for automotive applications. It enables microcontrollers and devices to communicate with each other without the need for a host computer. The protocol uses a multi-master design, allowing any network device to transmit data whenever the bus is available.

This decentralized approach is designed to maintain network integrity even if a component fails, making it exceptionally reliable in environments with significant electrical noise, such as vehicles. This reliability ensures consistent communication among the various electronic devices within such settings.

The Portenta Mid Carrier incorporates CAN bus functionality, featuring the TJA1049, a high-speed CAN FD transceiver module. This addition helps developers to integrate the dependable and efficient CAN bus communication protocol.

Portenta Mid Carrier CAN Bus Port
Portenta Mid Carrier CAN Bus Port

The CAN bus interface on the Portenta Mid Carrier is available through High-Density connectors. It is easily accessible via the screw terminal connectors and breakout male header provided on the carrier.

Pin NumberSilkscreen PinPortenta Standard PinHigh-Density PinInterface
26CAN0 TXCAN0_TXJ1-50CANH
28CAN0 RXCAN0_RXJ1-52CANL
30CAN1 TXCAN1_TXJ1-49CANH
32CAN1 RXCAN1_RXJ1-51CANL

For instance, the Portenta Mid Carrier can communicate with a Portenta Machine Control via CAN1 found within the screw terminal block. The image below shows what the connection would look like between these two devices.

Portenta Mid Carrier CAN Bus Interface Connection Example
Portenta Mid Carrier CAN Bus Interface Connection Example

If the Breakout Header (J14) is selected as the desired port to establish CAN bus communication, the following image shows the connection example that can either use CAN0 or CAN1

Portenta Mid Carrier CAN Bus Interface Connection Example
Portenta Mid Carrier CAN Bus Interface Connection Example

If two Portenta Mid Carriers are available, for example, and were to be used to communicate between themselves via CAN bus. The Portenta Machine Control is replaced with a Portenta Mid Carrier, which applies the same wiring setup.

Please do remember to enable the CAN1 port using the dedicated CAN1 switch (SW2) by positioning both levers to the ON state if it is to be connected via the screw terminal block (J4).

For stable CAN bus communication, it is recommended to install a 120 Ω termination resistor between CANH and CANL lines.

Using Linux

For the Portenta X8, administrative (root) privileges are preferably required to enter commands in the shell to manage the CAN bus protocol. To enable the CAN transceiver, the following command can be used:

1echo 164 > /sys/class/gpio/export && echo out > /sys/class/gpio/gpio164/direction && echo 0 > /sys/class/gpio/gpio164/value

This sequence of commands initializes the CAN transceiver by exporting GPIO 164, configuring its direction as output, and setting its value to

LOW (0)
.

On the Portenta X8, the following command helps verify and load the necessary CAN modules:

1sudo modprobe can-dev

After loading the

can-dev
module, the system must reboot for the changes to take effect:

1echo "can-dev" | sudo tee > /etc/modules-load.d/can-dev.conf
2sudo systemctl reboot

Docker containers provide a specialized environment for command-based operations on the Portenta X8's shell, such as sending CAN frames using the cansend command:

1cansend

To send a specific CAN frame with the cansend command, the syntax is as follows:

1cansend <CAN Interface [can0 | can1]> <CAN ID>#<Data_Payload>
  • <CAN Interface [can0 | can1]>
    : Defines the CAN interface on the Portenta X8 that will be used.
  • <CAN ID>
    : This is the message's identifier, crucial for prioritization within the network, and can be an 11-bit or 29-bit number.
  • <Data_Payload>
    : The actual transmitted data can be up to 8 bytes for standard frames.

For example, to send a standard message on the

can0
interface with an ID of
123
and a payload of
DEADBEEF
:

1cansend can0 123#DEADBEEF

Or to send an extended frame with a 29-bit ID defined with

1F334455
and a data payload of
1122334455667788
.

1cansend can0 1F334455#1122334455667788

The image below shows an example illustrating the Portenta X8 sending data over CAN1 to a Portenta Machine Control programmed to receive any incoming information within the CAN bus.

Portenta Mid Carrier with Portenta X8 & Portenta Machine Control
Portenta Mid Carrier with Portenta X8 & Portenta Machine Control

If you would like to know about using CAN bus on a Portenta Machine Control, you can check within its user manual here.

To prepare the environment for the cansend command, clone the following Docker container repository:

1git clone https://github.com/pika-spark/pika-spark-containers

Navigate to the can-utils-sh directory:

1cd pika-spark-containers/can-utils-sh

Build the Docker container:

1./docker-build.sh

Run the Docker container with the desired CAN interface and bitrate:

1sudo ./docker-run.sh can0 | can1 [bitrate]

It is also possible to use the canflood script, which writes data continuously over the CAN bus. The following image shows the Portenta Mid Carrier with the Portenta X8 sending a continuous stream of data over CAN1 to a receiving Portenta Machine Control.

Portenta Mid Carrier with Portenta X8 & Portenta Machine Control
Portenta Mid Carrier with Portenta X8 & Portenta Machine Control

For monitoring and dumping received CAN frames, navigate to the candump directory after cloning the repository:

1cd pika-spark-containers/candump

Build the Docker container:

1./docker-build.sh

Run it with the specified CAN interface and bitrate:

1sudo ./docker-run.sh can0 | can1 [bitrate]

As an example, the command to start monitoring on

can0
with a bitrate of
500,000
is as follows:

1sudo ./docker-run.sh can0 500000

The image below shows the Portenta X8 receiving data from a Portenta Machine Control over the CAN1 line.

Portenta Mid Carrier with Portenta X8 & Portenta Machine Control
Portenta Mid Carrier with Portenta X8 & Portenta Machine Control

If you would like to know about using CAN bus on a Portenta Machine Control, you can check within its user manual here.

For more information regarding this container utility, please check can-utils-sh and candump.

The following are the

bustype
options available in the python-can library, which cater to various CAN interfaces and devices:

  • socketcan: Used for the SocketCAN interface native to the Linux environment.
  • virtual: Establishes a virtual CAN bus for simulation or testing without physical CAN bus hardware.
  • pcan: Applicable for Peak-System PCAN-USB adapters.
  • canalystii: For Canalyst II CAN interface modules.
  • kvaser: For interfacing with Kvaser CAN hardware.
  • systec: Meant for SYS TEC electronic's CAN interfaces.
  • vector: For CAN interfaces by Vector that use the XL Driver Library.
  • usb2can: Compatible with 8devices' USB2CAN converter.
  • ixxat: For IXXAT devices that utilize the VCI driver.
  • nican: For National Instruments' range of CAN hardware.
  • iscan: For Intrepid Control Systems (ICS) neoVI tools.

Each bus type aligns with a specific CAN interface or hardware type, ranging from generic Linux interfaces to proprietary devices.

Below is a Python® script that includes functions to send standard and extended CAN messages. The

main()
function sets up a virtual CAN bus for demonstration. It sends a sequence of standard and extended CAN messages:

1import can
2import time
3
4def send_standard_can_message(channel, message_id, data):
5 msg = can.Message(arbitration_id=message_id, data=data, is_extended_id=False)
6 channel.send(msg)
7
8def send_extended_can_message(channel, message_id, data):
9 msg = can.Message(arbitration_id=message_id, data=data, is_extended_id=True)
10 channel.send(msg)
11
12def main():
13 # Assuming you're using a virtual channel for the CAN bus for testing.
14 # If you're using real hardware like the SocketCAN interface, change 'virtual' to 'socketcan'.
15 bus = can.interface.Bus(channel='virtual', bustype='virtual')
16
17 while True:
18 print("Sending packet ... ", end="")
19 send_standard_can_message(bus, 0x12, [ord('h'), ord('e'), ord('l'), ord('l'), ord('o')])
20 print("done")
21
22 time.sleep(1)
23
24 print("Sending extended packet ... ", end="")
25 send_extended_can_message(bus, 0xabcdef, [ord('w'), ord('o'), ord('r'), ord('l'), ord('d')])
26 print("done")
27
28 time.sleep(1)
29
30if __name__ == "__main__":
31 main()

The continuing script defines functions to receive and display CAN messages.

1import can
2
3def receive_can_messages(bus):
4 while True:
5 message = bus.recv() # Blocks until a message is received
6 print_received_message(message)
7
8def print_received_message(message):
9 print("Received ", end="")
10
11 if message.is_extended_id:
12 print("extended ", end="")
13
14 if message.is_remote_frame:
15 print("RTR ", end="")
16
17 print("packet with id 0x{:X}".format(message.arbitration_id), end="")
18
19 if message.is_remote_frame:
20 print(" and requested length {}".format(message.dlc))
21 else:
22 print(" and length {}".format(len(message.data)))
23
24 # Only print packet data for non-RTR packets
25 for byte in message.data:
26 print(chr(byte), end="")
27 print()
28
29 print()
30
31def main():
32 # Assuming you're using a virtual channel for testing.
33 # If you're using real hardware like the SocketCAN interface, change 'virtual' to 'socketcan'.
34 bus = can.interface.Bus(channel='virtual', bustype='virtual')
35 print("CAN Receiver Callback")
36 receive_can_messages(bus)
37
38if __name__ == "__main__":
39 main()

The

main()
function in the second script initiates the CAN bus using a 'virtual' channel for demonstration purposes and begins receiving and displaying CAN messages.

It can also be tested with a dedicated USB stick that drives the CAN bus and connects to the Portenta Mid Carrier.

The following image resembles when it sends data from the USB CAN hosted device to the Portenta Mid Carrier.

Portenta Mid Carrier with Portenta X8 & USB CAN
Portenta Mid Carrier with Portenta X8 & USB CAN

The image below resembles when data is received on the USB CAN hosted device sent from the Portenta Mid Carrier using the canflood script.

Portenta Mid Carrier with Portenta X8 & USB CAN
Portenta Mid Carrier with Portenta X8 & USB CAN

These methodologies, or setup, are helpful in debugging and analyzing the CAN bus communication between devices.

Using Arduino IDE

For Portenta C33, the following examples are provided to explore the functionality of the CAN bus protocol on Arduino IDE.

The CAN Read example tailored for Portenta C33 begins CAN communication at 500 kbps. It remains in constant vigilance for incoming messages, showing the received data as it arrives.

1#include <Arduino_CAN.h>
2
3/**************************************************************************************
4 * SETUP/LOOP
5 **************************************************************************************/
6
7void setup()
8{
9 Serial.begin(115200);
10 while (!Serial) { }
11
12 if (!CAN.begin(CanBitRate::BR_500k))
13 {
14 Serial.println("CAN.begin(...) failed.");
15 for (;;) {}
16 }
17}
18
19void loop()
20{
21 if (CAN.available())
22 {
23 CanMsg const msg = CAN.read();
24 Serial.println(msg);
25 }
26}

The CAN Write example, also set at 500 kbps, builds and sends a specific message format. This message includes a fixed preamble followed by an incrementing counter value that updates with each loop iteration.

1/**************************************************************************************
2 * INCLUDE
3 **************************************************************************************/
4
5#include <Arduino_CAN.h>
6
7/**************************************************************************************
8 * CONSTANTS
9 **************************************************************************************/
10
11static uint32_t const CAN_ID = 0x20;
12
13/**************************************************************************************
14 * SETUP/LOOP
15 **************************************************************************************/
16
17void setup()
18{
19 Serial.begin(115200);
20 while (!Serial) { }
21
22 if (!CAN.begin(CanBitRate::BR_500k))
23 {
24 Serial.println("CAN.begin(...) failed.");
25 for (;;) {}
26 }
27}
28
29static uint32_t msg_cnt = 0;
30
31void loop()
32{
33 /* Assemble a CAN message with the format of
34 * 0xCA 0xFE 0x00 0x00 [4 byte message counter]
35 */
36 uint8_t const msg_data[] = {0xCA,0xFE,0,0,0,0,0,0};
37 memcpy((void *)(msg_data + 4), &msg_cnt, sizeof(msg_cnt));
38 CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data);
39
40 /* Transmit the CAN message, capture and display an
41 * error core in case of failure.
42 */
43 if (int const rc = CAN.write(msg); rc < 0)
44 {
45 Serial.print ("CAN.write(...) failed with error code ");
46 Serial.println(rc);
47 for (;;) { }
48 }
49
50 /* Increase the message counter. */
51 msg_cnt++;
52
53 /* Only send one message per second. */
54 delay(1000);
55}

If CAN1 is the desired CAN bus port, the examples change to adapt such configurations. The example to write via CAN bus on CAN1 would be as follows:

1/**************************************************************************************
2 * COMPILE TIME CHECKS
3 **************************************************************************************/
4
5#ifndef ARDUINO_PORTENTA_C33
6# error "CAN1 is only available on Portenta C33."
7#endif /* ARDUINO_PORTENTA_C33 */
8
9/**************************************************************************************
10 * INCLUDE
11 **************************************************************************************/
12
13#include <Arduino_CAN.h>
14
15/**************************************************************************************
16 * CONSTANTS
17 **************************************************************************************/
18
19static uint32_t const CAN_ID = 0x20;
20
21/**************************************************************************************
22 * SETUP/LOOP
23 **************************************************************************************/
24
25void setup()
26{
27 Serial.begin(115200);
28 while (!Serial) { }
29
30 /* You need to enable the CAN transceiver
31 * by commenting in below code when using
32 * a Portenta H33 on a Portenta Max Carrier.
33 * Note: Only CAN1 is available on the Portenta
34 * Max Carrier's RJ10 CAN connector.
35 */
36#if (PIN_CAN1_STBY >= 0)
37 pinMode(PIN_CAN1_STBY, OUTPUT);
38 digitalWrite(PIN_CAN1_STBY, LOW);
39#endif
40
41 if (!CAN1.begin(CanBitRate::BR_500k))
42 {
43 Serial.println("CAN.begin(...) failed.");
44 for (;;) {}
45 }
46}
47
48static uint32_t msg_cnt = 0;
49
50void loop()
51{
52 /* Assemble a CAN message with the format of
53 * 0xCA 0xFE 0x00 0x00 [4 byte message counter]
54 */
55 uint8_t const msg_data[] = {0xCA,0xFE,0,0,0,0,0,0};
56 memcpy((void *)(msg_data + 4), &msg_cnt, sizeof(msg_cnt));
57 CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data);
58
59 /* Transmit the CAN message, capture and display an
60 * error core in case of failure.
61 */
62 if (int const rc = CAN1.write(msg); rc < 0)
63 {
64 Serial.print ("CAN.write(...) failed with error code ");
65 Serial.println(rc);
66 for (;;) { }
67 }
68
69 /* Increase the message counter. */
70 msg_cnt++;
71
72 /* Only send one message per second. */
73 delay(1000);
74}

To read on the CAN1 port would be as follows:

1/**************************************************************************************
2 * COMPILE TIME CHECKS
3 **************************************************************************************/
4
5#ifndef ARDUINO_PORTENTA_C33
6# error "CAN1 is only available on Portenta C33."
7#endif /* ARDUINO_PORTENTA_C33 */
8
9/**************************************************************************************
10 * INCLUDE
11 **************************************************************************************/
12
13#include <Arduino_CAN.h>
14
15/**************************************************************************************
16 * SETUP/LOOP
17 **************************************************************************************/
18
19void setup()
20{
21 Serial.begin(115200);
22 while (!Serial) { }
23
24 /* You need to enable the CAN transceiver
25 * by commenting in below code when using
26 * a Portenta H33 on a Portenta Max Carrier.
27 * Note: Only CAN1 is available on the Portenta
28 * Max Carrier's RJ10 CAN connector.
29 */
30#if (PIN_CAN1_STBY >= 0)
31 pinMode(PIN_CAN1_STBY, OUTPUT);
32 digitalWrite(PIN_CAN1_STBY, LOW);
33#endif
34
35 if (!CAN1.begin(CanBitRate::BR_500k))
36 {
37 Serial.println("CAN.begin(...) failed.");
38 for (;;) {}
39 }
40}
41
42void loop()
43{
44 if (CAN1.available())
45 {
46 CanMsg const msg = CAN1.read();
47 Serial.println(msg);
48 }
49}

All these examples can be found within Arduino IDE by navigating to File -> Examples -> Arduino_CAN.

More information on using CAN bus on a Portenta Machine Control can be checked within its user manual here.

The next image shows an expected communication outcome between a Portenta Mid Carrier paired with the Portenta C33 and a Portenta Machine Control.

Portenta Mid Carrier with Portenta C33 & Portenta Machine Control
Portenta Mid Carrier with Portenta C33 & Portenta Machine Control

The following image shows an expected communication outcome with roles inversed between a Portenta Mid Carrier paired with the Portenta C33 and a Portenta Machine Control.

Portenta Mid Carrier with Portenta C33 & Portenta Machine Control
Portenta Mid Carrier with Portenta C33 & Portenta Machine Control

UART

The Portenta Mid Carrier supports UART communication, accommodating multiple UART interfaces. The following table outlines the available pins on the Portenta Mid Carrier designated for UART communication protocols:

Pin NumberSilkscreen PinPortenta Standard PinHigh-Density Pin
3RTS0SERIAL0_RTSJ1-38
4RTS1SERIAL1_RTSJ1-37
7RX0SERIAL0_RXJ1-36
8RX1SERIAL1_RXJ1-35
9TX0SERIAL0_TXJ1-34
10TX1SERIAL1_TXJ1-33
11CTS0SERIAL0_CTSJ1-40
12CTS1SERIAL1_CTSJ1-39
15RTS2SERIAL2_RTSJ2-30
16RTS3SERIAL3_RTSJ2-29
19RX2SERIAL2_RXJ2-28
20RX3SERIAL3_RXJ2-27
21TX2SERIAL2_TXJ2-26
22TX3SERIAL3_TXJ2-25
23CTS2SERIAL2_CTSJ2-32
24CTS3SERIAL3_CTSJ2-31

Portenta Mid Carrier UART Pins
Portenta Mid Carrier UART Pins

Refer to the board pinout section in the user manual for pin location. To interact with these UART pins, you can use the (Serial) library functions.

Using Linux

With root access on the Portenta X8, it is possible to list the serial ports available within Linux using the command:

1ls /dev/ttyUSB* /dev/ttyACM* /dev/ttymxc*
2
3// Similar to following
4/dev/ttyUSB0 /dev/ttyUSB2 /dev/ttymxc1 /dev/ttymxc3
5/dev/ttyUSB1 /dev/ttyUSB3 /dev/ttymxc2

Typical serial devices might be listed as /dev/ttyUSBx, /dev/ttyACMx, or /dev/ttymxcx, with an example output being /dev/ttymxc2.

The Python® script below demonstrates how to employ the pyserial library for UART communication. The processData function is defined to handle the incoming data, which you can customize for your specific application.

1import serial
2import time
3
4# Define the processData function (you'll need to fill this in based on your requirements)
5def processData(data):
6 print("Received:", data) # For now, just print the data. Modify as needed.
7
8# Set up the serial port
9ser = serial.Serial('/dev/ttymxc2', 9600) # Use the appropriate port and baud rate for your device
10
11incoming = ""
12
13while True:
14 # Check for available data and read individual characters
15 while ser.in_waiting:
16 c = ser.read().decode('utf-8') # Read a single character and decode from bytes to string
17
18 # Check if the character is a newline (line-ending)
19 if c == '\n':
20 # Process the received data
21 processData(incoming)
22
23 # Clear the incoming data string for the next message
24 incoming = ""
25 else:
26 # Add the character to the incoming data string
27 incoming += c
28
29 time.sleep(0.002) # Delay for data buffering, equivalent to Arduino's delay(2);

This script maintains a serial connection on port /dev/ttymxc2 with a baud rate of 9600. It reads incoming data character by character until a newline (

\n
) is encountered, signaling the end of a data packet.

After processing the data, it resets in preparation for the next message. The

time.sleep(0.002)
ensures a buffer period for incoming data, akin to
delay(2);
used in the Arduino environment.

Using Arduino IDE

The examples provided below show UART communication for using Portenta H7 or C33. Establishing the baud rate (bits per second) in the

setup()
function is crucial for effective UART communication.

1// Start UART communication at 9600 baud
2Serial.begin(9600);

The

Serial1.available()
and
Serial1.read()
functions read incoming data. This is done by employing a
while()
loop to monitor for new data continuously and to read characters one at a time.

When a character indicating the end of a line is detected, the following code segment processes and compiles the received characters into a String variable:

1void loop() {
2 while (Serial1.available()) {
3 delay(2);
4 char c = Serial1.read();
5 if (c == '\n') {
6 processData(incoming);
7 incoming = "";
8 } else {
9 incoming += c;
10 }
11 }
12}
13
14void processData(String data) {
15 Serial.println("Received: " + data); // Print on Serial Monitor
16}

Consequently, the following provides a complete example illustrating the reception of incoming data via UART:

1String incoming = "";
2
3void setup() {
4 Serial1.begin(9600); // For communication with Arduino using RX1 and TX1
5 Serial.begin(9600); // For debugging over USB
6}
7
8void loop() {
9 while (Serial1.available()) {
10 delay(2);
11 char c = Serial1.read();
12 if (c == '\n') {
13 processData(incoming);
14 incoming = "";
15 } else {
16 incoming += c;
17 }
18 }
19}
20
21void processData(String data) {
22 Serial.println("Received: " + data); // Print on Serial Monitor
23}

To ease data transmission over UART, which can complement the receiving board as depicted above, consider the example below:

1void setup() {
2 Serial1.begin(9600);
3}
4
5void loop() {
6 Serial1.println("Hello from Portenta!");
7 delay(1000);
8}

By using these codes, you will notice the message

"Hello from Portenta!"
being sent to the recipient Portenta board when used with a Portenta Mid Carrier.

The

Serial.write()
function is instrumental for UART (Universal Asynchronous Receiver-Transmitter) data transmission, particularly when sending raw or an array of bytes.

1// Transmit the string "Hello world!
2Serial.write("Hello world!");

The example code above dispatches the string

"Hello world!"
to another device via UART using the
Serial.write()
method.

To transmit strings through UART, the following methods can be used:

1// Transmit the string "Hello world!"
2Serial.print("Hello world!");
3
4// Transmit the string "Hello world!" followed by a newline character
5Serial.println("Hello world!");

The key difference is that

Serial.println()
appends a newline character (
\n
) after transmitting the string, leading the cursor to shift to the following line, whereas
Serial.print()
does not include this newline character.

So, using the example provided above for the Portenta X8 and assigning the Portenta H7, each attached to the Portenta Mid Carrier, the result may look as follows:

UART Communication between Portenta X8 & H7
UART Communication between Portenta X8 & H7

Support

If you encounter any issues or have questions while working with the Portenta Mid Carrier, we provide various support resources to help you find answers and solutions.

Help Center

Explore our Help Center, which offers a comprehensive collection of articles and guides for the Portenta Mid Carrier. The Arduino Help Center is designed to provide in-depth technical assistance and help you make the most of your device.

Forum

Join our community forum to connect with other Portenta Mid Carrier users, share your experiences, and ask questions. The forum is an excellent place to learn from others, discuss issues, and discover new ideas and projects related to the Portenta Mid Carrier.

Contact Us

Please get in touch with our support team if you need personalized assistance or have questions not covered by the help and support resources described before. We're happy to help you with any issues or inquiries about the Portenta Mid Carrier.

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.