| … | … | @@ -0,0 +1,67 @@ |
| 1 | # Arduino/ | |
| 2 | ||
| 3 | Atmel-based Arduino firmware (Mega, Uno, Nano). Most predate the ESP32 | |
| 4 | sketches in `../esp32/` and `../esp324xlight/`. Many sketches require | |
| 5 | built-in Arduino libraries for PWM and networking, plus the bundled | |
| 6 | modules in `libraries/` here. | |
| 7 | ||
| 8 | ## Sketch index | |
| 9 | ||
| 10 | ### Light controllers | |
| 11 | ||
| 12 | | Sketch | Purpose | Status | | |
| 13 | |---|---|---| | |
| 14 | | [`LightArm/`](LightArm/) | Mega-based spotlight robot firmware with Dynamixel servos + 100 W LED. Listens on TCP/1337. See [`../LightArm/readme`](../LightArm/readme) for the full subproject docs. | Active | | |
| 15 | | [`LEDController/`](LEDController/) | Ethernet-enabled high-resolution LED controller — pre-ESP32 path. | Reference | | |
| 16 | | [`DMX_LED_Controller/`](DMX_LED_Controller/) | DMX-driven LED controller | Reference | | |
| 17 | | [`DMX_LED_Controller2/`](DMX_LED_Controller2/) | Revision 2 of DMX LED controller | Reference | | |
| 18 | | [`NanoLEDController/`](NanoLEDController/) | Arduino Nano variant of LED controller | Reference | | |
| 19 | | [`RGB_LEDController/`](RGB_LEDController/) | RGB-specific LED controller | Reference | | |
| 20 | ||
| 21 | ### Motion / servo | |
| 22 | ||
| 23 | | Sketch | Purpose | | |
| 24 | |---|---| | |
| 25 | | [`Blenderbot/`](Blenderbot/) | Blender-driven robot (likely earlier ghettopticon-style) | | |
| 26 | | [`PrinbooLimbs/`](PrinbooLimbs/) | "Prinboo" robot limb control | | |
| 27 | | [`ServoShieldKeyboard/`](ServoShieldKeyboard/) | Adafruit Servo Shield + keyboard input demo | | |
| 28 | | [`ServoShieldSerial/`](ServoShieldSerial/) | Adafruit Servo Shield + serial control | | |
| 29 | | [`servotest/`](servotest/) | Servo test sketch | | |
| 30 | | [`TestDynamixel/`](TestDynamixel/) | Dynamixel servo test | | |
| 31 | | [`test_dynamixel_torque/`](test_dynamixel_torque/) | Dynamixel torque test | | |
| 32 | | [`TestSteppers/`](TestSteppers/) | Stepper motor test | | |
| 33 | | [`sonartest/`](sonartest/) | Sonar sensor test | | |
| 34 | ||
| 35 | ### DMX / RDM | |
| 36 | ||
| 37 | | Sketch | Purpose | | |
| 38 | |---|---| | |
| 39 | | [`RDM_Slave/`](RDM_Slave/) | RDM slave (Remote Device Management for DMX) | | |
| 40 | | [`RDM_Slave_Eeprom/`](RDM_Slave_Eeprom/) | RDM slave with EEPROM storage | | |
| 41 | ||
| 42 | ### Misc | |
| 43 | ||
| 44 | | File | Purpose | | |
| 45 | |---|---| | |
| 46 | | [`CodeGenCommands.h`](CodeGenCommands.h) | Code-generated command list header | | |
| 47 | ||
| 48 | ## libraries/ | |
| 49 | ||
| 50 | The `libraries/` subdir contains library copies pinned to known-working | |
| 51 | versions for these sketches: | |
| 52 | ||
| 53 | - `Adafruit_PWM_Servo_Driver_Library/` — Adafruit PCA9685 PWM driver | |
| 54 | - `Bioloid-fixed/`, `Bioloid-fixed-UART0/` — Dynamixel/Bioloid driver, patched | |
| 55 | - `Conceptinetics/` — DMX library | |
| 56 | - More | |
| 57 | ||
| 58 | When building these sketches, point the Arduino IDE's sketchbook | |
| 59 | location at this directory (or symlink) so it finds these specific | |
| 60 | versions. The main `~/Arduino/libraries/` may have newer versions that | |
| 61 | break these older sketches. | |
| 62 | ||
| 63 | ## See also | |
| 64 | ||
| 65 | - [`../LightArm/readme`](../LightArm/readme) — full LightArm subproject docs | |
| 66 | - [`../esp32/README.md`](../esp32/README.md) — newer ESP32-based sketches | |
| 67 | - [`../esp324xlight/README.md`](../esp324xlight/README.md) — current production firmware |
| … | … | @@ -0,0 +1,249 @@ |
| 1 | # cthuloid hardware notes | |
| 2 | ||
| 3 | Pin assignments, electrical conventions, and gotchas across the hardware | |
| 4 | in this repo. Two distinct hardware paths are documented here: | |
| 5 | ||
| 6 | 1. **Studiolight** — 4-light × RGB rig driven by ESP32 + MOSFETs | |
| 7 | 2. **LightArm** — Mega-based spotlight robot with Dynamixel servos and a high-current LED | |
| 8 | ||
| 9 | (Plus a brief note on the `ghettopticon` cardboard robot and the historical | |
| 10 | ESP8266 rigs.) | |
| 11 | ||
| 12 | --- | |
| 13 | ||
| 14 | ## Studiolight (current production rig) | |
| 15 | ||
| 16 | ### Controller | |
| 17 | ||
| 18 | | Item | Value | | |
| 19 | |---|---| | |
| 20 | | Board | ESP32 Wrover Module | | |
| 21 | | MCU | ESP32-D0WDQ6, dual-core 240 MHz, with hardware LEDC peripheral | | |
| 22 | | PSRAM | Not used (the daemon's PSRAM probe always fails on the boards we have, harmless) | | |
| 23 | | USB-UART | CP2102 (Silicon Labs) — `/dev/ttyUSBn` on Linux | | |
| 24 | | Power input to MCU | USB 5 V *or* board's 5 V rail (don't have both at once — back-feeds the regulator) | | |
| 25 | ||
| 26 | ### Firmware build target | |
| 27 | ||
| 28 | The board is selected as **"ESP32 Wrover Module"** in the Arduino IDE. | |
| 29 | Full fqbn: | |
| 30 | ||
| 31 | ``` | |
| 32 | esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none | |
| 33 | ``` | |
| 34 | ||
| 35 | The Wrover board profile pins PSRAM on (`-DBOARD_HAS_PSRAM`), but the | |
| 36 | PSRAM chip on our boards isn't present/accessible — at boot you'll see: | |
| 37 | ||
| 38 | ``` | |
| 39 | E (312) quad_psram: PSRAM ID read error: 0xffffffff, PSRAM chip not found or not supported | |
| 40 | ``` | |
| 41 | ||
| 42 | This is harmless; the rest of boot proceeds. The Wrover profile is still | |
| 43 | the correct pick because of its flash size / partition layout. | |
| 44 | ||
| 45 | ### PWM pin map (12 channels = 4 lights × RGB) | |
| 46 | ||
| 47 | The 12 GPIO pins are grouped into RGB triples, sent over the wire in | |
| 48 | **BGR** order: | |
| 49 | ||
| 50 | | Light | GPIO position 0 (Blue) | GPIO position 1 (Green) | GPIO position 2 (Red) | | |
| 51 | |---|---|---|---| | |
| 52 | | 1 | **32** | **33** | **25** | | |
| 53 | | 2 | **26** | **27** | **14** | | |
| 54 | | 3 | **12** | **13** | **2** | | |
| 55 | | 4 | **23** | **22** | **21** | | |
| 56 | ||
| 57 | Defined in firmware as: | |
| 58 | ||
| 59 | ```c | |
| 60 | const int PWMPins[] = {32, 33, 25, 26, 27, 14, 12, 13, 2, 23, 22, 21}; | |
| 61 | ``` | |
| 62 | ||
| 63 | ### ESP32 strapping-pin caveats | |
| 64 | ||
| 65 | Three of the GPIOs used are strapping pins that read special values at boot: | |
| 66 | ||
| 67 | - **GPIO 2** — connected to onboard LED on some dev boards. Used for light 3 red here. Must be low (or floating) at boot. | |
| 68 | - **GPIO 12** — strapping pin for flash voltage. **Must be low at boot** or the chip refuses to start. If your light 3 green channel has any external pull-up, the board will brick at boot. Make sure the MOSFET gate has a strong pull-down resistor that wins at power-on. | |
| 69 | - **GPIO 15** — not in our pin list, no issue. | |
| 70 | ||
| 71 | If a board mysteriously fails to boot after wiring it into the rig, GPIO | |
| 72 | 12 is the first suspect. Disconnect the rig from the ESP32 (just the | |
| 73 | GPIO 12 line) and try again to confirm. | |
| 74 | ||
| 75 | ### PWM electrical chain | |
| 76 | ||
| 77 | ``` | |
| 78 | ESP32 GPIO (3.3 V logic) | |
| 79 | │ | |
| 80 | └─→ MOSFET gate (per channel) | |
| 81 | │ | |
| 82 | └─→ LED ←── 600 W power supply | |
| 83 | ``` | |
| 84 | ||
| 85 | #### Power supply | |
| 86 | ||
| 87 | - 600 W bench/server PSU | |
| 88 | - Rail voltage chosen to match each LED string: | |
| 89 | - Blue/green LED strings → ~32 V rail | |
| 90 | - Red LED strings → ~26 V rail (lower forward voltage) | |
| 91 | - Common ground tied to ESP32 ground | |
| 92 | ||
| 93 | #### MOSFETs | |
| 94 | ||
| 95 | Currently using one MOSFET per channel (12 total), driven directly from | |
| 96 | the ESP32 GPIO. **This is the known weakest link in the rig** — see the | |
| 97 | "Driver bandwidth" section below. | |
| 98 | ||
| 99 | ### Known LED-side conventions | |
| 100 | ||
| 101 | - **LEDs are wired active-low** (common-anode through the MOSFET). GPIO HIGH = LED off; GPIO LOW = LED on. | |
| 102 | - The firmware compensates with `#define INVERT_HIGH_AND_LOW`, which inverts the PWM duty before writing to LEDC. The user-facing protocol then has the intuitive convention: input `0` = off, input `65534` = full on. | |
| 103 | - **Red diodes are voltage-limited.** With the 600 W supply rail set for blue/green, the red diodes can only safely be driven to ~31% duty before exceeding their voltage rating. Firmware enforces this via `#define RED_MAX_PWM 45000`: any value above 45000 in a red position-2 channel is rejected at parse. The Blender controller respects a tighter `LIGHT_PWM_RED_MAX = 30000` as the normal-operation max; 45000 is the hardware safety redline. | |
| 104 | ||
| 105 | ### Driver bandwidth — the dominant electrical caveat | |
| 106 | ||
| 107 | ESP32 GPIO sources only ~20–40 mA. High-power MOSFET gates have several | |
| 108 | nF of capacitance, so direct-drive transitions take 1–10 µs. | |
| 109 | ||
| 110 | Empirical bandwidth ceiling on the current rig (ESP32 GPIO direct-drive, | |
| 111 | no gate-driver IC): | |
| 112 | ||
| 113 | | Freq | Brightness | Notes | | |
| 114 | |---|---|---| | |
| 115 | | ≤ 1000 Hz | Full | Sweet spot. Use this. | | |
| 116 | | 1200 Hz | Just-perceptible dimming | First hint of slew limit | | |
| 117 | | 2000 Hz | Clearly dimmer | Driver only reaches ~85–90% target | | |
| 118 | | 5000 Hz | Noticeable drop | Driver topology bandwidth limit | | |
| 119 | | 20000 Hz | Red nearly off, all dim | Way past bandwidth — output is averaged ripple | | |
| 120 | ||
| 121 | Red drops out first because the lower-voltage red string carries higher | |
| 122 | current, amplifying gate-transition `I²R` losses and `di/dt` interactions | |
| 123 | with parasitic wire inductance. | |
| 124 | ||
| 125 | **This is a hardware ceiling, not a firmware bug.** No software fix | |
| 126 | recovers full brightness at 20 kHz — the energy literally isn't getting | |
| 127 | to the LED through an underdriven MOSFET gate. | |
| 128 | ||
| 129 | ### Recommended upgrade path (if you need >1 kHz) | |
| 130 | ||
| 131 | Add a dedicated MOSFET gate driver IC between each ESP32 GPIO and the | |
| 132 | MOSFET: | |
| 133 | ||
| 134 | | Part | Drive | Cost | Notes | | |
| 135 | |---|---|---|---| | |
| 136 | | **TC4427** | 1.5 A push-pull | ~$1 | Two channels per chip; very common | | |
| 137 | | **UCC27517** | 4 A | ~$1.50 | Single channel; faster | | |
| 138 | | **MIC4422** | 9 A | ~$2 | Overkill for this rig but options exist | | |
| 139 | ||
| 140 | All of these level-shift 3.3 V logic to 12 V (or whatever rail you | |
| 141 | supply) gate drive and switch in tens of nanoseconds. Add a 10–100 Ω | |
| 142 | gate resistor to damp ringing. | |
| 143 | ||
| 144 | With gate drivers in the loop, the bandwidth ceiling moves from ~1.2 kHz | |
| 145 | into the tens of kHz, unlocking 20 kHz / 11-bit camera-safe operation. | |
| 146 | ||
| 147 | ### Camera-banding considerations | |
| 148 | ||
| 149 | Used at 1 kHz, the PWM rate is below most camera shutter speeds. Banding | |
| 150 | is visible at ~1/2000 shutter and faster. For a Nikon D7000: | |
| 151 | ||
| 152 | | Shutter | Risk at 1 kHz | Risk at 5 kHz | Risk at 20 kHz | | |
| 153 | |---|---|---|---| | |
| 154 | | 1/60 | None | None | None | | |
| 155 | | 1/250 | None | None | None | | |
| 156 | | 1/1000 | Slight | None | None | | |
| 157 | | 1/2000 | Yes | Slight | None | | |
| 158 | | 1/4000 | Yes | Yes | Slight | | |
| 159 | ||
| 160 | If you're shooting video or fast stills, you want gate drivers in the | |
| 161 | chain so you can push to 20 kHz cleanly. If you're shooting normal-shutter | |
| 162 | stills only, 1 kHz / 16-bit (current state) is fine. | |
| 163 | ||
| 164 | --- | |
| 165 | ||
| 166 | ## LightArm (Mega + Dynamixel spotlight robot) | |
| 167 | ||
| 168 | Documented in [`LightArm/readme`](LightArm/readme). Summary: | |
| 169 | ||
| 170 | ### Controller | |
| 171 | ||
| 172 | | Item | Value | | |
| 173 | |---|---| | |
| 174 | | Board | Arduino Mega | | |
| 175 | | Network | Ethernet shield | | |
| 176 | | Default IPv4 | 10.0.2.255 (set in firmware EEPROM) | | |
| 177 | | TCP port | 1337 | | |
| 178 | ||
| 179 | ### Components | |
| 180 | ||
| 181 | - 2 × Dynamixel servos (USART) | |
| 182 | - 100 W LED with heatsink + fan | |
| 183 | - Custom LED driver board (schematic: `LightArm/LED MOSFET board.fzz`) | |
| 184 | - 12 V supply + step-up converter to 33 V for the LED | |
| 185 | - The same `pwm`/`plevel`/`speed`/`p`/`r` command protocol family as studiolight (defined in `Arduino/LightArm/LightArm.ino`) | |
| 186 | ||
| 187 | ### Mac address derivation | |
| 188 | ||
| 189 | MAC address is generated from the IP address — change the IP via the `ip` | |
| 190 | command and the MAC follows. DHCP isn't supported. | |
| 191 | ||
| 192 | --- | |
| 193 | ||
| 194 | ## Ghettopticon (cardboard wheelchair robot) | |
| 195 | ||
| 196 | Documented in [`ghettopticon/README`](ghettopticon/README). Summary: | |
| 197 | ||
| 198 | - Arduino + Adafruit Servo Shield drives up to 9 servos | |
| 199 | - Python (from Blender or standalone scripts) talks to Arduino over **USB serial** (not network) | |
| 200 | - Protocol: control byte `0xFF` followed by N bytes (one per servo, range 0–180) | |
| 201 | - BUFSIZE in the Arduino firmware must match the number of servos used | |
| 202 | ||
| 203 | This subproject is electrically independent from the studiolight rig and | |
| 204 | the original README notes it should probably live in its own repo. | |
| 205 | ||
| 206 | --- | |
| 207 | ||
| 208 | ## ESP8266 historical rigs | |
| 209 | ||
| 210 | `esp8266/esp8266-frak` and `esp8266/esp8266-rgb` were earlier-generation | |
| 211 | single-light controllers. ESP8266 has fewer PWM channels and lower clock | |
| 212 | than ESP32, which is why the project moved to ESP32 + the 12-channel | |
| 213 | `32wifi12frak` (and then to `esp324xlight`). Kept for reference; | |
| 214 | unlikely to be actively maintained. | |
| 215 | ||
| 216 | --- | |
| 217 | ||
| 218 | ## Pcb/ — PCB and breakout schematics | |
| 219 | ||
| 220 | [`Pcb/`](Pcb/) contains Fritzing files (`.fzz`) and rendered images for | |
| 221 | the various boards we use. See [`Pcb/README.md`](Pcb/README.md) for the | |
| 222 | index. | |
| 223 | ||
| 224 | Highlights: | |
| 225 | - `12v5v-breakout.fzz` — dual-rail breakout | |
| 226 | - `esp32-breakout.fzz` (and `.1`, `.2`, `.3x` variants) — ESP32 carrier | |
| 227 | - `sm-msft*.fzz` — MOSFET driver boards for the studiolight rig | |
| 228 | - `together-test.fzz` — assembly test | |
| 229 | ||
| 230 | --- | |
| 231 | ||
| 232 | ## Power-on checklist | |
| 233 | ||
| 234 | For a fresh studiolight unit: | |
| 235 | ||
| 236 | 1. **GPIO 12 pull-down verified** — measure between GPIO 12 and ground | |
| 237 | on the carrier. Should be < 1 kΩ pull-down. If not, the ESP32 won't boot. | |
| 238 | 2. **Common ground** between ESP32 and 600 W PSU. | |
| 239 | 3. **USB and 5 V rail not both connected.** One or the other. | |
| 240 | 4. **MOSFET gates pulled down** (gate resistor or driver IC) so all 12 | |
| 241 | channels are OFF on power-on (not floating). | |
| 242 | 5. Power on the 600 W PSU and the ESP32 USB (or 5 V). | |
| 243 | 6. Wait for the boot fade self-test (10 s off → on → off across all 12 | |
| 244 | channels). If you see this, the chip is alive, PWM is configured, and | |
| 245 | all 12 GPIOs are at least functional. | |
| 246 | 7. From a dev box: `ping studiolight.local` (or the unit's IP) — should | |
| 247 | respond. | |
| 248 | 8. `telnet studiolight.local 1337` — should print `<hostname> ready`. | |
| 249 | 9. Send `pwm 65534 65534 30000 65534 65534 30000 65534 65534 30000 65534 65534 30000` for the warm-white reference. |
| … | … | @@ -0,0 +1,304 @@ |
| 1 | # cthuloid wire protocol | |
| 2 | ||
| 3 | Plain-text, line-based, ASCII commands over TCP. Used by: | |
| 4 | ||
| 5 | - **studiolight units** (4-light × RGB rigs) running [`esp324xlight`](esp324xlight/) (modern) or [`esp32/32wifi12frak`](esp32/) (legacy) — port **1337** | |
| 6 | - **LightArm robots** (spotlight arms with Dynamixel servos) running [`Arduino/LightArm`](Arduino/) — port **1337** | |
| 7 | ||
| 8 | The studiolight and LightArm command sets overlap heavily (both inherit | |
| 9 | PWM + plevel + freq commands) but diverge where the underlying hardware | |
| 10 | differs (LightArm adds servo commands; studiolight adds gamma, OTA-related | |
| 11 | commands). | |
| 12 | ||
| 13 | This document covers the **studiolight protocol** as implemented by | |
| 14 | `esp324xlight`. For LightArm-specific commands see | |
| 15 | [`LightArm/readme`](LightArm/readme) and | |
| 16 | [`Arduino/LightArm/LightArm.ino`](Arduino/). | |
| 17 | ||
| 18 | ## Connection | |
| 19 | ||
| 20 | ```bash | |
| 21 | telnet studiolight.local 1337 # mDNS, all units claim studiolight.local — disambiguate by IP if multiple | |
| 22 | telnet 192.168.50.154 1337 # exact IP | |
| 23 | ``` | |
| 24 | ||
| 25 | On connect the unit sends a one-line greeting: | |
| 26 | ||
| 27 | ``` | |
| 28 | <hostname> ready | |
| 29 | ``` | |
| 30 | ||
| 31 | Each command is terminated by `\n` (or `\r\n` — `\r` is tolerated). | |
| 32 | Whitespace between tokens is a single space. | |
| 33 | ||
| 34 | ## Channel value semantics | |
| 35 | ||
| 36 | Each PWM channel takes an integer 0..65535. With `INVERT_HIGH_AND_LOW` | |
| 37 | defined and an active-low LED driver, the mapping is: | |
| 38 | ||
| 39 | | Input | Output duty | LED state | | |
| 40 | |---|---|---| | |
| 41 | | `0` | 100% high (pin always HIGH) | **off** (active-low LED) | | |
| 42 | | `30000` | ~54% high | ~46% bright | | |
| 43 | | `45000` | ~31% high | ~69% bright — red's protective ceiling | | |
| 44 | | `65534` | ~0% high (pin always LOW) | **full on** | | |
| 45 | ||
| 46 | Higher value = brighter LED. **Red channels (position 2 of each RGB | |
| 47 | triple) are capped at 45000** to keep average voltage below the red | |
| 48 | diodes' ~26 V rating; values above that are rejected at parse time. | |
| 49 | ||
| 50 | ## Per-light channel order | |
| 51 | ||
| 52 | The 12 channels are grouped into **4 RGB triples** (one per light), and | |
| 53 | each triple is sent in **BGR order**: | |
| 54 | ||
| 55 | | Light | Pin pos 0 (Blue) | Pin pos 1 (Green) | Pin pos 2 (Red) | | |
| 56 | |---|---|---|---| | |
| 57 | | 1 | GPIO 32 | GPIO 33 | GPIO 25 | | |
| 58 | | 2 | GPIO 26 | GPIO 27 | GPIO 14 | | |
| 59 | | 3 | GPIO 12 | GPIO 13 | GPIO 2 | | |
| 60 | | 4 | GPIO 23 | GPIO 22 | GPIO 21 | | |
| 61 | ||
| 62 | The Blender-side controller in | |
| 63 | [`LightArm/blender/lightarm_panel.py`](LightArm/blender/) reads | |
| 64 | `obj.data.color[0]` (red), `[1]` (green), `[2]` (blue), and emits them as | |
| 65 | `pwm_b pwm_g pwm_r` — so the on-wire order matches the firmware's BGR | |
| 66 | position layout. | |
| 67 | ||
| 68 | ## Commands | |
| 69 | ||
| 70 | ### `pwm <v1> <v2> ... <vN>` | |
| 71 | ||
| 72 | Set channel values immediately. `N` ≤ 12. Values follow the BGR-per-light | |
| 73 | order above. Channels beyond `N` are left at their previous value. | |
| 74 | ||
| 75 | ``` | |
| 76 | pwm 0 0 0 0 0 0 0 0 0 0 0 0 # all 12 channels to off | |
| 77 | pwm 65534 65534 30000 65534 65534 30000 65534 65534 30000 65534 65534 30000 | |
| 78 | # all 4 lights to "warm white" reference | |
| 79 | pwm 65534 65534 0 # only light 1's red to full bright | |
| 80 | ``` | |
| 81 | ||
| 82 | Brief 3-step interpolation from previous → target with no inter-frame | |
| 83 | delay, so visually it snaps. | |
| 84 | ||
| 85 | **Range:** 0–65535 per channel (red position capped at 45000). | |
| 86 | **Response:** `OK pwm <count>` on success, `ERR pwm <reason>` on failure. | |
| 87 | ||
| 88 | ### `pwm2 <numPeriods> <periodMs> <v1> ... <vN>` | |
| 89 | ||
| 90 | Fade from each channel's current value to its target over | |
| 91 | `numPeriods × periodMs` milliseconds. | |
| 92 | ||
| 93 | | Arg | Range | Meaning | | |
| 94 | |---|---|---| | |
| 95 | | `numPeriods` | 1–1000 | number of interpolation steps | | |
| 96 | | `periodMs` | 1–1000 | milliseconds between steps | | |
| 97 | | `v1..vN` | 0–65535 | target value per channel (BGR order, ≤ 12) | | |
| 98 | ||
| 99 | Interpolation is **gamma-corrected** per-channel using the `gamma` setting. | |
| 100 | ||
| 101 | ``` | |
| 102 | pwm2 100 10 0 0 0 0 0 0 0 0 0 0 0 0 # 1 s fade to "off" on all 12 | |
| 103 | pwm2 1000 30 65534 65534 30000 ... (×4) # 30 s smooth fade to white reference | |
| 104 | pwm2 500 1 65534 65534 30000 ... (×4) # 0.5 s smooth fade (max smoothness) | |
| 105 | ``` | |
| 106 | ||
| 107 | For maximum smoothness over any duration: max out `numPeriods` (1000) and set | |
| 108 | `periodMs = total_ms / 1000`. Minimum honoured `periodMs` is 1 (the | |
| 109 | FreeRTOS tick floor). | |
| 110 | ||
| 111 | **Response:** `OK pwm2 <count>`. | |
| 112 | ||
| 113 | ### `freq [hz]` | |
| 114 | ||
| 115 | Get or set the PWM frequency for all 12 channels. | |
| 116 | ||
| 117 | With no argument: print current freq + auto-selected resolution. | |
| 118 | With one argument: set freq (range 1 to `maxfreq` = 40000) and reconfigure | |
| 119 | the LEDC peripheral. | |
| 120 | ||
| 121 | Resolution is auto-selected: the firmware picks the highest bit-depth | |
| 122 | (up to 16) that fits in the LEDC peripheral's 80 MHz clock at the | |
| 123 | requested freq. | |
| 124 | ||
| 125 | ``` | |
| 126 | freq # report current — e.g. "OK freq 1000 Hz at 16-bit (dutyMax=65535)" | |
| 127 | freq 1000 # 1 kHz; full 16-bit res | |
| 128 | freq 5000 # 5 kHz; auto-drops to 13-bit | |
| 129 | freq 20000 # 20 kHz; auto-drops to 11-bit | |
| 130 | ``` | |
| 131 | ||
| 132 | **Hardware caveat:** when ESP32 GPIO directly drives a MOSFET gate (no | |
| 133 | gate-driver IC), bandwidth limits cause LED brightness to droop at freqs | |
| 134 | above ~1.2 kHz. Sweet spot for the current rig: `freq 1000` (full bit-depth | |
| 135 | + full LED brightness). See [`HARDWARE.md`](HARDWARE.md) for details. | |
| 136 | ||
| 137 | ### `gamma [b] [g] [r]` | |
| 138 | ||
| 139 | Get or set per-channel gamma exponents used by `pwm2`'s perceptual | |
| 140 | interpolation. Three floats in **BGR order**, range 0.5–5.0. | |
| 141 | ||
| 142 | | Gamma | Behaviour | | |
| 143 | |---|---| | |
| 144 | | `1.0` | linear in input value (default, no perceptual correction) | | |
| 145 | | `1.5–1.8` | gentle perceptual correction | | |
| 146 | | `2.2` | full perceptual correction (slow start, fast end) | | |
| 147 | | `> 2.5` | aggressively slow this channel's ramp | | |
| 148 | ||
| 149 | Higher gamma slows that channel's *perceived* ramp during fades. Useful | |
| 150 | when red appears to lead green/blue because red LEDs are more | |
| 151 | luminous-per-duty. | |
| 152 | ||
| 153 | ``` | |
| 154 | gamma # report current | |
| 155 | gamma 1.0 1.0 1.5 # red slowed mildly | |
| 156 | gamma 1.2 1.2 1.8 # all corrected, red corrected most | |
| 157 | ``` | |
| 158 | ||
| 159 | **Response:** `gamma B=<b> G=<g> R=<r>`. | |
| 160 | ||
| 161 | ### `hostname [name]` | |
| 162 | ||
| 163 | Get or set the unit's mDNS / OTA / greeting hostname. Set is persisted to | |
| 164 | NVS and takes effect after a `reboot`. Valid: 1–63 ASCII chars | |
| 165 | (letters / digits / hyphens). | |
| 166 | ||
| 167 | ``` | |
| 168 | hostname # → "hostname: studiolight" | |
| 169 | hostname studiolight-stage # stored; reboot to activate | |
| 170 | reboot # apply now | |
| 171 | ``` | |
| 172 | ||
| 173 | ### `reboot` | |
| 174 | ||
| 175 | Soft-restart the chip via `ESP.restart()`. Useful after `hostname` change | |
| 176 | or to clear a stuck state. | |
| 177 | ||
| 178 | ``` | |
| 179 | reboot # → "Rebooting..." then connection drops | |
| 180 | ``` | |
| 181 | ||
| 182 | ### `verbose 0 | 1` | |
| 183 | ||
| 184 | Toggle the `GOT <cmd>` / `OK <cmd>` / `ERR <cmd>` markers that are echoed | |
| 185 | to telnet for every command. Default ON for debugging. Turn OFF when a | |
| 186 | tight-loop controller (like the Blender script) is driving the unit and | |
| 187 | doesn't want ack chatter. | |
| 188 | ||
| 189 | ``` | |
| 190 | verbose 0 # quiet for production | |
| 191 | verbose 1 # noisy for debugging | |
| 192 | ``` | |
| 193 | ||
| 194 | ### `plevel [level]` | |
| 195 | ||
| 196 | Set the verbosity of the underlying `PrintLevel`-tagged Serial output | |
| 197 | (printInfo / printAck / printError calls scattered through the code). | |
| 198 | Affects Serial, not telnet. Levels: | |
| 199 | ||
| 200 | | Level | Shows | | |
| 201 | |---|---| | |
| 202 | | `silent` | nothing | | |
| 203 | | `error` | errors only | | |
| 204 | | `ack` | + success confirmations | | |
| 205 | | `info` | + per-pin info lines (default) | | |
| 206 | ||
| 207 | ``` | |
| 208 | plevel ack # quieter Serial during fades | |
| 209 | ``` | |
| 210 | ||
| 211 | ### `s ...` | |
| 212 | ||
| 213 | Servo command. Reserved for future per-light tilt/pan servo control; | |
| 214 | currently a stub that just prints "Got Servo shit" to Serial. | |
| 215 | ||
| 216 | ### Unrecognized commands | |
| 217 | ||
| 218 | The dispatch's default handler emits: | |
| 219 | ||
| 220 | ``` | |
| 221 | ERR unrecognized: <whatever you typed> | |
| 222 | ``` | |
| 223 | ||
| 224 | ## Reference Blender integration | |
| 225 | ||
| 226 | The Blender add-on at `LightArm/blender/lightarm_panel.py` (or | |
| 227 | equivalent) registers a `frame_change_post` handler that walks scene | |
| 228 | objects whose names start with `bgr-<index>-<ip>` and emits one `pwm` | |
| 229 | command per IP per frame: | |
| 230 | ||
| 231 | ```python | |
| 232 | # in handler: | |
| 233 | ip = obj.name.split('-')[2] | |
| 234 | pwm_r = pwmFromLuminosity(obj.data.color[0], LIGHT_PWM_RED_MAX) # 0..30000 cap for red | |
| 235 | pwm_g = pwmFromLuminosity(obj.data.color[1]) # 0..65534 for G | |
| 236 | pwm_b = pwmFromLuminosity(obj.data.color[2]) # 0..65534 for B | |
| 237 | ss = f"{pwm_b} {pwm_g} {pwm_r} " # BGR order | |
| 238 | # aggregate 4 triples into one `pwm B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4` command | |
| 239 | sendString(ip, f"pwm {bgr_for_4_lights}\n") | |
| 240 | ``` | |
| 241 | ||
| 242 | Two practical conventions to know: | |
| 243 | ||
| 244 | - **Red is capped at 30000 client-side** even though the firmware would | |
| 245 | accept up to 45000. The 30000 is normal-operation max; 45000 is the | |
| 246 | hardware-safety redline. | |
| 247 | - **Same value re-sent is a no-op visually** because `pwm` snaps. To | |
| 248 | observe a change, send a different value or use `pwm2` (which | |
| 249 | interpolates from the current state). | |
| 250 | ||
| 251 | ## Reference colour points (empirical) | |
| 252 | ||
| 253 | For light 1 with the others off: | |
| 254 | ||
| 255 | | Command | Result | | |
| 256 | |---|---| | |
| 257 | | `pwm 65534 65534 25000 65534 65534 65534 65534 65534 65534 65534 65534 65534` | Neutral white from light 1 | | |
| 258 | | `pwm 65534 65534 20000 65534 65534 65534 65534 65534 65534 65534 65534 65534` | Cooler white | | |
| 259 | | `pwm 65534 65534 30000 65534 65534 65534 65534 65534 65534 65534 65534 65534` | Warmer white | | |
| 260 | ||
| 261 | ## Long-run stability | |
| 262 | ||
| 263 | The firmware handles the following automatically so a unit stays usable | |
| 264 | across days/weeks: | |
| 265 | ||
| 266 | - **WiFi reconnect** after AP drops (every 10 s health check, calls `WiFi.reconnect()`) | |
| 267 | - **Idle TCP timeout** drops stale telnet sessions after 5 min (works around the arduino-esp32 `connected()` bug that can keep dead sockets alive for tens of minutes) | |
| 268 | - **Old client `stop()`** on every new accept so a fresh telnet displaces a hung one | |
| 269 | - **`WiFi.setSleep(false)`** for persistent-server stability | |
| 270 | - **Watchdog feeding** during long fades (vTaskDelay yields properly) | |
| 271 | ||
| 272 | If a unit appears unresponsive: try a fresh `telnet`. If still dead, send | |
| 273 | `reboot` from another telnet. If that fails too, the unit is hung at a | |
| 274 | layer below `loop()` (rare). | |
| 275 | ||
| 276 | ## OTA firmware push | |
| 277 | ||
| 278 | Once a unit is on the network, push new firmware without USB: | |
| 279 | ||
| 280 | ```bash | |
| 281 | arduino-cli upload \ | |
| 282 | --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none \ | |
| 283 | --port studiolight.local \ | |
| 284 | --protocol network \ | |
| 285 | --upload-field password=studiolight-ota \ | |
| 286 | esp324xlight/esp324xlight.ino | |
| 287 | ``` | |
| 288 | ||
| 289 | Password is set in the firmware via `#define OTA_PASSWORD` (default | |
| 290 | `studiolight-ota`). During the ~5-second upload, the main loop pauses | |
| 291 | and incoming commands are ignored; the chip auto-reboots into the new | |
| 292 | firmware on completion. | |
| 293 | ||
| 294 | ## Diagnostic flow when commands "do nothing" | |
| 295 | ||
| 296 | 1. Can you `telnet studiolight.local 1337` and see the greeting? If no: | |
| 297 | network / mDNS issue. Try the IP directly. | |
| 298 | 2. After connecting, type a known-noop like `gamma`. Do you see | |
| 299 | `gamma B=1.00 G=1.00 R=1.00`? If no: command parser hung. `reboot`. | |
| 300 | 3. Send `pwm 65534 65534 65534 65534 65534 65534 65534 65534 65534 65534 65534 65534`. Do all | |
| 301 | LEDs go full off? If yes: protocol fine, your earlier command was | |
| 302 | probably a no-op (sending the same value that was already set). | |
| 303 | 4. If some channels are stuck regardless: likely hardware (MOSFET | |
| 304 | gate-drive issue, wiring break). See [`HARDWARE.md`](HARDWARE.md). |
| … | … | @@ -0,0 +1,71 @@ |
| 1 | # Pcb/ | |
| 2 | ||
| 3 | Fritzing schematic + PCB layout files for the various breakouts and | |
| 4 | driver boards used by the cthuloid hardware. All files are | |
| 5 | [Fritzing](https://fritzing.org/) `.fzz` format unless noted. | |
| 6 | ||
| 7 | ## File index | |
| 8 | ||
| 9 | ### Power breakouts | |
| 10 | ||
| 11 | | File | Purpose | | |
| 12 | |---|---| | |
| 13 | | `12v5v-breakout.fzz` | Dual-rail 12 V / 5 V breakout | | |
| 14 | ||
| 15 | ### ESP32 carriers | |
| 16 | ||
| 17 | | File | Purpose | | |
| 18 | |---|---| | |
| 19 | | `esp32-breakout.fzz` | Original ESP32 carrier board | | |
| 20 | | `esp32-breakout1.fzz`, `esp32-breakout (1).fzz` | Revision 1 | | |
| 21 | | `esp32-breakout2.fzz` | Revision 2 | | |
| 22 | | `esp32-breakout3x.fzz` | 3x variant (likely 3-channel) | | |
| 23 | | `esp32-breakout/` | Working dir / export for one of the above | | |
| 24 | | `breakout.fzz`, `breakout-export/`, `breakout_pcb.jpg` | Generic breakout (likely current studiolight carrier) | | |
| 25 | ||
| 26 | ### MOSFET driver boards (studiolight rig) | |
| 27 | ||
| 28 | | File | Purpose | | |
| 29 | |---|---| | |
| 30 | | `sm-msft1.6.fzz` | Single-MOSFET driver board, rev 1.6 | | |
| 31 | | `sm-msft1.6/` | Export dir for 1.6 | | |
| 32 | | `sm-msft1.6_pcb.jpg` | Rendered PCB image for 1.6 | | |
| 33 | | `sm-msft1.7.fzz` | Revision 1.7 | | |
| 34 | | `sm-msft1.7/` | Export dir for 1.7 | | |
| 35 | | `sm-msft3-0.1.fzz` | Revision 3.0 alpha | | |
| 36 | | `sm-msft3-0.1_pcb.jpg` | Rendered image for 3.0 alpha | | |
| 37 | | `sm-3xmsft.fzz` | 3-channel MOSFET driver board | | |
| 38 | ||
| 39 | These drive the 12 channels of the studiolight rig (one MOSFET per | |
| 40 | channel). The "sm-msft" boards are the per-channel driver boards used | |
| 41 | in the current rig. The "3xmsft" variant integrates three drivers on one | |
| 42 | board (one RGB triple). | |
| 43 | ||
| 44 | ### Misc | |
| 45 | ||
| 46 | | File | Purpose | | |
| 47 | |---|---| | |
| 48 | | `mb-test.fzz` | Test board | | |
| 49 | | `together-test.fzz`, `together-test/` | Assembly test (probably combines breakout + MOSFET boards) | | |
| 50 | ||
| 51 | ## Hardware notes | |
| 52 | ||
| 53 | The current MOSFET driver boards drive the gates **directly from ESP32 | |
| 54 | GPIO** (no dedicated gate-driver IC). This works at ≤1 kHz PWM but the | |
| 55 | output droops at higher freqs due to slow gate-charge transitions. See | |
| 56 | [`../HARDWARE.md`](../HARDWARE.md#driver-bandwidth--the-dominant-electrical-caveat) | |
| 57 | for the bandwidth analysis and the recommended gate-driver upgrade path. | |
| 58 | ||
| 59 | The MOSFETs used in the field are likely common N-channel logic-level | |
| 60 | parts (e.g., IRLZ44N, IRF3205, AOD508). Exact part numbers should be | |
| 61 | read off the boards or from the Fritzing files. A future PCB revision | |
| 62 | should add a TC4427-class gate driver between the ESP32 and the | |
| 63 | MOSFET to unlock higher-freq camera-safe operation. | |
| 64 | ||
| 65 | ## Opening the files | |
| 66 | ||
| 67 | [Fritzing](https://fritzing.org/) is the original tool. KiCad can import | |
| 68 | Fritzing files via plugins but the schematic capture may need cleanup. | |
| 69 | ||
| 70 | Rendered PCB images (`*_pcb.jpg`) give a quick visual reference without | |
| 71 | needing Fritzing installed. |
| … | … | @@ -1,18 +1,117 @@ |
| 1 | 1 | # cthuloid |
| 2 | 2 | |
| 3 | Cthuloid is an experimental autonomous theater umbrella project by Circus Automatic. | |
| 4 | ||
| 5 | build a theater system that integrates robotics and performance into a hackable physical space where the performer can dictate lighting design to follow their every movement. Future plans include sound integration, shape changing scenery and more. | |
| 6 | ||
| 7 | Inspired largely by video game level design, it will use blender and other programs for its internal representation. low cost electronics / sensors will be crafted for its real world implementation. a big goal is to form the feedback loop which will allow for more dynamic performance possibilities. | |
| 8 | --- | |
| 9 | ||
| 10 | Folders of note: | |
| 11 | - ### LightArm | |
| 12 | *-ethernet-enabled spotlight robot project* | |
| 13 | - ### Arduino/LightArm | |
| 14 | *-firware for the spotlight robot* | |
| 15 | - ### Arduino/LEDController | |
| 16 | *-firmware for ethernet-enabled controller for LEDs at high resolution* | |
| 17 | - ### ghettopticon | |
| 18 | *-humanoid robot mounted on a motorized wheelchair (should be moved to its own repo)* | |
| 3 | Cthuloid is an experimental autonomous theatre umbrella project by Circus Automatic. | |
| 4 | ||
| 5 | The goal: a theatre system that integrates robotics and performance into a | |
| 6 | hackable physical space where the performer can dictate lighting design to | |
| 7 | follow their every movement. Future plans include sound integration, | |
| 8 | shape-changing scenery, and richer feedback loops between performer and stage. | |
| 9 | ||
| 10 | Inspired largely by video-game level design — Blender is used as the | |
| 11 | modelling and simulation environment, and low-cost electronics/sensors are | |
| 12 | crafted for the real-world implementation. | |
| 13 | ||
| 14 | ## Repository map | |
| 15 | ||
| 16 | | Folder | What lives here | Status | | |
| 17 | |---|---|---| | |
| 18 | | [`esp324xlight/`](esp324xlight/) | **Modern** 4-light × RGB controller firmware for ESP32 (Arduino core 3.x). Auto-resolution PWM, gamma-corrected fades, OTA, NVS-persisted hostname, TCP stale-client mitigations, runtime command surface. **Current production firmware** for studiolight units. | Active | | |
| 19 | | [`esp32/`](esp32/) | Older ESP32 sketches including the legacy `32wifi12frak` that `esp324xlight` evolved from, plus FastLED/Artnet variants, color tests, and a rotary-encoder lightswitch POC. | Mixed — `32wifi12frak` still works against Arduino core 2.0.17; others are experimental | | |
| 20 | | [`esp8266/`](esp8266/) | ESP8266 sketches (`esp8266-frak`, `esp8266-rgb`) — earlier-generation single-light controllers before the ESP32 12-channel design. | Historical | | |
| 21 | | [`Arduino/`](Arduino/) | Older Atmel-based Arduino firmware: `LightArm` (Mega-based spotlight robot), `LEDController`, `DMX_LED_Controller`, `Blenderbot`, `PrinbooLimbs`, `RDM_Slave`, ServoShield demos, etc. | Mixed | | |
| 22 | | [`LightArm/`](LightArm/) | Ethernet-enabled spotlight-robot project. Contains the Android app, Blender models + Python bridge, and standalone scripts. Firmware lives in `Arduino/LightArm/`. | Active | | |
| 23 | | [`cuemgr/`](cuemgr/) | Console-based cueing program for running theatre productions. Speaks both to LightArm robots (TCP) and DMX fixtures (via OLA). | Active | | |
| 24 | | [`ghettopticon/`](ghettopticon/) | Cardboard humanoid robot mounted on a motorized wheelchair, driven by Adafruit Servo Shield + Python from Blender. The original README notes this should probably move to its own repo. | Active (separate concern) | | |
| 25 | | [`Pcb/`](Pcb/) | Fritzing schematic + PCB files for the various breakout boards (12 V / 5 V breakout, ESP32 breakouts, MOSFET driver boards). | Reference | | |
| 26 | | [`rgb-strip.py`](rgb-strip.py) | Standalone Python script (top-level) for testing RGB strip behaviour over the network protocol. | Utility | | |
| 27 | | [`chrisclean.sh`](chrisclean.sh) | Maintenance script. | Utility | | |
| 28 | ||
| 29 | ## Hardware stack at a glance | |
| 30 | ||
| 31 | The current 4-light rig (driven by `esp324xlight`): | |
| 32 | ||
| 33 | - **Controller**: ESP32 Wrover Module | |
| 34 | - **PWM**: 12 channels (4 lights × RGB) via ESP32 LEDC peripheral. Default: 1 kHz @ 16-bit (camera-safe; runtime-tunable with the `freq` command) | |
| 35 | - **Drivers**: 600 W power supply → MOSFET per channel → LED. The ESP32 GPIO currently drives the MOSFET gate directly (3.3 V); this works at ≤1 kHz but droops at higher freqs. A dedicated gate-driver IC is the upgrade path — see [`HARDWARE.md`](HARDWARE.md). | |
| 36 | - **LEDs**: Common-anode RGB at higher rail voltage. Blue/green at ~32 V, red at ~26 V. Red gets a protective input cap (`RED_MAX_PWM=45000`) so it can't be over-driven. | |
| 37 | - **Network**: WiFi → mDNS (`studiolight.local`) → TCP server on port 1337 + ArduinoOTA on port 3232. | |
| 38 | ||
| 39 | The LightArm sub-project uses an Arduino Mega with Dynamixel servos for a fully separate spotlight-robot hardware path. | |
| 40 | ||
| 41 | Full pin assignments, voltages, and electrical caveats: [`HARDWARE.md`](HARDWARE.md). | |
| 42 | ||
| 43 | ## Software stack at a glance | |
| 44 | ||
| 45 | - **Firmware** — Arduino IDE / arduino-cli targeting ESP32 (Arduino core 3.x for `esp324xlight`, 2.0.17 for legacy `32wifi12frak`). See [`esp324xlight/README.md`](esp324xlight/README.md) for the build matrix. | |
| 46 | - **Show control** — Python (`cuemgr/`) speaks to multiple light units over TCP and to DMX hardware via the [OLA](https://www.openlighting.org/) (Open Lighting Architecture) daemon. | |
| 47 | - **Modelling / authoring** — Blender, with Python add-ons in `LightArm/blender/` (and `ghettopticon/blender/`) that translate Blender object positions/lighting into wire-protocol commands sent at every frame change. | |
| 48 | - **Network protocol** — Plain-text line-based commands over TCP/1337. Same command grammar (`pwm`, `pwm2`, `freq`, `gamma`, …) on both the LightArm robots and the studiolight units. Full grammar: [`PROTOCOL.md`](PROTOCOL.md). | |
| 49 | ||
| 50 | ## Quick start for a new developer | |
| 51 | ||
| 52 | ```bash | |
| 53 | # 1. Talk to a running studiolight from your workstation | |
| 54 | telnet studiolight.local 1337 | |
| 55 | > pwm 65534 65534 30000 65534 65534 30000 65534 65534 30000 65534 65534 30000 | |
| 56 | # (all 4 lights to a "warm white" reference per the Blender script convention) | |
| 57 | ||
| 58 | # 2. Build & flash the modern firmware (with the ESP32 core switcher installed) | |
| 59 | use-esp32-core 3.3.8 | |
| 60 | arduino-cli compile --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none esp324xlight/esp324xlight.ino | |
| 61 | arduino-cli upload --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none --port /dev/ttyUSB0 esp324xlight/esp324xlight.ino | |
| 62 | ||
| 63 | # 3. Or push firmware over the air (no USB), once the unit is on the network: | |
| 64 | arduino-cli upload --port studiolight.local --protocol network --upload-field password=studiolight-ota \ | |
| 65 | --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none \ | |
| 66 | esp324xlight/esp324xlight.ino | |
| 67 | ||
| 68 | # 4. Run a show from a cuesheet | |
| 69 | cd cuemgr | |
| 70 | python cuemgr.py | |
| 71 | ``` | |
| 72 | ||
| 73 | ## Repo + deployment | |
| 74 | ||
| 75 | cthuloid lives in a [pear-git-v3](https://github.com/holepunchto) bare repo | |
| 76 | at `~/git-repos/cthuloid.git` and is replicated peer-to-peer via Hyperswarm. | |
| 77 | The former GitHub origin is retained as `git remote github` for fallback. | |
| 78 | ||
| 79 | ```bash | |
| 80 | git push # → pear-git-v3 (origin) | |
| 81 | git push github master # → GitHub (legacy) | |
| 82 | ``` | |
| 83 | ||
| 84 | ## ESP32 core switcher | |
| 85 | ||
| 86 | `esp324xlight` builds against ESP32 Arduino core **3.3.8**, and the legacy | |
| 87 | `esp32/32wifi12frak` requires core **2.0.17**. Both are installed | |
| 88 | side-by-side at `~/.arduino15/packages/esp32/hardware/esp32/`. The | |
| 89 | `use-esp32-core` script at `~/.local/bin/use-esp32-core` flips which one is | |
| 90 | active for the IDE / arduino-cli: | |
| 91 | ||
| 92 | ```bash | |
| 93 | use-esp32-core 3.3.8 # modern (default; for esp324xlight and anything new) | |
| 94 | use-esp32-core 2.0.17 # legacy (only when rebuilding 32wifi12frak) | |
| 95 | use-esp32-core list # show current state | |
| 96 | ``` | |
| 97 | ||
| 98 | ## Documentation map | |
| 99 | ||
| 100 | | File | Scope | | |
| 101 | |---|---| | |
| 102 | | [README.md](README.md) | This overview | | |
| 103 | | [PROTOCOL.md](PROTOCOL.md) | Wire protocol shared by all light/robot units | | |
| 104 | | [HARDWARE.md](HARDWARE.md) | Pin assignments, voltages, MOSFET driving notes, hardware-side gotchas | | |
| 105 | | [esp324xlight/README.md](esp324xlight/README.md) | Current production firmware — full command reference, OTA workflow, stability audit | | |
| 106 | | [esp32/README.md](esp32/README.md) | Legacy + experimental ESP32 sketches | | |
| 107 | | [esp8266/README.md](esp8266/README.md) | ESP8266 single-light sketches | | |
| 108 | | [Arduino/readme](Arduino/readme) | Short index of Atmel sketches | | |
| 109 | | [LightArm/readme](LightArm/readme) | Spotlight-robot subproject | | |
| 110 | | [cuemgr/readme](cuemgr/readme) | Cueing program | | |
| 111 | | [ghettopticon/README](ghettopticon/README) | Cardboard wheelchair robot | | |
| 112 | | [Pcb/README.md](Pcb/README.md) | Schematic + PCB index | | |
| 113 | ||
| 114 | ## Credits | |
| 115 | ||
| 116 | Circus Automatic. Contributors include Jacob Fenwick, Fleeky Flanco, | |
| 117 | Francesco Capodieci, Vrav, and others through the years. |
| … | … | @@ -0,0 +1,131 @@ |
| 1 | # esp32/ | |
| 2 | ||
| 3 | Legacy + experimental ESP32 sketches. The **production firmware** for the | |
| 4 | 4-light studiolight rig is one directory up at | |
| 5 | [`../esp324xlight/`](../esp324xlight/) — start there if you're looking at | |
| 6 | the current system. | |
| 7 | ||
| 8 | Most sketches here predate the `esp324xlight` rewrite. They are kept for | |
| 9 | historical reference and because some still run on hardware that hasn't | |
| 10 | been migrated to the modern firmware. | |
| 11 | ||
| 12 | ## Build targets | |
| 13 | ||
| 14 | | Sketch | Requires ESP32 core | Notes | | |
| 15 | |---|---|---| | |
| 16 | | `32wifi12frak/` | **2.0.17** (legacy) | The direct predecessor of `esp324xlight`. Still works, with current fixes baked in. | | |
| 17 | | `32wifi12frakadafruit/` | unknown | Variant using Adafruit PWM library | | |
| 18 | | `32wifi12frakfastled/` | unknown | Variant using FastLED | | |
| 19 | | `32wifi12frakfled/` | unknown | Variant — unclear how it differs from `32wifi12frakfastled` | | |
| 20 | | `esp32-arnet-fastled/` | unknown | ArtNet → FastLED bridge | | |
| 21 | | `esp32-rotary-encoder/` | any | Rotary encoder lightswitch POC | | |
| 22 | | `esp8266-pwm-net/` | n/a (ESP8266) | Old single-light controller, lives here for grouping reasons | | |
| 23 | | `sc_test/` | any | SerialCommand library test | | |
| 24 | | `sketch_colortest/` | any | Standalone colour-test sketch | | |
| 25 | ||
| 26 | Use the `use-esp32-core` script to switch between cores when building | |
| 27 | against the legacy 2.0.17 vs the modern 3.x: | |
| 28 | ||
| 29 | ```bash | |
| 30 | use-esp32-core 2.0.17 # for 32wifi12frak | |
| 31 | use-esp32-core 3.3.8 # for everything else / esp324xlight | |
| 32 | ``` | |
| 33 | ||
| 34 | ## `32wifi12frak/` — the original 12-channel studiolight firmware | |
| 35 | ||
| 36 | This is the firmware that drove the studiolight rig before | |
| 37 | `esp324xlight` was written. It implements the same 12-channel PWM | |
| 38 | controller and the same plain-text TCP protocol (port 1337), but: | |
| 39 | ||
| 40 | - Uses the **old LEDC API** (`ledcSetup` + `ledcAttachPin` + channel-based `ledcWrite`) which was removed in ESP32 Arduino core 3.x. Builds only against **core 2.0.17**. | |
| 41 | - Uses `WiFiServer::available()` (the deprecated form of `accept()`). | |
| 42 | - The 12 `ledChannel0..11` globals are explicit; channels aren't auto-allocated. | |
| 43 | - Has been patched in-tree with the same fixes we added to `esp324xlight`: | |
| 44 | - `freq = 1000` (was 20000, which is impossible at 16-bit on the LEDC peripheral and silently fails) | |
| 45 | - GOT/OK/ERR telnet diagnostic markers | |
| 46 | - TCP stale-client mitigations (`#1527` / `#4773` workarounds, `WiFi.setSleep(false)`, 5-min idle timeout) | |
| 47 | - `MAX_PWM 65535` (was 65534 → an off-by-one that rejected legitimate full-off commands) | |
| 48 | - `verbose 0|1` runtime toggle for the diag markers | |
| 49 | ||
| 50 | If a deployed unit is still on `32wifi12frak` and you want to keep it | |
| 51 | there, build with: | |
| 52 | ||
| 53 | ```bash | |
| 54 | use-esp32-core 2.0.17 | |
| 55 | arduino-cli compile --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none 32wifi12frak/32wifi12frak.ino | |
| 56 | ``` | |
| 57 | ||
| 58 | Otherwise, **flash `esp324xlight` instead** — same hardware, same | |
| 59 | protocol, plus OTA, gamma correction, auto-resolution PWM, NVS-persisted | |
| 60 | hostname, etc. | |
| 61 | ||
| 62 | ## `32wifi12frakadafruit/`, `32wifi12frakfastled/`, `32wifi12frakfled/` | |
| 63 | ||
| 64 | Forks of `32wifi12frak` that experiment with alternative driver | |
| 65 | libraries: | |
| 66 | ||
| 67 | - **Adafruit** — uses the Adafruit PWM Servo Driver Library (off-board PCA9685) | |
| 68 | - **FastLED** — uses the FastLED library (typically for WS2811-class addressable strips) | |
| 69 | - **fled** — unclear, possibly a different FastLED variant | |
| 70 | ||
| 71 | These were experiments; only `32wifi12frak` (and its successor `esp324xlight`) made it to the active rig. Build status of these forks isn't currently verified. | |
| 72 | ||
| 73 | ## `esp32-arnet-fastled/` | |
| 74 | ||
| 75 | ArtNet receiver bridged to a FastLED-driven strip. Useful for plugging | |
| 76 | an ESP32-based light into a lighting console that speaks ArtNet/sACN | |
| 77 | instead of our custom TCP protocol. Untested in the current rig. | |
| 78 | ||
| 79 | ## `esp32-rotary-encoder/` | |
| 80 | ||
| 81 | Two sketches: | |
| 82 | ||
| 83 | - `lightswitch_poc/` — proof-of-concept "lightswitch" UI that uses a | |
| 84 | rotary encoder to control brightness on a single channel | |
| 85 | - `rotary-encoder/` — generic rotary encoder driver | |
| 86 | ||
| 87 | Added more recently (2025-ish) as exploratory work on physical-UI inputs | |
| 88 | to complement the network-driven rig. | |
| 89 | ||
| 90 | ## `esp8266-pwm-net/` | |
| 91 | ||
| 92 | ESP8266 (not ESP32) sketch for a single-light controller. Lives here for | |
| 93 | historical filing reasons; functionally belongs with `../esp8266/`. The | |
| 94 | ESP8266 path was abandoned in favour of ESP32 once channel count | |
| 95 | requirements grew past what ESP8266 could provide. | |
| 96 | ||
| 97 | ## `sc_test/` | |
| 98 | ||
| 99 | Bare-minimum sketch that uses just the SerialCommand (`SC`) library — | |
| 100 | the same dispatcher all the WiFi sketches use. Useful for debugging the | |
| 101 | command parser in isolation if a `pwm`/`pwm2`/etc. handler stops | |
| 102 | responding. | |
| 103 | ||
| 104 | ## `sketch_colortest/` | |
| 105 | ||
| 106 | Standalone colour-test sketch. Cycles through hard-coded colour | |
| 107 | combinations to verify that the wiring on a new rig is correct (which | |
| 108 | GPIO drives which colour on each light). Useful when bringing up a new | |
| 109 | unit. | |
| 110 | ||
| 111 | ## Build with arduino-cli (any sketch here) | |
| 112 | ||
| 113 | ```bash | |
| 114 | # Pick the right core | |
| 115 | use-esp32-core 2.0.17 # for 32wifi12frak only | |
| 116 | # OR | |
| 117 | use-esp32-core 3.3.8 # for most other sketches here | |
| 118 | ||
| 119 | # Compile | |
| 120 | arduino-cli compile \ | |
| 121 | --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none \ | |
| 122 | <sketch-dir>/<sketch>.ino | |
| 123 | ||
| 124 | # Upload over USB | |
| 125 | arduino-cli upload \ | |
| 126 | --fqbn esp32:esp32:esp32wrover:FlashFreq=80,UploadSpeed=921600,PartitionScheme=default,DebugLevel=none \ | |
| 127 | --port /dev/ttyUSB0 \ | |
| 128 | <sketch-dir>/<sketch>.ino | |
| 129 | ``` | |
| 130 | ||
| 131 | `esp324xlight` is the only sketch in this repo that supports OTA (over-WiFi) uploads. |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | # esp8266/ | |
| 2 | ||
| 3 | Historical ESP8266 single-light controllers. The project moved to ESP32 | |
| 4 | + 12-channel control (now [`esp324xlight/`](../esp324xlight/)) because | |
| 5 | ESP8266 doesn't have enough hardware PWM channels for 4 lights × RGB. | |
| 6 | ||
| 7 | These sketches are kept for reference / for resurrecting an ESP8266 | |
| 8 | single-light if you have one in a drawer. | |
| 9 | ||
| 10 | ## `esp8266-frak/` | |
| 11 | ||
| 12 | ESP8266 controller for a single light, using the same `pwm` / `plevel` | |
| 13 | command grammar as the larger ESP32 rigs (subset thereof). Connects to | |
| 14 | WiFi and listens on TCP/1337. | |
| 15 | ||
| 16 | ## `esp8266-rgb/` | |
| 17 | ||
| 18 | ESP8266 RGB controller — like `esp8266-frak` but with three channels | |
| 19 | (R, G, B) instead of one. | |
| 20 | ||
| 21 | ## Building | |
| 22 | ||
| 23 | ESP8266 sketches require the **ESP8266 Arduino core**, which is a | |
| 24 | separate package from the ESP32 cores managed by the `use-esp32-core` | |
| 25 | script: | |
| 26 | ||
| 27 | ```bash | |
| 28 | arduino-cli core install esp8266:esp8266 | |
| 29 | arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2 <sketch>.ino | |
| 30 | arduino-cli upload --fqbn esp8266:esp8266:nodemcuv2 --port /dev/ttyUSB0 <sketch>.ino | |
| 31 | ``` | |
| 32 | ||
| 33 | (Board fqbn depends on your specific ESP8266 board — `nodemcuv2`, | |
| 34 | `d1_mini`, `huzzah`, etc.) | |
| 35 | ||
| 36 | ## Why this isn't the current path | |
| 37 | ||
| 38 | ESP8266 has: | |
| 39 | - Only 1 hardware PWM channel (the rest are software-PWM, jittery) | |
| 40 | - Less RAM and Flash | |
| 41 | - Slower CPU (80 MHz vs ESP32's 240 MHz) | |
| 42 | ||
| 43 | For a 12-channel RGB rig the ESP32's LEDC peripheral (16 hardware PWM | |
| 44 | channels, dedicated timer hardware) is decisively the right answer. | |
| 45 | The ESP8266 controllers worked for a single-light/single-channel | |
| 46 | configuration but didn't scale to the studiolight rig. | |
| 47 | ||
| 48 | See [`../esp324xlight/`](../esp324xlight/) for the current 12-channel | |
| 49 | firmware. |