adding files

This commit is contained in:
2026-03-21 23:51:53 +01:00
commit 8cb0240ca2
19 changed files with 2162 additions and 0 deletions

282
docs/DRO.md Normal file
View File

@@ -0,0 +1,282 @@
# Digital Read Out (DRO) Application
The `dro.py` file implements a Digital Read Out system for a lathe. It displays the current position of the lathe tool on two axes (X and Z) by reading encoder data from an Arduino via I2C communication.
## Overview
The application follows the **Model-View-Controller (MVC)** architectural pattern, which separates concerns:
- **Model**: Manages the state of the axes and their positions
- **View**: Displays the positions in a Tkinter GUI
- **Controller**: Handles user input and I2C communication
## Architecture
### Data Flow
```
Arduino (Encoder Data)
↓ (I2C)
Controller (Serial Communication) → Model (Position Calculation)
↓ ↓
View (GUI Update) ← ← ← ← ← ← ← ← ← ← Observers
```
## Classes
### Axis
Represents a single axis (X or Z) with position, scale, and offset.
**Attributes:**
- `_position`: Current position in mm
- `_scale`: Scale factor (mm per encoder step)
- `_offset`: Offset applied to position (mm)
- `_name`: Axis identifier ('x' or 'z')
**Methods:**
- `set_position(pos)`: Set the raw position
- `get_position()`: Get the raw position
- `set_scale(scale)`: Set the scale factor
- `get_scale()`: Get the scale factor
- `set_offset(offset)`: Set the offset
- `get_offset()`: Get the offset
### Model
Manages the state of both axes and notifies observers of changes. Implements the **Observer pattern** for MVC binding.
**Enums:**
- `XMode.RADIUS (0)`: X axis displays radius (default)
- `XMode.DIAMETER (1)`: X axis displays diameter
- `Updated.POS_X (0)`: X position changed
- `Updated.POS_Z (1)`: Z position changed
- `Updated.X_MODE (2)`: X mode (radius/diameter) changed
- `Updated.FULLSCREEN (3)`: Fullscreen state changed
**Key Methods:**
- `attach(observer)`: Register an observer (typically the View)
- `notify(updated)`: Notify all observers of changes
- `steps_to_position(axis, steps)`: Convert encoder steps to millimeters
- `set_position(axis, pos)`: Update a position (notifies if changed)
- `set_offset(axis, offset)`: Set the zero offset for calibration
- `get_effective_position(axis)`: Get position with offset applied; converts diameter to radius if needed
- `set_toggle_x_mode()`: Toggle between radius and diameter display
- `set_scale(axis, scale)`: Set the steps-to-mm conversion factor
**Special Behavior:**
- Position updates only trigger notifications if the change is ≥ 0.005 mm (prevents chatter)
- When X axis is in DIAMETER mode, displayed position is doubled
### Controller
Handles user interaction and I2C communication with the Arduino.
**Hardware:**
- I2C Bus: 1 (standard Raspberry Pi I2C)
- Arduino Address: 0x08
**Data Format:**
- Reads 4 bytes from Arduino every poll cycle
- Bytes 0-1: X position (little-endian signed 16-bit integer)
- Bytes 2-3: Z position (little-endian signed 16-bit integer)
**Methods:**
- `poll_i2c_data()`: Read encoder positions from Arduino via I2C
- `handle_x_position_update(steps)`: Process X encoder data
- `handle_z_position_update(steps)`: Process Z encoder data
- `handle_btn_x0_press()`: Set X zero point (sets offset to -current position)
- `handle_btn_z0_press()`: Set Z zero point
- `handle_btn_toggle_x_mode()`: Switch between radius/diameter display
- `hanlde_btn_x()`: Open dialog to manually set X position
- `hanlde_btn_z()`: Open dialog to manually set Z position
- `toggle_fullscreen()`: Toggle fullscreen display (F11)
- `end_fullscreen()`: Exit fullscreen mode (Escape)
- `handle_btn_calc()`: Launch system calculator (galculator)
- `shutdown()`: Shutdown the system (produces clean shutdown)
### View
Tkinter GUI displaying positions, buttons, and status. Acts as an observer of the Model.
**GUI Layout:**
```
┌─────────────────────────────────┐
│ X Position │ [X] [X_0] │
│ Z Position │ [Z] [Z_0] │
│ Mode │ │
│ │ [r/D] [Calc] [Off]│
└─────────────────────────────────┘
```
**Display Features:**
- Large font (80pt) for easy reading from distance
- Green text on black background for visibility
- Real-time position updates at 10 Hz
- Mode indicator (R for radius, D for diameter)
**Button Functions:**
- **X / Z**: Open dialog to set position
- **X_0 / Z_0**: Set zero point (calibrate)
- **r/D**: Toggle radius/diameter mode
- **Calc**: Launch calculator
- **Power**: Shutdown system
**Keyboard Shortcuts:**
- **F11**: Toggle fullscreen
- **Escape**: Exit fullscreen
- **A/Z** (test mode): Rotate X encoder clockwise/counterclockwise
- **S/X** (test mode): Rotate Z encoder clockwise/counterclockwise
## Usage
### Basic Startup
```bash
python3 dro.py
```
### Command Line Options
```bash
# Maximize window on startup
python3 dro.py --zoomed
# Run in test mode (no I2C communication, use keyboard to simulate encoders)
python3 dro.py --test
# Both options
python3 dro.py --zoomed --test
```
### Typical Workflow
1. **Startup**: Application connects to Arduino via I2C and displays current encoder positions
2. **Zero Axes**: Press X_0 and Z_0 buttons at the tool's starting position
3. **Move Tool**: As you move the tool, positions update in real-time
4. **Set Position**: Click X or Z button to set a specific position (useful for manual adjustments)
5. **Toggle Mode**: Press r/D to switch X axis between radius and diameter display
6. **Shutdown**: Click the power button or use the system menu
### Offset/Zero Calibration
The offset mechanism allows setting the zero point at any position:
```
Displayed Position = Raw Position + Offset
```
When you press X_0 or Z_0, the system sets:
```
Offset = -Current_Position
```
This makes the current display read zero.
### Radius vs Diameter Mode
The X axis can display in two modes:
- **Radius (r)**: Shows actual distance from spindle centerline
- **Diameter (D)**: Shows diameter (actual = radius × 2)
The Model automatically converts when displaying:
```
Diameter Display = Radius × 2
```
## Configuration
### Default Scales
```python
model.set_scale('x', -2.5/200) # -12.5 mm per 1000 steps (negative = flip direction)
model.set_scale('z', 90/1000) # 0.09 mm per step
```
These calibration values should be adjusted based on your encoder specifications:
- Negative X scale flips the direction to match lathe conventions
- Scale = (Distance Travel in mm) / (Encoder Steps)
### Update Frequency
- GUI updates every 100ms (10 Hz)
- I2C polling rate matches GUI updates
## Dependencies
### Python Packages
- `tkinter`: GUI framework (built-in with Python)
- `smbus2`: I2C communication (falls back to fake_smbus.py if not available)
- Standard library: `enum`, `subprocess`, `getopt`, `sys`, `shutil`, `logging`
### Hardware
- Raspberry Pi (tested on Pi 4)
- Arduino Nano (with I2C firmware at address 0x08)
- 2 Rotary encoders connected to Arduino
### System
- `galculator`: Calculator application (optional)
- `sudo` access for shutdown command
## Error Handling
The application gracefully handles several error conditions:
### I2C Communication Errors
```python
try:
data = self._bus.read_i2c_block_data(self._address, 0, 4)
except IOError as e:
logger.error(f"I2C read error: {e}")
except OSError as e:
logger.error(f"I2C OS error: {e}")
```
Errors are logged but don't crash the application. The last known position is retained.
### Missing Dependencies
If `smbus2` is not installed, the application falls back to the local `RPi/fake_smbus.py`:
```python
try:
import smbus2
except ImportError:
import RPi.fake_smbus as smbus2
logger.warning('smbus2 not available; using fake smbus2.py')
```
## Testing Mode
The `--test` flag enables keyboard simulation of encoder rotation:
```
X Axis:
A = Rotate clockwise (increase X)
Z = Rotate counterclockwise (decrease X)
Z Axis:
S = Rotate clockwise (increase Z)
X = Rotate counterclockwise (decrease Z)
```
This allows testing the GUI without physical encoders or Arduino.
## Known Issues / Notes
- Method name typo: `hanlde_btn_x()` and `hanlde_btn_z()` should be `handle_btn_x()` and `handle_btn_z()`
- Position updates use a 0.005 mm hysteresis threshold (prevents noise-induced updates)
- Fullscreen mode requires the Tkinter window to support the platform's fullscreen API
## Performance Considerations
- GUI updates are non-blocking and scheduled on the Tkinter event loop
- I2C reads are synchronous and run every 100ms (10 Hz update rate)
- Position filtering reduces redundant updates by ~99% in typical operation
- StringVar widgets minimize GUI redraws by only updating when values change
## Future Enhancements
- Add tool offset/wear compensation
- Support more than 2 axes
- Persistent configuration file for scales and offsets
- Network interface for remote monitoring
- Data logging of position history