Graham Chow
Published © MIT

Astro Pi LED on Windows IoT

Driving the LEDs on the Astro Pi in Windows IoT. The LEDs are meant to be showing the Raspberry Pi logo.

BeginnerFull instructions provided5,341
Astro Pi LED on Windows IoT

Things used in this project

Story

Read more

Code

Driver Code

C#
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);
        }

    }
}

Main

C#
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

Graham Chow

Graham Chow

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

Comments