Files
deskscreen/FW/AIP650E_COMPONENT_SPEC.md
T
2026-05-13 11:45:58 +02:00

32 KiB

ESPHome AiP650E Display Monitor Component Specification

Microcontroller Proxy Architecture

Document Version: 3.0
Date: April 2026
Target Platform: ESP32-S3 (with optional AVR/STM32 proxy)
Original IC: Wuxi I-CORE AiP650E (2-line Serial Interface LED Controller/Driver)


1. Overview

This specification describes an ESPHome custom component for reading standing desk display information via a protocol proxy microcontroller. The architecture uses a small AVR or STM32 microcontroller to reliably capture the time-critical AiP650E protocol from the original desk controller, then exposes decoded display data to the ESP32-S3 via I2C.

1.1 Architecture Rationale

Given that the ESP32-S3 is already fully loaded with:

  • High-resolution display rendering (SPI)
  • Capacitive touchscreen input processing
  • WiFi/BLE connectivity with background tasks

A dedicated protocol proxy microcontroller provides:

  • Reliable timing capture - No jitter from WiFi, rendering, or touch processing
  • Clean separation of concerns - Timing logic isolated from UI/connectivity logic
  • Simpler integration - ESP32 reads simple I2C data instead of complex protocol capture
  • Better real-time guarantees - AVR/STM32 can dedicate 100% resources to protocol
  • Inexpensive - Arduino Nano, STM32F401, etc. cost $5-15

1.2 System Overview

The original control board's microcontroller continues to manage motor control, height calculations via Hall sensors, and display updates. A small proxy microcontroller captures the AiP650E bus communication and makes display data available to the ESP32-S3 via I2C. The ESP32-S3 runs ESPHome with a simple I2C driver component to read display values and button states, focusing on UI rendering and Home Assistant integration.


2. System Architecture

┌───────────────────────────────────────────────────────────────────┐
│                  Standing Desk Control System                     │
└───────────────────────────────────────────────────────────────────┘

ORIGINAL MICROCONTROLLER (32-bit MCU) - Original Board
├── Function: Motor control, height calculation, display updates
├── Connected:
│   ├── H-Bridge Motor Driver (pins 1, 2, 3, 5)
│   ├── Hall Sensor Inputs (pins 11, 12, 17, 18)
│   ├── Motor Sense/Voltage Sense (pins 19, 20)
│   ├── SEGM_CLK (pin 16) ──────────┐
│   └── SEGM_DIO  (pin 15) ──────────┼──→ [ATtiny85 Proxy]
│                                     │
└── Behavior:                          │
    └── Sends display commands via CLK/DIO

ATtiny85 PROXY MICROCONTROLLER (8-pin DIP)
├── Function: AiP650E protocol capture & I2C bridge
├── Pin Assignment (8 pins total):
│   ├── Pin 2: PB3 ← SEGM_CLK (from original, CLK input/interrupt)
│   ├── Pin 3: PB4 ← SEGM_DIO (from original, DIO data input)
│   ├── Pin 5: PB0 → SCL (to ESP32 I2C, with 4.7k pull-up)
│   ├── Pin 6: PB1 → SDA (to ESP32 I2C, with 4.7k pull-up)
│   ├── Pin 7: PB2 (spare for future use)
│   ├── Pin 4: GND (common ground)
│   └── Pin 8: VCC (3.3V from ESP32)
└── Behavior:
    ├── Captures CLK/DIO protocol continuously
    ├── Decodes display segments in real-time
    ├── Exposes 7 I2C registers (0x00-0x04 + control)
    └── 100% reliability (no WiFi/render jitter)

ESP32-S3 (ESPHome) - With Touchscreen
├── Function: UI rendering, touch input, WiFi, Home Assistant
├── Connected:
│   ├── Display panel (SPI) - 30+ pins
│   ├── Touch controller (I2C/SPI)
│   ├── I2C SCL (GPIO21) ← ATtiny85 (reads display data)
│   ├── I2C SDA (GPIO20) ← ATtiny85 (reads display data)
│   ├── GPIO14 ← KEY_UP (direct from original board pin 14)
│   ├── GPIO13 ← KEY_DOWN (direct from original board pin 13)
│   └── WiFi antenna (internal)
└── Behavior:
    ├── Renders touchscreen UI
    ├── Polls I2C every 100ms for display data
    ├── Updates Home Assistant via WiFi
    ├── Handles touch input & button monitoring
    └── Lightweight I2C component (simple polling)

2.1a Hardware Wiring Summary

┌─────────────────┐  ┌──────────────────┐  ┌─────────────────┐
│    Original     │  │    ATtiny85      │  │    ESP32-S3     │
│    Desk Micro   │  │    Proxy         │  │  (ESPHome)      │
├─────────────────┤  ├──────────────────┤  ├─────────────────┤
│ Pin 16 (CLK)  ──┼─→ Pin 2 (PB3)     │  │                 │
│ Pin 15 (DIO)  ──┼─→ Pin 3 (PB4)     │  │                 │
│ Pin 14 (UP)   ──┼──────────────────────→ GPIO14          │
│ Pin 13 (DOWN) ──┼──────────────────────→ GPIO13          │
│                 │  Pin 5 (PB0/SCL) ─┬──→ GPIO21 (SCL)   │
│                 │  Pin 6 (PB1/SDA) ─┼──→ GPIO20 (SDA)   │
│ GND          ───┼─→ Pin 4 (GND) ────┼──→ GND            │
│                 │  Pin 8 (VCC) ◄────┼── GPIO 3.3V       │
│                 │                    │  [4.7k pulls]    │
└─────────────────┘  └──────────────────┘  └─────────────────┘

Key Points:

  • Buttons (UP/DOWN) connect directly to ESP32 - no proxy involvement
  • ATtiny85 focuses only on protocol capture (isolated, reliable)
  • ESP32 does UI/WiFi/rendering without timing pressure
  • Minimal wiring: just 4 GPIO + I2C + GND + 3.3V

AiP650E DISPLAY DRIVER ├── Status: PHYSICALLY REMOVED from board ├── Previously connected to: │ ├── Pin 2 (CLK) ← Original Micro (now disconnected) │ ├── Pin 3 (DIO) ← Original Micro (now disconnected) │ └── Segments/Grids (no longer needed) └── Replacement: ESP32-S3 software protocol decoder


### 2.2 Hardware Connections

ORIGINAL MICROCONTROLLER ESP32-S3 ┌──────────────────────┐ ┌──────────┐ │ │ │ │ │ Pin 16 (SEGM_CLK) ├─────────→ GPIO_CLK │ │ Pin 15 (SEGM_DIO) ├─────────→ GPIO_DIO │ │ │ │ │ │ [Motor & Hall] │ │ [WiFi] │ │ (isolated) │ │ [Touch] │ └──────────────────────┘ └──────────┘ │ │ │ [To Touchscreen] │ ┌────┴────┐ │ Removed │ │ AiP650E │ (Physically removed from board) │ Display │ │ Driver │ └─────────┘

BUTTONS (Shared or Duplicated) ┌──────────────┐ │ Key_Up ├─→ ESP32-S3 GPIO │ Key_Down ├─→ ESP32-S3 GPIO │ Mem 1 (K15) ├─→ Originally via AiP650E key matrix │ Mem 2 (K12) ├─→ (No longer accessible - can be removed) │ Mem 3 (K11) ├─→ (No longer accessible - can be removed) └──────────────┘


### 2.3 Protocol Monitoring Role

The ESP32-S3 acts as a **passive listener** on the AiP650E communication bus:

- **Does NOT send commands** to the AiP650E
- **Does NOT interfere** with original microcontroller operation
- **Listens to** CLK/DIO signals originating from the original microcontroller
- **Decodes** display data, brightness, and mode commands
- **Extracts** segment information from data writes to RAM addresses 0x68, 0x6A, 0x6C
- **Reports** findings to ESPHome as sensors and binary sensors

This approach ensures:
- ✅ Original motor control continues unaffected
- ✅ Height sensing via Hall sensors remains intact
- ✅ Display information is available to touchscreen UI
- ✅ No conflicts or bus contention
- ✅ Simple hardware modifications (just connect two lines and buttons)

---

## 3. Protocol Monitoring & Decoding

### 3.1 2-Line Serial Interface (Listener Perspective)

The ESP32-S3 monitors a 2-line serial protocol used by the original microcontroller to communicate with the AiP650E:

- **CLK (from original micro):** Clock signal - provides timing reference
- **DIO (from original micro):** Data signal - carries command and data bytes (open-drain, pulled high by resistor)

### 3.2 Bus Monitoring Basics

**Frame Structure (as monitored):**
- Start Condition: CLK high, DIO falling edge (high → low)
- Data Phase: 8 bits + 1 ACK bit (9 bits total per byte)
- Stop Condition: CLK high, DIO rising edge (low → high)

**Timing Requirements for Listening:**
- CLK low level width: typically ≥100 ns
- CLK high level width: typically ≥100 ns
- Minimum delay to sample DIO after CLK edge: ≥30 ns (add margin in software)
- Maximum transmission rate: 0-4 Mbps (typical 1-2 Mbps for this application)

**Key Difference - Listener Role:**
- ESP32 does NOT drive CLK or DIO low (no open-drain outputs)
- ESP32 only reads CLK and DIO states as inputs
- ESP32 uses GPIO interrupts on CLK rising edges to time DIO sampling
- No need to generate ACK bit (original micro is the master)

### 3.3 Command Decoding

The ESP32 monitors and decodes instructions sent by the original microcontroller:

**Instruction Format:** All instructions are 16-bit (2 bytes) transmitted MSB first:

[System Instruction Byte] [Display/Control Instruction Byte]


**System Instruction Byte (monitoring):**
- Expected value: `0x48` (binary: `01001000`)
- If received: System is being configured
- Component logs this state

**Display Instruction Byte (monitoring):**

Bit Position: B7 B6 B5 B4 B3 B2 B1 B0 Display Mode: D (B0: Display on=1 / off=0) Sleep Mode: W (B1: Sleep enable=1 / disable=0) SEG Control: S (B3: Seven-segment=1 / eight=0) Brightness: BR[2:0] (B5-B3: Brightness level 0-7)


**Monitored Parameters:**
- **Display On/Off:** Track whether display is active
- **Brightness Level:** 0=1 level, 1=2 levels, ..., 7=8 levels (standard range)
- **Display Mode:** 7-segment or 8-segment configuration
- **Sleep Mode:** Whether sleep mode is enabled

### 3.4 Display Data Monitoring

Display data is written to RAM addresses on the AiP650E. The ESP32 intercepts these writes:

**RAM Address Mapping (Monitored):**
Address Data Bits (B7-B0) Display
0x68 A B C D E F G DP DIG1 (leftmost/hundreds)
0x6A A B C D E F G DP DIG2 (middle/tens)
0x6C A B C D E F G DP DIG3 (rightmost/ones)
0x6E (reserved) DIG4 (not used for digits)

**Segment Decoding:**
- **A-G:** Standard 7-segment display segments (bits 0-6)
- **DP:** Decimal point (bit 7)

When the component detects writes to these addresses, it:
1. Captures the segment byte
2. Decodes which digit is shown (0-9) based on segment pattern
3. Reports the value as a sensor update
4. Stores raw segment data for debug purposes

### 3.5 Key Press Monitoring

The original microcontroller can request key data from the AiP650E. The ESP32 intercepts these requests:

**Get Key Command (monitored):**

Bit: B7 B6 B5 B4 B3 B2 B1 B0 Val: 0 1 0 0 1 X X 1 (0x49 with variable bits)


**Key Data Response Format (captured):**

Format: 01_BBBKKKK (8 bits data + 1 ACK bit)

  • BBB: Button row (0-3 for DIG1-DIG4)
  • KKK: Button column (0-7 for KI1-KI7)
  • Special: 00_101_110 (0x2E) = No key pressed

**Memory Button Status (via AiP650E monitoring):**

| Memory Button | AiP650E Row | AiP650E Column | Expected Value |
|---------------|------------|----------------|----------------|
| Mem 1 (K15 / KI5) | DIG4 (3) | KI5 (4) | `01_011_100` |
| Mem 2 (K12 / KI2) | DIG4 (3) | KI2 (1) | `01_001_100` |
| Mem 3 (K11 / KI1) | DIG4 (3) | KI1 (0) | `01_000_100` |
| No Key | Any | - | `00_101_110` (0x2E) |

**Note:** Memory button monitoring via AiP650E is optional - direct physical button connections to ESP32 GPIO are more reliable.

**Memory Button Status (via AiP650E monitoring):**

| Memory Button | AiP650E Row | AiP650E Column | Expected Value |
|---------------|------------|----------------|----------------|
| Mem 1 (K15 / KI5) | DIG4 (3) | KI5 (4) | `01_011_100` |
| Mem 2 (K12 / KI2) | DIG4 (3) | KI2 (1) | `01_001_100` |
| Mem 3 (K11 / KI1) | DIG4 (3) | KI1 (0) | `01_000_100` |
| No Key | Any | - | `00_101_110` (0x2E) |

**Note:** Memory button monitoring via AiP650E is optional - direct physical button connections to ESP32 GPIO are more reliable for the primary up/down buttons.

---

## 4. Component Specification

### 4.1 Component Name

```yaml
display_driver_aip650e:

4.2 Configuration Parameters

display_driver_aip650e:
  # Required: CLK and DIO pin definitions (listeners, not drivers)
  clk_pin: GPIO_PIN_NUMBER
  dio_pin: GPIO_PIN_NUMBER
  
  # Optional: Button GPIO pins
  button_up_pin: GPIO_PIN_NUMBER    # PIN 14 on original board (KEY_UP)
  button_down_pin: GPIO_PIN_NUMBER  # PIN 13 on original board (KEY_DOWN)
  
  # Optional: Display monitoring interval (default: 100ms)
  # How often to check for new display data from the original micro
  update_interval: 100ms
  
  # Optional: Enable button monitoring (default: true)
  enable_buttons: true
  
  # Optional: Debounce time for physical buttons (default: 50ms)
  button_debounce_time: 50ms
  
  # Optional: AiP650E protocol monitoring settings
  protocol:
    # Attempt to decode memory buttons from AiP650E bus (default: false)
    # Set to true only if keeping original memory button hardware
    monitor_memory_buttons: false
    
    # Communication timeout (default: 1000ms)
    # If no CLK edges detected for this duration, consider bus inactive
    timeout: 1000ms

4.3 Exposed Sensors

4.3.1 Display Value Sensor

Exposes the 3-digit height value being displayed by the original control board:

sensor:
  - platform: display_driver_aip650e
    name: "Standing Desk Height Display"
    id: desk_height_display
    unit_of_measurement: "mm"
    icon: "mdi:ruler-square"

Sensor Properties:

  • Type: Numeric sensor
  • Range: 0-999 (3 digits)
  • Update Rate: Whenever the original microcontroller updates the display (typically 1-10 times per second)
  • Source: Decoded from segment data on 0x68, 0x6A, 0x6C RAM addresses
  • Reliability: Mirrors exactly what the original display shows

4.3.2 Individual Digit Sensors

Optionally expose each digit separately:

sensor:
  - platform: display_driver_aip650e
    digit: 1  # DIG1 - hundreds
    name: "Desk Height - Hundreds"
  
  - platform: display_driver_aip650e
    digit: 2  # DIG2 - tens
    name: "Desk Height - Tens"
  
  - platform: display_driver_aip650e
    digit: 3  # DIG3 - ones
    name: "Desk Height - Ones"

4.3.3 Segment Data Sensor (Debug)

For troubleshooting, optionally expose raw segment data:

text_sensor:
  - platform: display_driver_aip650e
    name: "Display Raw Segments"
    id: raw_segments

Returns segment data as: "DIG1: ABCDEFGDP, DIG2: ..., DIG3: ..."

4.3.4 Display Control Status (Monitored)

Optional text sensor showing monitored control states:

text_sensor:
  - platform: display_driver_aip650e
    name: "Display Status"
    id: display_status

Returns: "on/off, brightness: X, mode: 7-seg/8-seg, ..."


5. Button Interface

5.1 Physical Button Sensors

The component monitors the physical KEY_UP and KEY_DOWN buttons connected directly to the ESP32 GPIO:

binary_sensor:
  - platform: display_driver_aip650e
    name: "UP Button"
    id: key_up
    button: up  # or specify: button_pin_name: up
    on_press:
      then:
        - logger.log: "UP button pressed"
    on_release:
      then:
        - logger.log: "UP button released"
  
  - platform: display_driver_aip650e
    name: "DOWN Button"
    id: key_down
    button: down
    on_press:
      then:
        - logger.log: "DOWN button pressed"

Button Characteristics:

  • Type: Binary sensor (pressed/released)
  • Source: Direct GPIO connection (pins 13, 14 on original board)
  • Debounce: Configurable (default 50ms)
  • Update Rate: Immediate on press/release

5.2 Optional Memory Button Monitoring

If the original memory button hardware is retained and connected to GPIO pins, they can be monitored:

binary_sensor:
  - platform: display_driver_aip650e
    name: "Memory Button 1"
    id: mem_button_1
    button: memory_1  # Optional: alternative to GPIO-based buttons
    on_press:
      then:
        - logger.log: "Memory 1 pressed"
  
  - platform: display_driver_aip650e
    name: "Memory Button 2"
    id: mem_button_2
    button: memory_2
  
  - platform: display_driver_aip650e
    name: "Memory Button 3"
    id: mem_button_3
    button: memory_3

Note: Memory buttons are NOT decoded from the AiP650E bus by default (unreliable). Instead, they should be wired directly to GPIO pins if needed.

5.3 Button Configuration

display_driver_aip650e:
  # Physical button pins
  button_up_pin: GPIO14
  button_down_pin: GPIO13
  
  # Optional memory button pins (if wired to ESP32)
  button_mem1_pin: GPIO_PIN  # Optional
  button_mem2_pin: GPIO_PIN  # Optional
  button_mem3_pin: GPIO_PIN  # Optional
  
  # Debouncing settings
  button_debounce_time: 50ms

6. Display Monitoring & Integration

6.1 Real-Time Display Monitoring

The component continuously monitors the AiP650E communication bus and extracts display information in real-time:

  • Segment Capture: Intercepts writes to display RAM (0x68, 0x6A, 0x6C)
  • Digit Decoding: Converts segment patterns to digit values (0-9)
  • State Tracking: Monitors brightness, display on/off status
  • Update Rate: Responds immediately to changes from original microcontroller

6.2 Integration with Touchscreen UI

The exposed sensors can be used to synchronize a touchscreen display with the original height information:

# Example: Display the monitored height on touchscreen
display_runner:
  - id: my_display
    components:
      - lambda: |-
          it.printf(10, 10, id(font), "Height: %.0fmm", id(desk_height_display).state);

6.3 Home Automation Integration

Button states can trigger automations:

automation:
  - trigger:
      platform: binary_sensor
      id: key_up
      action: press
    then:
      - logger.log: "User pressed UP - height increasing"
      - homeassistant.service:
          service: input_number.set_value
          data_template:
            entity_id: input_number.desk_height_setpoint
            value: "{{ (states('input_number.desk_height_actual') | float(0) + 10) | round(0) }}"

6.4 Limitations & Constraints

⚠️ Important Note: The ESP32 component is a passive listener only:

  • Cannot control the display (original microcontroller is the master)
  • Cannot modify display values shown
  • Cannot control motor directly (connected to original microcontroller)
  • Can read what the original system is displaying
  • Can read button inputs
  • Can provide UI feedback via touchscreen

To control the motor or display, communicate with the original microcontroller via a separate interface (if available, e.g., UART, I²C).


7. Configuration Examples

7.1 Basic Configuration (Listener Mode)

esphome:
  name: standing-desk-controller

esp32_s3:
  board: esp32-s3-devkitc-1
  variant: esp32s3

# Display driver monitor component
display_driver_aip650e:
  # Listen to SEGM_CLK and SEGM_DIO from original microcontroller
  clk_pin: GPIO16     # Connected to original board pin 16 (SEGM_CLK)
  dio_pin: GPIO15     # Connected to original board pin 15 (SEGM_DIO)
  
  # Monitor physical buttons
  button_up_pin: GPIO14    # Connected to original board pin 14 (KEY_UP)
  button_down_pin: GPIO13  # Connected to original board pin 13 (KEY_DOWN)
  
  # Update settings
  update_interval: 100ms
  button_debounce_time: 50ms

sensor:
  - platform: display_driver_aip650e
    name: "Desk Height Display"
    id: desk_height_display
    unit_of_measurement: "mm"
    icon: "mdi:ruler-square"

binary_sensor:
  - platform: display_driver_aip650e
    name: "UP Button"
    id: key_up
    button: up
    icon: "mdi:arrow-up"
  
  - platform: display_driver_aip650e
    name: "DOWN Button"
    id: key_down
    button: down
    icon: "mdi:arrow-down"

7.2 Advanced Configuration with Touchscreen Integration

esphome:
  name: standing-desk-esp32

esp32_s3:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  psram:
    mode: quad
    speed: 80MHz

# Display driver monitor
display_driver_aip650e:
  clk_pin: GPIO16
  dio_pin: GPIO15
  button_up_pin: GPIO14
  button_down_pin: GPIO13
  
  update_interval: 50ms
  button_debounce_time: 75ms
  
  protocol:
    timeout: 1000ms

# Touchscreen display
spi:
  clk_pin: GPIO6
  mosi_pin: GPIO7
  miso_pin: GPIO8

display:
  - platform: ili9xxx
    id: my_display
    model: ILI9488
    cs_pin: GPIO5
    dc_pin: GPIO9
    reset_pin: GPIO10
    update_interval: 200ms
    
    lambda: |-
      // Display the monitored height
      it.printf(160, 50, id(font_large), TextAlign::CENTER, 
                "Height: %.0f mm", id(desk_height_display).state);
      
      // Show button status
      if (id(key_up).state) {
        it.filled_rectangle(10, 120, 300, 50, COLOR_WHITE);
        it.printf(160, 145, id(font), TextAlign::CENTER, "UP PRESSED");
      }
      if (id(key_down).state) {
        it.filled_rectangle(10, 180, 300, 50, COLOR_WHITE);
        it.printf(160, 205, id(font), TextAlign::CENTER, "DOWN PRESSED");
      }

sensor:
  - platform: display_driver_aip650e
    name: "Desk Height Display"
    id: desk_height_display
    unit_of_measurement: "mm"
    icon: "mdi:ruler-square"
    on_value:
      then:
        - logger.log: 
            format: "Display height updated to: %.0f mm"
            args: ['x']
  
  - platform: display_driver_aip650e
    digit: 1
    name: "Height Hundreds"
    id: height_hundreds
  
  - platform: display_driver_aip650e
    digit: 2
    name: "Height Tens"
    id: height_tens
  
  - platform: display_driver_aip650e
    digit: 3
    name: "Height Ones"
    id: height_ones

text_sensor:
  - platform: display_driver_aip650e
    name: "Display Status"
    id: display_status

binary_sensor:
  - platform: display_driver_aip650e
    name: "UP Button"
    id: key_up
    button: up
    on_press:
      then:
        - logger.log: "UP button pressed - desk moving up"
    on_release:
      then:
        - logger.log: "UP button released"
  
  - platform: display_driver_aip650e
    name: "DOWN Button"
    id: key_down
    button: down
    on_press:
      then:
        - logger.log: "DOWN button pressed - desk moving down"
    on_release:
      then:
        - logger.log: "DOWN button released"

# Home Assistant integration
api:
  encryption:
    key: !secret api_key

ota:
  password: !secret ota_password

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  
  ap:
    ssid: "DeskScreen Fallback"
    password: !secret ap_password

7.3 Minimal Configuration (Monitor Only, No Buttons)

on_value:
  then:
    - logger.log: 
        format: "Display height updated to: %.0f mm"
        args: ['x']
  • platform: display_driver_aip650e digit: 1 name: "Height Hundreds" id: height_hundreds

  • platform: display_driver_aip650e digit: 2 name: "Height Tens" id: height_tens

  • platform: display_driver_aip650e digit: 3 name: "Height Ones" id: height_ones

text_sensor:

  • platform: display_driver_aip650e name: "Display Status" id: display_status

binary_sensor:

  • platform: display_driver_aip650e name: "UP Button" id: key_up button: up on_press: then: - logger.log: "UP button pressed - desk moving up" on_release: then: - logger.log: "UP button released"

  • platform: display_driver_aip650e name: "DOWN Button" id: key_down button: down on_press: then: - logger.log: "DOWN button pressed - desk moving down" on_release: then: - logger.log: "DOWN button released"

Home Assistant integration

api: encryption: key: !secret api_key

ota: password: !secret ota_password

wifi: ssid: !secret wifi_ssid password: !secret wifi_password

ap: ssid: "DeskScreen Fallback" password: !secret ap_password


### 7.3 Minimal Configuration (Monitor Only, No Buttons)

```yaml
esphome:
  name: standing-desk-monitor

esp32_s3:
  board: esp32-s3-devkitc-1

display_driver_aip650e:
  clk_pin: GPIO16
  dio_pin: GPIO15
  # Note: button pins omitted - can be added later if needed

sensor:
  - platform: display_driver_aip650e
    name: "Desk Height"
    id: desk_height_display

8. Data Structures

8.1 Segment Encoding

Standard 7-segment display layout:

     AAAA
    F    B
     GGGG
    E    C
     DDDD  DP

Bit Mapping (byte):
Bit 7: DP (decimal point)
Bit 6: G  (middle segment)
Bit 5: F  (top-left)
Bit 4: E  (bottom-left)
Bit 3: D  (bottom)
Bit 2: C  (bottom-right)
Bit 1: B  (top-right)
Bit 0: A  (top)

8.2 7-Segment Character Set (Common)

Char Hex Binary Display
0 0x3F 0011_1111 0
1 0x06 0000_0110 1
2 0x5B 0101_1011 2
3 0x4F 0100_1111 3
4 0x66 0110_0110 4
5 0x6D 0110_1101 5
6 0x7D 0111_1101 6
7 0x07 0000_0111 7
8 0x7F 0111_1111 8
9 0x6F 0110_1111 9

8.3 Component Internal State

component_state:
  comm_protocol:
    clk_pin: GPIO number
    dio_pin: GPIO number
    last_clk_state: boolean
    last_dio_state: boolean
    comm_timeout: ms
    
  display_state:
    digit1_segments: byte (0-255)
    digit2_segments: byte (0-255)
    digit3_segments: byte (0-255)
    brightness_level: 1-8
    power_on: boolean
    mode_7segment: boolean
    
  button_state:
    button1_pressed: boolean
    button2_pressed: boolean
    button3_pressed: boolean
    debounce_counters: [0, 0, 0]
    last_key_value: byte

9. Implementation Notes

9.1 Bus Listening & Protocol Decoding

  • No Active Driving: ESP32 only reads CLK and DIO - does not drive them low (no open-drain output)
  • GPIO Interrupt Handler: Use CLK rising edge interrupt to trigger DIO sampling
  • Timing Accuracy: Sample DIO on CLK rising edges with ≥30ns setup margin
  • Bit Shift Register: Accumulate 8 bits per clock cycle, detect start/stop conditions

9.2 Hardware Connections

  • CLK & DIO Lines: Connect directly from original microcontroller through appropriate level shifters (if voltage differs)
  • Voltage Levels: If original micro is 5V and ESP32 is 3.3V, use voltage divider or level shifter for DIO input
  • Pull-ups: CLK and DIO typically have internal or external pull-ups (10k) - no additional pull-ups needed on ESP32 side
  • Ground: Ensure common ground between original microcontroller and ESP32

9.3 Display Data Extraction

  • RAM Address Monitoring: Intercept writes to addresses 0x68 (DIG1), 0x6A (DIG2), 0x6C (DIG3)
  • Segment Decoding: Convert captured segment bytes to 7-segment patterns, match against known digit set (0-9)
  • Timing: Updates happen naturally as the original micro commands the display - no polling required
  • Jitter Reduction: Debounce rapid segment changes (may indicate display refresh) before reporting state change

9.4 Button Input Handling

  • Physical Buttons (UP/DOWN): Simple GPIO input with software debouncing
  • Debounce Implementation: Wait for 2 consecutive stable readings before reporting state change
  • Debounce Duration: Configurable, typically 50-75ms
  • Optional Memory Buttons: If wired to GPIO, use same debounce logic

9.5 Edge Cases & Error Handling

  • Bus Inactivity: If no CLK transitions for > 1 second, consider bus inactive/error
  • Partial Frames: If data frame is incomplete, discard and wait for next frame
  • ACK Bit Handling: Don't generate ACK (ESP32 is listener only) - just skip bit 9
  • Original Micro Disconnection: Component gracefully handles loss of bus activity, reports "offline" status
  • Voltage Spike Protection: Add 100nF capacitor near GPIO inputs if experiencing noise

10. Troubleshooting Guide

Issue Likely Cause Solution
No data received Incorrect GPIO pins Verify GPIO16 (CLK) and GPIO15 (DIO) are set correctly in config
No data received Hardware not connected Check physical wiring: original micro pin 16→GPIO16, pin 15→GPIO15
No data received Voltage levels If original micro is 5V, add level shifter for DIO line
Display values incorrect Segment decoding error Check 7-segment character set matches hardware (may use different convention)
Display updates too slow Bus monitoring timeout too high Reduce update_interval config parameter
Button presses not detected Physical button wiring Verify GPIO13/GPIO14 connected to KEY_DOWN/KEY_UP from original board
Intermittent display reading Electrical noise on CLK/DIO Add 100nF capacitor from GPIO pins to GND; check wiring length/routing
Bus detected but no display data Original micro not sending Original microcontroller may not be running; check its power and reset
Memory button decoding fails AiP650E not present Memory button monitoring requires AiP650E to be connected (currently removed)

11. Hardware Wiring Reference

11.1 GPIO Connection Map

Function Original Board ESP32-S3 GPIO Notes
Display Clock Pin 16 (SEGM_CLK) GPIO16 CLK input (read only)
Display Data Pin 15 (SEGM_DIO) GPIO15 DIO input (read only)
Up Button Pin 14 (KEY_UP) GPIO14 Button input (optional)
Down Button Pin 13 (KEY_DOWN) GPIO13 Button input (optional)
Ground GND GND Common reference

11.2 Level Shifting (if needed)

If original microcontroller operates at 5V and ESP32 at 3.3V:

DIO Line (5V → 3.3V):
  5V source → [10k Ω] → GPIO15 (3.3V max)
                    ├→ [GND via 10k Ω]
  Result: voltage divider, ~2.5V on GPIO (safe for 3.3V input)

CLK Line:
  Same as DIO if switching at high frequency (>1 MHz)
  Direct connection OK if clock frequency < 100 kHz

  • Datasheet: AiP650E-AX-XS-B037EN (Wuxi I-CORE Electronics)
  • Reference: 2-line Serial Interface / Common Cathode 8Seg 4Grid LED Controller/Driver
  • Hardware: Standing Desk Control Board (Original, with AiP650E physically removed)
  • Integration: ESP32-S3 with touchscreen UI
  • Original System: Microcontroller (32-bit) handling motor control and Hall sensors

13. Revision History

Version Date Changes
1.0 2026-04-23 Initial specification (master-driven architecture)
2.0 2026-04-23 Revised to listener-only architecture; ESP32 monitors bus instead of driving