8000
Skip to content

[max6956] Migrate to CachedGpioExpander to reduce I2C bus usage - #10589

Closed
bdraco wants to merge 13 commits into
devfrom
max6956_gpio_cache_banks
Closed

[max6956] Migrate to CachedGpioExpander to reduce I2C bus usage#10589
bdraco wants to merge 13 commits into
devfrom
max6956_gpio_cache_banks

Conversation

@bdraco
@bdraco bdraco commented Sep 4, 2025
Copy link
Copy Markdown
Member

What does this implement/fix?

This PR migrates the MAX6956 I/O expander component to use the CachedGpioExpander base class to significantly reduce I2C bus usage through intelligent caching.

The MAX6956 datasheet shows that pins 4-31 can be read in banks using registers 0x40, 0x48, 0x50, and 0x58. Previously, the component read each pin individually, resulting in one I2C transaction per digital_read() call. With this migration, the component now:

  1. Reads only the specific 8-pin bank containing the requested pin
  2. Caches the bank value for subsequent reads in the same loop cycle
  3. Invalidates the cache at the start of each loop to ensure fresh data

Performance improvement: Up to 8x reduction in I2C reads when accessing multiple pins from the same bank, significantly reducing bus congestion and improving responsiveness.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • 8000
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code quality improvements to existing code or addition of tests
  • Other

Related issue or feature (if applicable):

Pull request in esphome-docs with documentation (if applicable):

  • N/A (internal implementation change, no user-facing changes)

Test Environment

⚠️ Unable to test - I do not have MAX6956 hardware

Component should work on:

  • ESP32
  • ESP32 IDF
  • ESP8266
  • RP2040
  • BK72xx
  • RTL87xx

Example entry for config.yaml:

# Example configuration (unchanged from existing)
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

max6956:
  - id: max6956_hub
    address: 0x40
    brightness_mode: global
    brightness_global: 8

# Use as GPIO
binary_sensor:
  - platform: gpio
    name: "MAX6956 Pin 4"
    pin:
      max6956: max6956_hub
      number: 4
      mode:
        input: true
        pullup: true
      inverted: false

switch:
  - platform: gpio
    name: "MAX6956 Pin 12"
    pin:
      max6956: max6956_hub
      number: 12
      mode:
        output: true
      inverted: false

# Use as LED driver
output:
  - platform: max6956
    max6956: max6956_hub
    pin: 20
    id: led_output

light:
  - platform: monochromatic
    name: "MAX6956 LED"
    output: led_output

Implementation Details

Bank Mapping (Aligned with Base Class View)

  • Bank 0: Pins 0-7 (register 0x40 - only pins 4-7 are valid, bits D0-D3 for pins 4-7, D4-D7 read as 0)
  • Bank 1: Pins 8-15 (register 0x48 - 8 ports, bits D0-D7 for pins 8-15)
  • Bank 2: Pins 16-23 (register 0x50 - 8 ports, bits D0-D7 for pins 16-23)
  • Bank 3: Pins 24-31 (register 0x58 - 8 ports, bits D0-D7 for pins 24-31)

Key Changes

  1. Now inherits from CachedGpioExpander<uint8_t, 32> where:

    • uint8_t is the bank type (8 bits per bank)
    • 32 is the total pin address space (pins 0-31, with only 4-31 valid on the MAX6956)
  2. Implements three virtual methods:

    • digital_read_hw(): Reads the appropriate 8-pin bank based on requested pin
    • digital_read_cache(): Returns cached pin value from the bank
    • digital_write_hw(): Writes individual pin (unchanged)
  3. Cache alignment strategy:
    The base class views pins as a continuous 0-31 range, divided into 4 banks of 8.
    Our implementation aligns the cache with this view:

    • Bank 0 (pins 0-7): Only pins 4-7 are valid, stored in bits 4-7 of cache
    • Banks 1-3 (pins 8-31): Direct mapping to cache bits

    Special handling for Bank 0: Data from register 0x40 arrives in bits D0-D3,
    which we shift left by 4 to align with the base class's expectation that
    pin N maps to bit (N % 8) within its bank.

Testing Considerations

The implementation has been verified through analysis of the datasheet and base class behavior:

  • Pin 4 → Bank 0, Bit 4 (register 0x40, bit D0 shifted to bit 4)
  • Pin 7 → Bank 0, Bit 7 (register 0x40, bit D3 shifted to bit 7)
  • Pin 8 → Bank 1, Bit 0 (register 0x48, bit D0)
  • Pin 15 → Bank 1, Bit 7 (register 0x48, bit D7)
  • Pin 16 → Bank 2, Bit 0 (register 0x50, bit D0)
  • Pin 23 → Bank 2, Bit 7 (register 0x50, bit D7)
  • Pin 24 → Bank 3, Bit 0 (register 0x58, bit D0)
  • Pin 31 → Bank 3, Bit 7 (register 0x58, bit D7)

Checklist:

  • The code change is tested and works locally. ⚠️ Unable to test - no hardware
  • Tests have been added to verify that the new code works (under tests/ folder).

If user exposed functionality or configuration variables are added/changed:

  • N/A - Internal implementation change only

@github-actions
github-actions Bot commented Sep 4, 2025
Copy link
Copy Markdown
Contributor

To use the changes from this PR as an external component, add the following to your ESPHome configuration YAML file:

external_components:
  - source: github://pr#10589
    components: [max6956]
    refresh: 1h

(Added by the PR bot)

@codecov-commenter
codecov-commenter commented Sep 4, 2025
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.46%. Comparing base (801f3fa) to head (12ffd1b).
⚠️ Report is 1881 commits behind head on dev.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##              dev   #10589      +/-   ##
==========================================
+ Coverage   76.39%   76.46%   +0.06%     
==========================================
  Files          56       57       +1     
  Lines       12580    12631      +51     
  Branches     1764     1773       +9     
==========================================
+ Hits         9611     9658      +47     
- Misses       2545     2546       +1     
- Partials      424      427       +3     

see 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bdraco
bdraco marked this pull request as ready for review September 4, 2025 21:02
Copilot AI review requested due to automatic review settings September 4, 2025 21:02
@github-actions
github-actions Bot commented Sep 4, 2025
Copy link
Copy Markdown
Contributor

👋 Hi there! This PR modifies 3 file(s) with codeowners.

@looping40 - As codeowner(s) of the affected files, your review would be appreciated! 🙏

Note: Automatic review request may have failed, but you're still welcome to review.

This comment was marked as outdated.

@bdraco
bdraco commented Sep 4, 2025
Copy link
Copy Markdown
Member Author

This is the last I/O expander not using the CachedGpioExpander. It was a bit more complex to migrate because its got that strange half bank where 0-3 is used for other things

@bdraco
bdraco requested a review from Copilot September 4, 2025 21:11
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR migrates the MAX6956 I/O expander component to use the CachedGpioExpander base c 8000 lass to reduce I2C bus usage through intelligent caching of pin bank reads.

  • Replaces individual pin reads with bank-based reads that cache 8-pin groups
  • Implements the CachedGpioExpander interface with hardware and cache read methods
  • Adds automatic cache invalidation at the start of each loop cycle

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
esphome/components/max6956/max6956.h Inherits from CachedGpioExpander, adds bank constants and cache storage
esphome/components/max6956/max6956.cpp Replaces individual pin reads with bank-based caching implementation
esphome/components/max6956/__init__.py Adds gpio_expander as auto-loaded dependency

Comment thread esphome/components/max6956/max6956.h
Comment thread esphome/components/max6956/max6956.cpp
Comment thread esphome/components/max6956/max6956.cpp
@github-actions
github-actions Bot commented Oct 29, 2025
Copy link
Copy Markdown
Contributor

Memory Impact Analysis

Components: max6956
Platform: esp8266-ard

Metric Target Branch This PR Change
RAM 29,288 bytes 29,300 bytes 📈 🔸 +12 bytes (+0.04%)
Flash 274,839 bytes 275,147 bytes 📈 🔸 +308 bytes (+0.11%)
📊 Component Memory Breakdown
Component Target Flash PR Flash Change
[esphome]max6956 1,026 bytes 1,288 bytes 📈 🚨 +262 bytes (+25.54%)
app_framework 2,245 bytes 2,269 bytes 📈 +24 bytes (+1.07%)
🔍 Symbol-Level Changes (click to expand)

Changed Symbols

Symbol Target Size PR Size Change
esphome::max6956::MAX6956GPIOPin::digital_read() 38 bytes 140 bytes 📈 +102 bytes (+268.42%)
vtable for esphome::max6956::MAX6956 48 bytes 80 bytes 📈 +32 bytes (+66.67%)
setup 531 bytes 555 bytes 📈 +24 bytes (+4.52%)
max6956__max6956_1__pstorage 52 bytes 68 bytes 📈 +16 bytes (+30.77%)
esphome::max6956::MAX6956GPIOPin::digital_write(bool) 20 bytes 24 bytes 📈 +4 bytes (+20.00%)
esphome::max6956::MAX6956::set_brightness_global(unsigned char) 29 bytes 30 bytes 📈 +1 bytes (+3.45%)

New Symbols (top 15)

Symbol Size
esphome::max6956::MAX6956::digital_read_hw(unsigned char) 86 bytes
esphome::max6956::MAX6956::digital_read_cache(unsigned char) 22 bytes
esphome::max6956::MAX6956::loop() 16 bytes
non-virtual thunk to esphome::max6956::MAX6956::digital_write_hw(unsigned char, bool) 15 bytes
esphome::max6956::MAX6956::digital_write_hw(unsigned char, bool) 15 bytes
non-virtual thunk to esphome::max6956::MAX6956::digital_read_hw(unsigned char) 12 bytes
non-virtual thunk to esphome::max6956::MAX6956::digital_read_cache(unsigned char) 12 bytes
esphome::max6956::MAX6956::digital_read_hw(unsigned char)::BANK_REGS 4 bytes

Removed Symbols (top 15)

Symbol Size
esphome::max6956::MAX6956::digital_read(unsigned char) 44 bytes
esphome::max6956::MAX6956::digital_write(unsigned char, bool) 15 bytes

Note: This analysis measures static RAM and Flash usage only (compile-time allocation).
Dynamic memory (heap) cannot be measured automatically.
⚠️ You must test this PR on a real device to measure free heap and ensure no runtime memory issues.

This analysis runs automatically when components change. Memory usage is measured from a representative test configuration.

@github-actions
github-actions Bot commented Apr 8, 2026
Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want to keep it open. Also, please rebase your PR onto the latest dev branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!

@github-actions github-actions Bot added the stale label Apr 8, 2026
@bdraco
bdraco requested a review from Copilot April 8, 2026 01:03
@esphome esphome Bot added the medium-pr PR < 100 lines label Apr 8, 2026
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread esphome/components/max6956/max6956.h
@bdraco bdraco removed the stale label Apr 8, 2026
@github-actions
github-actions Bot commented Jul 8, 2026
Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want to keep it open. Also, please rebase your PR onto the latest dev branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!

@github-actions github-actions Bot added stale and removed stale labels Jul 8, 2026
@github-actions
Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want to keep it open. Also, please rebase your PR onto the latest dev branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!

@github-actions github-actions Bot added the stale label Jul 30, 2026
@esphome esphome Bot closed this Aug 7, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0