All my apps use serial communications, trust me its fun as it takes some getting used to since the VB6 days.
This is how I do it:
I create a routine that sends packets. Each packet is assigned its own event ID, or if the packet is always the same with a few changes, I just pull from global variables and rebuild/send packets.
I then start a timer of a defined value. I transmit the packet, and then I finish the subroutine.
The RX event will fire, or the timer will fire. RX event fires when there is an ACK or some other form of data, and from there I parse it. compare it against my eventID or some other type of variable so the program knows where I am at, persay.
If the RX event doesnt fire, the timer will. The timer fires, and either retries the send, or aborts the routine on a time-out condition. Failed to respond, etc..
Once the RX is complete, I call a subroutine that moves my variables to the next state, and starts a 2nd timer.
the 2nd timer fires, and calls my send routine with the new variable data, thus starting the process over again for the next sequence.
Every subroutine involved has checks and balances in it that make sure the serial connection is still present, or some other abort sequence hasnt fired. If any get met, then the whole operation gets aborted. (Other subroutines like buttons can fire while your timer is running or your waiting on an RX)