U8g2: Graphics Library for Monochrome OLEDs and LCDs

olikraus:
Indeed I guess, this will be the most simple solution. Just remove all other existing "_arduino" versions and use your own one.

Hey, Oliver, I got it working! Thanks for your guidance. Video.

Here is my constructor:

U8G2_KS0108_128X64_F u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE,
                          U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE,
                          /*enable=*/ D0, /*dc=*/ U8X8_PIN_NONE, /*cs0=*/ U8X8_PIN_NONE, 
                          /*cs1=*/ U8X8_PIN_NONE, /*cs2=*/ U8X8_PIN_NONE, /* reset=*/  U8X8_PIN_NONE); // Set R/W to low!

And here is the relavent section I updated in U8x8lib.cpp:

...
#else

#define GCLD_CS0 5
#define GCLD_CS1 7
#define GCLD_DC 6

/* this function completly replaces u8x8_byte_ks0108*/
extern "C" uint8_t u8x8_byte_arduino_ks0108(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  uint8_t i, b;
  uint8_t *data;
  static uint8_t ks0108_control = 0;

  switch(msg)
  {
    case U8X8_MSG_BYTE_SEND:
      data = (uint8_t *)arg_ptr;

      while( arg_int > 0 )
      {
 b = *data;
 data++;
 arg_int--;

        SPI.transfer(ks0108_control);
        SPI.transfer(b);

        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);
 u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->data_setup_time_ns);
        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);
 u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->write_pulse_width_ns);
        delayMicroseconds(5);
      }

      break;
      
    case U8X8_MSG_BYTE_INIT:
      SPI.begin();

      /* disable chipselect */
      bitWrite(ks0108_control, GCLD_CS0, 0); 
      bitWrite(ks0108_control, GCLD_CS1, 0); 
      /* no wait required here */
      
      /* ensure that the enable signal is low */
      u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);
      break;
    case U8X8_MSG_BYTE_SET_DC:

      bitWrite(ks0108_control, GCLD_DC, arg_int); 
      break;
    case U8X8_MSG_BYTE_START_TRANSFER:
      SPI.beginTransaction(SPISettings(100000000UL, MSBFIRST, SPI_MODE0));
      bitWrite(ks0108_control, GCLD_CS0, bitRead(arg_int, 0)); 
      bitWrite(ks0108_control, GCLD_CS1, bitRead(arg_int, 1)); 
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);
      break;
    case U8X8_MSG_BYTE_END_TRANSFER:
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);
      bitWrite(ks0108_control, GCLD_CS0, bitRead(arg_int, 0)); 
      bitWrite(ks0108_control, GCLD_CS1, bitRead(arg_int, 1));
      SPI.endTransaction(); 
      break;
    default:
      return 0;
  }
  return 1;
}

#endif
...

Any ideas for improvements?

I am using u8x8lib with a 128x32 SSD1306 HW I2C for a 1 button stopwatch that shows longest time measured, current elapsed time and previous measured time. Top row show "Best", "Timer" and "Final" text. Under this text, the respective time appears. However, my coding knowledge as only allowed me to get times is milliseconds to be displayed.

How can I make times show in minutes and seconds like "00:00"? (it is for a free flight glider remote control and flight times are under 30min or so).

/*Single button stopwatch with button deboucing. First click: start stopwatch;
  Second click: stop and display final time and longest session time on SSD1306
  using u8g2 U8x8 lib*/

#include <U8x8lib.h>

U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);

#define KeyH 2 //H key on my Arduino Uno Clone connected to D2

byte oldKeyHstate = LOW;

unsigned long clickTime = 0;
unsigned long flightTimer = 0;
unsigned long finalflightTime = 0;
unsigned long topflightTime = 0;

const unsigned long debounceTime = 50;  // milliseconds
unsigned long switchPressTime;  // when the switch last changed state

byte buttonPushCounter = 0;

void setup() {
  //  Serial.begin(9600);
  pinMode(KeyH, INPUT); //board has a pull down resistor, normally LOW or 0

  u8x8.begin();
  u8x8.setFlipMode(1);
}

void loop() {

  u8x8.setFont(u8x8_font_victoriamedium8_r);

  u8x8.drawString(0, 0, "TOP");
  u8x8.setCursor(0, 3);
  //u8x8.print(topflightTime);

  u8x8.drawString(4, 0, "TIMER");
  u8x8.drawString(10, 0, "FINAL");

  byte KeyHstate = digitalRead(KeyH);

  if (KeyHstate != oldKeyHstate) {
    if ((millis() - switchPressTime) > debounceTime) {
      switchPressTime = millis ();
      oldKeyHstate = KeyHstate;
      if (KeyHstate == HIGH) {
        buttonPushCounter++;
        clickTime = millis();
      }
    }
  }

  if (buttonPushCounter % 2 == 0) {
    u8x8.setCursor(10, 3);
    u8x8.print(finalflightTime);
  } else {
    flightTimer = (millis() - clickTime); //elapsed flight time
    finalflightTime = flightTimer;

    u8x8.setCursor(4, 3);
    u8x8.print(flightTimer);
  }
  if (finalflightTime >= topflightTime) {
    topflightTime = finalflightTime;

  }

  //  Serial.print("clickTime: ");
  //  Serial.print(clickTime);
  //  Serial.print("\t");
  //  //  Serial.print("flightTimer: ");
  //  Serial.print(flightTimer);
  //  Serial.print("\t");
  //  //  Serial.print("pushes: ");
  //  Serial.print(buttonPushCounter);
  //  Serial.println("\t");

}

filbot:
The first problem is that I was unable to use the F() syntax with u8g2.drawStr function to store strings in flash memory rather than RAM. On u8g I use u8g.drawStr(XX, YY, F("Text")); and it works fine, but when I tried u8g2.drawStr(XX, YY, F("Text")); I receive lot of compiler errors. It only works without the syntax from the function: u8g2.drawStr(XX, YY, "Text");. Is this expected to happen?

It's not a huge problem because I changed the code to u8g2.setCursor(XX,YY); and u8g2.print(F("Text")); but this way gave me a lot of more work since I couldn't just replace the text "u8g." to "u8g2.".

Well, indeed, this is not implemented for drawStr(). The F() macro is very specific to AVR uC. If you really need this functionally, please create an issue for the u8g2 github project.

filbot:
The second one is about custom fonts. I found a post where you told someone how to use bdf2u8g. I followed your instructions and it worked fine, I removed unwanted characters from 3 or 4 fonts that I use to save space but I couldn't figure out how to use these fonts with u8g2. Is it possible to use them with u8g2?

There is a new tool for this, which is now called "bdfconv". The issue tracker already has several questions about this, so you will find some instructions on this.
Also the FAQ covers the new font converter:

Let me know, if you need further help with bdfconv.exe.

Oliver

PaulRB:
Hey, Oliver, I got it working! Thanks for your guidance. Video.

Wow. Looks good. Thanks for sharing your results.

Oliver

FranciscoB:
I am using u8x8lib with a 128x32 SSD1306 HW I2C for a 1 button stopwatch that shows longest time measured, current elapsed time and previous measured time. Top row show "Best", "Timer" and "Final" text. Under this text, the respective time appears. However, my coding knowledge as only allowed me to get times is milliseconds to be displayed.

How can I make times show in minutes and seconds like "00:00"? (it is for a free flight glider remote control and flight times are under 30min or so).

Well, not really a U8g2 question, but let me try to answer this.

Input: milliseconds
Output: seconds, minutes

So the question is, how to calculate this. Seconds you get by deviding the milliseconds by 1000:
seconds = milliseconds / 1000;
however, this will be more than 60 seconds if the elapsed time is more than 60 seconds. For the 00:00 display you are interessted in the reminder,so you have to apply the modulo operation also:
seconds = (milliseconds / 1000) % 60;

For the minutes you need to calculate the seconds and than again device by 60:
minutes = (milliseconds / 1000) / 60;

with some optimization, this could look like this:

seconds = (milliseconds / 1000)
minutes = (seconds) / 60;
seconds = seconds % 60;
u8x8.print(u8x8_u8toa(minutes, 2));
u8x8.print(":");
u8x8.print(u8x8_u8toa(seconds, 2));

Oliver

Thank you very much for your clear and concise explanation, the code is now working.

Here is a quick video:

Here is the code:

/*Single button stopwatch with button deboucing. First click: start stopwatch;
  Second click: stop and display final time and longest session time on SSD1306
  using u8g2 U8x8 lib*/

#include <U8x8lib.h>

U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);

#define Button 2 //H key on my Arduino Uno Clone connected to D2

byte oldButtonstate = LOW;

unsigned long clickTime = 0;
unsigned long Timer = 0;
unsigned long oldTime = 0;
unsigned long topTime = 0;

unsigned long Timer_sec = 0;
unsigned long Timer_min = 0;

unsigned long topTime_sec = 0;
unsigned long topTime_min = 0;

const unsigned long debounceTime = 50;  // milliseconds
unsigned long switchPressTime;  // when the switch last changed state

byte buttonPushCounter = 0;

void setup() {

  pinMode(Button, INPUT); //board has a pull down resistor, normally LOW or 0

  u8x8.begin();
  u8x8.setFlipMode(1);
}

void loop() {

  u8x8.setFont(u8x8_font_victoriamedium8_r);

  u8x8.drawString(1, 3, "BEST"); // topTime

  u8x8.setCursor(8, 3);
  u8x8.print(u8x8_u8toa(topTime_min, 2));
  u8x8.print(":");
  u8x8.print(u8x8_u8toa(topTime_sec, 2));

  u8x8.drawString(1, 0, "TIME");
  u8x8.drawString(1, 1, "LAST");

  byte Buttonstate = digitalRead(Button);

  if (Buttonstate != oldButtonstate) {
    if ((millis() - switchPressTime) > debounceTime) {
      switchPressTime = millis ();
      oldButtonstate = Buttonstate;
      if (Buttonstate == HIGH) {
        buttonPushCounter++;
        clickTime = millis();
      }
    }
  }

  if (buttonPushCounter % 2 == 0) {
    u8x8.setCursor(8, 1);
    u8x8.print(u8x8_u8toa(Timer_min, 2));
    u8x8.print(":");
    u8x8.print(u8x8_u8toa(Timer_sec, 2));
  } else {
    Timer = (millis() - clickTime); //elapsed flight time

    Timer_sec = (Timer) / 1000;
    Timer_min = (Timer_sec) / 60;
    Timer_sec = Timer_sec % 60;

    u8x8.setCursor(8, 0);
    u8x8.print(u8x8_u8toa(Timer_min, 2));
    u8x8.print(":");
    u8x8.print(u8x8_u8toa(Timer_sec, 2));
    

    oldTime = Timer;
  }
  if (oldTime >= topTime) {
    topTime = oldTime;

    topTime_sec = (topTime) / 1000;
    topTime_min = (topTime_sec) / 60;
    topTime_sec = topTime_sec % 60;
  }
}

Oliver,

Thanks so much for taking the time for us. I have a problem I'd like to ask you about. When I run U8G, everything works fine. When I try running (basically) the same code, and the same wiring, with U8G2, I get 'snow' or 'static' on the screen (see pictures). I am running both sets of code on a Mega 2560 and the display is a 240x64 T6963c. I suspect it has something to do with the constructors?

My U8G constructor:
U8GLIB_T6963_240X64 u8g(8, 9, 10, 11, 4, 5, 6, 7, 14, 15, 17, 18, 16);

My U8G2 constructor:
U8G2_T6963_240X64_1_8080 U8G2_T6963_240X64_1_8080 u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, 17, 14, 15, 16);

Sometime, instead of 'snow' I see the code I intended to view but it flickers and even a portion of it is ghosted on the opposite side of the screen.

What do you think is going on?

Thanks for your help in advance.

Screen Shot 2017-03-05 at 2.31.58 PM.png

@FranciscoB
What happend to the wire, which was connected to pin 18 (RD line of the display)?
If you didn't reconnect the RD line to 5V, then you should do a "digitalWrite(18, 1)".

Oliver

Still no support for 4-bit images?

alidaf:
Still no support for 4-bit images?

No, U8g2 will concentrate on 1-bit devices only.

Oliver

I have managed to adapt U8g2 to drive the ks0108 display using pcf8574 i2c chips. I have been asked for the schematic and code. This seems like a good place to put them, where it could be easily found and perhaps of use to others. I hope that's OK.

U8x8lib.cpp (29.6 KB)

Paul, Thank you so much! downloading now and will be wiring this up in the morning

does this allow for control of the backlight?

also do i just replace the ccp in the u8g2 lib?

Backlight control is through analogWrite(D8, level). Don't forget level is 0..1023 on Wemos.

Yes, just replace the file.

I am impressed by your neat wiring. You could use a 16-bit expander instead of two 8-bit chips. Likewise, SPI or I2C.

However, it would be considerably simpler to just use a ST7920 in the first place.

Of course, I2C costs absolutely nothing in terms of pins (if you have an existing bus). SPI requires a separate chip-select for each device (and would be faster).

David.

David, you already gave all that advice before, earlier in this very thread. But maybe WilliamB has not seen those posts, where I used 74hc595, which was quicker.

Ah-ha. But I did not compliment you on your neat wiring until #64.

And the $64000 question is: will you ever build this on a real pcb?

David.

Yes, but probably a just a piece of tripad board that I can solder to the back of the display, acting as an adaptor and minimizing the connections between that and another board with the Arduino/Wemos on it.

But first I have to make the final decision between the 74hc595 and the pcf8475 circuit.

MCP23S17 would be better than two HC595.
MCP2317 or PCF8575 would be better than two PCF8574.

Of course, another approach is to use complete MCU e.g. a small PIC or AVR.

Yes, a little backpack is fairly easy to make on small piece of protoboard but it takes quite a bit of time to do the hand wiring and soldering. I don't think it is worth getting a pcb made.

David.

PaulRB:
I have managed to adapt U8g2 to drive the ks0108 display using pcf8574 i2c chips. I have been asked for the schematic and code. This seems like a good place to put them, where it could be easily found and perhaps of use to others. I hope that's OK.

Sure no problem to add the documentation here.
Yet one little question: If I remember previous discussion correctly, don't you also require a special constructor?
Maybe you can add the constructor line for the u8g2 object also.

Great work.

Oliver

Hi,

will this library work with SSD1306 128x64 over i2c? If so, how do I configure it? I can get mine to work with adafruit libs but not with the u8g :frowning:

Bob