diff --git a/src/devices/Blinkt/Blinkt.cs b/src/devices/Blinkt/Blinkt.cs
new file mode 100644
index 0000000000..3df9f2dc6e
--- /dev/null
+++ b/src/devices/Blinkt/Blinkt.cs
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Device.Gpio;
+using System.Threading;
+using System.Drawing;
+
+namespace Iot.Device.Blinkt
+{
+ ///
+ /// Driver for the Pimoroni Blinkt LED strip for Raspbery Pi
+ /// https://shop.pimoroni.com/products/blinkt
+ ///
+ /// This is a strip of 8 pixels that can be indendently controlled over GPIO.
+ ///
+ /// Setting the color is a 2-step process:
+ /// Set the color using Clear, SetPixel, or SetAll
+ /// Call Show to update the physical pixels
+ ///
+ public class Blinkt : IDisposable
+ {
+ private readonly GpioPin _datPin;
+ private readonly GpioPin _clkPin;
+
+ private readonly Color[] _pixels = new Color[NumberOfPixels];
+ private readonly bool _shouldDispose;
+ private GpioController _gpioController;
+
+ ///
+ /// The number of pixels in the Blinkt strip
+ ///
+ public const int NumberOfPixels = 8;
+
+ ///
+ /// The sleep time in milliseconds between each bit written to the Blinkt.
+ ///
+ /// The default value of 0 should be enough for most cases and works on the RPi 4 and 5,
+ /// but you can increase this if you experience issues with the Blinkt on faster CPUs.
+ ///
+ public int SleepTime { get; set; } = 0;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The GPIO pin number for the data pin. This defaults to 23,
+ /// and you should only change this if you connect the blinkt using cables instead of sitting it on the GPIO pins using the provided socket.
+ /// The GPIO pin number for the clock pin. This defaults to 24,
+ /// and you should only change this if you connect the blinkt using cables instead of sitting it on the GPIO pins using the provided socket.
+ /// The GPIO controller to use with the Blinkt. If not provided, a new controller is created.
+ /// True (the default) if the GPIO controller shall be disposed when disposing this instance.
+ public Blinkt(int datPin = 23, int clkPin = 24, GpioController? gpioController = null, bool shouldDispose = true)
+ {
+ for (int i = 0; i < NumberOfPixels; i++)
+ {
+ _pixels[i] = Color.Empty;
+ }
+
+ _shouldDispose = shouldDispose || gpioController is null;
+ _gpioController = gpioController ?? new();
+
+ _datPin = _gpioController.OpenPin(datPin, PinMode.Output);
+ _clkPin = _gpioController.OpenPin(clkPin, PinMode.Output);
+ }
+
+ ///
+ public void Dispose()
+ {
+ Clear();
+ Show();
+
+ // this condition only applies to GPIO devices
+ if (_shouldDispose)
+ {
+ _gpioController?.Dispose();
+ _gpioController = null!;
+ }
+ }
+
+ ///
+ /// Sets the brightness of all the pixels
+ ///
+ /// The brightess for the pixels
+ public void SetBrightness(byte brightness)
+ {
+ for (var i = 0; i < NumberOfPixels; i++)
+ {
+ _pixels[i] = Color.FromArgb(brightness, _pixels[i]);
+ }
+ }
+
+ ///
+ /// Clears all the LEDs by turning them off.
+ ///
+ /// After calling Clear, you must call Show to update the LEDs.
+ ///
+ public void Clear()
+ {
+ SetAll(Color.Empty);
+ }
+
+ ///
+ /// Shows the current state of the LEDs by applying the colors and brightness to the LEDs.
+ ///
+ public void Show()
+ {
+ Sof();
+
+ foreach (Color pixel in _pixels)
+ {
+ int r = pixel.R;
+ int g = pixel.G;
+ int b = pixel.B;
+ int brightness = (int)(31.0 * (pixel.A / 255.0)) & 0b11111;
+ WriteByte(0b11100000 | brightness);
+ WriteByte(b);
+ WriteByte(g);
+ WriteByte(r);
+ }
+
+ Eof();
+ }
+
+ ///
+ /// Sets the color of all the pixels.
+ /// This does not update the physical pixel, you must call Show to display the color.
+ ///
+ /// The color to set the pixel to
+ public void SetAll(Color color)
+ {
+ for (var i = 0; i < NumberOfPixels; i++)
+ {
+ SetPixel(i, color);
+ }
+ }
+
+ ///
+ /// Gets the color that the specified pixel is set to.
+ /// This may not reflect the actual color of the LED if Show has not been called.
+ ///
+ /// The index of the pixel to get
+ /// The color of the pixel
+ public Color GetPixel(int pixel)
+ {
+ if (pixel < 0 || pixel >= NumberOfPixels)
+ {
+ throw new ArgumentOutOfRangeException(nameof(pixel), $"Pixel must be between 0 and {NumberOfPixels - 1}");
+ }
+
+ return _pixels[pixel];
+ }
+
+ ///
+ /// Sets the color of the specified pixel to the specified color.
+ /// This does not update the physical pixel, you must call Show to display the color.
+ ///
+ /// The index of the pixel to update
+ /// The color to set on the pixel
+ /// The value of pixel must be between 0 and 7, otherwise this exception is thrown
+ public void SetPixel(int pixel, Color color)
+ {
+ if (pixel < 0 || pixel >= NumberOfPixels)
+ {
+ throw new ArgumentOutOfRangeException(nameof(pixel), $"Pixel must be between 0 and {NumberOfPixels - 1}");
+ }
+
+ _pixels[pixel] = color;
+ }
+
+ private void WriteByte(int b)
+ {
+ for (var i = 0; i < 8; i++)
+ {
+ _datPin.Write((b & 0b10000000) != 0);
+ _clkPin.Write(PinValue.High);
+ Thread.Sleep(SleepTime);
+ b <<= 1;
+ _clkPin.Write(PinValue.Low);
+ Thread.Sleep(SleepTime);
+ }
+ }
+
+ private void Eof()
+ {
+ _datPin.Write(PinValue.Low);
+ for (var i = 0; i < 36; i++)
+ {
+ _clkPin.Write(PinValue.High);
+ Thread.Sleep(SleepTime);
+ _clkPin.Write(PinValue.Low);
+ Thread.Sleep(SleepTime);
+ }
+ }
+
+ private void Sof()
+ {
+ _datPin.Write(PinValue.Low);
+ for (var i = 0; i < 32; i++)
+ {
+ _clkPin.Write(PinValue.High);
+ Thread.Sleep(SleepTime);
+ _clkPin.Write(PinValue.Low);
+ Thread.Sleep(SleepTime);
+ }
+ }
+ }
+}
diff --git a/src/devices/Blinkt/Blinkt.csproj b/src/devices/Blinkt/Blinkt.csproj
new file mode 100644
index 0000000000..2b43b907b7
--- /dev/null
+++ b/src/devices/Blinkt/Blinkt.csproj
@@ -0,0 +1,15 @@
+
+
+
+ $(DefaultBindingTfms)
+
+ false
+ 9
+
+
+
+
+
+
+
+
diff --git a/src/devices/Blinkt/Blinkt.sln b/src/devices/Blinkt/Blinkt.sln
new file mode 100644
index 0000000000..0fa92f4c56
--- /dev/null
+++ b/src/devices/Blinkt/Blinkt.sln
@@ -0,0 +1,67 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blinkt", "Blinkt.csproj", "{B82C190A-642B-465B-BD3F-DB56FFF22253}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{6A4DE7B1-03F3-4EE0-BF73-A0BAEF88BA2B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blinkt.Samples", "samples\Blinkt.Samples.csproj", "{3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "..\Common\Common.csproj", "{584651BB-91DA-4FFE-B32C-F13DD7BCE100}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Debug|x64.Build.0 = Debug|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Debug|x86.Build.0 = Debug|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x64.ActiveCfg = Release|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x64.Build.0 = Release|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x86.ActiveCfg = Release|Any CPU
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x86.Build.0 = Release|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x64.Build.0 = Debug|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x86.Build.0 = Debug|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x64.ActiveCfg = Release|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x64.Build.0 = Release|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x86.ActiveCfg = Release|Any CPU
+ {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x86.Build.0 = Release|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Debug|x64.Build.0 = Debug|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Debug|x86.Build.0 = Debug|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Release|Any CPU.Build.0 = Release|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Release|x64.ActiveCfg = Release|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Release|x64.Build.0 = Release|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Release|x86.ActiveCfg = Release|Any CPU
+ {584651BB-91DA-4FFE-B32C-F13DD7BCE100}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {3CFA13D6-1D29-4C87-B0C1-01A6901A50EF} = {6A4DE7B1-03F3-4EE0-BF73-A0BAEF88BA2B}
+ EndGlobalSection
+EndGlobal
diff --git a/src/devices/Blinkt/README.md b/src/devices/Blinkt/README.md
new file mode 100644
index 0000000000..23b4954004
--- /dev/null
+++ b/src/devices/Blinkt/README.md
@@ -0,0 +1,39 @@
+# Blinkt - 8-LED indicator strip
+
+## Summary
+
+[Blinkt!](https://shop.pimoroni.com/products/blinkt) offers eight APA102 pixels in the smallest (and cheapest) form factor to plug straight onto your Raspberry Pi.
+
+Each pixel on Blinkt! is individually controllable and dimmable allowing you to create gradients, pulsing effects, or just flash them on and off like crazy.
+
+## Binding Notes
+
+The Blinkt has 8 pixels that can be controlled independantly. This library is designed to mimic the functionality of the [Pimoroni Blinkt Python library](https://github.com/pimoroni/blinkt), except using System.Drawing.Color instead of separate R, G, B and brightness values. the Alpha channnel is used to set the brightness of the pixel.
+
+Setting the pixel values does not update the display. You must call the `Show` method to update the display. This allows you to configure how you want all the pixels to look before updating the display.
+
+## Usage
+
+Here is an example how to use the Blinkt:
+
+```csharp
+using System.Drawing;
+using Iot.Device.Blinkt;
+
+// Create the Blinkt
+var blinkt = new Blinkt();
+
+// Set all the pixels to random colors
+for (int i = 0; i < Blinkt.NUMBER_OF_PIXELS; i++)
+{
+ var color = Color.FromArgb(new Random().Next(0, 255), new Random().Next(0, 255), new Random().Next(0, 255));
+ blinkt.SetPixel(i, color);
+}
+
+// Update the display to show the new colors
+blinkt.Show();
+```
+
+## References
+
+[Blinkt on Pimoroni](https://shop.pimoroni.com/products/blinkt)
diff --git a/src/devices/Blinkt/category.txt b/src/devices/Blinkt/category.txt
new file mode 100644
index 0000000000..79a960891c
--- /dev/null
+++ b/src/devices/Blinkt/category.txt
@@ -0,0 +1,2 @@
+display
+led
diff --git a/src/devices/Blinkt/samples/Blinkt.Sample.cs b/src/devices/Blinkt/samples/Blinkt.Sample.cs
new file mode 100644
index 0000000000..54a4e224c9
--- /dev/null
+++ b/src/devices/Blinkt/samples/Blinkt.Sample.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Drawing;
+using System.Threading;
+using Iot.Device.Blinkt;
+
+// Light up pixels moving backwards and forwards in random colors
+
+// Create a new Blinkt instance
+Blinkt blinkt = new Blinkt();
+
+// A helper method to set a single pixel to a given color, clearing all others.
+// This also waits 100ms so the color can be seen
+static void SetOnePixel(Blinkt blinkt, Color color, int i)
+{
+ // Turn all pixels off first - this is not reflected in the hardware till we call Show
+ blinkt.Clear();
+
+ // Set the pixel at index i to the given color
+ blinkt.SetPixel(i, color);
+
+ // Update the hardware to reflect the changes
+ blinkt.Show();
+
+ // Wait for a bit before moving to the next pixel
+ Thread.Sleep(100);
+}
+
+// Loop forever
+while (true)
+{
+ // Generate a random color
+ Color color = Color.FromArgb(new Random().Next(0, 255), new Random().Next(0, 255), new Random().Next(0, 255));
+
+ // Loop through the pixels, lighting them in the given color
+ for (int i = 0; i < Blinkt.NumberOfPixels; i++)
+ {
+ SetOnePixel(blinkt, color, i);
+ }
+
+ // Loop through the pixels in reverse order, lighting them in the given color
+ // We skip the first pixel so there is a more pleasing bounce effect
+ for (int i = Blinkt.NumberOfPixels - 2; i >= 0; i--)
+ {
+ SetOnePixel(blinkt, color, i);
+ }
+}
diff --git a/src/devices/Blinkt/samples/Blinkt.Samples.csproj b/src/devices/Blinkt/samples/Blinkt.Samples.csproj
new file mode 100644
index 0000000000..859e440ecc
--- /dev/null
+++ b/src/devices/Blinkt/samples/Blinkt.Samples.csproj
@@ -0,0 +1,12 @@
+
+
+
+ Exe
+ $(DefaultSampleTfms)
+
+
+
+
+
+
+