Can LMIC 1.6 be set up to use a single channel and SF?

Hi @arjanvanb and @mogyoros,

Thanks for advices, I have tested @arjanvanb solution and it works for me too in ABP mode with the LMiC 1.5 but don’t know if it works for 1.6.

1 Like

Hi Arjan!
I gave it an another chance to make it work as you wrote, but no luck. During join it still tries all 3 base frequencies.
Maybe beacuse I am using OTAA? I will try to look into deeper tomorrow…
My code (examples/ttn-otaa):

void setup() {
// For Pinoccio Scout boards
digitalWrite(VCC_ENABLE, HIGH);
// LMIC init
// Reset the MAC state. Session and pending data transfers will be discarded.
int channel = 0;
int dr = DR_SF7;
for(int i=0; i<9; i++) { // For EU; for US use i<71
    if(i != channel) {
LMIC_setDrTxpow(dr, 14);
// Start job (sending automatically starts OTAA too)


Console output:

401: engineUpdate, opmode=0x808
Packet queued
613: engineUpdate, opmode=0xc
63718: engineUpdate, opmode=0xc
64407: TXMODE, freq=868500000, len=23, SF=7, BW=125, CR=4/5, IH=0
380736: RXMODE_SINGLE, freq=868500000, SF=7, BW=125, CR=4/5, IH=0
446212: RXMODE_SINGLE, freq=869525000, SF=12, BW=125, CR=4/5, IH=0
456355: engineUpdate, opmode=0xc
921139: engineUpdate, opmode=0xc
921832: TXMODE, freq=868100000, len=23, SF=7, BW=125, CR=4/5, IH=0
1238289: RXMODE_SINGLE, freq=868100000, SF=7, BW=125, CR=4/5, IH=0
1303765: RXMODE_SINGLE, freq=869525000, SF=12, BW=125, CR=4/5, IH=0
1313908: engineUpdate, opmode=0xc
1817924: engineUpdate, opmode=0xc
1818620: TXMODE, freq=868300000, len=23, SF=7, BW=125, CR=4/5, IH=0
2134950: RXMODE_SINGLE, freq=868300000, SF=7, BW=125, CR=4/5, IH=0
2200426: RXMODE_SINGLE, freq=869525000, SF=12, BW=125, CR=4/5, IH=0
2210570: engineUpdate, opmode=0xc
2495607: engineUpdate, opmode=0xc

And so on… As you can see It uses all 3 base channels during OTAA join.

Confirmed. Works fine when using ABP. So the library definately handles this function differently when using OTAA.


:warning: Any packet forwarder that is not a full gateway is harmful to other users of The Things Network, and should not be used if there is even a small chance that other TTN users are in your wide area neighbourhood. So, the following solution is probably not what you need, and is only appropriate if you know what you’re doing.

I understand, show me the code.
Really, I understand, show me the code.
Really, even though I could not come up with the answer myself, I really do understand this is likely to harm others, but I still need the code.

For 1.5, if one does not want to change the library, here is how one can make an ABP or OTAA sketch work with a single channel gateway and Matthijs Kooijman’s LMiC:

  • Somewhere above onEvent define:

    // Define the single channel and data rate (SF) to use
    int channel = 0;
    int dr = DR_SF7;
    // Disables all channels, except for the one defined above, and sets the
    // data rate (SF). This only affects uplinks; for downlinks the default
    // channels or the configuration from the OTAA Join Accept are used.
    // Not LoRaWAN compliant; FOR TESTING ONLY!
    void forceTxSingleChannelDr() {
        for(int i=0; i<9; i++) { // For EU; for US use i<71
            if(i != channel) {
        // Set data rate (SF) and transmit power for uplink
        LMIC_setDrTxpow(dr, 14);
  • For ABP, in init() replace LMIC_setDrTxpow(DR_SF7,14); with:

    // Only use one channel and SF
  • For OTAA, in init() after LMIC_reset(); add:

    // Make LMiC initialize the default channels, choose a channel, and
    // schedule the OTAA join
    // LMiC will already have decided to send on one of the 3 default
    // channels; ensure it uses the one we want
    LMIC.txChnl = channel;
    // ...and make sure we see the EV_JOINING event being logged
  • For OTAA, for EV_JOINED in onEvent use:

    case EV_JOINED:
        // Ignore the channels from the Join Accept
        // Disable link check validation (automatically enabled during join)

But note:

  • Only tested with LMiC 1.5, for EU868, channel 0.

  • For OTAA this assumes the first attempt succeeds; if the Join Request or Join Accept are somehow lost, LMiC will still try different channels and data rates due to the logic in nextJoinState

  • Only tested with an OTAA Join Accept received in RX1 (for EU868 using the same channel as the uplink/join request); when received in RX2 (for EU868 always using 869.525 on SF9), LMiC might also use other values to send? (But maybe the LMIC_setDrTxpow(dr, 14) suffices.)


For Matthijs’ LMiC 1.5, the OTAA flow is:

  • In examples/ttn-otaa/ttn-otaa.ino, calling LMIC_reset clears the keys after which LMIC_setTxData2 is called to schedule some data to be sent.

  • In src/lmic.c, engineUpdate controls the flow, like if something has been scheduled for transmission. When LMIC.devaddr == 0 this will first trigger LMIC_startJoining:

    • LMIC_startJoining calls initJoinLoop, which for EU868 also calls initDefaultChannels. (For US915, that’s already done in LMIC_reset.)

    • In initJoinLoop, for EU868 one out of 3 channels is selected (LMIC.txChnl = os_getRndU1() % 3) while for US915 it’s set to the first channel (LMIC.txChnl = 0).

    • The actual join is not started yet; LMIC_startJoining basically schedules joining to be the first thing to do when time permits. So when manually calling LMIC_startJoining, one can quickly use LMIC.txChnl = channel to change the channel that LMiC selected.

  • Whenever a join attempt fails, nextJoinState will select another channel and/or data rate (SF), without checking if the channel is activated; see One cannot change that behavior from one’s own sketch.


For the US, which uses 64 channels while gateways might only use 8 (or one, for a single-channel gateway), the following from April 2016 might be relevant:


I’ve not validated, but someone might want to create a pull request if this bug is currently still in the code


Thank you very much! Now my node is sending uplinks in a single channel in US _915

Hello Guys,

First of all thank you so much for the information. I am really new in this world, and I am trying to build the complete system, LoraWan Gateway (Raspberry Pi 3B + Lora Shield v1.4) and a device (arduino nano connected to Lora RFM95).

I have already did the systems with help of this 2 tutorials:

And after I edit my code as arjanvanb explain to have transmission single channel… What is very strange for me is that I can always receive the transmission in LoraWan Gateway but after when I see in ttn site in data, sometimes the addr is wrong, as can be seen in the picture… Anyone understands2018-03-27_01-00-51 why?

Thank you so much and I hope you could help me :slight_smile:

Best regards,
Valter Mário

Why do you think the DevAddr is wrong? Which one do you expect?

Seems to me that your gateway is simply also receiving LoRa (or LoRaWAN) packets from other devices, which are not yours. That is to be expected, as the gateway radio receiver just listens and forwards to TTN, which will ignore the packets when from unknown devices. (A TTN DevAddr always starts with hexadecimal 26 or 27.)

1 Like

Looks like you are also forwarding packets with CRC errors. If so, please disable that.
While testing make sure to keep at least 3 meter between the node and the gateway.

Wow! Today when I woke up I took my system into working again but this time with Gateway 3 meters away and apparently it’s working perfect!

Since I had the opportunity to talk with you, I would like ask another thing to you: Apparently, my gateway only support uplink. Do you know some API or another way of doing it support Downlink with this hardware (Raspberry Pi 3 + Lora Shield)?

Thank you so much for your advice, it’s amazing have a community that answer so fast!

Thank you so much!

Have you checked DRAGINO problems and solutions topic for downlink enabled RPi setup?

Hi Wklenk,
Thanks for your posts and supports.
I have some few questions.
1-The single channel gateway you pointed in this link ( ) does not support downlinks so can the gateway send downlinks ??
2- I have a LoRaWan end device that runs the LMIC v.16 ( from your git repo), I can send data with it but I can’t receive any and I honestly have hard times understanding the code.
I would like to know if the is a version of the code that supports ABP since it is the only authentification supported by single channel gateway. I have to spend too much time trying to make it work, please help if you can Thanks.
Note: my end device and my gateway are based on RPI+LoRa GPS HAT.

The code supports ABP, but there is no example how to do it.

You can check the example given here:

The important lines are these:

// LoRaWAN NwkSKey, network session key
// This is the default Semtech key, which is used by the early prototype TTN
// network.
static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };

// LoRaWAN AppSKey, application session key
// This is the default Semtech key, which is used by the early prototype TTN
// network.
static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };

// LoRaWAN end-device address (DevAddr)
static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node!


 #ifdef PROGMEM
    // On AVR, these values are stored in flash and only copied to RAM
    // once. Copy them to a temporary buffer here, LMIC_setSession will
    // copy them into a buffer of its own again.
    uint8_t appskey[sizeof(APPSKEY)];
    uint8_t nwkskey[sizeof(NWKSKEY)];
    memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
    memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
    LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
    // If not running an AVR with PROGMEM, just use the arrays directly
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
1 Like

Hi, don’t know if some people still have this issue but I tested in the context of a single-channel gateway (SCG) with downlink capabilities and it seems it is not really possible to avoid changing a bit the LMIC source to really have for instance join-accept sent on a fix frequency and especially another SF than the default one at SF7. The solution proposed by @arjanvanb only work if SF7 is used by both the device for all uplinks and the SCG. If the device and the SCG want to use for instance SF12, then calling LMIC_startJoining(); and set LMIC.txChnl to 0 corrupt the LMIC timing because the transmission time has been computed based on SF7. The results is that the RX1 window is scheduled too early compared to what is then advertised by the join-accept (5s window). Therefore the modification proposed by @mogyoros work better.

Meanwhile 2020, I’d really recommend getting one of the cheap full gateway options instead.

1 Like

Yes totally agree, we are using RPI+RAK2245 a lot. But we also deploy a lot of very cheap single-channel gateway for small-holders in Africa who only want to manage a dozen of sensors for rural applications.

I am new to this technology, but i saw when you go to the lmic library, you can edit the frequency setup for Rx-Tx by commenting all except one, or by chosing the max channel number to 1. but not sure that it will help since the channel switches all the time to find the best Bandwidth.

What you are suggesting makes the software non LoRaWAN compliant.

No, the channel switches all the time to avoid overloading a single frequency. Which is exactly what happens when people start using non compliant single channel solutions.

This thread is dated and single channel packet forwarders (single channel nodes included) are no longer supported on the forum. See: Single Channel Packet Forwarders are deprecated and NOT Supported [guidelines]

Advice for those new to LoRaWAN: better learn the concepts of LoRaWAN and how to properly use it.