hello_activity

Example of the component cube32esp/cube32_bsp v0.0.17
# Hello SW Activity Recognition Example

Software-based activity recognition demo using LSM6DSOX accelerometer (and
optionally gyroscope) data with feature extraction and threshold-based decision
tree running on the ESP32 CPU.

**Purpose**: Direct comparison with the MLC hardware version
(`hello_mlc_activity`) for recognition sensitivity and system resource
utilisation analysis.

## USE_GYRO Toggle

A compile-time `#define USE_GYRO` at the top of `main.cpp` controls the sensor mode:

| Mode | `USE_GYRO` | Sensors | Features | UI Title | Notes |
|------|-----------|---------|----------|----------|-------|
| Accel only | `0` | Accel 26Hz ±4g | 12 (accel) | "SW Accel" | Fair MLC comparison (MLC uses accel only) |
| Accel + Gyro | `1` | Accel + Gyro 26Hz | 15 (accel + gyro) | "SW Accel+Gyro" | Better Biking/Driving separation |

Set `USE_GYRO=0` for an apples-to-apples comparison with MLC. Set `USE_GYRO=1`
for improved Biking vs Driving distinction at the cost of ~0.55 mA extra gyro power.

## Activities Detected

| Code | Activity   | Color       | Icon |
|------|-----------|-------------|------|
| 0    | Stationary | Blue-grey   | ⏹    |
| 1    | Walking    | Green       | ▶    |
| 4    | Jogging    | Orange      | ⚡   |
| 8    | Biking     | Blue        | ↻    |
| 12   | Driving    | Purple      | 📍   |

## UI Features (dark theme, adapts to the screen size)

The layout is computed from the display size at start-up (the log shows `Screen WxH, canvas WxH`):

| Screen | Chart canvas |
|--------|--------------|
| 240×240 | 220×60 |
| 320×320 | 300×140 |

The canvas spans the full width (10 px margins) and all the height between the duration timer and the legend; the history bars and the accelerometer waveform stretch with it, and the legend columns are spread over the screen width.

Same elements as the MLC version:
- Large activity icon + label (center)
- Stability timer (duration of current activity)
- Scrolling activity history timeline
- Raw accelerometer waveform mini-graph
- Legend row with color-coded activity labels
- **Resource metrics** in status line: classification latency (ms), free heap (KB), accel magnitude

## Sensor Configuration (matches MLC UCF)

| Parameter      | Value   | USE_GYRO=0 | USE_GYRO=1 |
|---------------|---------|------------|------------|
| Accel ODR     | 26 Hz   | ✓          | ✓          |
| Accel FS      | ±4g     | ✓          | ✓          |
| Gyro ODR      | 26 Hz   | —          | ✓          |
| Gyro FS       | ±500 dps| —          | ✓          |
| Window size   | 75      | ✓          | ✓          |
| Window stride | 37      | ✓          | ✓          |
| Sample struct | 12 bytes| ✓ (3 floats) | — (6 floats) |
| Sample struct | 24 bytes| —          | ✓ (6 floats) |

## Algorithm

### Feature Extraction

**Accelerometer per-axis (×3 axes = 12 features)** — matches MLC features:
- **Mean**: arithmetic mean of samples
- **Variance**: sample variance around the mean
- **Peak-to-peak**: max − min dynamic range
- **Zero-crossings**: count of mean-crossings per second

**Gyroscope (3 features, USE_GYRO=1 only)** — additional to MLC:
- **Magnitude mean**: mean of |ω| = √(ωx² + ωy² + ωz²)
- **Magnitude variance**: variance of |ω|
- **Energy**: mean of (ωx² + ωy² + ωz²)

### Decision Tree

Threshold-based classification using extracted features:

1. **Stationary**: avg accel variance < STATIONARY_VAR
2. **Jogging**: avg ZCR > JOG_ZCR AND avg P2P > JOG_P2P
3. **Walking**: avg variance > MOTION_VAR_MIN AND avg ZCR > WALK_ZCR AND avg P2P > WALK_P2P
4. **Driving**:
   - `USE_GYRO=1`: gyro energy < DRIVE_GYRO_ENERGY AND avg variance < DRIVE_VAR_MAX
   - `USE_GYRO=0`: avg variance < DRIVE_VAR_MAX AND avg ZCR < WALK_ZCR AND avg P2P < WALK_P2P
5. **Biking**: remaining sustained motion above MOTION_VAR_MIN
6. **Stationary** (fallback): low-level motion not meeting any criteria

**Temporal smoothing**: majority vote over 3 consecutive windows (~4 seconds) before updating displayed activity.

### Tuning Thresholds

All thresholds are `constexpr` at the top of `main.cpp`:

```cpp
THRESH_STATIONARY_VAR    = 0.01f    // g²
THRESH_JOG_ZCR           = 4.0f     // crossings/sec
THRESH_WALK_ZCR          = 1.2f     // crossings/sec
THRESH_WALK_P2P          = 0.2f     // g
THRESH_JOG_P2P           = 0.8f     // g
THRESH_MOTION_VAR_MIN    = 0.02f    // g²
THRESH_DRIVE_GYRO_ENERGY = 50.0f    // (dps)² — USE_GYRO=1 only
THRESH_DRIVE_VAR_MAX     = 0.08f    // g²
```

Adjust these based on your mounting orientation and use case.

## Resource Metrics

Displayed on the UI status line and console for comparison with MLC:

| Metric              | SW Version          | MLC Version |
|--------------------|---------------------|-------------|
| CPU overhead       | Sampling task + classify | ~0% (on-sensor) |
| Classification latency | Measured per window | ~0 (hardware) |
| Heap usage         | Sample buffers + task stack | UCF loading only |

## Prerequisites

- Enable **IMU** in IMU Configuration
- Enable **LVGL** display
- Enable fonts: `LV_FONT_MONTSERRAT_10`, `14`, `18`, `24`
- MLC is **not required** (key difference from `hello_mlc_activity`)

## Build & Flash

```bash
source ~/esp/v5.5.1/esp-idf/export.sh 2>/dev/null
idf.py menuconfig
# Navigate: CUBE32 Application → Hello SW Activity Recognition Example
idf.py build && idf.py flash monitor
```

## Comparison Procedure

1. Flash `hello_mlc_activity`, record console output for 2+ minutes with known activities
2. Flash `hello_activity` (this example), repeat same activity sequence
3. Compare:
   - **Sensitivity**: which detects transitions faster/more accurately?
   - **CPU**: SW version shows latency; MLC is zero CPU
   - **Heap**: compare `esp_get_free_heap_size()` between versions
   - **Responsiveness**: SW classifies every ~1.4s (50% overlap), MLC every ~2.88s

## Console Output Format

```
Activity: Walking      |mag|=1.12g  Duration: 8s  Latency: 11842us  Heap: 184320
Activity: Jogging      |mag|=1.85g  Duration: 3s  Latency: 12105us  Heap: 184320
```

To create a project from this example, run:

idf.py create-project-from-example "cube32esp/cube32_bsp=0.0.17:hello_activity"

or download archive (~16.41 KB)