# ESP32 FlatPanel An open-source automated telescope flat-field panel built around the **Arduino Nano ESP32**. Controls LED brightness, a motorised cover, and a dew heater — all accessible from ASCOM, INDI, and a standalone desktop application. --- ## Features | Feature | Detail | |---------|--------| | **LED brightness** | 8-bit PWM (0–255) via logic-level MOSFET | | **Motorised cover** | MG995 servo rotates panel in/out of the telescope light path | | **Dew heater** | 12V resistive element, 0–100% PWM — keeps the diffuser clear | | **WiFi mode** | Native ASCOM Alpaca REST API on port 11111; auto-discovered via mDNS and UDP broadcast | | **USB mode** | Alnitak serial protocol at 9600 baud — plug-and-play with N.I.N.A., SGPro, APT and `indi_flipflat` | | **Both simultaneously** | WiFi and USB are active at the same time in a single firmware build | | **ASCOM** | Use ASCOM Remote (free, official) — no custom COM driver required | | **INDI** | Custom `indi_esp32_flatpanel` driver with WiFi and USB connection modes | | **Desktop app** | Dark-themed PyQt6 controller for direct manual operation | --- ## Repository layout ``` flatpanel/ ├── firmware/ ESP32 firmware (PlatformIO / Arduino) │ ├── platformio.ini │ └── src/ │ ├── config.h ← edit this first │ └── main.cpp ├── indi-driver/ INDI driver (C++, Linux) │ ├── CMakeLists.txt │ ├── indi_esp32_flatpanel.h │ ├── indi_esp32_flatpanel.cpp │ └── indi_esp32_flatpanel.xml ├── controller/ Python desktop controller (all platforms) │ ├── flatpanel_controller.py │ └── requirements.txt ├── docs/ │ ├── system-diagram.svg Block diagram — open in any browser │ ├── wiring-diagram.svg Colour-coded wiring — open in any browser │ └── BUILD_NOTES.md Step-by-step assembly guide ├── WIRING.md Quick-reference wiring + BOM ├── README.md This file └── CHANGELOG.md ``` --- ## Hardware required | Component | Role | Pin | |-----------|------|-----| | Arduino Nano ESP32 | Main controller | — | | MG995 servo | Cover open/close | D9 | | IRLZ44N MOSFET × 2 | LED brightness + dew heater | D3, D5 | | LM2596 buck module | 12V → 5V for ESP32 + servo | — | | 12V PSU ≥ 4A | Powers everything | — | | White LED strip 12V (Ra ≥ 90) | Flat-field illumination | — | | Frosted acrylic 3mm | Light diffuser | — | | Dew heater element 12V ~24Ω | Anti-dew resistive heater | — | | 100Ω resistor × 2 | MOSFET gate series | — | | 10kΩ resistor × 2 | MOSFET gate pull-down | — | See [docs/BUILD_NOTES.md](docs/BUILD_NOTES.md) for the full BOM, dew heater sizing table, and step-by-step assembly instructions. See [docs/wiring-diagram.svg](docs/wiring-diagram.svg) for the colour-coded wiring diagram. --- ## Quick-start ### 1 Configure the firmware Edit `firmware/src/config.h`: ```cpp #define WIFI_SSID "your_network" #define WIFI_PASSWORD "your_password" // Tune to your mechanical stops after assembly: #define SERVO_CLOSED 0 // panel in flat position #define SERVO_OPEN 90 // panel clear of telescope // Set false when using USB/Alnitak mode to keep the serial line clean: #define SERIAL_DEBUG true ``` ### 2 Flash ```bash cd flatpanel pio run -t upload # PlatformIO CLI # or use the PlatformIO IDE extension in VS Code ``` ### 3 Verify Open a serial monitor at **9600 baud**. You should see the ESP32's IP address printed on boot. Browse to: ``` http://esp32-flatpanel.local:11111/management/v1/configureddevices ``` Expected response contains `"DeviceType":"CoverCalibrator"`. --- ## Connection modes ### WiFi — ASCOM Alpaca (no driver needed) The ESP32 **is** an Alpaca server. Use **ASCOM Remote** as the bridge to ASCOM COM: 1. Install [ASCOM Platform 6.6+](https://ascom-standards.org/Downloads/Index.htm) and [ASCOM Remote](https://github.com/ASCOMInitiative/ASCOM.Remote/releases). 2. In ASCOM Remote, add a **CoverCalibrator** device → host `esp32-flatpanel.local`, port `11111`, device `0`. 3. In N.I.N.A. / SGPro / APT, choose **ASCOM CoverCalibrator** and select the ASCOM Remote device. The ESP32 is also discovered automatically by software that supports Alpaca UDP discovery (N.I.N.A. 3+, Cartes du Ciel). ### WiFi — INDI ```bash # Build and install the custom driver (once) cd flatpanel/indi-driver cmake -B build -DCMAKE_INSTALL_PREFIX=/usr cmake --build build -j$(nproc) sudo cmake --install build # Start (KStars/Ekos loads it automatically after install) indiserver indi_esp32_flatpanel ``` In Ekos: select **ESP32 FlatPanel**, set Connection Mode to **WiFi / Alpaca**, enter the host and port. ### USB — Alnitak (plug-and-play) Set `SERIAL_DEBUG false` in `config.h` and reflash. Then connect via USB-C. The firmware speaks the standard Alnitak protocol so existing software works without any driver: | Software | Built-in support | |----------|-----------------| | N.I.N.A. | ✅ Alnitak / Flip-Flat | | Sequence Generator Pro | ✅ Alnitak | | Astro Photography Tool | ✅ Alnitak | | INDI (`indi_flipflat`) | ✅ ships with INDI | | Our INDI driver | ✅ USB mode in Connection tab | > **Dew heater via USB:** The standard Alnitak protocol has no dew heater concept. > Our INDI driver and the Python controller use custom extension commands `>T` (set) / `>U` (get) that are silently ignored by other software. ### Desktop controller app Works in both WiFi and USB mode. Dark-themed for night use. ```bash pip install PyQt6 requests pyserial python flatpanel/controller/flatpanel_controller.py ``` --- ## Alpaca API reference Base URL: `http://:11111/api/v1/covercalibrator/0/` | Method | Endpoint | Parameters | Description | |--------|----------|-----------|-------------| | GET | `coverstate` | — | 2=Open, 3=Closed, 4=Moving | | GET | `calibratorstate` | — | 1=Off, 3=Ready | | GET | `brightness` | — | Current brightness 0–255 | | GET | `maxbrightness` | — | Always 255 | | GET | `dewheater` | — | Current dew heater 0–100% *(custom)* | | PUT | `opencover` | — | Rotate panel out of light path | | PUT | `closecover` | — | Rotate panel into flat position | | PUT | `haltcover` | — | Stop cover movement | | PUT | `calibratoron` | `Brightness=N` | Turn on LED at brightness N | | PUT | `calibratoroff` | — | Turn off LED | | PUT | `dewheater` | `Percentage=N` | Set dew heater 0–100% *(custom)* | | PUT | `action` | `Action=SetDewHeater`
`Parameters=N` | Alpaca-standard alternative to PUT dewheater | | PUT | `action` | `Action=GetDewHeater` | Returns current dew % as string Value | Management endpoints: `/management/apiversions`, `/management/v1/description`, `/management/v1/configureddevices` --- ## Alnitak serial protocol reference Port: USB-C at 9600 baud, 8N1. Command format: `>CXXX\r` · Response format: `*CDDVVV\r` (DD = device type 19). | Cmd | Description | Example | |-----|-------------|---------| | `>P000` | Ping | `*P19000` | | `>S000` | State (motor + light) | `*S19` + motor(1) + light(0) + `00` | | `>B128` | Set brightness 0–255 | `*B19128` | | `>J000` | Get brightness | `*J19128` | | `>L000` | Light on (keep current brightness) | `*L19000` | | `>D000` | Light off | `*D19000` | | `>O000` | Open cover | `*O19000` | | `>C000` | Close cover | `*C19000` | | `>H000` | Halt cover | `*H19000` | | `>T050` | **Set dew heater 50%** *(custom)* | `*T19050` | | `>U000` | **Get dew heater %** *(custom)* | `*U19050` | Motor state in `>S000` response: `0`=unknown/moving · `1`=closed · `2`=open Light state: `0`=off · `1`=on --- ## Pin assignments | D-pin | GPIO | Function | Config define | |-------|------|----------|---------------| | D9 | — | MG995 servo signal | `SERVO_PIN` | | D3 | — | LED MOSFET gate | `LED_PWM_PIN` | | D5 | — | Dew heater MOSFET gate | `DEW_HEATER_PIN` | All pins use the Arduino Nano ESP32 D-number scheme. LEDC channel 0 → LED · channel 1 → dew heater. --- ## Building the INDI driver **Dependencies:** `libindi-dev`, `libcurl4-openssl-dev`, `nlohmann-json3-dev`, `cmake` ```bash sudo apt install libindi-dev libcurl4-openssl-dev nlohmann-json3-dev cmake build-essential cd flatpanel/indi-driver cmake -B build -DCMAKE_INSTALL_PREFIX=/usr cmake --build build -j$(nproc) sudo cmake --install build ``` If `nlohmann-json3-dev` is unavailable, CMake will fetch it automatically via `FetchContent`. --- ## Troubleshooting | Symptom | Fix | |---------|-----| | No boot message on Serial | Check VIN wiring; try USB-C direct to PC (bypasses LM2596) | | Servo twitches constantly | LM2596 sagging; re-measure 5V under servo load | | LED stays on at 0% | Missing 10kΩ gate pull-down on MOSFET-A | | Alnitak commands ignored | Set `SERIAL_DEBUG false`, reflash | | `Connection refused` on port 11111 | ESP32 not yet connected to WiFi; check credentials | | Dew heater not warming | Verify MOSFET-B drain voltage ≈ 0V when active; check element resistance | | Python app import error | `pip install PyQt6 requests pyserial` | | INDI build fails | `sudo apt install libindi-dev` | --- ## Diagrams | File | Description | |------|-------------| | [docs/system-diagram.svg](docs/system-diagram.svg) | Architecture block diagram | | [docs/wiring-diagram.svg](docs/wiring-diagram.svg) | Colour-coded wiring diagram | Open either file in a web browser — no additional software needed. --- ## Licence Released under the **MIT Licence** — see `LICENCE` file. ASCOM and Alpaca are trademarks of the ASCOM Initiative. INDI is maintained by the INDI developers at indilib.org.