feat: add basic app that consumes mqtt uplinks from chirpstack

This commit is contained in:
Alexis Fourmaux 2026-05-07 00:13:17 +02:00
parent 87ffc9cad7
commit 31a1c14831
10 changed files with 570 additions and 0 deletions

6
app/Dockerfile Normal file
View file

@ -0,0 +1,6 @@
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY main.py .
CMD ["python", "main.py"]

30
app/main.py Normal file
View file

@ -0,0 +1,30 @@
import paho.mqtt.client as mqtt
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
stream=sys.stdout, # Docker lit stdout par défaut
force=True
)
log = logging.getLogger(__name__)
def on_connect(client, userdata, flags, reason_code, properties):
log.info(f"Connected with result code {reason_code}")
client.subscribe("application/+/device/+/event/up")
def on_message(client, userdata, msg):
log.info(msg.topic+" "+str(msg.payload))
if __name__ == "__main__":
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("mosquitto", 1883, 60)
mqttc.loop_forever()

1
app/requirements.txt Normal file
View file

@ -0,0 +1 @@
paho-mqtt==v2.1.0

View file

@ -0,0 +1,45 @@
[integration.mqtt.auth.generic]
servers=["tcp://mosquitto:1883"]
username=""
password=""
[integration.mqtt]
event_topic_template="eu868/gateway/{{ .GatewayID }}/event/{{ .EventType }}"
state_topic_template="eu868/gateway/{{ .GatewayID }}/state/{{ .StateType }}"
command_topic_template="eu868/gateway/{{ .GatewayID }}/command/#"
[backend]
type="basic_station"
[backend.basic_station]
bind=":3001"
tls_cert=""
tls_key=""
ca_cert=""
region="EU868"
frequency_min=863000000
frequency_max=870000000
[[backend.basic_station.concentrators]]
[backend.basic_station.concentrators.multi_sf]
frequencies=[
868100000,
868300000,
868500000,
867100000,
867300000,
867500000,
867700000,
867900000,
]
[backend.basic_station.concentrators.lora_std]
frequency=868300000
bandwidth=250000
spreading_factor=7
[backend.basic_station.concentrators.fsk]
frequency=868800000

View file

@ -0,0 +1,7 @@
# See https://www.chirpstack.io/gateway-bridge/install/config/ for a full
# configuration example and documentation.
[integration.mqtt.auth.generic]
servers=["tcp://mosquitto:1883"]
username=""
password=""

View file

@ -0,0 +1,105 @@
# Logging.
[logging]
# Log level.
#
# Options are: trace, debug, info, warn error.
level="info"
# PostgreSQL configuration.
[postgresql]
# PostgreSQL DSN.
#
# Format example: postgres://<USERNAME>:<PASSWORD>@<HOSTNAME>/<DATABASE>?sslmode=<SSLMODE>.
#
# SSL mode options:
# * disable - Do not use TLS
# * prefer - Attempt to connect with TLS but allow sessions without
# * require - Require the use of TLS
dsn="postgres://chirpstack:chirpstack@$POSTGRESQL_HOST/chirpstack?sslmode=disable"
# Max open connections.
#
# This sets the max. number of open connections that are allowed in the
# PostgreSQL connection pool.
max_open_connections=10
# Min idle connections.
#
# This sets the min. number of idle connections in the PostgreSQL connection
# pool (0 = equal to max_open_connections).
min_idle_connections=0
# Redis configuration.
[redis]
# Server address or addresses.
#
# Set multiple addresses when connecting to a cluster.
servers=[
"redis://$REDIS_HOST/",
]
# TLS enabled.
tls_enabled=false
# Redis Cluster.
#
# Set this to true when the provided URLs are pointing to a Redis Cluster
# instance.
cluster=false
# Network related configuration.
[network]
# Network identifier (NetID, 3 bytes) encoded as HEX (e.g. 010203).
net_id="000000"
# Enabled regions.
#
# Multiple regions can be enabled simultaneously. Each region must match
# the 'name' parameter of the region configuration in '[[regions]]'.
enabled_regions=[
"as923",
"as923_2",
"as923_3",
"as923_4",
"au915_0",
"cn470_10",
"cn779",
"eu433",
"eu868",
"in865",
"ism2400",
"kr920",
"ru864",
"us915_0",
"us915_1",
]
# API interface configuration.
[api]
# interface:port to bind the API interface to.
bind="0.0.0.0:8080"
# Secret.
#
# This secret is used for generating login and API tokens, make sure this
# is never exposed. Changing this secret will invalidate all login and API
# tokens. The following command can be used to generate a random secret:
# openssl rand -base64 32
secret="you-must-replace-this"
[integration]
enabled=["mqtt"]
[integration.mqtt]
server="tcp://$MQTT_BROKER_HOST:1883/"
json=true

View file

@ -0,0 +1,280 @@
# This file contains an example EU868 configuration.
[[regions]]
# ID is an user-defined identifier for this region.
id="eu868"
# Description is a short description for this region.
description="EU868"
# Common-name refers to the common-name of this region as defined by
# the LoRa Alliance.
common_name="EU868"
# 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]
# Topic prefix.
#
# The topic prefix can be used to define the region of the gateway.
# Note, there is no need to add a trailing '/' to the prefix. The trailing
# '/' is automatically added to the prefix if it is configured.
topic_prefix="eu868"
# MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws)
server="tcp://$MQTT_BROKER_HOST: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=false
# 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. If left blank,
# a random id will be generated by ChirpStack.
client_id=""
# Keep alive interval.
#
# This defines the maximum time that that should pass without communication
# between the client and server.
keep_alive_interval="30s"
# 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=868100000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=868300000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=868500000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=867100000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=867300000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=867500000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=867700000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=867900000
bandwidth=125000
modulation="LORA"
spreading_factors=[7, 8, 9, 10, 11, 12]
[[regions.gateway.channels]]
frequency=868300000
bandwidth=250000
modulation="LORA"
spreading_factors=[7]
[[regions.gateway.channels]]
frequency=868800000
bandwidth=125000
modulation="FSK"
datarate=50000
# 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=869525000
# 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=3
# 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
# Below is the common set of extra channels. Please make sure that these
# channels are also supported by the gateways.
[[regions.network.extra_channels]]
frequency=867100000
min_dr=0
max_dr=5
[[regions.network.extra_channels]]
frequency=867300000
min_dr=0
max_dr=5
[[regions.network.extra_channels]]
frequency=867500000
min_dr=0
max_dr=5
[[regions.network.extra_channels]]
frequency=867700000
min_dr=0
max_dr=5
[[regions.network.extra_channels]]
frequency=867900000
min_dr=0
max_dr=5

View file

@ -0,0 +1,2 @@
listener 1883
allow_anonymous true

View file

@ -0,0 +1,7 @@
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname="$POSTGRES_DB" <<-EOSQL
create extension pg_trgm;
create extension hstore;
EOSQL

87
docker-compose.yml Normal file
View file

@ -0,0 +1,87 @@
services:
chirpstack:
image: chirpstack/chirpstack:4
command: -c /etc/chirpstack
restart: unless-stopped
volumes:
- ./configuration/chirpstack:/etc/chirpstack
depends_on:
- postgres
- mosquitto
- redis
environment:
- MQTT_BROKER_HOST=mosquitto
- REDIS_HOST=redis
- POSTGRESQL_HOST=postgres
ports:
- "8080:8080"
chirpstack-gateway-bridge:
image: chirpstack/chirpstack-gateway-bridge:4
restart: unless-stopped
ports:
- "1700:1700/udp"
volumes:
- ./configuration/chirpstack-gateway-bridge:/etc/chirpstack-gateway-bridge
environment:
- INTEGRATION__MQTT__EVENT_TOPIC_TEMPLATE=eu868/gateway/{{ .GatewayID }}/event/{{ .EventType }}
- INTEGRATION__MQTT__STATE_TOPIC_TEMPLATE=eu868/gateway/{{ .GatewayID }}/state/{{ .StateType }}
- INTEGRATION__MQTT__COMMAND_TOPIC_TEMPLATE=eu868/gateway/{{ .GatewayID }}/command/#
depends_on:
- mosquitto
chirpstack-gateway-bridge-basicstation:
image: chirpstack/chirpstack-gateway-bridge:4
restart: unless-stopped
command: -c /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge-basicstation-eu868.toml
ports:
- "3001:3001"
volumes:
- ./configuration/chirpstack-gateway-bridge:/etc/chirpstack-gateway-bridge
depends_on:
- mosquitto
chirpstack-rest-api:
image: chirpstack/chirpstack-rest-api:4
restart: unless-stopped
command: --server chirpstack:8080 --bind 0.0.0.0:8090 --insecure
ports:
- "8090:8090"
depends_on:
- chirpstack
postgres:
image: postgres:14-alpine
restart: unless-stopped
volumes:
- ./configuration/postgresql/initdb:/docker-entrypoint-initdb.d
- postgresqldata:/var/lib/postgresql/data
environment:
- POSTGRES_USER=chirpstack
- POSTGRES_PASSWORD=chirpstack
- POSTGRES_DB=chirpstack
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save 300 1 --save 60 100 --appendonly no
volumes:
- redisdata:/data
mosquitto:
image: eclipse-mosquitto:2
restart: unless-stopped
ports:
- "1883:1883"
volumes:
- ./configuration/mosquitto/config/:/mosquitto/config/
application:
build: ./app
restart: unless-stopped
depends_on:
- mosquitto
volumes:
postgresqldata:
redisdata: