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.
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.
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:
If you want to use the Portenta Hat Carrier with a Portenta X8, check the following bullet points:
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:
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.
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.
The Portenta Hat Carrier, designed for Portenta SOM boards like the Portenta X8, H7, and C33, offers a diverse power supply range:
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.
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.
Item | Onboard modules |
---|---|
J1, J2 | High-Density connectors for Portenta boards |
J3 | JTAG male connector for debugging |
J4 | USB-A female connector for data logging and external devices |
J5 | 40-pin male header compatible with Raspberry Pi® Hats |
J6 | 16-pin male header for analog, GPIOs, UART and RTC battery pins |
J7 | MicroSD slot for data logging and media purposes |
J8 | RJ45 connector for Ethernet |
J9 | Screw terminal for power supply and CAN FD support |
J10 | MIPI camera connector, exclusive for Portenta X8 |
J11 | PWM male header connector to control fan speed |
SW2 | DIP switch with two sliders: ETH CENTER TAP and BTSEL |
PB1 | User Button |
The full datasheet is available and downloadable as PDF from the link below:
The full schematics are available and downloadable as PDF from the link below:
The full STEP files are available and downloadable from the link below:
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.
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.
With the Portenta mounted to the carrier, you can proceed to power the carrier and start prototyping.
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.
The Portenta Hat Carrier can deliver a maximum of 1.5 A.
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:
Subsequently, you can check how the Portenta Hat Carrier distributes power resources with the 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:
Parameter | Min | Typ | Max | Unit |
---|---|---|---|---|
VIN from onboard screw terminal* of the Carrier | 7.0 | - | 32.0 | V |
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.5 | A |
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.
The Portenta Hat Carrier provides different functionalities based on the connected Portenta family board, as shown in the table below:
Features | Portenta X8 | Portenta H7 | Portenta 33 |
---|---|---|---|
40-Pin Header | Compatible | Compatible | Compatible |
16-Pin Header | Compatible | Compatible | Compatible |
MIPI Camera | Compatible | Incompatible | Incompatible |
Ethernet | 1 Gbit (DIP: OFF) | 100 Mbit (DIP: ON) | 100 Mbit (DIP: ON) |
PWM Fan | Available | Available | Available |
JTAG Debug | Available | Available | Available |
Storage Exp. | MicroSD slot | MicroSD slot | MicroSD slot |
CAN FD | Available | Available | Available |
USB-A Support | Available | Available | Available |
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.
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.
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.
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 shell2sudo 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/value2echo 0 >/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.
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 python32import time3
4class GPIOController:5 def __init__(self, gpio_number):6 self.gpio_number = gpio_number7 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 GPIO41 gpio.export()42
43 # Set as output44 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.
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:
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 board2void setup() {3 // Initialize the digital pin of the chosen SOM as an output4 pinMode(<DIGITAL_PIN>, OUTPUT);5}6
7// the loop function runs over and over again forever8void loop() {9 digitalWrite(<DIGITAL_PIN>, HIGH); // turn the LED on (HIGH is the voltage level)10 delay(1000); // wait for a second11 digitalWrite(<DIGITAL_PIN>, LOW); // turn the LED off by making the voltage LOW12 delay(1000); // wait for a second13}
Make sure to replace
<DIGITAL_PIN>
with the appropriate value for your chosen Portenta board: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 board2void setup() {3 // Initialize the digital pin of the chosen SOM as an output4 pinMode(PF_4, OUTPUT);5}6
7// the loop function runs over and over again forever8void loop() {9 digitalWrite(PF_4, HIGH); // turn the LED on (HIGH is the voltage level)10 delay(1000); // wait for a second11 digitalWrite(PF_4, LOW); // turn the LED off by making the voltage LOW12 delay(1000); // wait for a second13}
After successfully uploading the sketch, the user-configurable LED will start blinking. The following clip illustrates the expected LED blink pattern.
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.
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.
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.
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.
The Portenta Hat Carrier features a USB interface suitable for data logging and connecting external devices.
If you are interested in the USB-A port pinout, the following table may serve to understand its connection distribution:
Pin number | Power Net | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
1 | +5V | VIN / USB0_VBUS | J1-21, J1-24, J1-32, J1-41, J1-48 | |
2 | USB0_D_N | J1-28 | USB D- | |
3 | USB0_D_P | J1-26 | USB D+ | |
4 | GND | GND | J1-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.
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.
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 15010
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 INITIALIZATION29 */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_callback40 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 connected47 */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 filesystem68 */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 filesystem74 // this should only happen on the first boot75 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 folder86 */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 directory111 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 CONTENT172 */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 bytes219 */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 FILE230 */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 CONTENT248 */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 // Empty296}
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.
Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin |
---|---|---|---|
1 | A0 | ANALOG_A0 | J2-73 |
2 | A1 | ANALOG_A1 | J2-75 |
3 | A2 | ANALOG_A2 | J2-77 |
4 | A3 | ANALOG_A3 | J2-79 |
5 | A4 | ANALOG_A4 | J2-74 |
6 | A5 | ANALOG_A5 | J2-76 |
7 | A6 | ANALOG_A6 | J2-78 |
8 | A7 | ANALOG_A7 | J2-80 |
The built-in features of the Arduino programming language (
function) can be used to access the eight analog input pins on the Arduino IDE.analogRead()
Please, refer to the board pinout section of the user manual to find the analog pins on the board.
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 None8
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 V17 new_value = (float) (value/65535)*3.318 print(f"Value mapped between 0-3.3 V: {new_value}")
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 value2int potentiometerPin = A0;3int potentiometerValue = 0;4
5void setup() {6 // Initialize Serial communication7 Serial.begin(9600);8}9
10void loop() {11 // Read the voltage value from the potentiometer12 potentiometerValue = analogRead(potentiometerPin);13
14 // Print the potentiometer voltage value to the Serial Monitor15 Serial.print("- Potentiometer voltage value: ");16 Serial.println(potentiometerValue);17
18 // Wait for 1000 milliseconds19 delay(1000);20}
The following example can be considered for Portenta C33:
1#include "analogWave.h" // Include the library for analog waveform generation2
3analogWave wave(DAC); // Create an instance of the analogWave class, using the DAC pin4
5int freq = 10; // in hertz, change accordingly6
7void setup() {8 Serial.begin(115200); // Initialize serial communication at a baud rate of 1152009 wave.sine(freq); // Generate a sine wave with the initial frequency10}11
12void loop() {13 // Read an analog value from pin XX and map it to a frequency range14 freq = map(analogRead(XX), 0, 1024, 0, 10000);15
16 // Print the updated frequency to the serial monitor17 Serial.println("Frequency is now " + String(freq) + " hz");18
19 wave.freq(freq); // Set the frequency of the waveform generator to the updated value20 delay(1000); // Delay for one second before repeating21}
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.
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 number | Silkscreen | Power Net | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|---|
1 | VIN 7-32VDC | INPUT_7V-32V | |||
2 | GND | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
3 | GND | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
4 | 5V | +5V | VIN | J1-21, J1-24, J1-32, J1-41, J1-48 | |
5 | CANH | J1-49 (Through U1) | CAN BUS - CANH | ||
6 | CANL | J1-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.
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.
The MIPI connector is distributed as follows:
Pin number | Power Net | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
1 | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54 | |
2 | CAM_D0_D0_N | J2-16, J2-24, J2-33, J2-44, J2-57, J2-70 | ||
3 | CAM_D1_D0_P | J2-14 | ||
4 | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
5 | CAM_D2_D1_N | J2-12 | ||
6 | CAM_D3_D1_P | J2-10 | ||
7 | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
8 | CAM_CK_CK_N | J2-20 | ||
9 | CAM_VS_CK_P | J2-18 | ||
10 | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
11 | GPIO_5 | J2-56 | ||
12 | NC | NC | ||
13 | I2C1_SCL | J1-45 | I2C 1 SCL | |
14 | I2C1_SDA | J1-43 | I2C 1 SDA | |
15 | +3V3_PORTENTA | VCC | J2-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:
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 12fw_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 12setenv overlays ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_rasptenta_base ov_carrier_rasptenta_ov5647_camera_mipi3saveenv
Define the runtime directory for
Wayland
and load the necessary module for the OV5647 camera:1export XDG_RUNTIME_DIR=/run # location of wayland-0 socket2modprobe 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/video02v4l2-ctl -d /dev/video0 --list-ctrls
Using
GStreamer
, capture a single frame in JPEG
format:1export GST_DEBUG=32gst-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).
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.
The fan can be connected via PWM pins available on the Portenta Hat Carrier. The connector has the following structure:
Pin number | Silkscreen | Power Net | Portenta HD Standard Pin | High-Density Pin |
---|---|---|---|---|
1 | PWM9 | PWM_9 | J2-68 | |
2 | N/A | |||
3 | 5V | +5V | VIN | J1-21, J1-24, J1-32, J1-41, J1-48 |
4 | GND | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 |
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 channel5 with open(f"{base_path}/export", "w") as f:6 f.write(str(pwm_channel))7
8 # Set period9 with open(f"{base_path}/pwm{pwm_channel}/period", "w") as f:10 f.write(str(period))11
12 # Set duty cycle13 with open(f"{base_path}/pwm{pwm_channel}/duty_cycle", "w") as f:14 f.write(str(duty_cycle))15
16 # Enable the PWM channel17 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 PWM27 setup_pwm(0, 9, 100000, 50000) # 50% duty28
29 # Read and print thermal temperature30 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 subprocess2
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 channel7 subprocess.run(f"echo {pwm_channel} | sudo tee {base_path}/export", shell=True)8
9 # Set period10 subprocess.run(f"echo {period} | sudo tee {base_path}/pwm{pwm_channel}/period", shell=True)11
12 # Set duty cycle13 subprocess.run(f"echo {duty_cycle} | sudo tee {base_path}/pwm{pwm_channel}/duty_cycle", shell=True)14
15 # Enable the PWM channel16 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
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-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.
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.
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.
The following table shows an in-depth connector designation:
Pin number | Silkscreen | Power Net | Portenta HD Standard Pin | High-Density Pin |
---|---|---|---|---|
1 | N/A | SDC_D2 | J1-63 | |
2 | N/A | SDC_D3 | J1-65 | |
3 | N/A | SDC_CMD | J1-57 | |
4 | N/A | VDD_SDCARD | VSD | J1-72 |
5 | N/A | SDC_CLK | J1-55 | |
6 | N/A | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 |
7 | N/A | SDC_D0 | J1-59 | |
8 | N/A | SDC_D1 | J1-61 | |
CD1 | N/A | SDC_CD | J1-67 | |
CD2 | N/A | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 |
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:
1lsblk2
3# or4fdisk -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/sdcard2ls
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
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 filesystem15 // this should only happen on the first boot16 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 directory38 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 // Empty48}
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 15010
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 INITIALIZATION22 */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 filesystem37 */38 Serial.println("Mounting SDCARD...");39 int err = fs.mount(&block_device);40 if (err) {41 // Reformat if we can't mount the filesystem42 // this should only happen on the first boot43 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 folder53 */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 directory78 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 CONTENT139 */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 bytes186 */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 FILE197 */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 CONTENT215 */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 // Empty263}
Once the script has successfully compiled, the result should resemble the following image:
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.
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.
For configurations when the Portenta Hat Carrier is combined with the Portenta X8, the DIP switch governs these settings:
DIP Switch Designation | Position: ON | Position: OFF |
---|---|---|
ETH CENTER TAP | Ethernet Disabled | Ethernet Enabled |
BTSEL | Flashing 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 Designation | Position: ON | Position: OFF |
---|---|---|
ETH CENTER TAP | Ethernet Enabled | Ethernet Disabled |
BTSEL | Not used | Not used |
This flexibility ensures that the Portenta Hat Carrier remains adaptable to the unique needs of each paired Portenta board.
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.
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.
The following table shows an in-depth connector designation:
Pin number | Silkscreen | Power Net | Portenta HD Standard Pin | High-Density Pin |
---|---|---|---|---|
1 | N/A | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 |
2 | ETH CENTER TAP | |||
3 | N/A | ETH_D_P | J1-13 | |
4 | N/A | ETH_D_N | J1-15 | |
5 | N/A | ETH_C_P | J1-9 | |
6 | N/A | ETH_C_N | J1-11 | |
7 | N/A | ETH_B_P | J1-5 | |
8 | N/A | ETH_B_N | J1-7 | |
9 | N/A | ETH_A_P | J1-1 | |
10 | N/A | ETH_A_N | J1-3 | |
11 | N/A | ETH_LED2 | J1-19 | |
12 | N/A | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 |
13 | N/A | N/A | ||
14 | N/A | ETH_LED1 | J1-17 |
Ethernet connection speeds differ based on the associated Portenta board:
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 Device | ETH CENTER TAP DIP SWITCH |
---|---|
Portenta X8 | Position: OFF |
Portenta H7/C33 | Position: 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.
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 shell2sudo 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:1ifconfig2
3# Or4ip 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:
1iperf32 |___bin3 |___include4 |___lib5 |___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 shell2iperf3 -c <Server IP>3
4# For Windows5.\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
library used to create the socket and establish a computer network.socket
The below script would be used for Server side (TCP/IP) operations:
1#!/usr/bin/env python32
3import socket4
5def start_server():6 HOST = '127.0.0.1' # Localhost7 PORT = 65432 # Port to listen on8
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 break20 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 python32
3import socket4
5def start_client():6 HOST = '127.0.0.1' # The server's hostname or IP address7 PORT = 65432 # The port used by the server8
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 socket2import threading3
4def server_function():5 HOST = '127.0.0.1'6 PORT = 654327
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 = 6543223
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 thread32 server_thread = threading.Thread(target=server_function)33 server_thread.start()34
35 # Wait a bit to ensure server is up before starting client36 threading.Event().wait(1)37
38 # Start client function39 client_function()40
41 # Join server thread42 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.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 shield7// 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 assign15IPAddress ip(192, 168, 2, 177);16IPAddress myDns(192, 168, 2, 1);17
18// Initialize the Ethernet client library19// with the IP address and port of the server20// that you want to connect to (port 80 is default for HTTP):21EthernetClient client;22
23// Variables to measure the speed24unsigned long beginMicros, endMicros;25unsigned long byteCount = 0;26bool printWebData = true; // set to false for better speed measurement27
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 only35 }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 present42 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 hardware46 }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 available82 // 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 assign9IPAddress ip(10, 130, 22, 84);10
11// Initialize the Ethernet client library12// with the IP address and port of the server13// 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 only22 }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 above31 // 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.conn