Aqara Gateway Modified to an ESPHome Item

by enggmaug in Circuits > Electronics

38 Views, 0 Favorites, 0 Comments

Aqara Gateway Modified to an ESPHome Item

01-Xiaomi-Aqara-Gateway-2-1095678101.jpg

Introduction

Many first-generation Aqara/Xiaomi gateways are now obsolete because users have migrated to Zigbee2MQTT, ZHA, or other local Zigbee coordinators.

Instead of throwing the hardware away, it can be repurposed into a fully local ESPHome device while keeping:

  1. The original enclosure
  2. The mains power supply
  3. The RGB light ring
  4. The built-in speaker

This project documents the reverse-engineering process used to identify the LED control interface and determine whether the original processor board could be replaced by an ESP32.

Supplies

1 - Unused Xiaomi / Aqara Gateway v2 (legacy LAN protocol)


2 - ESP32 flashed with ESPHome FW (many guides online)

==> I used anESP32-S3 Super Mini - It is cheap and does the job, in a tiny form factor


3 - MAX98357A Audio Amplifier

==> Also Cheap !

Open the Gateway

26-06-01 15-54-57 1065.jpg
26-06-01 15-55-21 1067.jpg

After disassembly, the gateway is composed of two stacked PCBs.

Upper PCB

Contains:

  1. Main processor
  2. Wi-Fi interface
  3. Zigbee radio
  4. Audio circuitry

Identified components:

  1. Xiaomi module MHCW03P
  2. NXP JN5169 Zigbee controller

Lower PCB

Contains:

  1. AC/DC power supply
  2. LED ring
  3. LED driver circuitry

The two boards communicate through a 14-pin mezzanine connector.

Map the Connector


On the 14 ways connector, you will need :

PinFunction

# 2 - GND

#4 - Red

#5 - Green

#6 - Blue

#10 - 5V

#14 - 3.3V

All other pins are not to be used for this project.


Pin #1 has a square copper pad and a silkscreen arrow marking.

I considered the Pins to be arranged like that :

#1 - #14

#2 - #13

#3 - #12

#4 - #11

#5 - #10

#6 - #9

#7 - #8

Reuse the Original Power Board

The original power board can be retained without modification.

We will use its LEDs and Power supply.


Required signals:

3.3 V
5 V
GND
R
G
B

The original processor board is no longer needed, you can discard it.

Install an ESP32

Connect the ESP32 directly to the RGB inputs.

Example wiring:

ESP32 Aqara PCB

GPIO11 ---> R
GPIO11 ---> G
GPIO12 ---> B

3.3V ---> 3.3V
GND ---> GND

Because the RGB inputs already accept 3.3 V PWM signals, no MOSFETs or level shifters are required.

Reuse the Speaker

The gateway contains a small 8 Ω speaker mounted directly in the enclosure.

An I²S audio amplifier can be added:

ESP32
MAX98357A
Original 8 Ω Speaker

This enables:

  1. Alarm sounds
  2. Voice announcements
  3. Home Assistant TTS
  4. Notification tones

The overall wiring is simple.

Just be carefull to avoid putting your gear in the center of the area, as this is where the speaker will come sit. Anticipate the wiring to allow you placing the boards in a convenient way.

ESP32 ==> MAX98537A ==> SPEAKER
GPIO7 > LRC
GPIO6 > SO
GPIO5 > BCLK
GPIO4 > DIN
HP + > SPEAKER +
HP - > SPEAKER -

ESPHome Configuration

Example of ESPHome RGB and sound configuration - this can be modified as you need... I did not try WAV files yet, but they surely must work if not too big. If you don't know coding, ask your favorite AI.

esphome:
name: lightsound
friendly_name: Light&Sound

esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
encryption:
# Put your API KEY :
key: "API KEY"

ota:
- platform: esphome
# Put your OTA PWD :
password: "OTA PWD"

wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enter object IP :
use_address: 192.168.1.xx
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Lightsound Fallback Hotspot"
password: "pwdToBeDefined"



captive_portal:

# -------------------------
# RGB Aqara ring
# -------------------------
output:
- platform: ledc
pin: GPIO10
id: red
frequency: 1200 Hz

- platform: ledc
pin: GPIO11
id: green
frequency: 1200 Hz

- platform: ledc
pin: GPIO12
id: blue
frequency: 1200 Hz

light:
- platform: rgb
name: "LightSound"
id: lightsound_rgb
red: red
green: green
blue: blue
restore_mode: RESTORE_DEFAULT_OFF
default_transition_length: 300ms
effects:
- strobe:
name: "Intrusion"
colors:
- state: true
red: 100%
green: 0%
blue: 0%
duration: 200ms
- state: false
duration: 200ms

- strobe:
name: "Fire"
colors:
- state: true
red: 100%
green: 100%
blue: 100%
duration: 300ms
- state: false
duration: 200ms

- strobe:
name: "Water Leak"
colors:
- state: true
red: 0%
green: 0%
blue: 100%
duration: 300ms
- state: false
duration: 300ms

- pulse:
name: "Technical"
transition_length: 500ms
update_interval: 700ms

# -------------------------
# MAX98357A I2S amplifier
# -------------------------
switch:
- platform: gpio
name: "LightSound Amplifier"
id: max98357_enable
pin: GPIO6
restore_mode: ALWAYS_OFF

- platform: template
name: "LightSound Intrusion Loop"
id: intrusion_loop
optimistic: true
restore_mode: ALWAYS_OFF
on_turn_on:
- switch.turn_on: max98357_enable
- delay: 50ms
- script.execute: intrusion_alarm
on_turn_off:
- script.stop: intrusion_alarm
- rtttl.stop
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- platform: template
name: "LightSound Fire Loop"
id: fire_loop
optimistic: true
restore_mode: ALWAYS_OFF
on_turn_on:
- switch.turn_on: max98357_enable
- delay: 50ms
- script.execute: fire_alarm
on_turn_off:
- script.stop: fire_alarm
- rtttl.stop
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- platform: template
name: "LightSound Water Leak Loop"
id: water_loop
optimistic: true
restore_mode: ALWAYS_OFF
on_turn_on:
- switch.turn_on: max98357_enable
- delay: 50ms
- script.execute: water_alarm
on_turn_off:
- script.stop: water_alarm
- rtttl.stop
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

i2s_audio:
- id: i2s_out
i2s_lrclk_pin: GPIO7
i2s_bclk_pin: GPIO5

speaker:
- platform: i2s_audio
id: max98357_speaker
dac_type: external
i2s_audio_id: i2s_out
i2s_dout_pin: GPIO4
channel: mono
sample_rate: 16000
bits_per_sample: 16bit
buffer_duration: 100ms

rtttl:
id: lightsound_rtttl
speaker: max98357_speaker
gain: 0.7

# -------------------------
# Buttons
# -------------------------
button:
- platform: template
name: "LightSound Stop"
on_press:
- script.execute: stop_all

- platform: template
name: "LightSound Notification"
on_press:
- script.execute: notification_alert

- platform: template
name: "LightSound Action Required"
on_press:
- script.execute: action_required_alert

- platform: template
name: "LightSound Technical Alert"
on_press:
- script.execute: technical_alert

- platform: template
name: "LightSound Doorbell"
on_press:
- script.execute: doorbell_alert

- platform: template
name: "LightSound School Time"
on_press:
- script.execute: school_alert

- platform: template
name: "LightSound Wake Up"
on_press:
- script.execute: wakeup_alert

- platform: template
name: "LightSound Internet Down"
on_press:
- script.execute: internet_down_alert

- platform: template
name: "LightSound Internet Restored"
on_press:
- script.execute: internet_restored_alert

- platform: template
name: "LightSound NAS Error"
on_press:
- script.execute: nas_error_alert

- platform: template
name: "LightSound 3D Print Done"
on_press:
- script.execute: print_done_alert

- platform: template
name: "LightSound 3D Print Paused"
on_press:
- script.execute: print_paused_alert

# -------------------------
# Scripts
# -------------------------
script:
- id: stop_all
mode: restart
then:
- switch.turn_off: intrusion_loop
- switch.turn_off: fire_loop
- switch.turn_off: water_loop
- script.stop: intrusion_alarm
- script.stop: fire_alarm
- script.stop: water_alarm
- rtttl.stop
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: notification_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 0%
green: 40%
blue: 100%
brightness: 50%
- rtttl.play: "notify:d=16,o=6,b=120:e,g"
- delay: 2s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: action_required_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 100%
green: 45%
blue: 0%
brightness: 80%
- rtttl.play: "action:d=8,o=5,b=120:c,p,c"
- delay: 4s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: technical_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 60%
green: 0%
blue: 100%
brightness: 90%
effect: "Technical"
- rtttl.play: "tech:d=8,o=5,b=100:d,p,d,p,2a"
- delay: 6s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: doorbell_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 0%
green: 30%
blue: 100%
brightness: 80%
- rtttl.play: "door:d=4,o=5,b=90:g,2c"
- delay: 3s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: school_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 0%
green: 80%
blue: 100%
brightness: 80%
- rtttl.play: "school:d=8,o=5,b=140:g,c6,e6,2g6"
- delay: 5s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: wakeup_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 100%
green: 25%
blue: 0%
brightness: 25%
- rtttl.play: "wakeup:d=8,o=5,b=80:c,e,g,p,c6"
- delay: 5s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: internet_down_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 100%
green: 0%
blue: 0%
brightness: 80%
- rtttl.play: "netdown:d=8,o=5,b=100:c6,a,f,c"
- delay: 8s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: internet_restored_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 0%
green: 100%
blue: 0%
brightness: 80%
- rtttl.play: "netok:d=8,o=5,b=140:c,e,g,c6"
- delay: 4s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: nas_error_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 60%
green: 0%
blue: 100%
brightness: 90%
effect: "Technical"
- rtttl.play: "naserr:d=8,o=5,b=90:d,p,d,p,2f"
- delay: 8s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: print_done_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 0%
green: 100%
blue: 0%
brightness: 70%
- rtttl.play: "printok:d=8,o=5,b=140:c,e,g,2c6"
- delay: 5s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: print_paused_alert
mode: restart
then:
- script.execute: stop_all
- switch.turn_on: max98357_enable
- delay: 50ms
- light.turn_on:
id: lightsound_rgb
red: 100%
green: 45%
blue: 0%
brightness: 80%
- rtttl.play: "pause:d=8,o=5,b=100:a,p,a"
- delay: 8s
- light.turn_off: lightsound_rgb
- switch.turn_off: max98357_enable

- id: intrusion_alarm
mode: restart
then:
- while:
condition:
switch.is_on: intrusion_loop
then:
- light.turn_on:
id: lightsound_rgb
red: 100%
green: 0%
blue: 0%
brightness: 100%
effect: "Intrusion"
- rtttl.play: "intrude:d=16,o=5,b=220:a,c6,a,c6,a,c6,a,c6"
- delay: 3s

- id: fire_alarm
mode: restart
then:
- while:
condition:
switch.is_on: fire_loop
then:
- light.turn_on:
id: lightsound_rgb
red: 100%
green: 100%
blue: 100%
brightness: 100%
effect: "Fire"
- rtttl.play: "fire:d=4,o=6,b=120:a,p,a,p,a,2p"
- delay: 4s

- id: water_alarm
mode: restart
then:
- while:
condition:
switch.is_on: water_loop
then:
- light.turn_on:
id: lightsound_rgb
red: 0%
green: 0%
blue: 100%
brightness: 100%
effect: "Water Leak"
- rtttl.play: "water:d=8,o=4,b=90:c,p,c,p,c"
- delay: 4s

On the Way

I am struggling adding more pics... The site will not let me... weird. I'll come back later.