> For the complete documentation index, see [llms.txt](https://docs.theseus.us/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.theseus.us/cyclops/cyclops-box/pi-5-walkthrough/hardware-setup.md).

# Hardware Setup

**Cyclops requires a drive** to store maps and recordings, a **camera**, and a **connection to the flight controller**. We will walk through each of those and ensure they are properly setup.

For our own systems we store everything on a single boot micro SD card. This helps save on system cost. The connection to the flight controller is a serial connection that uses mavlink to communicate. Minimizing the length of the serial cable can help eliminate noise and mavlink connection issues.

## Setting up an NVME as the storage device

You only need to follow these directions if you plan on using an NVME with cyclops. If you are satisfied the read/write speeds of the SD card, you can skip this step.

{% file src="/files/A9imv78mOM9IUeRuPAaM" %}

This [tutorial on mounting a drive on linux](https://www.wikihow.com/Linux-How-to-Mount-Drive#:~:text=To%20mount%20a%20drive%20on,to%20mount%20and%20unmount%20drives.) will work with a PCIE NVME drive or a USB SSD.\
**OR**\
you can run the above script to setup a USB SSD drive.

You may pick any name for the drive. I am using a Samsung T9 SSD, so I call it `/t9`

```bash
pi@pi5:~$ sudo Downloads/setup-storage-mount.sh /dev/sda2 /mnt/t9
Device: /dev/sda2
UUID: C443-4727
Filesystem: exfat
Mount point: /mnt/t9

Creating systemd mount unit: mnt-t9.mount
Created symlink /etc/systemd/system/multi-user.target.wants/mnt-t9.mount → /etc/systemd/system
/mnt-t9.mount.
Creating udev rule for auto-mount on plug: /etc/udev/rules.d/99-auto-mount-C443-4727.rules
Mounting...

✓ Successfully mounted /dev/sda2 to /mnt/t9
```

Verify that the drive is mounted with:

```bash
lsblk
```

<figure><img src="/files/g3e2tTAYxFnCG9y7fg7p" alt=""><figcaption></figcaption></figure>

## Setup UART Serial

Your device needs to communicate with the flight controller using a serial connection. There are many ways to set this up. You can find docs on the manufacturer's website for your specific hardware setup.

In the case of the nanopi, you can use a serial hat. In the case of the raspi 5, you can look at their provided [docs](https://www.raspberrypi.com/documentation/computers/configuration.html#configure-uarts) on configuring this.

Once you've set up the serial connection, verify you have the serial connections:

```
pi@pi5:~$ ls /dev/tty*
```

### Raspberry Pi 5 Serial Setup (Ubuntu 24)

#### Setup script

To save time, if using a new rpi 5 we recommend using this setup script after flashing the boot sd card. The script will apply some default configurations so you don't need to manually setup the serial port.

On first boot, cloud-init will:

* Set hostname: clop-$DEVICE\_NUM
* Create user 'pi' (password: pi, sudo)
* Enable SSH with password auth
* Set static IP: 192.168.218.100
* Create /home/pi/recordings
* Disable EEE on eth0 (Pi 5 link-flap fix)
* Setup pi serial port
* Reboot to apply boot config changes

{% file src="/files/oZeH01vOyNBLVrz2034s" %}

#### Manual setup of MAVLink Serial

UART0 is used for the MAVLink serial connection between the Pi 5 and the flight controller at 921600 baud.

**Pinout: (**[**Pi 5 pinout diagram**](https://vilros.com/pages/raspberry-pi-5-pinout)**)**

| Function | GPIO | Header Pin | Connects To |
| -------- | ---- | ---------- | ----------- |
| TX       | 14   | Pin 8      | FC RX       |
| RX       | 15   | Pin 10     | FC TX       |
| GND      | —    | Any GND    | FC GND      |

Both the Pi 5 and flight controller operate at 3.3V logic. No level shifting required.

**Background — Pi 5 UART naming:**

On Pi 5, GPIO 14/15 are driven by a PL011 UART in the RP1 southbridge, which shows up at MMIO `0x1f00030000`. The device node it enumerates to depends on the OS image:

* **Ubuntu 24.04 Desktop (Pi 5):** `/dev/ttyAMA0` (with a `/dev/serial0` symlink in some releases)
* **Ubuntu 24.04 Server (recommended for 2GB Pi 5):** `/dev/ttyAMA0`, no `/dev/serial0` symlink
* A second node `/dev/ttyAMA10` (at `0x107d001000`) will also appear — this is an internal SoC UART, **not** GPIO 14/15. Ignore it.

To confirm which node is GPIO 14/15, check `dmesg`:

bash

```bash
sudo dmesg | grep -iE 'pl011|107d001|1f00030'
```

The line containing `1f00030000.serial` identifies the GPIO header UART (typically `ttyAMA0`).

**Setup:**

1. Disable the serial console if active (Desktop images sometimes enable this; Server typically does not):

bash

```bash
    sudo systemctl disable serial-getty@ttyAMA0.service
    sudo systemctl stop serial-getty@ttyAMA0.service
```

2. Remove `console=ttyAMA0,115200` or `console=serial0,115200` from `/boot/firmware/cmdline.txt` if present:

bash

```bash
    sudo nano /boot/firmware/cmdline.txt
```

{% code overflow="wrap" %}

```
The file should be a single line. Remove only the `console=ttyAMA0,...` or `console=serial0,...` token (leave everything else intact). Save with `Ctrl+O`, Enter, `Ctrl+X`.
```

{% endcode %}

3\. Edit `/boot/firmware/config.txt`. Under the `[all]` section, ensure both of these are set:

```bash
    sudo nano /boot/firmware/config.txt
```

```
    enable_uart=1
    dtoverlay=uart0-pi5
```

{% code overflow="wrap" %}

```
Note: on Ubuntu for Pi 5, `enable_uart=1` alone is **not** sufficient — the `uart0-pi5` overlay is required to instantiate the PL011 on GPIO 14/15.
```

{% endcode %}

4\. Reboot. UART is available at `/dev/ttyAMA0`.

**Verification:**

Connect the Pi to the flight controller and ensure the FC's corresponding serial port is configured to output MAVLink2 at 921600 baud. Then:

bash

```bash
sudo stty -F /dev/ttyAMA0 921600 raw -echo
sudo cat /dev/ttyAMA0 | head -c 200 | xxd
```

You should see binary output with recurring `fd` bytes (MAVLink2 start byte) followed by a length byte and incrementing sequence numbers. Example:

```
00000000: fd1c 0000 d901 0121 0000 4704 1200 5d2c  .......!..G...],
00000010: 0000 18ae 0000 6a04 0000 6904 0000 0000  ......j...i.....
```

This confirms MAVLink data is flowing and the serial link is working. `Ctrl+C` to stop.

**Troubleshooting:**

* `cat: /dev/ttyAMA0: No such file or directory` → `uart0-pi5` overlay not loaded. Verify `config.txt` and reboot.
* Only `/dev/ttyAMA10` exists → same cause; the GPIO UART hasn't been instantiated.
* No `0x1f00030000.serial` line in `dmesg` → overlay didn't apply; check for typos in `config.txt`.
* Data flowing but garbled even at correct baud → check TX/RX aren't swapped, and confirm FC is set to MAVLink2 (not MAVLink1, which starts with `fe`).

## MAVLink over udp/tcp

For cyclops versions >=1.20.0, mavlink connection to the flight controller over udp/tcp is supported. The cli tool `vns-config` on the edge device is the only way to configure an ip mavlink connection.

{% code title="On the edge device" %}

```bash
vns-config add mavproxy.device <mavproxy-endpoint>
```

{% endcode %}

Do not provide a baudrate for UDP or TCP endpoints. The endpoint string is passed directly to MAVProxy, so use MAVProxy endpoint syntax for `udp`, `udpin`, `udpout`, `tcp`, `tcpin`, or `tcpout` endpoints:

| Endpoint                    | Use                                                             |
| --------------------------- | --------------------------------------------------------------- |
| `udp:192.168.1.10:14550`    | Use MAVProxy's UDP endpoint mode for `192.168.1.10:14550`       |
| `udpin:0.0.0.0:14550`       | Listen for UDP MAVLink packets on local port `14550`            |
| `udpout:192.168.1.10:14550` | Send UDP MAVLink packets to `192.168.1.10:14550`                |
| `tcp:192.168.1.10:5760`     | Connect to a TCP MAVLink server                                 |
| `tcpin:0.0.0.0:5760`        | Listen for a TCP MAVLink client                                 |
| `tcpout:192.168.1.10:5760`  | Use MAVProxy's TCP output endpoint mode for `192.168.1.10:5760` |

When you add an endpoint that already exists, `vns-config` updates that entry. Adding an existing network endpoint without a baudrate clears any baudrate from that endpoint.

Restart MAVProxy after changing endpoints:

```bash
sudo systemctl restart mavproxy
```

Then verify the active configuration:

```bash
vns-config list mavproxy.devices
journalctl -u mavproxy.service -f
```

### **Example: UDP MAVLink from a Flight Controller**

Configure the NanoPi to listen for MAVLink on UDP port `14550`:

```bash
vns-config add mavproxy.device udpin:0.0.0.0:14550
sudo systemctl restart mavproxy
```

Configure the flight controller or companion network sender to send MAVLink to the NanoPi IP address on port `14550`.

### **Example: TCP MAVLink Server**

Configure MAVProxy to connect to a TCP MAVLink server at `192.168.1.10:5760`:

```bash
vns-config add mavproxy.device tcp:192.168.1.10:5760
sudo systemctl restart mavproxy
```

### **Example: Replace Serial with Ethernet**

Remove the default serial ports and add a UDP network endpoint:

```bash
vns-config remove mavproxy.device /dev/ttyS4
vns-config remove mavproxy.device /dev/ttyS5
vns-config add mavproxy.device udpin:0.0.0.0:14550
sudo systemctl restart mavproxy
```

## USB Port Full Power (GPIO Header Power Input)

When powering the Pi 5 via the GPIO header (pins 2/4 for 5V, bypassing the USB-C PMIC), USB ports default to a 600mA total current budget. This must be overridden to provide full power to USB peripherals.

**Edit `/boot/firmware/config.txt`:**

bash

```bash
sudo nano /boot/firmware/config.txt
```

Add this line under the `[all]` section:

```
usb_max_current_enable=1
```

Save with `Ctrl+O`, Enter, `Ctrl+X`. Reboot to apply:

bash

```bash
sudo reboot
```

This raises the total USB port current budget to 1.6A.

#### Recap

We have now set up all of our hardware.&#x20;

Your addresses may vary, but we have:

```
Recording drive at /mnt/t9
Serial connections at /dev/ttySC0 and /dev/ttySC1
Video device at /dev/video0
```

Great job!
