{"id":51018675,"url":"https://github.com/dattazigzag/ring_eye_sim","last_synced_at":"2026-06-21T14:01:39.355Z","repository":{"id":362298342,"uuid":"1253623468","full_name":"dattazigzag/ring_eye_sim","owner":"dattazigzag","description":"Artnet video/screen data sender to Neopixels for simulating and testing robot eye interactions and styles, live on Hardware ","archived":false,"fork":false,"pushed_at":"2026-06-03T14:47:26.000Z","size":8676,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-03T15:05:05.914Z","etag":null,"topics":["artnet-dmx","design-tools","esp32","neopixels","platform","platformio","process","teensy"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dattazigzag.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T16:46:46.000Z","updated_at":"2026-06-03T14:59:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dattazigzag/ring_eye_sim","commit_stats":null,"previous_names":["dattazigzag/ring_eye_sim"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/dattazigzag/ring_eye_sim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dattazigzag%2Fring_eye_sim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dattazigzag%2Fring_eye_sim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dattazigzag%2Fring_eye_sim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dattazigzag%2Fring_eye_sim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dattazigzag","download_url":"https://codeload.github.com/dattazigzag/ring_eye_sim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dattazigzag%2Fring_eye_sim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34610832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["artnet-dmx","design-tools","esp32","neopixels","platform","platformio","process","teensy"],"created_at":"2026-06-21T14:01:36.258Z","updated_at":"2026-06-21T14:01:39.347Z","avatar_url":"https://github.com/dattazigzag.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Ring Eye Sim · Art-Net Sender\n\n**Drive two NeoPixel \"eye\" rings from a video or a live screen region, sampled, color-corrected, and streamed as Art-Net DMX.**\n\n[![Export macOS (Apple Silicon)](https://github.com/dattazigzag/ring_eye_sim/actions/workflows/export-macos.yml/badge.svg)](https://github.com/dattazigzag/ring_eye_sim/actions/workflows/export-macos.yml)\n![Platform](https://img.shields.io/badge/platform-macOS%20Apple%20Silicon-black)\n![Processing](https://img.shields.io/badge/Processing-4.5.2-006699)\n[![License: LGPL](https://img.shields.io/badge/license-LGPL-blue)](LICENSE)\n\n\u003c/div\u003e\n\n---\n\n## What it is\n\nA Processing sketch that turns any video clip or a draggable live screen-capture \"lens\" into pixel data for **two side-by-side ring displays**: a **right \"eye\" (main)** and a **left \"eye\" (clone)**. Each eye overlays a parametric NeoPixel-style ring, samples the color under every LED, runs it through a gamma / brightness pipeline, and streams the result as **Art-Net DMX** on its **own universe** so two physical rings can be driven independently and in perfect sync.\n\nIt's a hardware-in-the-loop design tool: drop in an animation, see exactly what the ring will show, and send it straight to the lights or to the bundled software **tester** when the hardware isn't on the bench.\n\n\u003e Input is **either** a video file **or** a live screen region (mutually exclusive). Move / scale and play / pause are shared across both eyes; each eye can be flipped horizontally and/or vertically on its own.\n\n## How it works\n\n```mermaid\nflowchart LR\n    SRC[\"Video file / \u003cbr\u003escreen lens\"] --\u003e MH[\"MediaHandler:\u003cbr\u003esingle decode,shared frame\"]\n    MH --\u003e R[\"Right eye (main):\u003cbr\u003emirror to 480x480\"]\n    MH --\u003e L[\"Left eye (clone):\u003cbr\u003emirror to 480x480\"]\n    R --\u003e RS[\"Ring sample\u003cbr\u003e(per LED)\"]\n    L --\u003e LS[\"Ring sample\u003cbr\u003e(per LED)\"]\n    RS --\u003e CP[\"Color pipeline:\u003cbr\u003egamma + brightness\u003cbr\u003e(Sampler)\"]\n    LS --\u003e CP\n    CP --\u003e D0[\"Art-Net universe 0\"] --\u003e RX((\"right ring / tester\"))\n    CP --\u003e D1[\"Art-Net universe 1\"] --\u003e LX((\"left ring / tester\"))\n    CP -. \"layout via MQTT (retained)\" .-\u003e MQ[\"topic ring/config\"]\n    MQ -.-\u003e RX\n```\n\n**Key principles**\n\n- **Single decode, shared frame.** The video is decoded once; both eyes render the same frame, frame-perfect sync, half the work, one pipeline to babysit.\n- **Mirror is the only per-eye property.** H/V flip happens at draw time; sampling reads the framebuffer *after* the flip, so each ring follows its own mirror with no special-casing.\n- **Sampling = inscribed circle per cell.** Each LED averages the pixels under its disc, rotation-invariant and allocation-free.\n- **Dual-universe Art-Net.** Right = universe 0, left = universe 1 (shared subnet); broadcast or per-eye unicast.\n- **MQTT is a side-channel, not the data path.** Pixels go over Art-Net; only the layout (`N`, universe, subnet, retained) is published on `ring/config` so a preview receiver mirrors geometry live. No broker → Art-Net still runs.\n- **Screen-capture lens.** A transparent, resizable, always-on-top window grabs any desktop region into the same pipeline.\n\n\u003e **→ [Full pipeline deep-dive](PIPELINE.md)**: the two-container model, ring geometry, the sampling math, the DMX channel map, and the three receivers, walked through with diagrams.\n\n---\n\n## Run it\n\n### A · The released app (no Processing needed)\n\n**One-time machine setup.** Run this once per Mac, it installs a Java 17+ runtime and the optional mosquitto broker, and makes sure mosquitto isn't left running as a background service (the app manages its own on `:1883`):\n```bash\ncurl -fsSL https://raw.githubusercontent.com/dattazigzag/ring_eye_sim/main/setup.sh | bash\n```\nNeeds [Homebrew](https://brew.sh); if it's missing the script prints the official one-liner and stops.\n\n**Then, for each release:** download the latest zip from **[Releases](https://github.com/dattazigzag/ring_eye_sim/releases)**, unzip it, and from that folder:\n```bash\nxattr -dr com.apple.quarantine ring_eye_sim_artnet_sender.app    # clear the download quarantine (once per download)\nopen ring_eye_sim_artnet_sender.app                              # or just double-click the .app\n```\n\u003e [!Note]\n\u003e The `xattr` line is needed because the app is **self-signed, not Apple-notarized**, so macOS quarantines it on download. CI signs every build (see **Distributing** for why), but self-signing alone doesn't clear Gatekeeper.\n  \n\n\u003e [!Important]\n\u003e In the app, press **`A`** to enable Art-Net → click **Allow** on the **\"find devices on your local network\"** prompt. It is required as without it macOS silently blocks all DMX.\n\n\u003e [!Tip]\n\u003e screen-capture lens → grant **Screen Recording** (System Settings → Privacy \u0026 Security → Screen Recording) and relaunch.\n\n\u003e [!Warning]\n\u003e **Apple Silicon only**, no Rosetta. Java isn't bundled (keeps it native + small), so a Java 17+ runtime must be present. [setup.sh](setup.sh) installs it.\n\n---\n\n### Distributing (maintainer)\n\nmacOS 26 **silently blocks LAN/Art-Net for unsigned, and even ad-hoc-signed, apps**; only a *real* signing identity makes macOS offer the Local Network \"Allow\" prompt. I use a **self-signed** identity (`RingEyeSim Local`) stored as encrypted repo secrets, so **CI signs every build automatically**. _You just download from Releases, unzip and run `xattr ...` cmd on the app and grant permissions on the first run ..._\n\n**Secrets** (already configured; rotate only if the cert changes):\n- `MACOS_CERT_P12_BASE64`: base64 of the identity exported as `.p12`\n- `MACOS_CERT_PASSWORD`: that `.p12`'s passphrase\n\nTo regenerate: create a self-signed **Code Signing** identity named `RingEyeSim Local` (Keychain Access → Certificate Assistant, \"Let me override defaults\" → Extended Key Usage: Code Signing, **login** keychain), then:\n```bash\nsecurity export -k ~/Library/Keychains/login.keychain-db -t identities -f pkcs12 -o ringeyesim-ci.p12\nbase64 -i ringeyesim-ci.p12 | pbcopy   # -\u003e MACOS_CERT_P12_BASE64 ; set the password secret ; then rm the .p12\n```\n\n**What colleagues get:** because the cert is self-signed (not Apple-notarized), each Mac still clears Gatekeeper once (the `xattr` step in section A) and clicks **Allow** on the Local Network prompt **once**. That's the trade-off for skipping a paid Apple Developer ID.\n\n**Local fallback**: to sign a `workflow_dispatch` artifact (those don't create a Release) or a hand-built app, `ci/sign-release.sh \u003czip-or-app\u003e` signs with the same identity from your login keychain.\n\n### B · From source in Processing\n\n1. Install **Processing 4.5.2**.\n2. **Sketch → Import Library → Manage Libraries** → install **Video**, **ControlP5**, **artnet4j**, **MQTT** (Joël Gähwiler), and **Drop / SDrop**.\n3. Open `Processing/ring_eye_sim_artnet_sender/` and press **Run**.\n\n- **Renderer is P3D** (GPU). P3D windows don't accept drag-and-drop, so load a clip with the \u003ckbd\u003eO\u003c/kbd\u003e key / **OPEN VIDEO** button.\n- **macOS Screen Recording** permission is needed for the screen-capture lens, enable Processing under Privacy \u0026 Security → Screen Recording, then restart it.\n- **High-DPI:** runs at `pixelDensity(2)` for a crisp display (sampling is density-aware). If a GStreamer video freeze or perf drop appears, re-enable `pixelDensity(1)` in `settings()`, the known-good fallback.\n- **MQTT broker:** see below, the sketch auto-launches mosquitto if installed, else skips MQTT (Art-Net unaffected).\n\n---\n\n## Keyboard controls\n\n| Key | Action | \u0026nbsp; | Key | Action |\n|---|---|---|---|---|\n| \u003ckbd\u003eO\u003c/kbd\u003e | Open / load a video | | \u003ckbd\u003eD\u003c/kbd\u003e | Toggle screen-capture lens |\n| \u003ckbd\u003eSpace\u003c/kbd\u003e | Play / pause | | \u003ckbd\u003eG\u003c/kbd\u003e | Grid overlay |\n| \u003ckbd\u003e← → ↑ ↓\u003c/kbd\u003e | Move video (both eyes) | | \u003ckbd\u003eL\u003c/kbd\u003e | Cell labels |\n| \u003ckbd\u003e⌘↑\u003c/kbd\u003e / \u003ckbd\u003e⌘↓\u003c/kbd\u003e | Scale up / down | | \u003ckbd\u003eC\u003c/kbd\u003e | Sampled-color preview |\n| \u003ckbd\u003eR\u003c/kbd\u003e | Reset transform | | \u003ckbd\u003eA\u003c/kbd\u003e | Art-Net send on / off |\n| \u003ckbd\u003eM\u003c/kbd\u003e | Cycle color mode | | \u003ckbd\u003e[\u003c/kbd\u003e / \u003ckbd\u003e]\u003c/kbd\u003e | Brightness − / + |\n| \u003ckbd\u003eS\u003c/kbd\u003e | Save config | | \u003ckbd\u003e⌫\u003c/kbd\u003e | Clear video |\n\nPer-eye **flip H/V**, **universe**, and **IP**, plus color and Art-Net transport, live in the on-screen panel.\n\n---\n\n## MQTT broker (Optional)\n\nPixels travel over Art-Net; the preview tester also needs the ring **layout**, published over MQTT (topic `ring/config`, retained). Install a local broker:\n\n```bash\nbrew install mosquitto\n```\n\nYou don't start it manually: on launch the app **ensures a broker is running**: it reuses one already on `localhost:1883`, or spawns mosquitto itself and shuts down only a broker it started. With mosquitto absent, MQTT is skipped and **Art-Net is never affected**.\n\n## Preview tester (software receiver)\n\n`Processing/tools/tailored_dmx_receiver/` renders a NeoPixel-ring twin of the **main (right)** eye, the exact post-gamma colors the hardware would receive, reading pixels over Art-Net and layout over MQTT. Use it to validate output with no hardware on the bench.\n\n**Start order:** mosquitto → sender → tester.\n\n---\n\n## Hardware receivers\n\nTwo physical receivers consume the **same U0 / U1 Art-Net stream**, pick whichever suits the install. The sender never changes; the receiver decides how universes map to rings.\n\n- **Teensy 4.1: wired, both eyes on one board.** `microcontroller/ring_eye_sim_artnet_receiver_teensy41_pio/`, NativeEthernet on the custom 4-port PCB, driving **both rings from a single node** (default U0 → Port 1 / right, U1 → Port 2 / left), with an OLED that shows the node IP to point the sender at. Per-port universe map, static-or-DHCP bring-up, color order, and ring calibration all live in its `src/config.h`. → [Teensy receiver README](microcontroller/ring_eye_sim_artnet_receiver_teensy41_pio/README.md#configuration-srcconfigh)\n- **ESP32-C3: WiFi, one ring per board.** `microcontroller/ring_eye_sim_artnet_receiver_esp32c3/`, one universe per board (`universe = 0` or `1`), so you flash **two boards for two eyes**. WiFi credentials, fixed/DHCP IP, LED count and data pin are set in `config.h` (copied from `config.h.template`). → [ESP32-C3 receiver README](microcontroller/ring_eye_sim_artnet_receiver_esp32c3/README.md)\n\nThe software [preview tester](#preview-tester-software-receiver) above mirrors the right eye when no hardware is on the bench.\n\n---\n\n## Releasing (GitHub Actions)\n\nBuilds run on a GitHub-hosted **macOS Apple-Silicon** runner, no local export required.\n\n- **Cut a release**: push a version tag:\n  ```bash\n  git tag v1.0.0 \u0026\u0026 git push origin v1.0.0\n  ```\n  The workflow exports the app and attaches `ring_eye_sim-v1.0.0-macos-aarch64.zip` to a new **GitHub Release** (notes auto-generated).\n- **Dry run**: run **Export macOS (Apple Silicon)** from the **Actions** tab (`workflow_dispatch`): same zip as a downloadable **artifact**, no release created.\n\nIt pins Processing 4.5.2 (checksum-verified), pulls the Video library's Apple-Silicon GStreamer natives fresh, adds the vendored libraries in `ci/libraries/`, exports `--no-java --variant=macos-aarch64`, patches the bundle's `Info.plist` (reverse-DNS id + `NSLocalNetworkUsageDescription`), embeds the app icon, and **code-signs** it with the `RingEyeSim Local` identity from repo secrets. See **Distributing** and `.github/workflows/export-macos.yml`.\n\n## Repo layout\n\n| Path | What |\n|---|---|\n| `Processing/ring_eye_sim_artnet_sender/` | the sender app (sketch source) |\n| `Processing/tools/tailored_dmx_receiver/` | software preview tester |\n| `microcontroller/ring_eye_sim_artnet_receiver_teensy41_pio/` | hardware receiver, **Teensy 4.1**, wired, both eyes on one 4-port board (U0→Port 1, U1→Port 2) |\n| `microcontroller/ring_eye_sim_artnet_receiver_esp32c3/` | hardware receiver, **ESP32-C3**, WiFi, one ring per board (one universe each; two boards = two eyes) |\n| `ci/libraries/` | Processing libraries vendored for CI |\n| `ci/sign-release.sh` | sign a release locally before distributing (macOS) |\n| `setup.sh` | one-time end-user machine prep (`curl … \\| bash`): Java 17+ (latest LTS) + mosquitto via Homebrew |\n| `.github/workflows/export-macos.yml` | export + release pipeline |\n\n## License\n\n[GNU Lesser General Public License](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdattazigzag%2Fring_eye_sim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdattazigzag%2Fring_eye_sim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdattazigzag%2Fring_eye_sim/lists"}