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)
|
||||
Reference in New Issue
Block a user