Compare commits

...

4 Commits

Author SHA1 Message Date
Mohamad
b54a2dbbb4 changed libs 2025-05-02 08:34:42 +02:00
Mohamad
9faad61b23 wokwi diagram and config 2025-05-02 08:34:35 +02:00
Mohamad
5943413731 working demo (hopefully) + readme 2025-05-02 08:34:06 +02:00
Mohamad
60469f1a0f bs 2025-03-14 09:59:41 +01:00
5 changed files with 2310 additions and 109 deletions

View File

@ -0,0 +1,176 @@
{
"version": 1,
"author": "Generated for IR_Tracker_Optimized_ProdReady.ino v2.2.2 (Button Sim)",
"editor": "wokwi-arduino-ide",
"parts": [
{ "type": "wokwi-arduino-uno", "id": "uno" },
{
"type": "wokwi-uln2003-stepper-driver",
"id": "drvX",
"top": -150,
"left": -150,
"attrs": { "label": "Stepper X Driver" }
},
{
"type": "wokwi-stepper-motor",
"id": "motX",
"top": -150,
"left": -280,
"attrs": { "label": "Stepper X" }
},
{
"type": "wokwi-uln2003-stepper-driver",
"id": "drvY",
"top": -50,
"left": -150,
"attrs": { "label": "Stepper Y Driver" }
},
{
"type": "wokwi-stepper-motor",
"id": "motY",
"top": -50,
"left": -280,
"attrs": { "label": "Stepper Y" }
},
{
"type": "wokwi-pushbutton",
"id": "irL",
"top": -134.4,
"left": 220.8,
"attrs": { "label": "Button L (D2)", "color": "cyan" }
},
{
"type": "wokwi-pushbutton",
"id": "irR",
"top": -86.4,
"left": 220.8,
"attrs": { "label": "Button R (D3)", "color": "cyan" }
},
{
"type": "wokwi-pushbutton",
"id": "irU",
"top": -134.4,
"left": 288,
"attrs": { "label": "Button U (D12)", "color": "cyan" }
},
{
"type": "wokwi-pushbutton",
"id": "irD",
"top": -86.4,
"left": 288,
"attrs": { "label": "Button D (D13)", "color": "cyan" }
},
{
"type": "wokwi-pushbutton",
"id": "irC",
"top": -38.4,
"left": 259.2,
"attrs": { "label": "Button C (A0)", "color": "white" }
},
{
"type": "wokwi-pushbutton",
"id": "swX",
"top": 100,
"left": -150,
"attrs": { "label": "Limit X (A4)", "color": "blue" }
},
{
"type": "wokwi-pushbutton",
"id": "swY",
"top": 100,
"left": -100,
"attrs": { "label": "Limit Y (A5)", "color": "blue" }
},
{
"type": "wokwi-led",
"id": "ledSearch",
"top": 100,
"left": 200,
"attrs": { "label": "Searching (A1)", "color": "yellow" }
},
{
"type": "wokwi-resistor",
"id": "resSearch",
"top": 65,
"left": 230,
"rotate": 90,
"attrs": { "value": "220" }
},
{
"type": "wokwi-led",
"id": "ledTrack",
"top": 100,
"left": 250,
"attrs": { "label": "Tracking (A2)", "color": "green" }
},
{
"type": "wokwi-resistor",
"id": "resTrack",
"top": 65,
"left": 280,
"rotate": 90,
"attrs": { "value": "220" }
},
{
"type": "wokwi-led",
"id": "ledLost",
"top": 100,
"left": 300,
"attrs": { "label": "Lost (A3)", "color": "red" }
},
{
"type": "wokwi-resistor",
"id": "resLost",
"top": 65,
"left": 330,
"rotate": 90,
"attrs": { "value": "220" }
}
],
"connections": [
[ "drvX:OUT1", "motX:COIL1", "blue", [ "v0" ] ],
[ "drvX:OUT2", "motX:COIL2", "pink", [ "v0" ] ],
[ "drvX:OUT3", "motX:COIL3", "yellow", [ "v0" ] ],
[ "drvX:OUT4", "motX:COIL4", "orange", [ "v0" ] ],
[ "uno:8", "drvX:IN1", "green", [ "v0" ] ],
[ "uno:9", "drvX:IN2", "green", [ "v0" ] ],
[ "uno:10", "drvX:IN3", "green", [ "v0" ] ],
[ "uno:11", "drvX:IN4", "green", [ "v0" ] ],
[ "drvY:OUT1", "motY:COIL1", "blue", [ "v0" ] ],
[ "drvY:OUT2", "motY:COIL2", "pink", [ "v0" ] ],
[ "drvY:OUT3", "motY:COIL3", "yellow", [ "v0" ] ],
[ "drvY:OUT4", "motY:COIL4", "orange", [ "v0" ] ],
[ "uno:4", "drvY:IN1", "purple", [ "v0" ] ],
[ "uno:5", "drvY:IN2", "purple", [ "v0" ] ],
[ "uno:6", "drvY:IN3", "purple", [ "v0" ] ],
[ "uno:7", "drvY:IN4", "purple", [ "v0" ] ],
[ "uno:2", "irL:2.r", "cyan", [ "v0", "h-10" ] ],
[ "irL:1.r", "uno:GND", "black", [ "h0", "v130", "h-280", "v-20"] ],
[ "uno:3", "irR:2.r", "cyan", [ "v0", "h-10" ] ],
[ "irR:1.r", "uno:GND", "black", [ "h0", "v90", "h-280", "v-20" ] ],
[ "uno:12", "irU:2.l", "cyan", [ "v0", "h10" ] ],
[ "irU:1.l", "uno:GND", "black", [ "h0", "v130", "h-340", "v-20" ] ],
[ "uno:13", "irD:2.l", "cyan", [ "v0", "h10" ] ],
[ "irD:1.l", "uno:GND", "black", [ "h0", "v90", "h-340", "v-20" ] ],
[ "uno:A0", "irC:2.l", "cyan", [ "v0", "h-5" ] ],
[ "irC:1.l", "uno:GND", "black", [ "h0", "v45", "h-310", "v-20" ] ],
[ "uno:A4", "swX:2.r", "orange", [ "v0" ] ],
[ "swX:1.r", "uno:GND", "black", [ "h0" ] ],
[ "uno:A5", "swY:2.r", "orange", [ "v0" ] ],
[ "swY:1.r", "uno:GND", "black", [ "h0" ] ],
[ "uno:A1", "resSearch:1", "yellow", [ "v0" ] ],
[ "resSearch:2", "ledSearch:A", "yellow", [ "v0" ] ],
[ "ledSearch:C", "uno:GND", "black", [ "h0" ] ],
[ "uno:A2", "resTrack:1", "green", [ "v0" ] ],
[ "resTrack:2", "ledTrack:A", "green", [ "v0" ] ],
[ "ledTrack:C", "uno:GND", "black", [ "h0" ] ],
[ "uno:A3", "resLost:1", "red", [ "v0" ] ],
[ "resLost:2", "ledLost:A", "red", [ "v0" ] ],
[ "ledLost:C", "uno:GND", "black", [ "h0" ] ],
[ "uno:5V", "drvX:VCC", "red", [ "h27", "v-115", "h-211" ] ],
[ "uno:5V", "drvY:VCC", "red", [ "h27", "v-15", "h-180" ] ],
[ "uno:GND", "drvX:GND", "black", [ "h20", "v-100", "h-188", "v-15" ] ],
[ "uno:GND", "drvY:GND", "black", [ "h20", "v-100", "h-188", "v85" ] ]
],
"dependencies": {}
}

View File

@ -13,4 +13,4 @@ platform = atmelavr
board = uno
framework = arduino
lib_deps =
arduino-libraries/Stepper@^1.1.3
waspinator/AccelStepper@^1.64

View File

@ -0,0 +1,433 @@
# Arduino IR Tracker with Stepper Motors (Optimized)
**Version:** 2.2.2 (Production Ready Candidate)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
**CRITICAL WARNINGS:**
> **1. HARDWARE VERIFICATION:** Failure to verify **ALL** pin assignments in the code's `SYSTEM CONFIGURATION & TUNING` section against your physical hardware **WILL** lead to malfunction or system halt. Default pins are provided but **MUST** be confirmed.
>
> **2. I2C PIN CONFLICT:** The default pins for the limit switches (`limitSwitchPinX = A4`, `limitSwitchPinY = A5`) directly conflict with the Arduino Uno's I2C pins (SDA/SCL). If you plan to use **ANY** I2C devices (displays, sensors, etc.) with this project, you **MUST** change `limitSwitchPinX` and `limitSwitchPinY` to other unused digital or analog pins in the configuration block.
---
## Table of Contents
1. [Overview](#overview)
2. [Features](#features)
3. [Hardware Requirements](#hardware-requirements)
4. [Wiring Instructions](#wiring-instructions)
- [Stepper Motors & Drivers](#stepper-motors--drivers)
- [IR Sensors](#ir-sensors)
- [Limit Switches](#limit-switches)
- [Status LEDs](#status-leds)
- [Power Supply](#power-supply)
5. [Software Setup](#software-setup)
- [Dependencies](#dependencies)
- [Flashing](#flashing)
6. [Code Configuration](#code-configuration)
- [Build Mode](#build-mode)
- [Pin Definitions](#pin-definitions)
- [Motor & Movement Parameters](#motor--movement-parameters)
- [Tracking Control (P-Controller)](#tracking-control-p-controller)
- [Local Search Parameters](#local-search-parameters)
- [Timing Parameters](#timing-parameters)
- [Stall Detection Parameters](#stall-detection-parameters)
7. [Software Overview](#software-overview)
- [Core Loop](#core-loop)
- [State Machine](#state-machine)
- [Key Algorithms](#key-algorithms)
- [Optimizations](#optimizations)
- [Coordinate System](#coordinate-system)
8. [**CRITICAL: Tuning Guide**](#critical-tuning-guide)
- [Methodology](#methodology)
- [Tuning Order](#tuning-order)
- [1. Speeds & Acceleration](#1-speeds--acceleration)
- [2. Proportional Control (Kp & Deadband)](#2-proportional-control-kp--deadband)
- [3. Stall Detection](#3-stall-detection)
- [4. Timings & Local Search](#4-timings--local-search)
9. [Troubleshooting](#troubleshooting)
10. [Future Improvements](#future-improvements)
11. [License](#license)
---
## Overview
This project implements firmware for an Arduino Uno to control a 2-axis (Pan/Tilt) platform equipped with stepper motors. Its primary goal is to automatically track a moving infrared (IR) light source using an array of five IR sensors (Left, Right, Up, Down, Center).
The system performs automatic homing using limit switches, searches for the IR signal using sweep patterns, tracks the signal using Proportional (P) control logic, and attempts to re-acquire the signal using a local search pattern if it's lost. The code is optimized for performance on the resource-constrained Arduino Uno, utilizing direct port I/O for faster input reading and LED updates, and fixed-point arithmetic for the control calculations.
This firmware is designed for platforms using 28BYJ-48 stepper motors driven by ULN2003 driver boards and common active-LOW IR receiver modules (like TSOP series).
---
## Features
- **2-Axis Stepper Control:** Drives two stepper motors (e.g., 28BYJ-48) via ULN2003 drivers using the `AccelStepper` library for smooth acceleration and non-blocking operation.
- **Automatic Homing:** Uses limit switches on both axes to establish a known starting position (0,0).
- **IR Signal Tracking:** Employs 5 active-LOW IR sensors (L, R, U, D, C) to detect the direction of an IR source.
- **Proportional (P) Control:** Uses fixed-point P-control logic to smoothly move the platform towards the detected IR signal for tracking.
- **Sweep Search:** Performs a configurable wide-angle sweep pattern across both axes if the signal is not initially detected.
- **Local Search:** Initiates a multi-phase local search pattern (probe -> perpendicular sweep -> expanding box) if the signal is lost during tracking, attempting re-acquisition.
- **Stall Detection:** Includes a safety feature to detect if a motor is commanded to move but fails to do so (indicating a stall or obstruction), triggering an error state.
- **State Machine Logic:** Organizes functionality into distinct states (Homing, Sweeping, Centering, Tracking, Local Search, Error, etc.) for robust control flow.
- **Status LEDs:** Provides visual feedback on the current system state (Searching, Tracking, Lost/Error) via LEDs.
- **Performance Optimizations:** Utilizes direct port manipulation for faster I/O and fixed-point math for control calculations, crucial for Arduino Uno performance.
- **Highly Configurable:** Allows tuning of speeds, acceleration, P-control gains, deadbands, search patterns, timings, and pin assignments via constants in the code.
- **Debug/Production Builds:** Supports conditional compilation to enable/disable Serial output for debugging or maximum performance.
---
## Hardware Requirements
1. **Microcontroller:** Arduino Uno R3 (or compatible ATmega328P-based board).
2. **Stepper Motors:** 2 x 28BYJ-48 5V Stepper Motors (or similar, ensure `stepsPerRevolution` is adjusted if different).
3. **Stepper Motor Drivers:** 2 x ULN2003 Driver Boards (or equivalent darlington array driver).
4. **IR Sensors:** 5 x Active-LOW IR Receiver Modules (e.g., TSOP38238, TSOP4838, VS1838B). Must output LOW when IR is detected. Ensure they operate at the correct voltage (usually 3.3V or 5V).
5. **Limit Switches:** 2 x Mechanical Limit Switches (Microswitches preferred). Configuration assumes Normally Open (NO) wiring connected to GND and Signal pins (Active LOW when pressed).
6. **Status LEDs:** 3 x Standard LEDs (e.g., Red, Yellow, Green) with appropriate current-limiting resistors (e.g., 220-330 Ohm for 5V).
7. **Power Supply:**
- **CRITICAL:** A stable **external 5V power supply** capable of providing sufficient current for **both** stepper motors running simultaneously (e.g., 1A-2A minimum, depending on motor draw). **Do not** attempt to power the motors directly from the Arduino's 5V pin or USB, as this will cause instability and potentially damage the Arduino.
- The Arduino itself can be powered via USB or a separate 7-12V supply into the VIN pin/barrel jack.
8. **Jumper Wires & Breadboard (Optional):** For connections.
9. **Pan/Tilt Mechanism:** A physical structure to mount the motors, sensors, and Arduino.
---
## Wiring Instructions
**IMPORTANT:** Double-check all connections before applying power. Refer to the `Pin Definitions` section in the code configuration and verify these defaults match your wiring.
### Stepper Motors & Drivers
- Connect the 28BYJ-48 motor connector to the output connector on its corresponding ULN2003 board.
- Connect the ULN2003 board's VCC/+ and GND/- pins to your **external 5V power supply**.
- Connect the ULN2003 board's IN1, IN2, IN3, IN4 pins to the Arduino pins defined for the respective motor (X or Y).
- **X-Axis Motor (Defaults):**
- ULN2003 IN1 -> Arduino D8 (`stepperX_pin1`)
- ULN2003 IN2 -> Arduino D10 (`stepperX_pin2`)
- ULN2003 IN3 -> Arduino D9 (`stepperX_pin3`)
- ULN2003 IN4 -> Arduino D11 (`stepperX_pin4`)
- _(Note the AccelStepper library sequence P1,P3,P2,P4)_
- **Y-Axis Motor (Defaults):**
- ULN2003 IN1 -> Arduino D4 (`stepperY_pin1`)
- ULN2003 IN2 -> Arduino D6 (`stepperY_pin2`)
- ULN2003 IN3 -> Arduino D5 (`stepperY_pin3`)
- ULN2003 IN4 -> Arduino D7 (`stepperY_pin4`)
- _(Note the AccelStepper library sequence P1,P3,P2,P4)_
### IR Sensors
- Connect the VCC pin of each sensor to Arduino 5V (or 3.3V if required by the sensor module).
- Connect the GND pin of each sensor to Arduino GND.
- Connect the OUT/Signal pin of each sensor to the corresponding Arduino digital input pin:
- Left Sensor OUT -> Arduino D2 (`sensorPinL`)
- Right Sensor OUT -> Arduino D3 (`sensorPinR`)
- Up Sensor OUT -> Arduino D12 (`sensorPinU`)
- Down Sensor OUT -> Arduino D13 (`sensorPinD`)
- Center Sensor OUT -> Arduino A0 (`sensorPinC`)
### Limit Switches
- The code assumes Active LOW switches connected between the Arduino pin and GND.
- Connect one terminal of the switch (usually COM - Common) to Arduino GND.
- Connect the **Normally Open (NO)** terminal of the switch to the corresponding Arduino digital input pin. (The internal pull-up resistor on the Arduino pin will keep it HIGH until the switch is pressed, pulling it LOW).
- X-Axis Limit Switch (NO) -> Arduino A4 (`limitSwitchPinX`) **(!!! I2C CONFLICT !!!)**
- Y-Axis Limit Switch (NO) -> Arduino A5 (`limitSwitchPinY`) **(!!! I2C CONFLICT !!!)**
- _(If using Normally Closed (NC) contacts, you'll need to connect COM to 5V, NC to the pin, and potentially disable the internal pull-up and invert the logic in `readInputsOptimized`)_.
### Status LEDs
- Connect the **Anode** (longer lead) of each LED to the corresponding Arduino digital output pin _through_ its current-limiting resistor (e.g., 220 Ohm).
- Connect the **Cathode** (shorter lead, flat side) of each LED to Arduino GND.
- Searching LED Anode (+ resistor) -> Arduino A1 (`ledPinSearching`)
- Tracking LED Anode (+ resistor) -> Arduino A2 (`ledPinTracking`)
- Lost/Error LED Anode (+ resistor) -> Arduino A3 (`ledPinLost`)
### Power Supply
- Connect the **External 5V Power Supply Positive (+)** output to the VCC/+ pins of **both** ULN2003 driver boards.
- Connect the **External 5V Power Supply Ground (-)** output to the GND/- pins of **both** ULN2003 driver boards **AND** to one of the Arduino GND pins. **This common ground connection is essential!**
- Power the Arduino board itself via its USB port or a separate 7-12V supply connected to the VIN pin or barrel jack.
---
## Software Setup
### Dependencies
1. **Arduino IDE** or **PlatformIO:** Install your preferred development environment.
2. **AccelStepper Library:** This library is required.
- **Arduino IDE:** Go to `Sketch` -> `Include Library` -> `Manage Libraries...`. Search for "AccelStepper" by Mike McCauley and install it.
- **PlatformIO:** Add `AccelStepper` to the `lib_deps` list in your `platformio.ini` file (e.g., `lib_deps = mikemccauley/AccelStepper @ ^1.61`). PlatformIO will usually install it automatically on build.
### Flashing
1. **Download/Clone:** Get the project code (`IR_Tracker_Optimized_ProdReady.ino` or similar).
2. **Configure:** **CRITICALLY REVIEW AND EDIT** the parameters in the `SYSTEM CONFIGURATION & TUNING` section of the code, especially the `Pin Definitions`, to match your exact hardware wiring. Resolve the I2C conflict if necessary.
3. **Open:** Load the `.ino` file in your Arduino IDE or open the project folder in PlatformIO.
4. **Select Board & Port:**
- **Arduino IDE:** Go to `Tools` -> `Board` and select "Arduino Uno". Go to `Tools` -> `Port` and select the correct serial port for your connected Arduino.
- **PlatformIO:** Ensure your `platformio.ini` specifies `board = uno`. PlatformIO usually auto-detects the port.
5. **Upload:**
- **Arduino IDE:** Click the "Upload" button (right arrow icon).
- **PlatformIO:** Use the "Upload" task (e.g., click the alien head icon in the VS Code status bar and select `PlatformIO: Upload`).
6. **Monitor (Optional):** If `DEBUG_BUILD` is defined in the code, open the Serial Monitor (Arduino IDE: `Tools` -> `Serial Monitor`; PlatformIO: `PlatformIO: Serial Monitor` task) and set the baud rate to **115200**. You should see startup messages and telemetry data.
---
## Code Configuration
All user-configurable parameters are located at the top of the `.ino` file within the `SYSTEM CONFIGURATION & TUNING` section. **Tuning these values is essential for proper operation.**
### Build Mode
- `#define DEBUG_BUILD`: Uncomment this line to enable detailed `Serial` output for debugging, state changes, and telemetry. Comment it out for a production build (disables most `Serial` calls for maximum performance and reduced code size).
### Pin Definitions
- `stepperX_pin1`, `stepperX_pin3`, `stepperX_pin2`, `stepperX_pin4`: Arduino pins connected to the X-axis ULN2003 driver (IN1, IN3, IN2, IN4 respectively due to AccelStepper).
- `stepperY_pin1`, `stepperY_pin3`, `stepperY_pin2`, `stepperY_pin4`: Arduino pins connected to the Y-axis ULN2003 driver.
- `sensorPinL`, `sensorPinR`, `sensorPinU`, `sensorPinD`, `sensorPinC`: Arduino pins connected to the respective IR sensor output pins.
- `limitSwitchPinX`, `limitSwitchPinY`: Arduino pins connected to the limit switches. **WARNING: Defaults A4/A5 conflict with I2C!**
- `ledPinSearching`, `ledPinTracking`, `ledPinLost`: Arduino pins connected to the status LEDs (via resistors).
### Motor & Movement Parameters
- `stepsPerRevolution`: Total steps for one full 360° motor rotation (2038 for 28BYJ-48 in half-step mode).
- `homingSpeed`, `homingAcceleration`: Speed/acceleration during the homing sequence. Must be reliable enough to trigger switches without stalling.
- `sweepSpeedDefault`, `sweepAccelerationDefault`: Speed/acceleration during the wide-angle sweep search. Can be faster than homing/tracking if the motors handle it.
- `trackSpeedDefault`, `trackAccelerationDefault`: Base speed/acceleration used during centering and tracking adjustments. Higher values react faster but may overshoot.
- `localSearchSpeed`, `localSearchAccel`: Speed/acceleration during the local search patterns. Often slower for more precise small movements.
- `searchRangeX_deg`, `searchRangeY_deg`: Maximum range (in degrees) the system will sweep during the initial search.
- `postHomingMoveOffSwitchSteps`: Small number of steps to move away from the limit switch immediately after it's triggered during homing. Prevents sitting directly on the switch.
- `HOMING_OVERSHOOT_MULTIPLIER`: Used internally to calculate the initial large move distance during homing to ensure the switch is definitely hit.
### Tracking Control (P-Controller)
- `Kp_X_float`, `Kp_Y_float`: **CRITICAL TUNING PARAMETERS.** Proportional gain for each axis. Determines how strongly the system reacts to tracking errors. Start low (e.g., 5.0-10.0) and increase carefully. Too high causes oscillation/overshoot; too low causes sluggish response/loss of target.
- `trackingDeadband_steps`: **CRITICAL TUNING PARAMETER.** A zone (in steps) around the target position where small errors are ignored. Prevents jitter/hunting when centered. Too high makes centering less precise; too low causes constant small adjustments.
### Local Search Parameters
- `localSearchProbeSteps`: Initial distance (steps) to move in the last known direction of the target when signal is lost. Tune based on sensor beam width and expected target speed.
- `localSearchSweepSteps`: Distance (steps) for the perpendicular sweeps in the local search cross pattern.
- `localSearchBoxStepSize`: Step size for each leg of the expanding box pattern.
- `localSearchMaxCycles`: Number of full box layers to attempt before giving up local search and reverting to a full sweep.
### Timing Parameters
- `debounceDelayMs`, `limitSwitchDebounceMs`: Software debounce time for sensors and switches to filter out noise/bouncing. Increase if inputs seem flaky.
- `signalLostTimeoutMs`: Duration (ms) the system will wait in TRACKING state without any sensor input before declaring the signal lost and entering LOCAL_SEARCH.
- `searchFailDelayMs`: Delay (ms) before retrying a full sweep after completing one without finding the signal (`SEARCH_FAILED` state).
- `stateTimeoutMs`, `homingTimeoutMs`, `localSearchTimeoutMs`: Safety timeouts (ms) for specific states to prevent the system from getting stuck indefinitely. Triggers an error if exceeded.
- `telemetryIntervalMs`: How often (ms) telemetry data is printed to Serial (if `ENABLE_TELEMETRY` is active).
- `postHomingPauseMs`: Brief pause (ms) after successful homing before starting the first search sweep.
### Stall Detection Parameters
- `stallCheckIntervalMs`: How frequently (ms) the code checks for potential motor stalls.
- `stallTimeoutThresholdMs`: Duration (ms) a motor must appear stuck (not moving despite being commanded) before a stall error is triggered.
- `stallPositionTolerance`: Minimum number of steps the motor position must change between checks to be considered _not_ stalled. Increase slightly if getting false positives during slow moves or vibration.
---
## Software Overview
### Core Loop
The `loop()` function performs the essential tasks repeatedly:
1. **`stepperX.run()` / `stepperY.run()`:** Calls the AccelStepper library functions to manage motor stepping, acceleration, and non-blocking movement based on current targets. **This is critical and must be called frequently.**
2. **`readInputsOptimized()`:** Reads the state of all IR sensors and limit switches using direct port I/O and applies software debouncing. Updates global state variables (`sensorActive[]`, `limitSwitchActiveX/Y`).
3. **Update `lastSignalTime`:** If in TRACKING or CENTERING state and a sensor is active, updates the timestamp of the last known signal detection.
4. **`handleStateMachine()`:** The core logic router. Based on the `currentState`, it calls the appropriate function or performs actions for that state (e.g., `handleHomingStates`, `handleTrackingLogicFixedPoint`, `handleLocalSearch`).
5. **`updateStatusLEDsOptimized()`:** Sets the status LEDs according to the `currentState` using direct port I/O.
6. **`checkMotorStall()`:** Periodically checks if motors appear stalled.
7. **`checkStateTimeouts()`:** Checks if certain states have run longer than their allowed safety timeout.
8. **`printTelemetry()`:** (Conditional) Prints status information to the Serial Monitor at regular intervals if `ENABLE_TELEMETRY` is defined.
### State Machine
The system operates based on a finite state machine (`enum class State`). This ensures predictable behavior and handles different operational modes cleanly. The primary states and transitions are:
- **INITIALIZING -> CHECK_PIN_CONFLICTS:** Initial power-up state.
- **CHECK_PIN_CONFLICTS -> HOMING_X:** After setup and pin checks.
- **HOMING_X -> POST_HOMING_MOVE_OFF_X -> HOMING_Y -> POST_HOMING_MOVE_OFF_Y -> POST_HOMING_DELAY:** Sequence to home both axes using limit switches.
- **POST_HOMING_DELAY -> SWEEP_X:** Homing complete, start searching.
- **SWEEP_X:** Sweeps the X-axis back and forth.
- If signal found -> **CENTERING**
- If full X sweep complete (no signal) -> **SWEEP_Y**
- **SWEEP_Y:** Sweeps the Y-axis back and forth.
- If signal found -> **CENTERING**
- If full Y sweep complete (no signal) -> **SEARCH_FAILED**
- **CENTERING:** Signal detected, performing fine adjustments to center the target using tracking logic.
- If move completes and signal still present -> **TRACKING**
- If move completes and signal lost -> **LOCAL_SEARCH**
- **TRACKING:** Continuously adjusts position to keep the signal centered using tracking logic.
- If signal lost (timeout) -> **LOCAL_SEARCH**
- **LOCAL_SEARCH:** Signal lost during tracking, executing probe/sweep/box patterns to re-acquire.
- If signal re-acquired -> **CENTERING**
- If local search times out or exhausts patterns -> **SWEEP_X** (restart full search)
- **SEARCH_FAILED:** Full sweep completed without finding signal. Pauses, then restarts search -> **SWEEP_X**.
- **(Any State) -> ERROR:** Triggered by fatal errors (stall detected, safety timeout, config error). System halts.
### Key Algorithms
- **Fixed-Point P-Control (`handleTrackingLogicFixedPoint`)**: Calculates an error value based on which IR sensors are active. This error (represented as a small integer) is multiplied by a pre-scaled gain (`Kp_X/Y_scaled`) using integer math. The result is scaled back down (using bit-shifting) to determine the number of steps to move. A deadband ignores very small calculated moves.
- **Local Search (`handleLocalSearch`)**: A multi-phase strategy:
1. _Probe:_ Moves a short distance in the direction the target was last seen.
2. _Sweep:_ Performs a cross-pattern sweep perpendicular to the last known direction.
3. _Box:_ If the above fail, executes an expanding square spiral pattern.
- **Direct Port I/O (`readInputsOptimized`, `updateStatusLEDsOptimized`)**: Reads multiple input pins or writes multiple output pins belonging to the same AVR port (PORTB, PORTC, PORTD) in a single operation using bit masks, significantly faster than multiple `digitalRead`/`digitalWrite` calls.
### Optimizations
- **Non-Blocking Stepper Control:** `AccelStepper` library handles movement without blocking the main loop.
- **Direct Port I/O:** Faster reading of sensors/switches and writing to LEDs.
- **Fixed-Point Math:** Avoids slow floating-point calculations in the time-critical tracking loop.
- **Conditional Serial Output:** `Serial` printing is computationally expensive; disabling it via `DEBUG_BUILD` provides a significant performance boost.
- **`F()` Macro / PROGMEM:** Stores constant strings in Flash memory instead of RAM.
- **Optimized Data Types:** Uses smallest appropriate data types (`uint8_t`, etc.) where possible.
- **Inlining:** Suggests inlining for small utility functions.
### Coordinate System
- **Origin (0, 0):** Defined by the limit switches during homing. Typically the bottom-left or top-left corner depending on switch placement and motor direction.
- **X-Axis:** Positive steps generally move the platform Right. Negative steps move Left.
- **Y-Axis:** Positive steps generally move the platform Down. Negative steps move Up.
_(Note: This depends on motor wiring and mounting. If movement is inverted, you might need to reverse the motor direction logic or simply invert the sign in the P-control calculations if that's easier.)_
---
## **CRITICAL: Tuning Guide**
**This system WILL NOT WORK correctly or reliably without careful tuning.** Default values are starting points ONLY and are unlikely to be optimal for your specific hardware (motors, power supply, mechanism friction, sensor spacing, IR source characteristics).
### Methodology
1. **Patience:** Tuning takes time and observation.
2. **One Parameter at a Time:** Change only _one_ value, upload, test its effect, then decide on the next change. Changing multiple values makes it impossible to know what caused an improvement or regression.
3. **Observe & Listen:** Watch the physical movement. Is it smooth? Jerky? Does it overshoot? Listen to the motors. Do they sound strained or skip steps (clicking noise)?
4. **Use Serial Monitor (with `DEBUG_BUILD`):** Enable `DEBUG_BUILD` during tuning. The telemetry output (`TLM: ...`) provides valuable information about the current state, position, and sensor readings.
5. **Start Conservatively:** Begin with lower speeds, accelerations, and Kp values, then gradually increase them.
### Tuning Order
Follow this general order for best results:
1. **Speeds & Acceleration:** Find the physical limits of your motors and mechanism.
2. **Proportional Control (Kp & Deadband):** Tune the tracking responsiveness and stability. This is the most critical part for tracking performance.
3. **Stall Detection:** Fine-tune the safety net once movement is generally reliable.
4. **Timings & Local Search:** Adjust timeouts and search patterns based on observed performance.
### 1. Speeds & Acceleration
- **Goal:** Find the highest `...Speed...` and `...Acceleration...` values your motors can reliably handle without stalling (buzzing without turning) or skipping steps under load.
- **Parameters:** `homingSpeed`, `homingAcceleration`, `sweepSpeedDefault`, `sweepAccelerationDefault`, `trackSpeedDefault`, `trackAccelerationDefault`, `localSearchSpeed`, `localSearchAccel`.
- **Testing:**
- Start with low values (e.g., Speed=300, Accel=300).
- Trigger a long move (like the initial sweep after homing). Listen carefully.
- Gradually increase Speed first. If it stalls, reduce speed and try increasing Acceleration. Sometimes higher acceleration helps overcome initial friction, but too high also causes stalls.
- Find the maximum reliable values for sweep/homing first. `trackSpeed` can often be slightly lower for smoother fine adjustments. `localSearchSpeed` is usually slower still.
- **Symptoms:**
- _Too High:_ Motors stall, buzz, vibrate heavily, skip steps (lose position accuracy).
- _Too Low:_ Movement is unnecessarily slow.
### 2. Proportional Control (Kp & Deadband)
- **Goal:** Achieve smooth, responsive tracking that centers accurately without oscillation (shaking) or excessive jitter.
- **Parameters:** `Kp_X_float`, `Kp_Y_float`, `trackingDeadband_steps`.
- **Testing:**
- Get the system into the `TRACKING` state (have it find and lock onto the IR source).
- Slowly move the IR source side-to-side and up-and-down across the sensors.
- Observe how the platform reacts.
- **Start with `Kp` LOW (e.g., 5.0-10.0)** and `trackingDeadband_steps` relatively low (e.g., 2-5).
- **Symptoms & Adjustments:**
- **Oscillation/Shaking:** Target bounces back and forth past the center. -> **Decrease `Kp_float`** for the relevant axis.
- **Overshooting:** Moves quickly past the center, then corrects back. -> **Decrease `Kp_float`**. Possibly increase `trackAccelerationDefault` if the overshoot happens _during_ deceleration (less common).
- **Sluggish/Slow Response:** Takes a long time to center, might lose target easily if it moves quickly. -> **Increase `Kp_float`** slowly. Ensure `trackSpeedDefault` is adequate.
- **Jitter/Hunting when Centered:** Constantly making tiny movements back and forth even when the target seems centered. -> **Increase `trackingDeadband_steps`**. `Kp_float` might also be slightly too high.
- **Large Dead Zone:** Target has to move significantly off-center before the platform reacts. -> **Decrease `trackingDeadband_steps`**.
- **Note:** Kp and Deadband interact. You may need to adjust both iteratively. Tuning Kp is usually the primary focus.
### 3. Stall Detection
- **Goal:** Reliably detect genuine motor stalls without triggering falsely during normal, possibly slow or jerky, operation.
- **Parameters:** `stallCheckIntervalMs`, `stallTimeoutThresholdMs`, `stallPositionTolerance`.
- **Testing:**
- Operate the system normally after tuning speeds and P-control.
- If you get unexpected "Stall Detected" errors during normal moves:
- Try increasing `stallTimeoutThresholdMs` (gives it more time to recover).
- Try increasing `stallPositionTolerance` slightly (allows for more vibration/tiny movements).
- Less likely, slightly increase `stallCheckIntervalMs`.
- If you physically stall a motor (carefully!) and it's _not_ detected:
- Decrease `stallTimeoutThresholdMs`.
- Ensure `stallPositionTolerance` is not too high.
- Verify your `...Speed...` and `...Acceleration...` values aren't simply too high, causing the stall in the first place (see Step 1).
- **Note:** This is a safety net. Frequent stalls usually mean speeds/accel are too high or there's a mechanical issue.
### 4. Timings & Local Search
- **Goal:** Optimize how the system handles signal loss and searching based on your environment and IR source.
- **Parameters:** `signalLostTimeoutMs`, `localSearch...` parameters, `searchFailDelayMs`.
- **Testing:**
- Observe behavior when the IR signal is temporarily blocked or moves out of range.
- If it enters `LOCAL_SEARCH` too quickly for brief dropouts -> Increase `signalLostTimeoutMs`.
- If `LOCAL_SEARCH` fails to re-acquire a nearby target -> Adjust `localSearchProbeSteps`, `localSearchSweepSteps`, `localSearchBoxStepSize`. Make steps larger if the target might have moved further; smaller for finer searching. Consider increasing `localSearchMaxCycles`.
- If `LOCAL_SEARCH` takes too long before giving up -> Decrease `localSearchTimeoutMs` or `localSearchMaxCycles`.
- Adjust `searchFailDelayMs` based on how long you want to wait before a full retry after a failed search.
---
## Troubleshooting
- **No Serial Output:**
- Ensure `DEBUG_BUILD` is uncommented in the code.
- Verify Serial Monitor baud rate is set to 115200.
- Check Arduino connection, port selection in IDE/PlatformIO.
- Check Arduino power.
- **System Doesn't Start / No LEDs:**
- Check Arduino power supply (USB or VIN).
- Check wiring, especially power and ground connections.
- **Motors Buzz/Vibrate, Don't Turn:**
- **Insufficient Power:** External 5V supply cannot provide enough current for the motors. Use a higher current rating supply.
- **Wiring Error:** Double-check ULN2003 IN1-4 connections to Arduino pins match the code _exactly_. Order matters! Check motor connector is fully seated.
- **Speed/Acceleration Too High:** Lower the `...Speed...` and `...Acceleration...` values significantly and try again.
- **Mechanical Binding:** Ensure the platform moves freely by hand.
- **Homing Fails:**
- **Switch Not Triggered:** Check limit switch wiring (GND to COM, Arduino Pin to NO). Check physical actuation. Decrease `homingSpeed` if moving too fast past switch. Increase `HOMING_OVERSHOOT_MULTIPLIER` move if it stops short. Check `homingTimeoutMs`.
- **Doesn't Move Off Switch:** Increase `postHomingMoveOffSwitchSteps`. Check switch isn't stuck. Verify switch logic is Active LOW in code (`readInputsOptimized`).
- **Tracking Issues (Oscillation, Sluggish, Jitter):**
- See the **Proportional Control (Kp & Deadband)** tuning section above. This requires careful adjustment.
- **False Stall Detection:**
- See the **Stall Detection** tuning section above. Adjust timeouts and tolerance. Check speeds/mechanics.
- **Stall Not Detected:**
- See the **Stall Detection** tuning section. Decrease timeout. Verify speeds aren't too high.
- **I2C Devices Not Working:**
- **PIN CONFLICT!** You are likely using limit switches on A4/A5. You **MUST** change the `limitSwitchPinX` and `limitSwitchPinY` definitions in the code to unused pins and rewire accordingly.
- **Movement Direction Reversed:**
- Easiest fix: In `handleTrackingLogicFixedPoint`, invert the sign of the calculated `stepsToMoveX` or `stepsToMoveY` if only one axis is reversed. Alternatively, physically reverse the connection order for the problematic motor on the ULN2003 board (e.g., swap IN1<->IN4, IN2<->IN3) or reverse the motor connector itself if possible.
---
## Future Improvements
- **Smoother Acceleration Profiles:** Implement S-curve acceleration instead of linear acceleration (AccelStepper might support this or require modification/alternative library).
- **PID Control:** Replace the P-controller with a full PID (Proportional-Integral-Derivative) controller for potentially better handling of overshoot and steady-state error, though tuning becomes more complex.
- **Adaptive Tuning:** Implement logic to adjust Kp or other parameters dynamically based on performance.
- **Improved Sensor Filtering:** Use more advanced digital filtering techniques on sensor readings if noise is an issue.
- **Web Interface/Remote Control:** Add an ESP8266 or ESP32 for Wi-Fi connectivity, allowing remote monitoring, control, and tuning via a web interface.
- **Support for Different Hardware:** Abstract hardware interactions (pin definitions, sensor reading logic) to make porting to different microcontrollers or sensors easier.
- **Obstacle Avoidance:** Integrate distance sensors to prevent collisions.
- **Target Prediction:** Implement basic prediction algorithms (e.g., Kalman filter) if tracking fast-moving targets.
---
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file (if included) or the link below for details.
[https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
[wokwi]
version=1
firmware='.pio/build/uno/firmware.hex'
elf='.pio/build/uno/firmware.elf'