 void onEvent (ev_t ev) {
 }
 
+osjob_t txjob;
+osjob_t timeoutjob;
+static void tx_func (osjob_t* job);
+
 // Transmit the given string and call the given function afterwards
 void tx(const char *str, osjobcb_t func) {
   os_radio(RADIO_RST); // Stop RX first
-  delay(1); // Wait a bit, without this os_radio below asserts
+  delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet
   LMIC.dataLen = 0;
   while (*str)
     LMIC.frame[LMIC.dataLen++] = *str++;
   LMIC.osjob.func = func;
   os_radio(RADIO_TX);
+  Serial.println("TX");
 }
 
 // Enable rx mode and call func when a packet is received
 void rx(osjobcb_t func) {
   LMIC.osjob.func = func;
-  // Enable RX continuously
+  LMIC.rxtime = os_getTime(); // RX _now_
+  // Enable "continuous" RX (e.g. without a timeout, still stops after
+  // receiving a packet)
   os_radio(RADIO_RXON);
+  Serial.println("RX");
+}
+
+static void rxtimeout_func(osjob_t *job) {
+  #ifdef LED_RED
+  digitalWrite(LED_RED, LOW); // on
+  digitalWrite(LED_GREEN, HIGH); // off
+  #else
+  digitalWrite(LED_BUILTIN, LOW); // off
+  #endif
 }
 
 static void rx_func (osjob_t* job) {
-  Serial.print("RX ");
+  #ifdef LED_RED
+  digitalWrite(LED_RED, HIGH); // off
+  digitalWrite(LED_GREEN, HIGH); // off
+  delay(10);
+  digitalWrite(LED_GREEN, LOW); // on
+  #else
+  digitalWrite(LED_BUILTIN, HIGH); // off
+  delay(10);
+  digitalWrite(LED_BUILTIN, HIGH); // on
+  #endif
+
+  // Timeout RX (i.e. update led status) after 1 second without RX
+  os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(2500), rxtimeout_func);
+
+  // Reschedule TX so that it should not collide with the other side's
+  // next TX
+  os_setTimedCallback(&txjob, os_getTime() + ms2osticks(500), tx_func);
+
+  Serial.print("Got ");
   Serial.print(LMIC.dataLen);
   Serial.println(" bytes");
   Serial.write(LMIC.frame, LMIC.dataLen);
   Serial.println();
+
+  // Restart RX
+  rx(rx_func);
 }
 
 static void txdone_func (osjob_t* job) {

  delay(1000);
  #endif

  // initialize runtime env
  os_init();

  Serial.println("Started");
  Serial.flush();

  // setup initial job
  os_setCallback(&initjob, init_func);
}

void loop() {

}

// log text to USART and toggle LED
static void tx_func (osjob_t* job) {
  // say hello
  tx("Hello, world!", txdone_func);
  // reschedule job every 1000-1500ms, unless packets are received, then
  // rx_func will reschedule more often
  os_setTimedCallback(job, os_getTime() + ms2osticks(1000 + random(500)), tx_func);
}

// application entry point
void setup() {
