Hardware & Firmware
Hardware & Firmware

A PC-Controlled WS2812B LED System with Arduino and C#

An old maker project: driving a WS2812B addressable LED strip with an Arduino and changing effects/brightness over serial from a C# Windows Forms app, with the wiring, mode logic, command protocol and code.

Arduino + C# ile PC kontrollü WS2812B LED sistemi kapak görseli

In this post I walk through an old maker project: controlling a WS2812B addressable LED strip from an Arduino, and changing the effects from a small Windows app I wrote in C#. For me this was not just a "light up an LED" experiment; it was a small but instructive system that brought together Arduino, serial communication, a C# Windows Forms UI and addressable LED effects.

What Was This Project?

My goal was not to leave the WS2812B strip running fixed effects inside the Arduino, but to make it controllable from the computer. The basic idea was:

text
Bilgisayardaki C# arayüzü

Seri port üzerinden komut gönderme

Arduino

WS2812B LED şerit

Efekt / renk / parlaklık değişimi

So there would be a small control panel on the PC, the mode I picked would go to the Arduino over serial, and the Arduino would run the matching effect on the strip. A nice learning project that combined electronics and software.

Parts I Used

Materials Used
  • Arduino Uno / Nanocontroller
  • WS2812B addressable LED strip
  • 5V power adapterstrip power
  • Jumper wires
  • C# / .NET Framework Windows FormsPC UI
  • Arduino IDE + Adafruit NeoPixel library

Because WS2812B LEDs are addressable, you can control each LED individually. Beyond simple color changes this lets you produce much livelier results: animations, transitions, meteor, fire, rainbow and so on.

Wiring Logic

On the wiring side the logic was quite simple. The data line goes from the Arduino’s D8 pin to the strip’s DIN end:

text
Arduino GND       -> WS2812B GND -> Adaptör GND
Arduino D8 (DATA) -> WS2812B DIN
Adaptör 5V        -> WS2812B 5V

The most important point was sharing GND: if the Arduino and the adapter powering the strip do not share a common ground, the data line may not work reliably. You also need to watch the arrow direction on the WS2812B; the data line must enter the strip from the correct end, the DIN side.

System Architecture

The project had two main parts: the Arduino side driving the strip, and the C# UI sending commands.

text
1. Arduino side
   - Controls the WS2812B strip.
   - Reads commands from the serial port.
   - Changes the effect based on the command.
   - Interprets brightness commands.

2. PC side (C# Windows Forms)
   - Lists serial ports, connects to the Arduino.
   - Sends effect commands via buttons.
   - Sends a brightness command via a scrollbar.

Thanks to this structure, the strip became controllable from the computer without changing the Arduino code.

The Arduino Side

On the Arduino side I used the Adafruit NeoPixel library. At the start the LED pin (D8) and LED count were defined:

cpp
#include <Adafruit_NeoPixel.h>

#define PIN 8
#define NUM_LEDS 171

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);

Here PIN is the Arduino pin the strip’s data line is connected to (D8), and NUM_LEDS is the total number of LEDs on the strip. In setup the strip is initialized and serial communication is opened:

cpp
void setup() {
  strip.begin();
  strip.show();
  Serial.begin(9600);
  veri.reserve(5);
}

Starting serial at 9600 baud means the C# program on the PC side has to connect at the same baud rate.

Mode Logic

On the Arduino side incoming commands were interpreted through a mod variable:

text
PC'den "1"  gelirse -> RGBLoop
PC'den "2"  gelirse -> Strobe
PC'den "14" gelirse -> Fire
PC'den "16" gelirse -> Meteor Rain
PC'den "21" gelirse -> OFF

In the code this was handled with switch (mod):

cpp
switch (mod) {
  case 1:  RGBLoop(); break;
  case 2:  Strobe(0xff, 0xff, 0xff, 10, 50, 1000); break;
  case 14: Fire(55, 120, 15); break;
  case 16: meteorRain(0xff, 0xff, 0xff, 10, 64, true, 30); break;
  case 21: setAll(0, 0, 0); break;
}

Simple but practical: by sending just a number from the PC I could trigger different effects on the Arduino.

Supported Effects

The project had multiple LED effects; some solid color, some animation, some with randomness:

text
RGBLoop      Strobe        HalloweenEyes
NewKITT      Twinkle       Sparkle
SnowSparkle  RunningLights colorWipe
rainbowCycle theaterChase  theaterChaseRainbow
Fire         meteorRain    Red / Green / Blue
Random mode  OFF

What I liked was that the Arduino worked not like a "light up an LED" device but like an effect engine. The PC app became the control panel sending commands to that engine.

A Tiny Command Protocol over Serial

A very small command protocol emerged. Numeric commands to change the effect:

text
1  -> RGBLoop      14 -> Fire        19 -> Blue
2  -> Strobe       16 -> Meteor Rain 20 -> Random
17 -> Red          18 -> Green       21 -> OFF

For brightness I used a different format:

text
b0
b20
b50
b100

Here the b told the Arduino this was a brightness command; the number after it was a value from 0 to 100. The Arduino mapped it to the 0-255 range and applied it:

cpp
if (veri.charAt(0) == 'b') {
  veri = veri.substring(1, 4);
  parlaklik = veri.toInt();
  parlaklik = map(parlaklik, 0, 100, 0, 255);
  strip.setBrightness(parlaklik);
}

This little protocol is one of my favorite parts of the project: very simple, but functional enough.

Catching New Commands During Long Animations

Problem

Effects like meteorRain, rainbowCycle and Fire consist of long loops. While they run, if a new command arrives from the PC the Arduino would only notice it after the current animation finished. Switching from one effect to another meant waiting a long time.

To fix it I checked the serial port frequently inside the effects:

cpp
if (serialEvent() != false) break;

So if a new command arrives while an animation is running, the loop breaks and we move to the new mode. This small detail made the project noticeably more usable.

On the microcontroller side it is not enough to just write the effect; you also have to think about how fast it reacts to commands from outside.

The PC Side: C# Windows Forms UI

On the PC side there was a small Windows Forms app in C# and .NET Framework. It listed the serial ports, connected to the selected one, sent effect commands via buttons and a brightness command via a scrollbar. When the app opened, the available serial ports were listed:

csharp
foreach (var serialPort in SerialPort.GetPortNames()) {
    comboBoxSeriPortlar.Items.Add(serialPort);
}

Serial port settings were applied on connect:

csharp
serialPort1.PortName = comboBoxSeriPortlar.Text;
serialPort1.BaudRate = 9600;
serialPort1.StopBits = StopBits.One;
serialPort1.DataBits = 8;

When a button was pressed, the effect number went straight to the Arduino:

csharp
private void button1_Click(object sender, EventArgs e)
{
    serialPort1.Write("1");
}

Plain but instructive: a UI action on the C# side turned into a physical LED effect on the Arduino over serial.

Brightness Control

In the PC app I used a scrollbar for brightness. When it changed, the value was sent with a b prefix:

csharp
int parlaklik = hScrollBar1.Value;
serialPort1.Write(string.Concat("b", parlaklik));

So the Arduino could tell, from the first character of the incoming data, whether it was an effect or a brightness command:

text
"14"  -> efekt komutu
"b50" -> parlaklık komutu

Even a distinction this small makes the system tidier; for me it was a simple example of "mini protocol design".

What I Learned

This project taught me a few things. Serial communication between an Arduino and a computer is simple but powerful; controlling a physical LED system from a small C# UI is fun. Addressable LEDs are far more flexible than classic RGB; controlling each LED gives a lot of freedom in animation. In microcontroller projects, being able to react quickly to external commands matters. And even in small projects, clear documentation (pin, command list, library, how it works) means the project can be understood again years later.

Repo

The project’s GitHub repo (Arduino code, the C# serial port program, the wiring diagram):

text
https://github.com/YasarKah/WS2812B-Arduino-PC

Conclusion

This was one of my early maker projects where I brought electronics and software together. The core idea still holds:

Send a command from a small UI on the computer, let the Arduino interpret it, and show it as a physical effect on the WS2812B strip.

I want to archive these older projects on yasarkah.com too, because they show not just the result but how I thought and how I learned on the electronics and software side.