adding files
This commit is contained in:
228
docs/ARDUINO_I2C_PROTOCOL.md
Normal file
228
docs/ARDUINO_I2C_PROTOCOL.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# Arduino I2C Protocol
|
||||
|
||||
This document describes the I2C communication protocol between the Raspberry Pi (DRO application) and the Arduino (encoder interface).
|
||||
|
||||
## Hardware Setup
|
||||
|
||||
### I2C Configuration
|
||||
- **Bus**: I2C Bus 1 (standard Raspberry Pi I2C)
|
||||
- **Arduino Slave Address**: 0x08
|
||||
- **Data Rate**: Standard (100 kHz) or Fast (400 kHz)
|
||||
- **Pull-up Resistors**: 4.7 kΩ (typical I2C standards)
|
||||
|
||||
### Connections
|
||||
```
|
||||
Raspberry Pi Arduino
|
||||
GPIO2 (SDA) <-> SDA
|
||||
GPIO3 (SCL) <-> SCL
|
||||
GND <- GND
|
||||
5V (optional)-> 5V (if Arduino powered separately)
|
||||
```
|
||||
|
||||
## Data Format
|
||||
|
||||
The Arduino transmits encoder positions as a 4-byte block:
|
||||
|
||||
### Memory Layout
|
||||
```
|
||||
Byte 0-1: X Position (little-endian signed 16-bit integer)
|
||||
Byte 2-3: Z Position (little-endian signed 16-bit integer)
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
If encoder reads:
|
||||
- X encoder: +5000 steps
|
||||
- Z encoder: -200 steps
|
||||
|
||||
The transmitted bytes would be:
|
||||
```
|
||||
Byte 0: 0x88 (LSB of 5000)
|
||||
Byte 1: 0x13 (MSB of 5000)
|
||||
Byte 2: 0x38 (LSB of -200)
|
||||
Byte 3: 0xFF (MSB of -200 in two's complement)
|
||||
```
|
||||
|
||||
### Python Parsing
|
||||
```python
|
||||
import struct
|
||||
|
||||
# Read from I2C
|
||||
data = bus.read_i2c_block_data(address, 0, 4)
|
||||
|
||||
# Convert to signed integers
|
||||
x_position = int.from_bytes(bytes(data[0:2]), 'little', signed=True)
|
||||
z_position = int.from_bytes(bytes(data[2:4]), 'little', signed=True)
|
||||
|
||||
# Or using struct:
|
||||
x_pos, z_pos = struct.unpack('<hh', bytes(data))
|
||||
```
|
||||
|
||||
## Communication Protocol
|
||||
|
||||
### Single Read Operation
|
||||
|
||||
```
|
||||
Master (RPi) Slave (Arduino)
|
||||
│
|
||||
├─ START condition ────────────────→
|
||||
│
|
||||
├─ Address + READ ─────────────────→
|
||||
│
|
||||
↓ ↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Arduino sends 4 bytes of data │
|
||||
│ (Acknowledgement from master) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├─ STOP condition ──────────────────→
|
||||
```
|
||||
|
||||
### Timing
|
||||
|
||||
- **Read Operation Time**: ~1-2 ms at 100 kHz
|
||||
- **Poll Interval**: 100 ms (10 Hz refresh)
|
||||
- **Data Transmission**: Asynchronous (happens in parallel with GUI rendering)
|
||||
|
||||
## Arduino Firmware Requirements
|
||||
|
||||
The Arduino sketch must:
|
||||
|
||||
1. **Initialize I2C Slave**
|
||||
```cpp
|
||||
Wire.begin(0x08); // Address 0x08
|
||||
Wire.onRequest(requestEvent); // Register event handler
|
||||
```
|
||||
|
||||
2. **Maintain Encoder Position Variables**
|
||||
```cpp
|
||||
volatile int16_t x_position = 0;
|
||||
volatile int16_t z_position = 0;
|
||||
```
|
||||
|
||||
3. **Update Positions from Encoders**
|
||||
- Attach interrupts to encoder pins
|
||||
- Increment/decrement positions on encoder pulses
|
||||
|
||||
4. **Respond to I2C Read Requests**
|
||||
```cpp
|
||||
void requestEvent() {
|
||||
byte buffer[4];
|
||||
|
||||
// Convert to bytes (little-endian)
|
||||
buffer[0] = (byte)x_position;
|
||||
buffer[1] = (byte)(x_position >> 8);
|
||||
buffer[2] = (byte)z_position;
|
||||
buffer[3] = (byte)(z_position >> 8);
|
||||
|
||||
Wire.write(buffer, 4);
|
||||
}
|
||||
```
|
||||
|
||||
## Encoder Interface
|
||||
|
||||
The Arduino handles the low-level encoder reading:
|
||||
|
||||
### Encoder Connection
|
||||
```
|
||||
Rotary Encoder (2-bit Gray code or quadrature)
|
||||
│
|
||||
├─ Signal A ──→ Arduino Interrupt Pin
|
||||
├─ Signal B ──→ Arduino Pin
|
||||
└─ GND ──────→ Arduino GND
|
||||
```
|
||||
|
||||
### Common Encoder Types
|
||||
|
||||
1. **Quadrature Encoder**
|
||||
- 2 signals 90° out of phase
|
||||
- Allows detection of direction and speed
|
||||
- Typical: KY-040 module
|
||||
|
||||
2. **Incremental Encoder**
|
||||
- Pulse + Direction signals
|
||||
- Requires external direction determination
|
||||
|
||||
3. **Absolute Encoder**
|
||||
- Maintains position across power cycles
|
||||
- More complex protocol (often SPI/USB)
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Arduino Side
|
||||
- **Buffer Overrun**: Ensure encoder ISR is fast (< 100 µs)
|
||||
- **I2C Collision**: Wire library handles this automatically
|
||||
- **Power Loss**: Position may reset unless using volatile storage
|
||||
|
||||
### Raspberry Pi Side
|
||||
The DRO application handles:
|
||||
```python
|
||||
try:
|
||||
data = self._bus.read_i2c_block_data(self._address, 0, 4)
|
||||
except IOError as e:
|
||||
# I2C bus error (timeout, NACK, collision)
|
||||
logger.error(f"I2C read error: {e}")
|
||||
except OSError as e:
|
||||
# System-level I2C error
|
||||
logger.error(f"I2C OS error: {e}")
|
||||
```
|
||||
|
||||
If a read fails, the last known position is retained.
|
||||
|
||||
## Debugging I2C Communication
|
||||
|
||||
### Check I2C Bus
|
||||
```bash
|
||||
# List I2C devices
|
||||
i2cdetect -y 1
|
||||
|
||||
# Expected output for Arduino at 0x08:
|
||||
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
# 00: -- -- -- -- -- -- -- -- 08 -- -- -- --
|
||||
```
|
||||
|
||||
### Manual Read Test
|
||||
```bash
|
||||
# Read 4 bytes from address 0x08, register 0
|
||||
i2cget -y 1 0x08 0 i 4
|
||||
|
||||
# Expected output: four hex bytes
|
||||
```
|
||||
|
||||
### Python Test
|
||||
```python
|
||||
import smbus2
|
||||
|
||||
bus = smbus2.SMBus(1)
|
||||
data = bus.read_i2c_block_data(0x08, 0, 4)
|
||||
print(f"Raw bytes: {[hex(b) for b in data]}")
|
||||
|
||||
x = int.from_bytes(bytes(data[0:2]), 'little', signed=True)
|
||||
z = int.from_bytes(bytes(data[2:4]), 'little', signed=True)
|
||||
print(f"X: {x}, Z: {z}")
|
||||
|
||||
bus.close()
|
||||
```
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
- **Latency**: ~2-5 ms (I2C + processing)
|
||||
- **Throughput**: 100 reads/second (limited by 100 ms GUI update interval)
|
||||
- **Bandwidth**: 4 bytes × 10 Hz = 320 bytes/second
|
||||
- **Jitter**: ±10 ms (non-deterministic on Linux)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Likely Cause | Solution |
|
||||
|---------|--------------|----------|
|
||||
| I2C not detected | Arduino not running | Upload firmware to Arduino |
|
||||
| Data is zeros | Encoder not moving | Check encoder connections |
|
||||
| Erratic values | Noise on I2C bus | Add pull-up resistors, shorten wires |
|
||||
| Periodic dropouts | I2C collision | Use slower clock speed (100 kHz) |
|
||||
| Position drifts | Encoder misconfiguration | Calibrate scale factor |
|
||||
|
||||
## References
|
||||
|
||||
- [I2C Specification](https://www.i2c-bus.org/)
|
||||
- [Arduino Wire Library](https://www.arduino.cc/en/Reference/Wire)
|
||||
- [Raspberry Pi I2C Setup](https://learn.adafruit.com/adafruit-16-channel-pwm-servo-driver/using-the-adafruit-library)
|
||||
282
docs/DRO.md
Normal file
282
docs/DRO.md
Normal 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
|
||||
307
docs/INSTALLATION.md
Normal file
307
docs/INSTALLATION.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# DRO Installation & Setup Guide
|
||||
|
||||
This guide walks through setting up the Digital Read Out application on a Raspberry Pi.
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Hardware**: Raspberry Pi 4 (or later)
|
||||
- **OS**: Raspbian/Raspberry Pi OS (Bullseye or later)
|
||||
- **Python**: 3.7+
|
||||
- **Arduino**: Arduino Nano with I2C encoder firmware
|
||||
|
||||
## Step 1: Install Dependencies
|
||||
|
||||
### Enable I2C
|
||||
```bash
|
||||
sudo raspi-config
|
||||
# Navigate to: Interfacing Options → I2C → Yes
|
||||
# Reboot
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
### Install Python Libraries
|
||||
```bash
|
||||
# Update package manager
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
|
||||
# Install required packages
|
||||
sudo apt-get install python3-tk python3-dev python3-pip
|
||||
|
||||
# Install Python dependencies
|
||||
pip3 install smbus2
|
||||
pip3 install adafruit-circuitpython-ads1x15 # Optional: for ADC
|
||||
```
|
||||
|
||||
### Optional: System Calculator
|
||||
```bash
|
||||
sudo apt-get install galculator
|
||||
```
|
||||
|
||||
## Step 2: Configure I2C for Shutdown
|
||||
|
||||
Allow the application to shutdown without a password:
|
||||
|
||||
```bash
|
||||
sudo visudo
|
||||
```
|
||||
|
||||
Add this line to the end of the file (replace `pi` with your username):
|
||||
```
|
||||
pi ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown
|
||||
```
|
||||
|
||||
Save with Ctrl+X, then Y, then Enter.
|
||||
|
||||
## Step 3: Upload Arduino Firmware
|
||||
|
||||
The Arduino must run firmware that:
|
||||
1. Reads from two rotary encoders
|
||||
2. Maintains position counters
|
||||
3. Responds to I2C read requests at address 0x08
|
||||
|
||||
See [ARDUINO_I2C_PROTOCOL.md](./ARDUINO_I2C_PROTOCOL.md) for protocol details.
|
||||
|
||||
Example firmware structure:
|
||||
```cpp
|
||||
#include <Wire.h>
|
||||
|
||||
volatile int16_t x_position = 0;
|
||||
volatile int16_t z_position = 0;
|
||||
|
||||
void setup() {
|
||||
Wire.begin(0x08);
|
||||
Wire.onRequest(requestEvent);
|
||||
|
||||
// Setup encoder interrupts
|
||||
pinMode(2, INPUT);
|
||||
pinMode(3, INPUT);
|
||||
attachInterrupt(0, x_encoder_isr, CHANGE);
|
||||
attachInterrupt(1, z_encoder_isr, CHANGE);
|
||||
}
|
||||
|
||||
void requestEvent() {
|
||||
byte buffer[4];
|
||||
buffer[0] = (byte)x_position;
|
||||
buffer[1] = (byte)(x_position >> 8);
|
||||
buffer[2] = (byte)z_position;
|
||||
buffer[3] = (byte)(z_position >> 8);
|
||||
Wire.write(buffer, 4);
|
||||
}
|
||||
|
||||
// Encoder ISR routines...
|
||||
void loop() {}
|
||||
```
|
||||
|
||||
Refer to the `i2c_encoder/` directory for the complete Arduino sketch.
|
||||
|
||||
## Step 4: Verify I2C Connection
|
||||
|
||||
Test I2C communication:
|
||||
|
||||
```bash
|
||||
# Install I2C tools
|
||||
sudo apt-get install i2c-tools
|
||||
|
||||
# Scan for I2C devices
|
||||
i2cdetect -y 1
|
||||
|
||||
# Should show Arduino at address 0x08:
|
||||
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
# 00: -- -- -- -- -- -- -- -- 08 -- -- -- --
|
||||
```
|
||||
|
||||
## Step 5: Auto-Start Configuration
|
||||
|
||||
### Option A: Desktop File (GUI Autostart)
|
||||
|
||||
Create `/home/pi/.config/autostart/dro.desktop`:
|
||||
```ini
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=DRO
|
||||
Exec=python3 /home/pi/dro/dro.py --zoomed
|
||||
StartupNotify=false
|
||||
X-GNOME-Autostart-always-run=true
|
||||
```
|
||||
|
||||
### Option B: Systemd Service
|
||||
|
||||
Create `/etc/systemd/system/dro.service`:
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Digital Read Out (DRO) Application
|
||||
After=graphical.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=pi
|
||||
WorkingDirectory=/home/pi/dro
|
||||
ExecStart=/usr/bin/python3 /home/pi/dro/dro.py --zoomed
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical.target
|
||||
```
|
||||
|
||||
Enable and start:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable dro.service
|
||||
sudo systemctl start dro.service
|
||||
```
|
||||
|
||||
### Option C: Shell Script Autostart
|
||||
|
||||
Create `/home/pi/dro/autostart.sh`:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
cd /home/pi/dro
|
||||
python3 dro.py --zoomed
|
||||
```
|
||||
|
||||
Make executable:
|
||||
```bash
|
||||
chmod +x /home/pi/dro/autostart.sh
|
||||
```
|
||||
|
||||
Add to crontab (for user pi):
|
||||
```bash
|
||||
crontab -e
|
||||
# Add line:
|
||||
@reboot /home/pi/dro/autostart.sh
|
||||
```
|
||||
|
||||
## Step 6: Test the Installation
|
||||
|
||||
### Test Mode (without Arduino)
|
||||
```bash
|
||||
cd ~/dro
|
||||
python3 dro.py --test
|
||||
|
||||
# Use keyboard to test:
|
||||
# A/Z = X encoder up/down
|
||||
# S/X = Z encoder up/down
|
||||
```
|
||||
|
||||
### With Hardware
|
||||
```bash
|
||||
# Start the application
|
||||
python3 dro.py --zoomed
|
||||
|
||||
# Check logs (if systemd service)
|
||||
journalctl -u dro.service -f
|
||||
```
|
||||
|
||||
## Step 7: Calibration
|
||||
|
||||
### Set Scale Factors
|
||||
|
||||
Edit `dro.py` and adjust these lines based on your encoder specifications:
|
||||
|
||||
```python
|
||||
# X axis: negative to flip direction
|
||||
model.set_scale('x', -2.5/200) # mm per step
|
||||
|
||||
# Z axis
|
||||
model.set_scale('z', 90/1000) # mm per step
|
||||
```
|
||||
|
||||
**How to determine:**
|
||||
1. Move the tool a known distance (e.g., 10 mm)
|
||||
2. Record the encoder step count
|
||||
3. Scale = Distance (mm) / Steps
|
||||
4. Use negative for X if the direction is backwards
|
||||
|
||||
### Set Zero Points
|
||||
|
||||
1. Position tool at starting location
|
||||
2. Press **X_0** button to zero X axis
|
||||
3. Press **Z_0** button to zero Z axis
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No I2C Communication
|
||||
```bash
|
||||
# Check I2C is enabled
|
||||
raspi-config
|
||||
|
||||
# Verify devices are connected
|
||||
i2cdetect -y 1
|
||||
|
||||
# Check for USB connections (if Arduino is USB)
|
||||
ls -la /dev/ttyUSB*
|
||||
```
|
||||
|
||||
### GUI Not Appearing
|
||||
```bash
|
||||
# Check display settings
|
||||
export DISPLAY=:0
|
||||
python3 dro.py
|
||||
|
||||
# Verify Tkinter is installed
|
||||
python3 -c "import tkinter; print('Tkinter OK')"
|
||||
```
|
||||
|
||||
### I2C Errors
|
||||
- Check pull-up resistors (4.7 kΩ is standard)
|
||||
- Verify Arduino firmware is running
|
||||
- Try slower I2C speed: edit smbus2 initialization
|
||||
|
||||
### Position Drift
|
||||
- Recalibrate scale factors
|
||||
- Check encoder hardware connections
|
||||
- Verify encoder isn't slipping
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
/home/pi/dro/
|
||||
├── dro.py # Main application
|
||||
├── autostart.sh # Auto-start script
|
||||
├── power.png # Power button icon
|
||||
├── test_model.py # Unit tests
|
||||
├── README.md # Project overview
|
||||
├── docs/
|
||||
│ ├── DRO.md # Application documentation
|
||||
│ └── ARDUINO_I2C_PROTOCOL.md # I2C protocol
|
||||
└── i2c_encoder/
|
||||
├── i2c_encoder.ino # Arduino firmware
|
||||
└── i2c_test.py # Arduino test script
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Reduce Update Interval** (for faster response):
|
||||
```python
|
||||
self._update_interval_ms = 50 # 20 Hz instead of 10 Hz
|
||||
```
|
||||
|
||||
2. **Increase I2C Speed**:
|
||||
```python
|
||||
bus = smbus2.SMBus(1)
|
||||
bus.bus.set_clock(400000) # 400 kHz fast mode
|
||||
```
|
||||
|
||||
3. **Use GPIO Directly** (instead of I2C) for more performance:
|
||||
- Requires hardware changes
|
||||
- RPi.GPIO library for direct GPIO reading
|
||||
|
||||
## Security Notes
|
||||
|
||||
- The DRO needs `sudo` access to shutdown (configured via sudoers)
|
||||
- Store any sensitive config files outside the repo
|
||||
- Use `.env` files for sensitive data (not committed to git)
|
||||
|
||||
## Getting Help
|
||||
|
||||
- Check logs: `journalctl -u dro.service -f`
|
||||
- Test I2C: `i2cget -y 1 0x08 0 i 4`
|
||||
- Python console test:
|
||||
```bash
|
||||
python3
|
||||
import smbus2
|
||||
bus = smbus2.SMBus(1)
|
||||
print(bus.read_i2c_block_data(0x08, 0, 4))
|
||||
```
|
||||
158
docs/README.md
Normal file
158
docs/README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Documentation
|
||||
|
||||
This directory contains comprehensive documentation for the DRO (Digital Read Out) application.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **[INSTALLATION.md](./INSTALLATION.md)** - Setup guide for Raspberry Pi
|
||||
- System requirements
|
||||
- Dependency installation
|
||||
- Arduino firmware setup
|
||||
- Auto-start configuration
|
||||
- Troubleshooting
|
||||
|
||||
2. **[DRO.md](./DRO.md)** - Application architecture and usage
|
||||
- Architecture overview (MVC pattern)
|
||||
- Class descriptions (Axis, Model, Controller, View)
|
||||
- Usage instructions and workflows
|
||||
- Configuration and calibration
|
||||
- GUI layout and keyboard shortcuts
|
||||
|
||||
3. **[ARDUINO_I2C_PROTOCOL.md](./ARDUINO_I2C_PROTOCOL.md)** - Hardware protocol details
|
||||
- I2C communication format
|
||||
- Data layout (4-byte packets)
|
||||
- Arduino firmware requirements
|
||||
- Encoder interface details
|
||||
- Debugging and troubleshooting
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
```
|
||||
DRO.md
|
||||
├── Overview - What is DRO?
|
||||
├── Architecture - MVC pattern and data flow
|
||||
├── Classes - Detailed class documentation
|
||||
│ ├── Axis - Single axis representation
|
||||
│ ├── Model - State management and observers
|
||||
│ ├── Controller - User input and I2C
|
||||
│ └── View - Tkinter GUI
|
||||
├── Usage - How to run the application
|
||||
├── Configuration - Scale factors and settings
|
||||
├── Error Handling - Exception handling
|
||||
├── Testing - Test mode documentation
|
||||
└── Performance - Design considerations
|
||||
|
||||
INSTALLATION.md
|
||||
├── Requirements - Hardware and software
|
||||
├── Step 1-7 - Complete setup walkthrough
|
||||
│ ├── Dependencies
|
||||
│ ├── I2C configuration
|
||||
│ ├── Arduino firmware
|
||||
│ ├── I2C verification
|
||||
│ ├── Auto-start
|
||||
│ ├── Testing
|
||||
│ └── Calibration
|
||||
├── Troubleshooting - Common issues and fixes
|
||||
└── Tips - Performance optimization
|
||||
|
||||
ARDUINO_I2C_PROTOCOL.md
|
||||
├── Hardware Setup - Pinout and connections
|
||||
├── Data Format - 4-byte packet structure
|
||||
├── Communication Protocol - I2C timing diagrams
|
||||
├── Arduino Requirements - Firmware template
|
||||
├── Encoder Interface - Quadrature signals
|
||||
├── Error Handling - Error cases
|
||||
├── Debugging - Testing tools
|
||||
└── Troubleshooting - Problem solving
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### MVC Architecture
|
||||
|
||||
The application separates concerns:
|
||||
- **Model**: Manages encoder positions, scale factors, and offsets
|
||||
- **View**: Tkinter GUI displaying positions and buttons
|
||||
- **Controller**: I2C communication, button handling, input processing
|
||||
|
||||
### Position Calculation
|
||||
|
||||
```
|
||||
Raw Position (steps from encoder)
|
||||
↓ × scale factor
|
||||
↓
|
||||
Device Position (mm)
|
||||
↓ + offset
|
||||
↓
|
||||
Displayed Position (mm, possibly × 2 for diameter)
|
||||
```
|
||||
|
||||
### I2C Communication
|
||||
|
||||
```
|
||||
Arduino → 4 bytes (X lo, X hi, Z lo, Z hi) → DRO Application
|
||||
↓
|
||||
Convert to signed 16-bit integers
|
||||
↓
|
||||
Apply scale factors
|
||||
↓
|
||||
Update display (10 Hz)
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Change Scale Factors
|
||||
**File**: `dro.py` → Search for `model.set_scale()`
|
||||
|
||||
```python
|
||||
model.set_scale('x', -2.5/200) # X axis: mm per step
|
||||
model.set_scale('z', 90/1000) # Z axis: mm per step
|
||||
```
|
||||
|
||||
### Change GUI Update Rate
|
||||
**File**: `dro.py` → Class `View.__init__()`
|
||||
|
||||
```python
|
||||
self._update_interval_ms = 100 # Change to 50 for 20 Hz
|
||||
```
|
||||
|
||||
### Enable Auto-Start
|
||||
**See**: `INSTALLATION.md` → Step 5
|
||||
|
||||
Three options: Desktop file, Systemd service, or Cron job
|
||||
|
||||
### Test Without Arduino
|
||||
**Command**: `python3 dro.py --test`
|
||||
|
||||
Simulates encoders with keyboard (A/Z/S/X keys)
|
||||
|
||||
### Debug I2C Issues
|
||||
**See**: `ARDUINO_I2C_PROTOCOL.md` → Debugging section
|
||||
|
||||
Tools: `i2cdetect`, `i2cget`, Python test script
|
||||
|
||||
## Troubleshooting Index
|
||||
|
||||
| Issue | Documentation |
|
||||
|-------|----------------|
|
||||
| Application won't start | INSTALLATION.md: Troubleshooting |
|
||||
| I2C not detected | ARDUINO_I2C_PROTOCOL.md: Debugging |
|
||||
| Positions are wrong | DRO.md: Configuration, INSTALLATION.md: Calibration |
|
||||
| Arduino not communicating | ARDUINO_I2C_PROTOCOL.md: Troubleshooting |
|
||||
| Missing dependencies | INSTALLATION.md: Step 1 |
|
||||
| Auto-start not working | INSTALLATION.md: Step 5 |
|
||||
|
||||
## Contributing to Docs
|
||||
|
||||
When updating documentation:
|
||||
1. Keep technical accuracy
|
||||
2. Include code examples where relevant
|
||||
3. Update the table of contents
|
||||
4. Cross-reference related sections
|
||||
5. Test all commands/instructions before documenting
|
||||
|
||||
## See Also
|
||||
|
||||
- **README.md** - Project overview
|
||||
- **code-review-generic.instructions.md** - Code review guidelines
|
||||
- **python.instructions.md** - Python coding standards
|
||||
191
docs/dro-schematics_mk2.drawio
Normal file
191
docs/dro-schematics_mk2.drawio
Normal file
File diff suppressed because one or more lines are too long
106
docs/mock-gui.drawio
Normal file
106
docs/mock-gui.drawio
Normal file
@@ -0,0 +1,106 @@
|
||||
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.0.16 Chrome/132.0.6834.196 Electron/34.2.0 Safari/537.36" version="26.0.16">
|
||||
<diagram name="Sida-1" id="yWZbiVy1Htm2IWZYSUBU">
|
||||
<mxGraphModel dx="1087" dy="1162" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-3" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeColor=#666666;strokeWidth=1;dashed=0;rounded=1;arcSize=5;recursiveResize=0;html=1;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="59" y="240" width="460" height="360" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-4" value="w-frame" style="shape=rect;strokeColor=none;fillColor=#008cff;strokeWidth=1;dashed=0;rounded=1;arcSize=20;fontColor=#ffffff;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;html=1;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-3">
|
||||
<mxGeometry x="5" width="90" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-27" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeColor=#666666;strokeWidth=1;dashed=0;rounded=1;arcSize=5;recursiveResize=0;html=1;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-3">
|
||||
<mxGeometry x="70" y="30" width="370" height="155" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-28" value="x-frame" style="shape=rect;strokeColor=none;fillColor=#008cff;strokeWidth=1;dashed=0;rounded=1;arcSize=20;fontColor=#ffffff;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;html=1;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-27">
|
||||
<mxGeometry x="5" width="80" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-29" value="X0" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-27">
|
||||
<mxGeometry x="250" y="60" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-30" value="X" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-27">
|
||||
<mxGeometry x="310" y="60" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-33" value="<font style="font-size: 43px;">1000.000</font>" style="rounded=1;whiteSpace=wrap;html=1;align=right;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-27">
|
||||
<mxGeometry x="10" y="55" width="230" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-34" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeColor=#666666;strokeWidth=1;dashed=0;rounded=1;arcSize=5;recursiveResize=0;html=1;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-3">
|
||||
<mxGeometry x="70" y="185" width="370" height="155" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-35" value="z-frame" style="shape=rect;strokeColor=none;fillColor=#008cff;strokeWidth=1;dashed=0;rounded=1;arcSize=20;fontColor=#ffffff;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;html=1;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-34">
|
||||
<mxGeometry x="5" width="80" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-36" value="Z0" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-34">
|
||||
<mxGeometry x="250" y="60" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-37" value="Z" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-34">
|
||||
<mxGeometry x="310" y="60" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-38" value="<font style="font-size: 43px;">1000.000</font>" style="rounded=1;whiteSpace=wrap;html=1;align=right;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-34">
|
||||
<mxGeometry x="10" y="55" width="230" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-6" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeColor=#666666;strokeWidth=1;dashed=0;rounded=1;arcSize=5;recursiveResize=0;html=1;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="519" y="240" width="250" height="360" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-7" value="e-frame" style="shape=rect;strokeColor=none;fillColor=#008cff;strokeWidth=1;dashed=0;rounded=1;arcSize=20;fontColor=#ffffff;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;html=1;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="5" width="80" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-5" value="1" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="10" y="70" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-8" value="2" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="70" y="70" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-9" value="3" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="130" y="70" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-11" value="4" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="10" y="130" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-12" value="5" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="70" y="130" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-13" value="6" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="130" y="130" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-14" value="7" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="10" y="190" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-15" value="8" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="70" y="190" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-16" value="9" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="130" y="190" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-17" value="." style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="10" y="250" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-18" value="0" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="70" y="250" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-19" value="+/-" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="130" y="250" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-20" value="enter" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="190" y="190" width="50" height="110" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-21" value="r/D" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="190" y="130" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-40" value="" style="group" vertex="1" connectable="0" parent="1AcGMTNSweSiUO7pp5MJ-6">
|
||||
<mxGeometry x="190" y="70" width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-22" value="" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="1AcGMTNSweSiUO7pp5MJ-40">
|
||||
<mxGeometry width="50" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="1AcGMTNSweSiUO7pp5MJ-39" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;endWidth=11.03448275862069;endSize=5.3517241379310345;strokeColor=#FFFFFF;strokeWidth=3;" edge="1" parent="1AcGMTNSweSiUO7pp5MJ-40">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="43" y="25" as="sourcePoint" />
|
||||
<mxPoint x="7" y="24.660000000000025" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
Reference in New Issue
Block a user