Skip to content

Commit cc38585

Browse files
committed
Hat carrier user manual patch
1 parent f1e0039 commit cc38585

File tree

1 file changed

+49
-228
lines changed
  • content/hardware/04.pro/carriers/portenta-hat-carrier/tutorials/user-manual

1 file changed

+49
-228
lines changed

content/hardware/04.pro/carriers/portenta-hat-carrier/tutorials/user-manual/content.md

Lines changed: 49 additions & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -417,228 +417,7 @@ The file is uploaded to `/home/fio` directory. Navigate to the directory using A
417417
python3 hello_world_python.py
418418
```
419419

420-
Portenta Hat Carrier's user programmable LED will start blinking whenever the script is running. If you wish to upload to a containerized environment, as a Docker container, the following command can be used to do so:
421-
422-
```bash
423-
docker cp hello_world_python.py mycontainer:/app/
424-
```
425-
426-
Then the script can be used by accessing the container with the following command:
427-
428-
```bash
429-
docker exec -it mycontainer sh
430-
```
431-
432-
#### Hello World Using Linux and Docker
433-
<br></br>
434-
435-
We can use the Python® script and create a Docker image as well for containerized applications.
436-
437-
For you convenience, the example files can be downloaded [here](assets/hello_world_led.zip) and used as a template for containerizing further examples.
438-
439-
The example script to use will be the following sketch:
440-
441-
```python
442-
#!/usr/bin/env python3
443-
444-
import serial
445-
import logging
446-
447-
class GPIOController:
448-
def __init__(self, gpio_number):
449-
self.gpio_number = gpio_number
450-
self.gpio_path = f"/sys/class/gpio/gpio{gpio_number}/"
451-
452-
def export(self):
453-
with open("/sys/class/gpio/export", "w") as f:
454-
f.write(str(self.gpio_number))
455-
456-
def unexport(self):
457-
with open("/sys/class/gpio/unexport", "w") as f:
458-
f.write(str(self.gpio_number))
459-
460-
def set_direction(self, direction):
461-
with open(f"{self.gpio_path}direction", "w") as f:
462-
f.write(direction)
463-
464-
def read_direction(self):
465-
with open(f"{self.gpio_path}direction", "r") as f:
466-
return f.read().strip()
467-
468-
def set_value(self, value):
469-
with open(f"{self.gpio_path}value", "w") as f:
470-
f.write(str(value))
471-
472-
def read_value(self):
473-
with open(f"{self.gpio_path}value", "r") as f:
474-
return int(f.read().strip())
475-
476-
def main():
477-
478-
logging.info("============================================")
479-
logging.info("Hello World PHC!")
480-
logging.info("============================================")
481-
482-
ser = serial.Serial()
483-
ser.baudrate = 19200
484-
ser.port = '/dev/ttymxc3'
485-
ser.bytesize = 8
486-
ser.parity = 'N'
487-
ser.stopbits = 2
488-
ser.timeout = 1
489-
logging.debug("Configured serial port with:\n\r%s" % str(ser))
490-
logging.debug("Opening serial port")
491-
492-
gpio = GPIOController(163)
493-
494-
# Export GPIO
495-
gpio.export()
496-
497-
# Set as output
498-
gpio.set_direction("out")
499-
if gpio.read_direction() == "out":
500-
print("GPIO set as output.")
501-
502-
# Turn on (set to 1) and then off (set to 0)
503-
while True:
504-
gpio.set_value(1)
505-
time.sleep(1)
506-
gpio.set_value(0)
507-
time.sleep(1)
508-
509-
# Unexport
510-
# gpio.unexport()
511-
512-
if __name__ == "__main__":
513-
main()
514-
```
515-
516-
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.
517-
518-
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.
519-
520-
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.
521-
522-
```bash
523-
# dockerfile
524-
525-
# Use an official Python runtime as the base image
526-
FROM python:3.9-slim
527-
528-
COPY requirements.txt ./
529-
530-
RUN set -ex \
531-
&& pip3 --disable-pip-version-check --no-cache-dir install \
532-
-r requirements.txt \
533-
&& rm ./requirements.txt
534-
535-
# Set the working directory
536-
WORKDIR /app
537-
538-
# Copy the local code to the container
539-
COPY ./src/hello_world_led.py ./
540-
541-
RUN chmod 755 -R .
542-
543-
# Run the Python script
544-
CMD ["python", "./hello_world_led.py"]
545-
```
546-
547-
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.
548-
549-
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.
550-
551-
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.
552-
553-
```
554-
pyserial==3.4
555-
```
556-
557-
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.
558-
559-
```bash
560-
# docker-compose.yaml
561-
562-
version: '3.6'
563-
564-
services:
565-
hello_world_led:
566-
image: hello_world_led:latest
567-
container_name: 'hello_world_led'
568-
restart: unless-stopped
569-
environment:
570-
- PYTHONUNBUFFERED=1
571-
volumes:
572-
- '/var/run/secrets:/app/config/'
573-
- '/run/arduino_hw_info.env:/run/arduino_hw_info.env:ro'
574-
extra_hosts:
575-
- 'm4-proxy:host-gateway'
576-
devices:
577-
- '/dev/ttymxc3'
578-
tty: true
579-
user: "0"
580-
```
581-
582-
This file tree diagram illustrates how the directory should be structured, containing both the Python® script and the associated Docker components:
583-
584-
```
585-
└── hello_world_led
586-
├── src
587-
│ ├── hello_world_led.py
588-
├── Docker-build.conf
589-
├── docker-compose.yaml
590-
├── dockerfile
591-
└── requirements.txt
592-
```
593-
594-
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:
595-
596-
```
597-
adb push <local directory path> /home/fio
598-
```
599-
600-
Once the files are transferred, access the shell of the Portenta X8 with elevated privileges:
601-
602-
```
603-
adb shell
604-
sudo su -
605-
```
606-
607-
To execute the script, first build the Docker image. Navigate to the directory containing the previously mentioned files and run:
608-
609-
```bash
610-
sudo docker build . -t hello_world_led
611-
```
612-
613-
The commands provided outline the process of starting the container with the necessary permissions, ensuring access to the required components on the Portenta X8.
614-
615-
The following commands run the container and ensure that the script inside can interact with the GPIOs, even within the container's environment.
616-
617-
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.
618-
619-
```bash
620-
docker compose up
621-
```
622-
623-
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.
624-
625-
```bash
626-
docker run --privileged hello_world_led
627-
```
628-
629-
This is useful for certain operations like direct hardware access, which is likely necessary for GPIO interactions.
630-
631-
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.
632-
633-
```bash
634-
docker run -v /sys:/sys hello_world_led
635-
```
636-
637-
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.
638-
639-
The Python® scripts found later in the user manual can be used to containerize and leverage benefits of using Docker.
640-
641-
***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).***
420+
Portenta Hat Carrier's user programmable LED will start blinking whenever the script is running.
642421

643422
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.
644423

@@ -766,24 +545,66 @@ Devices with a USB-A interface, such as storage drives, can be used for logging
766545
#### Using Linux
767546
<br></br>
768547

769-
As an example, following command on Portenta X8's shell can be used to test write command with a USB memory drive.
548+
As an example, following command on Portenta X8's shell can be used to test write command with a USB memory drive. To write a file, following sequence of commands can help you accomplish such task.
549+
550+
```bash
551+
dmesg -w
552+
```
553+
554+
The `dmesg -w` command displays kernel messages, helping you monitor system events in real-time. It is particularly useful to see if it has recognized the USB drive when plugged in.
555+
556+
```bash
557+
lsblk
558+
```
559+
560+
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 is the partition designation, of the plugged-in USB drive.
561+
562+
```bash
563+
mkdir -p /mnt/USBmount
564+
```
565+
566+
The `mkdir -p` command creates the directory `/mnt/USBmount`. If the directory already exists, this command won't produce an error. This directory will be used as a mount point for the USB drive.
770567

771568
```bash
772-
dd if=/dev/urandom of=random.bin bs=1M count=128
569+
mount -t vfat /dev/sda1 /mnt/USBmount
773570
```
774571

775-
This command will create a _random.bin_ file filled with 128 Megabytes of random data. It reads data from the system's pseudo-random number generator `/dev/urandom` and writes it to the file in chunks of 1 Megabyte.
572+
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.
573+
574+
```bash
575+
dd if=/dev/urandom of=/mnt/USBmount/random.bin bs=1K count=16
576+
```
577+
578+
This command will create a _random.bin_ file filled with 16 Kilobytes of random data. It reads data from the system's pseudo-random number generator `/dev/urandom` and writes it to the file in chunks of 1 Kilobyte.
776579

777580
To read the _random.bin_ file with random data, you can use the following command:
778581

779582
```bash
780-
dd if=random.bin bs=1M count=128 | hexdump -C
583+
dd if=/mnt/USBmount/random.bin bs=1K count=16 | hexdump -C
781584
```
782585

783-
This will read the previously generated _random.bin_ file and displays its content in a hexadecimal format on the console. Data is read in chunks of 1 Megabyte up to 128 Megabytes and then processed for display using `hexdump`.
586+
This will read the previously generated _random.bin_ file and displays its content in a hexadecimal format on the console. Data is read in chunks of 1 Kilobyte up to 16 Kilobytes and then processed for display using `hexdump`.
784587

785588
***Reading the entire _random.bin_ file with the `hexdump` command will produce a large output on the console. Use with caution.***
786589

590+
n the Portenta X8's shell, if you aim to create a text file containing the message `Hello, World!` on a USB memory drive, you can employ the command:
591+
592+
```bash
593+
dd if=<(echo -n "Hello, World!") of=/mnt/USBmount/helloworld.txt
594+
```
595+
596+
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`.
597+
598+
Subsequently, the message gets inscribed into a file named _helloworld.txt_ situated in the `/mnt/USBmount` directory.
599+
600+
After creating the file, if you wish to retrieve its contents and display them on the shell, you can use:
601+
602+
```bash
603+
dd if=/mnt/USBmount/helloworld.txt bs=1K count=1
604+
```
605+
606+
This command directs `dd` to peruse the contents of _helloworld.txt_. With a specified block size of 1 Kilobyte, the reading is confined to a single block—adequate given the brevity of the `Hello, World!` message. Upon executing this command, the content of the text file will be displayed on your shell.
607+
787608
#### Using Arduino IDE
788609
<br></br>
789610

@@ -2762,7 +2583,7 @@ It can interact with up to four relay ports on the board. Among its various feat
27622583
```python
27632584
from __future__ import print_function
27642585

2765-
import smbus
2586+
import smbus2 import SMBus
27662587

27672588
# The number of relay ports on the relay board.
27682589
# This value should never change!

0 commit comments

Comments
 (0)