C# and .NET
From Fundamentals to Robotics
Contents
I Foundations: The .NET Platform
3
1 What .NET Actually Is
1.1 The platform, not just a language . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 .NET Framework vs .NET (Core) vs Mono . . . . . . . . . . . . . . . . . . . . . .
1.3 Compilation model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
4
4
5
2 Environment and Tooling
2.1 Installing and verifying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Project types you’ll actually use . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 NuGet: the package ecosystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
6
6
6
II The C# Language: Core to Advanced
7
3 Syntax, Types, and Control Flow
3.1 Hello World and program structure . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Value types vs reference types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Primitive types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4 Control flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
8
8
9
9
4 Object-Oriented C#
4.1 Classes, fields, properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Inheritance, interfaces, polymorphism . . . . . . . . . . . . . . . . . . . . . . . . .
4.3 Records and immutability (C# 9+) . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4 Structs vs classes – when to choose which . . . . . . . . . . . . . . . . . . . . . . .
10
10
10
11
11
5 Collections and Generics
12
5.1 Core collection types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
5.2 Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6 Error Handling and Resource Management
13
6.1 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.2 IDisposable and using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
7 Delegates, Events, and Functional Features
14
7.1 Delegates and lambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7.2 Events – the backbone of reactive telemetry . . . . . . . . . . . . . . . . . . . . . . 14
8 LINQ (Language Integrated Query)
15
1
C# & .NET for Robotics
CONTENTS
9 Asynchronous and Concurrent Programming
9.1 Why this matters for you specifically . . . . . . . . . . . . . . . . . . . . . . . . . .
9.2 async/await . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.3 Thread-safety essentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
16
16
16
10 Memory, Performance, and Advanced Language Features
10.1 Garbage collection, in brief . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2 Span<T> and low-allocation code . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.3 Nullable reference types (C# 8+) . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.4 Pattern matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.5 Unsafe code, pointers, and stackalloc . . . . . . . . . . . . . . . . . . . . . . . . .
10.6 Attributes and reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
18
18
18
18
19
19
11 Modern C# Language Features by Version
20
III
21
C# in Robotics and Backend Systems
12 Where C# Actually Fits in a Robotics Stack
22
12.1 An honest map of the ecosystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
13 Serial and Embedded Communication (ESP32/Arduino ↔ C#)
23
13.1 Reading a serial stream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
13.2 On Linux (e.g. a Raspberry Pi backend) the port name differs . . . . . . . . . . . . 23
14 ROS Integration Paths for C#
14.1 Path 1: rosbridge_suite + ROS# (WebSocket/JSON) . . . . . . . . . . . . . . . .
14.2 Path 2: Unity Robotics Hub (ROS-TCP-Connector) . . . . . . . . . . . . . . . . .
14.3 Path 3: ros2cs / ros2-for-unity – native ROS 2 nodes in C# . . . . . . . . . . . . .
24
24
24
24
15 Backend and Telemetry Systems
15.1 ASP.NET Core Web API – a telemetry ingestion service . . . . . . . . . . . . . . .
15.2 SignalR – real-time push to a live dashboard . . . . . . . . . . . . . . . . . . . . .
15.3 gRPC – efficient service-to-service communication . . . . . . . . . . . . . . . . . . .
15.4 Persisting session data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
26
26
26
26
16 .NET on Embedded Linux (Raspberry Pi–class boards)
28
IV
29
Advancing Further
17 Interop and Performance for Hardware-Adjacent Work
30
17.1 P/Invoke – calling native C libraries from C# . . . . . . . . . . . . . . . . . . . . . 30
17.2 Server GC vs Workstation GC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
18 Testing and Project Hygiene
31
19 A Suggested Learning Roadmap
32
20 Reference Resources
33
2
Part I
Foundations: The .NET Platform
3
Chapter 1
What .NET Actually Is
1.1 The platform, not just a language
”.NET” refers to a whole platform: a runtime, a set of base libraries, compilers, and tooling. C#
is the primary language you write for that platform, but the platform itself is language-agnostic
– F# and Visual Basic also compile down to the same intermediate form.
Key Idea
Three things you must keep conceptually separate:
• C# (the language) – syntax and grammar rules.
• CLR (Common Language Runtime) – the virtual machine that executes compiled
code, handles memory (garbage collection), type safety, and JIT compilation.
• BCL (Base Class Library) – the standard library: collections, I/O, threading, networking, etc.
1.2 .NET Framework vs .NET (Core) vs Mono
• .NET Framework – the original (2002–2019), Windows-only, legacy. You will still meet it
in old industrial software and some Windows-only automation tools, but it is not where you
should build new things.
• .NET (formerly “.NET Core”) – the modern, cross-platform, open-source successor. This
is what you install today. As of mid-2026 the current releases are .NET 10 (LTS, supported
until Nov 2028) and .NET 9 (STS). Use .NET 10 for anything you start now.
• Mono / Unity’s runtime – an independent CLR implementation, historically used for
cross-platform and mobile; it is what Unity uses to run your C# scripts in the Editor and
in most builds. This matters directly for you because Unity Robotics simulation runs on
Mono/IL2CPP, not on the .NET runtime you install via the SDK.
Note
Practical rule: when you dotnet run a console app, you’re on the modern .NET runtime.
When you write a script inside Unity, you’re on Mono/IL2CPP with a slightly different
(usually older-feature) subset of C#. Don’t assume every language feature you read about
works identically in both.
4
C# & .NET for Robotics
CHAPTER 1. WHAT .NET ACTUALLY IS
1.3 Compilation model
C# source → Roslyn compiler → IL (Intermediate Language), packaged into an assembly
(.dll/.exe) → at run time, the CLR’s JIT (Just-In-Time) compiler turns IL into native
machine code for your CPU. There is also AOT (Ahead-Of-Time) compilation (Native AOT
in modern .NET), which skips the JIT step entirely and produces a native binary directly –
relevant later when you care about startup latency on embedded Linux boards (e.g., a Raspberry
Pi controller).
5
Chapter 2
Environment and Tooling
2.1 Installing and verifying
1
2
3
4
# after installing the .NET 10 SDK
dotnet −−version
dotnet −−list −sdks
dotnet −−list − runtimes
2.2 Project types you’ll actually use
Template
Use case
console
classlib
CLI tools, telemetry loggers, small utilities, quick experiments
Reusable library (e.g., a “TorqueX.Telemetry” data-parsing library)
Long-running background service (e.g., a serial-port listener
that forwards ESP32 data)
ASP.NET Core REST API – backend for a telemetry dashboard
Browser-based UI without JavaScript – good for a lightweight
live dashboard
Unit test project
worker
webapi
blazorserver /
blazorwasm
xunit
1
2
3
4
dotnet new console −n TorqueX . TelemetryLogger
cd TorqueX . TelemetryLogger
dotnet add package System .IO. Ports
dotnet run
2.3 NuGet: the package ecosystem
NuGet is to .NET what pip is to Python. dotnet add package <Name> pulls a package into
your project’s .csproj file, which is the human-readable, XML-based project descriptor (roughly
analogous to requirements.txt + build config combined).
6
Part II
The C# Language: Core to
Advanced
7
Chapter 3
Syntax, Types, and Control Flow
3.1 Hello World and program structure
1
2
// Modern C# (top −level statements , no explicit Main needed )
Console . WriteLine ("Hello , TorqueX ");
3
4
5
6
7
8
9
10
11
12
13
14
// Equivalent classic form
namespace TorqueX
{
class Program
{
static void Main( string [] args)
{
Console . WriteLine ("Hello , TorqueX ");
}
}
}
3.2 Value types vs reference types
This is the single most important conceptual split in C#, and it maps directly onto performancesensitive robotics code.
• Value types (int, double, bool, struct, enum) live on the stack (or inline inside containing
objects) and are copied by value.
• Reference types (class, string, arrays, delegates) live on the heap; variables hold a reference (pointer) to the object.
1
2
3
4
struct Vector3 // value type −− copied , no GC pressure
{
public double X, Y, Z;
}
5
6
7
8
9
class Motor // reference type −− lives on the heap
{
public double RpmSetpoint ;
}
Note
For a telemetry loop reading thousands of sensor samples per second (e.g., Hall-effect pulses
for the 6-stroke engine RPM), preferring struct for small, short-lived data avoids unnecessary
8
C# & .NET for Robotics
CHAPTER 3. SYNTAX, TYPES, AND CONTROL FLOW
heap allocation and garbage-collector pressure. This is the same instinct you already have
from C/C++ stack vs heap thinking.
3.3 Primitive types
Type
Size
Notes
int
long
float
double
decimal
4 bytes
8 bytes
4 bytes
8 bytes
16 bytes
bool
char
string
1 byte
2 bytes
ref type
Signed 32-bit integer, default for whole numbers
Signed 64-bit
Single precision; suffix f (e.g. 3.14f)
Double precision; default for decimal literals
High-precision, base-10 – for financial calcs, not sensor
math
true/false
UTF-16 code unit
Immutable sequence of char
3.4 Control flow
1
int rpm = ReadHallSensor ();
2
3
4
5
6
7
8
9
10
if (rpm > 8000)
{
TriggerRevLimiter ();
}
else if (rpm < 800)
{
Console . WriteLine ("Idle or stalled ");
}
11
12
13
14
15
16
17
18
19
20
21
22
23
24
switch ( engineState )
{
case EngineState .Idle:
break ;
case EngineState . BurnPhase :
InjectFuel ();
break ;
case EngineState . CoastPhase when rpm > 6000:
CutIgnition ();
break ;
default :
throw new InvalidOperationException (" Unknown state ");
}
25
26
27
28
for (int i = 0; i < samples . Length ; i++) { /∗ ... ∗/ }
foreach (var sample in samples ) { /∗ ... ∗/ }
while ( isRunning ) { /∗ ... ∗/ }
9
Chapter 4
Object-Oriented C#
4.1 Classes, fields, properties
1
2
3
4
public class Motor
{
// Auto − implemented property ( backing field generated by compiler )
public double RpmSetpoint { get; set; }
5
// Read −only after construction
public string Id { get; init; }
6
7
8
private double _currentRpm ;
9
10
public Motor ( string id , double setpoint )
{
Id = id;
RpmSetpoint = setpoint ;
}
11
12
13
14
15
16
public void UpdateRpm ( double measured )
{
_currentRpm = measured ;
}
17
18
19
20
21
}
4.2 Inheritance, interfaces, polymorphism
1
2
3
4
5
public interface ISensor
{
double ReadValue ();
string Unit { get; }
}
6
7
8
9
10
11
12
public abstract class SensorBase : ISensor
{
public abstract double ReadValue ();
public abstract string Unit { get; }
public DateTime LastRead { get; protected set; }
}
13
14
15
16
public class ThermocoupleSensor : SensorBase
{
public override string Unit => "C";
17
10
C# & .NET for Robotics
CHAPTER 4. OBJECT-ORIENTED C#
public override double ReadValue ()
{
LastRead = DateTime . UtcNow ;
return ReadRawVoltageAndConvert ();
}
18
19
20
21
22
23
private double ReadRawVoltageAndConvert () => 0.0; // placeholder
24
25
}
Note
This maps directly onto your telemetry plan: ISensor as a common contract lets a single
polling loop treat Hall-effect RPM sensors and K-type thermocouples uniformly, while each
concrete class handles its own conversion math.
4.3 Records and immutability (C# 9+)
Records are reference types (or, with record struct, value types) with built-in value-based
equality and concise syntax – ideal for telemetry data packets.
1
2
3
4
5
6
public record TelemetryFrame (
DateTime Timestamp ,
double RpmHall ,
double TempThermocoupleC ,
double ThrottlePercent
);
7
8
9
var frame = new TelemetryFrame ( DateTime .UtcNow , 4500.2 , 87.6 , 32.0) ;
var frame2 = frame with { RpmHall = 4600.0 }; // non − destructive mutation
4.4 Structs vs classes – when to choose which
Use a struct when
Use a class when
Small (a handful of fields),
short-lived, value semantics wanted (e.g. a 3D vector, a single sensor sample)
You’re in a tight loop and
want to avoid GC pressure
Identity matters, object is large, or it needs
inheritance
You need shared mutable state referenced from
multiple places
11
Chapter 5
Collections and Generics
5.1 Core collection types
1
2
3
4
5
List <double > rpmSamples = new ();
Dictionary <string , ISensor > sensorsById = new ();
Queue < TelemetryFrame > incomingFrames = new ();
serial stream
Stack <string > commandHistory = new ();
double [] fixedBuffer = new double [512];
for hot loops
// FIFO buffer for a
// fixed −size , fastest
5.2 Generics
Generics let you write one algorithm that works across types, safely, with no boxing/unboxing
overhead.
1
2
3
4
public class RingBuffer <T>
{
private readonly T[] _data ;
private int _head;
5
public RingBuffer (int capacity ) => _data = new T[ capacity ];
6
7
public void Add(T item)
{
_data [ _head ] = item;
_head = ( _head + 1) % _data . Length ;
}
8
9
10
11
12
13
}
14
15
var rpmWindow = new RingBuffer <double >(50) ;
12
Chapter 6
Error Handling and Resource Management
6.1 Exceptions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try
{
var frame = ParseTelemetryPacket ( rawBytes );
ProcessFrame ( frame );
}
catch ( FormatException ex)
{
Log.Warn($" Malformed packet : {ex. Message }");
}
catch ( IOException ex) when (ex. Message . Contains ("port"))
{
Log.Error (" Serial port disconnected ");
ReconnectPort ();
}
finally
{
bytesProcessed ++;
}
6.2 IDisposable and using
Anything holding an unmanaged resource (file handles, serial ports, sockets, DB connections)
implements IDisposable. The using statement guarantees cleanup even if an exception is thrown
– directly analogous to a Python with block or RAII in C++.
1
using System .IO. Ports;
2
3
4
5
6
using var port = new SerialPort ("COM5", 115200) ;
port.Open ();
string line = port. ReadLine ();
// port. Dispose () is called automatically at end of scope
13
Chapter 7
Delegates, Events, and Functional Features
7.1 Delegates and lambdas
A delegate is a type-safe function pointer – C#’s mechanism for treating functions as values.
1
2
3
Func <double , double , double > computeTorque = (force , radius ) => force ∗
radius ;
Action <string > logger = msg => Console . WriteLine ($"[LOG] {msg}");
Predicate <double > isOverheating = temp => temp > 95.0;
4
5
6
double t = computeTorque (120.5 , 0.045) ;
logger (" Torque computed ");
7.2 Events – the backbone of reactive telemetry
1
2
3
public class SerialTelemetryListener
{
public event Action < TelemetryFrame > FrameReceived ;
4
public void OnDataArrived ( TelemetryFrame frame )
{
FrameReceived ?. Invoke ( frame ); // notify all subscribers
}
5
6
7
8
9
}
10
11
12
13
var listener = new SerialTelemetryListener ();
listener . FrameReceived += frame => Console . WriteLine ($"RPM: { frame . RpmHall }
");
listener . FrameReceived += frame => dashboard . Update ( frame );
Key Idea
This publish/subscribe pattern is conceptually identical to a ROS topic: a publisher fires
events, subscribers react, and neither needs to know about the other directly. Internalising
C# events makes the jump to ROS pub/sub almost free later.
14
Chapter 8
LINQ (Language Integrated Query)
LINQ lets you query collections declaratively – filter, transform, aggregate – much like pandas
operations in Python, but built into the language and fully type-checked at compile time.
1
List < TelemetryFrame > frames = LoadSessionLog ();
2
3
4
5
6
var overheatEvents = frames
. Where (f => f. TempThermocoupleC > 95)
. Select (f => new { f.Timestamp , f. TempThermocoupleC })
. ToList ();
7
8
9
double avgRpm = frames . Average (f => f. RpmHall );
double peakTemp = frames .Max(f => f. TempThermocoupleC );
10
11
12
13
var buckets = frames
. GroupBy (f => f. Timestamp . Second )
. Select (g => new { Second = g.Key , MeanRpm = g. Average (x => x. RpmHall )
});
15
Chapter 9
Asynchronous and Concurrent Programming
9.1 Why this matters for you specifically
A telemetry logger must read from a serial port, write to disk, and possibly serve a live dashboard
– all at once, without blocking. This is exactly what async/await and the Task Parallel Library
are for.
9.2 async/await
1
2
3
4
5
public async Task < TelemetryFrame > ReadFrameAsync ( SerialPort port)
{
string line = await Task.Run (() => port. ReadLine ());
return ParseTelemetryPacket (line);
}
6
7
8
9
10
11
12
13
14
15
public async Task LogSessionAsync ( SerialPort port , string path)
{
await using var writer = new StreamWriter (path);
while ( isRunning )
{
var frame = await ReadFrameAsync (port);
await writer . WriteLineAsync ( SerializeCsv ( frame ));
}
}
Note
async/await does not create a new thread by itself – it frees the calling thread while an I/O
operation is pending. For CPU-bound work (e.g., FFT on vibration data), use Task.Run or
Parallel.For; for I/O-bound work (serial ports, network, disk), use async/await directly.
9.3 Thread-safety essentials
1
2
private readonly object _lock = new ();
private readonly ConcurrentQueue < TelemetryFrame > _buffer = new ();
3
4
void Enqueue ( TelemetryFrame f) => _buffer . Enqueue (f); // lock −free , thread −
safe
5
6
lock (_lock )
16
C# & .NET for Robotics CHAPTER 9. ASYNCHRONOUS AND CONCURRENT PROGRAMMING
7
{
sharedCounter ++; // classic mutual exclusion for shared mutable state
8
9
}
17
Chapter 10
Memory, Performance, and Advanced
Language Features
10.1 Garbage collection, in brief
The CLR’s GC automatically reclaims heap memory. It is generational (Gen 0/1/2) and, by
default, non-deterministic in timing – meaning you cannot guarantee when a collection happens.
Note
Honest caveat for real-time control: standard .NET is not a hard real-time environment. GC pauses (typically sub-millisecond to a few ms) make it unsuitable for microsecondlevel control loops. That is exactly why your low-level engine/motor control stays on Arduino/ESP32 (C/C++, no GC), while C#/.NET is the right layer for supervisory control,
data logging, dashboards, and simulation – not the innermost control loop.
10.2 Span<T> and low-allocation code
1
2
3
4
ReadOnlySpan <char > packet = "RPM :4500 , TEMP :87.6 , THR :32". AsSpan ();
int rpmStart = packet . IndexOf ("RPM:") + 4;
ReadOnlySpan <char > rpmSlice = packet . Slice(rpmStart , 4);
double rpm = double .Parse ( rpmSlice );
10.3 Nullable reference types (C# 8+)
1
2
3
4
5
6
# nullable enable
string ? maybeNull = TryReadLine ();
if ( maybeNull is not null)
{
Console . WriteLine ( maybeNull . Length );
}
10.4 Pattern matching
1
2
3
4
5
6
string Describe ( object o) => o switch
{
int n when n > 8000 => "over − revving ",
int n => $"rpm = {n}",
TelemetryFrame { TempThermocoupleC : > 100 } => " overheat !",
null => "no data",
18
C# & .NET
CHAPTER
for Robotics
10. MEMORY, PERFORMANCE, AND ADVANCED LANGUAGE FEATURES
_ => " unknown "
7
8
};
10.5 Unsafe code, pointers, and stackalloc
1
2
3
4
5
unsafe
{
int∗ p = stackalloc int [10];
for (int i = 0; i < 10; i++) p[i] = i ∗ i;
}
Requires <AllowUnsafeBlocks>true</AllowUnsafeBlocks> in the .csproj. Rarely needed outside native interop or extreme performance work.
10.6 Attributes and reflection
1
2
3
4
5
6
[ Serializable ]
public class CalibrationProfile
{
[ Range (0, 10000) ]
public double MaxRpm { get; set; }
}
7
8
9
10
Type t = typeof ( CalibrationProfile );
foreach (var prop in t. GetProperties ())
Console . WriteLine (prop.Name);
Reflection underlies serialization, dependency injection, and ORMs (Entity Framework) – you
rarely write raw reflection code, but understanding it demystifies how those frameworks work.
19
Chapter 11
Modern C# Language Features by Version
Version
Ships with
Headline features
C# 8
.NET Core 3
C# 9
C# 10
C# 11
C# 12
.NET 5
.NET 6
.NET 7
.NET 8
C# 13
C# 14
.NET 9
.NET
10
(current
LTS)
.NET
11
(preview)
Nullable reference types, switch expressions, async
streams, ranges/indices
Records, init-only setters, top-level statements
File-scoped namespaces, global using, record structs
Raw string literals, generic math, required members
Primary constructors, collection expressions, default
lambda params
params collections, new lock type, partial properties
Extension members, the field keyword in properties
C# 15
1
2
3
4
5
6
Further pointer/unsafe relaxations, union types – not stable yet
// Primary constructors (C# 12)
public class Motor ( string id , double setpoint )
{
public string Id { get; } = id;
public double Setpoint { get; set; } = setpoint ;
}
7
8
9
// Collection expressions (C# 12)
double [] gains = [1.0 , 0.5 , 0.25];
10
11
12
13
14
15
16
17
// Raw string literals (C# 11)
string json = """
{
" sensor ": " thermocouple ",
"unit": "C"
}
""";
Note
Practical guidance: build against .NET 10 (LTS) with default C# 14. Don’t chase preview
features (C# 15/.NET 11) for real projects like TorqueX telemetry – LTS gives three years
of stability, which matters more than early access to new syntax.
20
Part III
C# in Robotics and Backend
Systems
21
Chapter 12
Where C# Actually Fits in a Robotics
Stack
12.1 An honest map of the ecosystem
It is important to be precise here, since your guide told you to learn C# and ROS: ROS and ROS
2’s official client libraries are rclpy (Python) and rclcpp (C++). There is no official
first-party C# client library. C# enters the robotics world through specific, well-established side
doors:
Role
Simulation
twin
How C# is used
&
digital
ROS bridge (WebSocket/JSON)
Native ROS 2 bindings
Backend / fleet management
Embedded Linux GPIO
Firmware
trollers)
(microcon-
Unity (C# scripting) is the dominant non-ROS-native
simulation engine used alongside ROS, via bridges (below)
rosbridge_suite runs on the ROS side; any C#/.NET
app (including Unity) talks to it over WebSockets using
ROS# (ros-sharp)
ros2cs / ros2-for-unity – community (not core-team)
C# bindings to the ROS 2 client library, giving real ROS
2 nodes in C#
ASP.NET Core Web APIs, SignalR, gRPC – dashboards,
data pipelines, remote monitoring, config management
System.Device.Gpio + Iot.Device.Bindings – .NET
running directly on a Raspberry Pi, talking to GPIO/I2C/SPI
Not C#’s domain – ESP32/Arduino stay in C/C++ (or
MicroPython); .NET does not run on these tiny MCUs
Key Idea
Given your stack (Arduino/ESP32 in C++, Python already known, TorqueX telemetry,
EcoSynapse’s Fusion 360 mechanical design), the most natural role for C# is the supervisory and simulation layer: a Unity-based digital twin of the SEM chassis or the MiniMRF
auger, a backend telemetry service that ingests ESP32 serial/MQTT data and serves a live
dashboard, or a desktop calibration/diagnostics tool. It is not going to replace your ESP32
firmware or your ROS nodes written in Python/C++ – it complements them.
22
Chapter 13
Serial and Embedded Communication
(ESP32/Arduino ↔ C#)
13.1 Reading a serial stream
1
using System .IO. Ports;
2
3
4
5
6
7
8
9
10
var port = new SerialPort ("COM5", 115200 , Parity .None , 8, StopBits .One);
port.Open ();
port. DataReceived += (sender , e) =>
{
string line = port. ReadLine ();
var frame = ParseTelemetryPacket (line);
FrameReceived ?. Invoke ( frame );
};
13.2 On Linux (e.g.
differs
1
a Raspberry Pi backend) the port name
var port = new SerialPort ("/dev/ ttyUSB0 ", 115200) ;
Note
Pair this with a simple framing protocol on the ESP32 side (e.g., comma-separated values
terminated by \n, or a compact binary packet with a checksum). Keep the C# side purely
a consumer of a well-defined protocol – don’t let parsing logic leak business logic from the
firmware.
23
Chapter 14
ROS Integration Paths for C#
14.1 Path 1: rosbridge_suite + ROS# (WebSocket/JSON)
Run rosbridge_server on your ROS machine; any C# app (desktop, Unity) connects over a
WebSocket and exchanges JSON-encoded ROS messages using the ROS# (ros-sharp) library.
Simple, works with ROS1 and ROS2, but has JSON serialization overhead – fine for dashboards
and teleoperation, not for high-frequency sensor streams.
1
2
3
// Conceptual usage pattern with ROS# in a Unity /. NET app
RosSocket rosSocket = new RosSocket (
new RosBridgeClient . Protocols . WebSocketNetProtocol ("ws
://192.168.1.50:9090 "));
4
5
6
rosSocket .Subscribe < std_msgs .String >("/ torquex / status ",
message => Debug .Log( message .data));
7
8
rosSocket .Advertise < geometry_msgs .Twist >("/ cmd_vel ");
14.2 Path 2: Unity Robotics Hub (ROS-TCP-Connector)
Unity’s official robotics tooling: a TCP-based connector (faster than the JSON bridge, since it
uses ROS’s own serialization), a URDF importer to bring your SolidWorks/Fusion 360 chassis
model into Unity as a physically simulated robot, and message-generation tooling that turns
.msg/.srv files into C# classes automatically.
Robotics Application
For TorqueX’s SEM chassis (already modelled in SOLIDWORKS Weldments) or EcoSynapse’s MiniMRF (modelled in Fusion 360), exporting to URDF and importing into Unity
via the Robotics Hub gives you a visual, physics-accurate digital twin you can drive with the
same commands your real ROS nodes would send – useful for testing control logic before it
touches real hardware.
14.3 Path 3: ros2cs / ros2-for-unity – native ROS 2 nodes in C#
A community-maintained (not core ROS team) C# binding directly to the ROS 2 client library
(rcl). This lets a C# process act as a genuine ROS 2 node – publishing, subscribing, and calling
services – without going through a bridge.
1
using ROS2;
2
3
4
var context = new Context ();
var node = context . CreateNode (" torquex_status_node ");
24
C# & .NET for Robotics
5
CHAPTER 14. ROS INTEGRATION PATHS FOR C#
var publisher = node. CreatePublisher < std_msgs .msg.String >("/ torquex / status "
);
6
7
8
var msg = new std_msgs .msg. String { Data = " engine_nominal " };
publisher . Publish (msg);
Note
Because these are community bindings rather than an officially supported ROS client library,
expect rougher edges (platform-specific build steps, version-matching requirements with your
ROS 2 distro) compared to rclpy/rclcpp. For a class project or team tool this is entirely
workable; for anything mission-critical, Python/C++ remain the safer default for the ROSfacing nodes themselves.
25
Chapter 15
Backend and Telemetry Systems
15.1 ASP.NET Core Web API – a telemetry ingestion service
1
2
var builder = WebApplication . CreateBuilder (args);
var app = builder . Build ();
3
4
5
6
7
8
app. MapPost ("/ telemetry ", ( TelemetryFrame frame ) =>
{
TelemetryStore .Save( frame );
return Results .Ok ();
});
9
10
app. MapGet ("/ telemetry / latest ", () => TelemetryStore . GetLatest (100) );
11
12
app.Run ();
15.2 SignalR – real-time push to a live dashboard
SignalR gives you WebSocket-based real-time updates without manually managing sockets – ideal
for a live ”engine dashboard” showing RPM/temperature as it streams in.
1
2
3
4
5
6
7
public class TelemetryHub : Hub
{
public async Task BroadcastFrame ( TelemetryFrame frame )
{
await Clients .All. SendAsync (" ReceiveFrame ", frame );
}
}
15.3 gRPC – efficient service-to-service communication
When your telemetry service, a simulation service, and a dashboard need to talk to each other
with strongly-typed, high-throughput calls (rather than loose JSON REST), gRPC (built on
Protocol Buffers) is the .NET-native answer, conceptually similar to ROS 2’s own DDS-based
service model.
15.4 Persisting session data
1
2
3
public class TelemetryDbContext : DbContext
{
public DbSet < TelemetryFrame > Frames { get; set; }
26
C# & .NET for Robotics
protected override void OnConfiguring ( DbContextOptionsBuilder options )
=>
options . UseSqlite ("Data Source = telemetry .db");
4
5
6
CHAPTER 15. BACKEND AND TELEMETRY SYSTEMS
}
Entity Framework Core is .NET’s ORM (object-relational mapper) – it turns C# classes into
database tables and back, similar in spirit to SQLAlchemy in Python.
27
Chapter 16
.NET on Embedded Linux (Raspberry
Pi–class boards)
If a future project (e.g., a MiniMRF controller or a mobile robot’s onboard computer) runs Linux
on something like a Raspberry Pi, .NET runs natively there too, with direct hardware access
libraries:
1
using System . Device .Gpio;
2
3
4
5
using var controller = new GpioController ();
controller . OpenPin (17, PinMode . Output );
controller . Write (17 , PinValue .High); // drive a relay / solenoid
6
7
8
using var i2c = I2cDevice . Create (new I2cConnectionSettings ( busId : 1,
deviceAddress : 0x68));
// e.g., talk to an IMU or RTC over I2C
Note
This is a genuine alternative path to Python’s RPi.GPIO/smbus for a Pi-based controller, if
you ever want the onboard supervisory logic (as opposed to the ESP32’s real-time firmware)
written in C# for consistency with a larger .NET backend.
28
Part IV
Advancing Further
29
Chapter 17
Interop and Performance for HardwareAdjacent Work
17.1 P/Invoke – calling native C libraries from C#
When a vendor only ships a C driver (common for specific sensor/motor-controller SDKs), P/Invoke lets C# call into it directly.
1
using System . Runtime . InteropServices ;
2
3
4
[ DllImport (" libmotorcontroller .so")]
private static extern int mc_set_speed (int channel , double rpm);
5
6
mc_set_speed (0, 4500.0) ;
17.2 Server GC vs Workstation GC
For a long-running backend/dashboard service, enabling Server GC in the project file:
1
<ServerGarbageCollection >true </ ServerGarbageCollection >
improves throughput on multi-core machines at the cost of slightly higher memory use – generally
the right choice for a telemetry ingestion service, while Workstation GC (the default) suits desktop
tools.
30
Chapter 18
Testing and Project Hygiene
1
2
3
4
5
6
7
8
9
public class TelemetryParserTests
{
[Fact]
public void ParsesValidPacket ()
{
var frame = TelemetryParser . Parse ("RPM :4500 , TEMP :87.6 , THR :32");
Assert . Equal (4500 , frame . RpmHall );
}
}
Run with dotnet test. Building the habit of a small xUnit suite around your packet parser and
calibration math pays off quickly once the telemetry protocol evolves mid-season.
31
Chapter 19
A Suggested Learning Roadmap
Stage
Focus
1.
Core language
Syntax, OOP, collections, LINQ, exceptions – build 3–4
small console tools (a unit converter, a CSV log parser, a
simple calibration calculator)
Serial port reading from an actual ESP32, write a CLI
telemetry logger to CSV
Turn the logger into an ASP.NET Core Web API + SignalR live dashboard
Set up rosbridge_suite on a ROS 2 machine/VM, connect via ROS# from a small C# console app; subscribe
to a topic, publish a command
Import a URDF (start with a simple robot, then your
own SEM/MiniMRF model) into Unity via Robotics Hub;
drive it via the ROS-TCP-Connector
Try ros2cs to write an actual ROS 2 publisher/subscriber
node in C#, alongside your existing Python/C++ nodes
Add EF Core persistence, basic tests, and package the
tool as a proper multi-project solution
2. Async & I/O
3. Backend basics
4. ROS bridge
5. Unity + digital twin
6. Native ROS
2 node
7. Polish
Note
Given your parallel ROS instruction from your guide, it’s worth doing ROS’s own
Python/C++ tutorials first (they are what the official docs and most course material assume), then layering C# in as the simulation/backend/dashboard layer once you’re comfortable with core ROS concepts (nodes, topics, services, actions). Trying to learn ROS through
C# bindings first will make the official documentation harder to follow, since almost all of it
is Python/C++.
32
Chapter 20
Reference Resources
• Official C# docs & tour of the language: https://learn.microsoft.com/en-us/
dotnet/csharp/
• .NET docs (runtime, BCL, ASP.NET Core, EF Core): https://learn.microsoft.
com/en-us/dotnet/
• Unity Robotics Hub (ROS-TCP-Connector, URDF Importer): https://github.
com/Unity-Technologies/Unity-Robotics-Hub
• ROS# (ros-sharp): https://github.com/siemens/ros-sharp
• ros2cs (native ROS 2 C# bindings): https://github.com/RobotecAI/ros2cs
• .NET IoT libraries (GPIO/I2C/SPI on Linux SBCs): https://github.com/dotnet/
iot
• Official ROS 2 documentation (Python/C++ – do this in parallel): https://docs.
ros.org/
33
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )