Feather SSD1306 OLED Driver

I have purchased a Feather OLED 128x32 display which uses the SSD1306 chip. Here is a “simplified” simplified driver for using this:

public class SSD1306OLEDDriver : BufferDrawTargetVerticalByteStrip1Bpp
{
	private readonly byte[] _data;
	private readonly byte[] _command = new byte[2];
	private readonly Timer _refreshBurnoutTimer;
	private readonly I2cDevice i2c;
	public IntPtr Hdc { get; }
	public DateTime _lastUpdated { get; private set; }
	/// <summary>
	/// Constructor for SSD1306 based display (Supports 128x64 and 128x32 displays).
	/// </summary>
	/// <param name="controller">I2cController</param>
	/// <param name="width">Width of the display</param>
	/// <param name="height">Height of device</param>
	public SSD1306OLEDDriver(I2cController controller, int width = 128, int height = 32) : base(width, height)
	{
		_data = new byte[((width * height) / 8) + 1];
		_data[0] = 0x40;
		Hdc = GraphicsManager.RegisterDrawTarget(this);
		i2c = controller.GetDevice(new I2cConnectionSettings(0x3C)
		{
			AddressFormat = I2cAddressFormat.SevenBit,
			BusSpeed = I2cBusSpeed.FastMode,
		});
		Initialize();
		_lastUpdated = DateTime.Now;

		//This timer toggles the screen after a period of time to prevent pixel burnout
		_refreshBurnoutTimer = new Timer((c) =>
	   {
		   if (DateTime.Now > _lastUpdated.AddMinutes(15))
		   {
			   SendCommand(0xAE);
			   Thread.Sleep(100);
			   SendCommand(0xAF);
			   _lastUpdated = DateTime.Now;
		   }

	   }, null, 60 * 1000, 5000);

	}

	private void Initialize()
	{
		Thread.Sleep(1500); //Allow display to power up

		SendCommand(0xAE); //turn off OLED panel
		SendCommand(0xD5); //set display clock divide ratio/oscillator frequency
		SendCommand(0x80);
		SendCommand(0xA6);
		SendCommand((byte)(Height - 1));
		SendCommand(0xD3);
		SendCommand(0x00);
		SendCommand(0x40 | 0x00);
		SendCommand(0x8D);
		SendCommand(0x14);
		SendCommand(0x20);
		SendCommand(0x00);
		SendCommand(0xA0 | 0x1);
		SendCommand(0xC8);
		SendCommand(0xDA);
		SendCommand(0x02);
		SendCommand(0x81);
		SendCommand(0x8F);
		SendCommand(0xD9);
		SendCommand(0xF1);
		SendCommand(0xDB);
		SendCommand(0x40);
		SendCommand(0xA4);
		SendCommand(0xA6);
		SendCommand(0x2E);
		SendCommand(0xAF);

		SendCommand(0x20);
		SendCommand(0x00);
		SendCommand(0x21);
		SendCommand(0);
		SendCommand(128 - 1);
		SendCommand(0x22);
		SendCommand(0);
		SendCommand(7);
	}

	private void SendCommand(byte cmd)
	{
		_command[1] = cmd;
		i2c.Write(_command);
	}

	public override void Dispose()
	{
		base.Dispose();
		i2c.Dispose();
		_refreshBurnoutTimer?.Dispose();
	}

	/// <summary>
	/// Writes the data to the OLED display
	/// </summary>
	public override void Flush()
	{
		base.Flush();
		//Clears the data buffer used to write to the display 
		Array.Clear(_data, 1, _data.Length - 1);

		//Writes the cleared buffer to the display
		i2c.Write(_data);

		//Copy the underlying buffer used by Graphics frame 
		Array.Copy(buffer, 0, _data, 1, _data.Length - 1);

		//Writes the buffer to the display
		i2c.Write(_data);

		//Clearing both buffers
		Array.Clear(_data, 1, _data.Length - 1);
		Array.Clear(buffer, 0, buffer.Length);
		_lastUpdated = DateTime.Now;
	}

	public void Clear()
	{
		Array.Clear(buffer, 0, buffer.Length);
		Flush();
	}
}

Here is a sample code for using such:

	class Program
{
	static void Main()
	{

		var adcController = GHIElectronics.TinyCLR.Devices.Adc.AdcController.FromName(STM32F4.AdcChannel.Id);
		var battery = adcController.OpenChannel(STM32F4.AdcChannel.Channel3);


		var i2cController = I2cController.FromName(STM32F4.I2cBus.I2c1);
		SSD1306OLEDDriver screen = new SSD1306OLEDDriver(i2cController);
		var graphics = Graphics.FromHdc(screen.Hdc);
		var brush = new SolidBrush(Color.White);
		var font = new Font("GHIMono8x5", 8);

		StringBuilder sb = new StringBuilder();
		while (true)
		{
			graphics.Clear(Color.Black);
			graphics.Flush();
			var b = (battery.ReadValue() * 2 * 3.3) / 4096;

			var pow = b.ToString("F2");
			sb.AppendLine("LYNK BOT");
			sb.AppendLine($"DATE:    {DateTime.Now.ToString("hh:mm:ss")}");
			sb.AppendLine($"BATTERY: {pow}v");
			graphics.DrawString(sb.ToString(), font, brush, 28, screen.Height / 2 - 16);
			graphics.DrawEllipse(new Pen(Color.White), 0, screen.Height / 2 - 16, 24, 24);
			graphics.DrawEllipse(new Pen(Color.White), 0, screen.Height / 2 - 16, 20, 20);
			graphics.Flush();
			Thread.Sleep(5000);
			sb.Clear();
		}
	}
}

Add the following Nuget packages

  • GHIElectronics.TinyCLR.Devices.I2c
  • GHIElectronics.TinyCLR.Drawing
  • GHIElectronics.TinyCLR.Devices.Adc
3 Likes

no pictures? :slight_smile:

A picture of a thousand words. :sunglasses:

2 Likes

yeah. Very cool.