commit 86fbf1670c9873091bb8de3c38a1019800563003 Author: Tropicananass Date: Sun May 25 18:14:25 2025 +0000 Initial commit diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..eaa8609 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.aarch64-unknown-linux-gnu] +linker = "/usr/bin/aarch64-linux-gnu-gcc" + +[target.armv7-unknown-linux-gnueabihf] +linker = "/usr/bin/arm-linux-gnueabihf-gcc" + +[build] +target = "armv7-unknown-linux-gnueabihf" diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..4311bfc --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,14 @@ +# Start from the official Rust dev container image for Debian 12 (bookworm) +FROM mcr.microsoft.com/devcontainers/rust:1-1-bookworm + +# Install ARM cross-compilation toolchain packages +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + gdb-multiarch \ + gcc-arm-linux-gnueabihf \ + g++-arm-linux-gnueabihf \ + binutils-arm-linux-gnueabihf && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN rustup target add armv7-unknown-linux-gnueabihf \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..499f06c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,41 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/rust +{ + "name": "lightsabre-devcontainer", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "dockerFile": "Dockerfile", + + // Use 'mounts' to make the cargo cache persistent in a Docker Volume. + "mounts": [ + { + "source": "devcontainer-cargo-cache-lightsabre", + "target": "/usr/local/cargo", + "type": "volume" + }, + "type=bind,source=/home/${localEnv:USER}/.ssh,target=/home/vscode/.ssh,readonly", + "type=bind,source=/home/${localEnv:USER}/.bashrc,target=/home/vscode/.bashrc,readonly", + "type=bind,source=/home/${localEnv:USER}/.bashrc.d,target=/home/vscode/.bashrc.d,readonly" + ], + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "rustc --version", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "streetsidesoftware.code-spell-checker", + "rust-lang.rust-analyzer", + "vadimcn.vscode-lldb"] + } + } + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8aa13ee --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Remote executable 'lightsabre_backend'", + "preLaunchTask": "rust: remote Rpi debug setup", + "targetCreateCommands": [ + "target create ${workspaceFolder}/target/armv7-unknown-linux-gnueabihf/debug/lightsabre_backend" + ], + "processCreateCommands": [ + "gdb-remote raspberrypi.local:17777" + ], + "env": { + "RUST_LOG": "debug" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bd9cba6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.target": "armv7-unknown-linux-gnueabihf" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..77273bd --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,38 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "rust: cargo ARM build (default)", + "type": "cargo", + "command": "build", + "args": ["--target", "armv7-unknown-linux-gnueabihf"], + "options": { + "cwd": "${workspaceFolder}", + "env": { + "CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER": "/usr/bin/arm-linux-gnueabihf-gcc", + "CARGO_BUILD_TARGET": "armv7-unknown-linux-gnueabihf" + } + }, + "problemMatcher": [ + "$rustc" + ], + "group": "build", + }, + { + "label": "rust: remote Rpi debug setup", + "type": "shell", + "command": "${workspaceFolder}/tools/remote_debug.sh", + "args": [ + "${workspaceFolder}", + "raspberrypi.local", + "17777" + ], + "group": "none", + "dependsOn": [ + "rust: cargo ARM build (default)", + ], + }, + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c6b5456 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,341 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "jiff" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "lightsabre_backend" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", + "rppal", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rppal" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ce3b019009cff02cb6b0e96e7cc2e5c5b90187dc1a490f8ef1521d0596b026" +dependencies = [ + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100755 index 0000000..0a637e4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "3" +members = ["lightsabre_backend"] diff --git a/README.md b/README.md new file mode 100755 index 0000000..4e63c65 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# LightSabre + +LightSabre is a project designed to control WS281x RGB LEDs using the Secondary Memory Interface (SMI) of a Raspberry Pi, tailored for scenographic and lighting applications. + +## Features + +- **WS281x LED Control:** High-performance driving of addressable RGB LEDs via the Raspberry Pi SMI. +- **ArtNet Support:** Receives color data over the ArtNet protocol, allowing the Raspberry Pi to function as a DMX controller. +- **Autonomous Mode:** Analyzes audio input from an I2S microphone, performing spectral decomposition to generate dynamic lighting effects. +- **Web Interface:** Local web server for configuring and tweaking autonomous behavior. +- **MIDI Integration:** Supports real-time control and parameter adjustment via MIDI controllers. + +## Technology Stack + +- **Backend:** Written in Rust for robust and efficient controller logic. Some low-level operations may be implemented in C or Assembly (to be determined) for optimal performance. +- **Frontend:** Built with Vue.js, providing a modern and responsive web interface for configuration and control. + +## Use Cases + +- Stage lighting and scenography +- Interactive art installations +- Music-synchronized light shows + +## Getting Started + +1. **Hardware Requirements** + - Raspberry Pi (any model with SMI support) + - WS281x-compatible RGB LED strip + - I2S microphone (for autonomous mode) + - Optional: MIDI controller + +2. **Software Setup** + - Install dependencies (see `requirements.txt`) + - Configure network for ArtNet or connect MIDI controller + - Access the web interface via your browser + +## Documentation + +- [ArtNet Protocol](https://art-net.org.uk/) +- [WS281x LEDs](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) +- [Raspberry Pi SMI](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html) +- [MIDI Protocol](https://www.midi.org/) + +## License + +MIT License + +--- + +*For detailed setup and usage instructions, see the [Wiki](./Wiki.md).* \ No newline at end of file diff --git a/lightsabre_backend/Cargo.toml b/lightsabre_backend/Cargo.toml new file mode 100644 index 0000000..f71f473 --- /dev/null +++ b/lightsabre_backend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "lightsabre_backend" +version = "0.1.0" +edition = "2024" + +[dependencies] +env_logger = "0.11.8" +log = "0.4.27" +rppal = "0.22.1" + +[features] +default = ["rpizero2", "16channel"] +rpizero2 = ["rpi3"] +rpizero = ["rpi"] +rpi4 = [] +rpi3 = [] +rpi2 = [] +rpi = [] +16channel = [] diff --git a/lightsabre_backend/src/devices/led_autoconvert.rs b/lightsabre_backend/src/devices/led_autoconvert.rs new file mode 100644 index 0000000..c06614a --- /dev/null +++ b/lightsabre_backend/src/devices/led_autoconvert.rs @@ -0,0 +1,160 @@ +#![allow(dead_code)] + +pub mod led { + // Constants + pub const TX_TEST: u8 = 0; + pub const LED_NBITS: usize = 24; + pub const LED_PREBITS: usize = 4; + pub const LED_POSTBITS: usize = 4; + pub const BIT_NPULSES: usize = 3; + + // Helper macros as functions + #[inline] + pub const fn led_dlen() -> usize { + LED_NBITS * BIT_NPULSES + } + + #[inline] + pub const fn led_tx_oset(n: usize) -> usize { + LED_PREBITS + (led_dlen() * n) + } + + #[inline] + pub const fn tx_buff_len(n: usize) -> usize { + led_tx_oset(n) + LED_POSTBITS + } + + // TXDATA_T: u8 or u16 depending on channel count + #[cfg(feature = "led_nchans_gt_8")] + pub type TxData = u16; + #[cfg(not(feature = "led_nchans_gt_8"))] + pub type TxData = u8; + + // Dummy types for external dependencies + pub struct MemMap; + pub struct VC_MEM; + pub struct DMA_CHAN_A; + + // Dummy global variables + pub static mut VC_MEM: Option = None; + pub static mut TXDATA: Option<*mut TxData> = None; + + // Buffer for TX data + pub static mut TX_BUFFER: [TxData; tx_buff_len(1)] = [0; tx_buff_len(1)]; + + pub fn swap_bytes(tx_buffer: &mut [u16]) { + for word in tx_buffer.iter_mut() { + *word = word.swap_bytes(); + } + } + + pub fn leddriver_setup() { + // Placeholder for setup logic + // videocore_setup, gpio_setup, smi_setup + + } + + pub fn leddriver_close() { + // Placeholder for close logic + // videocore_close, smi_close, gpio_close + } + + pub fn set_color(rgb: u32, index: usize, tx_buffer: &mut [TxData]) { + let mut msk = 0b1 << 23; // Start with the highest bit for the first color channel + let mut txd_index = led_tx_oset(index); + + for n in 0..LED_NBITS { + msk = match n { + 0 => 0x80_0000, + 8 => 0x8000, + 16 => 0x80, + _ => msk >> 1, + }; + tx_buffer[txd_index + 0] = TxData::MAX; + tx_buffer[txd_index + 1] = 0; + tx_buffer[txd_index + 2] = 0; + if (rgb & msk) != 0 { + tx_buffer[txd_index + 1] = TxData::MAX; + } + txd_index += BIT_NPULSES; + } + } + + pub fn rgb_txdata(rgbs: &[u32], index: usize, tx_buffer: &mut [TxData], led_nchans: usize) { + let mut msk = 0; + let mut txd_index = led_tx_oset(index); + + for n in 0..LED_NBITS { + msk = match n { + 0 => 0x800000, + 8 => 0x8000, + 16 => 0x80, + _ => msk >> 1, + }; + tx_buffer[txd_index + 0] = TxData::MAX; + tx_buffer[txd_index + 1] = 0; + tx_buffer[txd_index + 2] = 0; + for i in 0..led_nchans { + if (rgbs[i] & msk) != 0 { + tx_buffer[txd_index + 1] |= 1 << i; + } + } + txd_index += BIT_NPULSES; + } + } + + pub fn leddriver_refresh() { + // Placeholder for refresh logic + // swap_bytes, dma_active, memcpy, enable_dma, start_smi, usleep + } + + /// Convert HSV to packed RGB (GRB order) + pub fn color_hsv(hue: u16, sat: u8, val: u8) -> u32 { + let mut r: u8 = 0; + let mut g: u8 = 0; + let mut b: u8 = 0; + + let mut hue = ((hue as u32 * 1530 + 32768) / 65536) as u16; + + if hue < 510 { + b = 0; + if hue < 255 { + r = 255; + g = hue as u8; + } else { + r = (510 - hue) as u8; + g = 255; + } + } else if hue < 1020 { + r = 0; + if hue < 765 { + g = 255; + b = (hue - 510) as u8; + } else { + g = (1020 - hue) as u8; + b = 255; + } + } else if hue < 1530 { + g = 0; + if hue < 1275 { + r = (hue - 1020) as u8; + b = 255; + } else { + r = 255; + b = (1530 - hue) as u8; + } + } else { + r = 255; + g = 0; + b = 0; + } + + let v1 = 1 + val as u32; + let s1 = 1 + sat as u32; + let s2 = 255 - sat as u32; + + ((((((r as u32 * s1) >> 8) + s2) * v1) & 0xff00) << 8) + | (((((g as u32 * s1) >> 8) + s2) * v1) & 0xff00) + | (((((b as u32 * s1) >> 8) + s2) * v1) >> 8) + } +} \ No newline at end of file diff --git a/lightsabre_backend/src/devices/led_driver.rs b/lightsabre_backend/src/devices/led_driver.rs new file mode 100644 index 0000000..274296d --- /dev/null +++ b/lightsabre_backend/src/devices/led_driver.rs @@ -0,0 +1,59 @@ +// Constants +const LED_NBITS: usize = 24; // Number of data bits per LED +const LED_PREBITS: usize = 4; // Number of zero bits before LED data +const LED_POSTBITS: usize = 4; // Number of zero bits after LED data +const BIT_NPULSES: usize = 3; // Number of O/P pulses per LED bit + +#[cfg(feature = "rpi4")] // Timings for RPi v4 (1.5 GHz) +const SMI_TIMING: [u32; 4] = [10, 15, 30, 15]; // 400 ns cycle time +#[cfg(not(feature = "rpi4"))] // Timings for RPi v0-3 (1 GHz) +const SMI_TIMING: [u32; 4] = [10, 10, 20, 10]; // 400 ns cycle time + +// Use 16-bit data type for TxDataT +#[cfg(feature = "16channel")] +type TxDataT = u16; +// Use 8-bit data type for TxDataT +#[cfg(not(feature = "16channel"))] +type TxDataT = u8; + +// Helper macros as functions +#[inline] +const fn led_dlen() -> usize { + LED_NBITS * BIT_NPULSES +} + +#[inline] +const fn led_tx_oset(n: usize) -> usize { + LED_PREBITS + (led_dlen() * n) +} + +#[inline] +const fn tx_buff_len(n: usize) -> usize { + led_tx_oset(n) + LED_POSTBITS +} + +pub struct LedDriver { + // Placeholder for LED driver state + tx_buffer: [TxDataT; tx_buff_len(30)], +} + +impl LedDriver { + pub fn new() -> Result> { + // Initialize the LED driver + Ok(LedDriver { + tx_buffer: [TxDataT::default(); 4856], + }) + } + + pub fn set_color(&self, rgb: u32, index: usize) -> Result<(), Box> { + // Set the color for the LED at the specified index + // Placeholder for actual implementation + Ok(()) + } + + pub fn refresh(&self) -> Result<(), Box> { + // Refresh the LED states + // Placeholder for actual implementation + Ok(()) + } +} diff --git a/lightsabre_backend/src/devices/mod.rs b/lightsabre_backend/src/devices/mod.rs new file mode 100644 index 0000000..be63e16 --- /dev/null +++ b/lightsabre_backend/src/devices/mod.rs @@ -0,0 +1,6 @@ +pub mod led_driver; +pub mod selector; + +pub trait Device { + fn read(&self) -> Vec; +} diff --git a/lightsabre_backend/src/devices/selector.rs b/lightsabre_backend/src/devices/selector.rs new file mode 100644 index 0000000..089636a --- /dev/null +++ b/lightsabre_backend/src/devices/selector.rs @@ -0,0 +1,31 @@ +use rppal::gpio::{Gpio, InputPin}; + +use super::Device; +use rppal::gpio::Error; + +const SELECTOR_PINS: [u8; 4] = [27, 5, 6, 26]; // GPIO pin number for the LED + +pub struct Selector { + selector_pins: Vec, +} + +impl Selector { + pub fn new() -> Result { + let gpio = Gpio::new()?; + // Set up the GPIO pins for the selector, use pull-down resistors to ensure a known state when not pressed + let selector_pins: Vec = SELECTOR_PINS + .iter() + .map(|&pin| gpio.get(pin).unwrap().into_input_pulldown()) + .collect(); + Ok(Selector { selector_pins }) + } +} + +impl Device for Selector { + fn read(&self) -> Vec { + self.selector_pins + .iter() + .map(|pin| pin.read() == rppal::gpio::Level::High) + .collect() + } +} diff --git a/lightsabre_backend/src/main.rs b/lightsabre_backend/src/main.rs new file mode 100644 index 0000000..6b23abd --- /dev/null +++ b/lightsabre_backend/src/main.rs @@ -0,0 +1,26 @@ +use devices::Device; +use devices::led_driver::LedDriver; +use devices::selector::Selector; +use log::{debug, info}; +use rppal::system::DeviceInfo; + +mod devices; + +fn main() -> Result<(), Box> { + env_logger::init(); + info!("Executing on device: {}", DeviceInfo::new()?.model()); + + // Initialize GPIO + let selector = Selector::new()?; + let ledDriver = devices::led_driver::LedDriver::new()?; + debug!("Selector initialized."); + // Main loop + loop { + // Read the selector state + let selector_state = selector.read(); + debug!("Selector state: {:?}", selector_state); + + // Sleep for a short duration to avoid busy-waiting + std::thread::sleep(std::time::Duration::from_millis(100)); + } +} diff --git a/tools/remote_debug.sh b/tools/remote_debug.sh new file mode 100755 index 0000000..510554b --- /dev/null +++ b/tools/remote_debug.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +VSCODE_WS="$1" +SSH_REMOTE="$2" +GDBPORT="$3" + +APP="lightsabre_backend" +TARGET_ARCH="armv7-unknown-linux-gnueabihf" +BUILD_BIN_FILE="${VSCODE_WS}/target/${TARGET_ARCH}/debug/${APP}" +TARGET_USER="pi" +TARGET_BIN_FILE="/tmp/${APP}" +TARGET_CWD="/tmp" + +ssh "${TARGET_USER}@${SSH_REMOTE}" "killall lldb-server ${APP}" + +if ! rsync -avz "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then + # If rsync doesn't work, it may not be available on target. Fallback to trying SSH copy. + if ! scp "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then + exit 2 + fi +fi + +ssh -f "${TARGET_USER}@${SSH_REMOTE}" "sh -c 'cd ${TARGET_CWD}; RUST_LOG=debug nohup lldb-server g *:${GDBPORT} ${TARGET_BIN_FILE} > /dev/null 2>&1 &'" \ No newline at end of file