esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
420 stars 26 forks source link

Initial state for output GPIO pins #2939

Open ntqbit opened 3 weeks ago

ntqbit commented 3 weeks ago

Describe the problem you have/What new integration you would like

Set initial state (high/low) before configuring a GPIO pin as output.

Please describe your use case for this integration and alternatives you've tried:

I use SN74HC595 shift register to extend the number of outputs. For my application it is important that outputs of the shift register are never in high state unless explicitly enabled. When powered, sn74hc595 outputs will be enabled (in undefined state) if OE pin is low, even if the shift register nor latch register were not initialized. To ensure that outputs are not in high state until initialized from the mcu, I pull up the OE pin with external resistor.

Here is the standard configuration I use for sn74hc595 component:

sn74hc595:
  - id: "sr"
    data_pin: GPIO25
    clock_pin: GPIO14
    latch_pin: GPIO27
    oe_pin: GPIO26

When the component is being set up, even before the shift register is initialized with defined state, the OE is pin is pulled low for a brief moment.

components/sn74hc595.cpp:

void SN74HC595Component::pre_setup_() {
  ESP_LOGCONFIG(TAG, "Setting up SN74HC595...");

  if (this->have_oe_pin_) {  // disable output
    this->oe_pin_->setup();
    this->oe_pin_->digital_write(true);
  }
}

Calling setup on a GPIOPin (and ESP32InternalGPIOPin, one of its implementations) only initializes the mode (input, output, etc), but not the level. The default voltage level, in most cases, is low.

That is, after this->oe_pin_->setup(), the OE pin is pulled low until the GPIO is set up to its "initial" state with digital_write(true), causing the outputs of the shift register to enable for a brief moment. This brief moment with undefined output state, in my case, is unacceptable.

Workaround One possible workaround is to first configure the GPIO connected to the OE pin as input, to ensure that it is in high impedance state and not pulling the line down:


output:
  - platform: gpio
    id: "output_enable"
    pin:
      number: GPIO26
      mode: INPUT

Enable the output whenever ready:

  on_boot:
    priority: 500
    then:
      # do some initialization, ensure shift register initialized, etc
      # only then configure OE GPIO as output in low state
      # mode could be OUTPUT_OPEN_DRAIN or OUTPUT_PUSH_PULL
      - lambda: |-
          pinMode(26, OUTPUT_OPEN_DRAIN);
          digitalWrite(26, LOW);

Additional context

I don't have experience with other frameworks and microcontrollers like rp2040, but as far as I know, in arduino you can just use digitalWrite(initialState) before using pinMode(OUTPUT) to ensure that the pin is not initialized to wrong voltage level.

The changes required (such as adding a variable initial_state defaulting to low in pin schema) are backward compatible.