diff --git a/src/devices/Display/BarColor.cs b/src/devices/Display/BarColor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5d0e2151402738141ba43056b019a4bf7ddf5464
--- /dev/null
+++ b/src/devices/Display/BarColor.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Iot.Device.Display
+{
+ ///
+ /// Describes LED colors in a Bargraph.
+ ///
+ public enum BarColor
+ {
+ ///
+ /// Disable LED.
+ ///
+ Off = 0,
+
+ ///
+ /// Enable red LED.
+ ///
+ Red = 1,
+
+ ///
+ /// Enable green LED.
+ ///
+ Green = 2,
+
+ ///
+ /// Enable both green and red LEDs, producing a yellow color.
+ ///
+ Yellow = 3
+ }
+}
diff --git a/src/devices/Display/BiColorBarGraph.cs b/src/devices/Display/BiColorBarGraph.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eb9c40db1536207be41ce00ef2d7b6b254631649
--- /dev/null
+++ b/src/devices/Display/BiColorBarGraph.cs
@@ -0,0 +1,189 @@
+// 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.I2c;
+
+namespace Iot.Device.Display
+{
+ ///
+ /// Represents a 24-Segment bargraph that can display multiple colors.
+ ///
+ // Product: https://www.adafruit.com/product/1721
+ public class BiColorBarGraph : Ht16k33
+ {
+ private readonly byte[] _displayBuffer = new byte[7];
+ private readonly BarColor[] _biColorSegment = new BarColor[24];
+
+ ///
+ /// Initialize BarGraph display
+ ///
+ /// The to create with.
+ public BiColorBarGraph(I2cDevice i2cDevice)
+ : base(i2cDevice)
+ {
+ }
+
+ ///
+ /// Indexer for .
+ ///
+ public BarColor this[int index]
+ {
+ get => _biColorSegment[index];
+ set
+ {
+ _biColorSegment[index] = value;
+ UpdateBuffer(index);
+ if (BufferingEnabled)
+ {
+ Flush();
+ }
+ }
+ }
+
+ ///
+ /// Enable all LEDs.
+ ///
+ public void Fill(BarColor color)
+ {
+ byte fill = 0xFF;
+ switch (color)
+ {
+ case BarColor.Red:
+ _displayBuffer[1] = fill;
+ _displayBuffer[3] = fill;
+ _displayBuffer[5] = fill;
+ break;
+ case BarColor.Green:
+ _displayBuffer[2] = fill;
+ _displayBuffer[4] = fill;
+ _displayBuffer[6] = fill;
+ break;
+ case BarColor.Yellow:
+ Span displayBuffer = _displayBuffer;
+ displayBuffer.Fill(fill);
+ displayBuffer[0] = 0x00;
+ break;
+ default:
+ break;
+ }
+
+ if (BufferingEnabled)
+ {
+ _i2cDevice.Write(_displayBuffer);
+ }
+ }
+
+ ///
+ public override void Clear()
+ {
+ _displayBuffer.AsSpan().Clear();
+ if (BufferingEnabled)
+ {
+ _i2cDevice.Write(_displayBuffer);
+ }
+ }
+
+ ///
+ public override void Flush() => _i2cDevice.Write(_displayBuffer);
+
+ ///
+ public override void Write(ReadOnlySpan data, int startAddress = 0)
+ {
+ // Note: first byte is command data; does not affect display
+ foreach (byte b in data)
+ {
+ _displayBuffer[startAddress++] = b;
+ }
+
+ if (BufferingEnabled)
+ {
+ Flush();
+ }
+ }
+
+ /*
+ Task: Update data for the bargraph
+
+ The following diagram shows the intended orientation of the bargraph.
+
+ pins x x x x x x x x x x x x x x x x x x x x x x x x
+ 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+
+ Each bargraph has three segments, four LEDs each.
+ For the 24-segment bargraph, that's six segments of four LEDs.
+ Each segment is addressed separately, with 4 bits (of a byte).
+ The first and last 4 bits of each byte represent different segments.
+ Each bit represents an LED. If the bits are on, the LEDs are on.
+
+ Each bar contains two LEDs. These are separately controlled.
+ That means that there are eight bits to consider for each segement,
+ four for each color. All of which can be separately on or off.
+
+ There are seven (7) bytes in the buffer.
+
+ The first byte of the buffer is for control/command information.
+ It should always be `0` unless specifically sending commands (like for blinking).
+
+ Each of the following bytes are paired, the first for red and the second for green.
+ Each of the bytes in the pair are split, the first half for one segment,
+ and the second half for the matching segement on the other bargraph.
+
+ The bytes are laid out this way:
+ - first segment: first four bits of bytes[2] for red; first four bits of bytes[3] for green
+ - second segment: first four bits of bytes[4] for red; first four bits of bytes[5] for green
+ - third segment: first four bits of bytes[6] for red; first four bits of bytes[7] for green
+ ------------- // boundary of the bargraph units
+ - fourth segment: second four bits of bytes[2] for red; second four bits of bytes[3] for green
+ - fifth segment: second four bits of bytes[4] for red; second four bits of bytes[5] for green
+ - sixth segment: second four bits of bytes[6] for red; second four bits of bytes[7] for green
+
+ This is more obvious if you write some variation of value to the i2cdevice:
+
+ byte[] buffer =
+ {
+ 0, 255, 0, 0, 255, 255, 255
+ };
+ */
+ private void UpdateBuffer(int index)
+ {
+ // Tasks:
+ // Determine the location of the bar (for `index`).
+ // Produce a mask for the correct bit (within four bits).
+ // bitmask the correct bits dependending on the desired result
+ // Some basic math to use:
+ // x = index % 4 // which third (for example, for the 24 bar graph, there are six thirds)
+ // y = x % 3 // which third of the bar segment to use
+ // z = index / 12 // which of the bar segments to use
+ BarColor value = _biColorSegment[index];
+ int unit = index / 12;
+ int segment = index / 4;
+ int third = segment % 3;
+ int bit = index % 4 + (index / 12) * 4;
+ int mask = 1 << bit;
+ int bufferIndex = (third * 2) + 1;
+ byte red = _displayBuffer[bufferIndex];
+ byte green = _displayBuffer[bufferIndex + 1];
+
+ switch (value)
+ {
+ case BarColor.Off:
+ _displayBuffer[bufferIndex] = (byte)(red & ~mask);
+ _displayBuffer[bufferIndex + 1] = (byte)(green & ~mask);
+ break;
+ case BarColor.Red:
+ _displayBuffer[bufferIndex] = (byte)(red ^ mask);
+ break;
+ case BarColor.Green:
+ _displayBuffer[bufferIndex + 1] = (byte)(green ^ mask);
+ break;
+ case BarColor.Yellow:
+ _displayBuffer[bufferIndex] = (byte)(red ^ mask);
+ _displayBuffer[bufferIndex + 1] = (byte)(green ^ mask);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/src/devices/Display/Display.csproj b/src/devices/Display/Display.csproj
index 3dee55a98f280e77966e94f6b6901c95b1123364..549ca7a5f96dc82fa1e7fddce12080539ea68c04 100644
--- a/src/devices/Display/Display.csproj
+++ b/src/devices/Display/Display.csproj
@@ -13,8 +13,10 @@
+
+
-
\ No newline at end of file
+
diff --git a/src/devices/Display/Display.sln b/src/devices/Display/Display.sln
index 5d63314ec84d4f24b844af30a88f8860478d89b8..6f1824c7f9278852eca04c8024be2468db6f0211 100644
--- a/src/devices/Display/Display.sln
+++ b/src/devices/Display/Display.sln
@@ -7,6 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{20F2
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Large4Digit7SegmentDisplay.sample", "samples\Large4Digit7SegmentDisplay.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BiColorBargraph.sample", "samples\BiColorBargraph.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Display", "Display.csproj", "{4AAE8B9E-8540-4582-8083-D1F14104BAAC}"
EndProject
Global
diff --git a/src/devices/Display/README.md b/src/devices/Display/README.md
index f7e5f68962e7046f274d7fd1bfa12300b9a0aa9e..c14b0aac115d6662a1a610add6654a8c10c1884f 100644
--- a/src/devices/Display/README.md
+++ b/src/devices/Display/README.md
@@ -1,27 +1,24 @@
-# Segment display driver (HT16K33)
+# HT16K33 -- LED Matrix Display Driver
-This project contains multipurpose LED display drivers and binding implementations for concrete display configurations.
-
-## Documentation
-
-The **HT16K33** is LED display driver that supports multiple LED configurations and I2C communication.
+The [Ht16k33](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf) is a memory mapping and multi-function LED controller driver. It is used as a [backpack driver for several Adafruit products](https://www.adafruit.com/?q=Ht16k33). It supports multiple LED configurations and I2C communication.
Adafruit sells multiple display backpacks built upon this driver:
-- [Adafruit LED / SEGMENTED category](https://www.adafruit.com/category/103)
-- **Large4Digit7SegmentDisplay** is a binding that supports the **Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack** that comes in 3 colors:
-- [Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Yellow](https://www.adafruit.com/product/1268)
-- [Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Green](https://www.adafruit.com/product/1269)
-- [Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Red](https://www.adafruit.com/product/1270)
-- [HT16K33 datasheet](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf)
+- [1.2" 4-Digit 7-Segment Display w/I2C Backpack - Yellow](https://www.adafruit.com/product/1268)
+- [1.2" 4-Digit 7-Segment Display w/I2C Backpack - Green](https://www.adafruit.com/product/1269)
+- [1.2" 4-Digit 7-Segment Display w/I2C Backpack - Red](https://www.adafruit.com/product/1270)
+- [Bi-Color (Red/Green) 24-Bar Bargraph w/I2C Backpack Kit](https://www.adafruit.com/product/1721)
-More information on wiring can be found on the respective product pages.
+More information on wiring can be found on the respective product pages and at [adafruit/Adafruit_CircuitPython_HT16K33
+](https://github.com/adafruit/Adafruit_CircuitPython_HT16K33) (Adafruit-maintained Python bindings).
-## Usage
+## 7-Segment Display Usage
+
+![Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Green](https://cdn-shop.adafruit.com/970x728/1268-00.jpg)
```csharp
// Initialize display (busId = 1 for Raspberry Pi 2 & 3)
-var display = new Large4Digit7SegmentDisplay(I2cDevice.Create(new I2cConnectionSettings(busId: 1, address: Ht16k33.DefaultI2cAddress));
+using var display = new Large4Digit7SegmentDisplay(I2cDevice.Create(new I2cConnectionSettings(busId: 1, address: Ht16k33.DefaultI2cAddress));
// Set max brightness (automatically turns on display)
display.Brightness = display.MaxBrightness;
@@ -41,7 +38,25 @@ display.Dots = Dot.DecimalPoint;
// Send buffer to the device
display.Flush();
+```
-// Dispose display object (the device itself will not be turned off until powered down)
-display.Dispose();
+## Bi-Color Bargraph Usage
+
+![Bi-Color (Red/Green) 24-Bar Bargraph w/I2C Backpack Kit](https://cdn-shop.adafruit.com/970x728/1721-00.jpg)
+
+```csharp
+using BiColorBarGraph bargraph = new(I2cDevice.Create(new I2cConnectionSettings(busId: 1, Ht16k33.DefaultI2cAddress)))
+ {
+ // Set max brightness
+ Brightness = Ht16k33.MaxBrightness,
+ BufferingEnabled = true
+ };
+
+bargraph.Clear();
+
+bargraph[0] = BarColor.RED;
+bargraph[1] = BarColor.GREEN;
+bargraph[2] = BarColor.YELLOW;
+bargraph[3] = BarColor.OFF;
+bargraph[4] = BarColor.RED;
```
diff --git a/src/devices/Display/samples/BiColorBargraph.sample.csproj b/src/devices/Display/samples/BiColorBargraph.sample.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..9256c3d484cfa30bd7f8e7e164d7008567de737b
--- /dev/null
+++ b/src/devices/Display/samples/BiColorBargraph.sample.csproj
@@ -0,0 +1,11 @@
+
+
+ Exe
+ $(DefaultSampleTfms)
+ false
+
+
+
+
+
+
diff --git a/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj b/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj
index 6b37da30b7c8ebc08dc683c468a97c665c71fb4b..ce9bfe6c769cf7d7cece7b03dc5fa83bc1973111 100644
--- a/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj
+++ b/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj
@@ -2,8 +2,10 @@
Exe
$(DefaultSampleTfms)
+ false
+
-
\ No newline at end of file
+
diff --git a/src/devices/Display/samples/Program.BiColorBargraph.cs b/src/devices/Display/samples/Program.BiColorBargraph.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e4f9c400201074d812691f34c3bf2711ea601020
--- /dev/null
+++ b/src/devices/Display/samples/Program.BiColorBargraph.cs
@@ -0,0 +1,85 @@
+// 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.I2c;
+using System.Threading;
+using Iot.Device.Display;
+
+// Initialize display (busId = 1 for Raspberry Pi 2+)
+using BiColorBarGraph bargraph = new(I2cDevice.Create(new I2cConnectionSettings(busId: 1, Ht16k33.DefaultI2cAddress)))
+ {
+ // Set max brightness
+ Brightness = Ht16k33.MaxBrightness,
+ BufferingEnabled = true
+ };
+
+bargraph.Clear();
+
+bargraph[0] = BarColor.Red;
+bargraph[1] = BarColor.Green;
+bargraph[2] = BarColor.Yellow;
+bargraph[3] = BarColor.Off;
+bargraph[4] = BarColor.Red;
+
+Thread.Sleep(1000);
+bargraph.Clear();
+
+for (int i = 0; i < 24; i++)
+{
+ if (i % 2 is 1)
+ {
+ continue;
+ }
+
+ int num = i % 4;
+ BarColor color = (BarColor)(i % 3 + 1);
+ bargraph[i] = color;
+ Thread.Sleep(100);
+}
+
+for (int i = 23; i >= 0; i--)
+{
+ if (i % 2 is 0)
+ {
+ continue;
+ }
+
+ int num = i % 4;
+ BarColor color = (BarColor)(i % 3 + 1);
+ bargraph[i] = color;
+ Thread.Sleep(100);
+}
+
+Thread.Sleep(1000);
+bargraph.Clear();
+
+bargraph[0] = BarColor.Red;
+bargraph[6] = BarColor.Green;
+bargraph[11] = BarColor.Yellow;
+bargraph[12] = BarColor.Yellow;
+bargraph[18] = BarColor.Green;
+bargraph[23] = BarColor.Red;
+
+Thread.Sleep(1000);
+bargraph.Clear();
+
+byte[] customBuffer =
+{
+ 0, 255, 0, 0, 255, 255, 255
+};
+
+bargraph.Write(customBuffer);
+
+Thread.Sleep(1000);
+bargraph.Clear();
+
+bargraph.Fill(BarColor.Red);
+Thread.Sleep(1000);
+bargraph.Clear();
+bargraph.Fill(BarColor.Green);
+Thread.Sleep(1000);
+bargraph.Clear();
+bargraph.Fill(BarColor.Yellow);
+Thread.Sleep(1000);
+bargraph.Clear();
diff --git a/src/devices/Ht16k33/README.md b/src/devices/Ht16k33/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..40dd03d84d32440e381d6678ddcafd156536a728
--- /dev/null
+++ b/src/devices/Ht16k33/README.md
@@ -0,0 +1,5 @@
+# Ht16k33
+
+The [Ht16k33](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf) is a memory mapping and multi-function LED controller driver. It is used as a [backpack driver for several Adafruit products](https://www.adafruit.com/?q=Ht16k33).
+
+See the [Display](../Display/) folder for implementations of various products that use the Ht16k33.