Skip to content
Open
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
117 changes: 117 additions & 0 deletions m8-controller-box/README.md
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest having a README that is more similar to other READMEs we have in other top level folders (e.g here or here etc.). Though since this does have some additional information about the actual interfaces we could do a merge of the two styles but I would still prefer to have this table towards the top of the README so that user can quickly jump to the example that suits them.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this PR creates a new top-level directory we need to also add it to the first page README here

Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# M8 Controller Box (PR1)

The **M8 Controller Box (PR1)** is a USB-connected expansion module designed for integrating CAN devices, GPIO, relays, and peripheral interfaces into OAK-based systems.

This document serves as the **primary usage reference** for the M8 Controller Box within `oak-examples`.


## Key Features

### CAN Interface

* Supports **CAN 2.0A and 2.0B**
* Baud rates up to **1 Mbps**
* Native **Linux SocketCAN** interface

> For more information look at [can-example](./can-example/)

### USB Audio

* Integrated **buzzer / 3.5mm audio output**
* Available via internal USB connection

> For more information look at the [simple-example](./simple-example/)

### USB Expansion

* **2× USB-A ports**
* USB 2.0 speeds
* Up to **500mA current limit (shared)**


### Serial Interface

* **1× RS232 interface**

> For more information look at the [library example](https://github.com/luxonis/rp2040_u2if/blob/main/examples/ControllerBox/example_controller_box_serial.py).

### Isolated Strobe Driver

* Supports **5–24V strobe lights**
* Electrically isolated output

> For more information look at the [strobe-relay example](./strobe-relay-example/)

### GPIO

* **16× GPIO pins**
* 3.3V logic level
* Reverse voltage protection and ESD protection
* Configurable as input or output

**Electrical limits:**

* Total combined current must not exceed **50mA**

> For more information look at the [library example](https://github.com/luxonis/rp2040_u2if/blob/main/examples/ControllerBox/example_controller_box_gpio_irq.py).

### Power Relays

* **4× SPDT latching relays**
* Up to **16A current**
* Maximum **400VAC switching voltage**

> For more information look at the [strobe-relay example](./strobe-relay-example/) (library example [example](https://github.com/luxonis/rp2040_u2if/blob/main/examples/ControllerBox/example_controller_box_relay.py)).

### User Interface

* **3× physical buttons**
* **3× status LEDs**

> For more example look at the [simple-example](./simple-example/)

## Pinout

Device-level pinout is shown below:

![M8 Controller Box Schematics](media/schematics.png)



## Example Applications

The repository includes reference applications demonstrating typical usage.

### [simple-example](./simple-example/)

* Blinks LED 1
* Button 2 toggles LED 2


### [depthai-example](./depthai-example/)

* Based on Luxonis hand pose detection
* Turns on LED 1 when a hand is detected


### [can-example](./can-example/)

* Monitors button 1
* Sends CAN frame on press
* Uses Linux SocketCAN (`python-can`)

### [strobe-relay-example](./strobe-relay-example/)

* Detects barcodes using `pyzbar`
* On barcode detection it switches relay

## Notes

* GPIO and peripheral control is exposed via the **u2if (USB-to-interfaces) protocol**
* Example applications demonstrate recommended interaction patterns
* Library repository: [rp2040_u2if](https://github.com/luxonis/rp2040_u2if)


## Support

For integration support or early access features, contact support@luxonis.com
33 changes: 33 additions & 0 deletions m8-controller-box/can-example/.oakappignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Python virtual environments
venv/
.venv/

# Node.js
# ignore node_modules, it will be reinstalled in the container
node_modules/

# Multimedia files
media/

# Documentation
README.md

# VCS
.git/
.github/
.gitlab/

# The following files are ignored by default
# uncomment a line if you explicitly need it

# !*.oakapp

# Python
# !**/.mypy_cache/
# !**/.ruff_cache/

# IDE files
# !**/.idea
# !**/.vscode
# !**/.zed

52 changes: 52 additions & 0 deletions m8-controller-box/can-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# M8 CAN Transmission Example (Button Triggered)

This example demonstrates how to send data over the **CAN bus** from the M8 Controller Box by pressing a physical button.

The application runs inside a container directly on the device and uses `python-can` with the Linux SocketCAN interface.

## Functionality

This example performs the following actions:

- Monitors the **button 1**.
- When the button is pressed, a CAN frame is transmitted over the M8 CAN interface (`can0`).

This provides a minimal, practical reference for sending CAN messages from a containerized application running on the OAK4 device.

## CAN Interface Setup (Required)

Before running the application, the CAN interface must be configured on the target device where CAN messages should be transmitted.

Run the following commands on the device:

```bash
ip link set can0 type can bitrate 500000
ip link set can0 up
```

This:

- Configures the CAN bitrate to **500 kbps**
- Brings the `can0` interface online

The bitrate must match the rest of the CAN network.

## Monitoring CAN Traffic

To verify transmission, you can listen to CAN traffic on a Linux system using:

```bash
candump can0
```

When the **button 1** is pressed, a CAN frame will appear on the bus.

## Use Case

This example serves as a minimal reference implementation for:

- Sending CAN messages from the M8 Controller Box
- Integrating containerized applications with CAN-based systems
- Trigger-based CAN communication using GPIO inputs

It can be extended to transmit structured data, control messages, or sensor outputs over CAN.
3 changes: 3 additions & 0 deletions m8-controller-box/can-example/backend-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
echo "Starting Backend"
exec python3.12 -u /app/main.py
88 changes: 88 additions & 0 deletions m8-controller-box/can-example/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""
Controller Box CAN Button IRQ Example
------------------------------------

Demonstrates how to send a CAN frame using
ControllerBox button events without polling.
Uses GPIO interrupts instead of continuous polling.
"""

import time
import can
from luxonis_u2if import ControllerBox


# ------------------------------------------------------------
# Connect to ControllerBox device
# ------------------------------------------------------------

box = ControllerBox()


# ------------------------------------------------------------
# CAN configuration
# ------------------------------------------------------------

CAN_IFACE = "can0"
CAN_ID = 0x123
CAN_DATA = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]

bus = can.interface.Bus(channel=CAN_IFACE, interface="socketcan")


def send_can_frame():
"""
Send a predefined CAN frame.
"""
msg = can.Message(
arbitration_id=CAN_ID,
data=CAN_DATA,
is_extended_id=False,
)
bus.send(msg)
print(f"Sent CAN: {CAN_IFACE} id=0x{CAN_ID:X} data={CAN_DATA}")


# ------------------------------------------------------------
# Button callback
# ------------------------------------------------------------

BUTTON_INDEX = 1 # Button 1
button_pin = ControllerBox.BUTTON_PINS[BUTTON_INDEX - 1]

def button_cb(btn, state):
"""
Triggered on button press/release.

Parameters
----------
btn : int
Button index (1..3)
state : bool
True = pressed, False = released
"""
if btn == BUTTON_INDEX and state: # pressed
try:
send_can_frame()
except Exception as e:
print(f"[ERROR] Failed to send CAN frame: {e}")


# ------------------------------------------------------------
# Register callback
# ------------------------------------------------------------

box.set_btn_callback(button_cb)


print("ControllerBox ready")
print("Press Button 1 to send CAN frame")


# ------------------------------------------------------------
# Main loop
# ------------------------------------------------------------

# Nothing required here — button events handled via IRQ
while True:
time.sleep(1)
33 changes: 33 additions & 0 deletions m8-controller-box/can-example/oakapp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
identifier = "com.example.m8-controller-box.can-example"
entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"]
app_version = "1.0.2"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems strange that we start of with 1.0.2 version instead of 1.0.0 since this is an initial PR for this example

assign_frontend_port = true

prepare_container = [
{ type = "COPY", source = "./requirements.txt", target = "./requirements.txt" },
{ type = "RUN", command = "python3.12 -m pip install -r /app/requirements.txt --break-system-packages" },
{ type = "RUN", command = "bash -lc 'set -e; apt-get update && apt-get install -y libhidapi-hidraw0 libhidapi-libusb0'" },
]

build_steps = [
"mkdir -p /etc/service/backend",
"cp /app/backend-run.sh /etc/service/backend/run",
"chmod +x /etc/service/backend/run",
]

allowed_devices = [{ allow = true, access = "rwm" }]

additional_mounts = [
{ source = "/dev", target = "/dev", type = "devtmpfs", options = [
"mode=777",
] },
]

[base_image]
api_url = "https://registry-1.docker.io"
service = "registry.docker.io"
oauth_url = "https://auth.docker.io/token"
auth_type = "repository"
auth_name = "luxonis/oakapp-base"
image_name = "luxonis/oakapp-base"
image_tag = "1.2.6"
3 changes: 3 additions & 0 deletions m8-controller-box/can-example/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hidapi
python-can
git+https://github.com/luxonis/rp2040_u2if.git
33 changes: 33 additions & 0 deletions m8-controller-box/depthai-example/.oakappignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Python virtual environments
venv/
.venv/

# Node.js
# ignore node_modules, it will be reinstalled in the container
node_modules/

# Multimedia files
media/

# Documentation
README.md

# VCS
.git/
.github/
.gitlab/

# The following files are ignored by default
# uncomment a line if you explicitly need it

# !*.oakapp

# Python
# !**/.mypy_cache/
# !**/.ruff_cache/

# IDE files
# !**/.idea
# !**/.vscode
# !**/.zed

17 changes: 17 additions & 0 deletions m8-controller-box/depthai-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Hand Pose Example with M8 Controller LED Trigger

This project is based on the official Luxonis hand pose example:

https://github.com/luxonis/oak-examples/tree/main/neural-networks/pose-estimation/hand-pose

It demonstrates hand detection and hand landmark estimation using DepthAI.

## Added Functionality

In addition to the original example, this version integrates an **M8 Controller Box**.

When a hand is detected:

- **LED 1** on the M8 controller will turn on.

This allows simple hardware feedback triggered directly from hand detection events.
3 changes: 3 additions & 0 deletions m8-controller-box/depthai-example/backend-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
echo "Starting Backend"
exec python3.12 -u /app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
model: luxonis/mediapipe-hand-landmarker:224x224
platform: RVC2
Loading
Loading