Skip to content

ESP32 BLE-to-MQTT bridge for the Elite Aria cycling fan. Control via Home Assistant with bi-directional sync and automatic reconnection.

License

Notifications You must be signed in to change notification settings

tlsim/AriaFanBLE

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aria Fan BLE Controller

ESP32-based BLE bridge to control an Elite Aria cycling fan from Home Assistant via MQTT.

Overview

This project allows you to control an Elite Aria smart cycling fan using an ESP32 (M5Stack ATOM Lite) that acts as a BLE client and MQTT bridge. The fan can be controlled via Home Assistant or the fan's own physical controls - all states sync automatically.

Firmware Version: This implementation was developed and tested against an Aria fan running firmware version 9. Protocol details, UUIDs, and command formats may differ on other firmware versions.

Architecture

The ESP32 acts as a bridge between Home Assistant and the Aria fan:

Home Assistant (MQTT) ← WiFi → ESP32 ← BLE → Aria Fan

Both connections are required for operation:

  • Commands from HA require MQTT (to receive) + BLE (to send to fan)
  • Status updates require BLE (to receive from fan) + MQTT (to send to HA)
  • If either connection drops, the ESP32 automatically reconnects and restores the desired state

The fan's physical controls work independently and any changes are synced back to Home Assistant.

Hardware Requirements

  • ESP32 Board (tested with M5Stack ATOM Lite ESP32-PICO-D4)
    • Any ESP32 with BLE support should work
    • Optional: RGB LED for status indication (default GPIO 27)
    • Note: Physical button (GPIO 39) is not used in this implementation
  • Elite Aria Fan (with BLE support)
  • Home Assistant with MQTT broker (Mosquitto)

Features

Control Methods

  • Home Assistant Integration - Full speed and mode control via MQTT
  • Fan Physical Controls - Mode button and speed controls on the fan itself

Synchronization

  • Bi-directional Sync - Changes from any control method update all interfaces
  • Real-time Status - Fan speed and mode updates reflected in Home Assistant
  • Sensor Modes - Speed/Power/HR readings from ANT+ sensors automatically update HA

Reliability

  • Auto-reconnect - Automatically reconnects to fan if BLE connection drops
  • State Restoration - Restores desired fan speed/mode after BLE reconnection
  • MQTT Keepalive - Reconnects to MQTT broker if network issues occur
  • Visual Feedback - LED indicates connection status

Diagnostics

  • Web-based Logs - View last 30 log entries via web browser
  • Status Endpoint - JSON diagnostics with heap usage and connection status
  • MQTT Logging - Logs published to Home Assistant for long-term history
  • Event Tracking - Logs all speed/mode changes with source (MQTT/BLE/Reconnect)
  • Memory Safeguards - Graceful degradation when heap is critically low

Fan Modes

The Aria fan supports 5 operating modes:

Mode Description Sensor Type
Manual Direct speed control (0-100%) None
Speed Speed-based fan control ANT+ Speed sensor
Power Power-based fan control ANT+ Power meter
HR Heart rate-based fan control ANT+ or BLE HR monitor
Temp Temperature-based control BLE Temp sensor (⚠️ causes disconnect)

Note: Temp mode uses BLE and will temporarily disconnect the ESP32 (auto-reconnects in 10-15s).

LED Status Indicators

Color Status
🔴 Red Disconnected from fan
🟡 Yellow Scanning for fan
🟢 Green Connected to fan

Protocol Details

BLE Service & Characteristics

Service UUID: 347B0001-7635-408B-8918-8FF3949CE592

Characteristic UUIDs:

UUID Type Purpose
347B0012-7635-408B-8918-8FF3949CE592 Write Initial commands (not used)
347B0013-7635-408B-8918-8FF3949CE592 Read Configuration blob (variable size, not used)
347B0040-7635-408B-8918-8FF3949CE592 Write + Notify Control commands
347B0042-7635-408B-8918-8FF3949CE592 Notify Status updates

Note: These UUIDs are from a single Aria fan and may be device-specific rather than universal to all Aria fans. If your fan doesn't connect, use the nRF Connect app to discover the correct UUIDs for your device.

Commands (Characteristic 347B0040)

// Set Mode: [0x05, mode]
// 0x00=Manual, 0x01=Speed, 0x02=Power, 0x03=HR, 0x04=Temp
setAriaMode(0x00);  // Manual mode

// Set Speed: [0x03, 0x01, percentage]
setFanSpeed(50);  // 50% speed

Status Notifications (Characteristic 347B0042)

Format: [mode, speed, 0x00, 0x02]

Example: 00 32 00 02 = Manual mode (0x00), 50% speed (0x32)

Installation

1. Arduino IDE Setup

Board Support:

  • ESP32 by Espressif Systems - version 3.3.2
    • Install via: Tools → Board → Boards Manager → search "esp32"
    • Includes BLE libraries (BLEDevice, BLEUtils, BLEClient, etc.)
    • Includes WebServer library for diagnostic web interface

Required Libraries (install via Sketch → Include Library → Manage Libraries):

Library Purpose Tested Version
FastLED LED control 3.10.3
PubSubClient MQTT client 2.8

2. Configure Credentials

Create secrets.h:

#ifndef SECRETS_H
#define SECRETS_H

const char* WIFI_SSID = "your_wifi_ssid";
const char* WIFI_PASSWORD = "your_wifi_password";
const char* MQTT_SERVER = "homeassistant.local";
const int MQTT_PORT = 1883;
const char* MQTT_USER = "mqtt";
const char* MQTT_PASSWORD = "your_mqtt_password";

#endif

3. Setup MQTT in Home Assistant

Enable the MQTT integration in Home Assistant and configure credentials. See the official MQTT integration documentation for setup instructions.

Important: Use the same MQTT username and password in your secrets.h file (step 2 above).

4. Upload to ESP32

  1. Connect ESP32 board via USB
  2. Select appropriate board in Arduino IDE (e.g., ESP32 Pico Kit for ATOM Lite)
  3. Upload AriaFanBLE.ino
  4. If using a different LED pin, update LED_PIN in the code

5. Home Assistant Configuration

Add to configuration.yaml:

# MQTT Devices
mqtt:
  fan:
    - name: "Aria Fan"
      unique_id: "aria_fan_ble"
      command_topic: "aria/on/set"
      state_topic: "aria/on/state"
      percentage_command_topic: "aria/speed/set"
      percentage_state_topic: "aria/speed/state"
      percentage_value_template: "{{ value }}"
      speed_range_min: 1
      speed_range_max: 100
      optimistic: false

  select:
    - name: "Aria Fan Mode"
      unique_id: "aria_fan_mode"
      command_topic: "aria/mode/set"
      state_topic: "aria/mode/state"
      options:
        - "Manual"
        - "Speed"
        - "Power"
        - "HR"
        - "Temp"
      optimistic: false

Restart Home Assistant after saving.

MQTT Topics

Topic Direction Description
aria/on/set Command Turn fan ON/OFF
aria/on/state State Fan ON/OFF state
aria/speed/set Command Set fan speed (0-100)
aria/speed/state State Current fan speed (0-100)
aria/mode/set Command Set mode (Manual/Speed/Power/HR/Temp)
aria/mode/state State Current mode
aria/log/info State Diagnostic log messages

Usage

Home Assistant

  1. Fan Control - Use the fan entity (fan.aria_fan) with speed slider
  2. Mode Selection - Use the select entity (select.aria_fan_mode)
  3. Automations - Trigger fan based on workout start/stop, time, etc.

Example automation:

automation:
  - alias: "Start fan on workout"
    trigger:
      - platform: state
        entity_id: binary_sensor.workout_active
        to: "on"
    action:
      - service: select.select_option
        target:
          entity_id: select.aria_fan_mode
        data:
          option: "HR"
      - service: fan.turn_on
        target:
          entity_id: fan.aria_fan

Fan Controls

Use the mode and speed buttons on the fan itself - changes sync to Home Assistant automatically.

Diagnostic Logging

The controller includes comprehensive logging for debugging and monitoring.

Web Interface

Access logs and diagnostics via web browser:

  • Visit http://<esp32-ip-address>/logs - View last 30 log entries with timestamps
  • Visit http://<esp32-ip-address>/status - JSON endpoint with heap and connection status
  • Dark theme for readability
  • Logs survive until ESP32 reboot
  • Returns 503 error if heap is critically low (prevents crashes)

Log format: [timestamp] message

Timestamps are synchronized via NTP (falls back to uptime if NTP not yet synced).

Example logs during normal operation (BLE reconnect, fan may have reset):

[2025-10-13 14:32:15] MQTT command: Set speed to 75%
[2025-10-13 14:32:16] BLE notification: Speed changed to 75%
[2025-10-13 15:10:05] BLE disconnected - will attempt reconnection
[2025-10-13 15:10:20] BLE reconnected - target state: mode 0x00, speed 75%
[2025-10-13 15:10:21] Fan current state after reconnection: mode Manual (0x00), speed 25%
[2025-10-13 15:10:21] Scheduling state restore: mode 0x00, speed 75%
[2025-10-13 15:10:21] Restoring target state: mode 0x00, speed 75%
[2025-10-13 15:10:22] BLE notification: Speed changed to 75%

After ESP32 power cycle (first boot, fan state unchanged):

[+10s] BLE reconnected - target state: mode 0x00, speed 25%
[+11s] Fan current state after reconnection: mode Manual (0x00), speed 60%
[+11s] First connection after boot - accepting fan state (not restoring)

Before NTP sync, logs use uptime format: [+123s] message

Status Endpoint

The /status endpoint provides JSON-formatted diagnostics:

{
  "version": "1.0.0",
  "heap_free": 21540,
  "heap_size": 234812,
  "heap_min_free": 5872,
  "psram_free": 0,
  "psram_size": 0,
  "cpu_freq_mhz": 240,
  "uptime_sec": 458,
  "wifi_connected": true,
  "mqtt_connected": true,
  "ble_connected": true,
  "log_count": 30,
  "log_capacity": 30
}

Key metrics:

  • heap_free - Current available heap memory
  • heap_min_free - Minimum free heap since boot (indicates memory pressure)
  • log_count / log_capacity - Current vs maximum log entries

Memory constraints: The ESP32 has limited heap (~235KB total). After WiFi, MQTT, and BLE initialization, approximately 20-40KB remains free. The BLE stack alone consumes ~94KB. If heap_min_free drops below 10KB, consider reducing log activity or checking for memory leaks.

Home Assistant Integration

Add this sensor to configuration.yaml to store logs in HA database:

mqtt:
  sensor:
    - name: "Aria Fan Log"
      state_topic: "aria/log/info"
      value_template: "{{ value }}"
      icon: "mdi:text-box"

Then view logs in:

  • History panel: sensor.aria_fan_log entity history
  • Developer Tools → MQTT: Subscribe to aria/log/info
  • Logbook: Shows all log events with timestamps

Logs are retained according to your Home Assistant recorder settings (default: 10 days).

Log Sources

All events are logged with their source:

Event Type Log Message Example
MQTT Command MQTT command: Set speed to 50%
BLE Notification BLE notification: Speed changed to 60%
Mode Change BLE notification: Mode changed to HR (0x03)
Disconnection BLE disconnected - will attempt reconnection
Reconnection BLE reconnected - target state: mode 0x00, speed 0%
State Restore Restoring target state: mode 0x00, speed 0%

This helps identify the source of unexpected fan behavior (e.g., if fan randomly changes speed, check if it's from MQTT or the fan itself).

Troubleshooting

Fan Not Found

  • Ensure fan is powered on and in range
  • Check Serial Monitor for scan results
  • LED should be yellow when scanning

MQTT Connection Failed

  • Verify credentials in secrets.h
  • Check MQTT broker is running in Home Assistant
  • Look for error code in Serial Monitor:
    • -4 = Connection timeout
    • 4 = Bad username/password
    • 5 = Not authorized

Disconnects on Temp Mode

  • Expected behavior - Temp mode uses BLE exclusively
  • ESP32 will auto-reconnect in 10-15 seconds
  • Consider removing "Temp" from Home Assistant options if unused

Speed Not Updating in Sensor Modes

  • Ensure ANT+ sensor is paired with the fan via Elite app
  • Speed updates come from the sensor, not the ESP32
  • ESP32 correctly reads and reports the fan's actual speed

Phantom Button Presses (Resolved)

Issue: Earlier versions experienced phantom button presses caused by WiFi power save mode glitching GPIO39 on the M5Stack ATOM Lite.

Solution: Physical button functionality has been removed from the code since the device is rack-mounted and controlled exclusively via Home Assistant/MQTT. This eliminates the phantom press issue entirely.

Reference: ESPHome Issue #3403 documents this GPIO glitch issue on M5Stack ATOM hardware.

Technical Notes

Protocol Details

The BLE protocol was discovered through observation and testing:

  1. nRF Connect - Inspected BLE services and characteristics
  2. BLE Sniffer - ESP32 sketch to capture Elite app commands
  3. Testing - Validated command formats with the fan

Implementation notes:

  • Status notifications use format [mode, speed, 0x00, 0x02] with mode in byte 0
  • Control commands are sent to characteristic 347B0040
  • Implementation uses 500ms delay after mode changes for stability

Connection Management

  • BLE scan runs every 10 seconds when disconnected
  • Old BLE clients are properly cleaned up before reconnection
  • 500ms delay after mode changes for stability

Project Files

AriaFanBLE.ino      # Main ESP32 client code
secrets.h           # WiFi/MQTT credentials (not in git)
README.md           # This documentation

Note: The BLE sniffer tool used for protocol discovery is maintained as a separate sketch.

Credits

  • Elite - Aria smart fan manufacturer
  • M5Stack - ATOM Lite hardware
  • FastLED - LED control library
  • PubSubClient - MQTT library

Versioning

This project uses Semantic Versioning. See CHANGELOG.md for release history.

Current version: 1.0.0

License

MIT License - See LICENSE file for details.

This is an unofficial implementation and is not affiliated with or endorsed by Elite S.r.l.


Developed with: Claude Code (Anthropic) Hardware: M5Stack ATOM Lite (ESP32) Target Device: Elite Aria Smart Fan

About

ESP32 BLE-to-MQTT bridge for the Elite Aria cycling fan. Control via Home Assistant with bi-directional sync and automatic reconnection.

Topics

Resources

License

Stars

Watchers

Forks

Languages