FreeRTOS timers and serial reception missing characters

Am running 0.4.7 firmware (and very happy with it).

Decided to try out the newly implemented FreeRTOS timers in my application. They work fine as timers, but I am now having problems with serial reception which was not evident when running the SparkTimerInterval library timers.

Am consistently missing a character within a 100 byte packet.

Some of the timer call backs could be long winded.

I have moved the serial port polling out of loop() and into the new 0.4.7 function serial1Event().

Has anyone had the similar problems? Any ideas or work arounds?

Thanks - @UMD

If you could share your code, that’d help. Try to keep the timer interrupts as short as possible, but use them to set a flag which you can check in your loop.

@UMD, I agree with @Moors7 in keeping things simple in your timer callback functions. Software timers don’t generate interrupts per se but should follow the same rules. The Serial1 port is interrupt driven with a fixed buffer so servicing the port in order to prevent buffer overrun is important. Sharing your code would allow us to better help you :wink:

2 Likes

To extend @peekay123's comment about Serial1 you'd need to know that it is a common misconception -amongst Arduino users too - that serial1Event() will be seviced like an interrupt or an async event.
It isn't, despite the suggestive description of the function

or Arduino

To prevent buffer overflow you need to drop out of loop() or explicitly call serial1Event() before the buffer overflows as all the serialEvent() functions are mere calls to the handler functions between iterations of loop().

https://docs.particle.io/reference/firmware/photon/#serialevent-

See also links like
arduino uno - How does serialEvent gets into main()? - Arduino Stack Exchange
serialEvent call - Programming Questions - Arduino Forum

2 Likes

@ScruffR, you were spot on, I thought serial1Event() was an async event!

So, it really is only a function that neatens up code within loop() a little. Am thinking that in my case it is really just adding another layer of abstraction that I don’t need. I’ll drop it.

@Moors7, @peekay123 , @ScruffR, the following will show the problem. serial1Event() was just calling the serial device protocol handling function, which sits in a tight loop whilst characters are available and puts them to buffer. It is this one call which is missing the character in amongst the 100 or so bytes received during this single call:

  while ((Serial1.available() > 0) && (bufIdx < PACKET_BUFFER_SIZE))
  {
    if ((b = Serial1.read()) == -1)
      continue;   // looping just one more time
    buf[bufIdx++] = b;
  }

Theory is that the FreeRTOS timers are being given air to run by the Serial1,available() and Serial1.read() functions and because of their long winded nature (I know, I know!), is affecting serial reception.

Q1. Is there a better way of doing the above loop? Maybe I should try this instead:

  if ((count = Serial1.available()) > 0) && (bufIdx < PACKET_BUFFER_SIZE))
  {
       for (int i =0; i < count; i++)
      {
              if ((b = Serial1.read()) == -1)
                      break;   // out of loop
              buf[bufIdx++] = b;               
       }
 }

Am thinking that this halves the “air time” given to the timers… will report back.

Q2. I will also try stopping all timers during the data gathering. Is there something like noInterrupt() which could do this neatly?

Got to solve this problem!

Just a few comments:

  • Why do you need the extra loop iteration?
  • How big is your buffer and where do you reset b?
  • AFAIK the RX buffer of any UARTs on the Particles is less than 100byte (I think 32 or 64)
  • Your count aproach might not work, as expected since the buffer could receive extra characters while reading
  • What data rate are you expecting and how long are your other tasks running?
  • With extra high data rates and time demanding side tasks, you might need to make your code tolerant against data loss or need to ensure that your buffer gets emptied in time.

How about this?

  while ((bufIdx < PACKET_BUFFER_SIZE) && ((b = Serial1.read()) >= 0))
  {
    buf[bufIdx++] = b;
  }
1 Like

@ScruffR, the buffer size for Serial1 & 2 is 64 bytes. @UMD, it is not wise to stop interrupts. You speak of a 100 byte “packet”, however, in that packet do you have start/end of packet markers? Are the packets sent as a response to a request or are they asynchronous?

I agree with @ScruffRthat that several factors could affect your code’s ability to receive the data without a buffer overrun (which I am guessing is the problem). Answers to his questions would be good :smile:

1 Like

Lads, (@ScruffR, @peekay123, @Moors7) pleased to report case closed on this one.

(@ScruffR - agree your codeand comments, came to a similar conclusion re the consumer loop - much simpler now.)

Interim solution: when there are characters available in the serial rx buffer, stop one of my four FreeRTOS timers prior to calling the polling code that empties the buffer, and then start the timer again once finished. This fixed the issue… which then led to further investigation…

The specific timer that was stopped was used to light patterns on a NeoPixel LED strip. This routine took 1995 uS to execute.

Final solution: armed with the finding, reviewed the NeoPixel code… found that I had made a change only a few days earlier… changed NeoPixel variant from a 9 way strip to a 64 way matrix for another project.

Solution was to reduce the pixel count back to 9, so that the timer routine now responds in 291 uS.

Conclusion: nervousness solved regarding new timer feature. Lesson - change logs and impacts of these seemingly innocuous changes should always be reviewed. Lesson learnt!

Thanks - @UMD

2 Likes

@UMD, nice job hunting down the problem and sharing it with us. :wink:

The irony is @peekay123 that it means I end up removing your SparkIntervalTimer library from the app!

Thinking more about these sorts of issue, it is par for the course that a little nervousness creeps in when we have a moving feast. Gladly, it is on the improve with each iteration.

All good!

@UMD, I won’t take it personally :sob:

3 Likes