Tellurian.Trains.Adapters.Z21
1.8.0
Prefix Reserved
dotnet add package Tellurian.Trains.Adapters.Z21 --version 1.8.0
NuGet\Install-Package Tellurian.Trains.Adapters.Z21 -Version 1.8.0
<PackageReference Include="Tellurian.Trains.Adapters.Z21" Version="1.8.0" />
<PackageVersion Include="Tellurian.Trains.Adapters.Z21" Version="1.8.0" />
<PackageReference Include="Tellurian.Trains.Adapters.Z21" />
paket add Tellurian.Trains.Adapters.Z21 --version 1.8.0
#r "nuget: Tellurian.Trains.Adapters.Z21, 1.8.0"
#:package Tellurian.Trains.Adapters.Z21@1.8.0
#addin nuget:?package=Tellurian.Trains.Adapters.Z21&version=1.8.0
#tool nuget:?package=Tellurian.Trains.Adapters.Z21&version=1.8.0
Tellurian.Trains.Adapters.Z21
A .NET adapter for the Roco/Fleischmann Z21 digital command station, providing a high-level API for locomotive control, accessory switching, and system monitoring over UDP.
Features
- Locomotive Control: Drive, emergency stop, function control, and state query via
ILoco - Protocol Support: Encapsulates both XpressNet and LocoNet protocols
- Real-time Notifications: Observer-based notification system for state changes
- System Monitoring: Track voltage, current, temperature, and status
- Detector Support: Occupancy, transponder, and RailCom notifications via Z21 LocoNet detector API (LAN_LOCONET_DETECTOR 0xA4) and CAN detector API (LAN_CAN_DETECTOR 0xC4) for Z21 10808 modules
- Broadcast Filtering: Configurable subscription to specific event types
- Accessory Gateway Mode: Accessory commands (
IAccessory,ITurnout) are sent as wrapped LocoNet viaLAN_LOCONET_FROM_LANby default so that state-change feedback flows through the Z21's LocoNet forwarding (subscribeBroadcastSubjects.LocoNetTurnouts) and is decoded into protocol-agnosticAccessoryNotification. Opt out to plain XpressNet for DCC-only decoders by passinguseLocoNetForAccessories: falseto the constructor.
Quick Start
Dependency Injection Setup
Add the Z21 adapter to your application in Program.cs:
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Tellurian.Trains.Adapters.Z21;
using Tellurian.Trains.Communications.Channels;
using Tellurian.Trains.Communications.Interfaces.Locos;
var services = new ServiceCollection();
// Add logging
services.AddLogging(builder => builder.AddConsole());
// Configure Z21 endpoint (default: 192.168.0.111:21105)
var z21Endpoint = new IPEndPoint(IPAddress.Parse("192.168.0.111"), 21105);
// Register the UDP communication channel
services.AddSingleton<ICommunicationsChannel>(sp =>
new UdpDataChannel(
localPort: 21105,
remoteEndPoint: z21Endpoint,
logger: sp.GetRequiredService<ILogger<UdpDataChannel>>()));
// Register the Z21 adapter with an initial broadcast subscription.
// Pick only the subjects your application actually needs — the Z21 spec
// warns that broad subscriptions (e.g. all locos, all switches) can generate
// considerable network traffic, especially over Wi-Fi.
services.AddSingleton(sp => new Adapter(
sp.GetRequiredService<ICommunicationsChannel>(),
sp.GetRequiredService<ILogger<Adapter>>(),
BroadcastSubjects.RunningAndSwitching | BroadcastSubjects.SystemStateChanges));
// Register ILoco interface (the Adapter implements it)
services.AddSingleton<ILoco>(sp => sp.GetRequiredService<Adapter>());
var provider = services.BuildServiceProvider();
Basic Usage
using Tellurian.Trains.Adapters.Z21;
using Tellurian.Trains.Communications.Interfaces.Locos;
// Get the adapter from DI
var adapter = provider.GetRequiredService<Adapter>();
// Subscribe to notifications
adapter.Subscribe(new MyNotificationObserver());
// Start receiving notifications. The adapter automatically applies the
// BroadcastSubjects passed to its constructor to the Z21 — and will re-apply
// them on every (re)connect, because the Z21 forgets broadcast flags when
// the client reconnects.
await adapter.StartReceiveAsync();
// Change the subscription set at runtime if needed.
// The Z21 protocol only supports "set absolute" — the adapter tracks the
// current set so you can add/remove individual subjects conveniently.
await adapter.AddSubscriptionsAsync(BroadcastSubjects.LocoNetTurnouts);
await adapter.RemoveSubscriptionsAsync(BroadcastSubjects.SystemStateChanges);
// Or replace the whole set in one call:
await adapter.SubscribeAsync(BroadcastSubjects.LocoNetTurnouts);
// Control a locomotive
var locoAddress = new LocoAddress(3); // DCC address 3
var drive = new LocoDrive(new LocoSpeed(50, SpeedSteps.S128), LocoDirection.Forward);
await adapter.DriveAsync(locoAddress, drive);
// Toggle function (e.g., lights)
await adapter.SetFunctionAsync(locoAddress, new LocoFunction(0, true));
// Emergency stop
await adapter.EmergencyStopAsync(locoAddress);
Notification Observer
using Tellurian.Trains.Communications.Interfaces;
public class MyNotificationObserver : IObserver<Notification>
{
public void OnNext(Notification notification)
{
Console.WriteLine($"Received: {notification}");
}
public void OnError(Exception error)
{
Console.WriteLine($"Error: {error.Message}");
}
public void OnCompleted()
{
Console.WriteLine("Connection closed");
}
}
Z21 Hardware Connection
| Setting | Default Value |
|---|---|
| IP Address | 192.168.0.111 |
| UDP Port | 21105 |
| Protocol | UDP (bidirectional) |
Ensure your computer is on the same network as the Z21 command station.
Accessory Addressing
This library uses 1-based user addresses (1-2048) for accessories/turnouts, which matches how users typically configure their systems. The protocol layer automatically converts to 0-based wire addresses.
Z21 Address Shift (+4)
There is a historical ambiguity in DCC where Roco/Z21 numbers accessory modules starting from 0, while other manufacturers start from 1. This creates a 4-address shift between systems.
The Z21 has a hardware setting "DCC-Weichenadressverschiebung +4" to compensate. If your turnouts are off by 4 addresses compared to another system, enable this option in Z21 Maintenance.
Documentation
For detailed API documentation, architecture overview, and advanced usage patterns, see DOCUMENTATION.md.
Dependencies
Microsoft.Extensions.Logging.Abstractions- Logging infrastructureTellurian.Trains.Communications.Channels- UDP transport layerTellurian.Trains.Communications.Interfaces- Protocol-agnostic interfacesTellurian.Trains.Protocols.XpressNet- XpressNet protocol implementationTellurian.Trains.Protocols.LocoNet- LocoNet protocol implementation
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Tellurian.Trains.Communications.Channels (>= 1.8.0)
- Tellurian.Trains.Communications.Interfaces (>= 1.8.0)
- Tellurian.Trains.Protocols.LocoNet (>= 1.8.0)
- Tellurian.Trains.Protocols.XpressNet (>= 1.8.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.8.0 | 158 | 4/17/2026 |
| 1.7.15 | 137 | 4/16/2026 |
| 1.7.14 | 129 | 4/16/2026 |
| 1.7.13 | 127 | 4/16/2026 |
| 1.7.12 | 131 | 4/16/2026 |
| 1.7.11 | 121 | 4/15/2026 |
| 1.7.10 | 122 | 4/15/2026 |
| 1.7.9 | 127 | 4/15/2026 |
| 1.7.8 | 126 | 4/15/2026 |
| 1.7.7 | 126 | 4/15/2026 |
| 1.7.6 | 125 | 4/15/2026 |
| 1.7.5 | 126 | 4/15/2026 |
| 1.7.4 | 121 | 4/15/2026 |
| 1.7.3 | 244 | 3/28/2026 |
| 1.7.2 | 126 | 3/28/2026 |
| 1.7.1 | 140 | 3/27/2026 |
| 1.7.0 | 148 | 3/16/2026 |
| 1.6.1 | 145 | 2/22/2026 |
| 1.6.0 | 129 | 2/19/2026 |
| 1.5.0 | 125 | 2/11/2026 |