Things used in this project

Code

Driver CodeC#
Interfaces to the Sense Hat to drive the LEDs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
using Windows.Devices.Gpio;

namespace AstroPi
{
    public class LedHatFb : IDisposable
    {
        public const byte ADDRESS = 0x46;

        #region IDisposable Support
        public void Dispose()
        {
            if (_LedHatFbDevice != null)
            {
                _LedHatFbDevice.Dispose();
                _LedHatFbDevice = null;
            }
        }
        #endregion

        private I2cDevice _LedHatFbDevice = null;

        private byte [] _gamma = {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
          0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
          0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
          0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F};

        private byte[] _inverse_gamma = {
            0x00, 0x06, 0x08, 0x0A, 0x0C, 0x0D, 0x0E, 0x0F,
            0x10, 0x11, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16,
            0x16, 0x17, 0x18, 0x18, 0x19, 0x1A, 0x1A, 0x1B,
            0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F};

        public async void InitHardware()
        {
            try
            {
                string aqs = I2cDevice.GetDeviceSelector();
                DeviceInformationCollection collection = await DeviceInformation.FindAllAsync(aqs);

                I2cConnectionSettings settings = new I2cConnectionSettings(ADDRESS);
                settings.BusSpeed = I2cBusSpeed.StandardMode; // 100kHz clock
                settings.SharingMode = I2cSharingMode.Exclusive;
                _LedHatFbDevice = await I2cDevice.FromIdAsync(collection[0].Id, settings);
            }
            catch (Exception ex)
            {
                string error = ex.ToString();
            }
        }

        public void MapToHat(byte [] output, int outputOffset, byte [] input, int inputOffset)
        {
            for(int i=0;i<8;i++)
            {
                int co = inputOffset + i * 4;
                byte blue = input[co];
                byte green = input[co+1];
                byte red = input[co+2];
                byte alpha = input[co + 3];

                output[outputOffset + i] = (byte)(red);
                output[outputOffset + i + 8] = (byte)(green);
                output[outputOffset + i + 16] = (byte)(blue);
            }
        }

        #region low level

        private byte ReadByte(byte regAddr)
        {
            byte[] buffer = new byte[1];
            buffer[0] = regAddr;
            byte[] value = new byte[1];
            _LedHatFbDevice.WriteRead(buffer, value);
            return value[0];
        }

        private byte[] ReadBytes(byte regAddr, int length)
        {
            byte[] values = new byte[length];
            byte[] buffer = new byte[1];
            buffer[0] = regAddr;
            _LedHatFbDevice.WriteRead(buffer, values);
            return values;
        }

        void WriteByte(byte regAddr, byte data)
        {
            byte[] buffer = new byte[2];
            buffer[0] = regAddr;
            buffer[1] = data;
            _LedHatFbDevice.Write(buffer);
        }

        void WriteBytes(byte regAddr, byte[] values)
        {
            byte[] buffer = new byte[1 + values.Length];
            buffer[0] = regAddr;
            Array.Copy(values, 0, buffer, 1, values.Length);
            _LedHatFbDevice.Write(buffer);
        }

        #endregion

        public void WriteLEDs(int address, byte[] buffer)
        {
            if (buffer.Length + address > 192)
            {
                throw new ArgumentException("Address outside range (address + buffer length must be <= 192", "buffer");
            }
            if (address < 0)
            {
                throw new ArgumentException("Address can't be less than zero", "address");
            }
            byte[] b = new byte[buffer.Length];
            for (int i = 0; i < buffer.Length; i++)
            {
                b[i] = _gamma[buffer[i] >> 3];
            }
            WriteBytes((byte)address, b);
        }

        public void WriteLEDMatrix(byte [] buffer)
        {
            WriteLEDs(0, buffer);
        }

        public byte [] ReadLEDs(int address, int size)
        {
            if (size + address > 192)
            {
                throw new ArgumentException("Address outside range (address + size length must be <= 192", "size");
            }
            if (address < 0)
            {
                throw new ArgumentException("Address can't be less than zero", "address");
            }
            byte[] b = ReadBytes((byte)address, size);
            byte[] buffer = new byte[b.Length];
            for (int i = 0; i < b.Length; i++)
            {
                buffer[i] = (byte)(_inverse_gamma[b[i] & 0x1F] << 3);
            }
            return buffer;
        }

        public byte[] ReadLEDMatrix()
        {
            return ReadLEDs(0, 192);
        }

        public byte ReadWai()
        {
            return ReadByte(0xf0);
        }

        public byte ReadVersion()
        {
            return ReadByte(0xf1);
        }

        public byte ReadKeys()
        {
            return ReadByte(0xf2);
        }

        public byte ReadEEWp()
        {
            return ReadByte(0xf4);
        }

        // no idea what this does, maybe hold off using it
        public void WriteEEEp(byte value)
        {
            WriteByte(0xf4, value);
        }

        // no idea what this does, maybe hold off using it
        public void WriteAddress(byte value)
        {
            WriteByte(0xff, value);
        }

    }
}
MainC#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace AstroPi
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private LedHatFb _ledHat = new LedHatFb();
        public MainPage()
        {
            this.InitializeComponent();
            _ledHat.InitHardware();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            byte ver = _ledHat.ReadVersion();
            byte k = _ledHat.ReadKeys();
            byte wai = _ledHat.ReadWai();
            byte eewp = _ledHat.ReadEEWp();

            LoadBitmap("ms-appx:///assets/rp.png");
        }

        private async void LoadBitmap(string name)
        {
            StorageFile srcfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(name));

            using (IRandomAccessStream fileStream = await srcfile.OpenAsync(Windows.Storage.FileAccessMode.Read))
            {
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
                BitmapTransform transform = new BitmapTransform()
                {
                    ScaledWidth = 8,
                    ScaledHeight = 8
                };
                PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
                    BitmapPixelFormat.Bgra8,
                    BitmapAlphaMode.Straight,
                    transform,
                    ExifOrientationMode.IgnoreExifOrientation,
                    ColorManagementMode.DoNotColorManage
                );

                byte[] sourcePixels = pixelData.DetachPixelData();
                byte[] hatPixels = new byte[192];

                for(int i=0;i<(sourcePixels.Length/4)/8; i++)
                {
                    _ledHat.MapToHat(hatPixels, i * 8 * 3, sourcePixels, i * 8 * 4);
                }
                _ledHat.WriteLEDMatrix(hatPixels);
            }
        }

        private void Page_Unloaded(object sender, RoutedEventArgs e)
        {
            _ledHat.Dispose();
        }

        private void FlipButton_Click(object sender, RoutedEventArgs e)
        {
            byte[] read_buffer;
            read_buffer = _ledHat.ReadLEDMatrix();
            byte[] write_buffer = new byte[192];
            for (int i = 0; i < 192; i++)
            {
                write_buffer[i] = (byte)(0xFF - read_buffer[i]);
            }
            _ledHat.WriteLEDMatrix(write_buffer);
        }

        private void DimButton_Click(object sender, RoutedEventArgs e)
        {
            byte[] read_buffer;
            read_buffer = _ledHat.ReadLEDMatrix();
            byte[] write_buffer = new byte[192];
            for (int i = 0; i < 192; i++)
            {
                byte v = read_buffer[i];
                write_buffer[i] = (byte)(v);
            }
            _ledHat.WriteLEDMatrix(write_buffer);
        }
    }
}

Credits

Logo 300x300
Graham Chow

I'm a software developer on a long sabbatical. I've had wide experience from banking to underwater military sonar systems.

Replications

Did you replicate this project? Share it!

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Theremino HAL 7.2 – Hardware Abstraction Layer.
Easy
  • 67
  • 3

Full instructions

The HAL connects the hardware of InOut with high-level software via USB and Slots.

Theremino, Windows 10 and HAL. The link with the real world
Easy
  • 136
  • 3

Full instructions

Windows 10 and the HAL (Hardware Abstraction Layer). How to communicate with sensors and actuators in the most easy way.

Read the signal of the AB quadrature encoder with Theremino.
Easy
  • 268
  • 3

Full instructions

The simplest way to read A B quadrature encoder using Theremino Master.

Theremino SlotViewer, the simplest way to manage slots.
Easy
  • 141
  • 4

Full instructions

Theremino SlotViewer is the very simple way to manage the slots, read the value and modify in real time the value of each slot.

Eddystone beacons with the micro:bit
Easy
  • 177
  • 2

Protip

Experiment with the Physical Web by turning your micro:bit into an Eddystone beacon.

DIY Smart Home Doorbell 2.0 (works with Alexa)
Easy
  • 2,319
  • 15

Full instructions

Build your own DIY smart home doorbell for cheap, get notified when your guest knock at your door and remotely open by voice command

ProjectsCommunitiesContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login
Respect project
Feedback