Когда на ESP8266/ESP32/Arduino не хватает GPIO можно использовать чип MCP23017 для увеличения количества портов на 16. Управление чипом происходит по двум проводам I2C, коммуникация удобная. Цена за чип около 0,8 $. На Aliexpress достаточно большой выбор вариантов чипа и два основных варианта плат.
Wemos D1 mini (ESP8266) широко представлен на Aliexpress по цене менее 3 USD.
Поскольку плата поддерживает I2C адресацию позволяющую подключить 8 плат, суммарно к микроконтроллеру можно добавить 128 GPIO.
Схема соединения
Распиновка MCP23017 следующая:

Адресация I2C платы задается замыканием ножек A0-2 на +3,3 В или GND. Таблица адресации такая:

Схема соединения Wemos D1 с MCP23017:
| Wemos D1 Pin Name | MCP23017 Physical Pin | MCP23017 Pin Name | Description |
|---|---|---|---|
| D1 (SCL) | 12 | SCL | I/O Serial Clock input |
| D2 (SDA) | 13 | SDA | I/O Serial Data |
| 3V3 | 18 | Reset | |
| 3V3 | 9 | VDD | Power |
| GND | 10 | VSS | Ground |
| GND | 15 | A0 | Hardware address pin |
| GND | 16 | A1 | Hardware address pin |
| GND | 17 | A2 | Hardware address pin |
В качестве сенсора я использовал датчик пересечения линии. На схеме в качестве примера использована плата датчика дождя. Схемотехника сенсоров одинаковые, а для fritzing мне не удалось найти библиотеки для датчика пересечения линии.
При срабатывании сенсора сигнал поступает на вход GPA0 и ESP8266 зажигает светодиод скоммутированный к выходу GPB0.


Программа
Если MCP23017 подключен к ESP8266 корректно, при запуске скетча https://playground.arduino.cc/Main/I2cScanner будет найдено устройство.
11:04:46.058 -> Scanning... 11:04:46.058 -> I2C device found at address 0x20 ! 11:04:46.140 -> done
При работе с MCP23017 через библиотеку Adafruit MCP23017. потребуется таблица перевода входов Adafruit в названия входов и номера ножек на микросхеме.
| Adafruit PIN | Pin Name | Physical Pin |
| 0 | GPA0 | 21 |
| 1 | GPA1 | 22 |
| 2 | GPA2 | 23 |
| 3 | GPA3 | 24 |
| 4 | GPA4 | 25 |
| 5 | GPA5 | 26 |
| 6 | GPA6 | 27 |
| 7 | GPA7 | 28 |
| 8 | GPB0 | 1 |
| 9 | GPB1 | 2 |
| 10 | GPB2 | 3 |
| 11 | GPB3 | 4 |
| 12 | GPB4 | 5 |
| 13 | GPB5 | 6 |
| 14 | GPB6 | 7 |
| 15 | GPB7 | 8 |
В программе потребуется также адресация Adafruit, она отличается от адресации I2C.
| Address I2C | Adafruit address | A0 / PIN 15 | A1 / PIN 16 | A2 / PIN 17 |
|---|---|---|---|---|
| 0x20 | 0 | LOW | LOW | LOW |
| 0x21 | 1 | HIGH | LOW | LOW |
| 0x22 | 2 | LOW | HIGH | LOW |
| 0x23 | 3 | HIGH | HIGH | LOW |
| 0x24 | 4 | LOW | LOW | HIGH |
| 0x25 | 5 | HIGH | LOW | HIGH |
| 0x26 | 6 | LOW | HIGH | HIGH |
| 0x27 | 7 | HIGH | HIGH | HIGH |
Программа принимает сигнал с входа GPA0 и зажигает светодиод на выходе GPB0 MCP23017 и на плате Wemos D1.
#include <Wire.h>
#include "Adafruit_MCP23017.h"
#define MCP23017_GPA0 0 // Digital input on MCP23017 correspond to A0
//#define MCP23017_ADDR 0x20
#define LedPin 2 //A0
#define MCP23017_LED 8 //GPB0
Adafruit_MCP23017 mcp;
void setup()
{
Serial.begin(9600);
mcp.begin(); // use default address 0x20
mcp.pinMode(MCP23017_GPA0, INPUT); //Define GPA0 as input pin
mcp.pinMode(MCP23017_LED, OUTPUT); //Define GPB0 as output pin
//mcp.pullUp(MCP23017_GPA0, HIGH); // turn on a 100K pullup internally
pinMode(LedPin, OUTPUT); // use the LedPin LED as debugging
}
void loop()
{
int value_A0 = analogRead(A0); // reads the analog input from the IR distance sensor
bool value_D0 = mcp.digitalRead(MCP23017_GPA0);// reads the digital input from the IR distance sensor
mcp.digitalWrite(MCP23017_LED, !value_D0);
digitalWrite(LedPin, value_D0);
//Serial.println(value_D0 + " Value: " + String(value_A0));
//delay(1000);
}Подключение реле к MCP23017
Загорающийся светодиод — штука информативная, но в жизни при срабатывании датчика нужно выполнять какие-то действия, нередко физические: что-то открыть и закрыть, переместить и т.п. Поэтому подключим к светодиоду реле.

Что касается реле и подбора для микроконтроллеров — это тема для отдельной статьи, вариантов много и есть нюансы. Чтобы долго не мудрить, подключил к MCP23017 «родное» реле заточенное под Wemos D1 Mini. Такое реле недорогое, широко представлено у продавцов на Aliexpress, поддерживает уровень TTL 3V, т.е. не нужно ставить преобразователь уровней 3V <-> 5V.
Собственно, подключать его несложно. Управление идет по high level, т.е. по 1-це на управляющем входе D1. Если бы это был low level trigger, то в софте надо было ставить инверсию по логике, чтобы включение было по 0-ю, а отключение по 1-це на входе управления. Причем, не для всех реле программная инверсия срабатывает.
В общем, чтобы не иметь проблем при подключении реле к микроконтроллеру, работающему на напряжении 3,3 V, с соотвествующим уровнем TTL логики, нужно:
- Выбирать high level trigger/relay.
- С поддержкой напряжения срабатывания TTL логики 3,3 V.
- JD Vcc входом, чтобы отдельно запитать 5V обмотку реле.
- Гальванической (оптической) развязкой оптроном, чтобы не «спалить» микроконтроллер.

Итак, параллельно или вместо светодиода подключаем MCP23017 к реле.
| MCP23017 pin | Wemos D1 Mini relay shield | Примечания |
| GND | GND | |
| GPB0 | D1 | |
| 5V | На БП +5V для управления обмотками реле. | |
| GND | На БП GND для управления обмотками реле. |
После подключения, реле нормально коммутируется при срабатывании датчика. Программный код менять не нужно.
CJMCU-2317 (MCP23017) Fritzing part
Не нашел Fritzing part для платы CJMCU-2317 (MCP23017), поэтому пришлось создать свой вариант. Пользуйтесь и не забывайте лайкать, приятно. 🙂
Полезные ссылки
- https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
- https://medium.com/@wilko.vehreke/more-gpios-for-the-esp8266-with-the-mcp23017-b89f5e15cde3
- https://tronixstuff.com/2011/08/26/tutorial-maximising-your-arduinos-io-ports/
- https://arduinodiy.wordpress.com/2017/03/14/adding-an-mcp23017-16-port-io-expander-to-arduino-or-esp8266/
- http://ww1.microchip.com/downloads/en/DeviceDoc/21952a.pdf — документация от производителя
- http://omnigatherum.ca/wp/?p=338 — Fritzing part для датчика дождя.