Skip to content

Commit e37896f

Browse files
asanchezdelcclaude
andcommitted
Migrate ChirpStack Simulator to v3 API with JSON gateway support
Major changes: - Migrate from ChirpStack v4 API to v3 API (github.com/brocaar/chirpstack-api/go/v3) - Change gateway uplink format from protobuf to JSON (v3 gateway-bridge format) - Add JSON downlink parsing with protobuf fallback - Fix UUID generation for uplink IDs (proper 16-byte UUIDs) - Add multi-region examples (US915, EU868, AU915, AS923) with FSB2 configuration - Add MQTT configuration via command-line flags - Update Organization API (replaced Tenant from v4) - Add detailed logging for uplink/downlink debugging - Fix bandwidth conversion (Hz to kHz for JSON format) Examples: - New single_uplink examples for all major LoRaWAN regions - Comprehensive README with configuration and troubleshooting - MQTT credentials configurable via flags 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 172a3a0 commit e37896f

File tree

14 files changed

+776
-154
lines changed

14 files changed

+776
-154
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@
77

88
# builds
99
/build/
10+
11+
CLAUDE.md

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
# ChirpStack Simulator
1+
# ChirpStack Simulator v3
22

3-
ChirpStack Simulator is an open-source simulator for the [ChirpStack](https://www.chirpstack.io)
4-
open-source LoRaWAN<sup>&reg;</sup> Network-Server (v4). It simulates
5-
a configurable number of devices and gateways, which will be automatically
6-
created on starting the simulation.
7-
8-
This project has been developed together with [TWTG](https://www.twtg.io/).
3+
This is a forked project from the original [Chirpstack Simulator](https://github.com/brocaar/chirpstack-simulator), but for V3 network server.
94

105
## Building
116

@@ -28,6 +23,13 @@ For generating a configuration template, use the following command:
2823

2924
### Example
3025

26+
Run a single uplink
27+
28+
```shell
29+
./examples/single_uplink/single_uplink -region as923 -mqtt-server localhost:1883
30+
```
31+
32+
3133
```toml
3234
[general]
3335
# Log level

examples/single_uplink/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Single Uplink Examples - Multi-Region Support
2+
3+
This directory contains examples for simulating a single OTAA device uplink for different LoRaWAN regions.
4+
5+
## Supported Regions
6+
7+
- **US915** - FSB2 (Frequency Sub-Band 2)
8+
- **EU868** - European band
9+
- **AU915** - FSB2 (Frequency Sub-Band 2)
10+
- **AS923** - Asia-Pacific band (FSB2-style channels)
11+
12+
## Building
13+
14+
```bash
15+
cd examples/single_uplink
16+
go build -o single_uplink
17+
```
18+
19+
## Usage
20+
21+
Run the example with a specific region:
22+
23+
```bash
24+
# US915 FSB2
25+
./single_uplink -region us915
26+
27+
# EU868
28+
./single_uplink -region eu868
29+
30+
# AU915 FSB2
31+
./single_uplink -region au915
32+
33+
# AS923
34+
./single_uplink -region as923
35+
```
36+
37+
## Configuration
38+
39+
Before running, update the MQTT broker credentials in each region file:
40+
- `us915_fsb2.go`
41+
- `eu868.go`
42+
- `au915_fsb2.go`
43+
- `as923_fsb2.go`
44+
45+
Change this line in each file:
46+
```go
47+
simulator.WithMQTTCredentials("localhost:1883", "gateway_user", "gateway_pass"),
48+
```
49+
50+
To your ChirpStack MQTT broker details.
51+
52+
## Region-Specific Details
53+
54+
### US915 FSB2
55+
- **Frequency Sub-Band 2**: Channels 8-15
56+
- **Frequencies**: 903.9, 904.1, 904.3, 904.5, 904.7, 904.9, 905.1, 905.3 MHz
57+
- **Bandwidth**: 125 kHz
58+
- **Data Rate**: DR0 (SF10, most robust)
59+
- **Example Frequency**: 903.9 MHz
60+
61+
### EU868
62+
- **Default Channels**: 8 channels (0-7)
63+
- **Frequencies**: 868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9 MHz
64+
- **Bandwidth**: 125 kHz
65+
- **Data Rate**: DR5 (SF7, fastest)
66+
- **Example Frequency**: 868.1 MHz
67+
68+
### AU915 FSB2
69+
- **Frequency Sub-Band 2**: Channels 8-15
70+
- **Frequencies**: 916.8, 917.0, 917.2, 917.4, 917.6, 917.8, 918.0, 918.2 MHz
71+
- **Bandwidth**: 125 kHz
72+
- **Data Rate**: DR0 (SF10, most robust)
73+
- **Example Frequency**: 916.8 MHz
74+
75+
### AS923
76+
- **Channels**: Multiple 200 kHz channels
77+
- **Frequencies**: 923.2, 923.4, 923.6, 923.8, 924.0, 924.2, 924.4, 924.6 MHz
78+
- **Bandwidth**: 125 kHz
79+
- **Data Rate**: DR5 (SF7, fastest)
80+
- **Example Frequency**: 923.4 MHz
81+
82+
## Data Rates Reference
83+
84+
### US915/AU915
85+
| DR | SF | BW | Bit Rate |
86+
|----|----|----|----------|
87+
| DR0 | SF10 | 125 kHz | 980 bps |
88+
| DR1 | SF9 | 125 kHz | 1760 bps |
89+
| DR2 | SF8 | 125 kHz | 3125 bps |
90+
| DR3 | SF7 | 125 kHz | 5470 bps |
91+
| DR4 | SF8 | 500 kHz | 12500 bps |
92+
93+
### EU868/AS923
94+
| DR | SF | BW | Bit Rate |
95+
|----|----|----|----------|
96+
| DR0 | SF12 | 125 kHz | 250 bps |
97+
| DR1 | SF11 | 125 kHz | 440 bps |
98+
| DR2 | SF10 | 125 kHz | 980 bps |
99+
| DR3 | SF9 | 125 kHz | 1760 bps |
100+
| DR4 | SF8 | 125 kHz | 3125 bps |
101+
| DR5 | SF7 | 125 kHz | 5470 bps |
102+
| DR6 | SF7 | 250 kHz | 11000 bps |
103+
104+
## Troubleshooting
105+
106+
### "data-rate not found" Error
107+
108+
If you see an error like:
109+
```
110+
error="get data-rate index error: lorawan/band: data-rate not found"
111+
```
112+
113+
This means the combination of frequency, bandwidth, and spreading factor doesn't match a valid data rate for your region. Check:
114+
115+
1. **Frequency** is in the valid range for your region
116+
2. **Bandwidth** is correct (usually 125 kHz)
117+
3. **Spreading Factor** is valid for the region (SF7-SF12 for EU868/AS923, SF7-SF10 for US915/AU915)
118+
4. **Code Rate** is set to "4/5" (most common)
119+
120+
### Device Not Joining
121+
122+
Ensure:
123+
1. The device is created in ChirpStack Application Server
124+
2. The DevEUI and AppKey match what's configured in ChirpStack
125+
3. The gateway is connected and online
126+
4. The MQTT broker credentials are correct
127+
5. The device profile supports OTAA
128+
129+
## Example Output
130+
131+
Successful run:
132+
```
133+
INFO[0000] [US915-FSB2] Using GatewayID: 000080029c70bb4b
134+
INFO[0000] [US915-FSB2] Using DevEUI: 0201010101010101
135+
INFO[0000] [US915-FSB2] Using AppKey: 03010101010101010101010101010101
136+
INFO[0000] [US915-FSB2] Using frequency: 903900000 Hz (903.9 MHz)
137+
INFO[0000] simulator: connecting to mqtt broker server="localhost:1883"
138+
INFO[0000] simulator: subscribing to gateway mqtt topic gateway_id=000080029c70bb4b topic="us915_2/gateway/000080029c70bb4b/command/down"
139+
INFO[0000] simulator: new otaa device dev_eui=0201010101010101
140+
INFO[0000] [US915-FSB2] Waiting for device simulation to complete...
141+
INFO[0001] simulator: device OTAA activated dev_addr=01a2b3c4 dev_eui=0201010101010101
142+
INFO[0001] [US915-FSB2] Downlink received ack=true data="" f_port=0 fcnt_down=0 region=US915-FSB2
143+
INFO[0001] [US915-FSB2] Simulation completed!
144+
INFO[0001] Example completed successfully!
145+
```
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"sync"
7+
"time"
8+
9+
"github.com/brocaar/chirpstack-api/go/v3/gw"
10+
"github.com/brocaar/chirpstack-simulator/simulator"
11+
"github.com/brocaar/lorawan"
12+
log "github.com/sirupsen/logrus"
13+
)
14+
15+
// AS923 FSB2 Example
16+
// AS923 doesn't have traditional FSB concept like US/AU915, but we can use
17+
// a subset of channels similar to FSB2 approach
18+
// Using channels: 923.4, 923.6, 923.8, 924.0, 924.2, 924.4, 924.6, 924.8 MHz
19+
func AS923_FSB2_Example(mqttConfig MQTTConfig) {
20+
gatewayID := lorawan.EUI64{0, 0, 128, 2, 156, 112, 187, 75}
21+
log.Infof("[AS923-FSB2] Using GatewayID: %s", gatewayID.String())
22+
23+
devEUI := lorawan.EUI64{4, 4, 4, 4, 4, 4, 4, 4}
24+
log.Infof("[AS923-FSB2] Using DevEUI: %s", devEUI.String())
25+
26+
joinEUI := lorawan.EUI64{0, 0, 0, 0, 0, 0, 0, 0}
27+
log.Infof("[AS923-FSB2] Using JoinEUI: %s", joinEUI.String())
28+
29+
appKey := lorawan.AES128Key{3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}
30+
log.Infof("[AS923-FSB2] Using AppKey: %s", appKey.String())
31+
32+
// AS923 - Using 923.2 MHz (first default channel in AS923 band)
33+
// Changed from 923.4 to 923.2 MHz as it's the standard first channel
34+
var frequency uint32 = 923200000 // 923.2 MHz
35+
log.Infof("[AS923-FSB2] Using frequency: %d Hz (%.1f MHz)", frequency, float64(frequency)/1e6)
36+
37+
var wg sync.WaitGroup
38+
ctx := context.Background()
39+
40+
// Create gateway with proper AS923 region topic template
41+
sgw, err := simulator.NewGateway(
42+
simulator.WithMQTTCredentials(mqttConfig.Server, mqttConfig.Username, mqttConfig.Password),
43+
simulator.WithGatewayID(gatewayID),
44+
simulator.WithEventTopicTemplate("as923/gateway/{{ .GatewayID }}/event/{{ .Event }}"),
45+
simulator.WithCommandTopicTemplate("as923/gateway/{{ .GatewayID }}/command/{{ .Command }}"),
46+
)
47+
if err != nil {
48+
panic(err)
49+
}
50+
51+
// Device configuration with proper AS923 parameters
52+
_, err = simulator.NewDevice(ctx, &wg,
53+
simulator.WithDevEUI(devEUI),
54+
simulator.WithJoinEUI(joinEUI),
55+
simulator.WithAppKey(appKey),
56+
simulator.WithRandomDevNonce(),
57+
simulator.WithUplinkInterval(5*time.Second), // Increased to 5 seconds to avoid rapid retries
58+
simulator.WithUplinkCount(1), // Send only 1 uplink
59+
simulator.WithUplinkPayload(true, 10, []byte{1, 2, 3}),
60+
simulator.WithUplinkTXInfo(gw.UplinkTXInfo{
61+
Frequency: frequency,
62+
ModulationInfo: &gw.UplinkTXInfo_LoraModulationInfo{
63+
LoraModulationInfo: &gw.LoRaModulationInfo{
64+
Bandwidth: 125000, // 125 kHz - standard for AS923
65+
SpreadingFactor: 10, // SF10 - DR2 (more robust than SF7, better for testing)
66+
CodeRate: "4/5", // Standard code rate
67+
},
68+
},
69+
}),
70+
simulator.WithGateways([]*simulator.Gateway{sgw}),
71+
simulator.WithDownlinkHandlerFunc(func(conf, ack bool, fCntDown uint32, fPort uint8, data []byte) error {
72+
log.WithFields(log.Fields{
73+
"region": "AS923",
74+
"ack": ack,
75+
"fcnt_down": fCntDown,
76+
"f_port": fPort,
77+
"data": hex.EncodeToString(data),
78+
"confirmed": conf,
79+
}).Info("Downlink received")
80+
return nil
81+
}),
82+
)
83+
if err != nil {
84+
panic(err)
85+
}
86+
87+
log.Info("[AS923-FSB2] Waiting for device simulation to complete...")
88+
wg.Wait()
89+
log.Info("[AS923-FSB2] Simulation completed!")
90+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"sync"
7+
"time"
8+
9+
"github.com/brocaar/chirpstack-api/go/v3/gw"
10+
"github.com/brocaar/chirpstack-simulator/simulator"
11+
"github.com/brocaar/lorawan"
12+
log "github.com/sirupsen/logrus"
13+
)
14+
15+
// AU915 FSB2 Example
16+
// FSB2 uses channels 8-15 (916.8 MHz - 918.2 MHz)
17+
// Uplink frequencies: 916.8, 917.0, 917.2, 917.4, 917.6, 917.8, 918.0, 918.2 MHz
18+
func AU915_FSB2_Example(mqttConfig MQTTConfig) {
19+
gatewayID := lorawan.EUI64{0, 0, 128, 2, 156, 112, 187, 75}
20+
log.Infof("[AU915-FSB2] Using GatewayID: %s", gatewayID.String())
21+
22+
devEUI := lorawan.EUI64{3, 3, 3, 3, 3, 3, 3, 3}
23+
log.Infof("[AU915-FSB2] Using DevEUI: %s", devEUI.String())
24+
25+
appKey := lorawan.AES128Key{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}
26+
log.Infof("[AU915-FSB2] Using AppKey: %s", appKey.String())
27+
28+
// AU915 FSB2 - Channel 8 (first channel in FSB2)
29+
var frequency uint32 = 916800000 // 916.8 MHz
30+
log.Infof("[AU915-FSB2] Using frequency: %d Hz (%.1f MHz)", frequency, float64(frequency)/1e6)
31+
32+
var wg sync.WaitGroup
33+
ctx := context.Background()
34+
35+
sgw, err := simulator.NewGateway(
36+
simulator.WithMQTTCredentials(mqttConfig.Server, mqttConfig.Username, mqttConfig.Password),
37+
simulator.WithGatewayID(gatewayID),
38+
simulator.WithEventTopicTemplate("au915_2/gateway/{{ .GatewayID }}/event/{{ .Event }}"),
39+
simulator.WithCommandTopicTemplate("au915_2/gateway/{{ .GatewayID }}/command/{{ .Command }}"),
40+
)
41+
if err != nil {
42+
panic(err)
43+
}
44+
45+
_, err = simulator.NewDevice(ctx, &wg,
46+
simulator.WithDevEUI(devEUI),
47+
simulator.WithAppKey(appKey),
48+
simulator.WithRandomDevNonce(),
49+
simulator.WithUplinkInterval(time.Second),
50+
simulator.WithUplinkCount(1),
51+
simulator.WithUplinkPayload(true, 10, []byte{1, 2, 3}),
52+
simulator.WithUplinkTXInfo(gw.UplinkTXInfo{
53+
Frequency: frequency,
54+
ModulationInfo: &gw.UplinkTXInfo_LoraModulationInfo{
55+
LoraModulationInfo: &gw.LoRaModulationInfo{
56+
Bandwidth: 125000, // 125 kHz - standard for AU915
57+
SpreadingFactor: 10, // SF10 - DR0 for AU915 (most robust)
58+
CodeRate: "4/5", // Standard code rate
59+
},
60+
},
61+
}),
62+
simulator.WithGateways([]*simulator.Gateway{sgw}),
63+
simulator.WithDownlinkHandlerFunc(func(conf, ack bool, fCntDown uint32, fPort uint8, data []byte) error {
64+
log.WithFields(log.Fields{
65+
"region": "AU915-FSB2",
66+
"ack": ack,
67+
"fcnt_down": fCntDown,
68+
"f_port": fPort,
69+
"data": hex.EncodeToString(data),
70+
}).Info("Downlink received")
71+
return nil
72+
}),
73+
)
74+
if err != nil {
75+
panic(err)
76+
}
77+
78+
log.Info("[AU915-FSB2] Waiting for device simulation to complete...")
79+
wg.Wait()
80+
log.Info("[AU915-FSB2] Simulation completed!")
81+
}

0 commit comments

Comments
 (0)