Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
|
I loved the idea of sending stuff into space so I had to get one of these. Anyway, a bit of reverse engineering of the linux driver and it is easy to drive the LEDs from userland in Windows IoT.
I got the info from https://github.com/raspberrypi/rpi-sense. Word up to Serge Schneider and Raspberry PI for making it public.
It is terrible to to take photos of. I must have taken 50 just to get these two.
I don't normally include the full solution just the main source files because my Gui is lame.
If you stand back you can just make out the logo.
Of course the sense board has heaps of other sensors and even a joystick input.
I just found this and it looks like we can reprogram the hat (if we need to)
https://www.raspberrypi.org/wp-content/uploads/2015/08/Sense-HAT-V1_0.pdf
quote from the data sheet...
Scan joystick state every start (or end) of frame:
1. Turn off LEDs (take LED_OE_N high)
2. Make Port D [7:3] inputs
3. Read button state from Port D [7:3]
4. Make Port D outputs again, re-enable LED driver, start next frame.
NOTE In-pad pull ups must be enabled for Port D
This project by Mattias Larsson goes into much greater depth...
https://www.hackster.io/laserbrain/windows-iot-sense-hat
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);
}
}
}
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);
}
}
}
Comments