SSD1306 skips every other line

Posted on
  • I have bought a couple of these I2C 128x32 OLED displays. They almost work with the SSD1306 module, however every other line is missing (i.e. pixels for y=0 are not displayed, pixels for y=1 appear in what is actually y=0, pixels for y=2 are not displayed, pixels for y=3 are displayed in what is actually y=1 and so on, pixels for y=63 appear in the last line which is actually y=31).

    I had a look at the SSD1306 module which has the screen size hard-coded (maybe not the best idea) to 128x64. But if I change it to 128x32 (which would be correct), I get the exact same output, only a few lines further down. It seems that these particular displays need to be interfaced like 128x64 pixel displays but you need to transform the y coordinate of every pixel in the buffer to 2*y+1. What is the best way to patch the modules to achieve this?

  • Hi Dennis,

    I think the problem might be how the display is initialised - if you look at the Adafruit Arduino code they use slightly different initialisation code depending on which display is used.

    It might be worth trying to make sure the initialisation is just like Adafruit's, and to then see if it works fine then - without having to mess with rows of pixels?

    If that works, I'm open to ideas about how to work it back into the module without breaking it for people that are currently using it - maybe a setDisplayType function that gets exported and that updates the relevant initialisation code?

  • In case there are others who (like me) got stumped by this these settings worked for me by referencing the arduino code @Gordon refers to above (thanks Gordon!)

    var initCmds = new Uint8Array([ 0xAe, // disp off
                 0xD5, // clk div
                 0x80, // suggested ratio
                 0xA8, 0x1F, // set multiplex: 0x1F for 128x32, 0x3F for 128x64
                 0xD3,0x0, // display offset
                 0x40, // start line
                 0x8D,extVcc?0x10:0x14, // charge pump
                 0x20,0x0, // memory mode
                 0xA1, // seg remap 1 
                 0xC8, // comscandec
                 0xDA,0x02, // set compins - use 0x12 for 128x64
                 0x81,extVcc?0x9F:0xCF, // set contrast
                 0xD9,extVcc?0x22:0xF1, // set precharge
                 0xDb,0x40, // set vcom detect
                 0xA4, // display all on
                 0xA6, // display normal (non-inverted)
                 0xAf // disp on
  • Thanks! So it's literally just line 4 and line 11?

    Maybe if the initialisation was modified to something like:

    require("SSD1306").connect(I2C1, go, {height:32});

    it could do it automatically?

  • Almost! You should also change the page max in flipCmds from 7 to 3 (but if you're just sending a 128x32 buffer then it still displays ok):

    var initCmds = new Uint8Array([ 0xAe, // disp off
                 0xD5, // clk div
                 0x80, // suggested ratio
                 0xA8, C.OLED_HEIGHT-1,
                 0xD3,0x0, // display offset
                 0x40, // start line
                 0x8D,extVcc?0x10:0x14, // charge pump
                 0x20,0x0, // memory mode
                 0xA1, // seg remap 1 (use 0xA0 to flip horizontally)
                 0xC8, // comscandec (use 0xC0 to flip vertically)
                 0xDA, (C.OLED_HEIGHT==64)?0x12:0x02,
                 0x81,extVcc?0x9F:0xCF, // set contrast
                 0xD9,extVcc?0x22:0xF1, // set precharge
                 0xDb,0x40, // set vcom detect
                 0xA4, // display all on
                 0xA6, // display normal (non-inverted)
                 0xAf // disp on
    var flipCmds = [0x21, // columns
         0, C.OLED_WIDTH-1,
         0x22, // pages
         0, (C.OLED_HEIGHT==64)?0x7:0x03];

    The only other issue I had with the module was a lack of support for a CS pin. Adding that would be nice.

  • Thanks! It started out as I2C, so CS was never an issue. Are you using it on a bus that's shared with something else then?

  • Yes, Adafruit's BMP183 sensor - the existing BMP085/BMP180 module is of course I2C. I have modified the existing module to use SPI but I haven't tested it and the display at the same time yet. At some point I will create a "BMP183" module for it.

  • Ok, just made some changes but not published them yet. Please you try:

    var g = require("https://raw.githubusercontent.c­om/espruino/EspruinoDocs/master/devices/­SSD1306.js").connectSPI(spi, dc,  rst, callback, { height: 32, cs: yourCSPin });
  • That doesn't work I'm afraid:

    var initCmds = new Uint8Array([ 
                 0xAe, // 0 disp off
                 0xD5, // 1 clk div
                 0x80, // 2 suggested ratio
                 0xA8, 0, // 3 set multiplex, height-1
                 0xD3,0x0, // 5 display offset
                 0x40, // 7 start line
                 0x8D, extVcc?0x10:0x14, // 8 charge pump
                 0x20,0x0, // 10 memory mode
                 0xA1, // 12 seg remap 1
                 0xC8, // 13 comscandec
                 0xDA, 0x12, // 14 set compins, height==64 ? 0x12:0x02,
                 0x81, extVcc?0x9F:0xCF, // 16 set contrast
                 0xD9, extVcc?0x22:0xF1, // 18 set precharge
                 0xDb, 0x40, // 20 set vcom detect
                 0xA4, // 22 display all on
                 0xA6, // 23 display normal (non-inverted)
                 0xAf // 24 disp on

    Line 5 should be the height-1 and line 12 is hardcoded to 0x12 (should be 0x02 for 32 lines) .

    // commands sent when sending data to the display
    var flipCmds = [
         0x21, // columns
         0, C.OLED_WIDTH-1,
         0x22, // pages
         0, 7];

    Line 6 should be 3 for 32 lines and 7 for 64.

    exports.connectSPI = function(spi, dc,  rst, callback, options) {
      var cs = options?options.cs:undefined;
      var oled = Graphics.createArrayBuffer(C.OLED_WIDTH,­C.OLED_HEIGHT,1,{vertical_byte : true});

    This code does not take into account a height set in the options.

  • Isn't that what the 'update' function does?­/blob/master/devices/SSD1306.js#L58

    Good point about the Graphics height though. That'll need changing

  • ahh...yes... I thought I must have missed something crucial! :)

    function update(options) {
      if (options && options.height) {
        initCmds[4] = options.height-1;
        initCmds[15] = options.height==64 ? 0x12 : 0x02;
        flipCmds[5] = (options.height>>3)-1;

    Line 3 should be:

    initCmds[3] = options.height-1;

    ...and won't that break backwards-compatibility? I think if no options are set you should create one and default in options.height = 64.

  • I'm pretty sure it's initCmds[4] if you count which byte it is - it's supposed to be the one after 0xA8. Did it not work for you then?

    But yes, you're totally right about the backwards compatibility - I was trying to save a few bytes by setting stuff to 0, and it didn't work :)

    edit: just updated and checked with the 128x64 displays and it works here for those, with or without the options. Could you check on your 128x32?

  • Sorry, yes, you're right about initCmds[4].

    I didn't work at the time. But I just plugged in the board this morning and it worked. So I don't know what was wrong before...probably user error ;) I don't know if I didn't save() before but I've reloaded the code (using the Github module) to the pico and it I can confirm it works! :) Thanks Gordon!

  • Ok, great! Thanks for checking - I'll update the site with it this week.

  • Happy to help!

    I think my issue before was that I was maybe trying to write to the display too soon. The display's datasheet (page 15) mentions a 100ms delay in the power-up sequence. The breakout board has only a VIN which I guess is attached to both VCC and VDD. So I can't attach VCC 100ms after VDD.

    With further testing I intermittently had the screen not turn on. While it looked like the connectSPI() completed (I switch off LED1/2 in the callback) nothing displayed. When it didn't work no commands I sent over SPI and operations on "g" seemed to get it to display.

    By doing a setTimeout() with a 100ms delay before initialising the display I seem to have got around the issue - I've tested it about 30 times and I haven't had a failure to get the display to work.

  • Great work, this also solved my problem. And I can report that it works also over I2C.

  • Do you think that adding a 100ms delay into the module itself would help with this?

  • What solved the problem with my display was the changed init sequence. Mine works fine without the delay.

    In order to account for the slow power-up of other displays, the module could accept another parameter delay in the connect function... but then the connect would become asynchronous and developers would still have to make sure they don't use it before it's ready, so the complexity remains. Maybe a hint in the documentation is enough instead.

  • Just to let you know that I have 128x32 display in DS-D6 fitness tracker that skips lines with current code

    0xDA, (C.OLED_HEIGHT==64)?0x12:0x02,

    Even if it is 128x32 it works better with 0xDA, 0x12. Looks like it has RAM like 128x64. Not sure if it is normal for other 128x32 but it can hold two 128x32 images and I can flip between them by changing start offset.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

SSD1306 skips every other line

Posted by Avatar for Dennis @Dennis