# 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 ### 2.1 Tri-Microcontroller Design (Recommended: ATtiny85 Proxy) ``` ┌───────────────────────────────────────────────────────────────────┐ │ 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 ```yaml 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: ```yaml 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: ```yaml 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: ```yaml 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: ```yaml 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: ```yaml 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: ```yaml 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 ```yaml 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: ```yaml # 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: ```yaml 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) ```yaml 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 ```yaml 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 ```yaml 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 ``` --- ## 12. Related Documents - **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 |