Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Pimoroni Blinkt device binding #2370

Merged
merged 9 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions src/devices/Blinkt/Blinkt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// 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
{
/// <summary>
/// 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
/// </summary>
public class Blinkt : IDisposable
{
private const int DAT = 23;
private const int CLK = 24;
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved
private const int BRIGHTNESS = 7;

/// <summary>
/// The number of pixels in the Blinkt strip
/// </summary>
public const int NUMBER_OF_PIXELS = 8;
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

For constants, we're using like for variables Pascal Case. So, this one should be NumberOfPixels. We do exceptions for internal registers for examples to be closer to the binding documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!


private readonly Color[] _pixels = new Color[NUMBER_OF_PIXELS];
private readonly int _sleepTime = 0;
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved
private readonly GpioController _gpio;

private bool _gpioSetup = false;

/// <summary>
/// Initializes a new instance of the <see cref="Blinkt"/> class.
/// </summary>
public Blinkt()
{
for (int i = 0; i < NUMBER_OF_PIXELS; i++)
{
_pixels[i] = Color.Empty;
}

_gpio = new GpioController();
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved
}

/// <inheritdoc />
public void Dispose()
{
Clear();
Show();

_gpio.Dispose();
}

/// <summary>
/// Sets the brightness of all the pixels
/// </summary>
/// <param name="brightness">The brightess for the pixels</param>
public void SetBrightness(byte brightness)
{
for (var i = 0; i < NUMBER_OF_PIXELS; i++)
{
_pixels[i] = Color.FromArgb(brightness, _pixels[i]);
}
}

/// <summary>
/// Clears all the LEDs by turning them off.
///
/// After calling Clear, you must call Show to update the LEDs.
/// </summary>
public void Clear()
{
SetAll(Color.Empty);
}

/// <summary>
/// Shows the current state of the LEDs by applying the colors and brightness to the LEDs.
/// </summary>
public void Show()
{
if (!_gpioSetup)
{
_gpio.OpenPin(DAT, PinMode.Output);
_gpio.OpenPin(CLK, PinMode.Output);
_gpioSetup = true;
}

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();
}

/// <summary>
/// Sets the color of all the pixels.
/// This does not update the physical pixel, you must call Show to display the color.
/// </summary>
/// <param name="color">The color to set the pixel to</param>
public void SetAll(Color color)
{
for (var i = 0; i < NUMBER_OF_PIXELS; i++)
{
SetPixel(i, color);
}
}

/// <summary>
/// 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.
/// </summary>
/// <param name="pixel">The index of the pixel to get</param>
/// <returns>The color of the pixel </returns>
public Color GetPixel(int pixel)
{
if (pixel < 0 || pixel >= NUMBER_OF_PIXELS)
{
throw new ArgumentOutOfRangeException(nameof(pixel), $"Pixel must be between 0 and {NUMBER_OF_PIXELS - 1}");
}

return _pixels[pixel];
}

/// <summary>
/// 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.
/// </summary>
/// <param name="pixel">The index of the pixel to update</param>
/// <param name="color">The color to set on the pixel</param>
/// <exception cref="ArgumentOutOfRangeException">The value of pixel must be between 0 and 7, otherwise this exception is thrown</exception>
public void SetPixel(int pixel, Color color)
{
if (pixel < 0 || pixel >= NUMBER_OF_PIXELS)
{
throw new ArgumentOutOfRangeException(nameof(pixel), $"Pixel must be between 0 and {NUMBER_OF_PIXELS - 1}");
}

_pixels[pixel] = color;
}

private void WriteByte(int b)
{
for (var i = 0; i < 8; i++)
{
_gpio.Write(DAT, (b & 0b10000000) != 0);
_gpio.Write(CLK, PinValue.High);
Thread.Sleep(_sleepTime);
b <<= 1;
_gpio.Write(CLK, PinValue.Low);
Thread.Sleep(_sleepTime);
}
}

private void Eof()
{
_gpio.Write(DAT, PinValue.Low);
for (var i = 0; i < 36; i++)
{
_gpio.Write(CLK, PinValue.High);
Thread.Sleep(_sleepTime);
_gpio.Write(CLK, PinValue.Low);
Thread.Sleep(_sleepTime);
}
}

private void Sof()
{
_gpio.Write(DAT, PinValue.Low);
for (var i = 0; i < 32; i++)
{
_gpio.Write(CLK, PinValue.High);
Thread.Sleep(_sleepTime);
_gpio.Write(CLK, PinValue.Low);
Thread.Sleep(_sleepTime);
}
}
}
}
15 changes: 15 additions & 0 deletions src/devices/Blinkt/Blinkt.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(DefaultBindingTfms)</TargetFrameworks>
<!--Disabling default items so samples source won't get build by the main library-->
<EnableDefaultItems>false</EnableDefaultItems>
<LangVersion>9</LangVersion>
</PropertyGroup>

<ItemGroup>
<Compile Include="*.cs" />
<None Include="README.md" />
</ItemGroup>

</Project>
67 changes: 67 additions & 0 deletions src/devices/Blinkt/Blinkt.sln
Original file line number Diff line number Diff line change
@@ -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
39 changes: 39 additions & 0 deletions src/devices/Blinkt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Blinkt
jimbobbennett marked this conversation as resolved.
Show resolved Hide resolved

## 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)
2 changes: 2 additions & 0 deletions src/devices/Blinkt/category.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
display
led
49 changes: 49 additions & 0 deletions src/devices/Blinkt/samples/Blinkt.Sample.cs
Original file line number Diff line number Diff line change
@@ -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.NUMBER_OF_PIXELS; 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.NUMBER_OF_PIXELS - 2; i >= 0; i--)
{
SetOnePixel(blinkt, color, i);
}
}
12 changes: 12 additions & 0 deletions src/devices/Blinkt/samples/Blinkt.Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultSampleTfms)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../Blinkt.csproj" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions src/devices/Blinkt/samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# TODO: This needs to be determined
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved

Help Wanted Please
1 change: 1 addition & 0 deletions src/devices/Device-Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* [AXP192 - Enhanced single Cell Li-Battery and Power System Management IC](Axp192/README.md)
* [Bh1745 - RGB Sensor](Bh1745/README.md)
* [BH1750FVI - Ambient Light Sensor](Bh1750fvi/README.md)
* [Blinkt - 8-LED indicator strip](Blinkt/README.md)
Copy link
Member

Choose a reason for hiding this comment

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

that's not necessary and it's done automatically at merge time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

* [Bmm150 - Magnetometer](Bmm150/README.md)
* [BMP180 - barometer, altitude and temperature sensor](Bmp180/README.md)
* [BMxx80 Device Family](Bmxx80/README.md)
Expand Down
Loading