morzel.net

.net, js, html, arduino, java... no rants or clickbaits.

[OoB] Moving webcam with joystick and servos (Arduino/SharpDX/WinForms)

Time for the third episode of "Out of Boredom" series :) There was a Sonar project and something about shooting paintballs... This time you will learn how to use Arduino and .NET 4.5 to receive input from joystick and use it to control servos that move a webcam horizontally and vertically!

  • Here you can see a video of the project outcome: Vimeo
  • This repository contains all the code: GitHub

Controlling servos with joystick... Click to enlarge...

How it works? Short version: joystick (in my case Logitech Extreme 3D Pro) is connected to laptop with Windows 7 via USB. Desktop application (.NET 4.5/WinForms) uses SharpDX library (managed DirectX API) to pull position information from joystick. This info is presented in numerical and graphical way on UI (C# 5.0 async helps here). Joy position is then translated into desired pan&tilt servo angles and that data is sent to Arduino Uno through serial port. Arduino receives the data, and thanks to its Servo library, commands servos to move... 

Longer description is split into 2 parts. The first describes desktop app, the second shows Arduino sketch...

 

1. Desktop application

ServoJoy WinForms application... Click to enlarge...

Main task of the program is to read joystick position. This is easy thanks to SharpDX 2.6.2.0 library that wraps DirectX API so it can be conveniently operated with C#. SharpDX and SharpDX.DirectInput NuGet packages are used in the app. This is the class that contains all the code necessary to monitor joystick movements:

using SharpDX.DirectInput;
using System;
using System.Linq;
using System.Threading;

namespace ServoJoyApp
{
    public class JoystickMonitor
    {
        private string _joystickName;

        public JoystickMonitor(string joystickName)
        {
            _joystickName = joystickName;
        }

        public void PollJoystick(IProgress<JoystickUpdate> progress, CancellationToken cancellationToken)
        {
            var directInput = new DirectInput();
            
            DeviceInstance device = directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AttachedOnly)
                                        .SingleOrDefault(x => x.ProductName == _joystickName);

            if (device == null)
            {
                throw new Exception(string.Format("No joystick with \"{0}\" name found!", _joystickName));
            }
            
            var joystick = new Joystick(directInput, device.InstanceGuid);
            
            joystick.Properties.BufferSize = 128;
            joystick.Acquire();

            while (!cancellationToken.IsCancellationRequested)
            {
                joystick.Poll();
                JoystickUpdate[] states = joystick.GetBufferedData();
                
                foreach (var state in states)
                {
                    progress.Report(state);                    
                }
            }
        }
    }
}

DirectInput class lets us obtain access to joystick. Its GetDevices method is used to look for an attached joystick with particular name. If such device is found, object of Joystick class gets created. Joystick class has Poll method that fills a buffer containing information about joysticks states. State info comes in a form of JoystickUpdate structure. Such structure can be used to determine what button was pushed or what is the current position in y-axis for example.

Here's an example of reading current joystick position on x-axis:

if (state.Offset == JoystickOffset.X)
{
      int xAxisPosition = state.Value;
}

Position is kept in Value property but before using it you have to check what that value means. This can be done by comparing Offset property to desired JoystickOffset enum value. See the docs of JoystickOffset to see what kind of values you can read.

PollJoystick method presented earlier has fallowing signature:

public void PollJoystick(IProgress<JoystickUpdate> progress, CancellationToken cancellationToken)

IProgress generic interface was introduced in .NET 4.5 to allow methods to report task progress. PollJoystick method uses it to notify the rest of the program about changes in joystick state. This is done by progress.Report(state) call. The second parameter (with CancellationToken type) lets as stop joystick polling any time we want. PollJoystick method does it when IsCancellationRequested property of CancellationToken structure is set to true. Is it necessary to use this async-related stuff to poll joystick data? No - it's possible to put joystick polling loop directly in button event handler but then all the work will be executed in UI thread and it will make application unresponsive! Here's how you can run joystick monitoring in modern C#:

private async void btnJoystickMonitorStart_Click(object sender, EventArgs e)
{
    try
    {
        btnJoystickMonitorStart.Enabled = false;
        btnJoystickMonitorStop.Enabled = true;

        var joystickMonitor = new JoystickMonitor(txtJoystickName.Text.Trim());

        _joystickMonitorCancellation = new CancellationTokenSource();
        var progress = new Progress<JoystickUpdate>(s => ProcessJoystickUpdate(s));
        await Task.Run(() => joystickMonitor.PollJoystick(progress, _joystickMonitorCancellation.Token), _joystickMonitorCancellation.Token);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Oh no :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Notice that button even handler is marked with async keyword. Before PollJoystick task is started a new cancellation token is created and ProcessJoystickUpdate is set as a handler for asynchronous task progress notifications. When this setup is done joystick monitoring task is started with await Task.Run call...

This is a part of code responsible for handling joystick state changes:

private void ProcessJoystickUpdate(JoystickUpdate state)
{
    if (state.Offset == JoystickOffset.X)
    {
        int xAxisPercent = GetAxisValuePercentage(XAxisMax, state.Value);
        pnlXAxisPercent.Width = (int)xAxisPercent;
        lblXAxisPercent.Text = xAxisPercent + "%";
        lblXAxisValue.Text = state.Value.ToString();

        if (rbPanOnXAxis.Checked)
        {
            _panServoPosition = MapAxisValueToPanServoPosition(state.Value, XAxisMax);         
            lblPanServoPosition.Text = _panServoPosition.ToString();
        }
    }
	
	// ... more ...

As you can see JoysticUpdate structure is used to determine current position in x-axis. UI elements are updated and desired servo position is calculated... If you've looked carefully you might be wondering why if (rbPanOnXAxis.Checked) exists. This is done because the app lets its users decide whether pan (horizontal movement) servo should be bound to x-axis (right-left stick movement) or to zRotation-axis (controlled by twisting wrist on joystick - not all joysticks have this feature).

private byte MapAxisValueToPanServoPosition(double axisValue, double axisMax)
{            
    byte servoValue = (byte)Math.Round((axisValue / axisMax) * (PanServoMax - PanServoMin) + PanServoMin);
    return chkPanInvert.Checked ? (byte)(PanServoMax - servoValue) : servoValue;
}

Values of my joystick position are reported in 0 to 65535 range but only numbers from 0 to 180 are meaningful for servos I've used. That's why method MapAxisValueToPanServoPosition presented above was created...

Ok, so we are done with detecting joystick movements! Now we need to send desired servo position to Arduino. Fortunately this is really simple thanks to SerialPort component that you can use in WinForms programs. Just drag this component from toolbox and use code as below to control connection with Arduino (spArduino is the name I've given to SerialPort component):

private void btnArduinoConnectionToggle_Click(object sender, EventArgs e)
{
    try
    {
        if (spArduino.IsOpen)
        {
            spArduino.Close();

            btnArduinoConnectionToggle.Text = "Connect";
        }
        else
        {
            spArduino.BaudRate = (int)nudBaudRate.Value;
            spArduino.PortName = txtPortName.Text;

            spArduino.Open();
            btnArduinoConnectionToggle.Text = "Disconnect";
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Oh no :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

In my case Arduino is accessible via COM3 port and baud rate of 9600 is sufficient. That's right - despite the fact that the device is connected to PC with USB cable it is accessible via COM port.

Sending servo position to Arduino is really easy:

private void tmServoPosition_Tick(object sender, EventArgs e)
{
    if (spArduino.IsOpen)
    {
        spArduino.Write(new byte[] { _panServoPosition, _tiltServoPosition, SerialPackagesSeparator }, 0, 3);
    }
}

spArduino.Write call is used to send an array of three bytes to Arduino. Two values are for requested servo potions and the last one is used to separate the pairs so servo controlling program can always distinguish between pan and tilt values. Writing over serial port is executed inside Tick method of Timer component. This time I didn't bothered with manual creation of background task. I just dragged Timer component and adjusted its Enabled and Interval properties to make the app communicate with Arduino every 10 milliseconds...

 

2. Arduino sketch

We've discussed an program that can detect joystick movements and send servo position requests over serial port. Now time for a piece of microcontroller software that can actually force servos to move. This is the whole code:

#include <Servo.h>  

const byte setupReadyLedPin = 8;
const byte panServoPin = 10;
const byte tiltServoPin = 12;
const byte separator = 255;

Servo panServo; 
Servo tiltServo; 

void setup() {  
    pinMode(setupReadyLedPin, OUTPUT);
         
    panServo.attach(panServoPin);   
    tiltServo.attach(tiltServoPin);   
    
    Serial.begin(9600); // Open connection with PC
    
    digitalWrite(setupReadyLedPin, HIGH);
}

void loop() {      
    if (Serial.available() > 2) {            
        byte panAngle = Serial.read();
        byte tiltAngle = Serial.read();
        byte thirdByte = Serial.read();
         
        if (panAngle != separator && tiltAngle != separator && thirdByte == separator) {         
            // Moving servos
            panServo.write(panAngle);
            tiltServo.write(tiltAngle);
        }
    }       
}

Yup, it's that easy! Servo library is included to allow for panServo and tiltServo objects to be created. These objects (of type Servo) make it possible to command servos to move into desired positions. This is done by calling write method with desired angle, like this:

panServo.write(panAngle);

Before it can be done however, servos have to be assigned to Arduino's output pins. This is achieved by calls to attach method seen in setup function. Digital servos are controlled by duration of ON pulse calculated in 20ms intervals, 1.5ms ON pulse should command a servo to move to the middle... But Servo library does all the heavy lifting for you so you don't have to create proper control signals manually. All you need to do is connect 3 cables each servo has. The servos I own use brown cable for ground, red for plus and orange for control signal. Sonar project used single micro servo so the only power supply needed was the one included in USB. This time two servos are utilized so you should add external power supply. I've connected 1A AC/DC power adapter through a plug that is included on Arduino Uno board and the servos worked really well. Arduino has a built-in fuse that protects USB port from overcurrent (it's a resettable fuse that doesn't allow current bigger than 500mA)...

Communication with .NET application is implemented with Serial class. First, in setup function, a connection is established with Serial.begin(9600) call. Then inside a loop Serial.available method is used to check if packet with servo positions request has arrived from PC. If so, pan&tilt servo angles are read and servos are ordered to move.

 

This is all that is necessary to control two servos with joystick connected to a computer :) In my project I've used DGServo S3003 servos with pan&tilt bracket to move A4tech PK-910H webcam and I'm really happy with the results! While watching the clip you may think that camera is moving to much compared to joystick movements. Keep in mind however, that servos move in 180 degrees but my joystick operates in smaller range. This is why small stick movement results in big camera swing. Despite that, I was able to control servos position with 1-degree precision quite easily... 

Update 2015-01-11: Click here to see a video of my recent project that has pan and tilt servos used to build gun turret!

[OoB] Shooting paintball marker with relay, Arduino and .NET WinForms

My first Arduino based project was Sonar with C#, JS and HTML5. Now I continue the "Out of Boredom" series with a setup that allows firing a paintball marker (gun) with a command sent from a computer :) This time software stack is simpler - just a small Arduino sketch and uncomplicated WinForms (.NET/C#) application, but hardware requires a bit of electronics knowledge. Don't worry though, nothing too complicated - I'm definitely not an expert in this field... 

Controlling paintball marker with laptop... Click to enlarge...

The project is based around an electromechanical relay. Such relay is basically an electronically controlled switch that allows you to turn powerful devices on and off by sending a signal from Arduino's output pins. You can control very large motors or light bulbs for example. The beauty of this components is the fact that you can govern external circuits - there is no physical link between your control circuit and the thing you want to turn on/off. You can use a relay for devices that require huge current but you can also control more subtle equipment, and that's what I decided to do. I play paintball/speedball and I happen to own high-end electro-pneumatic marker called DM13. Such marker has electronically operated trigger and uses solenoid valve to shoot... I thought: "Wouldn't it be cool to press enter on my laptop and make this gun fire nearly 20 balls per second?"... See this video to see how it worked :)

This is a list of hardware parts used:

Element Role
Arduino Uno R3 Controlling trigger via relay and communicating with PC
JZC-11F 005-IZ SPDT relay Simulating trigger pull by closing trigger circuit
P2N2222AG NPN transistor Supplying current to operate relay 
1.2k Ohm resistor Limiting transistor base current
Green LED Signalling ready state
Red LED Signalling firing 
2x 330 Ohm resistor Limiting current going through diodes
Piezo buzzer Signalling read/fire with different tones
SPST switch Turning buzzer on/off
Breadboard and jumper wires or universal board Connecting components 

And this is the circuit diagram:

Circuit diagram... Click to enlarge...

LED connected to Pin 13 is used to signal that device is ready, LED attached to Pin 12 indicates firing (closed trigger circuit). LEDs are of course not connected to Arduino directly, there are resistors protecting them from overcurrent. Buzzer is there to make one tone when device is ready and another (higher) tone when device is firing. The purpose of a switch is to make your life easier while testing. Buzzer sound can get annoying quickly so you can turn it off...

These are the boring bits, the more interesting stuff is on the left side of the diagram. Pin 2 is connected (via resistor) to a base of NPN transistor and collector is attached to relay coil. The transistor is needed because the coil, which controls the switching function, needs more power then Arduino output pins can supply. In this circuit transistor is used not as amplifier but as a switch. When Pin 2 is set to HIGH (base-emitter voltage = 5V) the transistor reaches its fully-on state and current flows through collector energizing the coil. Putting HIGH state on Pin 2 results in a connection between relay's COM (Common) and NO (Normally Open) pins. I've checked my marker with a multimeter (in resistance mode) and I was able to see that pulling the trigger resulted in a closed circuit between middle and lower pins of the trigger switch. Attaching one cable between middle pin of the switch and COM pin, and another cable between lower switch pin and Normally Open pin gives the ability to simulate trigger pull. In other words: HIGH state on Arduino's Pin 2 equals trigger pull as far as maker is concerned*. It's quite simple but as you've seen on the video it works really well! One more thing: look on the diagram on the right-hand side of JZC-11F relay - there's a signal diode and it's purpose is to protect the transistor from voltage spikes that appear when Pin 2 to is put to LOW state (when supply voltage is removed from relay's coil). Such diode usage is called "flyback" or "freewheeling"... I first created this circuit on a breadboard and then soldered it on a universal board... Keep in mind that there are multiple Arduino relay shields available so you don't really have to create such transistor based circuit yourself. I did it because I think that it's a nice way to refresh some very basic electronics knowledge. Ok, we are done with hardware!

Now time for software (this GitHub repository contains all the code)!

Here's complete Arduino sketch:

const byte fireRelayPin = 2;
const byte fireBuzzerPin = 11;
const byte fireLedPin = 12;
const byte readyLedPin = 13;

const byte readyToFireMessage = 6; // ASCII ACK
const byte fireCommand = 70; // ASCII F
const byte triggerPullDelayInMs = 30;
const byte fireBuzzerHz = 1000;
const byte readyBuzzerHz = 400;

void setup() {  
    pinMode(fireRelayPin, OUTPUT);
    pinMode(fireBuzzerPin, OUTPUT);
    pinMode(fireLedPin, OUTPUT);
    pinMode(readyLedPin, OUTPUT);
  
    Serial.begin(9600);
    
    tone(fireBuzzerPin, readyBuzzerHz);  
    digitalWrite(readyLedPin, HIGH); 

    Serial.write(readyToFireMessage);     
}

void loop() {
   if (Serial.available()) {
        byte data = Serial.read(); 
        
        if (data == fireCommand) {
            pullTrigger();
            delay(triggerPullDelayInMs);  
            releaseTrigger();
        }
    }  
}

void pullTrigger() {
    digitalWrite(fireLedPin, HIGH);    
    digitalWrite(fireRelayPin, HIGH);  
    tone(fireBuzzerPin, fireBuzzerHz);     
}

void releaseTrigger() {
    digitalWrite(fireLedPin, LOW);    
    digitalWrite(fireRelayPin, LOW);  
    tone(fireBuzzerPin, readyBuzzerHz);  

    Serial.write(readyToFireMessage);     
}

Nothing complicated :) First, the usual (good) practice of creating constants for pin numbers and other useful values. Then there is a setup method that configures pin modes, initializes serial connection (used to communicate with PC), and turns on the "ready" LED. There is also a call to tune function. tune is used to generate sound by making piezoelectric element in buzzer vibrate at a designated frequency. In loop function program waits for data sent by PC via serial port and checks if this data means a fire command (ASCII letter 'F'). If so, a trigger pull is simulated followed by a slight delay and trigger release. The digitalWrite(fireRelayPin, HIGH); line is what makes paintball gun fire. If you've looked carefully ,you've noticed Serial.write(readyToFireMessage); calls in setup and releaseTrigger functions. These exist to let computer know that Arduino is ready to receive first fire command or is ready to process a new one.

This is the .NET 4.5 WinForms application build to control paintball marker:

Command application... Click to enlarge...

Pressing "Fire" button once makes the marker fire one shot, pressing and holding it makes it fire in a series. How many balls per second will be fired depends of course on triggerPullDelayInMs value, relay speed, marker settings (mine DM13 was setup in semi mode capped to around 20bps), paintball loader speed etc. Shooting is only possible when "Device is ready to fire" shows "YES" and "Safe" checkbox is not ticked.

Here's the code behind the CommandWindow show above (few particularity dull lines removed):

using System;
using System.Drawing;
using System.IO.Ports;
using System.Threading;
using System.Windows.Forms;

namespace PbFireApp
{
    public partial class CommandWindow : Form
    {
        private const byte ReadyToFireMessage = 6; // ASCII ACK
        private const byte FireCommand = 70; // ASCII F

        private bool _isReadyToFire;
        private bool IsReadyToFire
        {
            get
            {
                return _isReadyToFire;
            }
            set
            {
                _isReadyToFire = value;
                 SetIsReadyToFireLabel();
            }
        }

        public CommandWindow()
        {
            InitializeComponent();
            
            IsReadyToFire = false;
            spArduino.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        }

        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            byte data = (byte)spArduino.ReadByte();
            IsReadyToFire = data == ReadyToFireMessage;          
        }

        private void Fire()
        {
            if (!chkSafe.Checked && IsReadyToFire)
            {
                IsReadyToFire = false;
                spArduino.Write(new byte[] { FireCommand }, 0, 1);
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                if (spArduino.IsOpen)
                {
                    spArduino.Close();

                    btnConnect.Text = "Connect";                
                    gbFire.Enabled = false;
                }
                else
                {
                    spArduino.BaudRate = (int)nudBaudRate.Value;
                    spArduino.PortName = txtPortName.Text;

                    spArduino.Open();

                    btnConnect.Text = "Disconnect";            
                    gbFire.Enabled = true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Oh no :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
		
        // ... more ...

        delegate void SetIsReadyToFireLabelCallback();

        private void SetIsReadyToFireLabel()
        {
            if (lblIsReadyToFire.InvokeRequired)
            {
                SetIsReadyToFireLabelCallback d = new SetIsReadyToFireLabelCallback(SetIsReadyToFireLabel);
                Invoke(d, new object[] { });
            }
            else
            {
                lblIsReadyToFire.Text = IsReadyToFire ? "YES" : "NO";
                lblIsReadyToFire.ForeColor = IsReadyToFire ? Color.Orange : Color.Black;
            }
        }
    }
}

The app is so simple that I decided to put all the code in to form's cs file... SerialPort component is used to communicate with Arduino (I've written a bit more about serial communication in Sonar project posts). Paintball marker is instructed to fire when 'F' command (short for "Fire", ASCII code 70) is sent to Arduino. This line is responsible for it: 

spArduino.Write(new byte[] { FireCommand }, 0, 1);

DataReceivedHandler method is used to set IsReadyToFire property to true when ACK (Acknowledge, ASCII 6) message is obtained from Arduino...

The code is quite obvious except for the SetIsReadyToFireLabel method. Why it is needed? The color and text of a Label control are changed when IsReadyToFire property is set. And that can happen when DataReceivedHandler is executed. We can't directly change UI elements from SerialPort.DataReceived event handler because this will result in an InvalidOperationException with a message such as this: "Cross-thread operation not valid: Control 'lblIsReadyToFire' accessed from a thread other than the thread it was created on.".

And that's it, second "Out of Boredom" project is complete :) 

1. It should be possible to control solenoid valve directly but that would be more complicated and might result in damaging my precious DM13.…

[OoB] Sonar with Arduino, C#, JavaScript and HTML5 (Part 2)

Part 1 described the general idea behind Sonar project, hardware components used and Arduino sketch... This second post in "Out of Boredom" series is about C# and JavaScript programs that make it possible to display ultrasonic range sensor data in web browsers. The role of .NET application is to receive messages from Arduino over serial port and broadcast it to clients using SignalR library. JS/HTML5 clients use jquery.signalR lib to obtain information about servo position with distance to obstacles and use this data to render sonar image on canvas:

Sonar client image... Click to enlarge...

These links are in previous post, but just to remind you:

 

1. SonarServer 

SonarServer is a .NET 4.5 console app created in Visual Studio Express 2013 for Windows Desktop. It uses Microsoft.AspNet.SignalR.SelfHost and Microsoft.Owin.Cors NuGet packages to create self-hosted SignalR server. ASP.NET SignalR is a library designed to make it easy to create applications that are able to push data to clients running in web browsers. This is in contrast to normal web pages/apps behavior where the client (browser) asks server for action by issuing a request (such as GET or POST). SignalR allows clients to listen for messages send by a server... If possible SignalR will use WebSockets to enable efficient bi-directorial connection. If that option is not available due to either browser or server limitations, it will automatically switch to other push techniques like long polling or Server-Sent Events. When I tested the code on my laptop with Windows 7 Home Premium SP1, long polling was used on IE 11 and SSE in Chrome 37. Server was sending about 20 messages per second and clients didn't have any problems with handling that load (communication was on localhost). Self-hosting means that SignalR server doesn't have to be run on a web server such as IIS - it can exist in plain old console project! If you are completely new to SignalR check this tutorial... 

This is SonarServer project structure:

SonarServer solution...

SonarData.cs file contains such struct:

namespace SonarServer
{
    public struct SonarData
    {
        public byte Angle { get; set; }
        public byte Distance { get; set; }
    }
}

Server will send a list of such objects to clients.

SonarHub.cs contains a class derived form Hub. It doesn't declare any methods but is nonetheless useful. Library will use it to generate JavaScript proxy objects...

using Microsoft.AspNet.SignalR;

namespace SonarServer
{
    public class SonarHub : Hub
    {

    }
}

Startup.cs file looks like this:

using Microsoft.Owin.Cors;
using Owin;

namespace SonarServer
{
    class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);
            app.MapSignalR();
        }
    }
}

It configures the SignalR server, letting it support cross-domain connection thanks to UseCors call.

Below are the most important bits of Program.cs file. This code:

using (SerialPort sp = new SerialPort())
 {
     sp.PortName = "COM3";
     sp.ReceivedBytesThreshold = 3;
     sp.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

     sp.Open();

     Console.WriteLine("Serial port opened!");

     using (WebApp.Start<Startup>("http://localhost:8080/"))
     {
         Console.WriteLine("Server running!");
         Console.ReadKey();
     }
 }

is responsible for opening a connection with Arduino over serial port named "COM3" and starting SignalR server on localhost:8080ReceivedBytesThreshold property allows us to control the amount of bytes received from Arduino before DataReceivedHandler is called. You can increase this value if you want bigger packages of data to be broadcasted by server and rendered by clients. This is the part of DataReceivedHandler method that loads serial port data into a byte array:

int count = sp.BytesToRead;
[] data = new byte[count];
sp.Read(data, 0, count);

Such array of bytes is latter on added to custom buffer and processed to create a list of SonarData objects sent to SignalR clients. Part 1 mentioned that Arduino sends data to PC in bytes (array) packages containing three elements: [255, angle, distance]. The purpose of special 255 value is to separate angle-distance pairs of values which are used to create sonar image. We can't just send [angle, distance] stream from Arduino to PC, because the Server could easily loose track of which value is angle and which is distance. This might happen due to delays, buffering etc. Sure it's not a bulletproof protocol but it work well when I tested it. Lot's not get crazy with that - it's a hobby project, remember? :) Check ProcessSonarData method in repository if you want to see how an array of bytes is turned into SonarData list (with buffering taken into account)... 

The last missing piece of SonarServer puzzle is SendSonarDataToClients method:

private static void SendSonarDataToClients(List<SonarData> sonarDataForClients)
{
    var hub = GlobalHost.ConnectionManager.GetHubContext<SonarHub>();
    hub.Clients.All.sonarData(sonarDataForClients);

    Console.WriteLine("Sonar data items sent to clients. Samples count=" + sonarDataForClients.Count);
}

This is the thing that actually broadcasts data to clients running in web browsers. You may be wondering why SonarHub instance is not created directly with new operator and instead GetHubContext method is used. This is because SignalR is responsible for its hubs life cycle. Such code:

SonarHub sonarHub = new SonarHub();
sonarHub.Clients.All.sonarData(sonarDataForClients);

would result in the exception: System.InvalidOperationException: Using a Hub instance not created by the HubPipeline is unsupported." .

 

2. SonarClient

SonarClient is the subproject responsible for drawing sonar image. It's not a Visual Studio solution - just a few files:

SonarClient files...

I've tested SonarClient code in IE 11 and Chrome 37 and it worked really well. The assumption is that you run modern browser too. I didn't bother with any feature detection, it's enough for me that I have to write for IE9 at work - well, at least it's not IE 6, huh? ;) But if you want to do such thing I can recommend Modernizr library...

This is content of index.html file (with some boring parts removed for brevity):

<!DOCTYPE html>
<html>
<head>
    <title>Sonar - sample code from morzel.net blog post</title>
    <style>
        /* more */
    </style>
</head>
<body>
    <div>
        <canvas id="sonarImage" width="410" height="210"></canvas>

        <table>
              <!-- more -->            
        </table>
    </div>

    <a href="http://morzel.net" target="_blank">morzel.net</a>

    <script src="lib/jquery-1.6.4.js"></script>
    <script src="lib/jquery.signalR-2.1.1.js"></script>
    <script src="http://localhost:8080/signalr/hubs"></script>
    <script src="sonarStats.js"></script>
    <script src="sonarImage.js"></script>
    <script src="sonarConnection.js"></script>

    <script>
        $(function () {          
            sonarImage.init('sonarImage');
            sonarConnection.init('http://localhost:8080/signalr');
        });
    </script>
</body>
</html>

Most important bit of this HTML5 markup is the canvas element used to create sonar image. The page imports jquery and jquery.signalR libraries that make it possible to communicate with the Server. That line is particularly interesting:

 <script src="http://localhost:8080/signalr/hubs"></script>

SingalR automatically creates JavaScript proxy objects for server-client messaging, that line lets us load them into page. Last three script references are for JS modules responsible for: displaying info about data received from SonarServer, rendering sonar image and communication with server, respectively. Later there's a short script which initializes the modules after pages DOM is ready.

I will skip the description of sonarStats.js file (nothing fancy there - just filling some table cells). But sonarConnection.js should be interesting for you. This is the whole content:

var sonarConnection = (function () {
    'use strict';

    var sonarHub, startTime, numberOfMessages, numberOfSamples;

    var processSonarData = function (sonarData) {
        numberOfMessages++;
        $.each(sonarData, function (index, item) {
            numberOfSamples++;
            sonarImage.draw(item.Angle, item.Distance);
            sonarStats.fillTable(item.Angle, item.Distance, startTime, numberOfMessages, numberOfSamples);
        });
    };

    return {
        init: function (url) {
            $.connection.hub.url = url;

            sonarHub = $.connection.sonarHub;

            if (sonarHub) {
                sonarHub.client.sonarData = processSonarData;
                               
                startTime = new Date();
                numberOfMessages = 0;
                numberOfSamples = 0;

                $.connection.hub.start();
            } else {
                alert('Sonar hub not found! Are you sure the server is working and URL is set correctly?');
            }
        }
    };
}());

The init method sets SignalR hub URL along with sonarData handler and starts a connection with the .NET app. There is also some very basic hub availability check (jquery.signalR library has extensive support for connection related events but lets keep things simple here). processSonarData is invoked in response to Server calling hub.Clients.All.sonarData(sonarDataForClients). The processSonarData function receives an array of objects containing information about servo angle and distance to obstacles. SignalR takes care of proper serialization/deserialization of data - you don't have to play with JSON yourself. $.each function (part of jQuery) is used to invoke sonarImage.draw and sonarStats.fillTable methods for every item in sonarData array...

And here comes the module that changes pairs of angle-distance values into o nice sonar image (whole code of sonarImage.js):

var sonarImage = (function () {
    'use strict';

    var maxDistance = 100;
    var canvas, context;

    var fadeSonarLines = function () {
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height),
            pixels = imageData.data,
            fadeStep = 1,
            green,
            fadedGreen;

        for (var i = 0; i < pixels.length; i += 4) {
            green = pixels[i + 1];

            fadedGreen = green - fadeStep;
            pixels[i + 1] = fadedGreen;
        }

        context.putImageData(imageData, 0, 0);
    };

    return {
        init: function (canvasId) {
            canvas = document.getElementById(canvasId);
            context = canvas.getContext('2d');

            context.lineWidth = 2;
            context.strokeStyle = '#00FF00';
            context.fillStyle = "#000000";

            context.fillRect(0, 0, canvas.width, canvas.height);

            context.translate(canvas.width / 2, 0);
            context.scale(2, 2);
        },

        draw: function (angle, distance) {
            context.save();

            context.rotate((90 - angle) * Math.PI / 180);
            context.beginPath();
            context.moveTo(0, 0);
            context.lineTo(0, distance || maxDistance); // Treat 0 as above range
            context.stroke();

            context.restore();

            fadeSonarLines();
        }
    };
}());

Again we have an init method. It obtains 2d drawing context from canvas and uses it to set line width and color (green). It also sets inner color (black) and fills the whole canvas with it. context.translate call is used to move origin of coordinate system to the middle of canvas (horizontally). By default it sits in upper left corner. context.scale is used to make image two times bigger than it would be drawn in default settings. Read this post if you want to know more about canvas coordinates.

The draw method is invoked for each data sample (angle-distance pair) produced by Arduino and broadcasted with SonarServer. The distance to obstacles measured by HC-SR04 sensor is represented as a line. The bigger the distance the longer the line. This happens thanks to beginPath, moveTo, lineTo, and stroke calls. context.rotate method is responsible for showing the angle in which the sensor was pointing while measuring distance (angle of servo arm). As the servo moves around we want to change the direction in which distance line is drawn. Notice that code responsible for drawing the line is surrounded by context.save and context.restore calls. These two ensure that rotation transformation doesn't accumulate between draw method calls...

fadeSonarLines function is responsible for creating the effect of older sonar lines disappearing in a nice gradual way. context.getImageData method returns an array of RGBA values representing the pixels that create current canvas image. The function loops through image data and progressively reduces the intensity of green color component. This way sonar lines fade to black.

And viola - sonar image can be rendered in a browser :)

Isn't it amazing what is now possible on the web platform? I'm not exactly a dinosaur but I remember when displaying a div overlay on a page required use of hidden iframe (because select elements were otherwise rendered above the overlay)... Crazy times ;)

[OoB] Sonar with Arduino, C#, JavaScript and HTML5

This post marks the beginning of "Out of Boredom" series. It will be about creating stuff with my recently purchased Arduino Uno. Let's have a break from chores of professional programming and create something just for fun :)

My first Arduino based project is Sonar. It utilizes ultrasonic range sensor, servo, SignalR and canvas to create sonar image:

Sonar at work... Click to enlarge...

I am splitting the description into two posts. First post will focus on hardware components and Arduino sketch and the second will be about .NET and JavaScript applications. You can get complete code in this GitHub repository. You can also click here to see short video of the whole thing working.

Here are hardware elements used:

Element Role
Arduino Uno R3 Handling HC-SR04, controlling servo and communicating with PC
HC-SR04 Ultrasonic Ranging Module Measuring time it takes sound to bounce back from objects
9g Tower Pro Micro Servo Moving sensor to get 180 degree view
Red LED Signalling ready state
330 Ohm resistor Limiting current going through diode
Breadboard and few jumper wires Connecting components without soldering 

That's it, just a few cheap components! Virtually no electronics skills are required to complete this project. I assume, however, that you have basic Arduino knowledge and you know a bit about C# and JavaScript.

Here is the software stack:

Element Role
Arduino sketch (firmware) Measuring distance, moving servo and sending data to PC over serial port 
.NET/C# 4.5 console application with SignalR library  Receiving data from Arduino using SerialPort class and broadcasting angle and distance information to clients
HTML5 page with JavaScript SignalR library Receives data from server and creates sonar image using canvas element

Above might sound a bit overwhelming but I assure you that the code is short and not that complicated.

The basic idea goes like this: HC-SR04 sensor measures time it takes an ultrasonic signal to bounce from obstacles and this gives as a chance to calculate distance to these obstacles. Position of the sensor is controlled by servo. Information about distance to objects and direction in which the sensor is pointing is sent to PC that is running console application with SignalR sever. PC receives the data and sends it to JavaScript clients that are capable of presenting sonar data in nice visual way using HTML5 canvas element...

More details!

The main component (except for the Arduino of course) is the HC-SR04 Ultrasonic Ranging Module. This sensor works by sending sound signal at 40 kHz (so above human perception limits) and detecting the echo. That's why this project is called "Sonar" and not "Radar" - it uses sound waves (not radio waves) do detect objects. The sensor should work in ranges from 2cm up to 400cm at accuracy of few millimetres. But keep in mind that the shape an material of objects might affect performance. I tested it at maximum distance of about 2 meters and was happy with the results. You can use this sensor without any libraries. That requires doing things like putting HIGH value on Trig pin for 10uS to emit ultrasonic signal, measuring duration of HIGH pulse on Echo pin and calculating distance knowing that speed of sound in the air is around 340m/s... But there's a better way: you can use NewPing lib (link) to get the distance. If you don't know how to include new library in your Arduino sketch click here.

The second important component is the servo. HC-SR04 sensor has measuring angle of about 15 degrees. But if we move it around by attaching it to servo's arm we can easily get 180 degree view. I won't get into details on how servo works and how it is controlled in this post. I plan to make another post about shooting paintball marker with Arduino+laptop and I will describe it then. For now all you need to know is that Arduino comes with Servo library which makes it very easy to move servo into desired position (angle)... I utilized 9g Tower Pro Micro Servo in this project. It's powerful enough to move the sensor yet can be powered directly from Arduino's +5V pin.

Last physical components are LED used to signal the ready state (that is when setup function had completed) with its accompanying resistor. Making a diode shine is electronics equivalent of "Hello World!" so I'm sure you know how to handle LED. Even if not, you can always use the tiny built-in LED connected to pin 13 of Arduino Uno...

This diagram shows how hardware components should be connected:

Fritzing breadboard diagram

Here's the whole code that should be uploaded to Arduino:  

#include <NewPing.h>
#include <Servo.h>  

const byte setupReadyLedPin = 8;
const byte triggerPin = 10;
const byte echoPin = 11;
const byte servoPin = 12;

const byte maxDistanceInCm = 100;

byte angle;
byte angleStep;
byte angleStepDelayInMs = 50;

NewPing sonar(triggerPin, echoPin, maxDistanceInCm); 
Servo servo; 

void setup() {  
    pinMode(setupReadyLedPin, OUTPUT);
    
    angle = 0;
    angleStep = 1;
    
    servo.attach(servoPin);   
    servo.write(angle); 
    
    Serial.begin(9600); // Open connection with PC
    
    digitalWrite(setupReadyLedPin, HIGH);
}

void loop() {      
    alterServoMoveDirection();    
    
    measureAndSendDistance();
    
    angle += angleStep;
    servo.write(angle); // Move servo
    
    delay(angleStepDelayInMs);   
}

void alterServoMoveDirection() {
    if (angle == 180) {
       angleStep = -1; 
    } else if (angle == 0) {
       angleStep = 1;
    }
}

void measureAndSendDistance() {
    byte distanceInCm = sonar.ping_cm(); // Use ultrasound to measure distance   
      
    byte sonarData[] = {255, angle, distanceInCm};
    Serial.write(sonarData, 3); // Send data to PC
}

As stated before, I assume that you know something about Arduino programming and things like const, pinMode, delay, setup and loop don't require explanation...

First lines which should capture your attention are:

#include <NewPing.h>
#include <Servo.h>  

NewPing sonar(triggerPin, echoPin, maxDistanceInCm); 
Servo servo;

Above lines let us use NewPing and Servo classes to measure distance and move the sensor. Notice also that setup function has such lines:

servo.attach(servoPin);   
servo.write(angle);

These exist to set the pin used to control the servo and to move the servo into initial position at 0 degrees.

This line:

Serial.begin(9600);

allowes Arduino to talk to PC (in my case a laptop with Windows 7) over serial port. That's right, even though Arduino Uno is connected to computer via USB cable it actually uses COM port to communicate. On my machine its called "COM3" (screen shown below comes from Device Manager - my Windows is in Polish):

COM port for Arduino... Click to enlarge...

The value passed to begin method determinates baud rate (communication speed). It's important to have the same value used in software that communicats with Arduino.

The loop function moves servo and invokes measureAndSendDistance function which uses NewPing to calculate distance and Serial to send data to PC. This is how easy it is to get distance in cm thanks to NewPing lib:

byte distanceInCm = sonar.ping_cm()

If measured distance exceeds the maximum value specified as last parameter to NewPing constructor the value of 0 is returned. Check NewPing docs to see other useful functions of this lib.

And finally this is how Arduino sends data to PC:

byte sonarData[] = {255, angle, distanceInCm};
Serial.write(sonarData, 3); // Send data to PC

The first array element (255) is used as a marker/separator to easily distinguish pairs of angle-distance values. Its role will become clear in the second post which will describe SignalR server and clients... I assume that maxDistanceInCm const will never be set above 200 so distanceInCm will never have value of 255. 255 will never be sent as an angle too because our servo moves in 0..180 degrees range. Sure it might be a good idea to create const and avoid 255 magic number. Some validation would be useful too... But screw it, this project is just for fun! :)

Ok, you survived to the end of the first post about Arduino/.NET/JS/HTML sonar. The second post should be ready in about a week.

Update 2014-09-29: Here's the Part 2.

How fast is .NET Garbage Collector? Part 2.

Read the first part of the article if you haven’t done so already. Part 1 has a brief overview of what GC is and how it performs its magic. It contains a test of GC performance with regards to large array of bytes. You can also find there a detailed information about my test environment…

This part will focus on scenarios which put a lot more pressure on GC and appear more commonly in real world applications. You will see that even a tree of more than 100 million objects can be handled quickly… But first let’s see how GC responds to big array of type object:

static void TestSpeedForLargeObjectArray()
{
    Stopwatch sw = new Stopwatch();

    Console.Write(string.Format("GC.GetTotalMemory before creating array: {0:N0}. Press key any to create array...", GC.GetTotalMemory(true)));
    Console.Read();
    object[] array = new object[100 * 1000 * 1000]; // About 800 MB (3.2 GB when filled with object instances)
             
    sw.Start();
    for (int i = 0; i < array.Length; i++)
    {
        array[i] = new object();
    }
    Console.WriteLine("Setup time: " + sw.Elapsed);
    Console.WriteLine(string.Format("GC.GetTotalMemory after creating array: {0:N0}. Press Enter to set array to null...", GC.GetTotalMemory(true)));

    if (Console.ReadKey().Key == ConsoleKey.Enter)
    {
        Console.WriteLine("Setting array to null");
        array = null;
    }
    
    sw.Restart();
    GC.Collect();
    Console.WriteLine("Collection time: " + sw.Elapsed);
    Console.WriteLine(string.Format("GC.GetTotalMemory after GC.Collect: {0:N0}. Press any key to finish...", GC.GetTotalMemory(true)));

    Console.WriteLine(array); // To avoid compiler optimization...
    Console.ReadKey();
}

Above test creates and array of 100 million items. Initially such array takes about 800 megabytes of memory (on x64 platform). This part is allocated on LOH. When object instances are created total heap allocation jumps to 3.2 GB. Array items are tiny so they are part of Small Object Heap and initially belong to Gen 0.

Here are the test results for situation when array is set to null:

GC.GetTotalMemory before creating array: 41,736. Press key any to create array...
Press any key to fill array...
Setup time: 00:00:07.7910574
GC.GetTotalMemory after creating array: 3,200,057,616. Press Enter to set array to null...
Setting array to null
Collection time: 00:00:00.7481998
GC.GetTotalMemory after GC.Collect: 57,624. Press any key to finish...

It took only about 700 ms to reclaim over 3 GB of memory!

Take a look at this graph from Performance Monitor:

Managed memory counters for large object[] array; setting array to null... Click to enlarge...

You can see that while program was filling the array, Gen 0 and Gen 1 changed size (notice though that the scale for these is 100x bigger than scale for other counters). This means that GC cycles were triggered while items were created - this is expected behavior. Notice how Gen 2 and LOH size adds up to total bytes on managed heap.

What if instead of setting array reference to null we set array items to null?

Let’s see. Here’s the graph:

Managed memory counters for large object[] array; setting array items to null... Click to enlarge...

Notice that after GC.Collect is done 800 MB are still allocated - this is LOH memory held by array itself…

Here are the results:

GC.GetTotalMemory before creating array: 41,752. Press key any to create array...
Press any key to fill array...
Setup time: 00:00:07.7707024
GC.GetTotalMemory after creating array: 3,200,057,632. Press Enter to set array elements to null...
Setting array elements to null
Collection time: 00:00:01.0926220
GC.GetTotalMemory after GC.Collect: 800,057,672. Press any key to finish...

Ok, enough with arrays. One can argue that as continues blocks of memory they are easier to handle then complex objects structures that are abundant in real word programs.

Let’s create a very big tree of small reference types:

static int _itemsCount = 0;

class Item
{
    public Item ChildA { get; set; }
    public Item ChildB { get; set; }
    
    public Item()
    {
        _itemsCount++;
    }           
}

static void AddChildren(Item parent, int depth) 
{
    if (depth == 0)
    {
        return;
    }
    else
    {
        parent.ChildA = new Item();
        parent.ChildB = new Item();

        AddChildren(parent.ChildA, depth - 1);
        AddChildren(parent.ChildB, depth - 1);                
    }
}

static void TestSpeedForLargeTreeOfSmallObjects()
{
    Stopwatch sw = new Stopwatch();

    Console.Write(string.Format("GC.GetTotalMemory before building object tree: {0:N0}. Press any key to build tree...", GC.GetTotalMemory(true)));
    Console.ReadKey();

    sw.Start();
    _itemsCount = 0;       
    Item root = new Item();            
    AddChildren(root, 26);
    Console.WriteLine("Setup time: " + sw.Elapsed);
    Console.WriteLine("Number of items: " + _itemsCount.ToString("N0"));

    Console.WriteLine(string.Format("GC.GetTotalMemory after building object tree: {0:N0}. Press Enter to set root to null...", GC.GetTotalMemory(true)));

    if (Console.ReadKey().Key == ConsoleKey.Enter)
    {
        Console.WriteLine("Setting tree root to null");
        root = null;                
    }
    
    sw.Restart();
    GC.Collect();
    Console.WriteLine("Collection time: " + sw.Elapsed);
    Console.WriteLine(string.Format("GC.GetTotalMemory after GC.Collect: {0:N0}. Press any key to finish...", GC.GetTotalMemory(true)));
                
    Console.WriteLine(root); // To avoid compiler optimization...            
    Console.ReadKey();
}

The test presented above creates a tree with over 130 million nodes which take almost 4.3 GB of memory.

Here’s what happens when tree root is set to null:

GC.GetTotalMemory before building object tree: 41,616. Press any key to build tree...
Setup time: 00:00:14.3355583
Number of items: 134,217,727
GC.GetTotalMemory after building object tree: 4,295,021,160. Press Enter to set root to null...
Setting tree root to null
Collection time: 00:00:01.1069927
GC.GetTotalMemory after GC.Collect: 53,856. Press any key to finish...

Managed memory counters for large tree of small objects; setting tree root to null... Click to enlarge...

It took only 1.1 second to clear all the garbage! When root reference was set to null all nodes below it became useless as defined by mark and sweep algorithm… Notice that this time LOH is not utilized as no single object instance is over 85 KB threshold.

Now let’s see what happens when the root is not set to null and all the objects survive GC cycle:

GC.GetTotalMemory before building object tree: 41,680. Press any key to build tree...
Setup time: 00:00:14.3915412
Number of items: 134,217,727
GC.GetTotalMemory after building object tree: 4,295,021,224. Press Enter to set root to null...
Collection time: 00:00:03.7172580
GC.GetTotalMemory after GC.Collect: 4,295,021,184. Press any key to finish...

This time it took 3.7 sec (less than 28 nanoseconds per reference) for GC.Collect to run – remember that reachable references put more work on GC then dead one!

There is one more scenario we should test. Instead of setting root = null let's set root.ChildA = null. This way half of the tree would became unreachable. GC will have a chance to reclaim memory and compact it to avoid fragmentation. Check the results:

GC.GetTotalMemory before building object tree: 41,696. Press any key to build tree...
Setup time: 00:00:15.1326459
Number of items: 134,217,727
GC.GetTotalMemory after creating array: 4,295,021,240. Press Enter to set root.ChildA to null...
Setting tree root.ChildA to null
Collection time: 00:00:02.5062596
GC.GetTotalMemory after GC.Collect: 2,147,537,584. Press any key to finish...

Time for final test. Let’s create a tree of over 2 million complex nodes that contain some object references, small array and unique string. Additionally lets fill some of the MixedItem instances with byte array big enough to be put on Large Object Heap.

static int _itemsCount = 0;

class MixedItem
{
    byte[] _smallArray;
    byte[] _bigArray;
    string _uniqueString;

    public MixedItem ChildA { get; set; }
    public MixedItem ChildB { get; set; }

    public MixedItem()
    {
        _itemsCount++;

        _smallArray = new byte[1000];
        if (_itemsCount % 1000 == 0)
        {
            _bigArray = new byte[1000 * 1000];
        }

        _uniqueString = DateTime.Now.Ticks.ToString();
    }
}

static void AddChildren(MixedItem parent, int depth)
{
    if (depth == 0)
    {
        return;
    }
    else
    {
        parent.ChildA = new MixedItem();
        parent.ChildB = new MixedItem();

        AddChildren(parent.ChildA, depth - 1);
        AddChildren(parent.ChildB, depth - 1);
    }
}

static void TestSpeedForLargeTreeOfMixedObjects()
{
    Stopwatch sw = new Stopwatch();

    Console.Write(string.Format("GC.GetTotalMemory before building object tree: {0:N0}. Press any key to build tree...", GC.GetTotalMemory(true)));
    Console.ReadKey();

    sw.Start();
    _itemsCount = 0;
    MixedItem root = new MixedItem();
    AddChildren(root, 20);
    Console.WriteLine("Setup time: " + sw.Elapsed);
    Console.WriteLine("Number of items: " + _itemsCount.ToString("N0"));

    Console.WriteLine(string.Format("GC.GetTotalMemory after building object tree: {0:N0}. Press Enter to set root to null...", GC.GetTotalMemory(true)));

    if (Console.ReadKey().Key == ConsoleKey.Enter)
    {
        Console.WriteLine("Setting tree root to null");
        root = null;
    }

    sw.Restart();
    GC.Collect();
    Console.WriteLine("Collection time: " + sw.Elapsed);
    Console.WriteLine(string.Format("GC.GetTotalMemory after GC.Collect: {0:N0}. Press any key to finish...", GC.GetTotalMemory(true)));

    Console.WriteLine(root); // To avoid compiler optimization...
    Console.ReadKey();
}

How will GC perform when subjected to almost 4.5 GB of managed heap memory with such complex structure? Test results for setting root to null

GC.GetTotalMemory before building object tree: 41,680. Press any key to build tree...
Setup time: 00:00:11.5479202
Number of items: 2,097,151
GC.GetTotalMemory after building object tree: 4,496,245,632. Press Enter to set root to null...
Setting tree root to null
Collection time: 00:00:00.5055634
GC.GetTotalMemory after GC.Collect: 54,520. Press any key to finish...

Managed memory counters for large tree of mixed objects; setting tree root to null... Click to enlarge...

And in case you wonder, here's what happens when root is not set to null:

GC.GetTotalMemory before building object tree: 41,680. Press any key to build tree...
Setup time: 00:00:11.6676969
Number of items: 2,097,151
GC.GetTotalMemory after building object tree: 4,496,245,632. Press Enter to set root to null...
Collection time: 00:00:00.5617486
GC.GetTotalMemory after GC.Collect: 4,496,245,592. Press any key to finish...

So what it all means? The conclusion is that unless you are writing applications which require extreme efficiency or total guarantee of uninterrupted execution, you should be really glad that .NET uses automatic memory management. GC is a great piece of software that frees you from mundane and error prone memory handling. It lets you focus on what really matters: providing features for application users. I’ve been professionally writing .NET applications for past 8 years (enterprise stuff, mainly web apps and Windows services) and I’m yet to witness1 a situation when GC cost would be a major factor. Usually performance bottleneck lays in things like: bad DB configuration, inefficient SQL/ORM queries, slow remote services, bad network utilization, lack of parallelism, poor caching, sluggish client side rendering etc. If you avoid basic mistakes like creating to many strings you probably won’t even notice that there is a Garbage Collector :)

Update 31.08.2014: I've just run the most demanding test (big tree of small reference types with all objects surviving GC cycle) on my new laptop. The result is 3.3s compared to 3.7s result presented in the post. Test program: .NET 4.5 console app in Release mode run without debugger attached. Hardware: i7-4700HQ 2.4-3.4GHz 4 Core CPU, 8GB DDR3/1600MHz RAM. System: Windows 7 Home Premium x64.

1. I’ve met some out of memory exceptions related to LOH fragmentation. The good thing is that LOH algorithms are improving and x86 platform, which is especially susceptible to such errors, is becoming a thing of the past…