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
14 projects • 64 followers
I'm a software developer on a long sabbatical. I've had wide experience from banking to underwater military sonar systems.
Contact

Replications

Did you replicate this project? Share it!

I made one

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

Give feedback

Comments

Sign up / LoginProjectsPlatformsTopicsContestsLiveAppsBetaBlog