Using only 1 gpio as input and output in sequence

I’m using an esp32 and trying to control a 2ch impulse relay, where only a short pulse as a control signal is required to change state of the relay, this means that the state of the output pin does not correspond to the state of the relay as the relay could also be toggled manually. I’ve been using 1 of the 2 chanels to provide feedback to my esp32 on a separate gpio input pin. Later I realized that if I put a series resistor in the feedback circuit it’s possible to both read state and output relay toggle with 1 gpio.

My approach has been tested using micropython and is to have the gpio as an input pull_down as default but when I want to change the relay state I reinitialize the gpio to an output, send a pulse and then reinitialize back to an input.

This is working well but I would rather use esphome instead of micrpython if possible.
I found that I could use allow_other_uses: true to reuse same gpio several times in the config yaml but don’t get that to work and what I’ve seen in other examples it’s only used for multiple inputs not input/output. Guess it might be possible maybe using lambdas?

Would appreciate any ideas or hints.

Hi, I have the same problem. I also want to use a GPIO primarily as input, but switch to output and drive the pin on occasion. My idea is to define it as a normal binary sensor, so that it servers its main function, but then have a script that changes the pin mode to output, drives it and returns the pin mode back to input. So in practice I was thinking about something like this:

binary_sensor:
  - platform: gpio
    name: "Power Button State"
    id: mcu_kill
    icon: mdi:toggle-switch
    pin: 
      number: GPIO19

script:
  - id: simulate_button_press
    mode: single
    then:
      - lambda: |-
          ESP_LOGW("main", "Simulating Power Button Press");
          pinMode(19, OUTPUT);
          digitalWrite(19, HIGH);
          delay(200);
          digitalWrite(19, LOW);
          pinMode(19, INPUT);

Unfortunately I never got it to work properly. There are some posts where people claim that this approach works, but I still get the following errors:

power.yaml: In lambda function:
power.yaml:45:19: error: 'OUTPUT' was not declared in this scope
power.yaml:45:7: error: 'pinMode' was not declared in this scope
power.yaml:46:24: error: 'HIGH' was not declared in this scope
power.yaml:46:7: error: 'digitalWrite' was not declared in this scope
power.yaml:48:24: error: 'LOW' was not declared in this scope
power.yaml:49:19: error: 'INPUT' was not declared in this scope
*** [.pioenvs\irrigationsystem\src\main.cpp.o] Error 1

In practice we need to implement the one wire pin, but manually. Here is the setup function fron the esphome components:

void GPIOOneWireBus::setup() {
  this->t_pin_->setup();
  this->t_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
  // clear bus with 480µs high, otherwise initial reset in search might fail
  this->pin_.digital_write(true);
  this->pin_.pin_mode(gpio::FLAG_OUTPUT);
  delayMicroseconds(480);
  this->search();
}

It does more or less the same thing, so it must be possible… the question is how to do it in esphome itself. I was also thinnking to use the onewire component itself, but it does not allow custom configuration, because it is setup to work with the protocol only.

Using this approach, the script could possibly look something like this:

script:
  - id: simulate_button_press
    mode: single
    then:
      - lambda: |-
          ESP_LOGW("main", "Simulating Power Button Press");
          auto pin = ??? // I don't know how to get the pin class here
          pin->pin_mode(gpio::FLAG_OUTPUT);
          pin->digital_wright(true);
          delay(200);
          pin->digital_wright(false);
          pin->pin_mode(gpio::FLAG_INPUT);
          bool bit = pin->digital_read();

The GPIOpin class is virtual, so it cannot be used directly