Files
pi-dro/docs/DRO.md
2026-03-21 23:51:53 +01:00

8.7 KiB
Raw Blame History

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

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

  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

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

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() 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