
Занимаюсь проектом, на модуле ESP32 не хватило выводов! Не особо хотелось применять расширители портов на PCF8574 и ей подобные, в связи с чем пришлось городить костыль..
DevKit v1 на базе ESP-WROOM-32, на своих выводах (pins) имеет GPIO сконфигурированные только под цифровой вход, т.е. Input! Но ни как Input/Output. Это порты: GPIO34, GPIO35, GPIO36 и GPIO39.

Совсем не много ушло времени на понимание проблемы, и поиска пути решения задачи. Но перед этим стоит отметить, что мне в проекте, не хватило всего одного General Purpose Output. И благо эти pins можно конфигурировать на ADC!!!!!! Это важно!
Кратко, эти pins по своей конфигурации не имеют обсолютно ни каких внутрисхемных подтяжек штанов, вроде: pull-up или pull-down. Но зато эти пины по умолчанию находятся в высокоимпедансном состоянии High-Z, Hi-Z или по другому Off-State.
Входные пины GPIO34, GPIO35, GPIO36 и GPIO39 в ESP32 физически не имеют транзисторов структуры Push-Pull (которые «тянут» линию к 3V3 или к GND), а также не имеют встроенных резисторов подтяжки (Pull-up / Pull-down). Это чисто высокоимпедансные входы (Hi-Z). Однако, управляя режимом работы входного каскада, мы можем менять его входное сопротивление и входную емкость. Физически этот «костыль» превращает входной пин в управляемый ключ, который может переключаться между двумя состояниями: очень высокое сопротивление (Hi-Z) и низкое сопротивление (режим аналогового чтения АЦП / «заземление внутренней емкости»).
Схема с внешним транзистором «костыль». Мы используем входной пин ESP32 для управления затвором чувствительного полевого транзистора (MOSFET), который, в свою очередь, коммутирует нагрузку (например, светодиод, реле или логический вход другого устройства).
Принципиальная схема

Затвор транзистора должен быть подтянут к 3V3 через достаточно большое сопротивления, порядка 100k, чтобы ток, уходящий в пин ESP32 в режиме АЦП, был минимальным (микроамперы) и не сжег ввод, а транзистор должен быть с низким порогом открытия (Logic-Level MOSFET, например, 2N7002 или BSS138), — это ВАЖНО!!!!!
Принцип работы!
Состояние ВЫСОКОГО уровня (Логическая 1):
В коде мы настраиваем GPIO34 как обычный цифровой вход (INPUT). Пин находится в состоянии высокого сопротивления. Он никак не влияет на внешнюю цепь. Через внешний резистор R97 напряжение беспрепятственно поступает на затвор транзистора. Транзистор открывается. Нагрузка на стоке включается.
Состояние НИЗКОГО уровня (Логический 0):
В коде мы переводим пин в режим аналогового ввода (analogRead()) или подключаем его к внутреннему блоку емкостей ADC (с помощью низкоуровневых регистров, если требуется максимальная просадка). При включении ADC входное сопротивление пина резко падает (внутренний измерительный конденсатор и цепи коммутации ADC соединяются с землей для разряда). Ток от 3V3 через резистор R97 (100 кОм) уходит внутрь пина ESP32 на разрядную цепь, подтягивая за собой затвор транзистора к pull-down. Напряжение на затворе падает ниже порогового (обычно ниже 1В, для указанных выше транзисторов). Транзистор закрывается. Нагрузка выключается. 🤷♂️
Код программы который реализует это поведение:
cppconst int pseudoOutputPin = 34;void setup() { // Изначально переводим в INPUT (транзистор открыт, выдается "1") pinMode(pseudoOutputPin, INPUT); }void setPseudoOutput(bool level) { if (level == HIGH) { // Режим Hi-Z: внешняя подтяжка R1 вытягивает линию вверх pinMode(pseudoOutputPin, INPUT); } else { // Режим АЦП: активируем внутренние цепи, сажающие линию на землю // Вызываем чтение, чтобы запустить коммутацию емкостей АЦП analogRead(pseudoOutputPin); // Для удержания низкого уровня в некоторых ревизиях ESP32 // может потребоваться вызывать analogRead() в цикле loop }}void loop() { setPseudoOutput(HIGH); // Включаем нагрузку delay(2000); setPseudoOutput(LOW); // Выключаем нагрузку delay(2000);}
p.s.. собственно на этом все! Не особо надежно на мой взгляд, если честно.. ИСПОЛЬЗОВАТЬ НА СВОЙ СТРАХ И РИСК!!!

ссылка на оригинал статьи https://habr.com/ru/articles/1045670/