Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Mahmood ul Hassan
Published © GPL3+

How to interface Nordic Thingy:53 with 12864 OLED Display

Learn how to interface Nordic thingy:53 with SS1306 0.96inch OLED display.

IntermediateProtip30 minutes530
How to interface Nordic Thingy:53 with 12864 OLED Display

Things used in this project

Story

Read more

Schematics

dsc01065_kJSsClBOPK.JPG

thingy:53 -- OLED display
3V3 pin to VDD
GND pin to GND
04 pin to SDA
05 pin to SCL

Code

thingy53_nrf5340_cpuapp_ns.overlay

C/C++
// To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.

// You can also use the buttons in the sidebar to perform actions on nodes.
// Actions currently available include:

// * Enabling / disabling the node
// * Adding the bus to a bus
// * Removing the node
// * Connecting ADC channels

// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
// You can also visit the nRF DeviceTree extension documentation at https://nrfconnect.github.io/vscode-nrf-connect/devicetree/nrfdevicetree.html

&pinctrl {
	i2c2_default: i2c2_default {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
				<NRF_PSEL(TWIM_SCL, 0, 5)>;
		};
	};

	i2c2_sleep: i2c2_sleep {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
				<NRF_PSEL(TWIM_SCL, 0, 5)>;
			low-power-enable;
		};
	};
};

&i2c2 {
	compatible = "nordic,nrf-twim";
	status = "okay";
	clock-frequency = <I2C_BITRATE_FAST_PLUS>;

	pinctrl-0 = <&i2c2_default>;
	pinctrl-1 = <&i2c2_sleep>;
	pinctrl-names = "default", "sleep";

	oled_display: oled_display@3C {
		compatible = "i2c-device";
		reg = <0x3C>;
		label = "OLED_LCD";
	};
};

main.c

C/C++
/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/zephyr.h>
#include <zephyr/drivers/i2c.h>

#include "SSD1306.h"
#include "binary.h"

#define I2C0_NODE DT_NODELABEL(oled_display)
static const struct i2c_dt_spec oled_i2c = I2C_DT_SPEC_GET(I2C0_NODE);

#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16

static const unsigned char /*PROGMEM*/ logo16_glcd_bmp[] = {
        B00000000, B11000000,
        B00000001, B11000000,
        B00000001, B11000000,
        B00000011, B11100000,
        B11110011, B11100000,
        B11111110, B11111000,
        B01111110, B11111111,
        B00110011, B10011111,
        B00011111, B11111100,
        B00001101, B01110000,
        B00011011, B10100000,
        B00111111, B11100000,
        B00111111, B11110000,
        B01111100, B11110000,
        B01110000, B01110000,
        B00000000, B00110000
};


void testdrawcircle(void) {
        ssd1306_set_rotation(0);
        for (int16_t i=0; i< ssd1306_height()/2; i+=2)
        {
			ssd1306_draw_circle(ssd1306_width()/2, ssd1306_height()/2, i, WHITE);
			ssd1306_display();
			k_sleep(K_MSEC(200));
        }
}

void testdrawchar(void)
{
    ssd1306_clear_display();
    ssd1306_set_textsize(1);
    ssd1306_set_textcolor(WHITE);
    ssd1306_set_cursor(0, 0);

    for (uint8_t i = 0; i < 168; i++) {
        if (i == '\n') continue;
        ssd1306_write(i);
        if ((i > 0) && (i % 21 == 0))
            ssd1306_write('\n');
    }
    ssd1306_display();
}

void testdrawline(void)
{
    for (int16_t i = 0; i < ssd1306_width(); i += 4) {
        ssd1306_draw_line(0, 0, i, ssd1306_height() - 1, WHITE);
        ssd1306_display();
    }
    for (int16_t i = 0; i < ssd1306_height(); i += 4) {
        ssd1306_draw_line(0, 0, ssd1306_width() - 1, i, WHITE);
        ssd1306_display();
    }
	k_sleep(K_MSEC(250));

    ssd1306_clear_display();
    for (int16_t i = 0; i < ssd1306_width(); i += 4) {
        ssd1306_draw_line(0, ssd1306_height() - 1, i, 0, WHITE);
        ssd1306_display();
    }
    for (int16_t i = ssd1306_height() - 1; i >= 0; i -= 4) {
        ssd1306_draw_line(0, ssd1306_height() - 1, ssd1306_width() - 1, i, WHITE);
        ssd1306_display();
    }
	k_sleep(K_MSEC(250));

    ssd1306_clear_display();
    for (int16_t i = ssd1306_width() - 1; i >= 0; i -= 4) {
        ssd1306_draw_line(ssd1306_width() - 1, ssd1306_height() - 1, i, 0, WHITE);
        ssd1306_display();
    }
    for (int16_t i = ssd1306_height() - 1; i >= 0; i -= 4) {
        ssd1306_draw_line(ssd1306_width() - 1, ssd1306_height() - 1, 0, i, WHITE);
        ssd1306_display();
    }
	k_sleep(K_MSEC(250));

    ssd1306_clear_display();
    for (int16_t i = 0; i < ssd1306_height(); i += 4) {
        ssd1306_draw_line(ssd1306_width() - 1, 0, 0, i, WHITE);
        ssd1306_display();
    }
    for (int16_t i = 0; i < ssd1306_width(); i += 4) {
        ssd1306_draw_line(ssd1306_width() - 1, 0, i, ssd1306_height() - 1, WHITE);
        ssd1306_display();
    }
	k_sleep(K_MSEC(250));

    ssd1306_display();
	k_sleep(K_MSEC(250));
    ssd1306_clear_display();
}

void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h)
{
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2

    uint8_t icons[NUMFLAKES][3];

    // initialize
    for (uint8_t f = 0; f < NUMFLAKES; f++) {
        icons[f][XPOS] = rand() % ssd1306_width();
        icons[f][YPOS] = 0;
        icons[f][DELTAY] = (rand() % 5) + 1;
    }

    while (1) {
        // draw each icon
        for (uint8_t f = 0; f < NUMFLAKES; f++) {
            ssd1306_draw_bitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, WHITE);
        }
        ssd1306_display();
		k_sleep(K_MSEC(200));

        // then erase it + move it
        for (uint8_t f = 0; f < NUMFLAKES; f++) {
            ssd1306_draw_bitmap(icons[f][XPOS], icons[f][YPOS],  logo16_glcd_bmp, w, h, BLACK);
            // move it
            icons[f][YPOS] += icons[f][DELTAY];
            // if its gone, reinit
            if (icons[f][YPOS] > ssd1306_height()) {
                icons[f][XPOS] = rand() % ssd1306_width();
                icons[f][YPOS] = 0;
                icons[f][DELTAY] = (rand() % 5) + 1;
            }
        }
    }
}

void main(void)
{
	printk("SSD1306 Example Thingy:53! %s\n", CONFIG_BOARD);

	if (!device_is_ready(oled_i2c.bus)) {
		printk("OLED: device not ready.\n");
		return;
	}
	printk("OLED Device %p name is %s\n", oled_i2c.bus, oled_i2c.bus->name);

    ssd1306_begin(SSD1306_SWITCHCAPVCC, false);
	ssd1306_clear_display();
	ssd1306_display();
	k_sleep(K_MSEC(100));

	while(1){
		ssd1306_clear_display();
		ssd1306_set_textsize(2);
		ssd1306_set_textcolor(WHITE);
		ssd1306_set_cursor(15, 25);
		ssd1306_putstring("Thingy:53");
		ssd1306_display();
		k_sleep(K_MSEC(5000));

		ssd1306_clear_display();
		ssd1306_draw_pixel(10, 10, WHITE);
		ssd1306_display();
		k_sleep(K_MSEC(1000));

		ssd1306_draw_circle(SSD1306_LCDWIDTH / 2, SSD1306_LCDHEIGHT / 2, 30, WHITE);
		ssd1306_display();
		k_sleep(K_MSEC(1000));

		testdrawchar();
		k_sleep(K_MSEC(1000));

		ssd1306_clear_display();
		ssd1306_display();
		k_sleep(K_MSEC(1000));

		testdrawline();

		ssd1306_clear_display();
		ssd1306_draw_bitmap(30, 16,  logo16_glcd_bmp, 16, 16, 1);
		ssd1306_display();
		k_sleep(K_MSEC(1000));
	}

}

binary.h

C/C++
/*
  binary.h - Definitions for binary constants
  Copyright (c) 2006 David A. Mellis.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef Binary_h
#define Binary_h

#define B0 0
#define B00 0
#define B000 0
#define B0000 0
#define B00000 0
#define B000000 0
#define B0000000 0
#define B00000000 0
#define B1 1
#define B01 1
#define B001 1
#define B0001 1
#define B00001 1
#define B000001 1
#define B0000001 1
#define B00000001 1
#define B10 2
#define B010 2
#define B0010 2
#define B00010 2
#define B000010 2
#define B0000010 2
#define B00000010 2
#define B11 3
#define B011 3
#define B0011 3
#define B00011 3
#define B000011 3
#define B0000011 3
#define B00000011 3
#define B100 4
#define B0100 4
#define B00100 4
#define B000100 4
#define B0000100 4
#define B00000100 4
#define B101 5
#define B0101 5
#define B00101 5
#define B000101 5
#define B0000101 5
#define B00000101 5
#define B110 6
#define B0110 6
#define B00110 6
#define B000110 6
#define B0000110 6
#define B00000110 6
#define B111 7
#define B0111 7
#define B00111 7
#define B000111 7
#define B0000111 7
#define B00000111 7
#define B1000 8
#define B01000 8
#define B001000 8
#define B0001000 8
#define B00001000 8
#define B1001 9
#define B01001 9
#define B001001 9
#define B0001001 9
#define B00001001 9
#define B1010 10
#define B01010 10
#define B001010 10
#define B0001010 10
#define B00001010 10
#define B1011 11
#define B01011 11
#define B001011 11
#define B0001011 11
#define B00001011 11
#define B1100 12
#define B01100 12
#define B001100 12
#define B0001100 12
#define B00001100 12
#define B1101 13
#define B01101 13
#define B001101 13
#define B0001101 13
#define B00001101 13
#define B1110 14
#define B01110 14
#define B001110 14
#define B0001110 14
#define B00001110 14
#define B1111 15
#define B01111 15
#define B001111 15
#define B0001111 15
#define B00001111 15
#define B10000 16
#define B010000 16
#define B0010000 16
#define B00010000 16
#define B10001 17
#define B010001 17
#define B0010001 17
#define B00010001 17
#define B10010 18
#define B010010 18
#define B0010010 18
#define B00010010 18
#define B10011 19
#define B010011 19
#define B0010011 19
#define B00010011 19
#define B10100 20
#define B010100 20
#define B0010100 20
#define B00010100 20
#define B10101 21
#define B010101 21
#define B0010101 21
#define B00010101 21
#define B10110 22
#define B010110 22
#define B0010110 22
#define B00010110 22
#define B10111 23
#define B010111 23
#define B0010111 23
#define B00010111 23
#define B11000 24
#define B011000 24
#define B0011000 24
#define B00011000 24
#define B11001 25
#define B011001 25
#define B0011001 25
#define B00011001 25
#define B11010 26
#define B011010 26
#define B0011010 26
#define B00011010 26
#define B11011 27
#define B011011 27
#define B0011011 27
#define B00011011 27
#define B11100 28
#define B011100 28
#define B0011100 28
#define B00011100 28
#define B11101 29
#define B011101 29
#define B0011101 29
#define B00011101 29
#define B11110 30
#define B011110 30
#define B0011110 30
#define B00011110 30
#define B11111 31
#define B011111 31
#define B0011111 31
#define B00011111 31
#define B100000 32
#define B0100000 32
#define B00100000 32
#define B100001 33
#define B0100001 33
#define B00100001 33
#define B100010 34
#define B0100010 34
#define B00100010 34
#define B100011 35
#define B0100011 35
#define B00100011 35
#define B100100 36
#define B0100100 36
#define B00100100 36
#define B100101 37
#define B0100101 37
#define B00100101 37
#define B100110 38
#define B0100110 38
#define B00100110 38
#define B100111 39
#define B0100111 39
#define B00100111 39
#define B101000 40
#define B0101000 40
#define B00101000 40
#define B101001 41
#define B0101001 41
#define B00101001 41
#define B101010 42
#define B0101010 42
#define B00101010 42
#define B101011 43
#define B0101011 43
#define B00101011 43
#define B101100 44
#define B0101100 44
#define B00101100 44
#define B101101 45
#define B0101101 45
#define B00101101 45
#define B101110 46
#define B0101110 46
#define B00101110 46
#define B101111 47
#define B0101111 47
#define B00101111 47
#define B110000 48
#define B0110000 48
#define B00110000 48
#define B110001 49
#define B0110001 49
#define B00110001 49
#define B110010 50
#define B0110010 50
#define B00110010 50
#define B110011 51
#define B0110011 51
#define B00110011 51
#define B110100 52
#define B0110100 52
#define B00110100 52
#define B110101 53
#define B0110101 53
#define B00110101 53
#define B110110 54
#define B0110110 54
#define B00110110 54
#define B110111 55
#define B0110111 55
#define B00110111 55
#define B111000 56
#define B0111000 56
#define B00111000 56
#define B111001 57
#define B0111001 57
#define B00111001 57
#define B111010 58
#define B0111010 58
#define B00111010 58
#define B111011 59
#define B0111011 59
#define B00111011 59
#define B111100 60
#define B0111100 60
#define B00111100 60
#define B111101 61
#define B0111101 61
#define B00111101 61
#define B111110 62
#define B0111110 62
#define B00111110 62
#define B111111 63
#define B0111111 63
#define B00111111 63
#define B1000000 64
#define B01000000 64
#define B1000001 65
#define B01000001 65
#define B1000010 66
#define B01000010 66
#define B1000011 67
#define B01000011 67
#define B1000100 68
#define B01000100 68
#define B1000101 69
#define B01000101 69
#define B1000110 70
#define B01000110 70
#define B1000111 71
#define B01000111 71
#define B1001000 72
#define B01001000 72
#define B1001001 73
#define B01001001 73
#define B1001010 74
#define B01001010 74
#define B1001011 75
#define B01001011 75
#define B1001100 76
#define B01001100 76
#define B1001101 77
#define B01001101 77
#define B1001110 78
#define B01001110 78
#define B1001111 79
#define B01001111 79
#define B1010000 80
#define B01010000 80
#define B1010001 81
#define B01010001 81
#define B1010010 82
#define B01010010 82
#define B1010011 83
#define B01010011 83
#define B1010100 84
#define B01010100 84
#define B1010101 85
#define B01010101 85
#define B1010110 86
#define B01010110 86
#define B1010111 87
#define B01010111 87
#define B1011000 88
#define B01011000 88
#define B1011001 89
#define B01011001 89
#define B1011010 90
#define B01011010 90
#define B1011011 91
#define B01011011 91
#define B1011100 92
#define B01011100 92
#define B1011101 93
#define B01011101 93
#define B1011110 94
#define B01011110 94
#define B1011111 95
#define B01011111 95
#define B1100000 96
#define B01100000 96
#define B1100001 97
#define B01100001 97
#define B1100010 98
#define B01100010 98
#define B1100011 99
#define B01100011 99
#define B1100100 100
#define B01100100 100
#define B1100101 101
#define B01100101 101
#define B1100110 102
#define B01100110 102
#define B1100111 103
#define B01100111 103
#define B1101000 104
#define B01101000 104
#define B1101001 105
#define B01101001 105
#define B1101010 106
#define B01101010 106
#define B1101011 107
#define B01101011 107
#define B1101100 108
#define B01101100 108
#define B1101101 109
#define B01101101 109
#define B1101110 110
#define B01101110 110
#define B1101111 111
#define B01101111 111
#define B1110000 112
#define B01110000 112
#define B1110001 113
#define B01110001 113
#define B1110010 114
#define B01110010 114
#define B1110011 115
#define B01110011 115
#define B1110100 116
#define B01110100 116
#define B1110101 117
#define B01110101 117
#define B1110110 118
#define B01110110 118
#define B1110111 119
#define B01110111 119
#define B1111000 120
#define B01111000 120
#define B1111001 121
#define B01111001 121
#define B1111010 122
#define B01111010 122
#define B1111011 123
#define B01111011 123
#define B1111100 124
#define B01111100 124
#define B1111101 125
#define B01111101 125
#define B1111110 126
#define B01111110 126
#define B1111111 127
#define B01111111 127
#define B10000000 128
#define B10000001 129
#define B10000010 130
#define B10000011 131
#define B10000100 132
#define B10000101 133
#define B10000110 134
#define B10000111 135
#define B10001000 136
#define B10001001 137
#define B10001010 138
#define B10001011 139
#define B10001100 140
#define B10001101 141
#define B10001110 142
#define B10001111 143
#define B10010000 144
#define B10010001 145
#define B10010010 146
#define B10010011 147
#define B10010100 148
#define B10010101 149
#define B10010110 150
#define B10010111 151
#define B10011000 152
#define B10011001 153
#define B10011010 154
#define B10011011 155
#define B10011100 156
#define B10011101 157
#define B10011110 158
#define B10011111 159
#define B10100000 160
#define B10100001 161
#define B10100010 162
#define B10100011 163
#define B10100100 164
#define B10100101 165
#define B10100110 166
#define B10100111 167
#define B10101000 168
#define B10101001 169
#define B10101010 170
#define B10101011 171
#define B10101100 172
#define B10101101 173
#define B10101110 174
#define B10101111 175
#define B10110000 176
#define B10110001 177
#define B10110010 178
#define B10110011 179
#define B10110100 180
#define B10110101 181
#define B10110110 182
#define B10110111 183
#define B10111000 184
#define B10111001 185
#define B10111010 186
#define B10111011 187
#define B10111100 188
#define B10111101 189
#define B10111110 190
#define B10111111 191
#define B11000000 192
#define B11000001 193
#define B11000010 194
#define B11000011 195
#define B11000100 196
#define B11000101 197
#define B11000110 198
#define B11000111 199
#define B11001000 200
#define B11001001 201
#define B11001010 202
#define B11001011 203
#define B11001100 204
#define B11001101 205
#define B11001110 206
#define B11001111 207
#define B11010000 208
#define B11010001 209
#define B11010010 210
#define B11010011 211
#define B11010100 212
#define B11010101 213
#define B11010110 214
#define B11010111 215
#define B11011000 216
#define B11011001 217
#define B11011010 218
#define B11011011 219
#define B11011100 220
#define B11011101 221
#define B11011110 222
#define B11011111 223
#define B11100000 224
#define B11100001 225
#define B11100010 226
#define B11100011 227
#define B11100100 228
#define B11100101 229
#define B11100110 230
#define B11100111 231
#define B11101000 232
#define B11101001 233
#define B11101010 234
#define B11101011 235
#define B11101100 236
#define B11101101 237
#define B11101110 238
#define B11101111 239
#define B11110000 240
#define B11110001 241
#define B11110010 242
#define B11110011 243
#define B11110100 244
#define B11110101 245
#define B11110110 246
#define B11110111 247
#define B11111000 248
#define B11111001 249
#define B11111010 250
#define B11111011 251
#define B11111100 252
#define B11111101 253
#define B11111110 254
#define B11111111 255

#endif

glcdfont.c

C/C++
#ifndef FONT5X7_H
#define FONT5X7_H


#define PROGMEM


// Standard ASCII 5x7 font

static const unsigned char font[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
    0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
    0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
    0x18, 0x3C, 0x7E, 0x3C, 0x18,
    0x1C, 0x57, 0x7D, 0x57, 0x1C,
    0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
    0x00, 0x18, 0x3C, 0x18, 0x00,
    0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
    0x00, 0x18, 0x24, 0x18, 0x00,
    0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
    0x30, 0x48, 0x3A, 0x06, 0x0E,
    0x26, 0x29, 0x79, 0x29, 0x26,
    0x40, 0x7F, 0x05, 0x05, 0x07,
    0x40, 0x7F, 0x05, 0x25, 0x3F,
    0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
    0x7F, 0x3E, 0x1C, 0x1C, 0x08,
    0x08, 0x1C, 0x1C, 0x3E, 0x7F,
    0x14, 0x22, 0x7F, 0x22, 0x14,
    0x5F, 0x5F, 0x00, 0x5F, 0x5F,
    0x06, 0x09, 0x7F, 0x01, 0x7F,
    0x00, 0x66, 0x89, 0x95, 0x6A,
    0x60, 0x60, 0x60, 0x60, 0x60,
    0x94, 0xA2, 0xFF, 0xA2, 0x94,
    0x08, 0x04, 0x7E, 0x04, 0x08,
    0x10, 0x20, 0x7E, 0x20, 0x10,
    0x08, 0x08, 0x2A, 0x1C, 0x08,
    0x08, 0x1C, 0x2A, 0x08, 0x08,
    0x1E, 0x10, 0x10, 0x10, 0x10,
    0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
    0x30, 0x38, 0x3E, 0x38, 0x30,
    0x06, 0x0E, 0x3E, 0x0E, 0x06,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x5F, 0x00, 0x00,
    0x00, 0x07, 0x00, 0x07, 0x00,
    0x14, 0x7F, 0x14, 0x7F, 0x14,
    0x24, 0x2A, 0x7F, 0x2A, 0x12,
    0x23, 0x13, 0x08, 0x64, 0x62,
    0x36, 0x49, 0x56, 0x20, 0x50,
    0x00, 0x08, 0x07, 0x03, 0x00,
    0x00, 0x1C, 0x22, 0x41, 0x00,
    0x00, 0x41, 0x22, 0x1C, 0x00,
    0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
    0x08, 0x08, 0x3E, 0x08, 0x08,
    0x00, 0x80, 0x70, 0x30, 0x00,
    0x08, 0x08, 0x08, 0x08, 0x08,
    0x00, 0x00, 0x60, 0x60, 0x00,
    0x20, 0x10, 0x08, 0x04, 0x02,
    0x3E, 0x51, 0x49, 0x45, 0x3E,
    0x00, 0x42, 0x7F, 0x40, 0x00,
    0x72, 0x49, 0x49, 0x49, 0x46,
    0x21, 0x41, 0x49, 0x4D, 0x33,
    0x18, 0x14, 0x12, 0x7F, 0x10,
    0x27, 0x45, 0x45, 0x45, 0x39,
    0x3C, 0x4A, 0x49, 0x49, 0x31,
    0x41, 0x21, 0x11, 0x09, 0x07,
    0x36, 0x49, 0x49, 0x49, 0x36,
    0x46, 0x49, 0x49, 0x29, 0x1E,
    0x00, 0x00, 0x14, 0x00, 0x00,
    0x00, 0x40, 0x34, 0x00, 0x00,
    0x00, 0x08, 0x14, 0x22, 0x41,
    0x14, 0x14, 0x14, 0x14, 0x14,
    0x00, 0x41, 0x22, 0x14, 0x08,
    0x02, 0x01, 0x59, 0x09, 0x06,
    0x3E, 0x41, 0x5D, 0x59, 0x4E,
    0x7C, 0x12, 0x11, 0x12, 0x7C,
    0x7F, 0x49, 0x49, 0x49, 0x36,
    0x3E, 0x41, 0x41, 0x41, 0x22,
    0x7F, 0x41, 0x41, 0x41, 0x3E,
    0x7F, 0x49, 0x49, 0x49, 0x41,
    0x7F, 0x09, 0x09, 0x09, 0x01,
    0x3E, 0x41, 0x41, 0x51, 0x73,
    0x7F, 0x08, 0x08, 0x08, 0x7F,
    0x00, 0x41, 0x7F, 0x41, 0x00,
    0x20, 0x40, 0x41, 0x3F, 0x01,
    0x7F, 0x08, 0x14, 0x22, 0x41,
    0x7F, 0x40, 0x40, 0x40, 0x40,
    0x7F, 0x02, 0x1C, 0x02, 0x7F,
    0x7F, 0x04, 0x08, 0x10, 0x7F,
    0x3E, 0x41, 0x41, 0x41, 0x3E,
    0x7F, 0x09, 0x09, 0x09, 0x06,
    0x3E, 0x41, 0x51, 0x21, 0x5E,
    0x7F, 0x09, 0x19, 0x29, 0x46,
    0x26, 0x49, 0x49, 0x49, 0x32,
    0x03, 0x01, 0x7F, 0x01, 0x03,
    0x3F, 0x40, 0x40, 0x40, 0x3F,
    0x1F, 0x20, 0x40, 0x20, 0x1F,
    0x3F, 0x40, 0x38, 0x40, 0x3F,
    0x63, 0x14, 0x08, 0x14, 0x63,
    0x03, 0x04, 0x78, 0x04, 0x03,
    0x61, 0x59, 0x49, 0x4D, 0x43,
    0x00, 0x7F, 0x41, 0x41, 0x41,
    0x02, 0x04, 0x08, 0x10, 0x20,
    0x00, 0x41, 0x41, 0x41, 0x7F,
    0x04, 0x02, 0x01, 0x02, 0x04,
    0x40, 0x40, 0x40, 0x40, 0x40,
    0x00, 0x03, 0x07, 0x08, 0x00,
    0x20, 0x54, 0x54, 0x78, 0x40,
    0x7F, 0x28, 0x44, 0x44, 0x38,
    0x38, 0x44, 0x44, 0x44, 0x28,
    0x38, 0x44, 0x44, 0x28, 0x7F,
    0x38, 0x54, 0x54, 0x54, 0x18,
    0x00, 0x08, 0x7E, 0x09, 0x02,
    0x18, 0xA4, 0xA4, 0x9C, 0x78,
    0x7F, 0x08, 0x04, 0x04, 0x78,
    0x00, 0x44, 0x7D, 0x40, 0x00,
    0x20, 0x40, 0x40, 0x3D, 0x00,
    0x7F, 0x10, 0x28, 0x44, 0x00,
    0x00, 0x41, 0x7F, 0x40, 0x00,
    0x7C, 0x04, 0x78, 0x04, 0x78,
    0x7C, 0x08, 0x04, 0x04, 0x78,
    0x38, 0x44, 0x44, 0x44, 0x38,
    0xFC, 0x18, 0x24, 0x24, 0x18,
    0x18, 0x24, 0x24, 0x18, 0xFC,
    0x7C, 0x08, 0x04, 0x04, 0x08,
    0x48, 0x54, 0x54, 0x54, 0x24,
    0x04, 0x04, 0x3F, 0x44, 0x24,
    0x3C, 0x40, 0x40, 0x20, 0x7C,
    0x1C, 0x20, 0x40, 0x20, 0x1C,
    0x3C, 0x40, 0x30, 0x40, 0x3C,
    0x44, 0x28, 0x10, 0x28, 0x44,
    0x4C, 0x90, 0x90, 0x90, 0x7C,
    0x44, 0x64, 0x54, 0x4C, 0x44,
    0x00, 0x08, 0x36, 0x41, 0x00,
    0x00, 0x00, 0x77, 0x00, 0x00,
    0x00, 0x41, 0x36, 0x08, 0x00,
    0x02, 0x01, 0x02, 0x04, 0x02,
    0x3C, 0x26, 0x23, 0x26, 0x3C,
    0x1E, 0xA1, 0xA1, 0x61, 0x12,
    0x3A, 0x40, 0x40, 0x20, 0x7A,
    0x38, 0x54, 0x54, 0x55, 0x59,
    0x21, 0x55, 0x55, 0x79, 0x41,
    0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
    0x21, 0x55, 0x54, 0x78, 0x40,
    0x20, 0x54, 0x55, 0x79, 0x40,
    0x0C, 0x1E, 0x52, 0x72, 0x12,
    0x39, 0x55, 0x55, 0x55, 0x59,
    0x39, 0x54, 0x54, 0x54, 0x59,
    0x39, 0x55, 0x54, 0x54, 0x58,
    0x00, 0x00, 0x45, 0x7C, 0x41,
    0x00, 0x02, 0x45, 0x7D, 0x42,
    0x00, 0x01, 0x45, 0x7C, 0x40,
    0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
    0xF0, 0x28, 0x25, 0x28, 0xF0,
    0x7C, 0x54, 0x55, 0x45, 0x00,
    0x20, 0x54, 0x54, 0x7C, 0x54,
    0x7C, 0x0A, 0x09, 0x7F, 0x49,
    0x32, 0x49, 0x49, 0x49, 0x32,
    0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
    0x32, 0x4A, 0x48, 0x48, 0x30,
    0x3A, 0x41, 0x41, 0x21, 0x7A,
    0x3A, 0x42, 0x40, 0x20, 0x78,
    0x00, 0x9D, 0xA0, 0xA0, 0x7D,
    0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
    0x3D, 0x40, 0x40, 0x40, 0x3D,
    0x3C, 0x24, 0xFF, 0x24, 0x24,
    0x48, 0x7E, 0x49, 0x43, 0x66,
    0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
    0xFF, 0x09, 0x29, 0xF6, 0x20,
    0xC0, 0x88, 0x7E, 0x09, 0x03,
    0x20, 0x54, 0x54, 0x79, 0x41,
    0x00, 0x00, 0x44, 0x7D, 0x41,
    0x30, 0x48, 0x48, 0x4A, 0x32,
    0x38, 0x40, 0x40, 0x22, 0x7A,
    0x00, 0x7A, 0x0A, 0x0A, 0x72,
    0x7D, 0x0D, 0x19, 0x31, 0x7D,
    0x26, 0x29, 0x29, 0x2F, 0x28,
    0x26, 0x29, 0x29, 0x29, 0x26,
    0x30, 0x48, 0x4D, 0x40, 0x20,
    0x38, 0x08, 0x08, 0x08, 0x08,
    0x08, 0x08, 0x08, 0x08, 0x38,
    0x2F, 0x10, 0xC8, 0xAC, 0xBA,
    0x2F, 0x10, 0x28, 0x34, 0xFA,
    0x00, 0x00, 0x7B, 0x00, 0x00,
    0x08, 0x14, 0x2A, 0x14, 0x22,
    0x22, 0x14, 0x2A, 0x14, 0x08,
    0xAA, 0x00, 0x55, 0x00, 0xAA,
    0xAA, 0x55, 0xAA, 0x55, 0xAA,
    0x00, 0x00, 0x00, 0xFF, 0x00,
    0x10, 0x10, 0x10, 0xFF, 0x00,
    0x14, 0x14, 0x14, 0xFF, 0x00,
    0x10, 0x10, 0xFF, 0x00, 0xFF,
    0x10, 0x10, 0xF0, 0x10, 0xF0,
    0x14, 0x14, 0x14, 0xFC, 0x00,
    0x14, 0x14, 0xF7, 0x00, 0xFF,
    0x00, 0x00, 0xFF, 0x00, 0xFF,
    0x14, 0x14, 0xF4, 0x04, 0xFC,
    0x14, 0x14, 0x17, 0x10, 0x1F,
    0x10, 0x10, 0x1F, 0x10, 0x1F,
    0x14, 0x14, 0x14, 0x1F, 0x00,
    0x10, 0x10, 0x10, 0xF0, 0x00,
    0x00, 0x00, 0x00, 0x1F, 0x10,
    0x10, 0x10, 0x10, 0x1F, 0x10,
    0x10, 0x10, 0x10, 0xF0, 0x10,
    0x00, 0x00, 0x00, 0xFF, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0xFF, 0x10,
    0x00, 0x00, 0x00, 0xFF, 0x14,
    0x00, 0x00, 0xFF, 0x00, 0xFF,
    0x00, 0x00, 0x1F, 0x10, 0x17,
    0x00, 0x00, 0xFC, 0x04, 0xF4,
    0x14, 0x14, 0x17, 0x10, 0x17,
    0x14, 0x14, 0xF4, 0x04, 0xF4,
    0x00, 0x00, 0xFF, 0x00, 0xF7,
    0x14, 0x14, 0x14, 0x14, 0x14,
    0x14, 0x14, 0xF7, 0x00, 0xF7,
    0x14, 0x14, 0x14, 0x17, 0x14,
    0x10, 0x10, 0x1F, 0x10, 0x1F,
    0x14, 0x14, 0x14, 0xF4, 0x14,
    0x10, 0x10, 0xF0, 0x10, 0xF0,
    0x00, 0x00, 0x1F, 0x10, 0x1F,
    0x00, 0x00, 0x00, 0x1F, 0x14,
    0x00, 0x00, 0x00, 0xFC, 0x14,
    0x00, 0x00, 0xF0, 0x10, 0xF0,
    0x10, 0x10, 0xFF, 0x10, 0xFF,
    0x14, 0x14, 0x14, 0xFF, 0x14,
    0x10, 0x10, 0x10, 0x1F, 0x00,
    0x00, 0x00, 0x00, 0xF0, 0x10,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
    0xFF, 0xFF, 0xFF, 0x00, 0x00,
    0x00, 0x00, 0x00, 0xFF, 0xFF,
    0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
    0x38, 0x44, 0x44, 0x38, 0x44,
    0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
    0x7E, 0x02, 0x02, 0x06, 0x06,
    0x02, 0x7E, 0x02, 0x7E, 0x02,
    0x63, 0x55, 0x49, 0x41, 0x63,
    0x38, 0x44, 0x44, 0x3C, 0x04,
    0x40, 0x7E, 0x20, 0x1E, 0x20,
    0x06, 0x02, 0x7E, 0x02, 0x02,
    0x99, 0xA5, 0xE7, 0xA5, 0x99,
    0x1C, 0x2A, 0x49, 0x2A, 0x1C,
    0x4C, 0x72, 0x01, 0x72, 0x4C,
    0x30, 0x4A, 0x4D, 0x4D, 0x30,
    0x30, 0x48, 0x78, 0x48, 0x30,
    0xBC, 0x62, 0x5A, 0x46, 0x3D,
    0x3E, 0x49, 0x49, 0x49, 0x00,
    0x7E, 0x01, 0x01, 0x01, 0x7E,
    0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
    0x44, 0x44, 0x5F, 0x44, 0x44,
    0x40, 0x51, 0x4A, 0x44, 0x40,
    0x40, 0x44, 0x4A, 0x51, 0x40,
    0x00, 0x00, 0xFF, 0x01, 0x03,
    0xE0, 0x80, 0xFF, 0x00, 0x00,
    0x08, 0x08, 0x6B, 0x6B, 0x08,
    0x36, 0x12, 0x36, 0x24, 0x36,
    0x06, 0x0F, 0x09, 0x0F, 0x06,
    0x00, 0x00, 0x18, 0x18, 0x00,
    0x00, 0x00, 0x10, 0x10, 0x00,
    0x30, 0x40, 0xFF, 0x01, 0x01,
    0x00, 0x1F, 0x01, 0x01, 0x1E,
    0x00, 0x19, 0x1D, 0x17, 0x12,
    0x00, 0x3C, 0x3C, 0x3C, 0x3C,
    0x00, 0x00, 0x00, 0x00, 0x00
};
#endif // FONT5X7_H

SSD1306.c

C/C++
#include <zephyr/zephyr.h>
#include <zephyr/drivers/i2c.h>

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include "binary.h"
#include "ssd1306.h"

static uint8_t _vccstate;
static int16_t _width, _height, WIDTH, HEIGHT, cursor_x, cursor_y;
static uint8_t textsize, rotation;
static uint16_t textcolor, textbgcolor;
bool wrap,   // If set, 'wrap' text at right edge of display
     _cp437; // If set, use correct CP437 charset (default is off)

#include "glcdfont.c"

// the memory buffer for the LCD

#define I2C0_NODE DT_NODELABEL(oled_display)
static const struct i2c_dt_spec oled_i2c = I2C_DT_SPEC_GET(I2C0_NODE);

static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = {


          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xD0, 0x00, 0x00, 0xC0,
          0x80, 0x40, 0x40, 0x80, 0x80, 0x40, 0x40, 0x80, 0x00, 0x00, 0xC0, 0x80, 0x40, 0x40, 0x80, 0x80,
          0x40, 0x40, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00,
          0xE0, 0x10, 0xE0, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x80, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0xC0,
          0x80, 0x40, 0x40, 0x80, 0x00, 0x00, 0x80, 0x40, 0x40, 0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
          0xF0, 0x10, 0x10, 0x10, 0x10, 0xE0, 0x00, 0x00, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x10, 0x10, 0x10, 0x0F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F,
          0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x00,
          0x00, 0x00, 0x1F, 0x00, 0x00, 0x47, 0x38, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x07,
          0x00, 0x00, 0x00, 0x07, 0x18, 0x07, 0x00, 0x00, 0x0F, 0x10, 0x10, 0x10, 0x0F, 0x00, 0x00, 0x1F,

          #if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16)
          0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x4F, 0x50, 0x50, 0x48, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x1F, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x08,
          0x08, 0x08, 0x10, 0x20, 0x00, 0x00, 0xF8, 0x08, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00,
          0x00, 0x00, 0xF8, 0x08, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0xC0, 0x20, 0x20, 0x20, 0xC0,
          0x00, 0x00, 0xE0, 0x40, 0x20, 0x20, 0xC0, 0x40, 0x20, 0x20, 0xC0, 0x00, 0x00, 0xC0, 0x20, 0x20,
          0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x10, 0x90, 0x48, 0x48, 0x88, 0xC8, 0x10,
          0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x20, 0x20, 0xC0, 0x00, 0x00, 0xF8, 0x88, 0x88,
          0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0xF8, 0x88, 0x88, 0x88, 0x08, 0x00, 0x00, 0xE0, 0x58, 0x48,
          0x48, 0x88, 0x00, 0x00, 0x10, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x03, 0x04, 0x08,
          0x08, 0x08, 0x04, 0x02, 0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x07, 0x09, 0x09, 0x09, 0x05,
          0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x08, 0x08,
          0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x10, 0x27, 0x48, 0x48, 0x48, 0x4F, 0x48, 0x28,
          0x24, 0x13, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x00,
          0x00, 0x01, 0x06, 0x08, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x08,
          0x08, 0x07, 0x00, 0x00, 0x08, 0x0C, 0x0A, 0x09, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          #if (SSD1306_LCDHEIGHT == 64)
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x80, 0x40, 0x20, 0x20, 0x20, 0x40, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x80, 0xE0, 0x80,
          0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
          0x00, 0xE0, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x80, 0x00,
          0x80, 0x80, 0x00, 0x00, 0x80, 0xC0, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
          0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
          0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x22, 0x12, 0x0E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x20,
          0x00, 0x3F, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3F, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x3F, 0x00,
          0x00, 0x3F, 0x11, 0x20, 0x20, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xFF, 0x11,
          0x20, 0x20, 0x1F, 0x00, 0x00, 0x3F, 0x00, 0x01, 0x0E, 0x30, 0x0E, 0x01, 0x0E, 0x30, 0x0E, 0x01,
          0x00, 0x1F, 0x20, 0x20, 0x20, 0x1F, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x9F,
          0xA0, 0xA0, 0x91, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          #endif
          #endif
};

#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; }
#define adagfxswap(a, b) { int16_t t = a; a = b; b = t; }

// Return the size of the display (per current rotation)
int16_t ssd1306_width(void)
{
        return _width;
}

int16_t ssd1306_height(void)
{
        return _height;
}

void set_rotation(uint8_t x)
{
        rotation = (x & 3);
        switch (rotation) {
        case 0:
        case 2:
                _width  = WIDTH;
                _height = HEIGHT;
                break;
        case 1:
        case 3:
                _width  = HEIGHT;
                _height = WIDTH;
                break;
        }
}

void ssd1306_command(uint8_t c)
{
        uint8_t dta_send[] = {0x00, c};
        i2c_write_dt(&oled_i2c, dta_send, 2);
}

void ssd1306_begin(uint8_t vccstate, bool reset)
{
        _vccstate = vccstate;

        _width    = WIDTH;
        _height   = HEIGHT;
        cursor_y  = cursor_x    = 0;
        textsize  = 1;
        textcolor = textbgcolor = 0xFFFF;
        wrap      = true;
        _cp437    = false;

        _width = WIDTH = SSD1306_LCDWIDTH;
        _height = HEIGHT = SSD1306_LCDHEIGHT;
        rotation  = 0;


        if (reset) {
                k_sleep(K_MSEC(1));
        }

#if defined SSD1306_128_32
        // Init sequence for 128x32 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);        // 0xD5
        ssd1306_command(0x80);                              // the suggested ratio 0x80

        ssd1306_command(SSD1306_SETMULTIPLEX);              // 0xA8
        ssd1306_command(0x1F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);          // 0xD3
        ssd1306_command(0x0);                               // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);        // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                // 0x20
        ssd1306_command(0x00);                              // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                // 0xDA
        ssd1306_command(0x02);
        ssd1306_command(SSD1306_SETCONTRAST);               // 0x81
        ssd1306_command(0x8F);
        ssd1306_command(SSD1306_SETPRECHARGE);              // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x22);
        }
        else {
                ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);             // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);       // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);             // 0xA6
#endif

#if defined SSD1306_128_64
        // Init sequence for 128x64 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);        // 0xD5
        ssd1306_command(0x80);                              // the suggested ratio 0x80
        ssd1306_command(SSD1306_SETMULTIPLEX);              // 0xA8
        ssd1306_command(0x3F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);          // 0xD3
        ssd1306_command(0x0);                               // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);        // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                // 0x20
        ssd1306_command(0x00);                              // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                // 0xDA
        ssd1306_command(0x12);
        ssd1306_command(SSD1306_SETCONTRAST);               // 0x81
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x9F);
        }
        else {
                ssd1306_command(0xCF);
        }
        ssd1306_command(SSD1306_SETPRECHARGE);              // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x22);
        }
        else {
                ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);             // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);       // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);             // 0xA6
#endif

#if defined SSD1306_96_16
        // Init sequence for 96x16 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);        // 0xD5
        ssd1306_command(0x80);                              // the suggested ratio 0x80
        ssd1306_command(SSD1306_SETMULTIPLEX);              // 0xA8
        ssd1306_command(0x0F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);          // 0xD3
        ssd1306_command(0x00);                               // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);        // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                // 0x20
        ssd1306_command(0x00);                              // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                // 0xDA
        ssd1306_command(0x2); //ada x12
        ssd1306_command(SSD1306_SETCONTRAST);               // 0x81
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0xAF);
        }
        ssd1306_command(SSD1306_SETPRECHARGE);              // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x22);
        }
        else {
                ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);             // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);       // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);             // 0xA6
#endif

        ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
}



// the most basic function, set a single pixel
void ssd1306_draw_pixel(int16_t x, int16_t y, uint16_t color)
{
        if ((x < 0) || (x >= ssd1306_width()) || (y < 0) || (y >= ssd1306_height()))
                return;

        // check rotation, move pixel around if necessary
        switch (rotation) {
        case 1:
                ssd1306_swap(x, y);
                x = WIDTH - x - 1;
                break;
        case 2:
                x = WIDTH - x - 1;
                y = HEIGHT - y - 1;
                break;
        case 3:
                ssd1306_swap(x, y);
                y = HEIGHT - y - 1;
                break;
        }

        // x is which column
        switch (color) {
        case WHITE:
                buffer[x + (y / 8)*SSD1306_LCDWIDTH] |=  (1 << (y & 7));
                break;
        case BLACK:
                buffer[x + (y / 8)*SSD1306_LCDWIDTH] &= ~(1 << (y & 7));
                break;
        case INVERSE:
                buffer[x + (y / 8)*SSD1306_LCDWIDTH] ^=  (1 << (y & 7));
                break;
        }
}


void ssd1306_invert_display(uint8_t i)
{
        if (i) {
                ssd1306_command(SSD1306_INVERTDISPLAY);
        }
        else {
                ssd1306_command(SSD1306_NORMALDISPLAY);
        }
}


// startscrollright
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_right(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X00);
        ssd1306_command(0XFF);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

// startscrollleft
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_left(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X00);
        ssd1306_command(0XFF);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

// startscrolldiagright
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_diag_right(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA);
        ssd1306_command(0X00);
        ssd1306_command(SSD1306_LCDHEIGHT);
        ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X01);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

// startscrolldiagleft
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_diag_left(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA);
        ssd1306_command(0X00);
        ssd1306_command(SSD1306_LCDHEIGHT);
        ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X01);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

void ssd1306_stop_scroll(void)
{
        ssd1306_command(SSD1306_DEACTIVATE_SCROLL);
}

// Dim the display
// dim = true: display is dimmed
// dim = false: display is normal
void ssd1306_dim(bool dim)
{
        uint8_t contrast;

        if (dim) {
                contrast = 0; // Dimmed display
        }
        else {
                if (_vccstate == SSD1306_EXTERNALVCC) {
                        contrast = 0x9F;
                }
                else {
                        contrast = 0xCF;
                }
        }
        // the range of contrast to too small to be really useful
        // it is useful to dim the display
        ssd1306_command(SSD1306_SETCONTRAST);
        ssd1306_command(contrast);
}

void ssd1306_data(uint8_t c)
{
        uint8_t dta_send[] = {0x40, c};
        i2c_write_dt(&oled_i2c, dta_send, 2);
}

void ssd1306_display(void)
{
        ssd1306_command(SSD1306_COLUMNADDR);
        ssd1306_command(0); // Column start address (0 = reset)
        ssd1306_command(SSD1306_LCDWIDTH - 1); // Column end address (127 = reset)

        ssd1306_command(SSD1306_PAGEADDR);
        ssd1306_command(0); // Page start address (0 = reset)
#if SSD1306_LCDHEIGHT == 64
        ssd1306_command(7); // Page end address
#endif
#if SSD1306_LCDHEIGHT == 32
        ssd1306_command(3); // Page end address
#endif
#if SSD1306_LCDHEIGHT == 16
        ssd1306_command(1); // Page end address
#endif

        // I2C
        for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++)
        {
                uint8_t tmpBuf[17];
                // SSD1306_SETSTARTLINE
                tmpBuf[0] = 0x40;
                // data
                for (uint8_t j = 0; j < 16; j++) {
                        tmpBuf[j+1] = buffer[i];
                        i++;
                }
                i--;

                i2c_write_dt(&oled_i2c, tmpBuf, sizeof(tmpBuf));
        }
}

// clear everything
void ssd1306_clear_display(void)
{
        memset(buffer, 0, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8));
}

void ssd1306_draw_fast_hline(int16_t x, int16_t y, int16_t w, uint16_t color)
{
        bool __swap = false;
        switch (rotation) {
        case 0:
                // 0 degree rotation, do nothing
                break;
        case 1:
                // 90 degree rotation, swap x & y for rotation, then invert x
                __swap = true;
                ssd1306_swap(x, y);
                x = WIDTH - x - 1;
                break;
        case 2:
                // 180 degree rotation, invert x and y - then shift y around for height.
                x = WIDTH - x - 1;
                y = HEIGHT - y - 1;
                x -= (w - 1);
                break;
        case 3:
                // 270 degree rotation, swap x & y for rotation, then invert y  and adjust y for w (not to become h)
                __swap = true;
                ssd1306_swap(x, y);
                y = HEIGHT - y - 1;
                y -= (w - 1);
                break;
        }

        if (__swap) {
                ssd1306_draw_fast_vline_internal(x, y, w, color);
        }
        else {
                ssd1306_draw_fast_hline_internal(x, y, w, color);
        }
}

void ssd1306_draw_fast_hline_internal(int16_t x, int16_t y, int16_t w, uint16_t color)
{
        // Do bounds/limit checks
        if (y < 0 || y >= HEIGHT) {
                return;
        }

        // make sure we don't try to draw below 0
        if (x < 0) {
                w += x;
                x = 0;
        }

        // make sure we don't go off the edge of the display
        if ( (x + w) > WIDTH) {
                w = (WIDTH - x);
        }

        // if our width is now negative, punt
        if (w <= 0) {
                return;
        }

        // set up the pointer for  movement through the buffer
        register uint8_t *pBuf = buffer;
        // adjust the buffer pointer for the current row
        pBuf += ((y / 8) * SSD1306_LCDWIDTH);
        // and offset x columns in
        pBuf += x;

        register uint8_t mask = 1 << (y & 7);

        switch (color) {
        case WHITE:
                while (w--) {
                        *pBuf++ |= mask;
                };
                break;
        case BLACK:
                mask = ~mask;
                while (w--) {
                        *pBuf++ &= mask;
                };
                break;
        case INVERSE:
                while (w--) {
                        *pBuf++ ^= mask;
                };
                break;
        }
}

void ssd1306_draw_fast_vline(int16_t x, int16_t y, int16_t h, uint16_t color)
{
        bool __swap = false;
        switch (rotation) {
        case 0:
                break;
        case 1:
                // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
                __swap = true;
                ssd1306_swap(x, y);
                x = WIDTH - x - 1;
                x -= (h - 1);
                break;
        case 2:
                // 180 degree rotation, invert x and y - then shift y around for height.
                x = WIDTH - x - 1;
                y = HEIGHT - y - 1;
                y -= (h - 1);
                break;
        case 3:
                // 270 degree rotation, swap x & y for rotation, then invert y
                __swap = true;
                ssd1306_swap(x, y);
                y = HEIGHT - y - 1;
                break;
        }

        if (__swap) {
                ssd1306_draw_fast_hline_internal(x, y, h, color);
        }
        else {
                ssd1306_draw_fast_vline_internal(x, y, h, color);
        }
}


void ssd1306_draw_fast_vline_internal(int16_t x, int16_t __y, int16_t __h, uint16_t color)
{

        // do nothing if we're off the left or right side of the screen
        if (x < 0 || x >= WIDTH) {
                return;
        }

        // make sure we don't try to draw below 0
        if (__y < 0) {
                // __y is negative, this will subtract enough from __h to account for __y being 0
                __h += __y;
                __y = 0;

        }

        // make sure we don't go past the height of the display
        if ( (__y + __h) > HEIGHT) {
                __h = (HEIGHT - __y);
        }

        // if our height is now negative, punt
        if (__h <= 0) {
                return;
        }

        // this display doesn't need ints for coordinates, use local byte registers for faster juggling
        register uint8_t y = __y;
        register uint8_t h = __h;


        // set up the pointer for fast movement through the buffer
        register uint8_t *pBuf = buffer;
        // adjust the buffer pointer for the current row
        pBuf += ((y / 8) * SSD1306_LCDWIDTH);
        // and offset x columns in
        pBuf += x;

        // do the first partial byte, if necessary - this requires some masking
        register uint8_t mod = (y & 7);
        if (mod) {
                // mask off the high n bits we want to set
                mod = 8 - mod;

                // note - lookup table results in a nearly 10% performance improvement in fill* functions
                // register uint8_t mask = ~(0xFF >> (mod));
                static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
                register uint8_t mask = premask[mod];

                // adjust the mask if we're not going to reach the end of this byte
                if ( h < mod) {
                        mask &= (0XFF >> (mod - h));
                }

                switch (color) {
                case WHITE:
                        *pBuf |=  mask;
                        break;
                case BLACK:
                        *pBuf &= ~mask;
                        break;
                case INVERSE:
                        *pBuf ^=  mask;
                        break;
                }

                // fast exit if we're done here!
                if (h < mod) {
                        return;
                }

                h -= mod;

                pBuf += SSD1306_LCDWIDTH;
        }


        // write solid bytes while we can - effectively doing 8 rows at a time
        if (h >= 8) {
                if (color == INVERSE)  {  // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop
                        do  {
                                *pBuf = ~(*pBuf);

                                // adjust the buffer forward 8 rows worth of data
                                pBuf += SSD1306_LCDWIDTH;

                                // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
                                h -= 8;
                        }
                        while (h >= 8);
                }
                else {
                        // store a local value to work with
                        register uint8_t val = (color == WHITE) ? 255 : 0;

                        do  {
                                // write our value in
                                *pBuf = val;

                                // adjust the buffer forward 8 rows worth of data
                                pBuf += SSD1306_LCDWIDTH;

                                // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
                                h -= 8;
                        }
                        while (h >= 8);
                }
        }

        // now do the final partial byte, if necessary
        if (h) {
                mod = h & 7;
                // this time we want to mask the low bits of the byte, vs the high bits we did above
                // register uint8_t mask = (1 << mod) - 1;
                // note - lookup table results in a nearly 10% performance improvement in fill* functions
                static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
                register uint8_t mask = postmask[mod];
                switch (color) {
                case WHITE:
                        *pBuf |=  mask;
                        break;
                case BLACK:
                        *pBuf &= ~mask;
                        break;
                case INVERSE:
                        *pBuf ^=  mask;
                        break;
                }
        }
}


// Draw a circle outline
void ssd1306_draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
        int16_t f = 1 - r;
        int16_t ddF_x = 1;
        int16_t ddF_y = -2 * r;
        int16_t x = 0;
        int16_t y = r;

        ssd1306_draw_pixel(x0, y0 + r, color);
        ssd1306_draw_pixel(x0, y0 - r, color);
        ssd1306_draw_pixel(x0 + r, y0, color);
        ssd1306_draw_pixel(x0 - r, y0, color);

        while (x < y) {
                if (f >= 0) {
                        y--;
                        ddF_y += 2;
                        f += ddF_y;
                }
                x++;
                ddF_x += 2;
                f += ddF_x;

                ssd1306_draw_pixel(x0 + x, y0 + y, color);
                ssd1306_draw_pixel(x0 - x, y0 + y, color);
                ssd1306_draw_pixel(x0 + x, y0 - y, color);
                ssd1306_draw_pixel(x0 - x, y0 - y, color);
                ssd1306_draw_pixel(x0 + y, y0 + x, color);
                ssd1306_draw_pixel(x0 - y, y0 + x, color);
                ssd1306_draw_pixel(x0 + y, y0 - x, color);
                ssd1306_draw_pixel(x0 - y, y0 - x, color);
        }
}

void ssd1306_draw_circle_helper(int16_t x0, int16_t y0,
                                int16_t r, uint8_t cornername, uint16_t color)
{
        int16_t f     = 1 - r;
        int16_t ddF_x = 1;
        int16_t ddF_y = -2 * r;
        int16_t x     = 0;
        int16_t y     = r;

        while (x < y) {
                if (f >= 0) {
                        y--;
                        ddF_y += 2;
                        f     += ddF_y;
                }
                x++;
                ddF_x += 2;
                f     += ddF_x;
                if (cornername & 0x4) {
                        ssd1306_draw_pixel(x0 + x, y0 + y, color);
                        ssd1306_draw_pixel(x0 + y, y0 + x, color);
                }
                if (cornername & 0x2) {
                        ssd1306_draw_pixel(x0 + x, y0 - y, color);
                        ssd1306_draw_pixel(x0 + y, y0 - x, color);
                }
                if (cornername & 0x8) {
                        ssd1306_draw_pixel(x0 - y, y0 + x, color);
                        ssd1306_draw_pixel(x0 - x, y0 + y, color);
                }
                if (cornername & 0x1) {
                        ssd1306_draw_pixel(x0 - y, y0 - x, color);
                        ssd1306_draw_pixel(x0 - x, y0 - y, color);
                }
        }
}

void ssd1306_fill_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
        ssd1306_draw_fast_vline(x0, y0 - r, 2 * r + 1, color);
        ssd1306_fill_circle_helper(x0, y0, r, 3, 0, color);
}

// Used to do circles and roundrects
void ssd1306_fill_circle_helper(int16_t x0, int16_t y0, int16_t r,
                                uint8_t cornername, int16_t delta, uint16_t color)
{

        int16_t f     = 1 - r;
        int16_t ddF_x = 1;
        int16_t ddF_y = -2 * r;
        int16_t x     = 0;
        int16_t y     = r;

        while (x < y) {
                if (f >= 0) {
                        y--;
                        ddF_y += 2;
                        f     += ddF_y;
                }
                x++;
                ddF_x += 2;
                f     += ddF_x;

                if (cornername & 0x1) {
                        ssd1306_draw_fast_vline(x0 + x, y0 - y, 2 * y + 1 + delta, color);
                        ssd1306_draw_fast_vline(x0 + y, y0 - x, 2 * x + 1 + delta, color);
                }
                if (cornername & 0x2) {
                        ssd1306_draw_fast_vline(x0 - x, y0 - y, 2 * y + 1 + delta, color);
                        ssd1306_draw_fast_vline(x0 - y, y0 - x, 2 * x + 1 + delta, color);
                }
        }
}

// Bresenham's algorithm - thx wikpedia
void ssd1306_draw_line(int16_t x0, int16_t y0,
                       int16_t x1, int16_t y1,
                       uint16_t color)
{
        int16_t steep = abs(y1 - y0) > abs(x1 - x0);
        if (steep) {
                adagfxswap(x0, y0);
                adagfxswap(x1, y1);
        }

        if (x0 > x1) {
                adagfxswap(x0, x1);
                adagfxswap(y0, y1);
        }

        int16_t dx, dy;
        dx = x1 - x0;
        dy = abs(y1 - y0);

        int16_t err = dx / 2;
        int16_t ystep;

        if (y0 < y1) {
                ystep = 1;
        }
        else {
                ystep = -1;
        }

        for (; x0 <= x1; x0++) {
                if (steep) {
                        ssd1306_draw_pixel(y0, x0, color);
                }
                else {
                        ssd1306_draw_pixel(x0, y0, color);
                }
                err -= dy;
                if (err < 0) {
                        y0 += ystep;
                        err += dx;
                }
        }
}

// Draw a rectangle
void ssd1306_draw_rect(int16_t x, int16_t y,
                       int16_t w, int16_t h,
                       uint16_t color)
{
        ssd1306_draw_fast_hline(x, y, w, color);
        ssd1306_draw_fast_hline(x, y + h - 1, w, color);
        ssd1306_draw_fast_vline(x, y, h, color);
        ssd1306_draw_fast_vline(x + w - 1, y, h, color);
}

#if 0
void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y,
                                 int16_t h, uint16_t color)
{
        // Update in subclasses if desired!
        drawLine(x, y, x, y + h - 1, color);
}

void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y,
                                 int16_t w, uint16_t color)
{
        // Update in subclasses if desired!
        drawLine(x, y, x + w - 1, y, color);
}
#endif

void ssd1306_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
        // Update in subclasses if desired!
        for (int16_t i = x; i < x + w; i++) {
                ssd1306_draw_fast_vline(i, y, h, color);
        }
}

void ssd1306_fill_screen(uint16_t color)
{
        ssd1306_fill_rect(0, 0, _width, _height, color);
}

// Draw a rounded rectangle
void ssd1306_draw_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
        // smarter version
        ssd1306_draw_fast_hline(x + r, y, w - 2 * r, color);   // Top
        ssd1306_draw_fast_hline(x + r, y + h - 1, w - 2 * r, color); // Bottom
        ssd1306_draw_fast_vline(x, y + r, h - 2 * r, color);   // Left
        ssd1306_draw_fast_vline(x + w - 1, y + r, h - 2 * r, color); // Right
        // draw four corners
        ssd1306_draw_circle_helper(x + r, y + r, r, 1, color);
        ssd1306_draw_circle_helper(x + w - r - 1, y + r, r, 2, color);
        ssd1306_draw_circle_helper(x + w - r - 1, y + h - r - 1, r, 4, color);
        ssd1306_draw_circle_helper(x + r, y + h - r - 1, r, 8, color);
}

// Fill a rounded rectangle
void ssd1306_fill_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
        // smarter version
        ssd1306_fill_rect(x + r, y, w - 2 * r, h, color);

        // draw four corners
        ssd1306_fill_circle_helper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
        ssd1306_fill_circle_helper(x + r, y + r, r, 2, h - 2 * r - 1, color);
}

// Draw a triangle
void ssd1306_draw_triangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
        ssd1306_draw_line(x0, y0, x1, y1, color);
        ssd1306_draw_line(x1, y1, x2, y2, color);
        ssd1306_draw_line(x2, y2, x0, y0, color);
}

// Fill a triangle
void ssd1306_fill_triangle( int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
        int16_t a, b, y, last;

        // Sort coordinates by Y order (y2 >= y1 >= y0)
        if (y0 > y1) {
                adagfxswap(y0, y1);
                adagfxswap(x0, x1);
        }
        if (y1 > y2) {
                adagfxswap(y2, y1);
                adagfxswap(x2, x1);
        }
        if (y0 > y1) {
                adagfxswap(y0, y1);
                adagfxswap(x0, x1);
        }

        if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
                a = b = x0;
                if (x1 < a) a = x1;
                else if (x1 > b) b = x1;
                if (x2 < a) a = x2;
                else if (x2 > b) b = x2;
                ssd1306_draw_fast_hline(a, y0, b - a + 1, color);
                return;
        }

        int16_t
                dx01 = x1 - x0,
                dy01 = y1 - y0,
                dx02 = x2 - x0,
                dy02 = y2 - y0,
                dx12 = x2 - x1,
                dy12 = y2 - y1;
...

This file has been truncated, please download it to see its full contents.

SSD1306.h

C/C++
#ifndef __SSD1306_LIB_H
#define __SSD1306_LIB_H

#define pgm_read_byte(addr) (*(const unsigned char *)(addr))


#include <stdint.h>

typedef volatile uint8_t PortReg;
typedef uint32_t PortMask;


#define BLACK 0
#define WHITE 1
#define INVERSE 2

#define SSD1306_I2C_ADDRESS   (0x3C)  // 011110+SA0+RW - 0x3C or 0x3D
// Address for 128x32 is 0x3C
// Address for 128x64 is 0x3D (default) or 0x3C (if SA0 is grounded)

/*=========================================================================
    SSD1306 Displays
    -----------------------------------------------------------------------
    The driver is used in multiple displays (128x64, 128x32, etc.).
    Select the appropriate display below to create an appropriately
    sized framebuffer, etc.
    SSD1306_128_64  128x64 pixel display
    SSD1306_128_32  128x32 pixel display
    SSD1306_96_16
    -----------------------------------------------------------------------*/
#define SSD1306_128_64
//   #define SSD1306_128_32
//   #define SSD1306_96_16
/*=========================================================================*/


#if defined SSD1306_128_64
#define SSD1306_LCDWIDTH                  128
#define SSD1306_LCDHEIGHT                 64
#endif
#if defined SSD1306_128_32
#define SSD1306_LCDWIDTH                  128
#define SSD1306_LCDHEIGHT                 32
#endif
#if defined SSD1306_96_16
#define SSD1306_LCDWIDTH                  96
#define SSD1306_LCDHEIGHT                 16
#endif

#define SSD1306_SETCONTRAST 0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF

#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA

#define SSD1306_SETVCOMDETECT 0xDB

#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9

#define SSD1306_SETMULTIPLEX 0xA8

#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10

#define SSD1306_SETSTARTLINE 0x40

#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR   0x22

#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8

#define SSD1306_SEGREMAP 0xA0

#define SSD1306_CHARGEPUMP 0x8D

#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2

// Scrolling #defines
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A

#ifdef __cplusplus
extern "C" {
#endif

int16_t ssd1306_width(void);
int16_t ssd1306_height(void);
void set_rotation(uint8_t x);
void ssd1306_command(uint8_t c);
void ssd1306_begin(uint8_t vccstate, bool reset);
void ssd1306_draw_pixel(int16_t x, int16_t y, uint16_t color);
void ssd1306_invert_display(uint8_t i);
void ssd1306_start_scroll_right(uint8_t start, uint8_t stop);
void ssd1306_start_scroll_left(uint8_t start, uint8_t stop);
void ssd1306_start_scroll_diag_right(uint8_t start, uint8_t stop);
void ssd1306_start_scroll_diag_left(uint8_t start, uint8_t stop);
void ssd1306_stop_scroll(void);
void ssd1306_dim(bool dim);
void ssd1306_data(uint8_t c);
void ssd1306_display(void);
void ssd1306_clear_display(void);
void ssd1306_draw_fast_hline(int16_t x, int16_t y, int16_t w, uint16_t color);
void ssd1306_draw_fast_hline_internal(int16_t x, int16_t y, int16_t w, uint16_t color);
void ssd1306_draw_fast_vline(int16_t x, int16_t y, int16_t h, uint16_t color);
void ssd1306_draw_fast_vline_internal(int16_t x, int16_t __y, int16_t __h, uint16_t color);
void ssd1306_draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void ssd1306_draw_circle_helper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color);
void ssd1306_fill_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void ssd1306_fill_circle_helper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);
void ssd1306_draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void ssd1306_draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void ssd1306_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void ssd1306_fill_screen(uint16_t color);
void ssd1306_draw_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void ssd1306_fill_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void ssd1306_draw_triangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void ssd1306_fill_triangle( int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void ssd1306_draw_bitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void ssd1306_draw_bitmap_bg(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg);
void ssd1306_draw_xbitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
size_t ssd1306_write(uint8_t c);
void ssd1306_draw_char(int16_t x, int16_t y, uint8_t c, uint16_t color, uint16_t bg, uint8_t size);
void ssd1306_set_cursor(int16_t x, int16_t y);
int16_t ssd1306_get_cursor_x(void);
int16_t ssd1306_get_cursor_y(void);
void ssd1306_set_textsize(uint8_t s);
void ssd1306_set_textcolor(uint16_t c);
void ssd1306_set_textcolor_bg(uint16_t c, uint16_t b);
void ssd1306_set_textwrap(bool w);
uint8_t ssd1306_get_rotation(void);
void ssd1306_set_rotation(uint8_t x);
void ssd1306_cp437(bool x);
void ssd1306_putstring(char* buffer);
void ssd1306_puts(char* buffer);
#ifdef __cplusplus
}
#endif

#endif /* __SSD1306_LIB_H */

Credits

Mahmood ul Hassan
13 projects • 18 followers
Electronics Engineer with more than 13 years of experience in reverse engineering and test & measurement equipment designing
Contact

Comments

Please log in or sign up to comment.