How to use client.read() & client.write functions simultaneously with Interruppt

Dear All fellows.

I am trying to implement an IEC-60870-5-104 RTU at my Arduino UNO R3 board. For this purpose, I am using standard Ethernet.h library with Ethernet shield. When I implement the whole protocol in cyclic pattern i.e. no external hardware interrupts so it works fine. Similarly when I program it with external interrupts only (no cyclic running) again it works fine. But actual IEC 104 protocol uses both routines i.e. Cyclic running where it exchanges control messages with client master using client.read() function, and whenever any hardware interrupt occurs it must stop the cyclic running and send the data to client using client.write function. When I try to run both routines at a time, it does not work. What I conclude from the debugging that client.read(); and client.write functions could not be used in the pattern what I am programming and this is causing problem. To summarize, client.read is in the loop() function while client. write() function is in the ISR.
Please have a look at the following code:

#include <Ethernet.h>

byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB7 }; // MAC Address,
IPAddress ip(172, 31, 15, 2); // ip address of the monitored device or RTU
IPAddress gateway(172, 31,15, 1); // IP Address of Gateway, My Laptop or in network it may be Router.
IPAddress subnet(255, 255, 255, 248); // Subnet Mask
EthernetClient client; // It creates a Client instance,
EthernetServer iec104Server(2404); // for IEC 670-5-104- port- 2404, this is our RTU

uint8_t iec104ReciveArray[24]; //Incoming buffer,
uint8_t iec104SPI1[24]; //IEC data to be exchanged with client

int txcnt = 0; //counters of transmitted packets, 2 bytes each
int rxcnt = 0; //counters of received packets, 2 bytes each
uint8_t MessageLength;
// to avoid switch debouncing

long debouncing_time = 10; //Debouncing Time in Milliseconds
volatile unsigned long last_micros;

void setup()
{
pinMode(2, INPUT); //using pin 2 as harware interrupt, coupled with pin 4
pinMode(4, INPUT);

attachInterrupt(0,debounceInterrupt,CHANGE);// for spontaneous input

//server creation
Ethernet.begin(mac, ip, gateway, subnet); // Ethernet device initialization
}

void loop()
{
client = iec104Server.available();

if(client.available())
{
iec104ReciveArray[6]=00;
int i = 0;
while(client.available())
{
iec104ReciveArray = client.read();//write data to the receive buffer

  • i++;*
  • }*
    }
  • delay(20);*
    if(iec104ReciveArray[2]==7)
    STARTDTCON();
    else if(iec104ReciveArray[2]==67)
    TESTTFRCON();

}

// To avoid Switch Debouncing
void debounceInterrupt()
{
if((long)(micros() - last_micros) >= debouncing_time * 1000)

  • {*
  • SPONT(); // calling the send to client function for spontaneous data*
  • last_micros = micros();*
  • }*
    }
    void SPONT()
    {
  • bitWrite( iec104SPI1[15], 0, digitalRead( 4 )); // reads Pin 4 status*
  • iec104SPI1[0]=104;*
  • iec104SPI1[1]=14;//APDU length = APCI (4) + ASDU (10), M_SP_NA_1*
  • iec104SPI1[2]=lowByte(txcnt);*
  • iec104SPI1[3]=highByte(txcnt);*
  • iec104SPI1[4]=lowByte(rxcnt);*
  • iec104SPI1[5]=highByte(rxcnt);*
  • iec104SPI1[6]=1;//type 1, M_SP_NA_1*
  • iec104SPI1[7]=1;//no. of objects*
  • iec104SPI1[8]=3;//Spontaneous*
  • iec104SPI1[9]=00;//OA*
  • iec104SPI1[10]=01;// CA*
  • iec104SPI1[11]=00;// CA*
  • iec104SPI1[12]=00;//IOA*
  • iec104SPI1[13]=02;//IOA*
  • iec104SPI1[14]=00;//IOA*
  • txcnt=txcnt+2;*
  • client.write(iec104SPI1, 16);*
  • i=0;*
    }
    void STARTDTCON() // a function to send Start Data Confirmation message to client
    {
  • rxcnt=0;*
  • txcnt=0;*
  • iec104ReciveArray[2]=11;//type APDU, its STARTDT Confirmation U-Frame 68 04 0B 00 00 00,*
  • MessageLength = iec104ReciveArray[1]+2;//message length + 2 bytes Start and Lenght APCI,*
  • client.write(iec104ReciveArray, MessageLength);//sending back*
    }
    void TESTTFRCON() // a function to send a test transfer confirmation message to client.
    {
  • iec104ReciveArray[2] =131; //TI for TESTFR con, remains same*
  • MessageLength = iec104ReciveArray[1]+2;*
  • client.write(iec104ReciveArray, MessageLength);*
    }

Start by putting code tags round the code in your post to eliminate the italics when you use ­[­i] as an array index and to make it easier to select and copy

Thank you for the wise advice, here I go, please see the code.

#include <Ethernet.h>

byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB7 };             // MAC Address,
IPAddress ip(172, 31, 15, 2);                        // ip address of the monitored device or RTU
IPAddress gateway(172, 31,15, 1); // IP Address of Gateway, My Laptop or in network it may be Router.
IPAddress subnet(255, 255, 255, 248); // Subnet Mask
EthernetClient client; // It creates a Client instance,
EthernetServer iec104Server(2404); // for IEC 670-5-104- port- 2404, this is our RTU

uint8_t iec104ReciveArray[24]; //Incoming buffer,
uint8_t iec104SPI1[24]; //IEC data to be exchanged with client

int txcnt = 0; //counters of transmitted packets, 2 bytes each
int rxcnt = 0; //counters of received packets, 2 bytes each
uint8_t MessageLength;
// to avoid switch debouncing

long debouncing_time = 10; //Debouncing Time in Milliseconds
volatile unsigned long last_micros;

void setup()
{
pinMode(2, INPUT); //using pin 2 as harware interrupt, coupled with pin 4
pinMode(4, INPUT);

attachInterrupt(0,debounceInterrupt,CHANGE);// for spontaneous input

//server creation
Ethernet.begin(mac, ip, gateway, subnet); // Ethernet device initialization
 }

void loop()
{
client = iec104Server.available();

 if(client.available())
{
    iec104ReciveArray[6]=00;
    int i = 0;
    while(client.available())
    {
    iec104ReciveArray[i] = client.read(); //write data to the receive buffer
    i++;
    }
}
    delay(20);


if(iec104ReciveArray[2]==7)
STARTDTCON();
else if(iec104ReciveArray[2]==67)
TESTTFRCON();
 
   
}
 

// To avoid Switch Debouncing
void debounceInterrupt()
{
  if((long)(micros() - last_micros) >= debouncing_time * 1000)
  {
    SPONT(); // calling the send to client function for spontaneous data
    last_micros = micros();
  }
}

void SPONT()
{
      bitWrite( iec104SPI1[15], 0, digitalRead( 4 )); // reads Pin 4 status
      iec104SPI1[0]=104;
      iec104SPI1[1]=14;//APDU length = APCI (4) + ASDU (10), M_SP_NA_1
      iec104SPI1[2]=lowByte(txcnt);
      iec104SPI1[3]=highByte(txcnt);
      iec104SPI1[4]=lowByte(rxcnt);
      iec104SPI1[5]=highByte(rxcnt);
      iec104SPI1[6]=1;//type 1, M_SP_NA_1
      iec104SPI1[7]=1;//no. of objects
      iec104SPI1[8]=3;//Spontaneous
      iec104SPI1[9]=00;//OA
      iec104SPI1[10]=01;// CA
      iec104SPI1[11]=00;// CA
      iec104SPI1[12]=00;//IOA
      iec104SPI1[13]=02;//IOA
      iec104SPI1[14]=00;//IOA
      txcnt=txcnt+2;
      client.write(iec104SPI1, 16);
 
}

void STARTDTCON() // a function to send Start Data Confirmation message to client
{
  rxcnt=0;
  txcnt=0;
  iec104ReciveArray[2]=11;//type APDU, its STARTDT Confirmation U-Frame 68 04 0B 00 00 00,
  MessageLength = iec104ReciveArray[1]+2;//message length + 2 bytes Start and Lenght APCI,
  client.write(iec104ReciveArray, MessageLength);//sending back
}

void TESTTFRCON() // a function to send a test transfer confirmation message to client.
{
  iec104ReciveArray[2] =131; //TI for TESTFR con, remains same
  MessageLength = iec104ReciveArray[1]+2;
  client.write(iec104ReciveArray, MessageLength);
}
void debounceInterrupt()
{
  if((long)(micros() - last_micros) >= debouncing_time * 1000)
  {
    SPONT(); // calling the send to client function for spontaneous data
    last_micros = micros();
  }
}

Using micros() within an interrupt is not a good idea, but in general what you should do is make sure your 'client' processes are not interfered with by each other so instead (i'll leave the micros() in though it should be changed)

volatile bool sentToClient=false;

.....

void loop()
{
if (sentToClient) {
  sentToClient=false;
  SPONT(); // calling the send to client function for spontaneous data
}
client = iec104Server.available();

 if(client.available())
{
    iec104ReciveArray[6]=00;
    int i = 0;
    while(client.available())
    {
    iec104ReciveArray[i] = client.read(); //write data to the receive buffer
    i++;
    }
}
    delay(20);
if(iec104ReciveArray[2]==7)
STARTDTCON();
else if(iec104ReciveArray[2]==67)
TESTTFRCON();   
}

.....

void debounceInterrupt()
{
  if((long)(micros() - last_micros) >= debouncing_time * 1000)
  {
    sentToClient=true;
    last_micros = micros();
  }
}

Using micros() within an interrupt is not a good idea,

Why is it not a good idea ? As long as you capture its current value on entry to the ISR then you have a timestamp that you can use in the ISR

What I would not do is to read micros() again in the ISR, rather use the captured value

Dear UKHeliBob Sir & Deva_Rishi Sir,

I am very thankful to you for such a prompt response. Highly appreciated.

Deva Rishi sir, actually I was in search of switch debounce mechanism and I got this working code online, thanks to the programmer, it worked for me very well.

Actually I misunderstood the server.write() and client.write() functions of Ethernet.h library. I was using the functions in exactly opposite manner. My problem resolved when I used server.write(); instead of client.write(); in SPONT(); function
have a look at the code: But I am confused, why client.write(); is giving correct output in periodic loop?

void SPONT()
{ 
      bitWrite( iec104SPI1[15], 0, digitalRead( 4 ));
      iec104SPI1[0]=104;
      iec104SPI1[1]=14;//APDU length = APCI (4) + ASDU (10), M_SP_NA_1 
      iec104SPI1[2]=lowByte(txcnt);
      iec104SPI1[3]=highByte(txcnt);
      iec104SPI1[4]=lowByte(rxcnt);
      iec104SPI1[5]=highByte(rxcnt);
      iec104SPI1[6]=1;//type 1, M_SP_NA_1
      iec104SPI1[7]=1;//no. of objects
      iec104SPI1[8]=3;//Spontaneous
      iec104SPI1[9]=00;//OA
      iec104SPI1[10]=01;// CA
      iec104SPI1[11]=00;// CA
      iec104SPI1[12]=00;//IOA
      iec104SPI1[13]=02;//IOA
      iec104SPI1[14]=00;//IOA
      iec104Server.write(iec104SPI1, 16);
      txcnt=txcnt+2;
}

void STARTDTCON()  // Start Data Confirmation
{ 
  rxcnt=0;
  txcnt=0;
  iec104ReciveArray[2]=11;//type APDU, its STARTDT Confirmation U-Frame 
  MessageLength = 6;// remains same, Its Total Length including Start Byte and Message length Byte
  client.write(iec104ReciveArray, MessageLength);//sending back

}