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.
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.
- Arduino IDE 1.8.10+, Arduino IDE 2.0+, or Arduino Web Editor in case you want to use the auxiliary microcontroller of the Portenta X8 to run Arduino code.
In case you want to use the Portenta Hat Carrier with a Portenta H7/C33:
- Arduino IDE 1.8.10+, Arduino IDE 2.0+, or Arduino Web Editor is needed to use the Portenta H7/C33 to run Arduino code.
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.
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.
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.
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
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 |
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.
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.
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.
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.
Recommended Operating Conditions
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.
Carrier Characteristics Highlight
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.
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.
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 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/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 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.
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 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 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:- 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 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:
- Portenta C33 user manual.
- Portenta H7 set-up guide.
- Portenta X8 user manual. You can also read the tutorial providing a step-by-step guide on how to upload sketches to the M4 Core on Arduino Portenta X8 here.
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.
- Complete Portenta X8 pinout information
- Complete Portenta H7 pinout information
- Complete Portenta C33 pinout information
USB Interface
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.
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 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}
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.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 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 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}")
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 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}
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.
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.
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.
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:
- OmniVision OV5647 sensor (Raspberry Pi® Camera Module 1)
- Sony IMX219 sensor (Raspberry Pi® Camera Module 2)
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 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).
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.
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 |
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 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 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.
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.
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 |
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:
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
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 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
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.
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.
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.
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:
- 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 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.
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 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.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 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.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.
- Portenta X8 connectivity: Wi-Fi® configuration and Bluetooth®
- Portenta H7 connectivity: Wi-Fi® access point and BLE connectivity
- Portenta C33 User Manual: Wi-Fi® and Bluetooth®
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.
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.
To make it easier for developers, here is a comprehensive breakdown of the 40-pin header:
Pin Description | Pin | Pin | Pin Description |
---|---|---|---|
VCC (+3V3_PORTENTA) | 1 | 2 | VIN (+5V) |
I2C2_SDA (I2C 2 SDA) | 3 | 4 | VIN (+5V) |
I2C2_SCL (I2C 2 SCL) | 5 | 6 | GND |
PWM0 (PWM_0) | 7 | 8 | SERIAL3_TX (TX3 - UART 3 TX) |
GND | 9 | 10 | SERIAL3_RX (RX3 - UART 3 RX) |
GPIO2 | 11 | 12 | I2S_CK |
GPIO6 | 13 | 14 | GND |
SAI_D0 | 15 | 16 | SAI_CK |
VCC (+3V3_PORTENTA) | 17 | 18 | SAI_FS |
SPI1_MOSI (SPI1 COPI) | 19 | 20 | GND |
SPI1_MISO (SPI1 CIPO) | 21 | 22 | PWM1 (PWM_1) |
SPI1_CK (SPI1 SCK) | 23 | 24 | SPI1_CS (SPI1 CE) |
GND | 25 | 26 | PWM2 (PWM_2) |
I2C0_SDA (I2C 0 SDA) | 27 | 28 | I2C0_SCL (I2C 0 SCL) |
SERIAL1_RX (RX1- UART 1 RX) | 29 | 30 | GND |
PWM3 (PWM_3) | 31 | 32 | SERIAL1_TX (TX1- UART 1 TX) |
PWM4 (PWM_4) | 33 | 34 | GND |
I2S_WS (I2S WS) | 35 | 36 | PWM5 (PWM_5) |
PWM6 (PWM_6) | 37 | 38 | I2S_SDI (I2S SDI) |
GND | 39 | 40 | I2S_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 Description | Pins | Pins | Pin Description |
---|---|---|---|
ANALOG_A0 (A0) | 1 | 2 | ANALOG_A1 (A1) |
ANALOG_A2 (A2) | 3 | 4 | ANALOG_A3 (A3) |
ANALOG_A4 (A4) | 5 | 6 | ANALOG_A5 (A5) |
ANALOG_A6 (A6) | 7 | 8 | ANALOG_A7 (A7) |
PWM7 (PWM_7) | 9 | 10 | PWM8 (PWM_8) |
LICELL (RTC Power Source) | 11 | 12 | GPIO0 (PWM4) |
VCC (+3V3_PORTENTA) | 13 | 14 | SERIAL2_TX (TX2 - UART 2 TX) |
GND | 15 | 16 | SERIAL2_RX (RX2 - UART 2 RX) |
A visual representation of the header can be seen in the image below.
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:
Begin by positioning the Portenta-X8 securely onto the Portenta Hat Carrier. Make sure the High-Density connectors are securely connected.
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.
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 shell2sudo 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 python32
3# created 12 October 20234# by Riccardo Mereu & Massimo Pennazio5
6import os7
8if os.environ['CARRIER_NAME'] != "rasptenta":9 print("This script requires Portenta HAT carrier")10 exit(1)11
12import Portenta.GPIO as GPIO13
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 board3int actState = 0; // variable for reading the activation status4
5void setup() {6 pinMode(ledPin, OUTPUT); // initialize the User programmable LED of Portenta Hat Carrier7 pinMode(actPin, INPUT); // initialize the activation pin as an input8}9
10void loop() {11 actState = digitalRead(actPin); // read the state of the activation value12
13 if (actState == HIGH) { // if the GPIO pin has feedback14 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 board3
4int potValue = 0; // value read from the potentiometer5int 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 Carrier9 // choose either PD_5 for H7, or 30 for C3310}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 threshold15
16 if (ledThreshold >= 128){17 digitalWrite(ledPin, HIGH); // set the GPIO3 LED High if potentiometer is mapped above 128 value18 } else {19 digitalWrite(ledPin, LOW); // set the GPIO3 LED Low if potentiometer is mapped below 128 value20 }21 delay(10);22}
PWM Pins
The Portenta Hat Carrier has 10 digital pins with PWM functionality, mapped as follows:
Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin |
---|---|---|---|
40-Pin Header | |||
7 | PWM0 | PWM_0 | J2-59 |
22 | PWM1 | PWM_1 | J2-61 |
26 | PWM2 | PWM_2 | J2-63 |
31 | PWM3 | PWM_3 | J2-65 |
33 | PWM4 | PWM_4 | J2-67 |
36 | PWM5 | PWM_5 | J2-60 |
37 | PWM6 | PWM_6 | J2-62 |
16-Pin Header | |||
9 | PWM7 | PWM_7 | J2-64 |
10 | PWM8 | PWM_8 | J2-66 |
12 | PWM4 | GPIO_0 | J2-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 python32
3import time4import subprocess5
6# Define the PWM chip, channel, and other parameters7PWM_CHIP = 08PWM_CHANNEL = 9 # Replace with the correct channel if necessary9BASE_PATH = f"/sys/class/pwm/pwmchip{PWM_CHIP}/pwm{PWM_CHANNEL}"10PERIOD = 1000000 # 1 second in nanoseconds11FADE_DURATION = 0.03 # 30 milliseconds12
13# Define brightness and fade amount variables14brightness = 015fadeAmount = 5 * (PERIOD // 255) # Scale fadeAmount for our period16
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 += fadeAmount33 if brightness <= 0 or brightness >= 255:34 fadeAmount = -fadeAmount35 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 PWM2
3void setup() {4 pinMode(ledPin, OUTPUT); // Configure the pin as OUTPUT5}6
7void loop() {8 // Increase brightness9 for (int brightness = 0; brightness <= 255; brightness++) {10 analogWrite(ledPin, brightness);11 delay(10);12 }13
14 // Decrease brightness15 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.
The pins used for the JTAG debug port on the Portenta Hat Carrier are the following:
Pin number | Power Net | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
1 | +3V3_PORTENTA | VCC | J2-23, J2-34, J2-43, J2-69 | |
2 | JTAG_SWD | J1-75 | JTAG SWD | |
3 | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
4 | JTAG_SCK | J1-77 | JTAG SCK | |
5 | GND | GND | J1-22, J1-31, J1-42, J1-47, J1-54, J2-24, J2-33, J2-44, J2-57, J2-70 | |
6 | JTAG_SWO | J1-79 | JTAG SWO | |
7 | NC | NC | ||
8 | JTAG_TDI | J1-78 | JTAG TDI | |
9 | JTAG_TRST | J1-80 | JTAG TRST | |
10 | JTAG_RST | J1-73 | JTAG 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 consistency.
The boards, acting as carriers, allow various peripherals to be connected, such as temperature sensors or accelerometers. These carriers serve as expansion connectors. You might want to connect various peripherals and be able to add or remove them easily.
The concept of modularity is applied to the DTB, resulting in DTB overlays. The hardware configuration is split into multiple small files, each representing a different peripheral or function in the form of a DTB overlay.
During the early boot stage, these overlays are merged together into a single DTB and loaded into RAM. This approach enables users to select and change configurations with ease. However, it is important to note that changing the hardware configuration requires a system reboot to maintain system stability.
Handling DTB Overlays
To modify and maintain the Device Tree Blob (DTB) overlays of your Portenta X8 so it can support different hardware and devices, please read and execute the following steps.
Custom DTB Overlays
In cases where the required DTB overlay is not readily available and a specific configuration that is not part of the pre-compiled set is needed, it is possible to create customized DTB overlays.
DTB overlays originate from readable source files known as DTS files. Users with the respective experience can modify these DTS files and cross-compile them to create tailored overlays suited to their needs.
Automated Load And Carrier Detection
U-boot can be configured to automatically load specific DTB overlays based on the carrier board it detects, in this case the Portenta Hat Carrier, either by probing specific hardware or by reading an identification ID from an EEPROM.
For instance, for a Portenta-X8 placed on a Portenta HAT Carrier, upon logging into the board and executing subsequent commands on the shell, the expected output is as follows:
1fw_printenv overlays2overlays=ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_rasptenta_base3
4fw_printenv carrier_name5carrier_name=rasptenta6
7fw_printenv is_on_carrier8is_on_carrier=yes
This information is written by U-boot during boot in a step referred to as auto carrier detection. You can modify the variables from user space, but after a reboot, they revert to their default state unless the
carrier_custom
variable is set to 1
.1fw_setenv carrier_custom 1
This serves as an escape mechanism to enable user-based configurations.
1fw_setenv carrier_custom 12fw_setenv carrier_name rasptenta3fw_setenv is_on_carrier yes4fw_setenv overlays "ov_som_lbee5kl1dx ov_som_x8h7 ov_carrier_rasptenta_base ov_carrier_rasptenta_pwm_fan ov_carrier_rasptenta_ov5647_camera_mipi ov_rasptenta_iqaudio_codec"
The commands above enable functionalities such as a speed-controlled fan connector, an OV5647 based RPi v1.3 camera, and an IQ Audio Codec Zero audio HAT.
Hardware Configuration Layers
Hardware configuration is divided into the following layers:
- Layer 0: System on Module (SoM), prefixed with
.ov_som_
- Layer 1: Carrier boards, prefixed with
.ov_carrier_
- Layer 2: HATs and Cameras, which is usually a concatenation of the carrier name and the hat name or functionality.
EEPROMs, which store identification IDs, are typically defined on Layer 1 and accessible on I2C1. Some HATs may also have EEPROMs according to the Raspberry Pi® standard (ID_SD, ID_SC), accessible on I2C0.
There are some overlays which add specific functionalities. For example:
: Adds Wi-Fi®ov_som_lbee5kl1dx
: Adds the H7 external microcontrollerov_som_x8h7
: Base support for Portenta Hat Carrierov_carrier_rasptenta_base
When no known carrier is detected and the Portenta X8 is mounted as the main board, the first two overlays mentioned above are applied by default.
Distinction Between System And Hardware Configuration
The distinction between system and hardware configuration is crucial. System configuration includes settings such as user creation and Wi-Fi® passwords, whereas hardware configuration is explicitly defined through the device tree.
In production environments, the addition of custom compiled device tree overlays is restricted to maintain system integrity and security.
Raspberry Pi® HAT
The Portenta Hat Carrier is notable for its compatibility with Hardware Attached on Top (HAT) add-on boards.
A Hardware Attached on Top (HAT) is known as a standardized add-on module designed to be interfaced with compatible host systems. The HAT concept can be understood as a modular approach to hardware extension.
Following certain design rules to specific mechanical and electronic design criteria, a HAT usually has a built-in memory chip (EEPROM). This allows the host system to automatically recognize and potentially configure itself corresponding to the attached module.
The main objective of a HAT is to augment the functionalities of the host device, allowing for capabilities ranging from sensor integration and display enhancements to advanced audio processing and communication features.
The standardized design of HATs ensures they are compatible and easy to use with various devices. This makes them suitable for both seasoned professionals and enthusiasts.
These is the officially compatible list of HATs:
Stepper Motor HAT: it is a HAT that drives stepper motors, enabling precise control of rotation direction, angle, speed, and steps, suitable for projects like CNC, 3D printers, and robotics. It uses a DRV8825 dual H-bridge motor driver.
RPi Relay Board: it is a HAT that eases the control of high-voltage devices, featuring three channels, and photo coupling isolation. It helps provide safe device switching for various applications.
The example scripts are located within the Docker container. To access these scripts and test them with the Hat mounted, execute the following command:
1docker exec -it x8-devel sh
Upon gaining access to the container, all relevant Portenta Hat Carrier scripts are conveniently located in the
root/examples/portenta-hat-carrier
directory. Alternatively, you also have the option to manually dockerize the scripts to run them within ADB shell of the Portenta X8.To use the example script, a series of commands can be used. These will help you define and export necessary modules before executing the desired example. To start, the Python® shell can be launched within the container using:
1python3
To use modules like smbus2, the following sequence of commands will help you import such modules:
1import smbus2
To use a specific class or function from the module:
1from smbus2 import SMBus
Setting variables is straightforward. Depending on your requirement, assign values in either HEX or DEC:
1# Value can be in HEX or DEC2variable_name = Value
To run an example script within the Python® shell, consider using the following command:
1with open('example_script.py', 'r') as file:2... exec(file.read())
If you prefer traditional methods of execution, the following command can be used:
1python3 example_script.py
This last command for example is also applicable within ADB shell of the Portenta X8.
The following sections will help you become familiar with the examples found within the
root/examples/portenta-hat-carrier
directory. This directory contains both the RPi Relay Board and the Stepper Motor HAT. These examples are used within the Linux environment.RPi Relay Board
The RPi Relay Board is a dynamic board that consists of three channels: a high-level trigger and two low-level triggers. With the capability to manage up to 250V AC or 30V DC with a current rating of 2A, it operates using the I2C interface, renowned for its high-quality relays.
The continuing script offers an interface for interaction with the Seeed Studio Raspberry Pi Relay Board. Authored by John M. Wargo, it is a refined version of the sample code available on the Seeed Studio Wiki. The script uses the smbus library to interface with the relay board and has a default I2C address set to
0x20
.It can interact with up to four relay ports on the board. Among its various features, it can turn a specific relay on or off, toggle all relays simultaneously, toggle a particular relay's state, and retrieve the status of any relay. Furthermore, it has built-in error handling to ensure that a valid integer relay number is specified.
1from __future__ import print_function2
3from smbus2 import SMBus4
5# The number of relay ports on the relay board.6# This value should never change!7NUM_RELAY_PORTS = 48
9# Change the following value if your Relay board uses a different I2C address.10DEVICE_ADDRESS = 0x20 # 7 bit address (will be left shifted to add the read write bit)11
12# Don't change the values, there's no need for that.13DEVICE_REG_MODE1 = 0x0614DEVICE_REG_DATA = 0xff15
16bus = SMBus(3) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)17
18
19def relay_on(relay_num):20 global DEVICE_ADDRESS21 global DEVICE_REG_DATA22 global DEVICE_REG_MODE123
24 if isinstance(relay_num, int):25 # do we have a valid relay number?26 if 0 < relay_num <= NUM_RELAY_PORTS:27 print('Turning relay', relay_num, 'ON')28 DEVICE_REG_DATA &= ~(0x1 << (relay_num - 1))29 bus.write_byte_data(DEVICE_ADDRESS, DEVICE_REG_MODE1, DEVICE_REG_DATA)30 else:31 print('Invalid relay #:', relay_num)32 else:33 print('Relay number must be an Integer value')34
35
36def relay_off(relay_num):37 global DEVICE_ADDRESS38 global DEVICE_REG_DATA39 global DEVICE_REG_MODE140
41 if isinstance(relay_num, int):42 # do we have a valid relay number?43 if 0 < relay_num <= NUM_RELAY_PORTS:44 print('Turning relay', relay_num, 'OFF')45 DEVICE_REG_DATA |= (0x1 << (relay_num - 1))46 bus.write_byte_data(DEVICE_ADDRESS, DEVICE_REG_MODE1, DEVICE_REG_DATA)47 else:48 print('Invalid relay #:', relay_num)49 else:50 print('Relay number must be an Integer value')51
52
53def relay_all_on():54 global DEVICE_ADDRESS55 global DEVICE_REG_DATA56 global DEVICE_REG_MODE157
58 print('Turning all relays ON')59 DEVICE_REG_DATA &= ~(0xf << 0)60 bus.write_byte_data(DEVICE_ADDRESS, DEVICE_REG_MODE1, DEVICE_REG_DATA)61
62
63def relay_all_off():64 global DEVICE_ADDRESS65 global DEVICE_REG_DATA66 global DEVICE_REG_MODE167
68 print('Turning all relays OFF')69 DEVICE_REG_DATA |= (0xf << 0)70 bus.write_byte_data(DEVICE_ADDRESS, DEVICE_REG_MODE1, DEVICE_REG_DATA)71
72
73def relay_toggle_port(relay_num):74 print('Toggling relay:', relay_num)75 if relay_get_port_status(relay_num):76 # it's on, so turn it off77 relay_off(relay_num)78 else:79 # it's off, so turn it on80 relay_on(relay_num)81
82
83def relay_get_port_status(relay_num):84 # determines whether the specified port is ON/OFF85 global DEVICE_REG_DATA86 print('Checking status of relay', relay_num)87 res = relay_get_port_data(relay_num)88 if res > 0:89 mask = 1 << (relay_num - 1)90 # return the specified bit status91 # return (DEVICE_REG_DATA & mask) != 092 return (DEVICE_REG_DATA & mask) == 093 else:94 # otherwise (invalid port), always return False95 print("Specified relay port is invalid")96 return False97
98
99def relay_get_port_data(relay_num):100 # gets the current byte value stored in the relay board101 global DEVICE_REG_DATA102 print('Reading relay status value for relay', relay_num)103 # do we have a valid port?104 if 0 < relay_num <= NUM_RELAY_PORTS:105 # read the memory location106 DEVICE_REG_DATA = bus.read_byte_data(DEVICE_ADDRESS, DEVICE_REG_MODE1)107 # return the specified bit status108 return DEVICE_REG_DATA109 else:110 # otherwise (invalid port), always return 0111 print("Specified relay port is invalid")112 return 0
Next script showcases the utility of the relay board functions. At the onset, it activates all the relays and then deactivates them, pausing for a second between these actions.
Subsequently, it sequentially powers each relay on and off, with a one-second intermission in between. In the event of a keyboard interrupt, the script terminates and ensures all the relays are switched off.
1#!/usr/bin/python2
3from __future__ import print_function4
5import sys6import time7
8from relay_lib_seeed import *9
10
11def process_loop():12 # turn all of the relays on13 relay_all_on()14 # wait a second15 time.sleep(1)16 # turn all of the relays off17 relay_all_off()18 # wait a second19 time.sleep(1)20
21 # now cycle each relay every second in an infinite loop22 while True:23 for i in range(1, 5):24 relay_on(i)25 time.sleep(1)26 relay_off(i)27
28
29# Now see what we're supposed to do next30if __name__ == "__main__":31 try:32 process_loop()33 except KeyboardInterrupt:34 # tell the user what we're doing...35 print("\nExiting application")36 # turn off all of the relays37 relay_all_off()38 # exit the application39 sys.exit(0)
For implementation, this script draws functions from
relay_lib_seeed
, which was previously explained. Together, these scripts simplify the control of the RPi Relay Board.Stepper Motor HAT
Using the capabilities of the Portenta X8 alongside specific modules can increase its performance. One such module is the drv8825 HAT, designed specifically for driving stepper motors, especially when paired with the Portenta Hat Carrier.
To use the drv8825 HAT with Portenta Hat Carrier and Portenta X8, please follow these steps:
Place the Portenta X8 onto the Portenta Hat Carrier.
Align and position the drv8825 HAT on the Portenta Hat Carrier. Make sure to align its 40-Pin header.
Proceed by wiring the motor poles. This can be done using either the A1-A2, B1-B2 configurations or the alternative A3-B3, A4-B4 setups. Proper wiring is crucial for achieving the desired rotational motion in the motor.
To ensure the system is powered on, connect an external power source using the VIN-GND terminals. This powers both the Portenta X8 and the drv8825 HAT, securing stable electrical performance.
One distinguishing feature of the drv8825 HAT is the provision for micro-stepping. This feature enhances the precision of motor operations.
Adjust the micro-stepping settings using the DIP switches present on the drv8825 HAT to achieve the desired level of granularity in motor steps.
Once the hardware setup is ready, use the script below to perform a test run of the connected stepper motor:
1#!/usr/bin/env python32
3# created 12 October 20234# by Riccardo Mereu & Massimo Pennazio5
6import os7
8if os.environ['CARRIER_NAME'] != "rasptenta":9 print("This script requires Portenta HAT carrier")10 exit(1)11
12from rpi_python_drv8825.stepper import StepperMotor13
14motor = StepperMotor(enable_pin, step_pin, dir_pin, mode_pins, step_type, fullstep_delay)15
16motor.enable(True) # enables stepper driver17motor.run(6400, True) # run motor 6400 steps clowckwise18motor.run(6400, False) # run motor 6400 steps counterclockwise19motor.enable(False) # disable stepper driver
The parameters for the stepper motor must be defined. Consider the following parameters as an example:
1enable_pin = 122step_pin = 233dir_pin = 244mode_pins = (14, 15, 18)5step_type = '1/32'6fullstep_delay = .005
For a comprehensive understanding, and perhaps to delve into advanced configurations, Waveshare's Stepper Motor HAT (B) Wiki is an excellent resource. It provides extensive insights and details about the drv8825 HAT.
Communication
The Portenta Hat Carrier extends multiple communication protocols from the Portenta core board. These protocols are Serial Peripheral Interface (SPI), Inter-Integrated Circuit (I2C), Universal Asynchronous Receiver-Transmitter (UART), JTAG interface, and MIPI tailored for the Portenta X8 camera compatibility. This section offers further details on these protocols.
Dedicated pins are provided as well on the Portenta Hat Carrier for each communication protocol. They are easily accessible via the 40-pin male header connectors, simplifying the process of interfacing with various components, peripherals, and sensors. Additionally, a 16-pin header connector is present to support analog pins, PWM pins, LICELL, and serial communication.
SPI
The Portenta Hat Carrier supports SPI communication via two dedicated ports named
SPI0
and SPI1
. Both ports are available via High-Density connectors, while SPI1
is also available over 40-pin connector. This allows data transmission between the board and other SPI-compatible devices. The pins used in the Portenta Hat Carrier for the SPI communication protocol are the following:Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
19 | SPI1 COPI | SPI1_MOSI | J2-42 | SPI 1 MOSI |
21 | SPI1 CIPO | SPI1_MISO | J2-40 | SPI 1 MISO |
23 | SPI1 SCK | SPI1_CK | J2-38 | SPI 1 CK |
24 | SPI1 CE | SPI1_CS | J2-36 | SPI 1 CS |
Please, refer to the board pinout section of the user manual to find them on the board.
Using Linux
With admin (root) access, you can use the following commands within the shell for the Portenta X8:
1sudo modprobe spidev
Present sequence of commands is used to enable the SPI device interface on the Portenta X8. After adding the
spidev
module to the system's configuration, the system is rebooted to apply the changes.1echo "spidev" | sudo tee > /etc/modules-load.d/spidev.conf2sudo systemctl reboot
Following section configures a service named
my_spi_service
to use the SPI device available at /dev/spidev0.0
.1services:2 my_spi_service:3 devices:4 - '/dev/spidev0.0'
Using Arduino IDE
Include the
library at the top of your sketch to use the SPI communication protocol. This can be used with Portenta H7 or C33. The SPI library provides functions for SPI communication:SPI
1#include <SPI.h>
The
setup()
function compiles initial SPI processes, by setting the chip select (`CS) with the proper configuration.1void setup() {2 // Set the chip select pin as output3 pinMode(SS, OUTPUT);4
5 // Pull the CS pin HIGH to unselect the device6 digitalWrite(SS, HIGH);7
8 // Initialize the SPI communication9 SPI.begin();10}
Following commands can be used to establish transmission and exchange information with an SPI-compatible device.
1// Replace with the target device's address2byte address = 0x00;3
4// Replace with the value to send5byte value = 0xFF;6
7// Pull the CS pin LOW to select the device8digitalWrite(SS, LOW);9
10// Send the address11SPI.transfer(address);12
13// Send the value14SPI.transfer(value);15
16// Pull the CS pin HIGH to unselect the device17digitalWrite(SS, HIGH);
I2S
I2S, short for Inter-IC Sound, connects digital audio devices using an electrical serial bus interface.
It operates using three main lines:
- SCK (Serial Clock) or BCLK: the clock signal.
- WS (Word Select) or FS (Frame Select): Differentiates data for the Right or Left Channel.
- SD (Serial Data): transmits the audio data.
The Controller generates the SCK and WS signals, and its frequency is derived from SampleRate x Bits Per Channel x Number of Channels.
In an I2S setup, while one device acts as the Controller, others are in Peripheral mode. Audio data samples can vary from 4 to 32 bits, with higher sample rates and bits offering superior audio quality.
The pins used in the Portenta Hat Carrier for the I2S communication protocol are the following:
Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
12 | I2S CK | I2S_CK | J1-56 | I2S CK |
35 | I2S WS | I2S_WS | J1-58 | I2S WS |
38 | I2S SDI | I2S_SDI | J1-60 | I2S SDI |
40 | I2S SDO | I2S_SDO | J1-62 | I2S SDO |
SAI - Serial Audio Interface
Serial Audio Interface (SAI) is a versatile protocol for transmitting audio data between digital components. Unlike the fixed I2S standard, SAI supports multiple audio data formats and configurations. The carrier works with the following data lines:
- D0: This serves as the primary data line, transmitting or receiving audio data.
- CK (BCLK): The Bit Clock, governing the rate of individual audio data bit transmission or reception.
- FS: Frame Sync, marking the boundary of audio frames, often differentiating channels in stereo audio.
SAI protocol can operate both synchronously and asynchronously, adjusting to various audio system needs. Due to its adaptability, SAI suits complex audio tasks, systems with multi-channel requirements, and specific audio formats.
In essence, SAI offers greater flexibility than I2S, catering to a broader range of audio system configurations.
The pins used in the Portenta Hat Carrier for the SAI protocol are the following:
Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
15 | SAI D0 | SAI_D0 | J2-53 | SAI D0 |
16 | SAI CK | SAI_CK | J2-49 | SAI CK |
18 | SAI FS | SAI_FS | J2-51 | SAI FS |
I2C
The Portenta Hat Carrier supports I2C communication, which allows data transmission between the board and other I2C-compatible devices. The pins used in the Portenta Hat Carrier for the I2C communication protocol are the following:
Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
27 | I2C0 SDA | I2C0_SDA | J1-44 | I2C 0 SDA |
28 | I2C0 SCL | I2C0_SCL | J1-46 | I2C 0 SCL |
3 | I2C2 SDA | I2C2_SDA | J2-45 | I2C 2 SDA |
5 | I2C2 SCL | I2C2_SCL | J2-47 | I2C 2 SCL |
Please, refer to the pinout section of the user manual to find them on the board. The I2C pins are also available through the onboard ESLOV connector of the Portenta Hat Carrier.
Using Linux
For the Portenta X8, it is possible to use the following commands within the shell when you have admin (root) access:
1sudo modprobe i2c-dev
Present sequence of commands is used to enable the I2C device interface on the Portenta X8. After adding the
i2c-dev
module to the system's configuration, the system is rebooted to apply the changes.1echo "i2c-dev" | sudo tee > /etc/modules-load.d/i2c-dev.conf2sudo systemctl reboot
Following section configures a service named
my_i2c_service
to use the I2C device available at /dev/i2c-3
.1services:2 my_i2c_service:3 devices:4 - `/dev/i2c-3`
Within the Portenta X8 shell, you can use specific commands to quickly test I2C communication with compatible devices. The command below lists all the connected I2C devices:
1i2cdetect -y <I2C bus>
To communicate with and fetch information from a connected I2C device, use:
1i2cget -y <I2C bus> <device address> <register address>
Below are some brief examples to help you in using I2C with the Portenta X8 and the Portenta Hat Carrier.
Here, the SMBus (System Management Bus) communication, with SMBus-compatible libraries, is established with the device on
/dev/i2c-3
. A byte of data is read from the device at address 80 and offset 0, then printed.1from smbus2 import SMBus2
3# Connect to /dev/i2c-34bus = SMBus(3)5b = bus.read_byte_data(80, 0)6print(b)
The following code initializes the I2C bus using the smbus2 library and reads multiple bytes from the device. The
read_i2c_block_data
function reads a block of bytes from the I2C device at a given address.1from smbus2 import SMBus2
3# Initialize the I2C bus4bus = SMBus(3) # 3 indicates /dev/i2c-35
6device_address = 0x17num_bytes = 28
9# Read from the I2C device10data = bus.read_i2c_block_data(device_address, 0, num_bytes) # Starting address is 0 to read from11
12# Now data is a list of bytes13for byte in data:14 print(byte)
The next code shows how to write data to an I2C device using the smbus2 library. A byte of data (
value
) is written to a specific address (device_address
) with a given instruction.1from smbus2 import SMBus2
3# Initialize the I2C bus4bus = SMBus(3) # 3 indicates /dev/i2c-35
6device_address = 0x17instruction = 0x008value = 0xFF9
10# Write to the I2C device11bus.write_byte_data(device_address, instruction, value)
In the following code, the python-periphery library is used to interact with the I2C device. This is useful if a broad spectrum of protocols are required within the same script. The
I2C.transfer()
method performs both write and read operations on the I2C bus.A byte is read from the EEPROM at address
0x50
and offset 0x100
, then printed.1from periphery import I2C2
3# Open i2c-0 controller4i2c = I2C("/dev/i2c-3")5
6# Read byte at address 0x100 of EEPROM at 0x507msgs = [I2C.Message([0x01, 0x00]), I2C.Message([0x00], read=True)]8i2c.transfer(0x50, msgs)9print("0x100: 0x{:02x}".format(msgs[1].data[0]))10
11i2c.close()
Using Arduino IDE
To use I2C communication, include the
library at the top of your sketch. This can be used with Portenta H7 or C33. The Wire
Wire
library provides functions for I2C communication:1#include <Wire.h>
In the
setup()
function, initialize the I2C library:1// Initialize the I2C communication2Wire.begin();
Following commands are available to transmit or write with an I2C-compatible device.
1// Replace with the target device's I2C address2byte deviceAddress = 0x1;3
4// Replace with the appropriate instruction byte5byte instruction = 0x00;6
7// Replace with the value to send8byte value = 0xFF;9
10// Begin transmission to the target device11Wire.beginTransmission(deviceAddress);12
13// Send the instruction byte14Wire.write(instruction);15
16// Send the value17Wire.write(value);18
19// End transmission20Wire.endTransmission();
Following commands are available to request or read with an I2C-compatible device.
1// The target device's I2C address2byte deviceAddress = 0x1;3
4// The number of bytes to read5int numBytes = 2;6
7// Request data from the target device8Wire.requestFrom(deviceAddress, numBytes);9
10// Read while there is data available11while (Wire.available()) {12 byte data = Wire.read();13}
CAN Bus
The CAN bus, short for Controller Area Network bus, is a resilient communication protocol created by Bosch® in the 1980s for vehicles. It lets microcontrollers and devices interact without a central computer. Using a multi-master model, any system device can send data when the bus is available.
This approach ensures system continuity even if one device fails and is especially effective in electrically noisy settings like in vehicles, where various devices need reliable communication.
The Portenta Hat Carrier is equipped with CAN bus communication capabilities, powered by the TJA1049 module, and a high-speed CAN FD transceiver. With this, developers can leverage the robustness and efficiency of CAN communication in their projects.
Since the CAN bus pins are integrated within the High-Density connectors, they are conveniently accessible on the carrier through the screw terminal. This provides flexibility in connection, allowing developers to design and troubleshoot their systems easily.
Pin number | Silkscreen | High-Density Pin | Interface |
---|---|---|---|
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.
Using Linux
For the Portenta X8, when you have admin (root) access, you can execute the following commands within the shell to control the CAN bus protocol. The CAN transceiver can be enabled using the following command
1echo 164 > /sys/class/gpio/export && echo out > /sys/class/gpio/gpio164/direction && echo 0 > /sys/class/gpio/gpio164/value
This command sequence activates the CAN transceiver. It does so by exporting GPIO 164, setting its direction to "
out
", and then writing a value of "0
" to it. Writing 0 as a value to GPIO 164 means that it will set the GPIO to a LOW state.For Portenta X8, it is possible to use the following commands:
1sudo modprobe can-dev
The necessary modules for CAN (Controller Area Network) support on the Portenta X8 are loaded. The
can-dev
module is added to the system configuration, after which the system is rebooted to apply the changes.1echo "can-dev" | sudo tee > /etc/modules-load.d/can-dev.conf2sudo systemctl reboot
Within the Portenta X8's shell, Docker containers offer a streamlined environment for specific tasks, such as command-based CAN bus operations. The cansend command is one such utility that facilitates sending CAN frames. The command to issue such task is as follows:
1cansend
And as an example, if you need to send a specific CAN frame, the cansend command can be used to achieve this task. The command follows the format:
1cansend <CAN Interface [can0 | can1]> <CAN ID>#<Data_Payload>
: defines the CAN interface that the Portenta X8 will use with the Portenta Hat Carrier.<CAN Interface [can0 | can1]>
: is the identifier of the message and is used for message prioritization. The identifier can be in 11-bit or 29-bit format.<CAN ID>
: is the data payload of the CAN message and ranges from 0 to 8 bytes in standard CAN frames.<Data_Payload>
For instance, the following command example would send a CAN message on the
can0
interface with an ID of 123
, using an 11-bit identifier, and a data payload of DEADBEEF
:1cansend can0 123#DEADBEEF
While the following command example with a 29-bit identifier would send a CAN message with an extended ID of
1F334455
and a data payload of 1122334455667788
.1cansend can0 1F334455#1122334455667788
This command sends a message with an extended CAN ID and an 8-byte payload.
To use the cansend command, it is crucial to set up the appropriate environment. First, clone the following container repository
1git clone https://github.com/pika-spark/pika-spark-containers
Navigate to the can-utils-sh directory:
1cd pika-spark-containers/can-utils-sh
Build the Docker container:
1./docker-build.sh
Run the Docker container with the desired CAN interface and bitrate:
1sudo ./docker-run.sh can0 | can1 [bitrate]
Moreover, if your goal is to monitor and dump all received CAN frames, a slightly different procedure is to be followed. Having the container repository ready with its components, navigate to the candump directory:
1cd pika-spark-containers/candump
Build the Docker container:
1./docker-build.sh
Run the Docker container with the desired CAN interface and bitrate:
1sudo ./docker-run.sh can0 | can1 [bitrate]
As an example, the command can be structured as follows:
1sudo ./docker-run.sh can0 250000
For more information regarding this container utility, please check can-utils-sh and candump.
The list provided offers a quick reference for various
bustype
parameters supported by python-can library.- socketcan: For the SocketCAN interface, which is native to Linux.
- virtual: Creates a virtual CAN bus, useful for testing purposes when you do not have actual CAN hardware.
- pcan: For Peak-System PCAN-USB adapters.
- canalystii: For the Canalyst II interface.
- kvaser: For Kvaser CAN interfaces.
- systec: For SYS TEC electronic interfaces.
- vector: For Vector hardware using the XL Driver Library.
- usb2can: For the 8devices USB2CAN.
- ixxat: For IXXAT hardware using the VCI driver.
- nican: For National Instruments CAN hardware.
- iscan: For the Intrepid Control Systems (ICS) neoVI.
Each bustype corresponds to different CAN interfaces or devices, ranging from the native Linux SocketCAN interface to specific hardware devices like Kvaser, Vector, and more.
In the following Python® script,
send_standard_can_message
and send_extended_can_message
functions are defined to send standard and extended CAN messages respectively. The main()
function creates a CAN bus instance with a 'virtual' bus type for demonstration purposes and sends standard and extended CAN messages in a loop.1import can2import time3
4def send_standard_can_message(channel, message_id, data):5 msg = can.Message(arbitration_id=message_id, data=data, is_extended_id=False)6 channel.send(msg)7
8def send_extended_can_message(channel, message_id, data):9 msg = can.Message(arbitration_id=message_id, data=data, is_extended_id=True)10 channel.send(msg)11
12def main():13 # Assuming you're using a virtual channel for the CAN bus for testing.14 # If you're using real hardware like the SocketCAN interface, change 'virtual' to 'socketcan'.15 bus = can.interface.Bus(channel='virtual', bustype='virtual')16
17 while True:18 print("Sending packet ... ", end="")19 send_standard_can_message(bus, 0x12, [ord('h'), ord('e'), ord('l'), ord('l'), ord('o')])20 print("done")21
22 time.sleep(1)23
24 print("Sending extended packet ... ", end="")25 send_extended_can_message(bus, 0xabcdef, [ord('w'), ord('o'), ord('r'), ord('l'), ord('d')])26 print("done")27
28 time.sleep(1)29
30if __name__ == "__main__":31 main()
Continuing Python® script defines functions to receive and print incoming CAN messages. The receive_can_messages function continuously listens for CAN messages and calls print_received_message to display the details of the received message, such as whether it is an extended message or a remote transmission request (RTR) and its data.
1import can2
3def receive_can_messages(bus):4 while True:5 message = bus.recv() # Blocks until a message is received6 print_received_message(message)7
8def print_received_message(message):9 print("Received ", end="")10
11 if message.is_extended_id:12 print("extended ", end="")13
14 if message.is_remote_frame:15 print("RTR ", end="")16
17 print("packet with id 0x{:X}".format(message.arbitration_id), end="")18
19 if message.is_remote_frame:20 print(" and requested length {}".format(message.dlc))21 else:22 print(" and length {}".format(len(message.data)))23
24 # Only print packet data for non-RTR packets25 for byte in message.data:26 print(chr(byte), end="")27 print()28
29 print()30
31def main():32 # Assuming you're using a virtual channel for testing.33 # If you're using real hardware like the SocketCAN interface, change 'virtual' to 'socketcan'.34 bus = can.interface.Bus(channel='virtual', bustype='virtual')35 print("CAN Receiver Callback")36 receive_can_messages(bus)37
38if __name__ == "__main__":39 main()
The
main()
function initializes the CAN bus with a 'virtual' channel for example demonstration and starts the message listening process.Using Arduino IDE
For users working with the Portenta H7 or Portenta C33, the following simple examples can be used to test the CAN bus protocol's capabilities.
The CAN Read example for Portenta H7/C33 starts CAN communication at a rate of 250 kbps and continuously listens for incoming messages, displaying such information upon receipt.
1#include <Arduino_CAN.h>2
3/**************************************************************************************4 * SETUP/LOOP5 **************************************************************************************/6
7void setup()8{9 Serial.begin(115200);10 while (!Serial) { }11
12 if (!CAN.begin(CanBitRate::BR_250k))13 {14 Serial.println("CAN.begin(...) failed.");15 for (;;) {}16 }17}18
19void loop()20{21 if (CAN.available())22 {23 CanMsg const msg = CAN.read();24 Serial.println(msg);25 }26}
The CAN Write example, also set at 250 kbps, builds and sends a specific message format. This message includes a fixed preamble followed by an incrementing counter value that updates with each loop iteration.
1/**************************************************************************************2 * INCLUDE3 **************************************************************************************/4
5#include <Arduino_CAN.h>6
7/**************************************************************************************8 * CONSTANTS9 **************************************************************************************/10
11static uint32_t const CAN_ID = 0x20;12
13/**************************************************************************************14 * SETUP/LOOP15 **************************************************************************************/16
17void setup()18{19 Serial.begin(115200);20 while (!Serial) { }21
22 if (!CAN.begin(CanBitRate::BR_250k))23 {24 Serial.println("CAN.begin(...) failed.");25 for (;;) {}26 }27}28
29static uint32_t msg_cnt = 0;30
31void loop()32{33 /* Assemble a CAN message with the format of34 * 0xCA 0xFE 0x00 0x00 [4 byte message counter]35 */36 uint8_t const msg_data[] = {0xCA,0xFE,0,0,0,0,0,0};37 memcpy((void *)(msg_data + 4), &msg_cnt, sizeof(msg_cnt));38 CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data);39
40 /* Transmit the CAN message, capture and display an41 * error core in case of failure.42 */43 if (int const rc = CAN.write(msg); rc < 0)44 {45 Serial.print ("CAN.write(...) failed with error code ");46 Serial.println(rc);47 for (;;) { }48 }49
50 /* Increase the message counter. */51 msg_cnt++;52
53 /* Only send one message per second. */54 delay(1000);55}
UART
The Portenta Hat Carrier supports UART communication. The pins used in the Portenta Hat Carrier for the UART communication protocol are the following:
Pin number | Silkscreen | Portenta HD Standard Pin | High-Density Pin | Interface |
---|---|---|---|---|
40-Pin Header | ||||
8 | TX3 | SERIAL3_TX | J2-25 | UART 3 TX |
10 | RX3 | SERIAL3_RX | J2-27 | UART 3 RX |
29 | RX1 | SERIAL1_RX | J1-35 | UART 1 RX |
32 | TX1 | SERIAL1_TX | J1-33 | UART 1 TX |
16-Pin Header | ||||
14 | TX2 | SERIAL2_TX | J2-26 | UART 2 TX |
16 | RX2 | SERIAL2_RX | J2-28 | UART 2 RX |
Please, refer to the board pinout section of the user manual to find them on the board. The UART pins can be used through the built-in (Serial) library functions.
Using Linux
For the Portenta X8, when you have admin (root) access, you can execute the command
ls /dev/ttyUSB* /dev/ttyACM* /dev/ttymxc*
within the shell to list available serial ports in Linux. Typically, USB serial devices could appear as /dev/ttyUSBx, /dev/ttyACMx, or /dev/ttymxcx.1ls /dev/ttyUSB* /dev/ttyACM* /dev/ttymxc*
1/dev/ttymxc2 // Something similar
The output /dev/ttymxc2 is an example of a potential serial device you might find on Portenta X8.
Following Python® script uses the pyserial library to communicate with devices over UART. It defines the
processData
function which prints the received data. You can modify this function based on your application's needs.1import serial2import time3
4# Define the processData function (you'll need to fill this in based on your requirements)5def processData(data):6 print("Received:", data) # For now, just print the data. Modify as needed.7
8# Set up the serial port9ser = serial.Serial('/dev/ttymxc2', 9600) # Use the appropriate port and baud rate for your device10
11incoming = ""12
13while True:14 # Check for available data and read individual characters15 while ser.in_waiting:16 c = ser.read().decode('utf-8') # Read a single character and decode from bytes to string17
18 # Check if the character is a newline (line-ending)19 if c == '\n':20 # Process the received data21 processData(incoming)22
23 # Clear the incoming data string for the next message24 incoming = ""25 else:26 # Add the character to the incoming data string27 incoming += c28
29 time.sleep(0.002) # Delay for data buffering, equivalent to Arduino's delay(2);
The script sets up a serial connection on port /dev/ttymxc2 at a baud rate of 9600. It then continuously checks for incoming data. When a newline character (
\n
) is detected, indicating the end of a message, the script processes the accumulated data and then resets the data string to be ready for the next incoming message.The
time.sleep(0.002)
line adds a slight delay, ensuring data has enough time to buffer, similar to using delay(2);
in Arduino.Using Arduino IDE
For Portenta H7 or C33, the following examples can be used to test UART communication. For a proper UART communication, the baud rate (bits per second) must be set within the
setup()
function.1// Start UART communication at 9600 baud2Serial.begin(9600);
With the
Serial1.available()
function and the Serial1.read()
function, you can read incoming data by using a while()
loop to repeatedly check for available data and read individual characters. When a line-ending character is received, the code below processes the input and stores the incoming characters in a String variable.:Using the
Serial1.available()
method alongside the Serial1.read()
method allows you to read incoming data. The technique involves using a while()
loop to consistently check for available data and then read individual characters.Upon receiving a line-ending character, the code processes the accumulated characters and stores them in a String variable. The relevant section of the code is presented below:
1void loop() {2 while (Serial1.available()) {3 delay(2);4 char c = Serial1.read();5 if (c == '\n') {6 processData(incoming);7 incoming = "";8 } else {9 incoming += c;10 }11 }12}13
14void processData(String data) {15 Serial.println("Received: " + data); // Print on Serial Monitor16}
Consequently, the following provides a complete example illustrating the reception of incoming data via UART:
1String incoming = "";2
3void setup() {4 Serial1.begin(9600); // For communication with Arduino using RX1 and TX15 Serial.begin(9600); // For debugging over USB6}7
8void loop() {9 while (Serial1.available()) {10 delay(2);11 char c = Serial1.read();12 if (c == '\n') {13 processData(incoming);14 incoming = "";15 } else {16 incoming += c;17 }18 }19}20
21void processData(String data) {22 Serial.println("Received: " + data); // Print on Serial Monitor23}
For transmitting data over UART, which can complement the receiving board as depicted above, refer to the ensuing example:
1void setup() {2 Serial1.begin(9600);3}4
5void loop() {6 Serial1.println("Hello from Portenta!");7 delay(1000);8}
Using these codes, you should observe the message
"Hello from Portenta!"
being transmitted to the receiving Portenta board when coupled with a Portenta Hat Carrier.The
Serial.write()
method allows you to send data over UART (Universal Asynchronous Receiver-Transmitter). This method is used when you want to transmit raw bytes or send a byte array.1// Transmit the string "Hello world!2Serial.write("Hello world!");
Code snippet above sends the string "
Hello world!
" to another device through UART using the Serial.write()
function.The following methods are used to send strings over UART.
1// Transmit the string "Hello world!"2Serial.print("Hello world!");3
4// Transmit the string "Hello world!" followed by a newline character5Serial.println("Hello world!");
The difference between the two is that
Serial.println()
sends a newline character (\n
) after sending the string, thereby moving the cursor to the next line, whereas Serial.print()
does not.Support
If you encounter any issues or have questions while working with the Portenta Hat Carrier, we provide various support resources to help you find answers and solutions.
Help Center
Explore our Help Center, which offers a comprehensive collection of articles and guides for the Portenta Hat Carrier. The Arduino Help Center is designed to provide in-depth technical assistance and help you make the most of your device.
Forum
Join our community forum to connect with other Portenta Hat Carrier users, share your experiences, and ask questions. The forum is an excellent place to learn from others, discuss issues, and discover new ideas and projects related to the Portenta Hat Carrier.
Contact Us
Please get in touch with our support team if you need personalized assistance or have questions not covered by the help and support resources described before. We're happy to help you with any issues or inquiries about the Portenta Hat Carrier.
Suggest changes
The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.
License
The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.