diff --git a/AGENTS.md b/AGENTS.md index 3ce1694..e1c2dc4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,9 +2,11 @@ ## Project Structure & Module Organization -This repository contains a small ESP8266/ESP32 Arduino project for monitoring water level with conductive electrodes. +This repository contains a small ESP8266/ESP32 Arduino project for monitoring water level with conductive electrodes and a small PHP endpoint for logging measurements. - `arduino/arduino.ino` contains the Arduino sketch and main application logic. +- `server/log.php` accepts measurement POST requests and appends them as JSON Lines. +- `data/YYYY.jsonl` is created at runtime for logged measurements and should not be treated as source code. - `README.md` gives the high-level project purpose. - `LICENSE` contains licensing information. - `.agents/` is reserved for agent/tooling metadata and should not be treated as firmware source. @@ -25,11 +27,14 @@ arduino-cli monitor -p COM3 -c baudrate=115200 arduino-cli compile --fqbn esp32:esp32:esp32 arduino arduino-cli upload -p COM3 --fqbn esp32:esp32:esp32 arduino arduino-cli monitor -p COM3 -c baudrate=115200 + +php -l server/log.php ``` - `compile` verifies the sketch builds for the selected board. - `upload` flashes the firmware to the connected board. - `monitor` opens the serial console used by debug output. +- `php -l` checks the PHP logging endpoint for syntax errors. Adjust `--fqbn` and `COM3` for the actual board and port. @@ -43,6 +48,8 @@ Write C++ compatible with Arduino IDE and the ESP8266/ESP32 cores. Use two-space Prefer small functions with one responsibility. Keep hardware pins, URLs, timings, and debug flags as constants near the top of the sketch. +For PHP scripts, keep them framework-free unless the project grows. Return JSON responses, use explicit HTTP status codes, and keep generated log files under `data/`. + ## Hardware Notes For ESP8266 conductive level sensing, use external pull-down resistors from each `PIN_LEVEL1..4` input to `GND`. The common electrode is driven to positive voltage only during measurement. @@ -60,6 +67,13 @@ Before committing firmware changes: - Test `DEBUG == 2` for repeated Wi-Fi POST requests. - Test `DEBUG == 0` for production sleep/wake behavior. +Before committing server changes: + +- Run `php -l server/log.php`. +- Test that non-POST requests return HTTP 405 with `Allow: POST`. +- Test a form POST such as `level1=1&level2=0&level3=0&level4=1`. +- Verify that a single compact JSON object is appended to `data/YYYY.jsonl`. + ## Commit & Pull Request Guidelines Current Git history is minimal and uses a simple message such as `Initial commit`. Keep future commits short, imperative, and specific, for example `Add WiFi timeout handling`. diff --git a/server/log.php b/server/log.php new file mode 100644 index 0000000..04799c9 --- /dev/null +++ b/server/log.php @@ -0,0 +1,88 @@ + false, + 'error' => 'Method Not Allowed', + ]); + exit; +} + +$levels = []; +foreach (['level1', 'level2', 'level3', 'level4'] as $name) { + if (!array_key_exists($name, $_POST)) { + $levels[$name] = 0; + continue; + } + + if (is_array($_POST[$name])) { + respondJson(400, [ + 'success' => false, + 'error' => 'Invalid value for ' . $name, + ]); + exit; + } + + $value = trim((string) $_POST[$name]); + if ($value !== '0' && $value !== '1') { + respondJson(400, [ + 'success' => false, + 'error' => 'Invalid value for ' . $name, + ]); + exit; + } + + $levels[$name] = (int) $value; +} + +$record = [ + 'timestamp' => date('c'), + 'level1' => (int) $levels['level1'], + 'level2' => (int) $levels['level2'], + 'level3' => (int) $levels['level3'], + 'level4' => (int) $levels['level4'], +]; + +$dataDir = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data'; +if (!is_dir($dataDir)) { + if (!mkdir($dataDir, 0755, true) && !is_dir($dataDir)) { + respondJson(500, [ + 'success' => false, + 'error' => 'Failed to create data directory', + ]); + exit; + } +} + +$filename = $dataDir . DIRECTORY_SEPARATOR . date('Y') . '.jsonl'; +$json = json_encode($record); +if ($json === false) { + respondJson(500, [ + 'success' => false, + 'error' => 'Failed to encode record', + ]); + exit; +} + +$written = file_put_contents($filename, $json . PHP_EOL, FILE_APPEND | LOCK_EX); +if ($written === false) { + respondJson(500, [ + 'success' => false, + 'error' => 'Failed to write log file', + ]); + exit; +} + +respondJson(200, [ + 'success' => true, +]);