Files
AutoFilm-ESP32/docs/CURRENT_STATE.md

171 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AutoFilm-ESP32 — Current State
Snapshot of the pre-refactor PlatformIO Arduino tree (`9186d84`).
Integration going forward is **`develop`**; this refactor lands via PR from `refactor/esp-idf-modular-ui` into `develop`.
This document describes the tree **as it existed at that snapshot**, before the ESP-IDF modular refactor. Recipe *values* (times, temperatures, rotation counts) are treated as frozen; this document does not propose changing them.
## What the product is
AutoFilm-ESP32 is a Kindermann-style rotary film processor controller. The operator picks a process (C41, E6, B&W, ECN-2, Custom, B&WREV), walks the chemical steps, starts a timed agitation cycle, and is prompted to pour / drain between steps. Temperature is *measured* only; closed-loop bath heat is an external sous-vide.
## Toolchain and layout
| Item | Today |
| --- | --- |
| Build | PlatformIO `platformio.ini`, env `esp32dev` |
| Framework | Arduino on Espressif32 |
| Target | Classic ESP32-WROOM (`board = esp32dev`) |
| Layout | `src/*.cpp`, `include/*.h`, Arduino-style `setup()` / `loop()` |
| Entry | `src/AutoFilmESP32.cpp` |
Arduino libraries (all treated as future debt):
- `AccelStepper` — STEP/DIR stepper
- `LiquidCrystal_I2C` — 20×4 character LCD
- `Keypad` — matrix scan
- `OneWire` + `DallasTemperature` — DS18B20
There is **no** ESP-IDF `CMakeLists.txt`, no `sdkconfig`, no component split, no CI matrix.
## Hardware as wired in this tree
From `include/config.h` and `src/config.cpp`:
| Function | GPIO / bus |
| --- | --- |
| Stepper STEP (pulse) | 12 |
| Stepper DIR | 14 |
| Stepper EN (active LOW) | 27 |
| DS18B20 data | 13 |
| Beeper | 25 |
| LCD I²C | SDA 21, SCL 22, address `0x27`, 20×4 |
| Keypad rows | 19, 18, 5, 17, 16 |
| Keypad cols | 15, 2, 0, 4 |
Motor constants: `STEPS_PER_REV = 4800`, `RPM = 60`, acceleration `9600`. Enable is driven HIGH at boot (disabled).
Note — S3 panel (JC4827W543C) pin lock via `board_jc4827w543` accessors: motor EN 7 / STEP 16 / DIR 15 on header P3 (IO6/7/15/16), DS18B20 on 17 on header P4 (GND/3V3/17/18, UART1 unused), speaker amp I2S BCLK 42 / LRCLK 2 / DIN 41 (P7 Speak). Header P2 carries IO46/9/14/5 and is left unused (IO46 is input-only).
Keypad map (5×4):
```
F E # *
1 2 3 U
4 5 6 D
7 8 9 X ← 4 across, 4 down = Esc ('X')
L 0 R E
```
Keys `1`–`6` select programmes. `U`/`D` browse steps. `L`/`R` nudge step time ±5 s. `E` confirm. `X` escape.
## Module map
```
AutoFilmESP32.cpp setup/loop, LCD splash, watchdog off
config.cpp / .h globals, pin map, library objects
devSequence.cpp/.h six compiled-in recipes
menu.cpp / .h blocking menus + startProcessing()
display.cpp / .h starting menu, headings, mm:ss helper
temperature.cpp/.h DS18B20 read + LCD write
motor.cpp / .h FreeRTOS agitation task
sound.cpp / .h blocking tone() tunes
watchdog.h disables TWDT and idle-task WDT
```
There is no HAL. UI, recipes, and actuators share globals (`devPgm`, `run`, `lcd`, `stepper`, `keypad`, task handles).
## Recipe data
`struct devSequence` (`include/devSequence.h`):
- `processName[7]`
- `cycles` — number of steps
- `processTime[20]` — seconds
- `processCycleName[20][10]`
- `processCycle[2][20]` — `[0]` CW revolutions, `[1]` CCW revolutions
- `processTemp[3][20]` — `[0]` min, `[1]` preferred, `[2]` max °C
`NUM_DEV_SEQUENCES = 6`. Lookup is linear `strcmp` in `findSequenceByName()`.
### Compiled defaults (do not change values)
**C41** — 7 steps, ~38 °C developer:
| Step | Time s | Name | CW | CCW | Min | Pref | Max |
| --- | ---: | --- | ---: | ---: | ---: | ---: | ---: |
| 0 | 180 | Prewarm | 1 | 1 | 37.8 | 38 | 38.2 |
| 1 | 195 | Developer | 5.5 | 5 | 37.8 | 38 | 38.2 |
| 2 | 45 | Bleach | 5.5 | 5 | 32 | 38 | 38.2 |
| 3 | 180 | Fix | 5.5 | 5 | 32 | 38 | 38.2 |
| 4 | 60 | Rinse 1 | 3.5 | 3 | 32 | 38 | 38.2 |
| 5 | 60 | Rinse 2 | 3.5 | 3 | 32 | 38 | 38.2 |
| 6 | 30 | Fin Rinse | 1 | 1 | 32 | 38 | 38.2 |
**E6** — 12 steps (Preheat … Fin Rinse). FirstDev 360 s at 38 °C; ColorDev 360 s; Fin Rinse 30 s at 19–21 °C.
**ECN-2** — 9 steps. RemJet is a 0 s / 0 rotation placeholder (manual). Developer 210 s at 40.8–41.2 °C.
**B&W** — 7 steps. Developer 510 s at 19–21 °C.
**Custom** — 4×10 s stub (Developer / Stop / Fix / Rinse).
**B&WREV** — 12 steps. FirstDev 720 s; SecondDev 360 s; wash steps cooler (min 15.5 °C).
Left/right time edits mutate the in-RAM struct. They are **not** written to flash. Process names longer than 6 characters would not fit `processName[7]`.
## Runtime behaviour
1. `setup()`: Serial 115200, **watchdogs disabled**, I²C + LCD init, custom thermometer glyph, motor disabled, splash `AUTOFILM` + `V0.1.1 20240701` for 1 s.
2. `loop()` calls `startingMenu()` and never returns to a real idle loop while a programme is selected.
3. `startingMenu()` draws 1–6 and **busy-waits** until `devPgm != ""`.
4. `startDev()` walks `cycles`. If `run == 1` it chains `startProcessing()` for the next step (auto-advance after a completed step). Otherwise it shows Step / Time / Temp and waits for U/D/E/X/L/R.
5. `startProcessing()`:
- Shows step name, duration, preferred temp, `Ent:start Esc:quit`.
- Esc (`X`) here aborts **before** motion and returns 0.
- On Enter, records `processStartTime` / `processTimeMillis`, heap-allocates `MotorTaskParams`, and `xTaskCreatePinnedToCore(runMotorTask, …, core 0)`.
- Then **blocks** in `while (millis() < end) { update remaining; delay(410); readTemperature(); }`.
- Deletes the motor task, drives EN HIGH, plays a **blocking 10-beep alarm** (~7.5 s), frees params.
`runMotorTask` enables the driver and loops forever: CW `cwRotations` revs, then CCW `ccwRotations` revs, using `stepper.run()` busy-wait on each move. `processEndTime` is passed in and **never read**. The task does not check a stop flag. It only ends when the UI task `vTaskDelete`s it.
Temperature: `sensors.requestTemperatures()` + `getTempCByIndex(0) + 0.4 °C` offset, written to LCD row 3. Called from the UI wait loops. `updateTempDisplay(void *)` exists as a one-shot task that also talks to the LCD and then deletes itself; it is unused from `startProcessing()`.
Sound: `tone()` + `delay()` on the caller thread. `playTune()` is unused by the main flow.
## Concurrency as it stands
| Thread | Role | Problem |
| --- | --- | --- |
| Arduino `loopTask` | All UI, all input, all timing, temp, sound | Blocks for seconds to minutes |
| `MotorTask` (core 0) | Agitation | No cooperative stop; killed from outside |
| Idle 0/1 | — | Removed from TWDT |
LCD, `stepper`, and `sensors` are used without mutexes. That is safe only because the UI thread does not command the stepper while the motor task runs — except `vTaskDelete` can interrupt `stepper.run()` mid-pulse.
## Defects and hazards relevant to the refactor
1. **No stop while a step is running.** Esc is honoured only on the pre-start prompt. The run loop does not read the keypad. This violates the product rule that the machine must always stop on Esc.
2. **`vTaskDelete` of a live motor task** can leave STEP/DIR in an undefined state; EN is dropped only *after* delete.
3. **Watchdog deliberately off** (`include/watchdog.h`). A hung `stepper.run()` or OneWire slot will wedge the chip with no recovery.
4. **Busy-wait input** (`while (true)` + `keypad.getKey()`). `millis() % 1000 == 0` is not a 1 Hz tick; it is a one-millisecond race and can skip or repeat temp reads.
5. **`secondsToMinutesSeconds()` `malloc`s 6 bytes and never frees.** Called from the run loop ~twice per second.
6. **`startDev()` shadows the global** with `int run = 1;` on Enter, so the auto-advance path is inconsistent with the `run` flag used after `startProcessing()` returns.
7. **OneWire conversion blocks the UI** (typically 750 ms at 12-bit). Combined with `delay(410)` the remaining-time display stutters.
8. **Alarm after a step blocks everything** for several seconds; Esc cannot interrupt it.
9. **No input abstraction.** Touch cannot be added without rewriting `menu.cpp`.
10. **Board pins are compile-time literals.** The Guition S3 panel reuses many of these GPIOs for QSPI / GT911; the current map cannot be copied across.
## What is *not* in the tree
- ESP-IDF project or dual-firmware CI
- Touch / colour LCD support
- Pause / resume of a live step
- Persistent profiles (README already lists this as future)
- Pumps, valves, reservoirs, drain actuators
- Closed-loop heater control
- Unit tests beyond PlatformIO’s empty `test/README`
## README vs code
`README.MD` matches the hardware story (ESP32, NEMA-17, DS18B20, 2004 I²C, Kindermann tank) and the pour-start-drain loop. It still talks about opening the project in Arduino IDE / PlatformIO and does not mention FreeRTOS, the S3 panel, or stop-during-run.