This second post looks at expanding on the code used in post #1 to add a second daisy-chained MCP23017 to the first one.
I2C addresses when using multiple boards
As stated before, it’s possible to have up to 8 boards linked together, to give a total of 128 GPIO pins. If you feel that masochistic then I’ve added comments that allow you to see where blocks of code would need to be duplicated (and adjusted slightly in some cases) to increase the number of boards used.
Each board MUST have a unique I2C address and these are set by adding solder bridges to the pads labelled A0, A1 and A2 as described in the first post.
Interrupts when using multiple boards
This is probably the worst documented aspect of these boards.
I’ve found a method that works, but have no idea if it’s the ‘proper’ way of doing things…
As I said in post #1, you have to define a separate object for each board. I’ve used mcp_A, mcp_B etc. as the object names.
When an interrupt occurs, you get the expander pin number that triggered the interrupt by reading the getLastInterruptPin()
, but you have to do this for each board in the daisy-chain. If the interrupt occurred on one of the pins on the board that that you are interrogating then you’ll get a result of 0-15, depending on which pin it was, or 255 if it wasn’t on that board.
This means you have to cycle through the boards using x = mcp_A. getLastInterruptPin()
, x = mcp_B. getLastInterruptPin()
etc until x<255.
You then know which board, and which pin on that board triggered the interrupt.
Don’t forget what I said about needing to clear the interrupt register of the appropriate pin on the appropriate board by doing a mcp_x.digitalRead()
This is done in the clear_mcp_interrupts()
function, so every board/pin combination which has an interrupt attached to it must be included in this function.
The multiple board test scenario
To keep things simple, I’ve just echoed the test scenario I used in the first post – Board A works exactly the same way as in post #1 - pins PA0-PA7 have physical pushbuttons attached, pins PA0-PA7 have active LOW relays attached, virtual pins 0-7 have button widgets in push mode attached that mirror the physical buttons on PA0-PA8.
Board B is almost identical - pins PA0-PA7 have physical pushbuttons attached, pins PA0-PA7 have active LOW relays attached. The only difference is that because we’ve already used virtual pins 0-7, we use virtual pins 8-15 to mirror the physical buttons on Board B PA0-PA8.
In reality of course, the code would be much simpler if all 16 pins on Board A had physical pins attached and used interrupts to trigger the operation of the relays, all of which would be connected to Board B. I’ve not restructured it to work in that way, as I wanted to show how to handle interrupts from multiple boards as described above.
Some of the code initialisation operations could be performed using for
loops, arrays or simply shorthand declarations. I’ve not chosen to go down that route with this example, as it makes the code harder to unpick and adapt to different scenarios. It is starting to get cumbersome with just 2 boards giving 32 extra GPIOs, but hopefully most people won’t feel the need to go much bigger than this.
Splitting the code into multiple tabs would also make it easier to navigate, but that also makes it more difficult to share here, so I’ve kept everything in one place.
Wiring it all up for multiple boards
NodeMCU Expansion Board A
3v3 VCC (Red wire)
GND GND (Black Wire)
D2 (GPIO4) SDA (Blue wire)
D1(GPIO5) SCL (Yellow wire)
D5 (GPIO14) INTA (Orange wire
Not Connected INTB (Green wire)
Solder a 6-pin 0.1” header into the plated-through holes on Expansion Board A, and connect the corresponding pins on Expansion Board B:
Expansion Board A Expansion Board B
VCC VCC (Red wire)
GND GND (Black Wire)
SDA SDA (Blue wire)
SCL SCL (Yellow wire)
INTA INTA (Orange wire
INTB INTB (Green wire
Eight physical buttons are connected to pins PA0 to PA7 on Expansion Board A and the other side of the buttons are connected to GND on the expansion board
Eight physical buttons are connected to pins PA0 to PA7 on Expansion Board B and the other side of the buttons are connected to GND on the expansion board
Relay Board A Expansion Board A
In1 PB0
In2 PB1
In3 PB2
In4 PB3
In5 PB4
In6 PB5
In7 PB6
In8 PB7
GND GND
Relay Board A VCC and GND are connected to a separate 3.3v supply
Relay Board B Expansion Board B
In1 PB0
In2 PB1
In3 PB2
In4 PB3
In5 PB4
In6 PB5
In7 PB6
In8 PB7
GND GND
Relay Board B VCC and GND are connected to a separate 3.3v supply (the same one as used for Relay Board A, but separate from the NodeMCU power supply)
Eight Blynk button widgets in push mode are connected to virtual pins V0 to V7 to control the relays on Expansion Board A
Eight Blynk button widgets in push mode are connected to virtual pins V8 to V15 to control the relays on Expansion Board B
The multiple board code
#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <Wire.h>
#include "Adafruit_MCP23017.h" // https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
char auth[] = "REDACTED";
char ssid[] = "REDACTED";
char pass[] = "REDACTED";
// Connect pin SCL of the first expander (mcp_A) to SCL (D1)
// Connect pin SDA of the first expander (mcp_A) to SDA (D2)
// Connect pin INTA or INTB of the first expander (mcp_A) to ESP8266 pin D5 (GPIO14)
// don't solder A0,A1,A2 (default ID of 7) on the first expander (mcp_A)
// Connect 8 physical momentary push buttons to mcp_A pins 0 to 7 inclusive (labelled PA0 to PA7), with the other side of the button connected to GND
// Connect an 8-way Active LOW relay board to mcp_A pins 8 to 15 inclusive (labelled PB0 to PB7 inclusive)
// Attach Blynk button widgets (in Switch mode) to virtual pins 0 to 7 inclusive. These mirror the physical momentary buttons of mcp_A pins PA0 to PA7
// Solder 0.1" pitch pins to the connections at the far end of mcp_A and
// connect the corresponding wires from mcp_B on to these (INTB to INTB, INTA to INTA etc)
// solder A0 (to give an ID of 6) on the second expander (mcp_B)
// Connect 8 physical momentary push buttons to mcp_B pins 0 to 7 inclusive (labelled PA0 to PA7), with the other side of the button connected to GND
// Connect a second 8-way Active LOW relay board to mcp_B pins 8 to 15 inclusive (labelled PB0 to PB7 inclusive)
// Attach Blynk button widgets (in Switch mode) to virtual pins 8 to 15 inclusive. These mirror the physical momentary buttons of mcp_B pins PA0 to PA7
// If additional mcp boards are added (C, D, E etc) then connectors need to be soldered to the previous board in the daisy chain to allow them to connect together
// and each board needs to have a unique I2C address/ID, see below for details..
/*
The begin() param can be 0 to 7, the default param is 7 (no solder bridges on A0, A1 & A2) giving an address of 0x27.
Addr(BIN) Addr(hex) ID A2 A1 A0
010 0111 0x27 7 0 0 0 <--- Default for WaveShare Board
010 0110 0x26 6 0 0 1
010 0101 0x25 5 0 1 0
010 0100 0x24 4 0 1 1
010 0011 0x23 3 1 0 0
010 0010 0x22 2 1 0 1
010 0001 0x21 1 1 1 0
010 0000 0x20 0 1 1 1
*/
// Create the instances of the mcp objects
Adafruit_MCP23017 mcp_A;
Adafruit_MCP23017 mcp_B;
// duplicate the above code if additional mcp boards are added to the daisy chain
// Define the interrupt pin on the MCU
int esp_interrupt_pin=14; // GPIO14 (D5)
volatile unsigned long last_interrupt; // Used to store the time in millis of the last interrupt, for debouncing
int debounce_delay = 100; // The minimum delay in milliseconds between button presses, for debouncing
volatile int mcp_device_ID; // when an interrupt is detected and decoded the device which generated the interrupt is stored here
volatile uint8_t pin_num; // when an interrupt is detected and decoded the pin which generated the interrupt is stored here
// Define friendly names for the pins...
// These mcp_A pins will have physical buttons connected to them, with the other side connected to ground.
// the pins will be pulled-up when we declare them later...
byte mcp_A_pin_PA0=0;
byte mcp_A_pin_PA1=1;
byte mcp_A_pin_PA2=2;
byte mcp_A_pin_PA3=3;
byte mcp_A_pin_PA4=4;
byte mcp_A_pin_PA5=5;
byte mcp_A_pin_PA6=6;
byte mcp_A_pin_PA7=7;
// These mcp_A pins will be connected to an 8-way relay board, which is Active LOW
// so the relays activate when the pin is pulled LOW.
// These pins will also be pulled-up and initialised as HIGH when we declare them later...
byte mcp_A_pin_PB0=8;
byte mcp_A_pin_PB1=9;
byte mcp_A_pin_PB2=10;
byte mcp_A_pin_PB3=11;
byte mcp_A_pin_PB4=12;
byte mcp_A_pin_PB5=13;
byte mcp_A_pin_PB6=14;
byte mcp_A_pin_PB7=15;
// These mcp_B pins will have physical buttons connected to them, with the other side connected to ground.
// the pins will be pulled-up when we declare them later...
byte mcp_B_pin_PA0=0;
byte mcp_B_pin_PA1=1;
byte mcp_B_pin_PA2=2;
byte mcp_B_pin_PA3=3;
byte mcp_B_pin_PA4=4;
byte mcp_B_pin_PA5=5;
byte mcp_B_pin_PA6=6;
byte mcp_B_pin_PA7=7;
// These mcp_B pins will be connected to an 8-way relay board, which is Active LOW
// so the relays activate when the pin is pulled LOW.
// These pins will also be pulled-up and initialised as HIGH when we declare them later...
byte mcp_B_pin_PB0=8;
byte mcp_B_pin_PB1=9;
byte mcp_B_pin_PB2=10;
byte mcp_B_pin_PB3=11;
byte mcp_B_pin_PB4=12;
byte mcp_B_pin_PB5=13;
byte mcp_B_pin_PB6=14;
byte mcp_B_pin_PB7=15;
// duplicate the above definitions if additional mcp boards are added to the daisy chain
// Interrupt handler for pins mcb_A pins 0-7 and mcb_B pins 0-7
void ICACHE_RAM_ATTR intCallBack()
{
noInterrupts();
// If the interrupt occurred on mcp_A then reading the mcp_A interrupt register will return the pin number
// (0-15 or 0-7 in this case as we aren't using interrupts on pins 8-15),
// otherwise it will return 255.
// Therefore a value >15 means that the interrupt didn't occur on mcp_A but on one of the other mcp boards in the daisy chain so we
// repeat the process for each board until we get a value in the range of 0 to 15
// To later reference which board the interrupt came from we use mcp_device_ID
if (mcp_A.getLastInterruptPin()<15)
{
pin_num = mcp_A.getLastInterruptPin();
mcp_device_ID = 7;
}
else if (mcp_B.getLastInterruptPin()<15)
{
pin_num = mcp_B.getLastInterruptPin();
mcp_device_ID = 6;
}
// if more boards are added to the daisy chain then this else..if logic would be continued.
if(millis()-last_interrupt>=debounce_delay) // Debounce routine
{
// for debugging only...
Serial.println("_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-");
Serial.println("A button has been pressed!!!");
Serial.print("Device = ");
Serial.println(mcp_device_ID);
Serial.print("Pin = ");
Serial.println(pin_num);
Serial.println("_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-");
Serial.println();
process_button_press(); // Call the function that will activate the correct relay etc.
}
last_interrupt=millis();
interrupts();
clear_mcp_interrupts(); // Call the function that clears all of the interrupt registers
}
void process_button_press()
{
switch(mcp_device_ID)
{
case 7: //mcp_A
{
// This code assumes that the switch on mcp_A pin 0 controls the relay on mcp_A pin 8
// so we just add 8 to the number of the pin that triggered the interrupt
int current_pin_state = mcp_A.digitalRead(pin_num + 8); // Get the current state of the pin
mcp_A.digitalWrite(pin_num + 8, !current_pin_state); // Write the opposite state to the pin to toggle it
// This code assumes that we have Blynk button widgets (in Switch mode) on virtual pins that are numbered the same
// as the ones used for the physical buttons (V0 = MCP Pin 0, V1 = MCP Pin 1 etc)
// As the relays are Active LOW then we need to write the opposite state to the Blynk switch widget than we do to the MCP pin...
// Relay = Off (HIGH or 1) then Blynk button widget = 0
// Relay = On (LOW or 0) then Blynk button widget = 1
Blynk.virtualWrite(pin_num, current_pin_state);
}
break;
case 6: //mcp_B
{
// This is basically the same as the code for mcp_A
// with 8 switches controlling 8 relays
// The input pins are still called 0-7 and the output pins 8-15,
// so we still have a difference of 8 between the switch pin and the corresponding relay pin
// we'll use virtual pins 8-15 for the button widgets that mirror the physical buttons,
// so this time we need to add 8 to the pin number to give us the corresponding virtual pin number
int current_pin_state = mcp_B.digitalRead(pin_num + 8); // Get the current state of the pin
mcp_B.digitalWrite(pin_num + 8, !current_pin_state); // Write the opposite state to the pin to toggle it
// As the relays are Active LOW then we need to write the opposite state to the Blynk switch widget than we do to the MCP pin...
// Relay = Off (HIGH or 1) then Blynk button widget = 0
// Relay = On (LOW or 0) then Blynk button widget = 1
Blynk.virtualWrite(pin_num +8, current_pin_state); // this time we add 8 to the virtual pin number
}
break;
// additional case statements would be added if more expander boards are added to the daisy chain,
// assuming that interrupts are used on those additional boards
}
}
BLYNK_WRITE_DEFAULT()
{
int widget_pin = request.pin; // Which virtual pin triggered this BLYNK_WRITE_DEFAULT callback?
int widget_value = param.asInt(); // Get the value from the virtual pin (O = off, 1 = on)
if(widget_pin>=0 && widget_pin<=7) // if it was a virtual pin in the range 0-7 then it's a command meant for mcp_A
{
mcp_A.digitalWrite(widget_pin + 8, !widget_value); // Write the opposite state to the pin on mcp_A to toggle the relay
}
if(widget_pin>=8 && widget_pin<=15) // if it was a virtual pin in the range 8-15 then it's a command meant for mcp_B
{
mcp_B.digitalWrite(widget_pin + 8, !widget_value); // Write the opposite state to the pin on mcp_B to toggle the relay
}
// additional if statements would be added if more expander boards are added to the daisy chain,
// assuming that interrupts are used on those additional boards
}
void setup(){
Serial.begin(74880);
pinMode(esp_interrupt_pin,INPUT); // Initialise the MCU pin used for the Interrupt line (INTA or INTB on the Waveshare board)
mcp_A.begin(7); // default address, no solder pads bridged
mcp_B.begin(6); // solder pad A0 bridged
// duplicate the above code if more expander boards are added to the daisy chain
// We mirror INTA and INTB, so that only one line is required between MCP and Arduino for int reporting
// The INTA/B will not be Floating
// INTs will be signalled with a LOW
mcp_A.setupInterrupts(true,false,LOW);
mcp_B.setupInterrupts(true,false,LOW);
// duplicate the above code if more expanders are added to the daisy chain (assuming that more pins with interrupts are needed on these boards)
// In this next section I could have used the friendly names (0-15) defined in the declarations, but chose to stick with the
// names screen-printed on the mcp board to aid wiring and debugging.
// In the following blocks of code we declare each of our pins that will be used for interrupts (Pins 0-7) on mcb_A
// If other pins have interrupts assigned to them then they must be added to the clear_mcp_interrupts() function
// configuration for a button PA0 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA0, INPUT);
mcp_A.pullUp(mcp_A_pin_PA0, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA0,FALLING);
// configuration for a button PA1 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA1, INPUT);
mcp_A.pullUp(mcp_A_pin_PA1, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA1,FALLING);
// configuration for a button PA2 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA2, INPUT);
mcp_A.pullUp(mcp_A_pin_PA2, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA2,FALLING);
// configuration for a button PA3 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA3, INPUT);
mcp_A.pullUp(mcp_A_pin_PA3, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA3,FALLING);
// configuration for a button PA4 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA4, INPUT);
mcp_A.pullUp(mcp_A_pin_PA4, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA4,FALLING);
// configuration for a button PA5 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA5, INPUT);
mcp_A.pullUp(mcp_A_pin_PA5, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA5,FALLING);
// configuration for a button PA6 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA6, INPUT);
mcp_A.pullUp(mcp_A_pin_PA6, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA6,FALLING);
// configuration for a button PA7 on mcb_A
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_A.pinMode(mcp_A_pin_PA7, INPUT);
mcp_A.pullUp(mcp_A_pin_PA7, HIGH); // turn on a 100K pullup internally
mcp_A.setupInterruptPin(mcp_A_pin_PA7,FALLING);
// In the following blocks of code we declare each of our pins that will be used to control the relays (Pins 8-15) on mcb_A
// configuration for a button PB0 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB0, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB0, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB0, HIGH);
// configuration for a button PB1 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB1, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB1, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB1, HIGH);
// configuration for a button PB2 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB2, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB2, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB2, HIGH);
// configuration for a button PB3 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB3, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB3, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB3, HIGH);
// configuration for a button PB4 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB4, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB4, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB4, HIGH);
// configuration for a button PB5 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB5, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB5, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB5, HIGH);
// configuration for a button PB6 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB6, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB6, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB6, HIGH);
// configuration for a button PB7 on mcb_A
mcp_A.pinMode(mcp_A_pin_PB7, OUTPUT);
mcp_A.pullUp(mcp_A_pin_PB7, HIGH); // turn on a 100K pullup internally
mcp_A.digitalWrite(mcp_A_pin_PB7, HIGH);
// In the following blocks of code we declare each of our pins that will be used for interrupts (Pins 0-7) on mcb_B
// If other pins have interrupts assigned to them then they must be added to the clear_mcp_interrupts() function
// configuration for a button PA0 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA0, INPUT);
mcp_B.pullUp(mcp_B_pin_PA0, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA0,FALLING);
// configuration for a button PA1 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA1, INPUT);
mcp_B.pullUp(mcp_B_pin_PA1, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA1,FALLING);
// configuration for a button PA2 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA2, INPUT);
mcp_B.pullUp(mcp_B_pin_PA2, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA2,FALLING);
// configuration for a button PA3 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA3, INPUT);
mcp_B.pullUp(mcp_B_pin_PA3, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA3,FALLING);
// configuration for a button PA4 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA4, INPUT);
mcp_B.pullUp(mcp_B_pin_PA4, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA4,FALLING);
// configuration for a button PA5 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA5, INPUT);
mcp_B.pullUp(mcp_B_pin_PA5, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA5,FALLING);
// configuration for a button PA6 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA6, INPUT);
mcp_B.pullUp(mcp_B_pin_PA6, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA6,FALLING);
// configuration for a button PA7 on mcb_B
// interrupt will trigger when the pin is taken to ground by a pushbutton
mcp_B.pinMode(mcp_B_pin_PA7, INPUT);
mcp_B.pullUp(mcp_B_pin_PA7, HIGH); // turn on a 100K pullup internally
mcp_B.setupInterruptPin(mcp_B_pin_PA7,FALLING);
// In the following blocks of code we declare each of our pins that will be used to control the relays (Pins 8-15) on mcb_B
// configuration for a button PB0 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB0, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB0, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB0, HIGH);
// configuration for a button PB1 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB1, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB1, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB1, HIGH);
// configuration for a button PB2 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB2, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB2, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB2, HIGH);
// configuration for a button PB3 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB3, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB3, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB3, HIGH);
// configuration for a button PB4 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB4, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB4, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB4, HIGH);
// configuration for a button PB5 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB5, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB5, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB5, HIGH);
// configuration for a button PB6 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB6, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB6, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB6, HIGH);
// configuration for a button PB7 on mcb_B
mcp_B.pinMode(mcp_B_pin_PB7, OUTPUT);
mcp_B.pullUp(mcp_B_pin_PB7, HIGH); // turn on a 100K pullup internally
mcp_B.digitalWrite(mcp_B_pin_PB7, HIGH);
// duplicate the above code if more expander boards are added to the daisy chain and use the correct
// mode definition etc for the type of use for that pin (input, output, input with interrupt etc)
// If other board/pin combinations have interrupts assigned to them then they must be added to the clear_mcp_interrupts() function
clear_mcp_interrupts(); // Call the function that clears all of the interrupt registers
Blynk.begin(auth, ssid, pass);
// Here we attach an interrupt to the MCU pin that will be used to listen for interrupts from the mcp_A...
attachInterrupt(digitalPinToInterrupt(esp_interrupt_pin),intCallBack,FALLING);
// It's not necessary to do this for the other mcp's, as it's only mcp_A that's directly connected to the NodeMCU's interrupt pin
}
void clear_mcp_interrupts()
{
for(int loop=0;loop<=7;loop++) // Loop through the 8 pins on mcp_A that have interrupts attached
{
mcp_A.digitalRead(loop); // Read the pin to clear the interrupt register for that pin
}
// This could have been added to the same loop as above, but kept separate for transparency...
for(int loop=0;loop<=7;loop++) // Loop through the 8 pins on mcp_B that have interrupts attached
{
mcp_B.digitalRead(loop); // Read the pin to clear the interrupt register for that pin
}
// If interrupts are assigned to other board/pin combinations then they must be added to this function
}
BLYNK_CONNECTED()
{
// This code runs when the MCU connects or re-connects to the Blynk server
// We want to get the latest widget switch values and update the relays so that they match this
// Loop through pins 0-15 and do a Blynk.syncVirtual
// If more pins are used that relate to additional mcps, or different patterns of virtual pins are used on the existing mcps
// the this for loop range will need to be adjusted...
for(int loop=0;loop<=15;loop++)
{
Blynk.syncVirtual(loop); // This forces the BLYNK_WRITE_DEFAULT callback function to trigger for each virtual pin in turn
}
}
void loop()
{
Blynk.run();
}
Enjoy!
Pete.