-
Notifications
You must be signed in to change notification settings - Fork 392
Added m8-controller-box dir with examples #826
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
base: main
Are you sure you want to change the base?
Changes from all commits
dfdd1b1
1aeab25
de4830a
8f4ce81
767ae68
a33004f
66faf3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
|
||
|  | ||
|
|
||
|
|
||
|
|
||
| ## 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 |
| 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 | ||
|
|
| 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. |
| 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,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) |
| 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" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems strange that we start of with |
||
| 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" | ||
| 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 |
| 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 | ||
|
|
| 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. |
| 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 |
There was a problem hiding this comment.
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.