Skip to content

Conversation

@michael-betz
Copy link

@michael-betz michael-betz commented Aug 11, 2025

The linux kernel provides high resolution timestamps for received CAN frames.

This pull requests adds CanRaw.GetLastTimeStamp() to retrieve the timestamp of the last read frame.

The timestamp is in micro-seconds unix time.

Tested with an inno-maker USB2CAN dongle:

# Add virtual CAN interface called `vcan0`
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

dotnet run --project src/devices/SocketCan/samples/SocketCan.Samples.csproj receive vcan0

# in another window
cansend vcan0 123#DEADBEEF

# Console output
  Can.Samples.csproj receive vcan0
  Listening for any id
  Ts: 2025-08-24T17:59:17.9521580Z Id: 0x123 [SFF]: DEADBEEF
  Ts: 2025-08-24T17:59:18.2795050Z Id: 0x123 [SFF]: DEADBEEF
  Ts: 2025-08-24T17:59:19.2140580Z Id: 0x123 [SFF]: DEADBEEF
Microsoft Reviewers: Open in CodeFlow

which calls ioctl(SIOCGSTAMP) to get the timestamp of the last
read message from the kernel
also fix some bugs:
  * time_t and suseconds_t are of type `long` on debian
  * print timestamp in sample project

Tested on hardware on a Linux 6.12.33+deb13-amd64 x86_64 machine
with an Innomaker USB2CAN dongle
@dotnet-policy-service dotnet-policy-service bot added the area-device-bindings Device Bindings for audio, sensor, motor, and display hardware that can used with System.Device.Gpio label Aug 11, 2025
@michael-betz
Copy link
Author

@dotnet-policy-service agree

@krwq
Copy link
Member

krwq commented Aug 21, 2025

[Triage] Rather than adding GetTimeStamp API add another overload of TryReadFrame which out the timestamp or time. You should also make sure that when older version of kernel is used the error message tells you what version of kernel is required to use the new API. GetTimeStamp directly on CanRaw is very confusing.

return ((ulong)tv.tv_sec * 1000000 + (ulong)tv.tv_usec); // unix time in [us]
}

internal unsafe struct TimeVal
Copy link
Member

@krwq krwq Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be a comment that long is correct type only because we're using this in conjunction with latest kernel. Please double check this actually works with 32-bit OS-es (if not then please make sure to display correct error message)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note, you don't need to test it - just double check what type is defined in the latest kernel's .h file, if it's int64_t or equivalent we're good if it's platform specific like int then likely not

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I did some digging. In C, the ioctl call returns:

       struct timeval {
           time_t       tv_sec;   /* Seconds */
           suseconds_t  tv_usec;  /* Microseconds */
       };

time_t can be a signed integer of 32 bit or 64 bit, depending on the platform.

In C#, I map this to a long type which is always a 64 bit signed integer.

If I run this on a 64 bit system, everything fits bit by bit.

If I run this on a 32-bit system, the C# runtime will promote the 32-bit return value to long properly.

Please correct me if I'm wrong.

I will look into testing this on a 32 bit system.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is DateTime the correct for this timestamp?
When I see a DateTime, I expect this represent the "real" moment when the event occurred. I also expect to be able to compare this value with other DateTime instances coming from other sensors.

If instead this timestamp represents the time elapsed since the OS started, IMO the TimeSpan type would be more appropriate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kernel does indeed return the absolute value of the system clock when the CAN frame was received (microseconds since the 1.1.1970). So I think DateTime is a good type for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, thanks

  * remove GetTimeStamp()
  * adapt samples/Program.cs
  * add can interface name as a CLI parameter to Program.cs
@michael-betz
Copy link
Author

michael-betz commented Aug 26, 2025

You should also make sure that when older version of kernel is used the error message tells you what version of kernel is required to use the new API.

Okay, I did some research on this.
SocketCAN was merged into the mainline Linux kernel in version 2.6.25, which was released in April 2008.
This version already supports timestamps via ioctl(SIOCGSTAMP)

So seems like, if SocketCAN works, there is a very good chance that getting timestamps will also work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-device-bindings Device Bindings for audio, sensor, motor, and display hardware that can used with System.Device.Gpio

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants