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

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