Firmware (Arduino Nano ESP32 / PlatformIO): - Native ASCOM Alpaca CoverCalibrator REST API on port 11111 - Alnitak serial protocol on USB at 9600 baud (simultaneous with WiFi) - MG995 servo cover mechanism with non-blocking state machine (D9) - LED brightness PWM via IRLZ44N MOSFET, LEDC channel 0 (D3) - 12V dew heater PWM via IRLZ44N MOSFET, LEDC channel 1 (D5) - mDNS + UDP Alpaca discovery, WiFi watchdog reconnect - SERIAL_DEBUG flag to silence debug output in USB-only mode INDI driver (C++ / libcurl / nlohmann-json): - WiFi mode: HTTP Alpaca via libcurl - USB mode: Alnitak serial via POSIX termios - LightBoxInterface + DustCapInterface + dew heater number property Python controller (PyQt6): - Dark-themed desktop app for direct manual control - AlpacaBackend (requests) + AlnitakBackend (pyserial) - PollWorker QThread; cover, brightness, dew heater panels - QSettings persistence; auto serial port discovery Docs: - system-diagram.svg, wiring-diagram.svg (browser-renderable SVG) - BUILD_NOTES.md with BOM, LM2596 calibration, power-on checklist - WIRING.md quick-reference, README.md, CHANGELOG.md
275 lines
9.6 KiB
Markdown
275 lines
9.6 KiB
Markdown
# 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://<host>: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`<br>`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.
|