8.7 KiB
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 positionget_position(): Get the raw positionset_scale(scale): Set the scale factorget_scale(): Get the scale factorset_offset(offset): Set the offsetget_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 diameterUpdated.POS_X (0): X position changedUpdated.POS_Z (1): Z position changedUpdated.X_MODE (2): X mode (radius/diameter) changedUpdated.FULLSCREEN (3): Fullscreen state changed
Key Methods:
attach(observer): Register an observer (typically the View)notify(updated): Notify all observers of changessteps_to_position(axis, steps): Convert encoder steps to millimetersset_position(axis, pos): Update a position (notifies if changed)set_offset(axis, offset): Set the zero offset for calibrationget_effective_position(axis): Get position with offset applied; converts diameter to radius if neededset_toggle_x_mode(): Toggle between radius and diameter displayset_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 I2Chandle_x_position_update(steps): Process X encoder datahandle_z_position_update(steps): Process Z encoder datahandle_btn_x0_press(): Set X zero point (sets offset to -current position)handle_btn_z0_press(): Set Z zero pointhandle_btn_toggle_x_mode(): Switch between radius/diameter displayhanlde_btn_x(): Open dialog to manually set X positionhanlde_btn_z(): Open dialog to manually set Z positiontoggle_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
python3 dro.py
Command Line Options
# 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
- Startup: Application connects to Arduino via I2C and displays current encoder positions
- Zero Axes: Press X_0 and Z_0 buttons at the tool's starting position
- Move Tool: As you move the tool, positions update in real-time
- Set Position: Click X or Z button to set a specific position (useful for manual adjustments)
- Toggle Mode: Press r/D to switch X axis between radius and diameter display
- 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
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)sudoaccess for shutdown command
Error Handling
The application gracefully handles several error conditions:
I2C Communication Errors
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:
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()andhanlde_btn_z()should behandle_btn_x()andhandle_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