How to make RN2483 node use a fixed channel and spreading factor?

To only use one channel, the following worked for me for ABP with the TTN libraries along with with RN2483 version 1.0.1 (Dec 15 2015 09:38:09), on EU868.

Of course, this is only for testing. To use, e.g., channel 5:

// Initialise ABP and set up the 8 standard channels:
ttn.personalize(devAddr, nwkSKey, appSKey);

// Disable 7 of the 8 default channels. This basically disables the
// mandatory LoRaWAN channel hopping, so FOR TESTING ONLY.
exec("mac set ch status 0 off");
exec("mac set ch status 1 off");
exec("mac set ch status 2 off");
exec("mac set ch status 3 off");
exec("mac set ch status 4 off");
// exec("mac set ch status 5 off");
exec("mac set ch status 6 off");
exec("mac set ch status 7 off");

Beware that when disabling channels, the resulting single-channel node will have a very low maximum duty cycle of only 0.125%. The wiki explains:

In the European band, a transmission on a channel within a frequency band, also influences the other frequencies in that band.

…and:

As a per-channel duty cycle limit is easier to implement, you can also divide the sub-band duty cycle over the number of channels in that sub-band. So for example, in a sub-band with 8 channels and a duty cycle of 1%, each channel has a duty cycle of 1/8% (that’s 0.125%).

This method is also implemented by the RN2483 module, and as a result, instead of seeing the no_free_ch when you send too quickly after the first message you can send multiple messages before all 8 channels are “blocked” and the duty cycle is enforced.

One can validate that indeed the maximum duty cycle for, e.g., channel 5 is 0.125%:

// Print the default duty cycle: 799 means 100/(799+1) = 0.125%, which
// is 1% for the whole sub-band, divided over 8 channels; see explanation
// on https://www.thethingsnetwork.org/wiki/LoRaWAN/Duty-Cycle
exec("mac get ch dcycle 5");

Though the RN2483 allows one to set the maximum duty cycle for that one channel to be 1%, I think one should not do that. LoRaWAN mandates:

The end-device changes channel in a pseudo-random fashion for every transmission. The resulting frequency diversity makes the system more robust to interferences.

So, I’d say that disabling some default channels from a sub-band does not imply one can then (ab)use the full 1% for the one channel that is used. Instead, I feel that disabling default channels implies the node’s maximum duty cycle is decreased. (And it’s a maximum anyway; better stay below the maximum.)


The exec function as used above needs to be defined like below.

/**
 * Executes the given AT command and prints its result, if any. 
 * FOR TESTING ONLY; might cause a memory leak with String?
 */
String exec(const char* cmd) {
  debugSerial.print(cmd);
  debugSerial.print(F(": "));
  loraSerial.println(cmd);
  while(!loraSerial.available()) {
    delay(50);
  }
  String s = "";
  // This includes \r\n newline:
  while(loraSerial.available()) {
    s.concat((char)loraSerial.read());
  }
  debugSerial.write(s.c_str());
}