Wiznet chips have an interrupt pin, which is not currently used by the Ethernet library. While the library functions work very well without it, there are some situations where you might want to receive an interrupt from your Ethernet shield. Here is one example. Suppose you have a loop where the Arduino is doing time-critical stuff. The loop may run for a long time and you'd like to be able to interrupt it by user input over Ethernet. Usually, you would just call good old server.available() to check for that input, but that's not an option in time-critical code: it just takes too long. Enter the interrupt solution: under normal conditions, the code does not need to call any Ethernet functions at all, it just checks a flag. That flag is set by an interrupt service routine when Ethernet data arrives.
For us, that situation arises in a quantum physics experiment, but I'm sure it occurs in less exotic cases as well ;-). When searching for Arduino Ethernet interrupts, some old forum threads like this one come up high, but there doesn't seem to be a complete solution either in those threads or elsewhere. As I spent some time getting Ethernet interrupts to work, I'm sharing my solution here, hoping that it may be useful to others. It provides a complete solution for getting an interrupt whenever a socket has data available. This is just one use case. Others may need other interrupt conditions, but the basics are the same.
On the hardware side, we need a connection between the Wiznet chip's IR pin (called INTn) and a digital pin of the Arduino, as described in older threads and elsewhere. Some Ethernet shields have it by default, on others you need to set a jumper, or even solder the connection yourself. I'm using a Wiznet W5500 shield, which has INTn connected to digital pin 2 by default.
Next, we need to configure the Wiznet registers to generate the interrupt we want. Unfortunately, these registers are not quite identical between the different Wiznet chips. I'll focus on the W5500, which is the one used in the Arduino Ethernet Shield 2, in Wiznet's own W5500 shield, and many others. For the W5100, the code below will not work out of the box, although it shouldn't be hard to adapt.
The registers are described in the W5500 datasheet, available on the Wiznet website. Wiznet also has an application note on interrupt handling, available here. Although it is for the W5100 and does not specifically target Arduinos, it is helpful for understanding the basics.
We will work with the socket interrupt registers (SIR and SIMR). The Ethernet library currently doesn't give access to those registers, but this is easy to fix with a modified version of the w5100.h library file. Rather than hacking the library in place, I copied that file to the sketch folder, modified and renamed it, and included the modified file in the sketch. (Not sure I understand why that works, but it does.) In detail:
• Locate the w5100.h file of the Ethernet library. (On my computer, it is in "C:\Program Files (x86)\Arduino\libraries\Ethernet\src\utility". You can also download it from github.) Make a copy with a different name (w5100mod.h here) and place it in the folder containing your sketch.
• In that file, locate the line
__GP_REGISTER16(RTR, 0x0017); // Timeout address
and comment it out. (This line declares the RTR register at address 0x0017. This is correct for W5100, but the W5500 has the SIR register at that address.)
• Immediately below that line, add the following lines:
__GP_REGISTER16(RTR, 0x0019); // Correct RTR address for W5500
__GP_REGISTER8 (SIR, 0x0017); // Socket Interrupt (W5500)
__GP_REGISTER8 (SIMR, 0x0018); // Socket Interrupt Mask (W5500)
• Finally, locate the line
__SOCKET_REGISTER16(SnRX_WR, 0x002A)
and add the following line below it:
__SOCKET_REGISTER8(SnIMR, 0x002C) // IR mask
Then save the file.
Now, when you #include "w5100mod.h" in your sketch, you can read and write the W5500's socket interrupt registers. (Note again that this code only works for the W5500. It shouldn't be too hard to make a more complete version that detects the chip and declares all registers at their correct address for all Wiznet chips. Perhaps someone will do that in some future version of the Ethernet library, but for now, if your shield has the W5100 chip, you have a little bit of work left...)
In this example, we want an interrupt to occur when one of the sockets has data available. To do that,
• set the Wiznet registers so that the INTn line to goes low when this condition is fulfilled:
for (int i=0;i<8;i++) {
W5100.writeSnIMR(i,0x04); // Socket IR mask: RECV for all sockets
}
W5100.writeSIMR(0xFF); // Enable interrupts for all sockets
• tell the Arduino to generate an interrupt when the corresponding pin goes from high to low, which is done in the usual way: in setup(), add
#define INTn 2
// ...
attachInterrupt(digitalPinToInterrupt(INTn), socketISR, FALLING);
and add an interrupt handler
volatile int SIRflag=0;
void socketISR()
{
SIRflag++;
}
• Your code can now check SIRflag to check for new data, which is much faster than calling server.available(). Each time an interrupt occurs, the registers need to be reset:
for (int i=0;i<8;i++) {
W5100.writeSnIR(i,0xFF); // Clear socket i interrupt
}
W5100.writeSIR(0xFF); // Clear SIR
}
Once you have this working, you're all set for handling other Ethernet interrupts as well.