0.96' OLED display help

Hello all,

I am a complete new when it comes to LCDs, and programming in general. I recently purchased this small display, and I would just like to display a simply message on it so I can figure out how to go further with it. . I want to eventually use it to permanently display “Speed”, and have a section continually update to the current speed I am reading. Could someone help here? I have all the libraries, and tested the display with the correct library. The code I used to test is below, but it is very complicated to re-engineer to just say something simple.

Thanks in advance!

Joe

#include <Adafruit_SSD1306.h>

#include <Adafruit_GFX.h>



#include <SPI.h>
#include <Wire.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2


#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 };

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

void setup()   {                
  Serial.begin(9600);

  delay(2000);

  // Clear the buffer.
  display.clearDisplay();

  
  display.display();
  delay(2000);
  display.clearDisplay();

  // draw many lines
  testdrawline();
  display.display();
  delay(2000);
  display.clearDisplay();

  // draw rectangles
  testdrawrect();
  display.display();
  delay(2000);
  display.clearDisplay();

  // draw multiple rectangles
  testfillrect();
  display.display();
  delay(2000);
  display.clearDisplay();

  // draw mulitple circles
  testdrawcircle();
  display.display();
  delay(2000);
  display.clearDisplay();

  // draw a white circle, 10 pixel radius
  display.fillCircle(display.width()/2, display.height()/2, 10, WHITE);
  display.display();
  delay(2000);
  display.clearDisplay();

  testdrawroundrect();
  delay(2000);
  display.clearDisplay();

  testfillroundrect();
  delay(2000);
  display.clearDisplay();

  testdrawtriangle();
  delay(2000);
  display.clearDisplay();
   
  testfilltriangle();
  delay(2000);
  display.clearDisplay();

  // draw the first ~12 characters in the font
  testdrawchar();
  display.display();
  delay(2000);
  display.clearDisplay();

  // draw scrolling text
  testscrolltext();
  delay(2000);
  display.clearDisplay();

  // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("Hello, world!");
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.println(3.141592);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.print("0x"); display.println(0xDEADBEEF, HEX);
  display.display();
  delay(2000);
  display.clearDisplay();
  display.drawBitmap(30, 16,  logo16_glcd_bmp, 16, 16, 1);
  display.display();
display.invertDisplay(true);
  delay(1000); 
  display.invertDisplay(false);
  delay(1000); 
 testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH);
}


void loop() {
  
}


void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  uint8_t icons[NUMFLAKES][3];
 
  for (uint8_t f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS] = random(display.width());
    icons[f][YPOS] = 0;
    icons[f][DELTAY] = random(5) + 1;
    
    Serial.print("x: ");
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(" y: ");
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(" dy: ");
    Serial.println(icons[f][DELTAY], DEC);
  }

  while (1) {
    // draw each icon
    for (uint8_t f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, WHITE);
    }
    display.display();
    delay(200);
    
    // then erase it + move it
    for (uint8_t f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(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] > display.height()) {
 icons[f][XPOS] = random(display.width());
 icons[f][YPOS] = 0;
 icons[f][DELTAY] = random(5) + 1;
      }
    }
   }
}


void testdrawchar(void) {
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);

  for (uint8_t i=0; i < 168; i++) {
    if (i == '\n') continue;
    display.write(i);
    if ((i > 0) && (i % 21 == 0))
      display.println();
  }    
  display.display();
}

void testdrawcircle(void) {
  for (int16_t i=0; i<display.height(); i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, WHITE);
    display.display();
  }
}

void testfillrect(void) {
  uint8_t color = 1;
  for (int16_t i=0; i<display.height()/2; i+=3) {
    // alternate colors
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, color%2);
    display.display();
    color++;
  }
}

void testdrawtriangle(void) {
  for (int16_t i=0; i<min(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(display.width()/2, display.height()/2-i,
                     display.width()/2-i, display.height()/2+i,
                     display.width()/2+i, display.height()/2+i, WHITE);
    display.display();
  }
}

void testfilltriangle(void) {
  uint8_t color = WHITE;
  for (int16_t i=min(display.width(),display.height())/2; i>0; i-=5) {
    display.fillTriangle(display.width()/2, display.height()/2-i,
                     display.width()/2-i, display.height()/2+i,
                     display.width()/2+i, display.height()/2+i, WHITE);
    if (color == WHITE) color = BLACK;
    else color = WHITE;
    display.display();
  }
}

void testdrawroundrect(void) {
  for (int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i, display.height()/4, WHITE);
    display.display();
  }
}

void testfillroundrect(void) {
  uint8_t color = WHITE;
  for (int16_t i=0; i<display.height()/2-2; i+=2) {
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i, display.height()/4, color);
    if (color == WHITE) color = BLACK;
    else color = WHITE;
    display.display();
  }
}
   
void testdrawrect(void) {
  for (int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, WHITE);
    display.display();
  }
}

void testdrawline() {  
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(0, 0, i, display.height()-1, WHITE);
    display.display();
  }
  for (int16_t i=0; i<display.height(); i+=4) {
    display.drawLine(0, 0, display.width()-1, i, WHITE);
    display.display();
  }
  delay(250);
  
  display.clearDisplay();
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, WHITE);
    display.display();
  }
  for (int16_t i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, WHITE);
    display.display();
  }
  delay(250);
  
  display.clearDisplay();
  for (int16_t i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE);
    display.display();
  }
  for (int16_t i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE);
    display.display();
  }
  delay(250);

  display.clearDisplay();
  for (int16_t i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-1, 0, 0, i, WHITE);
    display.display();
  }
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, WHITE); 
    display.display();
  }
  delay(250);
}

void testscrolltext(void) {
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,0);
  display.clearDisplay();
  display.println("scroll");
  display.display();
 
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);    
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
}
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <Wire.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

void setup()   
{                
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("Speed");
  display.display();
}

void loop() 
{
}

Is there a “begin” to be called? (Sorry, I don’t have one of these displays)

I can see that the example may be a bit daunting. Just start with this:

#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

#include <SPI.h>
#include <Wire.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

void setup()   {                
  Serial.begin(9600);
  delay(2000);
}

void loop() {
  // Clear the buffer.
  display.clearDisplay();
  display.display();
  delay(2000);
  display.clearDisplay();
  // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("Hello, world!");
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.println(3.141592);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.print("0x"); display.println(0xDEADBEEF, HEX);
  display.display();
  delay(2000);  
}

Untested. But you should see how you can set the cursor position, size and colour of text.
And print strings, floats, variables, …

David.

Thank you for your reply, AWOL, but the code did not display anything. I am trying to figure out why.

David, thank you very much! Your code worked, so I can finally start messing with it to get it to display "Speed:" permanently, and then roll in the new speed value as it happens. Essentially I am building a radar detector. I will attach my code if either of you are interested.

Any additional insight on how it do this?

const uint16_t TICK_CNT = 3; // 255-(16MHz/1024/62Hz)  
static uint16_t freq = 0;
double sped = 0; //"speed" seems to be a reserved term

void setup() {

  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  
  Serial.begin(115200);

  noInterrupts();                     // disable all interrupts while we configure  
  // init Timer1 - 16-bit timer/counter
  TCNT1   = 0;                                  // start count at zero.        
  TCCR1B  |= _BV(CS12) | _BV(CS11) | _BV(CS10); // Increment T1 input on each positive edge 
                                                // using an external source. Table 16-5, pg 139.
  
  // init Timer2 - 8-bit timer/counter
  TCNT2   = TICK_CNT;                 // preload Timer2 to interrupt every 250 msec
  TIMSK2  = _BV(TOIE2);               // enable the Timer2 overflow interrupt 
  TCCR2B  |= _BV(CS22) |_BV(CS21) | _BV(CS20);   // init clock prescaler to 1024. Table 18-9, page 164.
  interrupts();                       // enable all interrupts
  
  Serial.println("Ready...");
}

ISR(TIMER1_OVF_vect) {
  // do nothing. this is just a dummy ISR in case it actually overflows.
  Serial.println("Inside Timer1 Overflow Interrupt.");
}

ISR(TIMER2_OVF_vect) {
  //Serial.print("TCNT1: ");
  //Serial.println(TCNT1);
  freq = TCNT1;
  //Serial.println(freq);
  TCNT1 = 0;
  TCNT2 = TICK_CNT;
  
   //Serial.print("Freq: ");
      //Serial.print(freq, DEC);
      //Serial.print(" Hz, Speed: ");
      //Serial.print(sped, 0);
      //Serial.println(" mph");
}

void loop() {
  
  if (freq >= 2) {
      freq = freq * 62;      // multiple the frequency * 4 (using leftshift 2 places). 250ms*4 = 1 sec.
      sped = freq * .03225;  // multiplying freq * 0.03225 will give speed in mph. 31Hz == 1 mph.
                             // see: http://www.microwave-solutions.com/contents/en-uk/d13_System_Design.html
      Serial.print("Freq: ");
      Serial.print(freq, DEC);
      Serial.print(" Hz, Speed: ");
      Serial.print(sped, 0);
      Serial.println(" mph");
      freq = 0;
  }
  //else {
     // freq = 0;      // multiple the frequency * 4 (using leftshift 2 places). 250ms*4 = 1 sec.
      //sped = 0 * .03225;  // multiplying freq * 0.03225 will give speed in mph. 31Hz == 1 mph.
                             // see: http://www.microwave-solutions.com/contents/en-uk/d13_System_Design.html
      //Serial.print("Freq: ");
      //Serial.print(freq, DEC);
      //Serial.print(" Hz, Speed: ");
      //Serial.print(sped, 0);
      //Serial.print(" mph");
      //freq=0;
//}
}

A few "setCursor" and swap the "Serial.print" for "display.print" would seem to me to be all you need.

Like I said, I don't have one.

If you are happy that it displays the correct speed to the Serial terminal, you just use the same style of print() statements to the OLED e.g. display.print(). Followed by a display.display() to show it.

Personally, I do not like the way that you are calculating the speed. But it should give you a reasonable result.

David.

p.s. I am sure that AWOL's example should work ok. The text size, colour etc should start with reasonable default values. Of course, neither he nor I have your display.

David,

Do you have any suggestions? This if off of someone else’s code, and I do not like it either. I have yet to test it on a vehicle though, so I am unaware of its accuracy.

I am currently working on integrating the two codes together to display what I need.

AWOL,

Thank you for trying to help. I do appreciate it. I am still altering your code to see if I can get it to work.

It should work. Only you know your wheel sizes, rpm etc. Most Arduinos have got crap clock accuracy because they use a ceramic resonator and not a crystal.

My only criticism is that you will have some jitter depending on how your sensor lines up with the timing window. Normally you would take continuous readings and display a "running average". This would make your motor car tachometer look steady and reasonable to read. Otherwise you have it alternating 99.9mph and 100.0mph so quickly that it is unreadable.

David.

Edit. freq should be volatile

If it doesn't work, leave and concentrate on the code that you have that works. All I did was cut out the stuff that didn't look to me to be important.

(BTW, it's 0.96", not 0.96' ;) )

AWOL,

I did notice that after the fact haha. Glad someone else did.

David,

Makes sense, but it seems to work decent as is like you said.

I cannot get the display to actually display the speed from my code. Below is what I have so far. It shows everything I want, BUT the speed.

I want it to be next to "Speed:" in the space as shown in the picture.

Any help?

https://drive.google.com/file/d/0B_rP32a4BFPJQ1VnOUdPT0hIaVU/view?usp=sharing

A photo isn't much use. Code, on the other hand . . .

Sorry about that. Here it is.

#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

#include <SPI.h>
#include <Wire.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
const uint16_t TICK_CNT = 3; // 255-(16MHz/1024/62Hz)  
static uint16_t freq = 0;
double sped = 0; //"speed" seems to be a reserved term

void setup()   { 
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial.begin(9600);
  noInterrupts();                     // disable all interrupts while we configure  
  // init Timer1 - 16-bit timer/counter
  TCNT1   = 0;                                  // start count at zero.        
  TCCR1B  |= _BV(CS12) | _BV(CS11) | _BV(CS10); // Increment T1 input on each positive edge 
                                                // using an external source. Table 16-5, pg 139.
  
  // init Timer2 - 8-bit timer/counter
  TCNT2   = TICK_CNT;                 // preload Timer2 to interrupt every 250 msec
  TIMSK2  = _BV(TOIE2);               // enable the Timer2 overflow interrupt 
  TCCR2B  |= _BV(CS22) |_BV(CS21) | _BV(CS20);   // init clock prescaler to 1024. Table 18-9, page 164.
  interrupts();                       // enable all interrupts
  
  Serial.println("Ready...");
  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  delay(2000);
   // Clear the buffer.
  display.clearDisplay();
  display.display();
  delay(2000);
  display.clearDisplay();
  // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("RADAR Speed Detector");
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.println(" Using Doppler Shift ");
  display.setTextSize(2);
  display.setTextColor(WHITE);
 display.println("Speed:");//display.print("HI");
 display.println("MPH");
 //display.print("hi");
  display.display();
  delay(2000);  
}

ISR(TIMER1_OVF_vect) {
  // do nothing. this is just a dummy ISR in case it actually overflows.
  Serial.println("Inside Timer1 Overflow Interrupt.");
}

ISR(TIMER2_OVF_vect) {
  //Serial.print("TCNT1: ");
  //Serial.println(TCNT1);
  freq = TCNT1;
  //Serial.println(freq);
  TCNT1 = 0;
  TCNT2 = TICK_CNT;
  
   //Serial.print("Freq: ");
      //Serial.print(freq, DEC);
      //Serial.print(" Hz, Speed: ");
      //Serial.print(sped, 0);
      //Serial.println(" mph");
}


void loop() {
 
  if (freq >= 2) {
      freq = freq * 62;      // multiple the frequency * 4 (using leftshift 2 places). 250ms*4 = 1 sec.
      sped = freq * .03225;  // multiplying freq * 0.03225 will give speed in mph. 31Hz == 1 mph.
                             // see: http://www.microwave-solutions.com/contents/en-uk/d13_System_Design.html
      Serial.print("Freq: ");
      Serial.print(freq, DEC);
      Serial.print(" Hz, Speed: ");
      display.setCursor(3,0);
      display.print(sped);
      Serial.print(sped, 0);
      Serial.println(" mph");
      freq = 0;
  }
  //else {
     // freq = 0;      // multiple the frequency * 4 (using leftshift 2 places). 250ms*4 = 1 sec.
      //sped = 0 * .03225;  // multiplying freq * 0.03225 will give speed in mph. 31Hz == 1 mph.
                             // see: http://www.microwave-solutions.com/contents/en-uk/d13_System_Design.html
      //Serial.print("Freq: ");
      //Serial.print(freq, DEC);
      //Serial.print(" Hz, Speed: ");
      //Serial.print(sped, 0);
      //Serial.print(" mph");
      //freq=0;
//}
}

You have no pictures.

I am suspicious of your maths. You create a Timer2 "window" of 16.12ms (i.e. 62Hz). But your comments say 250ms.

This means that "freq" will get updated before you have managed to send your text out to Serial or OLED. The Serial probably manages because it is interrupt-driven and you have a high baud rate.

Your OLED does not use interrupts and will be relatively slow.

Personally, I would either count freq over 250ms or use a running average of 16.12ms windows. Having calculated "freq" or "average_freq", I would use a copy in the display. e.g.

  if (freq >= 2) {
      int copy = freq * 62;
      sped = copy * .03225;  // multiplying freq * 0.03225 will give speed in mph. 31Hz == 1 mph.
                             // see: http://www.microwave-solutions.com/contents/en-uk/d13_System_Design.html
      Serial.print("Freq: ");
      Serial.print(copy, DEC);
      Serial.print(" Hz, Speed: ");
      Serial.print(sped, 0);
      Serial.println(" mph");
      freq = 0;
  }

Untested.

David.

David,

Like I have said before, it is someone else's code, so I am at its mercy. I am a hardware engineer, so this aspect isn't my forte unfortunately :blush: