Portenta Hat Carrier User Manual

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

Overview

The user manual offers a detailed guide on the Arduino Portenta Hat Carrier, consolidating all its features for easy reference. It will show how to set up, adjust, and assess its main functionalities.

Portenta Hat Carrier Overview

This manual will show the user how to proficiently operate the Portenta Hat Carrier, making it suitable for project developments related to industrial automation, manufacturing automation, robotics, and prototyping.

Hardware and Software Requirements

Hardware Requirements

To use the Portenta Hat Carrier, it is necessary to attach one of the boards from the Portenta Family:

Additionally, the following accessories are needed:

  • USB-C® cable (USB-C® to USB-A cable) (x1)
  • Wi-Fi® Access Point or Ethernet with Internet access (x1)

Software Requirements

If you want to use the Portenta Hat Carrier with a Portenta X8, check the following bullet points:

  • Make sure you have the latest Linux image. Refer to this section to confirm that your Portenta X8 is up-to-date.

To ensure a stable operation of the Portenta Hat Carrier with Portenta X8, the minimum Linux image version required for Portenta X8 is 746. To flash the latest image on your board, you can use the Portenta X8 Out-of-the-box or flash it manually downloading the latest version directly from this link.

In case you want to use the Portenta Hat Carrier with a Portenta H7/C33:

Product Overview

The Portenta Hat Carrier offers a platform for developing a variety of robotics and building automation applications. It provides access to multiple peripherals, including CAN FD, Ethernet, microSD, and USB, as well as a camera interface via MIPI and 8x analog pins. On the other hand, the dedicated debug pins for JTAG and the PWM fan connector help simplify your Portenta applications.

Portenta Hat Carrier Stacked Form

The carrier is adaptable, pairing seamlessly with Portenta X8 and converting it into an industrial Linux platform compatible with Raspberry Pi® Hats. It also works with Portenta H7 and Portenta C33, providing versatile solutions to meet demands across various requirements.

Compatible Portenta boards
Compatible Portenta boards

Carrier Architecture Overview

The Portenta Hat Carrier, designed for Portenta SOM boards like the Portenta X8, H7, and C33, offers a diverse power supply range:

  • 7-32V through its screw terminal
  • USB-C®
  • 5V pin on the 40-pin header

This versatility extends to its connectivity: a USB-A for peripherals, 1000 Mbit Base-T Ethernet, SPI, I2C, I2S, and UART interfaces accessible via a 40-pin male header, and MIPI camera support exclusive for the Portenta X8.

It integrates a microSD slot for storage and data logging, broad interface options through its 40-pin and 16-pin headers, JTAG pins for debugging, and a PWM fan connector for cooling. The Ethernet speed control is intuitive with a two-position DIP switch, allowing various profiles based on the paired Portenta board.

Portenta Hat Carrier board overview
Portenta Hat Carrier board overview

The Portenta Hat Carrier has the following characteristics:

  • Compatible SOM boards: The carrier is compatible with: 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 7-32 V power supply to power the Portenta board and the carrier, and a 5 V power supply.

    The USB-C® interface of the Portenta X8, H7, and C33 can supply the needed power to the board. Alternatively, the 5V pin from the 40-pin male header can be used to power the board. The carrier can deliver a maximum current of 1.5 A.

  • USB connectivity: A USB-A female connector is used for data logging and the connection of external peripherals like keyboards, mice, hubs, and similar devices.

  • Communication: The carrier supports Ethernet interface (X1) via RJ45 connector 1000 Base-T connected to High-Density pins (J1). If paired with Portenta H7 or C33, the maximum speed is limited to 100 Mbit Ethernet.

    The SPI (X1), I2C (x2), I2S (x1), and UART (x2) are accessible via a 40-pin male header connector. The I2C1 is already dedicated to the EEPROM memory but is accessible through a 40-pin male header connector on SCL2 and SDA2.

    The UARTs do not have flow control, and UART1 and UART3 can be accessed via a 40-pin connector while UART2 can be accessed via a 16-pin connector. The CAN (x1) bus is available with an onboard transceiver. The MIPI camera is also available but only when the Portenta X8 is attached. Examples of compatible devices include the OmniVision OV5647 and the Sony IMX219 sensors.

  • Storage: The board has a microSD card slot for data logging operation and bootloading operation from external memory.

  • Ethernet connectivity: The carrier offers a Gigabit Ethernet interface through an RJ45 connector 1000 Base-T. If the carrier is paired with Portenta H7 or C33, the maximum speed is limited to 100 Mbit Ethernet.

  • 40-pin male header connector: The connector allows for SPI (x1), I2S (x1), SAI (x1), 5V power pin (x2), 3V3 power pin (x2), I2C (x2), UART (x2), PWM pins (x7), GND (x8), and GPIO (X26). The I2C count includes the one that is dedicated to EEPROM. UARTs do not have flow control. The GPIO pins are shared with different functionalities.

  • 16-pin male header connector: The connector allows analog pins (x8), PWM (x2), LICELL (x1), GPIO (x1), 3V3 (x1), GND (x1), serial TX (x1), and serial RX (x1).

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

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

  • PWM fan connector: The board has an onboard PWM fan connector (x1) compatible with a 5 V fan with a PWM signal for speed regulation.

  • DIP switch: The carrier features a DIP switch with two positions, allowing for different profiles depending on the paired Portenta board. This DIP switch includes both the ETH CENTER TAP and BTSEL switches.

    The ETH CENTER TAP controls the Ethernet interface. The OFF position enables Ethernet for the Portenta X8. Conversely, the ON position enables Ethernet for the Portenta H7/C33.

    The BTSEL switch can be used to set the Portenta X8 into Flashing Mode when the switch is set to the ON position.

Carrier Topology

Portenta Hat Carrier Topology
Portenta Hat Carrier Topology
ItemOnboard modules
J1, J2High-Density connectors for Portenta boards
J3JTAG male connector for debugging
J4USB-A female connector for data logging and external devices
J540-pin male header compatible with Raspberry Pi® Hats
J616-pin male header for analog, GPIOs, UART and RTC battery pins
J7MicroSD slot for data logging and media purposes
J8RJ45 connector for Ethernet
J9Screw terminal for power supply and CAN FD support
J10MIPI camera connector, exclusive for Portenta X8
J11PWM male header connector to control fan speed
SW2DIP switch with two sliders: ETH CENTER TAP and BTSEL
PB1User Button

Pinout

Portenta Hat Carrier Pinout

Datasheet

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

Schematics

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

STEP Files

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

Mechanical Information

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

If you desire to design and manufacture a custom mounting device or create a custom enclosure for your carrier, 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 which are also available here.

Portenta Hat Carrier Overall Dimensions
Portenta Hat Carrier Overall Dimensions

First Use Of Your Portenta Hat Carrier

Stack The Carrier

The Portenta Hat Carrier design allows the user to easily stack the preferred Portenta board. The following figure shows how the Portenta Hat Carrier pairs with the Portenta boards via High-Density connectors.

Portenta Hat Carrier Stack Orientation

With the Portenta mounted to the carrier, you can proceed to power the carrier and start prototyping.

Power The Board

The Portenta Hat Carrier can be powered according to one of the following methods:

  • Using an external 7 to 32 V power supply connected to the VIN pin available on the screw terminal block of the board is the most recommended method. It ensures that the Portenta Hat Carrier, the SOM, and any connected hat receive power.

    For clarity on the connection points, please refer to the board pinout section of the user manual. Ensure the supplied current meets the specification for all components, as shown in the operating conditions table reported later on.

  • Using an external 5 V power supply to:

  • The 5V pin located on the 40-pin male header connector pins

  • The 5V pin located on the screw terminal of the board

    You can effectively power the Portenta Hat Carrier, the SOM, and any connected hat.

    For more details on this connection, kindly consult the board pinout section of the user manual. Again, ensure that the power supply's maximum current respects all components' specifications.

  • Using a USB-C® cable (not included) connected to the Portenta core board of your choice powers not only the selected core board, like the Portenta X8, H7, or C33, but also the Portenta Hat Carrier, and any connected hat that does not require a dedicated external power supply.

The Portenta Hat Carrier can deliver a maximum of 1.5 A.

Portenta Hat Carrier Power Connection Overview
Portenta Hat Carrier Power Connection Overview

The image below magnifies the location of the terminal block for the 7 - 32 V and 5 V power source of the Portenta Hat Carrier:

Portenta Hat Carrier Power Source within Terminal Block
Portenta Hat Carrier Power Source within Terminal Block

Subsequently, you can check how the Portenta Hat Carrier distributes power resources with the power tree diagram.

Portenta Hat Carrier Power Tree Diagram
Portenta Hat Carrier Power Tree Diagram

To ensure the safety and longevity of the board, it is essential to understand the carrier's operating conditions. The table below provides the recommended operating conditions for the carrier:

ParameterMinTypMaxUnit
VIN from onboard screw terminal* of the Carrier7.0-32.0V
USB-C® input from the connected Portenta family board-5.0-V
+5 VDC from the 40-pin header connector on the carrier-5.0-V
+5 VDC from the carrier's onboard screw terminal-5.0-V
Current supplied by the carrier--1.5A
Ambient operating temperature-40-85°C

The onboard screw terminal powers both the carrier and any connected Portenta board. Additionally, this terminal connector includes reverse polarity protection for enhanced safety.

Carrier Characteristics Highlight

The Portenta Hat Carrier provides different functionalities based on the connected Portenta family board, as shown in the table below:

FeaturesPortenta X8Portenta H7Portenta 33
40-Pin HeaderCompatibleCompatibleCompatible
16-Pin HeaderCompatibleCompatibleCompatible
MIPI CameraCompatibleIncompatibleIncompatible
Ethernet1 Gbit (DIP: OFF)100 Mbit (DIP: ON)100 Mbit (DIP: ON)
PWM FanAvailableAvailableAvailable
JTAG DebugAvailableAvailableAvailable
Storage Exp.MicroSD slotMicroSD slotMicroSD slot
CAN FDAvailableAvailableAvailable
USB-A SupportAvailableAvailableAvailable

The Portenta X8 is the specific Portenta family board that offers compatibility with Raspberry Pi® Hats on the 40-pin Header.

This provides a general idea of how the Portenta Hat Carrier will perform depending on the paired Portenta board. Each feature is explained in the following section after a quick guide covering how to properly interface the Portenta boards.

Hello World Carrier

Hello World Using Linux

Using Portenta X8 with Linux


To use the Portenta Hat Carrier with the Portenta X8, you will have to align the High-Density connectors along with the USB-C® port. The following diagram shows how the board stacks on the carrier.

Portenta Hat Carrier with X8
Portenta Hat Carrier with X8
Portenta Hat Carrier with X8
Portenta Hat Carrier with X8

For the stable functionality of the Portenta Hat Carrier when used with Portenta X8, it is crucial to have at least version 746 of the Linux image on the Portenta X8. Access and download the latest version directly through this link.

Hello World With Portenta X8 Shell


A series of Hello World examples will be used to ensure the Portenta Hat Carrier is correctly operating with the paired Portenta X8. These examples, using Linux commands, Python® scripts, and the Arduino IDE, aim to trigger the user-programmable LED connected to GPIO3 leveraging different methods and platforms.

We will begin with a Hello World example using Linux commands. The user-programmable LED can be controlled using commands within the Portenta X8's shell. Learn how to connect with the Portenta X8 shell here.

The following commands will help you set and control the GPIO3, which connects to the user-programmable LED.

Let us begin with the commands to access the Portenta X8's shell:

1adb shell
2sudo su -

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

The default password is

fio

This command grants you access as the root user, loading the root user's environment settings such as

$HOME
and
$PATH
.

The aforementioned commands allow you to access the Portenta X8's shell with elevated privileges. This allows you to modify system configurations that require administrative permissions.

Subsequently, use the following command to export the gpio device located under

/sys/class/
. In this context, GPIO3 corresponds to GPIO 163, which is associated with the user-programmable LED we aim to access.

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

Using the following commands will help you verify the available GPIO elements.

1ls /sys/class/gpio

It lists all the GPIOs previously initialized by the system. Meanwhile, the following command lists the details of GPIO 163, corresponding to GPIO3, which was previously imported:

1ls /sys/class/gpio/gpio163

The GPIO can now be configured verifying that GPIO3 elements were successfully exported. The following command with the

I/O
field will set the I/O state of the pin. The pin can be set either as Input using
in
or Output using
out
value.

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

For this example, we will replace the

<I/O>
field with
out
value within the following command:

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

To verify the pin setting, use the

cat
command. If correctly configured, this command will display the set value:

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

The GPIO is now set as an output, thus it can now be controlled by setting its state.

To set the pin High, you need to assign the value

1
, or
0
to set the pin
HIGH
or
LOW
. The command will require the
value
at the end to ensure the pin's state is controlled.

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

If you have finished 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 element configuration.

Hello World Using Linux and Python® Scripts


Previously, we manually toggled the LED linked to GPIO3 on the Portenta X8 via the command line. However, to automate this process and potentially extend our control logic, we can employ a Python® script for this purpose.

The script below is compatible with 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 PHC!")
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("User LED 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. It can then be pushed to Portenta X8 using the following command on a computer terminal:

1adb push hello_world_python.py /home/fio

The file is uploaded to the

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

1cd python3 hello_world_python.py

Now use the following command to run the script:

1python3 hello_world_python.py

Portenta Hat Carrier's user-programmable LED will start blinking whenever the script is running.

Please check out the Portenta X8 user manual to learn how the board operates, and maximize its potential when paired with the Portenta Hat Carrier. The Portenta Hat Carrier supports the Portenta X8 via High-Density connectors.

The Portenta X8 has the capability to operate in a Linux environment and it is based on 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 is also capable of operating within the Arduino environment and retains the same hardware setup as explained here.

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

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

  • Portenta H7
Portenta Hat Carrier with H7
Portenta Hat Carrier with H7
Portenta Hat Carrier with H7
Portenta Hat Carrier with H7
  • Portenta C33
Portenta Hat Carrier with C33
Portenta Hat Carrier with C33
Portenta Hat Carrier with C33
Portenta Hat 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 Hat Carrier. You will interact with the user-configurable LED connected to GPIO3, but this time within the Arduino environment.

Once any compatible Portenta board is connected to the Portenta Hat Carrier, launch the Arduino IDE 2 and set up the subsequent sketch:

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 LED on (HIGH is the voltage level)
10 delay(1000); // wait for a second
11 digitalWrite(<DIGITAL_PIN>, LOW); // turn the LED off by making the voltage LOW
12 delay(1000); // wait for a second
13}

Make sure to replace

<DIGITAL_PIN>
with the appropriate value for your chosen Portenta board:

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

For example, when using the Portenta X8, your script should look like this:

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 LED on (HIGH is the voltage level)
10 delay(1000); // wait for a second
11 digitalWrite(PF_4, LOW); // turn the LED off by making the voltage LOW
12 delay(1000); // wait for a second
13}

After successfully uploading the sketch, the user-configurable LED will start blinking. The following clip illustrates the expected LED blink pattern.

Portenta Hat Carrier Hello World Blink

Please check out the following documentation to learn more about each board and maximize its potential when paired with the Portenta Hat Carrier:

Please note that the Ethernet connectivity speed is limited to 100 Mbit when used with the Portenta H7 or C33.

For up-to-date performance of the Portenta X8 on the Portenta Hat Carrier, ensure you update to the latest Portenta X8 OS image. You can check here for more details.

Carrier Features and Interfaces

The carrier offers a diverse range of features and interfaces to cater to a variety of user requirements and applications. This section provides an overview of the main hardware interfaces, storage options, and configuration mechanisms integrated into the carrier.

Each sub-section further delves into the specifications of each feature, ensuring users will get comprehensive information for optimal utilization.

Hardware Interfaces

This sub-section introduces the essential hardware connection points and interfaces present on the Portenta Hat Carrier. Ranging from connectors and camera interfaces to fan control, you will be able to explore the physical interaction points of the carrier.

High-Density Connectors

The Portenta X8, H7, and C33 enhance functionality through High-Density connectors. For a comprehensive understanding of these connectors, please refer to the complete pinout documentation for each Portenta model.

USB Interface

The Portenta Hat Carrier features a USB interface suitable for data logging and connecting external devices.

Portenta Hat Carrier USB-A Port
Portenta Hat Carrier USB-A Port

If you are interested in the USB-A port pinout, the following table may serve to understand its connection distribution:

Pin numberPower NetPortenta HD Standard PinHigh-Density PinInterface
1+5VVIN / USB0_VBUSJ1-21, J1-24, J1-32, J1-41, J1-48
2USB0_D_NJ1-28USB D-
3USB0_D_PJ1-26USB D+
4GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70

Devices with a USB-A interface, such as storage drives, can be used for logging data. External devices include peripherals like keyboards, mouses, webcams, and hubs.

Using Linux


As an example, the following command on Portenta X8's shell can be used to test a write command with a USB memory drive. To write a file, the following sequence of commands can help you to accomplish such task.

1sudo su -

First of all, let's enter root mode to have the right permissions to mount and unmount related peripherals like our USB memory drive.

1lsblk

The

lsblk
command lists all available block devices, such as hard drives and USB drives. It helps in identifying the device name, like
/dev/sda1
which will be probably the partition designation of the USB drive you just plugged in. A common trick to identify and check the USB drive connected is to execute the
lsblk
command twice; once with the USB disconnected and the next one to the USB connected, to compare both results and spot easily the newly connected USB drive. Additionally, the command
lsusb
can be used to gather more information about the connected USB drive.

1mkdir -p /mnt/USBmount

The

mkdir -p
command creates the directory
/mnt/USBmount
. This directory will be used as a mount point for the USB drive.

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

This mount command mounts the USB drive, assumed to have a FAT filesystem (

vfat
), located at
/dev/sda1
to the directory
/mnt/USBmount
. Once mounted, the content of the USB drive can be accessed from the
/mnt/USBmount
directory with
cd
:

1cd /mnt/USBmount

Now if you do an

ls
you can see the actual content of the connected USB Drive.

1ls

Let's create a simple text file containing the message

Hello, World!
in the already connected USB memory drive using the following command:

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

This command uses the

dd
utility, combined with process substitution. Specifically, it seizes the output of the
echo
command, responsible for generating the
Hello, World!
message, and channels it as an input stream to
dd
.

Subsequently, the message gets inscribed into a file named helloworld.txt situated in the

/mnt/USBmount
directory.

After creating the file, if you wish to retrieve its contents and display them on the shell, you can use:

1cat helloworld.txt

This command

cat
prompts in the terminal the content of a file, in this case the words
Hello, World!
.

Now that you know how to locate, mount, write and read information from an external USB stick or hard drive you can expand the possibilities of your solution with the additional storage connected to the Portenta Hat Carrier.

Using Arduino IDE


The following example demonstrates how to use the USB interface of the Portenta Hat Carrier with the Portenta C33 to mount a Mass Storage Device (MSD).

Through this code, users will be able to effectively connect to, read from, and write to a USB storage device, making it easier to interact with external storage via 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}

Analog Pins

The 16-pin header connector of the Portenta Hat Carrier integrates the analog channels. The analog

A0
,
A1
,
A2
,
A3
,
A4
,
A5
,
A6
, and
A7
are accessible through these pins.

Portenta Hat Carrier Analog Pins
Portenta Hat Carrier Analog Pins
Pin numberSilkscreenPortenta HD Standard PinHigh-Density Pin
1A0ANALOG_A0J2-73
2A1ANALOG_A1J2-75
3A2ANALOG_A2J2-77
4A3ANALOG_A3J2-79
5A4ANALOG_A4J2-74
6A5ANALOG_A5J2-76
7A6ANALOG_A6J2-78
8A7ANALOG_A7J2-80

The built-in features of the Arduino programming language (

analogRead()
function) can be used to access the eight analog input pins on the Arduino IDE.

Please, refer to the board pinout section of the user manual to find the analog pins on the board.

Using Linux


Using the Portenta X8, you can obtain a voltage reading that falls within a 0 - 65535 range. This reading corresponds to a voltage between 0 and 3.3 V. To fetch this reading, use the command:

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

Where

<adc_pin>
is the number of the analog pin to read. For example, in the case of
A0
:

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

If you are working in Python®, the command can be implemented as shown in the 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 following example snippet, compatible with Portenta H7, shows how to read the voltage value from a potentiometer on

A0
. It will then display the readings on the Arduino IDE Serial Monitor.

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 considered for 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}

CAN FD (Onboard Transceiver)

The Portenta Hat Carrier features a dedicated CAN bus connected to a screw terminal block. It uses the TJA1049 module, a high-speed CAN FD transceiver integrated within the Portenta Hat Carrier.

The TJA1049 module supports ISO 11898-2:2016, SAE J2284-1, and SAE J2284-5 standards over the CAN physical layer, guaranteeing stable communication during the CAN FD fast phase.

Portenta Hat Carrier CAN Interface
Portenta Hat Carrier CAN Interface

Since CAN FD is part of the screw terminal block, we have highlighted the CAN bus ports within the screw terminal block pinout for reference.

Pin numberSilkscreenPower NetPortenta HD Standard PinHigh-Density PinInterface
1VIN 7-32VDCINPUT_7V-32V
2GNDGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
3GNDGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
45V+5VVINJ1-21, J1-24, J1-32, J1-41, J1-48
5CANHJ1-49 (Through U1)CAN BUS - CANH
6CANLJ1-51 (Through U1)CAN BUS - CANL

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

More information on how to use the CAN Bus protocol can be found within CAN Bus section under Pins chapter.

MIPI Camera

The Portenta X8 can interact with MIPI cameras through the dedicated camera connector. As a quick note, the out-of-the-box Alpine shell does not support certain commands directly through the ADB shell.

On the other hand, the Portenta H7 and C33 have no MIPI interface, so they cannot use the camera connector.

Portenta Hat Carrier MIPI Camera
Portenta Hat Carrier MIPI Camera

The MIPI connector is distributed as follows:

Pin numberPower NetPortenta HD Standard PinHigh-Density PinInterface
1GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54
2CAM_D0_D0_NJ2-16, J2-24, J2-33, J2-44, J2-57, J2-70
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

As mentioned before, the Portenta Hat Carrier supports the MIPI camera if paired with the Portenta X8. The flex cable can be used to interface a compatible camera with the platform. Compatible camera devices are as follows:

  • OmniVision OV5647 sensor (Raspberry Pi® Camera Module 1)
  • Sony IMX219 sensor (Raspberry Pi® Camera Module 2)
Portenta Hat Carrier MIPI Camera Mount

Using Linux


The following commands, using the Portenta X8 environment, allow you to capture a single frame and stream video at 30 FPS (Frames per Second) for 10 seconds from the Raspberry Pi Camera v1.3, which is based on the OV5647 CMOS sensor.

First, we need to set environment variables and specify the overlays for our camera and board setup:

1fw_setenv carrier_custom 1
2fw_setenv overlays ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_rasptenta_base ov_carrier_rasptenta_ov5647_camera_mipi

The U-Boot environment variables are modified with the above commands and

fw_setenv
sets the changes which are already persistent. The following command sequences can be used on the U-boot shell.

1setenv carrier_custom 1
2setenv overlays ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_rasptenta_base ov_carrier_rasptenta_ov5647_camera_mipi
3saveenv

Define the runtime directory for

Wayland
and load the necessary module for the OV5647 camera:

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

Before capturing or streaming, we need to 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

Using

GStreamer
, capture a single frame in
JPEG
format:

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 the user to capture one frame and save it as

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

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

This command allows the user to capture 300 frames at 30 FPS, which equals 10 seconds of video, and displays them using the

waylandsink
.

Following these steps, you will be able to successfully capture and stream video from the Raspberry Pi Camera v1.3 based on the OV5647 sensor.

For enhanced image quality, we recommend using a MIPI camera module with an integrated Image Signal Processor (ISP).

PWM Fan Control

The Portenta Hat Carrier is designed to be a thermal dissipation reference carrier for Portenta X8, including dedicated Pulse Width Modulation (PWM) pins for external fan control. The principle of PWM involves varying the width of the pulses sent to the device, in this case, a fan, to control its speed or position.

Portenta Hat Carrier Fan Mount

The fan can be connected via PWM pins available on the Portenta Hat Carrier. The connector has the following structure:

Pin numberSilkscreenPower NetPortenta HD Standard PinHigh-Density Pin
1PWM9PWM_9J2-68
2N/A
35V+5VVINJ1-21, J1-24, J1-32, J1-41, J1-48
4GNDGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70

Using Linux


The fan's speed can be controlled using the following code sequence when you are using the Portenta X8 within the Linux environment.

Export the PWM channel:

1echo 9 > /sys/class/pwm/pwmchip0/export

Set the PWM period. By defining the period, you determine the duration of one PWM "cycle". Here, we set it to 100,000, representing 100,000 nanoseconds or 100 microseconds:

1echo 100000 > /sys/class/pwm/pwmchip0/pwm9/period

The following command sets the "ON" duration within the given period. A 50% duty cycle, for instance, means the signal is on for half the period and off for the other half:

1echo 50000 > /sys/class/pwm/pwmchip0/pwm9/duty_cycle #50% duty

We will then enable the PWM channel exported previously:

1echo 1 > /sys/class/pwm/pwmchip0/pwm9/enable

You can use the following command if you want to monitor the temperature of the device or environment (optional step):

1cat /sys/devices/virtual/thermal/thermal_zone0/temp

It can be translated into a Python® script to automate the command sequence:

1def setup_pwm(pwm_chip, pwm_channel, period, duty_cycle):
2 base_path = f"/sys/class/pwm/pwmchip{pwm_chip}"
3
4 # Export the PWM channel
5 with open(f"{base_path}/export", "w") as f:
6 f.write(str(pwm_channel))
7
8 # Set period
9 with open(f"{base_path}/pwm{pwm_channel}/period", "w") as f:
10 f.write(str(period))
11
12 # Set duty cycle
13 with open(f"{base_path}/pwm{pwm_channel}/duty_cycle", "w") as f:
14 f.write(str(duty_cycle))
15
16 # Enable the PWM channel
17 with open(f"{base_path}/pwm{pwm_channel}/enable", "w") as f:
18 f.write("1")
19
20def get_thermal_temperature(zone=0):
21 with open(f"/sys/devices/virtual/thermal/thermal_zone{zone}/temp", "r") as f:
22 return int(f.read().strip())
23
24
25if __name__ == "__main__":
26 # Set up PWM
27 setup_pwm(0, 9, 100000, 50000) # 50% duty
28
29 # Read and print thermal temperature
30 temperature = get_thermal_temperature()
31 print(f"Thermal Temperature (thermal_zone0): {temperature}")

If you are logged in with normal privileges, the speed of the fan can be controlled using the following instruction sequence. Export the PWM channel using the command below:

1echo 9 | sudo tee /sys/class/pwm/pwmchip0/export

Set the PWM period:

1echo 100000 | sudo tee /sys/class/pwm/pwmchip0/pwm9/period

Determine the duty cycle at 50%:

1echo 50000 | sudo tee /sys/class/pwm/pwmchip0/pwm9/duty_cycle #50% duty

And activate the PWM channel:

1echo 1 | sudo tee /sys/class/pwm/pwmchip0/pwm9/enable

Consider the following Python® script if you would like to automate the command sequence:

1import subprocess
2
3def setup_pwm(pwm_chip, pwm_channel, period, duty_cycle):
4 base_path = f"/sys/class/pwm/pwmchip{pwm_chip}"
5
6 # Export the PWM channel
7 subprocess.run(f"echo {pwm_channel} | sudo tee {base_path}/export", shell=True)
8
9 # Set period
10 subprocess.run(f"echo {period} | sudo tee {base_path}/pwm{pwm_channel}/period", shell=True)
11
12 # Set duty cycle
13 subprocess.run(f"echo {duty_cycle} | sudo tee {base_path}/pwm{pwm_channel}/duty_cycle", shell=True)
14
15 # Enable the PWM channel
16 subprocess.run(f"echo 1 | sudo tee {base_path}/pwm{pwm_channel}/enable", shell=True)
17
18if __name__ == "__main__":
19 setup_pwm(0, 9, 100000, 50000) # 50% duty
Portenta Hat Carrier Fan Operation

By understanding the fundamentals of PWM and leveraging the capabilities of the Portenta Hat Carrier, you can effectively regulate fan speed as part of the main feature, ensuring optimal cooling performance and longevity of the device of interest.

Storage and Boot Options

Storage and boot-related options are provided to manage the device's data storage and control its operational sequences. Dive into this sub-section to understand the onboard storage options and boot initialization mechanisms with user-programmable actuators.

User-Programmable Push Button

The Portenta Hat Carrier boasts a streamlined, user-centric design with its multifunctional push button. The button is designed for general user-programmable functions.

A single button press can be customized according to the application's needs. Whether you need to start a specific event, switch between various states, or execute a particular action, this button is equipped for diverse implementations.

Portenta Hat Carrier Onboard Buttons
Portenta Hat Carrier Onboard Buttons

MicroSD Storage

The available microSD card slot offers the advantage of expanded storage. This is especially beneficial for processing large volumes of log data, whether from sensors or the onboard computer registry.

Portenta Hat Carrier microSD Expansion Slot
Portenta Hat Carrier microSD Expansion Slot

The following table shows an in-depth connector designation:

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 begin using a microSD card with Portenta X8, please use the following command to pull a Docker container that assists in setting up the necessary elements for interacting with the microSD card:

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

The command above will run the image immediately after the container image has been successfully pulled. You will find yourself inside the container once it is ready for use.

You will need to identify the partition scheme where the microSD card is located. If a partition table does not exist for the microSD card, you will have to use the

fdisk
command to create its partitions.

Inside the container, you can use the following commands.

To determine if the Portenta X8 has recognized the microSD card, you can use one of the following commands:

1lsblk
2
3# or
4fdisk -l

The microSD card usually appears as

/dev/mmcblk0
or
/dev/sdX
. Where X can be a, b, c, etc. depending on other connected storage devices.

Before accessing the contents of the microSD card, it needs to be mounted. For convenient operation, create a directory that will serve as the mount point:

1mkdir -p /tmp/sdcard

Use the following command to mount the microSD card to the previously created directory. Ensure you replace

XX
with the appropriate partition number (e.g., p1 for the first partition):

1mount /dev/mmcblk1p1 /tmp/sdcard

Navigate to the mount point and list the contents of the SD card:

1cd /tmp/sdcard
2ls

To write data to the microSD card, you can use the

echo
command. For example, type the following code to create a file named
hello.txt
with the content
"Hello World Carrier!"
:

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

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

1cat hello.txt

This will print on your shell the contents that were saved to the

hello.txt
file.

Once you are done with the operations related to the microSD card, it is important to unmount it properly:

1umount /tmp/sdcard

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

1mkfs.ext4 /dev/mmcblk1p1

Using Arduino IDE


To learn how to use the microSD card slot 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 Hat 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}

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 script has successfully compiled, the result should resemble the following image:

Portenta Hat Carrier with Portenta C33 SD Card Test
Portenta Hat Carrier with Portenta C33 SD Card Test

Configuration and Control

Configuration and control features allow the user to customize the device's behavior for their specific needs. If you are interested in learning how to set up network connectivity or adjust switch configurations, follow the section below.

DIP Switch Configuration

The Portenta Hat Carrier incorporates a DIP switch, giving users the ability to manage the behavior of the board. The configuration parameters of this switch differ based on which Portenta board it is paired with.

Portenta Hat Carrier DIP switch
Portenta Hat Carrier DIP switch

For configurations when the Portenta Hat Carrier is combined with the Portenta X8, the DIP switch governs these settings:

DIP Switch DesignationPosition: ONPosition: OFF
ETH CENTER TAPEthernet DisabledEthernet Enabled
BTSELFlashing Mode (ON)-

Setting the BTSEL switch to the

ON
position will place the board in Flashing Mode, allowing to update the OS Image of the Portenta X8.

When the Portenta Hat Carrier is combined with either the Portenta H7 or C33, the DIP switch adjustments are as follows:

DIP Switch DesignationPosition: ONPosition: OFF
ETH CENTER TAPEthernet EnabledEthernet Disabled
BTSELNot usedNot used

This flexibility ensures that the Portenta Hat Carrier remains adaptable to the unique needs of each paired Portenta board.

Network Connectivity

The Portenta Hat Carrier significantly augments the networking functionalities of the devices within the Portenta family through its integrated Ethernet port. Additionally, the Portenta devices destined for pairing with this carrier are inherently equipped with Wi-Fi® and Bluetooth® capabilities.

Thus, when conceptualizing and executing project developments, the user can proficiently exploit both the wired and wireless communication capabilities. The inherent wireless attributes of the Portenta devices, combined with the carrier's sophisticated onboard components and adaptable protocol choices, enable a comprehensive suite of communication solutions ideal for a wide range of applications.

Ethernet


The Portenta HAT Carrier features a gigabit Ethernet port with an RJ45 connector model TRJG16414AENL 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 1000BASE-T standard, complying with IEEE 802.3ab, guaranteeing high-speed, reliable network connections for data-intensive industrial applications.

Portenta Hat Carrier Ethernet Port
Portenta Hat Carrier Ethernet Port

The following table shows an in-depth connector designation:

Pin numberSilkscreenPower NetPortenta HD Standard PinHigh-Density Pin
1N/AGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
2ETH CENTER TAP
3N/AETH_D_PJ1-13
4N/AETH_D_NJ1-15
5N/AETH_C_PJ1-9
6N/AETH_C_NJ1-11
7N/AETH_B_PJ1-5
8N/AETH_B_NJ1-7
9N/AETH_A_PJ1-1
10N/AETH_A_NJ1-3
11N/AETH_LED2J1-19
12N/AGNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
13N/AN/A
14N/AETH_LED1J1-17

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 Hat Carrier. The following table shows the specific DIP switch configuration needed to enable Ethernet on the carrier:

Mounted Portenta DeviceETH CENTER TAP DIP SWITCH
Portenta X8Position: OFF
Portenta H7/C33Position: ON

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

It is advisable to connect the Portenta X8 through the Portenta HAT Carrier to a device with DHCP server capabilities, such as a network router, to ease the automatic assignment of an IP address. DHCP will allow the Portenta X8 to communicate with other devices on the network without manual IP configuration. Employing DHCP simplifies device management, supports dynamic reconfiguration, and provides an advantage for applications involving many devices.

In case you want to assign a manual IP to your device, or even create a direct network between your computer and your board, you can follow the multiple procedures available depending on your network devices and operating system.

Ethernet Interface With Linux


Using the Portenta X8 in combination with the Hat 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 Hat Carrier, and then connect them using an RJ45 LAN cable to your local network. Be sure that your computer and your devices are connected to the same network and are on the same IP range, been capable of seeing 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
. To measure the bandwidth, we will use the iperf3 tool, which is available here.

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

Begin by setting up the Portenta Hat 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 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:

1./iperf3 -v

It should display the version information for the iperf3 tool.

Activate the Portenta Hat 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 will be able to verify the Ethernet connection and its performance.

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

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

socket
library used 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 and 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.

If you would like to have a single script running both instances, 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.

Ethernet Interface With Arduino IDE


Below 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}

The following

Web Client
example can be considered 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}

Wi-Fi® & Bluetooth®


The Portenta Hat Carrier is designed to work flawlessly with wireless features. Among its numerous advantages is its capacity to use Wi-Fi® and Bluetooth® technologies present in the Portenta models like X8, H7, or C33. When these wireless options are activated, they can be effectively combined with the intrinsic capabilities and features that the carrier offers. This combination makes this solution more versatile and powerful for many different projects.

This integration not only broadens the spectrum of use cases for the Portenta Hat Carrier but also ensures that developers can use robust wireless communications in their applications. The effectiveness of onboard capabilities with these wireless features makes the Portenta Hat Carrier an indispensable tool for developers looking for versatile and powerful connectivity solutions.

For a comprehensive understanding of these connectivity options, kindly refer to the specific documentation for each Portenta model.

Pins

The Portenta Hat 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 Hat Carrier Back Side
Portenta Hat Carrier Back Side

In this section we will examine the 40 pin and 16 pin headers of the Portenta Hat Carrier. These headers are integral to the carrier's interfacing capabilities, providing diverse connectivity options for various applications.

40-Pin Header

The Portenta Hat Carrier provides a 40 pin header that serves as an important interface for numerous applications.

Portenta Hat Carrier 40-Pin Header
Portenta Hat Carrier 40-Pin Header

To make it easier for developers, here is a comprehensive breakdown of the 40-pin header:

Pin DescriptionPinPinPin Description
VCC (+3V3_PORTENTA)12VIN (+5V)
I2C2_SDA (I2C 2 SDA)34VIN (+5V)
I2C2_SCL (I2C 2 SCL)56GND
PWM0 (PWM_0)78SERIAL3_TX (TX3 - UART 3 TX)
GND910SERIAL3_RX (RX3 - UART 3 RX)
GPIO21112I2S_CK
GPIO61314GND
SAI_D01516SAI_CK
VCC (+3V3_PORTENTA)1718SAI_FS
SPI1_MOSI (SPI1 COPI)1920GND
SPI1_MISO (SPI1 CIPO)2122PWM1 (PWM_1)
SPI1_CK (SPI1 SCK)2324SPI1_CS (SPI1 CE)
GND2526PWM2 (PWM_2)
I2C0_SDA (I2C 0 SDA)2728I2C0_SCL (I2C 0 SCL)
SERIAL1_RX (RX1- UART 1 RX)2930GND
PWM3 (PWM_3)3132SERIAL1_TX (TX1- UART 1 TX)
PWM4 (PWM_4)3334GND
I2S_WS (I2S WS)3536PWM5 (PWM_5)
PWM6 (PWM_6)3738I2S_SDI (I2S SDI)
GND3940I2S_SDO (I2S SDO)

This layout is designed to ensure that developers have a clear understanding of each pin and its function.

16-Pin Header

The Portenta Hat Carrier features a 16-pin male header connector dedicated to analog input but also offers a variety of other functionalities. The table below provides a detailed mapping:

Pin DescriptionPinsPinsPin Description
ANALOG_A0 (A0)12ANALOG_A1 (A1)
ANALOG_A2 (A2)34ANALOG_A3 (A3)
ANALOG_A4 (A4)56ANALOG_A5 (A5)
ANALOG_A6 (A6)78ANALOG_A7 (A7)
PWM7 (PWM_7)910PWM8 (PWM_8)
LICELL (RTC Power Source)1112GPIO0 (PWM4)
VCC (+3V3_PORTENTA)1314SERIAL2_TX (TX2 - UART 2 TX)
GND1516SERIAL2_RX (RX2 - UART 2 RX)

A visual representation of the header can be seen in the image below.

Portenta Hat Carrier 16-Pin Header
Portenta Hat Carrier 16-Pin Header

It is characterized as follows:

  • Analog Pins: It integrates eight dedicated pins for analog channels. It ranges from A0 ~ A7, and each of these pins serves a unique analog channel, facilitating a range of analog signal measurements.

  • PWM Pins: Integrates dedicated PWM pins within the header. Pin 9 is labeled PWM7 (PWM_7), and Pin 10 is identified as PWM8 (PWM_8).

    Additionally, Pin 12, although a General-Purpose Input/Output (GPIO0), also supports PWM and is labeled as PWM4.

  • Serial Pins: It integrates UART 2 functionalities. Pin 14 is the transmit function, identified as SERIAL2_TX or TX2, while Pin 16 is dedicated to the receive function, labeled as SERIAL2_RX or RX2.

  • Power and Grounding: Pin 11, labeled as LICELL, serves as the Real Time Clock (RTC) power source.

    For providing a voltage source, Pin 13 offers a 3.3 V output, specifically for the Portenta module, and is marked as VCC (+3V3_PORTENTA). The Ground for this header is accessible via Pin 15, designated simply as GND.

GPIO Pins

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

Using Linux


Next conditions will help you properly set the hardware to test GPIO controls:

  1. Begin by positioning the Portenta-X8 securely onto the Portenta Hat Carrier. Make sure the High-Density connectors are securely connected.

  2. Each GPIO on the Portenta Hat Carrier is versatile and robust, designed to safely accept input voltages ranging between 0.0 V and 3.3 V. This input range ensures compatibility with an array of sensors and external devices.

  3. To prevent floating states and offer a consistent and predictable behavior, internal pull-ups are automatically enabled on all input pins. This default configuration means that, unless actively driven low, the pins will naturally read as high (or 3.3 V).

When all conditions are set and in place, access the Portenta X8's shell with admin (root) access as follows:

1adb shell
2sudo su -

Enter the password

fio
when prompted. Next, access the
x8-devel
Docker container with the command:

1docker exec -it x8-devel sh

Navigate to the directory containing the GPIO example, named

gpios.py
:

1cd root/examples/portenta-hat-carrier

Run the

gpios.py
script to read the status of all available GPIOs on the 40-pin header:

1#!/usr/bin/env python3
2
3# created 12 October 2023
4# by Riccardo Mereu & Massimo Pennazio
5
6import os
7
8if os.environ['CARRIER_NAME'] != "rasptenta":
9 print("This script requires Portenta HAT carrier")
10 exit(1)
11
12import Portenta.GPIO as GPIO
13
14GPIO.setmode(GPIO.BCM)
15
16all_pins_in_header = [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]
17
18GPIO.setup(all_pins_in_header, GPIO.IN)
19
20for pin in all_pins_in_header:
21 try:
22 print(GPIO.input(pin))
23 except RuntimeError as e:
24 print("Error reading GPIO %s: %s" % (str(pin), str(e)))
25
26GPIO.cleanup()

This script will help you verify the following considerations:

  • 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 that unintentional fluctuations are minimized.

By employing this script, not only do you gain a deeper insight into the state of your GPIOs, but you also save valuable time and reduce the margin for error.

Whether you are debugging, prototyping, or setting up a new project, this script is an invaluable tool for all Portenta Hat Carrier users.

You can also retrieve information about the available GPIOs on the Portenta X8's shell using the following command:

1cat /sys/kernel/debug/gpio

Using Arduino IDE


If Portenta Hat Carrier is paired with Portenta H7 or Portenta C33, consider using the following example:

1const int actPin = 2; // the number of the activation pin (GPIO)
2const int ledPin = <PD_5/30>; // User programmable LED GPIO3 corresponding to paired Portenta board
3int actState = 0; // variable for reading the activation status
4
5void setup() {
6 pinMode(ledPin, OUTPUT); // initialize the User programmable LED of Portenta Hat Carrier
7 pinMode(actPin, INPUT); // initialize the activation pin as an input
8}
9
10void loop() {
11 actState = digitalRead(actPin); // read the state of the activation value
12
13 if (actState == HIGH) { // if the GPIO pin has feedback
14 digitalWrite(ledPin, HIGH);
15 } else {
16 digitalWrite(ledPin, LOW);
17 }
18}

This example uses a designated GPIO pin to set the user-programmable LED on the Portenta Hat Carrier to either a HIGH or LOW state.

Alternatively, the following example controls the user-programmable LED on the Portenta Hat Carrier based on potentiometer input:

1const int potPin = A0; // the number of the potentiometer pin (16-Pin header)
2const int ledPin = <PD_5/30>; // User programmable LED GPIO3 corresponding to paired Portenta board
3
4int potValue = 0; // value read from the potentiometer
5int ledThreshold = 0; // PWM value for the LED brightness (0 to 255)
6
7void setup() {
8 pinMode(ledPin, OUTPUT); // initialize the User programmable LED of Portenta Hat Carrier
9 // choose either PD_5 for H7, or 30 for C33
10}
11
12void loop() {
13 potValue = analogRead(potPin); // read the pot value (0 to 1023)
14 ledThreshold = map(potValue, 0, 1023, 0, 255); // scale it for user programmable LED GPIO3 activation threshold
15
16 if (ledThreshold >= 128){
17 digitalWrite(ledPin, HIGH); // set the GPIO3 LED High if potentiometer is mapped above 128 value
18 } else {
19 digitalWrite(ledPin, LOW); // set the GPIO3 LED Low if potentiometer is mapped below 128 value
20 }
21 delay(10);
22}

PWM Pins

The Portenta Hat Carrier has 10 digital pins with PWM functionality, mapped as follows:

Pin numberSilkscreenPortenta HD Standard PinHigh-Density Pin
40-Pin Header
7PWM0PWM_0J2-59
22PWM1PWM_1J2-61
26PWM2PWM_2J2-63
31PWM3PWM_3J2-65
33PWM4PWM_4J2-67
36PWM5PWM_5J2-60
37PWM6PWM_6J2-62
16-Pin Header
9PWM7PWM_7J2-64
10PWM8PWM_8J2-66
12PWM4GPIO_0J2-46

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 = 1000000 # 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 included in the Arduino programming language can be used to access the PWM pins.

The example code shown below grabs a pin compatible with PWM functionality to control the brightness of an LED connected 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

For developers aiming to investigate and understand the intricate details of development, the Portenta Hat Carrier features a built-in JTAG interface. This tool is crucial for hardware debugging, offering real-time observation. Through the JTAG pins, users can smoothly debug and program, guaranteeing accurate and optimal device performance.

Portenta Hat Carrier onboard JTAG pin
Portenta Hat Carrier onboard JTAG pin

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

Pin numberPower NetPortenta HD Standard PinHigh-Density PinInterface
1+3V3_PORTENTAVCCJ2-23, J2-34, J2-43, J2-69
2JTAG_SWDJ1-75JTAG SWD
3GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
4JTAG_SCKJ1-77JTAG SCK
5GNDGNDJ1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70
6JTAG_SWOJ1-79JTAG SWO
7NCNC
8JTAG_TDIJ1-78JTAG TDI
9JTAG_TRSTJ1-80JTAG TRST
10JTAG_RSTJ1-73JTAG RST

Understanding Device Tree Blobs (DTB) Overlays

Device Tree Blobs (DTB) And DTB Overlays

In the world of embedded systems, U-boot and the Linux kernel use a concept called Device Tree Blobs (DTB) to describe a board's hardware configuration. This approach allows for a unified main source tree to be used across different board configurations, ensuring