Changes coming to ChirpStack

I’m planning to release a first test-version soon :slight_smile: I was hoping to have some test Docker images last week, but I got blocked by some issues. I think I have found a solution, so hopefully I can share test-images later this week :slight_smile:

Locally I already have a version of the ChirpStack Gateway OS running with a test-version of ChirpStack v4. I’m planning to share this test-version for the different Raspberry Pi versions soon.

There are still some features missing (connecting GW through GCP IoT Core / Azure IoT Hub, passive-roaming and some global integrations), but I’m planning to release the first test release(s) without these features so that people can already test v4 while I’m working on adding the remaining features :slight_smile:

As well I have most of the migration script ready to migrate the PostgreSQL and Redis data from v3 to v4 (which will also support multiple NS instances in case of multi-region setups).

2 Likes

I’m happy to share with you the first test build of ChirpStack v4. Currently this is Docker only (amd64).

git clone https://github.com/brocaar/chirpstack-docker.git
cd chirpstack-docker
git checkout v4
docker-compose up

It should take a couple of seconds to setup all the regions. After that, you can connect to http://localhost:8080.

See GitHub - brocaar/chirpstack-docker at v4 for some documentation.

Note: there are still some rough edges, but I’m looking forward to your feedback :slight_smile:

Default login is admin / admin:

9 Likes

Love the new UI. Clean and simplified. I’m excited to see what comes next. Also like that you’ve shifted some regional specific config options out of the device profile and into a toml file. That’s one source of error eliminated straight away. Would it be useful for admins to be able to see these settings from the GUI?

I know this is perhaps early to be asking this, but I’m curious about one feature you mentioned in your first post:

How do you plan to implement this? I see that so far, the deveui still has to remain unique, regardless of changing application or tenancy. I would expect that some devices will reuse their deveui across regions, if supporting switching. In this case would dynamic switching of device profile be the solution?

Would it be possible to get this onto UnRaid as well as it uses docker? Would be huge for many of us users.

The DevEUI is still used as unique identifier, so it can only be used once. For now, a device can work in a single region only. How this could work in the future is that you can attach multiple device-profiles to a single device to support for example both EU868 and IN865. Then based on the receiving gateway, it will either load one or the other device-profile.

If there is a lot of interest, then this could be something to consider. But I have never had any questions about this from anybody else so far…

There was a discussion about the REST API “removal”. I understand it was a gRPC bridge, though I am voting for having this service still available (at least optionally). We have some automation Python service built on top of ChirpStack’s API. Thanks!

The REST to gRPC bridge will not be included inside the binary. My plan is to provide this as an external component which you can run next to ChirpStack. Effectively this provide the same :slight_smile:

1 Like

This is perfect then! :slight_smile:

This is an incredible project, thanks @brocaar! Could you say something about the rationale in shifting from Golang to Rust?

1 Like

Same question, I’m interested too… BTW this is a very good news for us, it integrates better in our ecosystem…

The Concentratord component was the first ChirpStack in Rust. The main reason was that the Concentratord links against the Semtech HAL (in C). I tried to do this in Go at first, but using Rust and Bindgen (Introduction - The `bindgen` User Guide) it was a lot easier. While working on the Concentratord component I really enjoyed working with Rust.

Although the learning-curve is a bit steeper than Go, it forces you to think about the life-time and ownership of variables. Working simultaneously on Rust and Go projects, I noticed a lot of possible pitfalls in the Go code and also how Rust could have avoided some historical bugs. For example, when you pass a reference to a function in Rust, you know from its signature if it might modify the variable or not as by default, references are immutable.

Rust is very explicit if variables and references are mutable or not. Some other nice things are the error handling in Rust and enums. For example, if you have a match region { ... } the compiler will complain if not all regions are handled. Historically, I did forget this sometimes in Go :-).
An other thing that is quite nice is that when a variable goes out of scope in Rust, it is dropped (e.g. it might run some “destructor logic”). For example, when you wrap a variable within a mutex and you acquire a lock, you get a reference to that value. As soon as that reference goes out of scope, the mutex is unlocked. In Go, you are responsible for unlocking the mutex (which have caused some bugs in the past, or some complex code to make sure that in all cases a mutex would be unlocked at the right time).

When starting to work on ChirpStack v4, I started with an empty repo as I had to re-implement code from both the ChirpStack Application Server and the ChirpStack Network Server. As need to touch almost every line of code, it was the only time to re-consider Go or Rust. It is debatable which language is the right choice as some people started learning Go because of ChirpStack to make their own customizations. At the other hand, I want to provide a secure and stable LoRaWAN network-server to users and I think Rust helps (me) in achieving this. Both Go and Rust are great languages, but in the end I decided to use Rust for ChirpStack v4.

3 Likes

Greetings everyone. At this stage, I want to ask something. We are new to CS. We installed the system in the Docker environment. We added the Dragino dlos8 gateway. We are at the stage of adding nodes. (We will have our nodes in a few days.) After reading the forum, something struck me. Does installing CS with Docker structure meet all the requirements in terms of software? Is it sufficient for professional use? Otherwise, what should the installation steps be for professional use? (I’m not asking about the gateway and node parts. I’m only asking about the software parts for professional use.)

I’m not sure if this topic is the right place to discuss if Docker is suitable for production or not :slight_smile:

Here you go :slight_smile: https://github.com/chirpstack/chirpstack-rest-api

I will provide Docker images and packages as well (just pushed the initial source, still need to setup the build pipeline).

4 Likes

This is fantastic! Would this also include support for multiple channel plans within the same region? for example, could i specify that some gateways run US915 Band 1 and some run US915 Band 2? At the moment we’re running multiple NSs for this purpose.

Yes, this is supported too!

See the included config example, which enables us915_0 (channels 0-7+64) and us915_1 (8-15+65):

This maps to the following region configurations (these values must map the name in the region config, this value is user-defined, the file-names do not matter):

The common_name defines the LoRa Alliance defined region, but you can have multiple configurations per common_name, as long as the name (user-defined) is unique. You could define your own config for for example a 8 channel + 16 channel config :slight_smile:

1 Like

Sorry, but where is the CN470 band?

There are loads in there and by copying the templates, you can make this yourself.

  1. Add it to here like so (removing the **'s):
    "as923",
    "as923_2",
    "as923_3",
    "as923_4",
    "au915_0",
    "cn779",
    **"cn470",**
    "eu433",
    "eu868",
    "in865",
    "ism2400",
    "kr920",
    "ru864",
    "us915_0",
    "us915_1",
  1. Create the associated file (modifying possibly the CN779.toml one below) and save it as cn470.toml
# This file contains an example CN779 configuration.
[[regions]]

  # Name is an user-defined identifier for this region.
  name="cn779"

  # Common-name refers to the common-name of this region as defined by
  # the LoRa Alliance.
  common_name="CN779"


  # Gateway configuration.
  [regions.gateway]

    # Force gateways as private.
    #
    # If enabled, gateways can only be used by devices under the same tenant.
    force_gws_private=false


    # Gateway backend configuration.
    [regions.gateway.backend]

      # The enabled backend type.
      enabled="mqtt"

      # MQTT configuration.
      [regions.gateway.backend.mqtt]

        # Event topic template.
        event_topic="cn779/gateway/+/event/+"

        # Command topic template.
        command_topic="cn779/gateway/{{ gateway_id }}/command/{{ command }}"

        # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws)
        server="tcp://mosquitto:1883"

        # Connect with the given username (optional)
        username=""

        # Connect with the given password (optional)
        password=""

        # Quality of service level
        #
        # 0: at most once
        # 1: at least once
        # 2: exactly once
        #
        # Note: an increase of this value will decrease the performance.
        # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels
        qos=0

        # Clean session
        #
        # Set the "clean session" flag in the connect message when this client
        # connects to an MQTT broker. By setting this flag you are indicating
        # that no messages saved by the broker for this client should be delivered.
        clean_session=true

        # Client ID
        #
        # Set the client id to be used by this client when connecting to the MQTT
        # broker. A client id must be no longer than 23 characters. When left blank,
        # a random id will be generated. This requires clean_session=true.
        client_id=""

        # CA certificate file (optional)
        #
        # Use this when setting up a secure connection (when server uses ssl://...)
        # but the certificate used by the server is not trusted by any CA certificate
        # on the server (e.g. when self generated).
        ca_cert=""

        # TLS certificate file (optional)
        tls_cert=""

        # TLS key file (optional)
        tls_key=""


    # Gateway channel configuration.
    #
    # Note: this configuration is only used in case the gateway is using the
    # ChirpStack Concentratord daemon. In any other case, this configuration 
    # is ignored.
    [[regions.gateway.channels]]
      frequency=779500000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]

    [[regions.gateway.channels]]
      frequency=779700000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]

    [[regions.gateway.channels]]
      frequency=779900000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]


  # Region specific network configuration.
  [regions.network]
    
    # Installation margin (dB) used by the ADR engine.
    #
    # A higher number means that the network-server will keep more margin,
    # resulting in a lower data-rate but decreasing the chance that the
    # device gets disconnected because it is unable to reach one of the
    # surrounded gateways.
    installation_margin=10

    # RX window (Class-A).
    #
    # Set this to:
    # 0: RX1 / RX2
    # 1: RX1 only
    # 2: RX2 only
    rx_window=0

    # RX1 delay (1 - 15 seconds).
    rx1_delay=1

    # RX1 data-rate offset
    rx1_dr_offset=0

    # RX2 data-rate
    rx2_dr=0

    # RX2 frequency (Hz)
    rx2_frequency=786000000

    # Prefer RX2 on RX1 data-rate less than.
    #
    # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate
    # is smaller than the configured value, then the Network Server will
    # first try to schedule the downlink for RX2, failing that (e.g. the gateway
    # has already a payload scheduled at the RX2 timing) it will try RX1.
    rx2_prefer_on_rx1_dr_lt=0

    # Prefer RX2 on link budget.
    #
    # When the link-budget is better for RX2 than for RX1, the Network Server will first
    # try to schedule the downlink in RX2, failing that it will try RX1.
    rx2_prefer_on_link_budget=false

    # Downlink TX Power (dBm)
    #
    # When set to -1, the downlink TX Power from the configured band will
    # be used.
    #
    # Please consult the LoRaWAN Regional Parameters and local regulations
    # for valid and legal options. Note that the configured TX Power must be
    # supported by your gateway(s).
    downlink_tx_power=-1

    # ADR is disabled.
    adr_disabled=false

    # Minimum data-rate.
    min_dr=0

    # Maximum data-rate.
    max_dr=5


    # Rejoin-request configuration (LoRaWAN 1.1)
    [regions.network.rejoin_request]

      # Request devices to periodically send rejoin-requests.
      enabled=false

      # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4)
      # uplink messages. Valid values are 0 to 15.
      max_count_n=0

      # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10)
      # seconds. Valid values are 0 to 15.
      #
      # 0  = roughly 17 minutes
      # 15 = about 1 year
      max_time_n=0
    

    # Class-B configuration.
    [regions.network.class_b]

      # Ping-slot data-rate. 
      ping_slot_dr=0

      # Ping-slot frequency (Hz)
      #
      # set this to 0 to use the default frequency plan for the configured region
      # (which could be frequency hopping).
      ping_slot_frequency=0
1 Like

This is fantastic and a huge time/resource saver. Many thanks!
The example shows different event topics for the MQTT integration, i assume there would be a similar configuration if using Azure IoT Hub?