The factory test sketch does exactly what you want—it is designed to play ‘ping pong’ with another similarly configured module. It sends a message then goes into receive mode waiting for a response. When it receives a response, it transmits another message and goes back into receive mode.
As you probably note, the critical part of the main loop of the sketch, the switch statement at the end, effectively defines a state machine. The machine has three valid states: transmit (STATE_TX), receive (STATE_RX) and ‘idle’ (LOWPOWER). On completion of the lora_init() function, towards the end of the setup() function, the machine is set in the TX state and executes that part of the switch statement in the main loop.
In my opinion, the code in the main loop could be better structured so that its function was more obvious (in my view, the only code in the main loop should be the state machine switch statement) but, be that as it may, the code before the switch statement just performs some additional functions that are associated with the different states.
Now, I can’t explain exactly why some of the code is written like it is, or is even there, and for my own application I’ve had to restructure things as suggested above, leaving out some things that appear to be unnecessary as I’ve tried to unravel the logic that’s been used.
Nonetheless, having transmitted a message (twice, actually, it seems if you work through the code, and I’ll comment on this in a moment), the state machine looks like it goes into the LOWPOWER state, although my immediate expectation would be that it would go into the RX state, as per the instruction in the TX_Done function. Either way, the sketch then waits for input, either because it’s in the RX state waiting (Radio.Rx()), or an interrupt (Radio.irqProcess()) is triggered. When data is received, the RxDone() function is executed and the sketch goes back into TX state.
Because of my confusion here with what state the machine was actually in, I initially used only two states in my own application: if the machine wasn’t transmitting a message, it was in receive state waiting for one.
It looks like the sketch sends out a message twice to begin with and, if no response is received, it goes into deep sleep waiting for another node to send it a message (one node will always start up first and, until the second node starts up, there’ll be no one to play ping pong with). But this sending the message twice, and there’s also also code in there checking whether or not two messages have been received, is one thing I don’t currently understand, although, in my own application, I’ve had to implement a ‘resend policy’ because the first transmission of a packet is often not received. I’ve got a bit more work to do on that front, but I offer the comment here just in case anyone else encounters a similar problem.
If it helps, the following is my state machine:
switch ( state ) {
case TX_State: {
Serial.println("[loop] Into TX Mode...");
[ assemble message content ]
sendMessage();
state = RX_State;
break;
}
case RX_State: {
if( lora_idle ) {
lora_idle = false;
Serial.println("[loop] Into RX Mode...");
Radio.Rx(0);
}
Radio.IrqProcess( );
break;
}
default: {
state = RX_State;
break;
}
}
Because my application spends most of its time in the RX state, waiting for messages, the lora_idle boolean, which is set to true in the TxDone() and TxTimeout() functions after sleeping the radio, is used to check whether or not the receiver needs to be initialised.
My application works the opposite way around to yours, in that I start in receive state and respond to particular messages, returning to the receive state after sending the response. As I’ve suggested above, however, this application is not yet fully developed and will ultimately go into low power mode when waiting for incoming messages.