Portenta Max Carrier User Manual
Get a general overview of Portenta Max Carrier and its features.
Overview
This user manual offers a detailed guide on the Portenta Max Carrier, consolidating all its features for easy reference. It will show how to set up, adjust, and assess its main functionalities. This document will enable any user to proficiently operate the Portenta Max Carrier, making it suitable for project developments related to industrial automation, manufacturing automation, robotics, and prototyping.
Hardware and Software Requirements
Hardware Requirements
- Portenta Max Carrier (x1)
- Portenta X8 (x1)
- Portenta C33 (x1)
- Portenta H7 (x1)
- USB-C® cable (either USB-C® to USB-A or USB-C® to USB-C®) (x1)
- Wi-Fi® Access Point or Ethernet with Internet access (x1)
- Compatible antennas like the Arduino Pro 4G Module Antennas Kit (x1)
Software Requirements
Product Overview
Max Carrier transforms Portenta modules into single-board computers with edge AI capabilities for high-performance industrial, building automation and robotics applications. Thanks to its dedicated high-density connectors, Portenta Max Carrier can be paired with Portenta X8, H7, or C33, allowing any user to easily prototype and deploy multiple industrial projects.
This Arduino Pro carrier further augments Portenta connectivity options with Fieldbus, LoRa®, Cat-M1 and NB-IoT. Among the many available plug-and-play connectors, there are Gigabit Ethernet, USB-A, audio jacks, microSD, mini-PCIe, MIPI camera, FD-CAN, and Serial RS-232/422/485. Max Carrier can be powered via external supply (6-36V) or battery via the onboard 18650 Li-ion battery connector.
Carrier Architecture Overview
Here is an overview of the board's architecture's main components shown in the image above:
Compatible core: The board is compatible with Portenta X8 (ABX00049), Portenta H7 (ABX00042/ABX00045/ABX00046), and Portenta C33 (ABX00074). The Portenta H7 and C33 are limited in camera support and the Ethernet speed to 100 Mbit.
Power management: The Portenta Max Carrier can either be powered through the power jack (6 ~ 36V DC) or an 18650 Li-ion/LiPo battery (3.7V), which can be used as a backup power source if the external power supply fails. The battery is charged when the minimum input voltage to the power jack is met.
USB connectivity: The Portenta Max Carrier also includes a USB 2.0 High-Speed Hub controller based on the USB2514B/M2 that manages the 2 USB devices of the USB type A connector plus the LoRa® and PCIe modules. J15 is protected by an NCP383LMUAJAATXG power switch and current limiter.
A USB-A female connector can be used for data logging and the connection of external peripherals like keyboards, mice, hubs, and similar devices.
Ethernet connectivity: The Gigabit Ethernet physical interface (J17) is directly connected to the high density connector to the Portenta board. The connector includes an activity LED indication (orange) and speed indication (green). Note: Gigabit Ethernet functionality is only supported on the Portenta X8. Portenta H7 and Portenta C33 just support 100 Mbit Ethernet speed.
Serial Transceiver: The Portenta Max Carrier includes a multi-protocol transceiver supporting RS-232, RS-485, and RS-422 serial standards (configurable) based on the SP335 IC. It is connected to a 6P6C Connector (RJ11, RJ12, RJ14, RJ25).
CAN Transceiver: The Portenta Max Carrier includes a high speed CAN transceiver based on the TJA1049T/3J IC. It is connected to a 4P4C connector (RJ9, RJ10, RJ22).
Mini PCIe: The Portenta Max Carrier includes one female mini PCI Express card slot. The connector is right angled and the board includes 2 removable standoffs for external module support. The Max Carrier supports two different Mini PCIe sizes. Pins 8, 10, 12 and 14 are reserved for UIM (in this case SIM). Note: USB, I2C and SIM functionality over PCIe is available only for the X8.
Cellular Modem: The SARA-R412M-02B is a multi-region modem capable of connecting to 2G/Cat-M1/NB-IoT networks worldwide. A dedicated SMA connector is provided for connecting an external antenna. The chip operates over the 1V8 power line. A microSIM slot is available, the corresponding SIM card slot for the cell modem is on the top side of the board, directly adjacent to the module.
Audio: The Portenta Max Carrier enables connections to analog audio channels. This is done through the low power CS42L52 stereo CODEC providing ADC/DAC between analog signals and the I2S protocol. An internal Class D amplifier eliminates the need for external audio amplification circuitry.
LoRa® Module: The Portenta Max Carrier provides long range wireless connectivity for low bandwidth applications with the onboard Murata CMWX1ZZABZ-078 LoRa® transceiver module. This module operates at 3V3. A dedicated SMA connector is provided for connecting an external antenna.
MIPI Camera: The Portenta Max Carrier, when combined with a Portenta X8, supports MIPI cameras. The latter can be plugged into the onboard camera connector (J4) via a flexible flat cable. The camera support is perfect for machine/computer vision applications such as product line inspection, object detection, image classification and robotics.
Storage: The board has a MicroSD card slot for data logging operation.
Debug interface: Debugging capabilities are integrated directly into the Portenta Max Carrier and are accessible via micro USB. The J-link debugger is compatible with the Segger® J-Link OB and Blackmagic probes, driven by the STM32F405RGT6 controller. In addition to providing access to the Portenta board JTAG ports, different sniffer channels for I2C, CAN and UART lines are available. The debugger firmware can be updated via SWD on CN3. Additionally, headers for debugging the LoRa® are accessible via CN2 with SWD.
DIP switch: The carrier has a DIP switch with two positions and enables different profiles depending on the paired Portenta board. See the DIP Switches section for more details.
Carrier Topology
Ref. | Description | Ref. | Description |
---|---|---|---|
U1 | SARA-R412M-02B 4G LTE/Cat-M1/NB-IoT Modem IC | U2 | CS42L52-CNZ Stereo Codec IC |
U3 | USB2514Bi/M2 4-port USB 2.0 Hub IC | U4 | SP335EER1-L RS232/RS485/RS422 Transceiver IC |
U5 | TJA1049 CAN Transceiver IC | U6 | MPM3550EGLE Non-isolated DC-DC IC |
U7 | NCP383 Current Limiting IC | U8,U20,U21,U22 | SN74LVC1T45 Bi-directional logic level converter IC |
U9 | DSC6111HI2B 12MHz MEMS Oscillator IC | U10 | SN74LVC1G125 Single Bus Buffer Gate IC |
U11 | BQ24195RGET 4.5A Single Cell Charger IC | U12 | AP7311 1.8V 150mA LDO Linear Regulator IC |
U13 | TPS54620 6A Buck Regulator IC | U14 | AP2112K-3.3TRG1 3.3V 600mA LDO Regulator IC |
U15 | STM32F405RG 168MHz 32 bit Arm® Cortex®-M4 MCU IC | U16-U19 | 74LVC1G157 Single 2-input multiplexer IC |
U23 | CMWX1ZZABZ-078 Murrata LoRa® module | U24, U25 | LM73100 Ideal Diode with Reverse Polarity Protection |
J1, J2 | DF40HC(3.5)-80DS-0.4V(51) High Density Connectors | J3 | Right-Angle SMA Connector for Modem |
J4 | Camera 2-1734248-0 FPC Connector | J5 | FW-20-05-G-D-254-150 Signal Break |
J6 | 615006138421 RS232/RS485 Connector | J7 | 615006138421 CAN Connector |
J8 | 1759546-1 Mini PCIe Connector | J9 | Right-Angle SMA Connector for LoRa® |
J10 | ZX62-AB-5PA(31) Micro USB Debugger Connector with VBUS | J11 | 114-00841-68 Micro SD Connector |
J12 | SJ-3524-SMT-TR 3.5mm Headphone Out | J13 | SJ-3524-SMT-TR 3.5mm Line In Right |
J14 | SJ-3524-SMT-TR 3.5mm Line In Left | J15 | 61400826021 2-port USB 2.0 Female Connector |
J16 | 254TR Positive Li-ion Terminal | J17 | TRJK7003A97NL Gigabit Ethernet Connector |
J18 | 254TR Negative Li-ion Terminal | ||
J20 | 110990030 Connector for Speaker | X1 | PJ-102A 5.5mm Power Jack Adapter |
CN1 | FTSH-105-01-F-DV 10-pin JTAG Header | CN2 | Debug Header |
CN3 | LoRa® Debug Header | SIM1 | 2199337-5 microSIM Card Holder (for on-board modem) |
SW1 | 218-2LPST Boot Select Switch | SW2 | 218-2LPST Switch (2) |
PB1 | PTS820J25KSMTRLFS Power On Button | PB2 | PTS820J25KSMTRLFS Reset Button |
Carrier Characteristics Highlight
The Portenta Max Carrier extends the features of the Portenta X8, H7, and C33. The following table summarizes the carrier's characteristics depending on the paired Portenta.
Function | Portenta H7/C33 Support | Portenta X8 Support | Notes |
---|---|---|---|
USB Host | USB 1.0 | USB 2.0 | Max Speed: USB 1.0 - 12 Mbps, USB 2.0 - 480 Mbps |
Ethernet | 100 Mbps | 1 Gbps | |
CAN | Portenta C33 only | Yes | |
Mini PCIe (USB) | USB 1.0 | USB 2.0 | Max Speed: USB 1.0 - 12 Mbps, USB 2.0 - 480 Mbps |
Mini PCIe (PCIe) | No | PCIe 2.0 | |
Battery Charger | Yes | Yes | |
LoRa® | Yes | Yes | |
NBIoT/CatM1/2G | Yes | Yes | |
Camera | No | MIPI up to 4 lanes | No MIPI camera support on H7/C33 |
Audio | Limited | Yes | No firmware support for the H7 |
RS232/422/485 | Yes | Yes | |
on board JTAG debugging | Yes | No | |
on board console to USB | Yes | Yes | |
on board bus sniffing | Limited | Limited | Only hardware support |
The table above provides a general idea of how the Portenta Max Carrier performs 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.
Pinout
The full pinout is available and downloadable as PDF from the link below:
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:
Altium Files
The full Altium files are available and downloadable from the link below:
First Use
Stack The Carrier
The Portenta Max Carrier design enables an easy stack of the preferred Portenta board. The following figure shows how the Portenta boards pair via the High-Density connectors.
With the Portenta mounted to the carrier, you can proceed to power the carrier and begin prototyping.
Power The Board
The Portenta Max Carrier can be powered using the following methods:
- Using an external 6 to 36V power supply connected to the
of the board.Power Jack
- Using a 3.7V 18650 Li-ion battery inserted in the on-board battery socket.
- Using a USB-C® cable connected to the Portenta core board of your choice. (This option does not power the Modem and Mini PCIe connector).
Please ensure to connect the battery with the right polarity, not doing so may damage the board.
Hello World
Let's test the Portenta Max Carrier with the classic
Hello World
example used in the Arduino ecosystem. As the Max Carrier was designed to avoid having built-in LEDs for IoT purposes, we can not use a Blink
. Instead, we are going to leverage the Ethernet connectivity for a quick test. We will use this example to verify the board is working as expected with the different Portenta boards.To configure the Ethernet settings, depending on the paired Portenta board, you must properly set the provided DIP switch located on the Portenta Max Carrier.
The Portenta Max Carrier incorporates two DIP switches, giving users the ability to manage the behavior of the board. The configuration parameters of these switches differ based on which Portenta board it is paired with.
For configurations when the Portenta Max Carrier is combined with the Portenta boards, the DIP switch governs these settings:
Ethernet DIP Switch Designation | Position: ON | Position: OFF |
---|---|---|
1 - 2 | Ethernet Disabled for X8 / Enabled for H7/C33 | Ethernet Enabled for X8 / Disabled for H7/C33 |
For an in-depth understanding of the DIP switch, kindly refer to this section.
Using Linux
For the Portenta X8, be sure the ethernet DIP switches are both set to OFF. Then connect the board using a LAN cable to the network.
Enter to your Portenta X8 using
adb shell
and access to root (admin) by typing sudo su -
, the password is fio
by default.First, you need an internet connection to download the tool. You can establish one using the following commands:
1nmcli connection show # To find the ethernet device name ("eth0" in this case)
If your ethernet connection is up and running you should see something similar to this:
If not, you can create a DHCP network with a custom name and interface with the following commands:
1nmcli conn add con-name <NtwrkName> type ethernet ifname <DevName> ipv4.method auto # Create a DHCP network. <NtwrkName> will be the custom network alias and <DevName> must be the device name found with the past command.2
3nmcli conn up <NtwrkName> # Initiate the connection
To test if we are successfully connected, let's make a
ping
using:1ping -I eth0 -c 4 arduino.cc # ping 4 times to Arduino's webpage using ethernet
If you have a working ethernet connection to the internet, the ping should show the latency as follows:
Using Arduino IDE
To test the Ethernet connection using a Portenta H7 or a Portenta C33 we are going to use an example sketch that will retrieve your City information from the internet and show it through the Serial Monitor.
When using the Portenta H7 or the Portenta C33, be sure the ethernet DIP switches are both set to ON. Then connect the board using a LAN cable to the network.
1/**2 Purpose: This sketch connects a device to ip-api.com via Ethernet3 and fetches IP details for the device.4**/5
6// Include the necessary libraries.7#if defined(ARDUINO_PORTENTA_H7_M7)8 #include <PortentaEthernet.h> // for Portenta H7 9#elif defined(ARDUINO_PORTENTA_C33)10 #include <EthernetC33.h> // for Portenta C3311#endif12
13#include <Arduino_JSON.h>14
15// Server address for ip-api.com.16const char* server = "ip-api.com";17
18// API endpoint path to get IP details in JSON format.19String path = "/json/";20
21// Ethernet client instance for the communication.22EthernetClient client;23
24// JSON variable to store and process the fetched data.25JSONVar doc;26
27// Variable to ensure we fetch data only once.28bool dataFetched = false;29
30void setup() {31 // Begin serial communication at a baud rate of 115200.32 Serial.begin(115200);33
34 // Wait for the serial port to connect,35 // This is necessary for boards that have native USB.36 while (!Serial);37
38 // Attempt to start Ethernet connection via DHCP,39 // If DHCP failed, print a diagnostic message.40 if (Ethernet.begin() == 0) {41 Serial.println("- Failed to configure Ethernet using DHCP!");42
43 }44 printIPAddress();45 delay(2000);46}47
48void loop() {49 // Ensure we haven't fetched data already,50 // ensure the Ethernet link is active,51 // establish a connection to the server,52 // compose and send the HTTP GET request.53 if (!dataFetched) {54 if (Ethernet.linkStatus() == LinkON) {55 if (client.connect(server, 80)) {56 client.print("GET ");57 client.print(path);58 client.println(" HTTP/1.1");59 client.print("Host: ");60 client.println(server);61 client.println("Connection: close");62 client.println();63
64 // Wait and skip the HTTP headers to get to the JSON data.65 char endOfHeaders[] = "\r\n\r\n";66 client.find(endOfHeaders);67
68 // Read and parse the JSON response.69 String payload = client.readString();70 doc = JSON.parse(payload);71
72 // Check if the parsing was successful.73 if (JSON.typeof(doc) == "undefined") {74 Serial.println("- Parsing failed!");75 return;76 }77
78 // Extract and print the IP details.79 Serial.println("*** IP Details:");80 Serial.print("- IP Address: ");81 Serial.println((const char*)doc["query"]);82 Serial.print("- City: ");83 Serial.println((const char*)doc["city"]);84 Serial.print("- Region: ");85 Serial.println((const char*)doc["regionName"]);86 Serial.print("- Country: ");87 Serial.println((const char*)doc["country"]);88 Serial.println("");89
90 // Mark data as fetched.91 dataFetched = true;92 }93 // Close the client connection once done.94 client.stop();95 } else {96 Serial.println("- Ethernet link disconnected!");97 }98 }99}100
101void printIPAddress()102{103 Serial.print("Connected to: ");104 for (byte thisByte = 0; thisByte < 4; thisByte++) {105 // print the value of each byte of the IP address:106 Serial.print(Ethernet.localIP()[thisByte], DEC);107 Serial.print(".");108 }109
110 Serial.println();111}
If the connection is successful, you should see your IP address and location information printed out in the Arduino IDE Serial Monitor.
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
Configuration and Control
Configuration and control features enable the customization of the device's behavior to its specific needs. Whether you would like to set up network connectivity or adjust switch configurations, this sub-section will guide you through the carrier connectivity and profile setup processes.
DIP Switch Configuration
The Portenta Max Carrier incorporates two DIP switches, giving users the ability to manage the behavior of the board. The configuration parameters of these switches differ based on which Portenta board it is paired with.
For configurations when the Portenta Max Carrier is combined with the Portenta boards, the DIP switch governs these settings:
Ethernet DIP Switch Designation | Position: ON | Position: OFF |
---|---|---|
1 - 2 | Ethernet Disabled for X8 / Enabled for H7/C33 | Ethernet Enabled for X8 / Enabled for H7/C33 |
Boot DIP Switch Designation | Position: ON | Position: OFF |
---|---|---|
BOOT SEL | Enter Boot Mode for X8 / Not used for H7/C33 | Normal boot from Portenta onboard memory for X8 / Not used for H7/C33 |
BOOT | Not officially supported for X8 / Enter Boot mode for H7/C33 | Boot from MMC Memory for X8 / Normal Boot (Run) for H7/C33 |
This flexibility ensures that the Portenta Max Carrier remains adaptable to the unique needs of each paired Portenta board.
Learn more about how the different DIP Switches configurations work and help you to flash images to the Portenta X8 following this guide.
Network Connectivity
Ethernet
The Portenta Max Carrier is equipped with an Ethernet interface, specifically an RJ45 connector supporting 1000 Base-T.
Ethernet performance differs 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, you must properly set the provided DIP switch located on the Portenta Max Carrier. For an in-depth understanding of the DIP switch, kindly refer to this section.
Using Linux
Using the Portenta X8 in combination with the Max Carrier allows you to evaluate the Ethernet speed. First, ensure the Portenta X8 is mounted on the Max Carrier, and then connect them using a LAN cable.
To measure the bandwidth, use the
iperf3
tool, which is available here.Enter to your Portenta X8 using
adb shell
and access to root (admin) by typing sudo su -
, the password is fio
by default.First, you need an internet connection to download the tool. You can establish one using the following commands:
1nmcli connection show # To find the ethernet device name ("eth0" in this case)
If your ethernet connection is up and running you should see something similar to this:
If not, you can create a DHCP network with a custom name and interface with the following commands:
1nmcli conn add con-name <NtwrkName> type ethernet ifname <DevName> ipv4.method auto # Create a DHCP network. <NtwrkName> will be the custom network alias and <DevName> must be the device name found with the past command.2
3nmcli conn up <NtwrkName> # Initiate the connection
To test if we are successfully connected, let's make a
ping
using:1ping -I eth0 -c 4 arduino.cc # ping 4 times to Arduino's webpage using ethernet
If you have a working internet connection, the ping should show the latency as follows:
Now we know we are connected through ethernet, let's do the speed test. To install the
iperf3
tool, we can use the following commands:1mkdir -p ~/bin && source ~/.profile2wget -qO ~/bin/iperf3 https://github.com/userdocs/iperf3-static/releases/latest/download/iperf3-arm64v83chmod 700 ~/bin/iperf3
To verify the installation, type
~/bin/iperf3 -v
and the tool version should be printed on the terminal.As the speed test must be done between two devices to measure the link speed, we need to install
iperf3
on a second device, in this case on my PC. You can download it from here for your preferred OS.Once installed on both devices, we should set one as a
server
and the other one as a client
with the following commands respectively:1~/bin/iperf3 -s # run this on the Portenta X8 (Server)
1iperf3.exe -c <Server IP Address> # run this on your PC (Windows) from the iperf3 download directory and use the Portenta X8 IP address.
The speed results could be affected by your Ethernet cable quality or your PC Ethernet card.
Using Arduino IDE
To test the Ethernet connection using a Portenta H7 or a Portenta C33 we are going to use an example sketch that will retrieve your City information from the internet and show it through the Serial Monitor.
1/**2 Purpose: This sketch connects a device to ip-api.com via Ethernet3 and fetches IP details for the device.4**/5
6// Include the necessary libraries.7#if defined(ARDUINO_PORTENTA_H7_M7)8 #include <PortentaEthernet.h> // for Portenta H7 9#elif defined(ARDUINO_PORTENTA_C33)10 #include <EthernetC33.h> // for Portenta C3311#endif12
13#include <Arduino_JSON.h>14
15// Server address for ip-api.com.16const char* server = "ip-api.com";17
18// API endpoint path to get IP details in JSON format.19String path = "/json/";20
21// Ethernet client instance for the communication.22EthernetClient client;23
24// JSON variable to store and process the fetched data.25JSONVar doc;26
27// Variable to ensure we fetch data only once.28bool dataFetched = false;29
30void setup() {31 // Begin serial communication at a baud rate of 115200.32 Serial.begin(115200);33
34 // Wait for the serial port to connect,35 // This is necessary for boards that have native USB.36 while (!Serial);37
38 // Attempt to start Ethernet connection via DHCP,39 // If DHCP failed, print a diagnostic message.40 if (Ethernet.begin() == 0) {41 Serial.println("- Failed to configure Ethernet using DHCP!");42
43 }44 printIPAddress();45 delay(2000);46}47
48void loop() {49 // Ensure we haven't fetched data already,50 // ensure the Ethernet link is active,51 // establish a connection to the server,52 // compose and send the HTTP GET request.53 if (!dataFetched) {54 if (Ethernet.linkStatus() == LinkON) {55 if (client.connect(server, 80)) {56 client.print("GET ");57 client.print(path);58 client.println(" HTTP/1.1");59 client.print("Host: ");60 client.println(server);61 client.println("Connection: close");62 client.println();63
64 // Wait and skip the HTTP headers to get to the JSON data.65 char endOfHeaders[] = "\r\n\r\n";66 client.find(endOfHeaders);67
68 // Read and parse the JSON response.69 String payload = client.readString();70 doc = JSON.parse(payload);71
72 // Check if the parsing was successful.73 if (JSON.typeof(doc) == "undefined") {74 Serial.println("- Parsing failed!");75 return;76 }77
78 // Extract and print the IP details.79 Serial.println("*** IP Details:");80 Serial.print("- IP Address: ");81 Serial.println((const char*)doc["query"]);82 Serial.print("- City: ");83 Serial.println((const char*)doc["city"]);84 Serial.print("- Region: ");85 Serial.println((const char*)doc["regionName"]);86 Serial.print("- Country: ");87 Serial.println((const char*)doc["country"]);88 Serial.println("");89
90 // Mark data as fetched.91 dataFetched = true;92 }93 // Close the client connection once done.94 client.stop();95 } else {96 Serial.println("- Ethernet link disconnected!");97 }98 }99}100
101void printIPAddress()102{103 Serial.print("Connected to: ");104 for (byte thisByte = 0; thisByte < 4; thisByte++) {105 // print the value of each byte of the IP address:106 Serial.print(Ethernet.localIP()[thisByte], DEC);107 Serial.print(".");108 }109
110 Serial.println();111}
Wi-Fi® & Bluetooth®
The Portenta Max 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 Max 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 Max 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®
LTE CAT.M1 NB-IoT
To ensure connectivity in a wide variety of possible scenarios, the Max Carrier features cellular connectivity powered by the SARA-R412M-02B multi-band module.
You can easily connect your solution to the internet leveraging the more suitable communication protocol, from LTE, NB-IoT, Cat.M1 and more.
Recommended cellular antenna: ANT-5GW-SPS1-2
Using Arduino IDE
To use the cellular connectivity we are going to use a Portenta H7 alongside the Max Carrier. To drive the module we recommend the
MKRNB
library which can be downloaded directly from the Arduino IDE Library Manager.To quickly find out if the setup successfully connects to mobile networks, we are going to use an example code that can be found on File > Examples > MKRNB > NBWwebClient.
Go to the arduino_secrets.h tab that opens with the example and enter the PIN of the SIM card you are using into the
SECRET_PINNUMBER
variable.Note: A standard pre-paid SIM card typically has 0000 or 1234 as a PIN code. This varies from operator to operator, it is important to find out your PIN before uploading the code. Otherwise, too many unsuccessful attempts may block the SIM card.
Upload the code to your Portenta H7 and open the Serial Monitor to follow the connection process.
If the connection is successful, you should see the HTML content of the server printed in the Serial Monitor. The server is set as
example.com
as default. Feel free to change this and take a look at how it prints different web pages. Below you can see what will be printed in the Serial Monitor when connecting to example.com.
LoRa®
One feature that boosts Portenta Max Carrier possibilities is its onboard LoRa® module, the CMWX1ZZABZ-078 from Murata®. LoRaWAN® is a Low Power Wide Area Network (LPWAN) protocol designed to connect low-power devices to the Internet. It has been developed to meet and fulfill Internet of Things (IoT) devices' requirements, such as low power consumption and low data throughput.
A dedicated SMA connector (J9) is available for connecting an external antenna.
Recommended LoRa® antenna: ANT-8/9-IPW1-SMA
Using Linux
Empower your Portenta X8 connectivity with LoRa® by following this detailed guide on How to set up a multi-protocol gateway using the Portenta X8 and the Max Carrier
Using Arduino IDE
To learn how to leverage LoRa® capabilities with this carrier and the Arduino IDE, follow this detailed tutorial on How to Connect the Portenta Max Carrier with The Things Network using a Portenta H7
Audio Interface
The Portenta Max Carrier features a low-power but mighty stereo CODEC, ideal for audio applications powered by the Portenta X8. An internal Class D amplifier lets us play high-quality audio directly on external speakers.
The audio recording couldn't be simpler thanks to its variety of audio inputs, letting you connect a microphone or any other audio source.
Recording Audio
In the following example, we are going to record audio using an external microphone and store it on a micro SD card.
First of all, let's connect to the internet to download the tools and run the needed Docker containers.
Use
to easily connect to a WiFi network.nmcli device wifi connect <SSID> password <PASSWORD>
After inserting the micro SD in the Max Carrier slot, mount it with the following command.
1sudo mount -t vfat /dev/mmcblk1p1 /mnt
Run the docker image
debian:stable-slim
and set the peripherals to be used with the following command.1docker run -it -u '0' --device '/dev/snd:/dev/snd' -v '/mnt:/sdcard' --tmpfs /tmp --name "alsa-utils" debian:stable-slim bash
Install the audio management tools. This procedure will take a while.
1apt-get update && apt-get install alsa-utils ffmpeg -y
Define the microphone inputs as the audio sources and the input gain.
1amixer -c 1 sset 'ADC Left Mux' 'Input3A' # Mic Input2amixer -c 1 sset 'ADC Right Mux' 'Input3B' # Mic Input3amixer -c 1 sset 'ADC' 100% # Mic volume
Now, we are ready for the audio recording. Use
ffmpeg
command alongside the settings of your preference.The following command will record audio for 30 seconds at 44100 Hz and save it on
/sdcard/
as out.wav
.1ffmpeg -f alsa -i hw:1 -t 30 -ac 1 -ar 44100 -c:a pcm_s16le /sdcard/out.wav
If you want to learn more about the
tool and its options, here is a useful reference.ffmpeg
If we list the
/sdcard/
directory content with ls /scard/
command, the audio file should be there.You can open the audio file on your computer by copying it with the following commands:
- First, find the
container ID withalsa-utils
.docker ps -a
- Copy the file from the container to the X8 local storage with
sudo docker cp <CONTAINER ID>:/sdcard/out.wav /home/fio
- The audio file is now on
, from here you can pull it using/home/fio
from a terminal. Useadb
adb pull /home/fio/out.wav <destination path>
Playing Back Audio
In the following example, we are going to playback the previously recorded audio file and learn how to test the Max Carrier audio outputs.
First, it is important to know some commands to control the audio volume.
1# This sets the master output volume 2amixer -c 1 sset 'Master' 100% # accepts also a dB parameter 100% = 12dB3# This sets the headphone output volume 4amixer -c 1 sset 'Headphone' 0dB # 0dB = 100%5# This sets the speaker output volume 6amixer -c 1 sset 'Speaker' 100% # 100% = 0dB
By default, the Class D amplifier outputs are turned off. To play audio on the external speakers, you must turn them on with the following commands.
1amixer -c 1 sset 'SPK Right Amp' on # turn on the right channel speaker2amixer -c 1 sset 'SPK Left Amp' on # turn on the right channel speaker
The following command will play an example
.wav
audio file stored on /usr/share/sounds/alsa
.1speaker-test -c 2 -D hw:cs42l52audio -t wav
To play a sine wave at 440 Hz, use:
1speaker-test -c 2 -D hw:cs42l52audio -t sine -f 440
To play the recorded
.wav
file in the previous section, use this command.1aplay -c 2 -D hw:cs42l52audio -t wav /sdcard/out2.wav
To send a file from the Portenta X8 local storage to the container use: sudo docker cp /home/fio/<file>.wav <CONTAINER ID>:/sdcard
USB Interface
The Portenta Max 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 | USB0_VBUS | J1-24 | |
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 a 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!
.To unmount the USB drive use the following command from outside the USB folder:
1umount /dev/sda1 /mnt/USBmount
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 Max Carrier.
Using Arduino IDE
The following example demonstrates how to use the USB interface of the Portenta Max Carrier with the Portenta C33 to mount a Mass Storage Device (MSD).
The Max Carrier uses a USB hub IC (USB2514B) that manages the communication between the two USB-A ports and the USB interface of the Portenta SoM. To use it with the Arduino IDE, a library is needed and you can install it by searching for
USB251X
on the library manager and clicking on install.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 <Wire.h>2#include "SparkFun_USB251x_Arduino_Library.h" //Click here to install: http://librarymanager/All#USB251x3#include <vector>4#include <string>5#include "UsbHostMsd.h"6#include "FATFileSystem.h"7
8#define TEST_FS_NAME "USB"9#define TEST_FOLDER_NAME "TEST_FOLDER"10#define TEST_FILE "test.txt"11#define DELETE_FILE_DIMENSION 15012
13USB251x myHub;14USBHostMSD block_device;15FATFileSystem fs(TEST_FS_NAME);16
17std::string root_folder = std::string("/") + std::string(TEST_FS_NAME);18std::string folder_test_name = root_folder + std::string("/") + std::string(TEST_FOLDER_NAME);19std::string file_test_name = folder_test_name + std::string("/") + std::string(TEST_FILE);20
21/* this callback will be called when a Mass Storage Device is plugged in */22void device_attached_callback(void) {23 Serial.println();24 Serial.println("++++ Mass Storage Device detected ++++");25 Serial.println();26}27
28void setup() {29 /*30 * SERIAL INITIALIZATION31 */32 Serial.begin(115200);33 while (!Serial) {34 }35
36 Wire.begin();37
38 if (myHub.begin() == false) {39 Serial.println("Device not found. USB251xB may already be in hub mode. Please check wiring or reset the hub. Freezing...");40 while (1)41 ;42 }43
44 Serial.println("Writing default settings to hub");45 myHub.setDefaults(); //Write ROM defaults46 myHub.attach(); //Locks settings and begin acting as hub47
48
49 Serial.println();50 Serial.println("*** USB HOST Mass Storage Device example ***");51 Serial.println();52
53 /* attach the callback so that when the device is inserted the device_attached_callback54 will be automatically called */55 block_device.attach_detected_callback(device_attached_callback);56 /* list to store all directory in the root */57 std::vector<std::string> dir_list;58
59 /* 60 * Check for device to be connected61 */62
63 int count = 0;64 while (!block_device.connect()) {65 if (count == 0) {66 Serial.println("Waiting for Mass Storage Device");67 } else {68 Serial.print(".");69 if (count % 30 == 0) {70 Serial.println();71 }72 }73 count++;74 delay(1000);75 }76
77 Serial.println("Mass Storage Device connected.");78
79 /* 80 * MOUNTIN SDCARD AS FATFS filesystem81 */82
83 Serial.println("Mounting Mass Storage Device...");84 int err = fs.mount(&block_device);85 if (err) {86 // Reformat if we can't mount the filesystem87 // this should only happen on the first boot88 Serial.println("No filesystem found, formatting... ");89 err = fs.reformat(&block_device);90 }91
92 if (err) {93 Serial.println("Error formatting USB Mass Storage Device");94 while (1)95 ;96 }97
98 /* 99 * READING root folder100 */101
102 DIR *dir;103 struct dirent *ent;104 int dirIndex = 0;105
106 Serial.println("*** List USB Mass Storage Device content: ");107 if ((dir = opendir(root_folder.c_str())) != NULL) {108 while ((ent = readdir(dir)) != NULL) {109 if (ent->d_type == DT_REG) {110 Serial.print("- [File]: ");111 } else if (ent->d_type == DT_DIR) {112 Serial.print("- [Fold]: ");113 if (ent->d_name[0] != '.') { /* avoid hidden folders (.Trash might contain a lot of files) */114 dir_list.push_back(ent->d_name);115 }116 }117 Serial.println(ent->d_name);118 dirIndex++;119 }120 closedir(dir);121 } else {122 // Could not open directory123 Serial.println("Error opening USB Mass Storage Device\n");124 while (1)125 ;126 }127
128 if (dirIndex == 0) {129 Serial.println("Empty SDCARD");130 }131
132 bool found_test_folder = false;133
134 /* 135 * LISTING CONTENT of the first level folders (the one immediately present in root folder)136 */137
138 if (dir_list.size()) {139 Serial.println();140 Serial.println("Listing content of folders in root: ");141 }142 for (unsigned int i = 0; i < dir_list.size(); i++) {143 if (dir_list[i] == TEST_FOLDER_NAME) {144 found_test_folder = true;145 }146 Serial.print("- ");147 Serial.print(dir_list[i].c_str());148 Serial.println(":");149
150 std::string d = root_folder + std::string("/") + dir_list[i];151 if ((dir = opendir(d.c_str())) != NULL) {152 while ((ent = readdir(dir)) != NULL) {153 if (ent->d_type == DT_REG) {154 Serial.print(" - [File]: ");155 } else if (ent->d_type == DT_DIR) {156 Serial.print(" - [Fold]: ");157 }158 Serial.println(ent->d_name);159 }160 closedir(dir);161 } else {162 Serial.print("ERROR OPENING SUB-FOLDER ");163 Serial.println(d.c_str());164 }165 }166
167 /* 168 * CREATING TEST FOLDER (if does not exist already)169 */170
171 err = 0;172 if (!found_test_folder) {173 Serial.println("TEST FOLDER NOT FOUND... creating folder test");174 err = mkdir(folder_test_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);175 if (err != 0) {176 Serial.print("FAILED folder creation with error ");177 Serial.println(err);178 }179 }180
181 /* 182 * READING TEST FILE CONTENT183 */184
185 if (err == 0) {186 int file_dimension = 0;187 FILE *fp = fopen(file_test_name.c_str(), "r");188 if (fp != NULL) {189 Serial.print("Opened file: ");190 Serial.print(file_test_name.c_str());191 Serial.println(" for reading");192
193 fseek(fp, 0L, SEEK_END);194 int numbytes = ftell(fp);195 fseek(fp, 0L, SEEK_SET);196
197 Serial.print("Bytes in the file: ");198 Serial.println(numbytes);199 file_dimension = numbytes;200
201 if (numbytes > 0) {202 Serial.println();203 Serial.println("-------------------- START FILE CONTENT --------------------");204 }205
206 for (int i = 0; i < numbytes; i++) {207 char ch;208 fread(&ch, sizeof(char), 1, fp);209 Serial.print(ch);210 }211
212 if (numbytes > 0) {213 Serial.println("--------------------- END FILE CONTENT ---------------------");214 Serial.println();215 } else {216 Serial.println("File is EMPTY!");217 Serial.println();218 }219
220 fclose(fp);221 } else {222 Serial.print("FAILED open file ");223 Serial.println(file_test_name.c_str());224 }225
226 /*227 * DELETE FILE IF THE File dimension is greater than 150 bytes228 */229
230 if (file_dimension > DELETE_FILE_DIMENSION) {231 Serial.println("Test file reached the delete dimension... deleting it!");232 if (remove(file_test_name.c_str()) == 0) {233 Serial.println("TEST FILE HAS BEEN DELETED!");234 }235 }236
237 /*238 * APPENDING SOMETHING TO FILE 239 */240
241 fp = fopen(file_test_name.c_str(), "a");242 if (fp != NULL) {243 Serial.print("Opened file: ");244 Serial.print(file_test_name.c_str());245 Serial.println(" for writing (append)");246 char text[] = "This line has been appended to file!\n";247 fwrite(text, sizeof(char), strlen(text), fp);248 fclose(fp);249 } else {250 Serial.print("FAILED open file for appending ");251 Serial.println(file_test_name.c_str());252 }253
254 /*255 * READING AGAIN FILE CONTENT256 */257
258 fp = fopen(file_test_name.c_str(), "r");259 if (fp != NULL) {260 Serial.print("Opened file: ");261 Serial.print(file_test_name.c_str());262 Serial.println(" for reading");263
264 fseek(fp, 0L, SEEK_END);265 int numbytes = ftell(fp);266 fseek(fp, 0L, SEEK_SET);267
268 Serial.print("Bytes in the file: ");269 Serial.println(numbytes);270
271 if (numbytes > 0) {272 Serial.println();273 Serial.println("-------------------- START FILE CONTENT --------------------");274 }275
276 for (int i = 0; i < numbytes; i++) {277 char ch;278 fread(&ch, sizeof(char), 1, fp);279 Serial.print(ch);280 }281
282 if (numbytes > 0) {283 Serial.println("--------------------- END FILE CONTENT ---------------------");284 Serial.println();285 } else {286 Serial.println("File is EMPTY!");287 Serial.println();288 }289
290 fclose(fp);291
292 } else {293 Serial.print("FAILED open file for appending ");294 Serial.println(file_test_name.c_str());295 }296 }297}298
299void loop() {300 // Empty301}
The example code from above will read the USB drive connected and print all its content on the Serial Monitor. Also, it will create a test file.
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.
Using Linux
To begin using a microSD card, enter to your Portenta X8 using
adb shell
and access to root (admin) typing sudo su -
, the password is fio
by default.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 microSD card, it is important to unmount it properly:
1umount /tmp/sdcard
Warning: If you need to format the micro 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 #Warning: this will erase everything on your micro SD
Using Arduino IDE
For Portenta H7, you can use the following Arduino IDE script to test the mounted SD card within Portenta Max Carrier:
This example can also be found on the Arduino IDE built-in examples on File > Examples > Portenta_SDCARD > TestSDCARD
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}
With this code, the Portenta H7 will scan all the files and directories names on the micro SD card and list them on the Arduino IDE Serial Monitor.
For Portenta C33, you can use the following Arduino IDE script:
This example can also be found on the Arduino IDE built-in examples on File > Examples > Storage > TestSDCARD.
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 FILE 197 */198 199 fp = fopen(file_test_name.c_str(), "a");200 if(fp != NULL) {201 Serial.print("Opened file: ");202 Serial.print(file_test_name.c_str());203 Serial.println(" for writing (append)");204 char text[] = "This line has been appended to file!\n";205 fwrite(text, sizeof(char), strlen(text), fp);206 fclose(fp); 207 }208 else {209 Serial.print("FAILED open file for appending ");210 Serial.println(file_test_name.c_str());211 }212 213 /*214 * READING AGAIN FILE 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}
With this code, the Portenta C33 will scan all the files and directories names on the micro SD card and list them on the Arduino IDE Serial Monitor, also create a test file and read it back.
JTAG Pins
For developers aiming to investigate and understand the intricate details of development, the Portenta Max 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 Max 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 |
Communication
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 Max Carrier is equipped with CAN bus communication capabilities, powered by the TJA1049 module - a high-speed CAN FD transceiver. With this, developers can leverage the robustness and efficiency of CAN communication in their projects.
Using Linux
As a practical example, we are going to implement a communication between the Max Carrier using a Portenta X8 and a Portenta Machine Control using CAN.
For stable CAN bus communication, it is recommended to install 120 Ω termination resistors between CANH and CANL lines.
For the Portenta X8, when you have admin (root) access, you can execute the following lines of code within the shell to control the CAN bus interface. The CAN transceiver can be enabled using the following command:
echo 186 > /sys/class/gpio/export && echo out > /sys/class/gpio/gpio186/direction && echo 0 > /sys/class/gpio/gpio186/value
This command sequence activates the CAN transceiver. It does so by exporting GPIO 186 (
pwm3
), setting its direction to "out
", and then writing a value of "0
" to it. Writing 0 as a value to GPIO 186 means that it will set the GPIO to a LOW state.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 facilitates sending CAN frames. 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 bitrate:
1sudo ./docker-run.sh can0 [bitrate]
As an example, the command can be structured as follows for a 500 kbit/s communication:
1sudo ./docker-run.sh can0 500000
Now, you can send CAN messages using the
cansend
command as shown below:1cansend can0 123#CA
The command follows the format:
cansend <CAN Interface [can0 | can1]> <CAN ID>#<Data_Payload>
: defines the CAN interface (can0 to use the onboard transeiver).<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 both HEX.<CAN ID>
: is the data payload of the CAN message and ranges from 0 to 8 bytes in standard CAN frames.<Data_Payload>
This is how the communication is done between the Max Carrier with the Portenta X8 and the Machine Control.
For the Portenta Machine Control: Install the
Arduino_PortentaMachineControl
library from the Library Manager and use the following example sketch that can also be found on File > Examples > Arduino_PortentaMachineControl > CAN > ReadCan.1#include <Arduino_PortentaMachineControl.h>2
3void setup() {4 Serial.begin(9600);5 while (!Serial) {6 ; // wait for serial port to connect.7 }8
9 if (!MachineControl_CANComm.begin(CanBitRate::BR_500k)) {10 Serial.println("CAN init failed.");11 while(1) ;12 }13}14
15void loop() {16 if (MachineControl_CANComm.available()) {17 CanMsg const msg = MachineControl_CANComm.read();18 // Print the sender ID19 Serial.print("ID: ");20 Serial.println(msg.id, HEX);21
22 // Print the first Payload Byte23 Serial.print("Message received: ");24 Serial.println(msg.data[0], HEX);25 }26}
Moreover, if your goal is to monitor and dump all received CAN frames, a slightly different procedure has to be followed. When the container repository is ready with its components, navigate to the candump directory:
1cd pika-spark-containers/candump
Build the Docker container:
1./docker-build.sh
Now, you are able to receive CAN messages running this command:
1sudo ./docker-run.sh can0 500000 # last parameter is the bitrate
This is how the communication is done between the Max Carrier with the Portenta X8 and the Machine Control.
For the Portenta Machine Control: Install the
Arduino_PortentaMachineControl
library from the Library Manager and use the following example sketch that can also be found on File > Examples > Arduino_PortentaMachineControl > CAN > WriteCan.1#include <Arduino_MachineControl.h>2#include <CAN.h>3using namespace machinecontrol;4
5#define DATARATE_500KB 5000006
7void setup() {8 Serial.begin(9600);9 while (!Serial) {10 ; // wait for serial port to connect.11 }12
13 Serial.println("Start CAN initialization");14 comm_protocols.enableCAN();15 comm_protocols.can.frequency(DATARATE_500KB);16 Serial.println("Initialization done");17}18
19int counter = 0;20unsigned char payload = 0x49;21int payload_size = 1;22
23void loop() {24
25 mbed::CANMessage msg = mbed::CANMessage(13ul, &payload, payload_size);26 if (comm_protocols.can.write(msg)) {27 Serial.println("Message sent");28 } else {29 Serial.println("Transmission Error: ");30 Serial.println(comm_protocols.can.tderror());31 comm_protocols.can.reset();32 }33
34 delay(1000);35}
Using Arduino IDE
For users working with the Portenta C33, the following simple examples can be used to test the CAN bus protocol's capabilities.
CAN communication is not supported for the Portenta H7 on the Max Carrier.
The CAN Read example for Portenta C33 starts CAN communication at a rate of 500 kbps and continuously listens for incoming messages, displaying such information upon receipt.
1#include <Arduino_CAN.h>2
3void setup()4{5 Serial.begin(115200);6
7 while (!Serial) { } // open the serial monitor to start receiving8
9 if (!CAN.begin(CanBitRate::BR_500k))10 {11 Serial.println("CAN.begin(...) failed.");12 for (;;) {}13 }14 Serial.println("CAN.begin(...) Successful.");15}16
17void loop()18{19 if (CAN.available())20 {21 CanMsg const msg = CAN.read();22 Serial.println(msg);23 }24}
The CAN Write example, also set at 500 kbps, builds and sends a specific message format. This message includes a fixed preamble followed by an incrementing counter value that updates with each loop iteration.
1#include <Arduino_CAN.h>2
3static uint32_t const CAN_ID = 0x20;4
5void setup()6{7 Serial.begin(115200);8
9 while (!Serial) { } // open the serial monitor to start sending10
11 if (!CAN.begin(CanBitRate::BR_500k))12 {13 Serial.println("CAN.begin(...) failed.");14 for (;;) {}15 }16 Serial.println("CAN.begin(...) Successful.");17}18
19static uint32_t msg_cnt = 0;20
21void loop()22{23 /* Assemble a CAN message with the format of24 * 0xCA 0xFE 0x00 0x00 [4 byte message counter]25 */26 uint8_t const msg_data[] = {0xCA,0xFE,0,0,0,0,0,0};27 memcpy((void *)(msg_data + 3), &msg_cnt, sizeof(msg_cnt));28 CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data);29
30 /* Transmit the CAN message, capture and display an31 * error core in case of failure.32 */33 if (int const rc = CAN.write(msg); rc < 0)34 {35 Serial.print ("CAN.write(...) failed with error code ");36 Serial.println(rc);37 for (;;) { }38 }39
40 /* Increase the message counter. */41 msg_cnt++;42
43 /* Only send one message per second. */44 delay(1000);45}
As a practical example, we are going to implement the communication between the Max Carrier with a Portenta C33 and a Portenta Machine Control using CAN.
For stable CAN bus communication, it is recommended to install 120 Ω termination resistors between CANH and CANL lines.
- For the Portenta C33: Use the writing example from above.
- For the Portenta Machine Control: Install the
library from the Library Manager and use the following example sketch:Arduino_PortentaMachineControl.h
1#include <Arduino_PortentaMachineControl.h>2
3void setup() {4 Serial.begin(9600);5 while (!Serial) {6 ; // wait for serial port to connect.7 }8
9 if (!MachineControl_CANComm.begin(CanBitRate::BR_500k)) {10 Serial.println("CAN init failed.");11 while(1) ;12 }13}14
15void loop() {16 if (MachineControl_CANComm.available()) {17 CanMsg const msg = MachineControl_CANComm.read();18 // Print the sender ID19 Serial.print("ID: ");20 Serial.println(msg.id, HEX);21
22 // Print the Payload Bytes23 Serial.print("Message received: ");24 for (int i = 0; i < 4; i++) {25 Serial.print("0x");26 Serial.print(msg.data[i], HEX);27 Serial.print('\t');28 }29 Serial.println();30 }31}
Remember that the Portenta Machine Control must be programmed by selecting the
Portenta H7
as the target in the Arduino IDE.After uploading the code to the Max Carrier and the Machine Control, open both Serial Monitors and you will see the CAN messages exchange.
Serial RS-232/RS-485
The Portenta Max Carrier includes a multi-protocol transceiver supporting RS-232, RS-485, and RS-422 serial standards (configurable) based on the SP335 IC.
Default configuration:
- Full Duplex
- 232 protocol
- No data rate limit
- Enabled
Here is the connector pinout for reference:
We are going to implement the communication between the Portenta Max Carrier and the Machine Control leveraging two different protocols,
RS-485
and RS-232
. Use the following wiring respectively.Using Linux
In the Portenta Max Carrier, the UART used for the RS-232/485 transceiver is the
UART0
and its designation on the Portenta X8 is ttyX0
.To set up the serial communication so it matches the link requirements, we can configure the port baud rate, parity and stop bit as desired.
1stty -F /dev/ttyX0 115200 -parity cs8 -cstopb
The serial transceiver default configuration is set to RS-232, so we are going to use this protocol for the Linux example. Make sure to follow the respective wiring shown above.
We configured the Portenta Machine Control used for this example leveraging the code included with the
Arduino_PortentaMachineControl
library called RS232
which can be found on File > Examples > Arduino_PortentaMachineControl. This sketch will continuously send a message and wait to receive one using RS-232.After setting up the serial communication parameters, we can start receiving from the Machine Control with the following command:
1cat /dev/ttyX0
For sending we can use the following command instead:
1echo "Hello World" > /dev/ttyX0
As a containerized example, here we used Minicom as a serial communication tool.
First, run the container with the following options:
1docker run --privileged -it -u 0 --network host -v /dev:/dev debian:stable-slim bash
Install Minicom:
1apt-get update && apt-get install minicom -y
Once installed, run it with
minicom -s
, configure the serial port to /dev/ttyX0
and verify the baud rate is set to 115200
.Now, you should be able to send and receive data through the RS-232 serial transceiver using the Portenta X8, the Max Carrier and the Machine Control.
Using Arduino IDE
For users working with the Portenta H7 or Portenta C33, the following simple examples can be used to test the RS-232/485 communication.
To use these protocols some libraries are needed and you can install them by searching for
ArduinoRS485
and Arduino_PortentaMachineControl
on the library manager and clicking on install.Here is the example code for the Max Carrier with the Portenta H7, it will continuously send a message and wait for one. If a message arrives, it will be printed in the Serial Monitor.
RS-485 Example Code
1/*2 Circuit:3 - Portenta H74 - Max Carrier5 - A Slave device with RS485 interface (Tested with a Machine Control)6 - Connect PMC TXP/Y to Max Carrier RXP/A and TXN/Z to Max Carrier RXN/B7 - Connect PMC RXP/A to Max Carrier TXP/Y and RXN/B to Max Carrier TXN/Z8
9 created 21 Nov 202310 by Christopher Mendez11*/12
13#include <ArduinoRS485.h>14
15constexpr unsigned long sendInterval{ 1000 };16unsigned long sendNow{ 0 };17int counter = 0;18
19arduino::UART _UART4_{ PA_0, PI_9, NC, NC };20
21RS485Class rs485{ _UART4_, PA_0, PI_10, PJ_10 }; // UART4, TX, CTS, RTS22
23
24void setup() {25 // Set the Max Carrier Communication Protocols to default config26 RS485init();27 // RS485/RS232 default config is:28 // - RS485 mode29 // - Half Duplex30 // - No A/B and Y/Z 120 Ohm termination enabled31 delay(1000);32 // Enable the RS485/RS232 system33 rs485Enable(true);34 // Enable Full Duplex mode35 // This will also enable A/B and Y/Z 120 Ohm termination resistors36 rs485FullDuplex(true);37 // Specify baudrate, and preamble and postamble times for RS485 communication38 rs485.begin(115200, 0, 500);39 // Start in receive mode40 rs485.receive();41}42
43void loop() {44
45 if (rs485.available()) {46 Serial.write(rs485.read());47 }48
49 if (millis() > sendNow) {50
51 // Disable receive mode before transmission52 rs485.noReceive();53
54 rs485.beginTransmission();55 rs485.print("hello I'm Max ");56 rs485.println(counter++);57 rs485.endTransmission();58
59 // Re-enable receive mode after transmission60 rs485.receive();61 sendNow = millis() + sendInterval;62 }63}64
65void RS485init() {66 rs485Enable(false);67 rs485ModeRS232(false);68 rs485FullDuplex(false);69 rs485YZTerm(false);70 rs485ABTerm(false);71}72
73void rs485Enable(bool enable) {74 digitalWrite(PC_7, enable ? HIGH : LOW);75}76void rs485ModeRS232(bool enable) {77 digitalWrite(PC_6, enable ? LOW : HIGH);78}79void rs485YZTerm(bool enable) {80 digitalWrite(PG_3, enable ? HIGH : LOW);81}82void rs485ABTerm(bool enable) {83 digitalWrite(PJ_7, enable ? HIGH : LOW);84}85
86void rs485FullDuplex(bool enable) {87 digitalWrite(PA_8, enable ? LOW : HIGH);88 if (enable) {89 // RS485 Full Duplex require YZ and AB 120 Ohm termination enabled90 rs485YZTerm(true);91 rs485ABTerm(true);92 }93}
For the Portenta Machine Control, use the library's built-in example code. You can find it on File > Examples > Arduino_PortentaMachineControl > RS485_fullduplex.
Remember that the Portenta Machine Control must be programmed by selecting the
Portenta H7
as the target in the Arduino IDE.After uploading the code to the Max Carrier and the Machine Control, open both Serial Monitors and you will see the message exchange with a counter.
RS-232 Example Code
1/*2 Circuit:3 - Portenta H7 + Max Carrier4 - Arduino Portenta Machine Control (PMC)5 - Connect PMC TXN/Z to Max Carrier RXP/A6 - Connect PMC RXP/A to Max Carrier TXP/Z7
8 created 21 Nov 20239 by Christopher Mendez10*/11
12#include <ArduinoRS485.h>13
14constexpr unsigned long sendInterval{ 1000 };15unsigned long sendNow{ 0 };16int counter = 0;17
18arduino::UART _UART4_{ PA_0, PI_9, NC, NC }; // TX, RX19
20RS485Class rs485{ _UART4_, PA_0, PI_10, PJ_10 }; // UART4, TX, CTS, RTS21
22
23void setup() {24 // Set the Max Carrier Communication Protocols to default config25 RS485init();26 // RS485/RS232 default config is:27 // - RS485 mode28 // - Half Duplex29 // - No A/B and Y/Z 120 Ohm termination enabled30 delay(1000);31 // Enable the RS485/RS232 system32 rs485Enable(true);33 // Enable the RS232 mode34 rs485ModeRS232(true);35 // Specify baudrate for RS232 communication36 rs485.begin(115200);37 // Start in receive mode38 rs485.receive();39}40
41void loop() {42
43 if (rs485.available()) {44 Serial.write(rs485.read());45 }46
47 if (millis() > sendNow) {48 String log = "[";49 log += sendNow;50 log += "] ";51
52 String msg = "hello I'm Max ";53 msg += counter++;54
55 log += msg;56 Serial.println(log);57
58 // Disable receive mode before transmission59 rs485.noReceive();60
61 rs485.beginTransmission();62 rs485.println(msg);63 rs485.endTransmission();64
65 // Re-enable receive mode after transmission66 rs485.receive();67 sendNow = millis() + sendInterval;68 }69}70
71void RS485init() {72 rs485Enable(false);73 rs485ModeRS232(false);74 rs485FullDuplex(false);75 rs485YZTerm(false);76 rs485ABTerm(false);77}78
79void rs485Enable(bool enable) {80 digitalWrite(PC_7, enable ? HIGH : LOW);81}82void rs485ModeRS232(bool enable) {83 digitalWrite(PC_6, enable ? LOW : HIGH);84}85void rs485YZTerm(bool enable) {86 digitalWrite(PG_3, enable ? HIGH : LOW);87}88void rs485ABTerm(bool enable) {89 digitalWrite(PJ_7, enable ? HIGH : LOW);90}91
92void rs485FullDuplex(bool enable) {93 digitalWrite(PA_8, enable ? LOW : HIGH);94 if (enable) {95 // RS485 Full Duplex require YZ and AB 120 Ohm termination enabled96 rs485YZTerm(true);97 rs485ABTerm(true);98 }99}
For the Portenta Machine Control, use the library's built-in example code. You can find it on File > Examples > Arduino_PortentaMachineControl > RS232.
Remember that the Portenta Machine Control must be programmed by selecting the
Portenta H7
as the target in the Arduino IDE.After uploading the code to the Max Carrier and the Machine Control, open both Serial Monitors and you will see the message exchange with a counter and a time stamp.
Support
If you encounter any issues or have questions while working with the Portenta Max 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 Max 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 Max 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 Max 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 Max 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.