Typical Program Flow
This chapter describes the recommended sequence of API calls from application start through CAN communication and clean shutdown. The same logical flow applies to both the C API and the C# wrapper.
Initialize the library once per process. Choose shared (0) or exclusive (1) mode.
Overview Diagram
Application start
|
v
EmSACAN_Startup() (once per process)
|
v
EmSACAN_EnumerateInterfaces()
|
v
EmSACAN_Connect() (one handle / CanDragonDevice per device)
|
v
EmSACAN_WriteParameter() (CHANNELCONFIG or ADVCHANNELCONFIG, if needed)
|
v
EmSACAN_OpenChannel() (per CAN port you will use)
|
+----> EmSACAN_Write() / EmSACAN_Read() (main loop)
| EmSACAN_GetStatus() (optional monitoring)
|
v
EmSACAN_CloseChannel()
|
v
EmSACAN_Disconnect()
|
v
EmSACAN_Shutdown() (once per process)
|
v
Application exit
Step-by-Step Flow
1. Start the library
Call EmSACAN_Startup (C) or EmSACANLibrary.Startup (C#) once at the beginning of your process, before any other API function.
Exclusive vs non-exclusive mode (EnableExclusiveMode / enableExclusiveMode):
| Mode | Parameter | What you get |
|---|---|---|
| Shared (default) | 0 / false | Multiple applications—including CAN Dragon View and your own program—can access the same CAN Dragon concurrently. Port configuration may be locked if another client already configured that port. Best for development and mixed tooling environments. |
| Exclusive | 1 / true | Your application has sole USB access to each device it connects. No other application can use the same unit at the same time. You always own port configuration. Best when your application must run unattended as the only client. |
::note Call EmSACAN_Startup only once per process. A second call without an intervening EmSACAN_Shutdown returns EMSACAN_ERR_INVALID_STATE. ::
2. Enumerate devices
Call EmSACAN_EnumerateInterfaces to obtain a list of connected CAN Dragon units. Each entry includes serial number, description, and number of CAN ports.
If enumeration fails, call EmSACAN_GetLastError(NULL, …) (C) or EmSACANLibrary.GetLastError() (C#) for details.
3. Connect to a device
Call EmSACAN_Connect with the device serial number (wide string in C). Each successful connect returns a distinct handle (emsacan_handle_t in C, CanDragonDevice in C#). You may connect multiple devices in one process—use one handle per device.
The optional DisableRTCUpdate flag skips sending the host date and time to the device RTC on connect.
4. Configure the CAN port (before opening)
Set bit rate, termination, and feature flags with EmSACAN_WriteParameter using EMSACAN_PARAM_CHANNELCONFIG (simple bit rates in kbps) or EMSACAN_PARAM_ADVCHANNELCONFIG (explicit timing registers).
Check configuration lock first in shared mode:
int locked = 0;
if (EmSACAN_IsConfigurationLocked(handle, 0, &locked) == EMSACAN_OK && locked)
{
/* Another client already configured this port — read config or skip write */
}
Configuration must be applied before EmSACAN_OpenChannel.
5. Open the CAN port
Call EmSACAN_OpenChannel for each port you will use (zero-based index: 0 … NumberofChannels − 1). Traffic flows only after the port is open.
Optionally register a connection callback to receive USB disconnect/reconnect notifications while the port is open.
6. Exchange CAN frames
- Transmit: Build an emsacan_msg_t / EmSACANMessage and call EmSACAN_Write.
- Receive: Poll EmSACAN_Read in a loop. An empty queue returns EMSACAN_ERR_RXEMPTY (C) or
null(C#). - Monitor: Call EmSACAN_GetStatus for error-active, bus-off, and overrun flags.
For event-driven receive on Windows, register handles with EmSACAN_SetCANEvents and wait on those events instead of polling.
7. Close the port
Call EmSACAN_CloseChannel when finished with a port. This stops CAN traffic on that port.
8. Disconnect and shut down
Call EmSACAN_Disconnect for each handle (or Dispose on each CanDragonDevice in C#). Finally call EmSACAN_Shutdown once to release all library resources.
::caution
Always pair Startup with Shutdown and Connect with Disconnect, even after errors. Shutdown disconnects any sessions still open.
::
Minimal C Example
#include "EmSACAN.h"
int main(void)
{
emsacan_handle_t handle = 0;
emsacan_interface_t ifaces[8];
int count = 8;
wchar_t err[256];
if (EmSACAN_Startup(0) != EMSACAN_OK)
return 1;
if (EmSACAN_EnumerateInterfaces(ifaces, &count) != EMSACAN_OK || count == 0)
{
EmSACAN_GetLastError(0, err, 256);
EmSACAN_Shutdown();
return 1;
}
if (EmSACAN_Connect(ifaces[0].SerialNumber, 0, &handle) != EMSACAN_OK)
{
EmSACAN_Shutdown();
return 1;
}
emsacan_channel_config_t cfg = {
.flags = EMSACAN_CONFIG_FLAGS_NONE,
.termination = EMSACAN_TERMINATION_120,
.nominal_kbps = 500,
.data_kbps = 2000
};
EmSACAN_WriteParameter(handle, EMSACAN_PARAM_CHANNELCONFIG, 0,
(uint8_t *)&cfg, sizeof(cfg));
EmSACAN_OpenChannel(handle, 0, NULL, NULL);
emsacan_msg_t tx = { .id = 0x123, .len = 4, .type = EMSACAN_MSGTYPE_NONE };
tx.data[0] = 1; tx.data[1] = 2; tx.data[2] = 3; tx.data[3] = 4;
EmSACAN_Write(handle, 0, &tx);
emsacan_msg_t rx;
if (EmSACAN_Read(handle, 0, &rx) == EMSACAN_OK)
{
/* use rx.id, rx.data, rx.timestamp */
}
EmSACAN_CloseChannel(handle, 0);
EmSACAN_Disconnect(handle);
EmSACAN_Shutdown();
return 0;
}
Minimal C# Example
using EmSACAN;
EmSACANLibrary.Startup(enableExclusiveMode: false);
try
{
var list = EmSACANLibrary.EnumerateInterfaces();
if (list.Count == 0) return;
using (CanDragonDevice device = CanDragonDevice.Connect(list[0].SerialNumber, false))
{
device.WriteChannelConfig(new EmSACANChannelConfig
{
Termination = EmSACANTermination.Term120,
NominalKbps = 500,
DataKbps = 2000
}, channelNumber: 0);
device.OpenChannel(0);
device.Write(0, 0x123, new byte[] { 1, 2, 3, 4 });
EmSACANMessage? msg = device.Read(0);
if (msg.HasValue)
Console.WriteLine($"RX 0x{msg.Value.Id:X} len={msg.Value.Length}");
device.CloseChannel(0);
}
}
finally
{
EmSACANLibrary.Shutdown();
}