# ARMx API Reference
**Version 2.0 — REST & WebSocket API**

Base URL: `http://localhost:8000`

---

## 📡 WebSocket

### Connect
```
ws://localhost:8000/ws
```

### Server → Client Messages

#### `init` — Sent on connection
```json
{
  "type": "init",
  "config": {
    "channels": 8,
    "gestures": ["REST", "OPEN", "FIST", "PINCH", "THUMB", "INDEX", "MIDDLE_ONLY", "RING_ONLY", "PINKY_ONLY"],
    "simulate": true
  }
}
```

#### `data` — Streamed at 100 Hz
```json
{
  "type": "data",
  "emg": [0.12, 0.45, 0.03, 0.08, 0.22, 0.11, 0.07, 0.19],
  "imu": { "ax": 0.01, "ay": -0.02, "az": 9.81, "gx": 1.5, "gy": -0.8, "gz": 0.3 },
  "activation": [0.12, 0.45, 0.03, 0.08, 0.22, 0.11, 0.07, 0.19],
  "feature_map": [ { "RMS": 0.05, "MAV": 0.04, "Var": 0.001, "ZCR": 0.5, "WL": 0.41, "SSC": 0.55 }, "..." ],
  "signal_quality": [0.7, 1.0, 0.4, 0.7, 1.0, 0.7, 0.4, 1.0],
  "timestamp": 1715523600.123,
  "prediction": {
    "gesture": "INDEX",
    "confidence": 0.87,
    "probabilities": { "REST": 0.02, "INDEX": 0.87, "OPEN": 0.05, "..." }
  },
  "calibration": { "phase": "noise_detection", "progress": 0.6 }
}
```
> `prediction` and `calibration` keys only appear when active.

#### `event` — Hardware events
```json
{ "type": "event", "event": "connected" }
{ "type": "event", "event": "disconnected" }
{ "type": "event", "event": "connection_lost" }
```

### Client → Server Messages

#### Set simulator gesture
```json
{ "action": "set_sim_gesture", "gesture": "INDEX" }
```

#### Direct servo control (hardware only)
```json
{ "action": "servo_direct", "finger": 1, "angle": 180 }
```
`finger`: 0=Thumb, 1=Index, 2=Middle, 3=Ring, 4=Pinky | `angle`: 0–180°

---

## 🌐 REST API

### Status & Info

#### `GET /api/status`
Full system status.
```json
{
  "serial": { "connected": false, "port": null, "state": "disconnected" },
  "streaming": true,
  "simulate": true,
  "calibration": { "phase": "idle", "is_calibrated": false },
  "ml": { "is_trained": true, "last_prediction": "INDEX", "last_confidence": 0.87 },
  "control": { "active": false, "current_gesture": "REST" },
  "recording": { "recording": false, "session": null },
  "ws_clients": 1
}
```

#### `GET /api/ports`
List available serial ports.
```json
[{ "port": "COM3", "description": "USB Serial", "is_teensy": true }]
```

#### `GET /api/data/profiles`
List saved datasets and models.
```json
{ "datasets": ["default", "session1"], "models": ["default"] }
```

---

### Connection

#### `POST /api/connect?port=COM3`
Connect to Teensy. Omit `port` for auto-detect.
```json
{ "success": true, "info": { "port": "COM3", "connected": true } }
```

#### `POST /api/disconnect`
Disconnect hardware, return to simulation mode.
```json
{ "success": true }
```

---

### Streaming

#### `POST /api/stream/start`
Start data streaming.

#### `POST /api/stream/stop`
Stop data streaming.

---

### Calibration

#### `POST /api/calibration/noise`
Start noise floor detection (relax muscles for ~3s).

#### `POST /api/calibration/range`
Start range detection (contract muscles for ~5s).

#### `POST /api/calibration/save?name=default`
Save calibration profile.

#### `POST /api/calibration/load?name=default`
Load calibration profile.

---

### Training

#### `POST /api/training/start?gesture=INDEX`
Start recording training samples for a gesture.

Valid gestures: `REST`, `OPEN`, `FIST`, `PINCH`, `THUMB`, `INDEX`, `MIDDLE_ONLY`, `RING_ONLY`, `PINKY_ONLY`

#### `POST /api/training/stop`
Stop recording. Returns sample count.
```json
{ "success": true, "samples_recorded": 87, "dataset": { "total_samples": 432, "gestures": { "REST": 90, "INDEX": 87 } } }
```

#### `POST /api/training/save?name=default`
Save training dataset to `.npz` file.

#### `POST /api/training/load?name=default`
Load training dataset.

#### `POST /api/training/clear`
Clear all in-memory training data.

---

### Machine Learning

#### `POST /api/ml/train`
Train the classifier on collected data.
```json
{
  "success": true,
  "cv_accuracy": 0.923,
  "train_accuracy": 0.991,
  "n_samples": 720,
  "n_classes": 9,
  "model_type": "random_forest",
  "classes": ["FIST", "INDEX", "MIDDLE_ONLY", "OPEN", "PINCH", "PINKY_ONLY", "REST", "RING_ONLY", "THUMB"],
  "confusion_matrix": [[...]]
}
```

#### `POST /api/ml/save?name=default`
Save trained model to `.pkl` file.

#### `POST /api/ml/load?name=default`
Load a saved model.

---

### Servo Control

#### `POST /api/control/start`
Activate gesture-to-servo control loop.

#### `POST /api/control/stop`
Deactivate control loop.

#### `GET /api/control/limits`
Get current servo calibration limits and gesture percentages.
```json
{
  "servo_mins": [0, 0, 0, 0, 0],
  "servo_maxs": [180, 180, 180, 180, 180],
  "gesture_percentages": {
    "OPEN":        [1.0, 1.0, 1.0, 1.0, 1.0],
    "FIST":        [0.0, 0.0, 0.0, 0.0, 0.0],
    "THUMB":       [1.0, 0.0, 0.0, 0.0, 0.0],
    "INDEX":       [0.0, 1.0, 0.0, 0.0, 0.0],
    "MIDDLE_ONLY": [0.0, 0.0, 1.0, 0.0, 0.0],
    "RING_ONLY":   [0.0, 0.0, 0.0, 1.0, 0.0],
    "PINKY_ONLY":  [0.0, 0.0, 0.0, 0.0, 1.0],
    "REST":        [0.5, 0.5, 0.5, 0.5, 0.5]
  }
}
```

#### `POST /api/command/{gesture}`
Manually trigger a gesture command.
- Path param: `gesture` — any valid gesture name (case-insensitive)
```json
{ "success": true, "gesture": "INDEX" }
```

#### `POST /api/servo/{finger}/{angle}`
**Direct per-finger servo control** — bypasses ML pipeline.
- `finger`: 0–4 (Thumb=0, Index=1, Middle=2, Ring=3, Pinky=4)
- `angle`: 0–180 (clamped to safe range)
```json
{ "success": true, "finger": 1, "angle": 180, "command": "SERVO:1,180" }
```

---

### Session Recording

#### `POST /api/recording/start?name=session1`
Start recording data to CSV.
```json
{ "success": true, "session": "session1_2026-05-12" }
```

#### `POST /api/recording/stop`
Stop recording.

#### `GET /api/recordings`
List saved recordings.

#### `GET /api/recordings/download/{name}`
Download a recording as CSV.

#### `DELETE /api/recordings/{name}`
Delete a recording.

---

## 🔢 Gesture–Servo Mapping Reference

Percentages map to servo angle as: `angle = SERVO_MIN + (SERVO_MAX - SERVO_MIN) × pct`

| Gesture | Thumb | Index | Middle | Ring | Pinky |
|---|---|---|---|---|---|
| `OPEN` | 100% | 100% | 100% | 100% | 100% |
| `FIST` | 0% | 0% | 0% | 0% | 0% |
| `PINCH` | 0% | 0% | 100% | 100% | 100% |
| `THUMB` | 100% | 0% | 0% | 0% | 0% |
| `INDEX` | 0% | 100% | 0% | 0% | 0% |
| `MIDDLE_ONLY` | 0% | 0% | 100% | 0% | 0% |
| `RING_ONLY` | 0% | 0% | 0% | 100% | 0% |
| `PINKY_ONLY` | 0% | 0% | 0% | 0% | 100% |
| `REST` | 50% | 50% | 50% | 50% | 50% |

---

## 📐 Feature Extraction Reference

Each EMG channel produces 6 time-domain features per window (200ms, 50ms overlap):

| Feature | Name | Description |
|---|---|---|
| `RMS` | Root Mean Square | Signal energy / contraction intensity |
| `MAV` | Mean Absolute Value | Average rectified signal |
| `Var` | Variance | Signal variability |
| `ZCR` | Zero Crossing Rate | Frequency content indicator |
| `WL` | Waveform Length | Signal complexity |
| `SSC` | Slope Sign Changes | High-frequency content |

Total feature vector: **8 channels × 6 features = 48 features** per prediction.

A **50Hz notch filter** (IIR biquad) removes power-line interference before feature extraction.

---

## 🚀 Standalone Deploy API

These endpoints generate a self-contained Teensy firmware so the arm runs gesture recognition **without a laptop**.

### `POST /api/deploy`

Generate `firmware/standalone/standalone.ino` from the current trained model + calibration.

**Requirements:** A model must be trained first (`POST /api/ml/train` or loaded via `POST /api/ml/load`). Calibration is optional but strongly recommended.

**Response (success):**
```json
{
  "success": true,
  "path": "E:\\armx\\firmware\\standalone\\standalone.ino",
  "size_bytes": 142680,
  "classes": ["FIST", "INDEX", "MIDDLE_ONLY", "OPEN", "PINCH", "PINKY_ONLY", "REST", "RING_ONLY", "THUMB"],
  "model_type": "random_forest",
  "feature_count": 48,
  "timestamp": "2026-05-13 14:55:00",
  "message": "Firmware written to firmware/standalone/standalone.ino (142680 bytes). Open in Arduino IDE and flash to Teensy."
}
```

**Response (error — no trained model):**
```json
{ "success": false, "error": "No trained model available. Train a model first." }
```

> ⚠️ Each call **overwrites** the previous `standalone.ino`. There is only ever one deployed firmware file.

---

### `GET /api/deploy/status`

Returns metadata about the last generated standalone firmware.

**Response (deployed):**
```json
{
  "deployed": true,
  "path": "E:\\armx\\firmware\\standalone\\standalone.ino",
  "size_bytes": 142680,
  "last_deployed": "2026-05-13 14:55:00",
  "classes": ["FIST", "INDEX", "OPEN", "REST", "THUMB"],
  "model_type": "random_forest"
}
```

**Response (never deployed):**
```json
{ "deployed": false }
```

---

## 🔌 Firmware Modes — Switching Guide

ARMx supports two mutually exclusive firmware modes on the Teensy. Both `.ino` files live on your laptop and are never deleted.

| Mode | Firmware file | When to use |
|---|---|---|
| **Laptop Mode** | `firmware/emg_reader/emg_reader.ino` | Training, calibration, live dashboard |
| **Standalone Mode** | `firmware/standalone/standalone.ino` | Untethered use, no laptop required |

### Deploying to Standalone Mode

1. Train a model (`🚀 Train Model`) and optionally calibrate
2. Click **🚀 Deploy to Teensy** in the dashboard Deploy panel (or `POST /api/deploy`)
3. Open `firmware/standalone/standalone.ino` in Arduino IDE
4. Select **Teensy 4.1** from `Tools → Board`
5. Click **Upload** — the Teensy reboots in standalone mode

### Switching Back to Laptop Mode

1. Open `firmware/emg_reader/emg_reader.ino` in Arduino IDE
2. Select **Teensy 4.1** from `Tools → Board`
3. Click **Upload**
4. In ARMx dashboard, click **Connect** in the top bar

> 💡 The **💻 Switch Back to Laptop Mode** button in the Deploy panel shows these steps with a one-click copy of the firmware path.

### What the Standalone Firmware Contains

The generated `standalone.ino` is a complete, self-contained program — it does **not** depend on the laptop after flashing:

- ✅ 8-channel EMG reading (A0–A7, 1 kHz sampling)
- ✅ Moving average filter (window = 8)
- ✅ IMU via Wire2 (SDA=25, SCL=24)
- ✅ Feature extraction: RMS, MAV, Var, ZCR, WL, SSC per channel
- ✅ Calibration constants baked in as `float[]` arrays
- ✅ Trained gesture classifier compiled to C++ decision trees
- ✅ Majority-vote smoothing window
- ✅ 5-servo output with speed-ramping (pins 2–6)
- ✅ Serial heartbeat + `STATUS` command for debug over USB

### Standalone Serial Protocol

Even in standalone mode the Teensy outputs useful messages at 115200 baud (viewable in Arduino IDE Serial Monitor):

| Message | Meaning |
|---|---|
| `ARMX_STANDALONE:READY` | Boot complete |
| `GESTURE_CLASSES:REST,OPEN,...` | List of compiled-in classes |
| `PRED:FIST` | Gesture changed to FIST |
| `HB:REST` | Heartbeat every 5 s, current gesture |

Manual override is still available — send `STATUS\n` over serial to query current state.
