Engine/Client Usage

The engine package provides a high-level client for interacting with SimConnect. This document covers the complete API for direct simulator communication.

See also: Configuration Options for all available options when creating a client.

Creating a Client

Using the Root Package

import "github.com/mrlm-net/simconnect"

client := simconnect.NewClient("MyApp")

Using the Engine Package Directly

import "github.com/mrlm-net/simconnect/pkg/engine"

client := engine.New("MyApp")

simconnect.NewClient returns the engine.Client interface; engine.New returns *engine.Engine, which also has the methods outside the interface (the camera API and SetSystemEventState).

Connection Lifecycle

Connect

Establishes a connection to the SimConnect server.

if err := client.Connect(); err != nil {
    log.Fatal("Failed to connect:", err)
}

Disconnect

Stops the dispatcher, closes the stream and closes the connection.

defer client.Disconnect()

A client is single-use: after Disconnect (or the simulator quitting) its context is done and its stream closed. To connect again, create a new client (as pkg/manager does).

Stream

Returns a read-only channel for receiving SimConnect messages. The first call starts the dispatcher.

stream := client.Stream()
for msg := range stream {
    // Handle incoming messages
}

The channel closes after the simulator’s quit message, when the context is cancelled, or when the connection is lost: then the last message carries an Err wrapping engine.ErrConnectionLost (the pipe closed, or 100 dispatch errors in a row).

Data Definitions

Data definitions describe the structure of data you want to receive from or send to the simulator.

AddToDataDefinition

Adds a variable to a data definition. Call multiple times to build composite definitions.

import "github.com/mrlm-net/simconnect/pkg/types"

// Define aircraft position data
const PositionDefID = 1000

client.AddToDataDefinition(
    PositionDefID,
    "PLANE LATITUDE",
    "degrees",
    types.SIMCONNECT_DATATYPE_FLOAT64,
    0,    // epsilon (change threshold)
    0,    // datum ID
)
client.AddToDataDefinition(
    PositionDefID,
    "PLANE LONGITUDE",
    "degrees",
    types.SIMCONNECT_DATATYPE_FLOAT64,
    0, 0,
)
client.AddToDataDefinition(
    PositionDefID,
    "PLANE ALTITUDE",
    "feet",
    types.SIMCONNECT_DATATYPE_FLOAT64,
    0, 0,
)

ClearDataDefinition

Removes a previously created data definition.

client.ClearDataDefinition(PositionDefID)

RegisterDataset

Registers a pre-built dataset from the datasets package.

import "github.com/mrlm-net/simconnect/pkg/datasets/aircraft"

client.RegisterDataset(PositionDefID, aircraft.NewPositionDataset())

RegisterFacilityDataset

Registers a pre-built facility dataset from the datasets/facilities package. Unlike RegisterDataset which uses AddToDataDefinition, this uses AddToFacilityDefinition internally.

import "github.com/mrlm-net/simconnect/pkg/datasets/facilities"

client.RegisterFacilityDataset(AirportDefID, facilities.NewAirportFacilityDataset())
client.RegisterFacilityDataset(RunwayDefID, facilities.NewRunwayFacilityDataset())
client.RegisterFacilityDataset(ParkingDefID, facilities.NewParkingFacilityDataset())
client.RegisterFacilityDataset(FrequencyDefID, facilities.NewFrequencyFacilityDataset())

Available facility dataset constructors in pkg/datasets/facilities:

Constructor Data
NewAirportFacilityDataset() Airport info (name, ICAO, position, tower, transition)
NewRunwayFacilityDataset() Runway dimensions, heading, surface, lighting
NewParkingFacilityDataset() Parking spots, gates, ramps
NewFrequencyFacilityDataset() COM/NAV frequencies
NewTaxiPointFacilityDataset() Taxiway intersection points
NewTaxiPathFacilityDataset() Taxiway paths and routes
NewTaxiNameFacilityDataset() Taxiway names
NewHelipadFacilityDataset() Helipad locations and properties
NewJetwayFacilityDataset() Jetway data
NewPavementFacilityDataset() Runway pavement (length, width)
NewApproachLightsFacilityDataset() Runway approach lights
NewVASIFacilityDataset() Runway VASI
NewStartFacilityDataset() Start positions
NewDepartureFacilityDataset() SID procedures
NewArrivalFacilityDataset() STAR procedures
NewRunwayTransitionFacilityDataset() SID/STAR runway transitions
NewEnrouteTransitionFacilityDataset() SID/STAR enroute transitions
NewApproachFacilityDataset() Approach procedures
NewApproachTransitionFacilityDataset() Approach transitions
NewApproachLegFacilityDataset() Procedure legs (APPROACH_LEG)
NewFinalApproachLegFacilityDataset() Final approach legs
NewMissedApproachLegFacilityDataset() Missed approach legs
NewVORFacilityDataset() VOR navaid data
NewNDBFacilityDataset() NDB navaid data
NewWaypointFacilityDataset() Waypoint data
NewRouteFacilityDataset() Airway segments of a waypoint (ROUTE)

Requesting Data

RequestDataOnSimObject

Requests data for a specific sim object with optional periodic updates.

const PositionReqID = 1001

// Request once
client.RequestDataOnSimObject(
    PositionReqID,
    PositionDefID,
    types.SIMCONNECT_OBJECT_ID_USER,     // User aircraft
    types.SIMCONNECT_PERIOD_ONCE,        // Single request
    types.SIMCONNECT_DATA_REQUEST_FLAG_DEFAULT,
    0, 0, 0,
)

// Request every second
client.RequestDataOnSimObject(
    PositionReqID,
    PositionDefID,
    types.SIMCONNECT_OBJECT_ID_USER,
    types.SIMCONNECT_PERIOD_SECOND,
    types.SIMCONNECT_DATA_REQUEST_FLAG_CHANGED,  // Only when changed
    0, 0, 0,
)

RequestDataOnSimObjectType

Requests data for all objects of a specific type within a radius.

// Get all aircraft within 50 nautical miles
client.RequestDataOnSimObjectType(
    TrafficReqID,
    TrafficDefID,
    50 * 1852,  // Radius in meters (50nm)
    types.SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT,
)

SetDataOnSimObject

Sends data to the simulator to modify object state.

type AircraftPosition struct {
    Latitude  float64
    Longitude float64
    Altitude  float64
}

pos := AircraftPosition{
    Latitude:  47.4647,
    Longitude: -122.3078,
    Altitude:  10000,
}

client.SetDataOnSimObject(
    PositionDefID,
    types.SIMCONNECT_OBJECT_ID_USER,
    types.SIMCONNECT_DATA_SET_FLAG_DEFAULT,
    0,
    uint32(unsafe.Sizeof(pos)),
    unsafe.Pointer(&pos),
)

Processing Messages

Message Structure

The Stream() channel returns Message structs containing the raw SimConnect data.

type Message struct {
    *types.SIMCONNECT_RECV  // DwSize, DwVersion, DwID (types.DWORD)
    Size uint32             // Size of the message
    Err  error              // Set on a dispatch error
    // unexported: the pooled buffer
}

On a dispatch error the stream sends a Message with Err set and a nil SIMCONNECT_RECV. Check msg.Err != nil before reading msg.DwID. Release() returns the buffer to the pool.

Casting Message Data

Use CastDataAs to convert raw data to typed structs:

type AircraftPosition struct {
    Latitude  float64
    Longitude float64
    Altitude  float64
}

for msg := range client.Stream() {
    switch types.SIMCONNECT_RECV_ID(msg.DwID) {
    case types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
        data := msg.AsSimObjectData()
        if data.DwRequestID == PositionReqID {
            pos := engine.CastDataAs[AircraftPosition](&data.DwData)
            fmt.Printf("Position: %.4f, %.4f @ %.0fft\n",
                pos.Latitude, pos.Longitude, pos.Altitude)
        }
    }
}

Signature: func CastDataAs[T any](dwData *types.DWORD) *T

The struct layout must exactly match the SimConnect data definition order and types. Use float64 for SIMCONNECT_DATATYPE_FLOAT64, int32/uint32 for INT32, and fixed-size byte arrays for strings ([256]byte for STRING256).

Message Type Methods

The Message struct provides helper methods that cast the raw pointer to a typed struct. Each returns nil when DwID does not match:

Method SIMCONNECT_RECV_ID checked Return type
AsOpen() OPEN *types.SIMCONNECT_RECV_OPEN
AsException() EXCEPTION *types.SIMCONNECT_RECV_EXCEPTION
AsEvent() EVENT *types.SIMCONNECT_RECV_EVENT
AsEventFrame() EVENT_FRAME *types.SIMCONNECT_RECV_EVENT_FRAME
AsEventFilename() EVENT_FILENAME *types.SIMCONNECT_RECV_EVENT_FILENAME
AsEventObjectAddRemove() EVENT_OBJECT_ADDREMOVE *types.SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE
AsSimObjectData() SIMOBJECT_DATA *types.SIMCONNECT_RECV_SIMOBJECT_DATA
AsSimObjectDataBType() SIMOBJECT_DATA_BYTYPE *types.SIMCONNECT_RECV_SIMOBJECT_DATA_BTYPE
AsClientData() CLIENT_DATA *types.SIMCONNECT_RECV_CLIENT_DATA
AsAssignedObjectID() ASSIGNED_OBJECT_ID *types.SIMCONNECT_RECV_ASSIGNED_OBJECT_ID
AsFacilityData() FACILITY_DATA *types.SIMCONNECT_RECV_FACILITY_DATA
AsFacilityDataEnd() FACILITY_DATA_END *types.SIMCONNECT_RECV_FACILITY_DATA_END
AsFacilityList() AIRPORT_LIST, VOR_LIST, NDB_LIST or WAYPOINT_LIST *types.SIMCONNECT_RECV_FACILITIES_LIST
AsAirportList() / AsNDBList() / AsVORList() / AsWaypointList() the matching *_LIST *types.SIMCONNECT_RECV_AIRPORT_LIST etc.
AsSimObjectAndLiveryEnumeration() ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *types.SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST
AsEnumerateInputEvents() ENUMERATE_INPUT_EVENTS *types.SIMCONNECT_RECV_ENUMERATE_INPUT_EVENTS
AsGetInputEvent() GET_INPUT_EVENT *types.SIMCONNECT_RECV_GET_INPUT_EVENT
AsSubscribeInputEvent() SUBSCRIBE_INPUT_EVENT *types.SIMCONNECT_RECV_SUBSCRIBE_INPUT_EVENT
AsFlowEvent() FLOW_EVENT *types.SIMCONNECT_RECV_FLOW_EVENT
AsCommBus() COMM_BUS *types.SIMCONNECT_RECV_COMM_BUS (data via CommBusData())
AsCameraStatus() CAMERA_STATUS *types.SIMCONNECT_RECV_CAMERA_STATUS
AsCameraData() CAMERA_DATA (types.SIMCONNECT_DATA_CAMERA, bool): decoded, false instead of nil

The list messages (AsAirportList, AsNDBList, AsVORList, AsWaypointList, AsEnumerateInputEvents, AsSimObjectAndLiveryEnumeration) have an Entries() method that returns the entries following the header. The input event, flow event, CommBus and camera helpers are MSFS 2024 only: against MSFS 2020 they always return nil. There is no helper for the quit message (SIMCONNECT_RECV_ID_QUIT); the stream closes after it.

Parsing Strings

SimConnect returns null-terminated strings. Use BytesToString to convert:

title := engine.BytesToString(data.SzTitle[:])

Attributing Exceptions

SimConnect reports failed requests asynchronously as SIMCONNECT_RECV_ID_EXCEPTION messages that carry only the send ID of the packet that failed (DwSendID) and the index of the bad parameter (DwIndex). Record the send ID of each request with GetLastSentPacketID right after making it, then look it up when an exception arrives:

sent := map[uint32]string{}
track := func(desc string, err error) {
    if id, idErr := client.GetLastSentPacketID(); idErr == nil {
        sent[id] = desc
    }
}

track("GEAR_DOWN to AI", client.TransmitClientEvent(objectID, evtGearDown, 0,
    types.SIMCONNECT_GROUP_PRIORITY_HIGHEST, types.SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY))

// in the message loop
case types.SIMCONNECT_RECV_ID_EXCEPTION:
    e := msg.AsException()
    log.Printf("exception %d in %q (parameter %d)", e.DwException, sent[uint32(e.DwSendID)], e.DwIndex)

Call GetLastSentPacketID on the same goroutine immediately after the request; a request sent from another goroutine in between would be reported instead. The method is also available on manager.Manager.

System State

RequestSystemState

Requests simulator system state information.

client.RequestSystemState(
    StateReqID,
    types.SIMCONNECT_SYSTEM_STATE_SIM,  // Simulation running state
)

Response arrives as SIMCONNECT_RECV_ID_SYSTEM_STATE message.

Events

SubscribeToSystemEvent

Subscribes to simulator system events.

const PauseEventID = 2000

client.SubscribeToSystemEvent(PauseEventID, "Pause")
client.SubscribeToSystemEvent(2001, "Sim")
client.SubscribeToSystemEvent(2002, "SimStart")
client.SubscribeToSystemEvent(2003, "SimStop")

UnsubscribeFromSystemEvent

Removes a system event subscription.

client.UnsubscribeFromSystemEvent(PauseEventID)

MapClientEventToSimEvent

Maps a client event ID to a simulator event name.

const GearToggleEventID = 3000

client.MapClientEventToSimEvent(GearToggleEventID, "GEAR_TOGGLE")

TransmitClientEvent

Sends an event to the simulator.

// Toggle landing gear
client.TransmitClientEvent(
    types.SIMCONNECT_OBJECT_ID_USER,
    GearToggleEventID,
    0,  // Event data
    types.SIMCONNECT_GROUP_PRIORITY_HIGHEST,
    types.SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY,
)

TransmitClientEventEx1

Extended version with additional data parameters.

client.TransmitClientEventEx1(
    types.SIMCONNECT_OBJECT_ID_USER,
    eventID,
    types.SIMCONNECT_GROUP_PRIORITY_HIGHEST,
    types.SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY,
    [5]uint32{data0, data1, data2, data3, data4},
)

Notification Groups

AddClientEventToNotificationGroup

Groups events for priority-based handling.

const FlightControlsGroup = 4000

client.AddClientEventToNotificationGroup(
    FlightControlsGroup,
    GearToggleEventID,
    false,  // maskable
)

SetNotificationGroupPriority

Sets the priority for a notification group.

client.SetNotificationGroupPriority(
    FlightControlsGroup,
    types.SIMCONNECT_GROUP_PRIORITY_HIGHEST,
)

ClearNotificationGroup

Removes all events from a notification group.

client.ClearNotificationGroup(FlightControlsGroup)

AI Traffic

A guide with the kinds, IDs and lifecycle: Engine AI Objects.

AICreateParkedATCAircraft

Creates an AI aircraft parked at an airport.

client.AICreateParkedATCAircraft(
    "Airbus A320 Neo Asobo",
    "N12345",
    "KSEA",      // ICAO code
    CreateReqID,
)

AICreateEnrouteATCAircraft

Creates an AI aircraft following a flight plan.

client.AICreateEnrouteATCAircraft(
    "Boeing 747-8 Asobo",
    "UAL123",
    0,                    // Flight number
    "KSEA_KLAX.pln",      // Flight plan file
    0.5,                  // Progress (0.0-1.0)
    false,                // Touch-and-go
    CreateReqID,
)

AISetAircraftFlightPlan

Assigns a flight plan to an existing AI aircraft.

client.AISetAircraftFlightPlan(
    objectID,
    "KSEA_KLAX.pln",
    FlightPlanReqID,
)

AIRemoveObject

Removes an AI object from the simulation.

client.AIRemoveObject(objectID, RemoveReqID)

AIReleaseControl

Releases control of an AI object back to the simulator.

client.AIReleaseControl(objectID, ReleaseReqID)

AICreateNonATCAircraft

Creates a non-ATC-controlled aircraft at a specific position.

initPos := types.SIMCONNECT_DATA_INITPOSITION{
    Latitude:  47.4502,
    Longitude: -122.3088,
    Altitude:  5000,
    Pitch:     0,
    Bank:      0,
    Heading:   270,
    OnGround:  0,
    Airspeed:  120,
}
client.AICreateNonATCAircraft("Cessna Skyhawk Asobo", "N12345", initPos, CreateReqID)

AICreateSimulatedObject

Creates a non-aircraft simulated object (ground vehicle, boat, etc.).

client.AICreateSimulatedObject("Generic Ground Vehicle", initPos, CreateReqID)

EX1 Variants (Livery Selection)

The EX1 variants of AI creation methods add a livery parameter:

client.AICreateParkedATCAircraftEX1("Boeing 787-10 Asobo", "House", "N12345", "KSEA", ReqID)
client.AICreateEnrouteATCAircraftEX1("Airbus A320 Neo Asobo", "Asobo", "UAL123", 0, "plan.pln", 0.5, false, ReqID)
client.AICreateNonATCAircraftEX1("Cessna Skyhawk Asobo", "Blue", "N12345", initPos, ReqID)

EnumerateSimObjectsAndLiveries

Lists the installed models of an object type (SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT, HELICOPTER, BOAT, GROUND, ALL, USER) and their liveries, for traffic injection or aircraft pickers.

client.EnumerateSimObjectsAndLiveries(EnumReqID, types.SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT)

// in the message loop
if recv := msg.AsSimObjectAndLiveryEnumeration(); recv != nil && recv.DwRequestID == EnumReqID {
    for _, item := range recv.Entries() {
        title := engine.BytesToString(item.AircraftTitle[:]) // aircraft.cfg title
        livery := engine.BytesToString(item.LiveryName[:])   // empty without separate liveries
        fmt.Printf("%-60s  %s\n", title, livery)
    }
    if recv.DwEntryNumber+recv.DwArraySize >= recv.DwOutOf {
        // last batch
    }
}

Replies come in batches (SIMCONNECT_RECV_ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST): DwArraySize entries from DwEntryNumber, DwOutOf in all. Entries() reads the [256]byte AircraftTitle / LiveryName pairs in place after the 28-byte header.

Facilities

RequestFacilitiesList

Requests a list of facilities (airports, waypoints, etc.).

client.RequestFacilitiesList(
    FacilitiesReqID,
    types.SIMCONNECT_FACILITY_LIST_AIRPORT,
)

RequestFacilityData

Requests detailed data for a specific facility.

client.RequestFacilityData(
    FacilityDefID,
    FacilityDataReqID,
    "KSEA",
    "",  // Region (optional)
)

RequestFacilityDataEX1

Extended version that explicitly specifies the facility type.

client.RequestFacilityDataEX1(
    FacilityDefID,
    FacilityDataReqID,
    "KSEA",
    "",                                        // Region (optional)
    byte(types.SIMCONNECT_FACILITY_DATA_AIRPORT), // Explicit facility type
)

RequestJetwayData

Requests jetway data for a specific airport.

indexes := []int32{0, 1, 2}
client.RequestJetwayData("KSEA", uint32(len(indexes)), &indexes[0])

RequestAllFacilities

Requests all facilities of a specific type.

client.RequestAllFacilities(
    types.SIMCONNECT_FACILITY_LIST_AIRPORT,
    AllFacilitiesReqID,
)

SubscribeToFacilitiesEX1

Extended subscription with separate request IDs for facilities entering and leaving range.

client.SubscribeToFacilitiesEX1(
    types.SIMCONNECT_FACILITY_LIST_AIRPORT,
    NewInRangeReqID,
    OldOutRangeReqID,
)

AddFacilityDataDefinitionFilter

Filters facility data requests.

client.AddFacilityDataDefinitionFilter(
    FacilityDefID,
    "filterPath",
    filterDataPtr,
    filterDataSize,
)

ClearAllFacilityDataDefinitionFilters

Removes all filters from a facility definition.

client.ClearAllFacilityDataDefinitionFilters(FacilityDefID)

SubscribeToFacilities

Subscribes to facility updates within a radius.

client.SubscribeToFacilities(
    types.SIMCONNECT_FACILITY_LIST_AIRPORT,
    FacilitySubReqID,
)

AddToFacilityDefinition

Defines the data fields to receive for facilities.

client.AddToFacilityDefinition(FacilityDefID, "OPEN AIRPORT")
client.AddToFacilityDefinition(FacilityDefID, "NAME")
client.AddToFacilityDefinition(FacilityDefID, "LATITUDE")
client.AddToFacilityDefinition(FacilityDefID, "LONGITUDE")

Flight Operations

FlightLoad

Loads a saved flight file.

client.FlightLoad("C:/Users/Pilot/Flights/MyFlight.flt")

FlightPlanLoad

Loads a flight plan file.

client.FlightPlanLoad("C:/Users/Pilot/FlightPlans/KSEA_KLAX.pln")

FlightSave

Saves the current flight state.

client.FlightSave(
    "C:/Users/Pilot/Flights/SavedFlight.flt",
    "My Saved Flight",
    "Saved at KSEA runway 16L",
)

Other Client APIs

Not covered above:

  • SetSystemEventState, RequestNotificationGroup, RemoveClientEvent, RequestFacilitiesListEX1, UnsubscribeToFacilitiesEX1 — thin wrappers of the SimConnect functions of the same name.
  • SubscribeToFlowEvent / UnsubscribeFromFlowEvent — flow events (MSFS 2024), read with msg.AsFlowEvent().
  • engine.SystemStateFloat64(recv) — the float value of a SIMCONNECT_RECV_SYSTEM_STATE reply.
  • engine.PackWaypoints(wps) and engine.WaypointWireSize — pack SIMCONNECT_DATA_WAYPOINT slices for SetDataOnSimObject.
  • Client data areas (MapClientDataNameToID, CreateClientData, AddToClientDataDefinition, RequestClientData, SetClientData, ClearClientDataDefinition, all on the engine.Client interface): Client Data Areas. Input events (MSFS 2024): Input Events. Camera (MSFS 2024, *engine.Engine only): Camera.

Example: Complete Data Loop

package main

import (
    "fmt"
    "os"
    "os/signal"

    "github.com/mrlm-net/simconnect/pkg/engine"
    "github.com/mrlm-net/simconnect/pkg/types"
)

type AircraftData struct {
    Latitude  float64
    Longitude float64
    Altitude  float64
    Heading   float64
}

const (
    DataDefID = 1000
    DataReqID = 1001
)

func main() {
    client := engine.New("DataLoop")

    if err := client.Connect(); err != nil {
        panic(err)
    }
    defer client.Disconnect()

    // Define data structure
    client.AddToDataDefinition(DataDefID, "PLANE LATITUDE", "degrees", types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
    client.AddToDataDefinition(DataDefID, "PLANE LONGITUDE", "degrees", types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
    client.AddToDataDefinition(DataDefID, "PLANE ALTITUDE", "feet", types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
    client.AddToDataDefinition(DataDefID, "PLANE HEADING DEGREES TRUE", "degrees", types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)

    // Request periodic updates
    client.RequestDataOnSimObject(
        DataReqID, DataDefID,
        types.SIMCONNECT_OBJECT_ID_USER,
        types.SIMCONNECT_PERIOD_SECOND,
        types.SIMCONNECT_DATA_REQUEST_FLAG_CHANGED,
        0, 0, 0,
    )

    // Handle shutdown
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, os.Interrupt)

    stream := client.Stream()
    for {
        select {
        case <-sigChan:
            fmt.Println("Shutting down...")
            return
        case msg, ok := <-stream:
            if !ok {
                return
            }
            if msg.Err != nil {
                fmt.Println("stream error:", msg.Err)
                continue
            }
            if types.SIMCONNECT_RECV_ID(msg.DwID) == types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA {
                data := msg.AsSimObjectData()
                if data.DwRequestID == DataReqID {
                    aircraft := engine.CastDataAs[AircraftData](&data.DwData)
                    fmt.Printf("Pos: %.4f, %.4f | Alt: %.0fft | Hdg: %.1f°\n",
                        aircraft.Latitude, aircraft.Longitude,
                        aircraft.Altitude, aircraft.Heading)
                }
            }
        }
    }
}

See Also

CommBus (MSFS 2024)

CommBus passes events with string data (usually JSON) between SimConnect clients, WebAssembly modules and JavaScript gauges or panels: an in-sim add-on can answer an app this way (#678).

client.SubscribeToCommBusEvent(4242, "myaddon.reply")
client.CallCommBusEvent("myaddon.request", types.SIMCONNECT_COMM_BUS_BROADCAST_TO_DEFAULT, `{"icao":"LKPR"}`)

var parts engine.CommBusAssembler
for msg := range client.Stream() {
	if id, data, ok := parts.Add(&msg); ok {
		fmt.Println(id, data) // 4242 {"metar":"..."}
	}
}

SIMCONNECT_COMM_BUS_BROADCAST_TO_* says who a call reaches: JS, WASM, SIMCONNECT (other clients), SIMCONNECT_SELF_CALL (with SIMCONNECT, the caller too); DEFAULT is the first three. A subscribed event arrives as SIMCONNECT_RECV_ID_COMM_BUS (Message.AsCommBus, CommBusData); data too long for one message comes in parts, which CommBusAssembler joins. Checked live with a self call: 16 bytes and 9 KB, each back intact in one message.