Update: README included for better understanding and future developers
This commit is contained in:
273
README.md
Normal file
273
README.md
Normal file
@ -0,0 +1,273 @@
|
||||
# ESP32 IMU Bridge
|
||||
|
||||
This project bridges IMU data through an ESP32 to a PC, with optional gripper/relay control, real-time serial monitoring, CSV export, and signal-quality visualization.
|
||||
|
||||
## Features
|
||||
|
||||
- Reads WIT-format IMU UART frames on the ESP32: acceleration, gyro, angle, and temperature.
|
||||
- Streams custom binary packets from the ESP32 to the PC over USB serial at 50 Hz.
|
||||
- Allows the PC to control the gripper with simple keyboard commands:
|
||||
- `o`: OPEN
|
||||
- `c`: CLOSE
|
||||
- `s`: STOP
|
||||
- Generates statistics, CSV files, and plots from either live serial data or saved text logs.
|
||||
- Uses a physical switch on GPIO27 to trigger one-shot gripper open/close pulses.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
| --- | --- |
|
||||
| `main.py` | Main MicroPython program for the ESP32. It reads the IMU, controls DIO, and streams USB serial packets. |
|
||||
| `esp_bridge.py` | PC-side Python wrapper class for integrating the ESP32 bridge into other programs. |
|
||||
| `pc_reader.py` | PC-side real-time serial reader with keyboard gripper commands. |
|
||||
| `visualise.py` | Sampling, statistics, CSV export, and plotting tool. |
|
||||
| `test.py` | ESP32 UART pin scan/debug helper. |
|
||||
| `imu_data.csv` | Example or exported IMU sample data. |
|
||||
| `rcd.txt` | Example text log from `pc_reader.py`. |
|
||||
| `imu_quality*.png` | Visualization output images. |
|
||||
|
||||
## Hardware Wiring
|
||||
|
||||
### IMU UART
|
||||
|
||||
| IMU | ESP32 |
|
||||
| --- | --- |
|
||||
| TX yellow wire | GPIO16 / RX2 |
|
||||
| RX green wire | GPIO17 / TX2 |
|
||||
| GND | GND |
|
||||
|
||||
The default IMU baud rate is `921600`.
|
||||
|
||||
### Digital Inputs
|
||||
|
||||
Default digital input pins in `main.py`:
|
||||
|
||||
```python
|
||||
DIN_PINS = (25, 26, 27, 32)
|
||||
```
|
||||
|
||||
These inputs use `Pin.PULL_UP`, so normally:
|
||||
|
||||
- floating/open reads as `1`
|
||||
- connected to ground/closed reads as `0`
|
||||
|
||||
GPIO27 is used as the physical toggle switch:
|
||||
|
||||
- `OFF -> ON`: triggers a CLOSE pulse
|
||||
- `ON -> OFF`: triggers an OPEN pulse
|
||||
|
||||
### Digital Outputs / Relay
|
||||
|
||||
Default output pins in `main.py`:
|
||||
|
||||
```python
|
||||
DOUT_PINS = (18, 19)
|
||||
```
|
||||
|
||||
The relay is low-level triggered:
|
||||
|
||||
- GPIO HIGH: relay off
|
||||
- GPIO LOW: relay on
|
||||
|
||||
Current output logic:
|
||||
|
||||
- GPIO18 / IN1 LOW: CLOSE
|
||||
- GPIO19 / IN2 LOW: OPEN
|
||||
- both GPIOs HIGH: STOP
|
||||
|
||||
## ESP32 Setup
|
||||
|
||||
1. Flash MicroPython firmware to the ESP32.
|
||||
2. Upload `main.py` to the ESP32 root filesystem.
|
||||
3. Reset the ESP32.
|
||||
|
||||
After reset, the program stays quiet for about 3 seconds, then starts streaming binary packets over USB serial.
|
||||
|
||||
Example upload commands:
|
||||
|
||||
```bash
|
||||
mpremote connect /dev/ttyUSB0 fs cp main.py :main.py
|
||||
mpremote connect /dev/ttyUSB0 reset
|
||||
```
|
||||
|
||||
Replace `/dev/ttyUSB0` with your actual ESP32 serial port if needed.
|
||||
|
||||
## PC Environment
|
||||
|
||||
Python 3 is recommended.
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```bash
|
||||
python3 -m pip install pyserial matplotlib numpy
|
||||
```
|
||||
|
||||
If you only use `pc_reader.py` or `esp_bridge.py`, `pyserial` is enough.
|
||||
|
||||
## Real-Time Reading And Control
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 pc_reader.py /dev/ttyUSB0 --baud 115200
|
||||
```
|
||||
|
||||
The output should look similar to:
|
||||
|
||||
```text
|
||||
# 793 50.0Hz DIN=[0, 1, 0, 0] DOUT=[1, 0] ACC=[...] GYRO=[...] ANGLE=[...]
|
||||
```
|
||||
|
||||
Type a command in the terminal and press Enter:
|
||||
|
||||
```text
|
||||
o
|
||||
c
|
||||
s
|
||||
```
|
||||
|
||||
These commands mean OPEN, CLOSE, and STOP.
|
||||
|
||||
## Python API Usage
|
||||
|
||||
`esp_bridge.py` provides an `ESP32Bridge` class for use in other PC-side Python programs:
|
||||
|
||||
```python
|
||||
from esp_bridge import ESP32Bridge
|
||||
|
||||
with ESP32Bridge("/dev/ttyUSB0") as bridge:
|
||||
bridge.open_gripper()
|
||||
data = bridge.read_packet_with_frequency()
|
||||
print(data)
|
||||
bridge.stop_gripper()
|
||||
```
|
||||
|
||||
Continuous reading example:
|
||||
|
||||
```python
|
||||
from esp_bridge import ESP32Bridge
|
||||
|
||||
with ESP32Bridge("/dev/ttyUSB0") as bridge:
|
||||
for packet in bridge.iter_packets(seconds=10):
|
||||
print(packet["freq_hz"], packet["acc_g"], packet["angle_deg"])
|
||||
```
|
||||
|
||||
## Sampling, Export, And Visualization
|
||||
|
||||
Sample from serial for 10 seconds, save a CSV file, and generate a plot:
|
||||
|
||||
```bash
|
||||
python3 visualise.py --port /dev/ttyUSB0 --baud 115200 --seconds 10 --csv imu_data.csv --output imu_quality.png
|
||||
```
|
||||
|
||||
Save files only without opening a plot window:
|
||||
|
||||
```bash
|
||||
python3 visualise.py --port /dev/ttyUSB0 --baud 115200 --seconds 10 --csv imu_data.csv --output imu_quality.png --no-show
|
||||
```
|
||||
|
||||
Read from a `pc_reader.py` text log and plot it:
|
||||
|
||||
```bash
|
||||
python3 visualise.py --file rcd.txt --output imu_quality.png
|
||||
```
|
||||
|
||||
If the output file already exists, `visualise.py` appends a timestamp to avoid overwriting it, for example:
|
||||
|
||||
```text
|
||||
imu_quality_20260701_131902.png
|
||||
```
|
||||
|
||||
## Packet Protocol
|
||||
|
||||
The ESP32-to-PC binary packet format is defined in `main.py`, `pc_reader.py`, and `esp_bridge.py`:
|
||||
|
||||
```python
|
||||
PACKET_FORMAT = "<2sBBBI10hB"
|
||||
```
|
||||
|
||||
Field layout:
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| sync | `2s` | Fixed bytes `A5 5A` |
|
||||
| version | `B` | Currently `2` |
|
||||
| din_mask | `B` | 4-channel digital input bit mask |
|
||||
| dout_mask | `B` | 2-channel digital output bit mask |
|
||||
| time_ms | `I` | ESP32 `ticks_ms()` value |
|
||||
| raw_values | `10h` | acc xyz, gyro xyz, angle xyz, temperature |
|
||||
| checksum | `B` | Low 8 bits of the sum of all previous bytes |
|
||||
|
||||
Raw value scaling:
|
||||
|
||||
| Data | Scaling |
|
||||
| --- | --- |
|
||||
| Acceleration | `raw * 16 / 32768`, unit `g` |
|
||||
| Gyro | `raw * 2000 / 32768`, unit `deg/s` |
|
||||
| Angle | `raw * 180 / 32768`, unit `deg` |
|
||||
| Temperature | `raw / 100`, unit `deg C` |
|
||||
|
||||
## Common Parameters
|
||||
|
||||
The most useful hardware and timing parameters are in `main.py`:
|
||||
|
||||
```python
|
||||
IMU_RX_PIN = 16
|
||||
IMU_TX_PIN = 17
|
||||
DIN_PINS = (25, 26, 27, 32)
|
||||
DOUT_PINS = (18, 19)
|
||||
IMU_BAUD = 921600
|
||||
SEND_PERIOD_MS = 20
|
||||
SWITCH_PULSE_MS = 2000
|
||||
USE_REAL_DOUT = True
|
||||
```
|
||||
|
||||
Before testing a real relay, you can set:
|
||||
|
||||
```python
|
||||
USE_REAL_DOUT = False
|
||||
```
|
||||
|
||||
In this mode, the PC can still see `DOUT` state changes, but the ESP32 will not actually drive GPIO18/GPIO19.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Serial Port Not Found
|
||||
|
||||
On Linux, list possible serial ports with:
|
||||
|
||||
```bash
|
||||
ls /dev/ttyUSB* /dev/ttyACM*
|
||||
```
|
||||
|
||||
If you have permission issues, add your user to the `dialout` group or temporarily run the serial tool with `sudo`.
|
||||
|
||||
### Frequency Is Not 50 Hz
|
||||
|
||||
Check the following:
|
||||
|
||||
- The ESP32 has entered the `main.py` loop.
|
||||
- The PC-side baud rate is `115200`.
|
||||
- The IMU-to-ESP32 UART baud rate is `921600`.
|
||||
- The USB serial cable and power supply are stable.
|
||||
|
||||
### Data Is All Zero Or Angle Does Not Update
|
||||
|
||||
Check the IMU wiring and output format. The ESP32 parser currently handles WIT frames:
|
||||
|
||||
- `0x51`: acceleration
|
||||
- `0x52`: gyro
|
||||
- `0x53`: angle
|
||||
|
||||
The program treats an angle frame as the point where one full IMU update is complete.
|
||||
|
||||
### OPEN/CLOSE Direction Is Reversed
|
||||
|
||||
Depending on the relay and motor wiring, swap the relay inputs connected to GPIO18/GPIO19, or adjust the OPEN/CLOSE output logic in `set_gripper()`.
|
||||
|
||||
## Suggested Workflow
|
||||
|
||||
1. Use `pc_reader.py` to confirm the PC receives stable 50 Hz packets.
|
||||
2. Type `o`, `c`, and `s` to confirm `DOUT` state changes.
|
||||
3. Enable real relay output with `USE_REAL_DOUT = True` when ready.
|
||||
4. Use `visualise.py` to sample data and generate CSV/plot outputs for checking noise, frequency, and angle stability.
|
||||
Reference in New Issue
Block a user