TTN requests unsupported hwspec=sx1301/6 for Armbian LoRaWAN Basic Station gateway

Hi,

I’m trying to convert a Bobcat G285 into a The Things Network / The Things Stack gateway using LoRa Basics Station.

The hardware is:

  • Bobcat G285

  • Rockchip RK3568, ARM64

  • Semtech SX1302 LoRa concentrator

  • 2 × SX1250 radio front-ends

  • SPI connected through /dev/spidev1.0

  • Modified/custom Armbian-based Linux image specifically for the Bobcat G285

I’m using the standard Semtech LoRa Basics Station source from the official Git repository, without modifying the upstream source code.

The station builds and starts correctly:

Station: 2.0.6(corecell/std)

It successfully connects to:

wss://eu1.cloud.thethings.network:8887

TTN also identifies the gateway as:

platform: corecell

station: 2.0.6(corecell/std)

model: corecell

However, immediately after the connection TTN sends a router configuration containing:

hwspec=sx1301/6

and Basic Station terminates the connection with:

[RAL:ERRO] Unsupported hwspec=sx1301/6

[any:ERRO] Closing connection to muxs - error in s2e_onMsg

I traced this through the Semtech source. The Corecell build uses ral_lgw.c and sx130xconf.c, where the current code explicitly checks:

if( strcmp(hwspec, "sx1301/1") != 0 ) {

LOG(MOD_RAL|ERROR, “Unsupported hwspec: %s”, hwspec);

return 0;

}

So sx1301/6 is rejected before the SX1302 configuration is applied.

I’m trying to understand why TTS is sending sx1301/6 for this gateway. Is this related to the gateway model/hardware definition in TTS, or is sx1301/6 expected for some reason?

My goal is not to modify the Semtech upstream source code just to make the Bobcat work. I’d prefer to keep the standard Semtech Git source intact for future compatibility and, if necessary, solve this through the gateway configuration or an external/local build configuration.

Has anyone encountered hwspec=sx1301/6 with a Corecell/SX1302 gateway, or knows where this hwspec value is determined in The Things Stack?

Thanks,
Ad

Configuration

My current bobcat-g285/station.conf is:

{
"SX1302_conf": {

    "device": "/dev/spidev1.0",

    "lorawan_public": true,

    "clksrc": 0,

    "full_duplex": false,

    "radio_0": {

        "type": "SX1250",

        "rssi_offset": -215.4,

        "rssi_tcomp": {

            "coeff_a": 0,

            "coeff_b": 0,

            "coeff_c": 20.41,

            "coeff_d": 2162.56,

            "coeff_e": 0

        },

        "tx_enable": true,

        "antenna_gain": 0,

        "tx_gain_lut": \[

            {"rf_power": 12, "pa_gain": 1, "pwr_idx": 4},

            {"rf_power": 13, "pa_gain": 1, "pwr_idx": 5},

            {"rf_power": 14, "pa_gain": 1, "pwr_idx": 6},

            {"rf_power": 15, "pa_gain": 1, "pwr_idx": 7},

            {"rf_power": 16, "pa_gain": 1, "pwr_idx": 8},

            {"rf_power": 17, "pa_gain": 1, "pwr_idx": 9},

            {"rf_power": 18, "pa_gain": 1, "pwr_idx": 10},

            {"rf_power": 19, "pa_gain": 1, "pwr_idx": 11},

            {"rf_power": 20, "pa_gain": 1, "pwr_idx": 12},

            {"rf_power": 21, "pa_gain": 1, "pwr_idx": 13},

            {"rf_power": 22, "pa_gain": 1, "pwr_idx": 14},

            {"rf_power": 23, "pa_gain": 1, "pwr_idx": 15},

            {"rf_power": 24, "pa_gain": 1, "pwr_idx": 16},

            {"rf_power": 25, "pa_gain": 1, "pwr_idx": 17},

            {"rf_power": 26, "pa_gain": 1, "pwr_idx": 19},

            {"rf_power": 27, "pa_gain": 1, "pwr_idx": 20}

        \]

    },

    "radio_1": {

        "type": "SX1250",

        "rssi_offset": -215.4,

        "rssi_tcomp": {

            "coeff_a": 0,

            "coeff_b": 0,

            "coeff_c": 20.41,

            "coeff_d": 2162.56,

            "coeff_e": 0

        },

        "tx_enable": false

    }

},

"station_conf": {

    "radio_init": "/opt/bobcat300-ttn/basicstation/bobcat-g285/rinit.sh",

    "RADIO_INIT_WAIT": "5s",

    "RX_POLL_INTV": "10ms",

    "TC_TIMEOUT": "360s",

    "log_file": "stderr",

    "log_level": "DEBUG",

    "log_size": 10000000,

    "log_rotate": 3

}

}

The radio initialization script controls the Bobcat-specific GPIOs for powering and resetting the SX1302.

1 Like

What is the regional configuration on TTN? Can you show a screenshot?

Answered from AI. /6 means “I have 6 frequency plans for you.”

Basic station code can handle it according to AI again :slight_smile:

The summary:

Why TTS sends sx1301/6

TTS builds the hwspec string in pkg/pfconfig/lbslns/lbslns.go:

const configHardwareSpecPrefix = "sx1301"                                  // line 35
conf.HardwareSpec = fmt.Sprintf("%s/%d", configHardwareSpecPrefix, len(fps)) // line 315
  • fps is a list of frequency plans for your gateway’s region.
  • len(fps) is 6 for your band in this TTS version.
  • The prefix "sx1301" is a fixed constant — independent of your actual silicon (SX1302), independent of your gateway model.

So sx1301/6 literally means: “the frequency-plan list has 6 entries.” Each frequency plan contains a full set of channels; the count is of plans, not channels.

Why the solution is valid

The receiving side, sx130xconf_parse_setup in basicstation/src/sx130xconf.c, uses hwspec exactly once:

int sx130xconf_parse_setup (struct sx130xconf* sx130xconf, int slaveIdx,
                            str_t hwspec, char* json, int jsonlen) {
    if( strcmp(hwspec, "sx1301/1") != 0 ) {
        LOG(MOD_RAL|ERROR, "Unsupported hwspec: %s", hwspec);
        return 0;
    }
    ...
    parse_sx130x_conf(&D, sx130xconf);

After that check, hwspec is never referenced again — not passed to the parser, not stored, not used to size anything. It is a pure gate.

The parser (parse_sx130x_conf) fills fixed-size arrays (rfconf[LGW_RF_CHAIN_NB], ifconf[LGW_IF_CHAIN_NB]) from one concentrator config object. It has no notion of a plan count and does not loop over N. The only topology parameter is slaveIdx, which the corecell path passes as -1 — no slave fan-out.

Therefore:

  • /1 vs /6 changes nothing in what the parser does once the gate passes.
  • The /1 check is a vestigial guard — the lgw1/corecell path doesn’t need or use N.
  • The same codebase already uses the tolerant form (strncmp(hwspec, "sx1301/", 7)) in ral_lgw2.c and ral_master.c; only ral_lgw.c and sx130xconf.c still hard-code /1.

Relaxing the check in both ral_lgw.c (which produces your original Unsupported hwspec=sx1301/6 error) and sx130xconf.c (which would reject next) to accept sx1301/<N> cannot change parsing behavior for the config TTS sends. The message body is a single concentrator config; the /6 is metadata, and the single-concentrator parser is agnostic to it.

One-sentence version

TTS sends /6 because it derives the hwspec from len(frequency_plans) for your region, and the fix is valid because hwspec is used only as a string gate in the single-concentrator parser — the /N value is never used, so accepting /6 changes nothing downstream.