Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Portenta Hat Carrier: User Manual Fan Control Section Minor Typo Patch #1443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
jcarolinares merged 7 commits into main from taddy/hatCarrier_user_manual_update_v2
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -419,217 +419,6 @@ python3 hello_world_python.py

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

#### Hello World Using Linux and Docker
<br></br>

We can use the Python® script and create a Docker image as well for containerized applications.

For you convenience, the example files can be downloaded [here](assets/hello_world_led.zip) and used as a template for containerizing further examples.

The example script to use will be the following sketch:

```python
#!/usr/bin/env python3

import serial
import logging

class GPIOController:
def __init__(self, gpio_number):
self.gpio_number = gpio_number
self.gpio_path = f"/sys/class/gpio/gpio{gpio_number}/"

def export(self):
with open("/sys/class/gpio/export", "w") as f:
f.write(str(self.gpio_number))

def unexport(self):
with open("/sys/class/gpio/unexport", "w") as f:
f.write(str(self.gpio_number))

def set_direction(self, direction):
with open(f"{self.gpio_path}direction", "w") as f:
f.write(direction)

def read_direction(self):
with open(f"{self.gpio_path}direction", "r") as f:
return f.read().strip()

def set_value(self, value):
with open(f"{self.gpio_path}value", "w") as f:
f.write(str(value))

def read_value(self):
with open(f"{self.gpio_path}value", "r") as f:
return int(f.read().strip())

def main():

logging.info("============================================")
logging.info("Hello World PHC!")
logging.info("============================================")

ser = serial.Serial()
ser.baudrate = 19200
ser.port = '/dev/ttymxc3'
ser.bytesize = 8
ser.parity = 'N'
ser.stopbits = 2
ser.timeout = 1
logging.debug("Configured serial port with:\n\r%s" % str(ser))
logging.debug("Opening serial port")

gpio = GPIOController(163)

# Export GPIO
gpio.export()

# Set as output
gpio.set_direction("out")
if gpio.read_direction() == "out":
print("GPIO set as output.")

# Turn on (set to 1) and then off (set to 0)
while True:
gpio.set_value(1)
time.sleep(1)
gpio.set_value(0)
time.sleep(1)

# Unexport
# gpio.unexport()

if __name__ == "__main__":
main()
```

The provided Python® script begins with the import of required modules. The `serial` library, in particular, will help us to communicate over serial ports. This script then defines a `GPIOController` class that packages the functionalities we executed manually in the shell commands.

This abstraction makes it easier to manipulate the GPIO without having to rewrite shell commands for every operation. Functions like `export`, `unexport`, `set_direction`, and `set_value` are used to mirror the actions we took in our manual steps.

The decision to containerize the Python® script using Docker ensures that it runs in a consistent environment and is isolated from other processes. Docker provides a mechanism to create containerized applications that can be executed reliably across various platforms.

```bash
# dockerfile

# Use an official Python runtime as the base image
FROM python:3.9-slim

COPY requirements.txt ./

RUN set -ex \
&& pip3 --disable-pip-version-check --no-cache-dir install \
-r requirements.txt \
&& rm ./requirements.txt

# Set the working directory
WORKDIR /app

# Copy the local code to the container
COPY ./src/hello_world_led.py ./

RUN chmod 755 -R .

# Run the Python script
CMD ["python", "./hello_world_led.py"]
```

The _dockerfile_ begins by selecting an official Python® runtime as the base image, which ensures Python® is set up and ready to run scripts. Following this, the _`requirements.txt`_ file, which lists the Python® libraries our script depends upon, is copied into the Docker image. The pip command is then used to install these dependencies.

The script depends on external libraries for functionality, specifically the `pyserial` library for this instance, which aids in serial communication. This library is defined in the _`requirements.txt`_ file.

You must manually create this text file and add the following line to it. In further applications, this file will contain any Python® library that is needed to support your script.

```
pyserial==3.4
```

Within the dockerfile, a working directory, `/app`, is defined inside the container. The Python® script is copied into this directory and granted execution permissions, ensuring that it can run without issues. The concluding action sets the default command for the Docker container to initiate the Python® script when the container starts.

```bash
# docker-compose.yaml

version: '3.6'

services:
hello_world_led:
image: hello_world_led:latest
container_name: 'hello_world_led'
restart: unless-stopped
environment:
- PYTHONUNBUFFERED=1
volumes:
- '/var/run/secrets:/app/config/'
- '/run/arduino_hw_info.env:/run/arduino_hw_info.env:ro'
extra_hosts:
- 'm4-proxy:host-gateway'
devices:
- '/dev/ttymxc3'
tty: true
user: "0"
```

This file tree diagram illustrates how the directory should be structured, containing both the Python® script and the associated Docker components:

```
└── hello_world_led
├── src
│ ├── hello_world_led.py
├── Docker-build.conf
├── docker-compose.yaml
├── dockerfile
└── requirements.txt
```

For the Portenta X8, you will need to upload the Python® script along with the Docker elements. You can set up a directory (with a name of your choosing), in this case `hello_world_led`, within the container resources for dockerization to ease this transfer:

```
adb push <local directory path> /home/fio
```

Once the files are transferred, access the shell of the Portenta X8 with elevated privileges:

```
adb shell
sudo su -
```

To execute the script, first build the Docker image. Navigate to the directory containing the previously mentioned files and run:

```bash
sudo docker build . -t hello_world_led
```

The commands provided outline the process of starting the container with the necessary permissions, ensuring access to the required components on the Portenta X8.

The following commands run the container and ensure that the script inside can interact with the GPIOs, even within the container's environment.

Following command uses the _docker-compose.yml_ configuration file to start the services defined within. It is a tool for defining and running multi-container Docker applications.

```bash
docker compose up
```

Then we have the next command allowing us to run the `hello_world_led` container with elevated privileges, giving it almost the same level of access to the host as processes running outside containers.

```bash
docker run --privileged hello_world_led
```

This is useful for certain operations like direct hardware access, which is likely necessary for GPIO interactions.

Subsequently, the following command mounts the host's `/sys` directory into the `hello_world_led` container, which is often used for interacting with device drivers and kernel features.

```bash
docker run -v /sys:/sys hello_world_led
```

This allows the container to have direct access to the host's GPIOs and other system attributes. Given its access capabilities, the container can be run; however, always ensure the configuration is set correctly.

The Python® scripts found later in the user manual can be used to containerize and leverage benefits of using Docker.

***To delve deeper into the use of Docker and custom containers on the Portenta X8, refer to this [tutorial](https://docs.arduino.cc/tutorials/portenta-x8/custom-container).***

Please check out the [Portenta X8 user manual](https://docs.arduino.cc/tutorials/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 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](https://docs.arduino.cc/tutorials/portenta-x8/user-manual#linux-environment).
Expand Down Expand Up @@ -1443,7 +1232,7 @@ echo 50000 | sudo tee /sys/class/pwm/pwmchip0/pwm9/duty_cycle #50% duty
And activate the PWM channel:

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

Consider the following Python® script if you would like to automate the command sequence:
Expand Down Expand Up @@ -1990,7 +1779,19 @@ Using the Portenta X8 in combination with the Hat Carrier allows you to evaluate

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](https://iperf.fr/iperf-doc.php).

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](https://github.com/userdocs/iperf3-static).
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](https://github.com/userdocs/iperf3-static). In this case, we need _aarch64 / arm64_ version, thus we need to execute following commands:

```bash
mkdir -p ~/bin && source ~/.profile
```

```bash
wget -qO ~/bin/iperf3 https://github.com/userdocs/iperf3-static/releases/latest/download/iperf3-arm64v8
```

```bash
chmod 700 ~/bin/iperf3
```

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

Expand All @@ -2004,16 +1805,24 @@ By following the provided instructions, the tool should be located in the Linux
# ~bin/
```

Check the tool's version using the following command:

```bash
./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:

```bash
iperf3 -s
./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:

```bash
iperf3 -s -p <Port Number>
./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 Ethernet connection:
Expand Down Expand Up @@ -2794,7 +2603,7 @@ It can interact with up to four relay ports on the board. Among its various feat
```python
from __future__ import print_function

import smbus2 import SMBus
from smbus2 import SMBus

# The number of relay ports on the relay board.
# This value should never change!
Expand All @@ -2807,7 +2616,7 @@ DEVICE_ADDRESS = 0x20 # 7 bit address (will be left shifted to add the read wri
DEVICE_REG_MODE1 = 0x06
DEVICE_REG_DATA = 0xff

bus = smbus.SMBus(3) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
bus = SMBus(3) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)


def relay_on(relay_num):
Expand Down Expand Up @@ -3659,7 +3468,7 @@ def processData(data):
print("Received:", data) # For now, just print the data. Modify as needed.

# Set up the serial port
ser = serial.Serial('/dev/ttymxc1', 9600) # Use the appropriate port and baud rate for your device
ser = serial.Serial('/dev/ttymxc3', 9600) # Use the appropriate port and baud rate for your device

incoming = ""

Expand Down

AltStyle によって変換されたページ (->オリジナル) /