kmark / Bee

The simple DigiMesh XBee library for Arduino
Other
11 stars 9 forks source link

Interrupt mode? #2

Closed nzfarmer1 closed 8 years ago

nzfarmer1 commented 8 years ago

Hi kmark,

I'm using a slightly modified version of your library in my own project to provide a reliable streams based (select repeat arq based) protocol. Great library. Thanks.

As the interface to our protocol is a sub class of the Streams class we are essentially exposing a driver (albeit one that encapsulates Hardware or Software Serial).

To take our code to the next level, what would be ideal is to be able to have the actual xbee reads/writes done in interrupt mode.

I have done some crude experiments using an interrupt timer - but calling XBee tick within an interrupt is hazardous/unreliable and may need some better smarts than I have to make work properly.

Welcome your thoughts, and if you were interested in assisting with this.

Andrew

kmark commented 8 years ago

Hi! Thanks for using Bee.

From what I remember sending data with Arduino's HardwareSerial is done with interrupts as-is. When you write() to it that really just goes to a ring buffer that is then processed/consumed in the UDRE interrupt some time later. It should not be terribly important if you are calling write() from an interrupt or from the main loop but you may need to use interrupt guards (cli() and sei()) in your loop if you're writing from both. That way when your main loop is writing out data to the buffer it isn't being quite literally interrupted by an interrupt that is pushing its own data into the middle of our transmission in the main loop. As an example if we were writing the string "12345" all at once in our main loop, "hello" in our interrupt, and did not disable interrupts in the main loop during the write, we could potentially get "1234hello5" written into the buffer. Or "12hello345" or any such variation. When this data is transmitted it will probably make no sense to the receiver (but "12345hello" or "hello12345" might).

For read(), and in our case tick(), the short answer is that you can't do it from an interrupt because the read operation itself requires an additional interrupt to function. I'm not exactly sure how HardwareSerial all fits together but this seems to the general consensus. You can use a serialEvent but I'm not sure what the advantage is to that function over just checking in your main loop.

Interrupts are best kept as fast (do as little actual work) as possible to avoid a number of pitfalls. Serial and string-related stuff can be pretty taxing to do within an interrupt anyway. Is there any particular reason you want tick() moved into an interrupt?

Please excuse any factual errors in the above as I'm going off some quick Googling and what I remember the last time I had to work with Arduino. XCTU was instrumental for me in understanding the XBee API (particularly the API Frame Builder) and I highly recommend you give it a go.

nzfarmer1 commented 8 years ago

Hi Kevin

Thanks for your reply

We've encapsulated both SoftwareSerial and HardwareSerial and exposed the Xbee as its own Stream - complete with a pretty snazzy protocol to guarantee reliability without hitting performance (though I say so myself).

Now, this all works fine. But when integrating it into existing libraries that use Serial1 say or SoftwareSerial, it relies on their code calling .ready() or .available() - Modbus 4 Arduino for example.

To have this completely seamless, we'd need the tick to get called via an interrupt.

BTW. This project has been some 3 months for me and it's become a bit of an obsession - to see how fast I could reliably get data between two XBees. As of today we're getting 15.8kbps - or a small image in 25 seconds.

Is there somewhere I can forward you some code?

Andrew

nzfarmer1 commented 8 years ago

Just looking at serialEvent. This may be just the thing. Thanks again.

nzfarmer1 commented 8 years ago

serialEvent does the magic. thanks! One observation though, it's imperative that the code not get caught in a while loop. I also found that if there delays in the other party responding (or the transmission intervals were too short) it helped too call XBee.tick() from the loop as well.

kmark commented 8 years ago

Hi Andrew,

Thanks for the progress updates. Good to see serialEvent is working for you. I'm not entirely sure as to why though. Despite the name, serialEvent executes synchronously as part of your loop. If I'm not mistaken the Arduino code for it looks something like this:

int main() {
    setup();
    while(1) {
        loop();
        if(Serial.available()) {
            serialEvent();
        }
    }
    return 0;
}

So the serialEvent call is still being blocked by your loop. Here's an interesting EE.SE answer about max transfer speeds with the Arduino.

nzfarmer1 commented 8 years ago

Yes. You are right. It is not an interrupt per se. But for our purposes (integrating 3rd party libraries and replacing their "Serial" with our "xSerial" and changing as little of their code as possible) it is perfect. That said, am still keen to explore an interrupt driven approach.

kmark commented 8 years ago

I believe in order to do something more interrupt-driven that is also compatible with your project you'd have to completely replace how the Arduino communicates with the UART. I bet that could also help you eek out some extra speed as the Arduino software platform isn't known for its performance.