# OAK4 SoM Development Guide

Luxonis OAK4 SoM device supports flexible peripheral configuration, allowing users to enable or disable I2C, SPI, and UART
functionality on a variety of pins.

By default, most pins are configured as general-purpose I/O (GPIO). However, users can reconfigure supported pins to function as
peripheral interfaces. It's important to note that not all pins support all peripherals. The table below outlines which QUPv3 and
I2CHUB engines are available and how each can be configured.

If your use case requires a configuration not listed in the table or if the pins are marked as "Not currently exposed", please
contact Luxonis technical support to help with creating a custom configuration with different exposed options.

| **QUPv3 engine** | **Mode** | **Pins** | **Note** |
| --- | --- | --- | --- |
| QUPv3_SE0 | I2C | SDA: GPIO28SCL: GPIO29 | |
| QUPv3_SE1 | SPI | MISO: GPIO32MOSI: GPIO33SCLK: GPIO34CS0: GPIO35 | |
| QUPv3_SE2 | SPI | MISO: GPIO36MOSI: GPIO37SCLK: GPIO38CS0: GPIO39CS1: GPIO40CS2: GPIO41CS3: GPIO42 | |
| QUPv3_SE3 | / | / | Not currently exposed |
| QUPv3_SE4 | I2C | SDA: GPIO44SCL: GPIO45 | Enabled by default |
| QUPv3_SE5 | I2C | SDA: GPIO52SCL: GPIO53 | |
| QUPv3_SE6 | / | / | Not currently exposed |
| QUPv3_SE7 | UART | TX: GPIO26RX: GPIO27 | |
| QUPv3_SE8 | I2C | SDA: GPIO0SCL: GPIO1 | |
| QUPv3_SE9 | I3C | SDA: GPIO60SCL: GPIO61 | |
| QUPv3_SE10 | SPI | MISO: GPIO64MOSI: GPIO65SCLK: GPIO66CS0: GPIO67 | |
| QUPv3_SE11 | SPI | MISO: GPIO68MOSI: GPIO69SCLK: GPIO70CS0: GPIO71 | |
| QUPv3_SE12 | I2C | SDA: GPIO2SCL: GPIO3 | Enabled by default |
| QUPv3_SE13 | / | / | Not currently exposed |
| QUPv3_SE14 | UART | CTS: GPIO76RFR: GPIO77TX: GPIO78RX: GPIO79 | High-speed UART, hardware flow control |
| QUPv3_SE15 | / | / | Not currently exposed |
| I2CHUB_SE0 | / | / | Not currently exposed |
| I2CHUB_SE1 | / | / | Not currently exposed |
| I2CHUB_SE2 | I2C | SDA: GPIO20SCL: GPIO21 | Enabled by default |
| I2CHUB_SE3 | I2C | SDA: GPIO22SCL: GPIO23 | |
| I2CHUB_SE4 | I2C | SDA: GPIO4SCL: GPIO5 | |
| I2CHUB_SE5 | / | / | Not currently exposed |
| I2CHUB_SE6 | / | / | Not currently exposed |
| I2CHUB_SE7 | / | / | Not currently exposed |
| I2CHUB_SE8 | / | / | Not currently exposed |
| I2CHUB_SE9 | / | / | Not currently exposed |

## Device Tree Overlay Configuration

Peripheral configuration is achieved through Device Tree Overlays (DTOs), which are applied at boot time. Overlays must be placed
in the /persist/custom/dtbo directory on the device. During boot, the system applies all overlays found in this directory to
achieve the desired pin configuration. Files in this folder also survive OTA update and do not get deleted.

## I2C Configuration Example

### Connecting a BME280 Sensor via I2C

Suppose you want to connect a Bosch BME280 pressure sensor using GPIO52 (SDA) and GPIO53 (SCL). According to the table above,
these pins are managed by the QUPv3 SE5 engine.

To enable this interface, create a device tree overlay (e.g. bme280.dts) in your working directory:

```dts
/dts-v1/;
/plugin/;

/ {
  fragment@0 {
    target = <&qupv3_se5_i2c>;
    __overlay__ {
      status = "ok";
      #address-cells = <1>;
      #size-cells = <0>;

      bme280@76 {
        compatible = "bosch,bme280";
        reg = <0x76>;
      };
    };
  };
};
```

The important line is status = "ok"; which actually enables the selected I2C engine. Without it, the peripheral remains disabled.

> **Note:**
> For
> `target`
> , use
> `&qupv3_seX_i2c`
> for QUP engines, and
> `&qupv3_hub_i2cX`
> for HUB engines, where
> `X`
> is the engine index.

### Compiling the Overlay

Use the SDK Docker container to compile the source. Run the following inside your working directory:

```bash
docker run --rm \
  -v /etc/passwd:/etc/passwd:ro \
  -v /etc/shadow:/etc/shadow:ro \
  -v "$PWD":"$PWD" \
  -w "$PWD" \
  --user=$(id -u):$(id -g) --group-add 27 \
  luxonis/luxonis-os-rvc4:<SDK_version>-public \
  /bin/bash -c "dtc -@ -I dts -O dtb -o bme280.dtbo bme280.dts"
```

> **Note:**
> `<SDK_version>`
> must match the version of OS with which the target device was flashed (eg.
> `1.8.0`
> ). You can also use
> `latest`
> if you are using the latest OS release.

Transfer the compiled .dtbo file to the target device:

```bash
scp /app/test-overlay/bme280.dtbo user@<device-ip>:/persist/custom/dtbo/
```

Then reboot the device. Upon boot, the overlay will be applied automatically. Verify that the I2C device has been registered by
inspecting /sys/bus/i2c/devices/.

## SPI Configuration Example

Enabling an SPI interface follows the same approach. Here's an example DTO for enabling SPI on QUPv3 SE1:

```dts
/dts-v1/;
/plugin/;

/ {
  fragment@0 {
    target = <&qupv3_se1_spi>;
    __overlay__ {
      status = "ok";
    };
  };
};
```

Compile and transfer the overlay:

```bash
docker run --rm \
  -v /etc/passwd:/etc/passwd:ro \
  -v /etc/shadow:/etc/shadow:ro \
  -v "$PWD":"$PWD" \
  -w "$PWD" \
  --user=$(id -u):$(id -g) --group-add 27 \
  luxonis/luxonis-os-rvc4:latest-public \
  /bin/bash -c "dtc -@ -I dts -O dtb -o spi-overlay.dtbo spi-overlay.dts"
```

```bash
scp spi_se1.dtbo user@<device-ip>:/persist/custom/dtbo/
```

Reboot the device and the SPI interface should be enabled.

## Factory Mode

Factory mode is a mode of the Luxonis OS which makes the device easy to work with in a factory environment. It is useful for
testing the device. It can be enabled for any kind of OAK4 device, but is mostly useful for people developing with SoMs which also
ships in factory-mode by default.

Factory mode removes the root user's password and enables SSH login with an empty password (no password login).

Factory mode is entered by creating a file under /persist/factory/enabled (the content of the file doesn't matter).

Upon boot, factory mode is triggered if this file is found.

To revert and exit factory mode, run this command on the system:

```bash
rm /persist/factory/enabled && cp -r /persist/factory/original_files/* / && rm -rf /persist/factory/original_files && sync && reboot
```

Upon the next boot the system will run in normal mode, where the default root password is oelinux123 and SSH password login is
disabled.

## Troubleshooting

If an unsupported peripheral configuration is attempted—e.g., assigning a peripheral to an incompatible QUPv3 engine—the device
may fail to boot. In such cases, the TrustZone engine is preventing the system from proceeding.

To recover:

 1. Enter Emergency Download Mode (EDL).
 2. Re-flash the device with a known-good image.

For steps on how to perform Full OS Flash, check out the following page: [Full OS
Flash](https://docs.luxonis.com/software-v3/sw-stack/luxonis-os.md).

To aid in debugging, inspect kernel messages using dmesg:

```bash
dmesg | less
```

Look for errors related to device tree parsing, peripheral activation, or security policy violations. Optionally, check the logs
from device tree overlay loading script:

```bash
journalctl -u apply-overlays.service
```
