call interrupt function from hardwareserial.cpp

Hi all,
I am trying to connect arduino with an external device via serial.
The device need an ack for every packet sent, and this ack must arrive to the device in max 300ms.
If the ack isn’t received in time the device disconnects the data service.
I use Arduino mega 256
I’m trying to modify the standard hardwareserial lib to generate a timer interrupt when data arrive to the Serial1 of arduino, and the function called by the interrupt will examine the string and generate the appropriate ack.
Someone can suggest me the right way to do that?
I’ve tryed a lot of codes taken from the net, with timer interrupt, but i still can’t reach the target.
Many thanks.
Riccardo

300ms is like four weeks for an Arduino. You should be able to read the incoming characters via the normal Serial (or Serial1, 2, 3) and interpret the results before sending the ACK.

If you have something else in your code that takes longer than 300ms, like a delay() then remove that first.

300ms is a really really really really really really long time. Why do you think you need an interrupt for this? Just keep your loop tight and send your ack when you see data.

My application is quite complex. Arduino is connected to an instrument on Serial3 and a gps on Serial2, so is quite easy that the 300ms windows ends before it stop exchanging data on the other serials.

riccardop:
My application is quite complex. Arduino is connected to an instrument on Serial3 and a gps on Serial2, so is quite easy that the 300ms windows ends before it stop exchanging data on the other serials.

If that stuff causes your loop to take more than 300ms to loop, then you need to refactor that code.

the gps read function uses about 1000 ms to read valid data from gps (4800 baud)

It shouldn't. It should take nanoseconds to copy each character to a buffer as it arrives. When you've collected a full message, then a few more microseconds can process the message.

Your Arduino could be doing thousands of other useful things while it waits for another character.

the gps read function uses about 1000 ms to read valid data from gps (4800 baud)

You must be "blocked" inside that routine, because 1000ms is 1s, the update interval for most GPS devices. Are you really spending 100% of your time there?

Post your sketch, and I'm sure you'll get some good suggestions about how to do Multiple Things at the Same Time. More good info on the Useful Links page.

If you really have to work around other devices that are blocked, you could try the NMEA_isr.ino example in my NeoGPS library. It parses GPS characters during the RX character interrupt, without buffering. Debugging this can be more difficult, so I would suggest fixing the other structure problems first. NeoGPS is available from the IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Cheers, /dev

You can't just take snippets of code from the internet and try to get a program to do what you want. How soon after the message is received will you know it is proper and can send and ACK? That is the question you must answer and then code to make it happen. Is there a check byte or something at the end of the message that must be computed? Why are you currently waiting so long to "ack"?

Paul

4800 bps from GPS is approximately 500 chars/sec... If you allow roughly 150 chars for [u]worst case[/u] NMEA sentences, that's getting close to your 300mS host timeout.

So the above suggestion to parse-on-the-fly is a good one, or bump your GPS serial speed up if you can. You can parse the sentences individually and ACK for each sentence - don't wait for the whole bundle to arrive before working on the first sentence. (You're probably only interested in two or three of them for data anyway)... ignore the others until an interesting $Gxxxx arrives.

Have a look how reading the serial port is done in the updated Serial Input Basics thread. It uses a non-blocking approach and buffers the input till a complete message is received.

The below is based on example 2. I’ve renamed variables and functions to be able to cater for 2 serial ports. You can send the acknowledge in e.g. showGpsMsg(). It reads both serial ports “in parallel”. You might have to change the endmarkers to suite your needs.

// Example 2 - Receive with an end-marker

/*
  gps on serial 2; endmarker '\r'
  instruent on serial 3; endmarker '\n'
*/

const byte numCharsInstrument = 32;
char instrumentMessage[numCharsInstrument];
boolean instrumentMsgComplete = false;

const byte numCharsGps = 150;
char gpsMessage[numCharsGps];
boolean gpsMsgComplete = false;

void setup()
{
  Serial.begin(9600);
  Serial.println("<Arduino is ready>");
}

void loop()
{
  readInstrument();
  showInstrumentMsg();

  readGps();
  showGpsMsg();
}

void readInstrument()
{
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial3.available() > 0 && instrumentMsgComplete == false)
  {
    rc = Serial3.read();

    if (rc != endMarker)
    {
      instrumentMessage[ndx] = rc;
      ndx++;
      if (ndx >= numCharsInstrument)
      {
        ndx = numCharsInstrument - 1;
      }
    }
    else
    {
      instrumentMessage[ndx] = '\0'; // terminate the string
      ndx = 0;
      instrumentMsgComplete = true;
    }
  }
}

void readGps()
{
  static byte ndx = 0;
  char endMarker = '\r';
  char rc;

  while (Serial2.available() > 0 && gpsMsgComplete == false)
  {
    rc = Serial2.read();

    if (rc != endMarker)
    {
      gpsMessage[ndx] = rc;
      ndx++;
      if (ndx >= numCharsGps)
      {
        ndx = numCharsGps - 1;
      }
    }
    else
    {
      gpsMessage[ndx] = '\0'; // terminate the string
      ndx = 0;
      gpsMsgComplete = true;
    }
  }
}

void showInstrumentMsg()
{
  if (instrumentMsgComplete == true)
  {
    Serial.print("Instrument: ");
    Serial.println(instrumentMessage);
    instrumentMsgComplete = false;
  }
}

void showGpsMsg()
{
  if (gpsMsgComplete == true)
  {
    Serial.print("GPS: ");
    Serial.println(gpsMessage);
    gpsMsgComplete = false;
  }
}

Not tested but does compile.