Counting and Timing a shock sensor

Everything is working fine. (at least as fine as it was) It’s just not displaying correctly.

Data sheet for new display

http://www.newhavendisplay.com/specs/NH … ZW-AG5.pdf

Data sheet for original display. (same library worked for intermediate 4x20 display.)

http://www.sparkfun.com/datasheets/LCD/ … tended.pdf

I’m not sure how to tell what library works for which display.

I thought you’d still be on vaca ! I see that the wiring is the same, as far as functions and pin numbers go. So if everything is wired to the correct number then the next thing to check is the jumper settings. Apparently there are some jumpers that can select btw 2 different parallel modes and 1 serial mode. I didn’t see their locations on the diagram in the datasheet, so you’ll have to hunt for them by name (per page 4). Meanwhile I’ll try to figure out which, if any, mode corresponds to the library you’ve been using (HD44780 compatible).

A poster below asked what serial driver worked for this display. Someone, apparently from Newhaven tech support, indicated their controller was compatible with the HD44780 controller. I’d bet that also applies to the parallel interface so my guess is … jumper settings.

http://www.newhavendisplay.com/forum/vi … =10&t=2630

Also interesting …

http://www.newhavendisplay.com/forum/vi … =15&t=3649

FWIW I believe the Hitachi controller above is the HD44780 controller the Arduino library “knows”. So if we can get the timing right … and timing seems to be … jumpers. One thing that will need to be added is a lcd.cursor(0,0) after a clear as this controller doesn’t do an automatic return to “home” upon a clear.

http://en.wikipedia.org/wiki/Hitachi_HD … controller

My GoogleFu is strong tonite ! This blog, the answer has, seek we. Read it fully, I have not, but mods to the Arduino library, he has.

http://www.elcojacobs.com/controlling-a … h-arduino/

If you have any luck in getting the above library functions copied into your PC and everything all properly placed, here’s a version of the prior code that has 5 msec waits stuck anywhere I thought they might be needed and an lcd.setCursor(0,0) after every lcd.clear I could find. The new “clear” might reset the cursor, but I figure why not do it explicitly … just in case. Now I’m not sure if the initialization sequence at the link above needs also needs to be in the code explicitly or whether that happens behind the scenes when the (? revised ?) function lcd.begin is called. I guess you’ll find out if it doesn’t work. If not, then add it in. You may have to add in whatever new “includes” are needed by the library functions at the link above.

So perhaps this can return to the goodness it had at at one point.

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);

// set pin numbers
const int StartPin = 7;       // the number of the Start pushbutton pin
const int TargetAPin = 2;     // the number of the A targets input pin
const int TargetBPin = 3;     // the number of the B targets input pin
const int BuzzerPin = 10;     // the number of the buzzer output pin
const int LEDPin = 13;        // the number of the LED output pin
const int ScrollPin = 6;      // the number of the scroll button input pin

// initialize the constants
const unsigned long MaxTime = 60001;     // the max time the timer can run for = 60 secs
const unsigned long WaitTime = 1000;     // wait time btw start and timer running = 2 secs plus debounce
const unsigned long DB_delay = 1000;     // set a debounce wait time of 1000 msecs
const unsigned long BuzzTime5 = 500;     // set the on time for the buzzer, 500 msecs
const unsigned long BuzzTime2 = 200;     // set the on time for the buzzer, 200 msecs
const unsigned long BuzzTime10 = 1000;   // set the on time for the buzzer, 1000 msecs
const unsigned int FreqHi = 2000;        // High frequency of buzzer tone
const unsigned int FreqLo = 1000;        // Low frequency of buzzer tone
const byte MaxHits = 24;                 // Maximum number if hits allowed

// initialize global variables
volatile byte TimerState = 0;            // variable for state of timer, running or not running
volatile byte AupDtFlag = 0;             // variable indication an A hit has occurred
volatile byte BupDtFlag = 0;             // variable indication a B hit has occurred
volatile int secs = 0;                   // whole seconds of time for display
volatile int frac = 0;                   // fractional part of time for display
unsigned long StartTime = 0;             // variable to hold the start time
unsigned long BuzzTime = 500;            // variable to hold the buzzer on time
unsigned int Freq = 2000;                // variable for high or low buzzer tone
volatile byte A_count = 0;               // variable to hold the number of A hits
volatile byte B_count = 0;               // variable to hold the number of B hits
volatile unsigned long A_Times[MaxHits]; // array to hold up to MaxHits hit times for target A
volatile unsigned long B_Times[MaxHits]; // array to hold up to MaxHits hit times for target B
byte DisplayIndex = 0;                   // variable for controlling what's displayed
byte diMax = 99;                         // Display index maximum

byte StopHits = MaxHits;                 // Number if hits allowed before stopping
unsigned long StopTime = MaxTime;        // Time that the timer will stop at 
byte CoF = 0;                            // initialize the mode to be standard
//byte CoF = 1;                          // initialize the mode to be quick draw
//byte CoF = 2;                          // initialize the mode to be 21
byte NumShooters = 1;                    // initialize the number of shooters to 1
//byte NumShooters = 2;                  // initialize the number of shooters to 2
byte RanDly = 0;                         // random delay disabled
//byte RanDly = 1;                       // random delay enabled

void setup()
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  delay(5);
  lcd.clear();
  delay(5);
  lcd.setCursor(0, 0);
  delay(5);
  lcd.print("    Shot Timer 1    ");
  lcd.setCursor(0, 1);
  delay(5);
  lcd.print("    Initializing    ");
  delay(3000);

  // initialize output pins
  pinMode(BuzzerPin, OUTPUT);
  pinMode(LEDPin, OUTPUT);
  digitalWrite(BuzzerPin, LOW);
  digitalWrite(LEDPin, LOW);
   
  // initialize the input pins with internal pullups
  pinMode(StartPin, INPUT);
  pinMode(TargetAPin, INPUT);
  pinMode(TargetBPin, INPUT);
  digitalWrite(StartPin, HIGH);   
  digitalWrite(TargetAPin, HIGH);
  digitalWrite(TargetBPin, HIGH);

  // for now use pin 6 as button input
  pinMode(ScrollPin, INPUT);
  digitalWrite(ScrollPin, HIGH);

  // opens serial port, sets data rate to 9600 bps
  Serial.begin(9600);

  // setup ext pins as interrupts
  //attachInterrupt(0, ISR_A, FALLING);
  //attachInterrupt(1, ISR_B, FALLING);

  lcd.setCursor(0, 1);
  delay(5);
  lcd.print("      Ready       ");

  ClearData();
}


void loop()
{
  if (TimerState) // is timer running
  {
    // timer is running
    
    // send data to display if there have been any hits
    if (AupDtFlag == 1 || BupDtFlag == 1)
    {
      UpdateLCD();
    }
    
    // timer is running so now check for any stop conditions
    if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) >= StopTime) || A_count >= StopHits || B_count >= StopHits)
    {
      // update the LCD running display one last time
      UpdateLCD();
      
      // stop the timer
      StopTimer();
      tone(BuzzerPin, FreqLo, BuzzTime10);
      
      // just for the moment send times to PC for debug
      SendTimes();
      
      // delay enough to debounce stop button
      delay(DB_delay);
      
      // Setup the review-stopped state display based on number shooters and hits
      if(NumShooters == 1)
      {
        diMax = byte(A_count/8) - 1;   // DI goes 0 to number of displays - 1
        if (A_count % 8)                 // diMax indicates last screen of hits
        {
          diMax ++;                      // limit lines per number of hits
        }
        DisplayIndex = diMax;            // display will show list of hit times
      }
      else
      {
        diMax = byte(max(A_count,B_count)/4) - 1; // find how many whole screens
        if (max(A_count,B_count) % 4)
        {
          diMax++ ;              // add another screen if partial exists
        }
        DisplayIndex = diMax;    // display will show list of hit times
      }
    }
  }
  else
  {
  // timer is stopped look for start button or review button pushes

    // check for scroll button pushes, change display state as reqd
    if (digitalRead(ScrollPin) == LOW)
    {
      DisplayIndex++;             // increment DisplayIndex upon push
      if (DisplayIndex > diMax)
      {
        DisplayIndex = 0;         // wrap around of scrolling action
      }
      // update display
      UpdateLCD();
      delay(DB_delay);
    }

    // check to see if start button has been pushed
    if (digitalRead(StartPin) == LOW)
    {
      // start button pushed
      // Do the following debug code only to get system running
      // This will send message to PC to show button was pushed
      Serial.println("Timer is running");
      
      // turn on the LED to show timer is running
      digitalWrite(LEDPin, HIGH);
      
      // show the timer has been started
      lcd.clear();
      delay(5);
      lcd.setCursor(0, 0);
      delay(5);
      lcd.print("    Shot Timer v1   ");
      lcd.setCursor(0, 1);
      delay(5);
      lcd.print("     is running     ");
      lcd.setCursor(0, 2);
      delay(5);
      lcd.print("      Shooter:      ");
      lcd.setCursor(0, 3);
      delay(5);
      lcd.print("    --STANDBY--     ");
      
      // this will change when config mode is added ?
      switch (CoF)
      {
        case 1:
          //this is for quick draw mode
          StopTime = 10001;
          StopHits = 6;
          break;
        case 2:
          //this is for 21 mode
          StopTime = 1501;
          StopHits = MaxHits;
          break;
        default: 
          // the default = standard timer mode
          StopTime = MaxTime;
          StopHits = MaxHits;
      }
      
      // delay the Wait Time from start button push
      randomSeed(millis());
      // delay is min delay plus random if enabled
      delay(WaitTime);               // 1 sec 
      if(RanDly)
      {
        delay(random(2000, 4000));   // total delay btw 3 and 5 secs
      }
      
      // save the starting time of this run
      StartTime = millis();
      
      // set state of timer to running
      TimerState = 1;
      
      // turn on interrupts as needed for 1 or 2 shooters
      attachInterrupt(0, ISR_A, FALLING);
      if (NumShooters > 1)
      {
        attachInterrupt(1, ISR_B, FALLING);
      }
      
      // clear all the prior runs data
      ClearData();      

      // now buzz the speaker for 0.5 secs
      tone(BuzzerPin, FreqHi, BuzzTime5);
    }
  }
}


void StopTimer()
{
  // turn off interrupts
  detachInterrupt(0);
  detachInterrupt(1);
  TimerState = 0;
  // turn the LED off to show timer is stopped
  digitalWrite(LEDPin, LOW);
}


void ClearData()
{
  A_count = 0; 
  B_count = 0;
  for (byte i = 0; i < MaxHits; i++)
  {
    A_Times[i] = 0;
    B_Times[i] = 0;
  }
}


void SendTimes()
// temp routine to send data to serial monitor
{
  Serial.println("Timer is stopped");
  Serial.println("Here are the times for Shooter A");
  Serial.print("Number of hits : ");
  Serial.print("\t");
  Serial.println(A_count);
  Serial.println("A Hit and split times are : ");
  byte i = 0;
  unsigned long Split = 0;
  for (i = 0; i < A_count; i++)
  {
    Serial.print(i+1);
    Serial.print("\t");
    Serial.print(A_Times[i]);
    Serial.print("\t");
    if (i)
    {
      Split = A_Times[i] - A_Times[i-1];
      Serial.print(Split);
    }
    Serial.println("\t");
  }
  Serial.println("Here are the times for Shooter B");
  Serial.print("Number of hits : ");
  Serial.print("\t");
  Serial.println(B_count);
  Serial.println("B Hit and split times are : ");
  for (i = 0; i < B_count; i++)
  {
    Serial.print(i+1);
    Serial.print("\t");
    Serial.print(B_Times[i]);
    Serial.print("\t");
    if (i)
    {
      Split = B_Times[i] - B_Times[i-1];
      Serial.print(Split);
    }
    Serial.println("\t");
  }
}


void ISR_A()
{
  if(TimerState)
  {
    // store the hit time 
    A_Times[A_count] = millis() - StartTime;
    // increment the hit count and array index
    ++A_count;
    // set the Hit flag so other stuff will update
    AupDtFlag = 1;
  }
}


void ISR_B()
{
  if(TimerState)
  {
    // store the hit time 
    B_Times[B_count] = millis() - StartTime;
    // increment the hit count and array index
    ++B_count;
    // set the Hit flag so other stuff will update
    BupDtFlag = 1;
  }
}


void UpdateLCD()
{
AupDtFlag = 0;
BupDtFlag = 0;
// routine to format lines of data to be sent to LCD
// presently assume 4 lines of 20 characters
// formatting based number of shooters and timer state
  if(TimerState)
  {
  // timer is running
    // display number of hits so far
    lcd.clear();
    delay(5);
    lcd.setCursor(0, 0);
    delay(5);
    lcd.print(" A: # hits = ");
    if(A_count < 10)
    {
      lcd.print(' ');
    } 
    lcd.print(A_count);
    // now display the time of last hit
    lcd.setCursor(0, 1);
    delay(5);
    lcd.print("A Hit time = ");
    if(A_count)
    {
      TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
    }
    lcd.setCursor(0, 2);
    delay(5);
    // Chose 1 or 2 shooter portion of display
    if(NumShooters == 1)
    {
      // this is for single A shooter running display
      // display the split time btw last hit and prior hit
      unsigned long Split = 0;
      if (A_count > 1)
      {
        Split = A_Times[A_count-1] - A_Times[A_count-2];
      }
      lcd.print("Split time = ");
      TimeConvert(Split); // convert split time
      lcd.setCursor(0, 3);
      delay(5);
      // compute A hit factor
      //float HF = A_count/A_Times[A_count - 1];
      //lcd.print("Hit factor = ");
      //char buf[6];
      //PString(buf, sizeof(buf), HF);  // convert FP to char
      //lcd.print(buf);
    }      
    else
    {
      // this is for 2 shooter running display
      lcd.print(" B: # hits = ");
      if(B_count < 10)
      {
        lcd.print(' ');
      } 
      lcd.print(B_count);
      lcd.setCursor(0, 3);
      delay(5);
      lcd.print("B Hit time = ");
      if(B_count)
      {
        TimeConvert(B_Times[B_count-1]); // Now for B times
      }
    }    
  }
  else
  {
  // timer is stopped
    if(NumShooters == 1)
    {
      // this is single shooter review display
      lcd.clear();
      delay(5);
      lcd.setCursor(0, 0);
      delay(5);
      byte k = 0;                    // k will hold LCD line number
      byte j = DisplayIndex << 3;    // index for first hit time displayed
      byte jj = (A_count % 8);   // jj holds number hits on last display
      if (DisplayIndex == diMax)     // diMax indicates last screen of hits
      {
        jj = j + min(jj,4);          // limit lines according to number of hits
      }
      else
      {
        jj = j + 4;                  // there are enough hits to fill 4 lines
      }
      for (byte i = j; i < jj; i++)
      {
        k = (i % 4);
        lcd.setCursor(0,k);
        delay(5);
        lcd.print('A');
        if(i<9)
        {
         lcd.print(' ');             // add a space for single digit numbers
        }
        lcd.print(i+1);              // send hit # to LCD
        lcd.print(": ");
        TimeConvert(A_Times[i]);     // convert hit time to XX.xx format
        if(A_Times[i+4])             // if there's a non-zero time
        {
          lcd.print(' ');            // print a space
          if((i+4)<9)
          {
           lcd.print(' ');           // add a space for single digit numbers
          }          
          lcd.print(i+5);            // send hit # to LCD
          lcd.print(": ");
          TimeConvert(A_Times[i+4]); // convert hit time to XX.xx format
        }
      }
    }      
    else
    {
      // this is 2 shooter review display
      lcd.clear();
      delay(5);
      lcd.setCursor(0, 0);
      delay(5);
      byte k = 0;                     // k will hold LCD line number
      byte j = DisplayIndex << 2;     // index for first hit time displayed
      byte jj = max(A_count,B_count); // init jj to max hit count
      if (DisplayIndex == diMax)      // diMax indicates last screen of hits
      {
        jj = j + (jj % 4);            // limit lines per number of hits
      }
      else
      {
        jj = j + 4;                   // there are enough hits to fill 4 lines
      }
      for (byte i = j; i < jj; i++)
      {
        k = (i % 4);
        lcd.setCursor(0,k);
        delay(5);
        if(i<9)
        {
          lcd.print(' ');              // for hits 1-9 put in a leading space
        }
        lcd.print(i+1);               // print hit number
        lcd.print(" A: ");
        TimeConvert(A_Times[i]);      // convert hit time to XX.xx format
        if(B_Times[i]);
        {
          lcd.print(" B: ");
          TimeConvert(B_Times[i]);      // convert hit time to XX.xx format
        }
      }  
    }
  }
}


void TimeConvert(unsigned long input)
{
// takes the msecs as argument and sets the time for that hit as a XX.xxs string

  if (input != 0)         // Make sure there's a value...
  {
    secs = int((input + 5)/1000L);         // round msecs into whole secs
    frac = int(((input + 5) % 1000L)/10L); // find fractional rounded secs

    if (secs < 10)
    {
      lcd.print(' ');               // for sub 10 sec times add leading space
    }
    lcd.print(secs);                // now add whole secs 
    lcd.print('.');                 // now add period

    if (frac < 10)
    {
      lcd.print('0');               // If frac < 10 then pad with a zero
    }
    lcd.print(frac);                // now add fractional secs
  }
  else
  {
    lcd.print("     ");         // If the input variable is zero set time to 6 blank spaces
  }
}

ps - did those goodies include the 2 axis slidey joystick thingee ?

I’ll try the new code/library when I get home… hopefully.

Mrs. sspbass had car trouble this morning so I may get to turn wrenches for a while.

Yes, I got the joystick in as well!

sspbass:
Yes, I got the joystick in as well!

OK, if I have time I'll modify the starter UI code to use the joystick.

How do I download the files on this site for the library?

http://code.google.com/p/uberfridge/sou … EDFourBit/

sspbass:
How do I download the files on this site for the library?

http://code.google.com/p/uberfridge/sou … EDFourBit/

I can’t try it while at work or with the work PC but look here …

http://blog.functionalfun.net/2008/09/e … -from.html

FWIW - given the short length of the files, can you just copy ‘n’ paste them into notepad from the browser, save/name the files appropriately and stick them where they need to go ? (use the view raw file button)

Do I not need to worry about maintaining the .cpp and the .h file extensions?

Hmmmm, I’m looking at the .cpp file to figure out what has to change to use the new stuff. First of I see he has written code for a 7 wire interface to the LCD from the Arduino; the data lines D4-D7, the “read” strobe RS, the read/write line RW and the enable line EN. IIRC you’ve only been using a 6 wire interface, dropping the RW line. I’ll have to check to see if you need to wire that up to get that init stuff to function or whether (likely IMO) it’s just need if you’re to read from the LCD (something you’re not doing nor planning to I think).

Other than that (and 1 more thing) it looks like we need to add an #include OLEDFourBit.h (? .cpp ?) and change all the lcd.XXXXX() to OLEDFourBit.XXXXX(). ie - lcd.setCursor(0,0) to OLEDFourBit.setCursor(0,0).

The one more thing I don’t get yet is whether we need an OLEDFourBit.init() in the code and whether that needs to be before or after the OLEDFourBit.begin() ? The later function has comments about initializing the LCD but … I dunno. Perhaps DK will pick up on this thread again and help us children out. :shifty:

ps: yes maintain the full filenames with extensions; .h, .cpp, .whatever

So how the heck do I save a notepad file .txt as a .cpp file?

FWIW I think his .cpp file set the RW line = HIGH in only 2 places, the read-from-LCD function and the wait busy function, neither of which we’re going to use. That said his initial definition of the interface and subsequent functions do use the name and so if you don’t supply a pin number (like the 6 that are when the code says LiquidCrystal lcd(12, 11, 5, 4, 9, 8); then the compiler will puke and cry. So I think his .cpp file needs to be changed to eliminate any mention of the RW line. That’s not too bad to do and I’ll attempt to do that now. However I have to wonder if the RW line, left unconnected, is pulled down to a logic LOW, which is what I think it needs to be to write (not read) data to the LCD. Can you check the datasheet re: that ? If not you may need to add a pulldown (or hard ground ?) to that pin on the LCD.

At least this way it won’t be using up another pin, I don’t think there’s really any spares left that aren’t going to be used ???

D0/1 : serial interface to USB/PC

D2 : target A input

D3 : target b input

D4 : lcd data

D5 : lcd data

D6 : select button (for UI)

D7 : start/stop button

D8 : lcd data

D9 : lcd data

D10: buzzer pin

D11: lcd control

D12: lcd control

D13: running/stopped LED (I guess this could go)

sspbass:
So how the heck do I save a notepad file .txt as a .cpp file?

Copy and paste the words into Notepad and then do a Save As.

Where is says *.txt, erase all that and type XYZ.cpp or XYZ.h.

It’ll save as you typed w/o adding the .txt. Try it !

Actually save his original file as XYZ.old. I have to modify it anyway and that new file will have to be named XYZ.cpp

(XYZ being OLEDFourBit, but check my spelling/typing)

Actually nevermind, just copy the .txt files into the proper directory. I have to mod the .h file as well. So I’ll post both of those for you to copy to that saame place. And then do more mods on the code I posted earlier … so forget that code !

OK, here are the modified .h and .cpp files. I removed all references to the rw line and got rid of the “readChar” function and any mention of it. I also changed the “WaitBusy” function to a simple 5 ms wait, which is what he had done originally (and had commented out). So this may run slow but hopefully will compile and run. Should be real real interesting ! :liar:

The OLEDFourBit.h file…

#ifndef OLEDFourBit_h
#define OLEDFourBit_h

#include <inttypes.h>
#include "Print.h"

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x28
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_ENGLISH_JAPANESE   0x00
#define LCD_WESTERN_EUROPEAN_1 0x01
#define LCD_ENGLISH_RUSSIAN    0x02
#define LCD_WESTERN_EUROPEAN_2 0x03


class OLEDFourBit : public Print {
public:
  OLEDFourBit(uint8_t rs, uint8_t enable,
		uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  
  void init(uint8_t rs, uint8_t enable,
	    uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
    
  void begin(uint8_t cols, uint8_t rows);

  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t); 
  virtual void write(uint8_t);
  void command(uint8_t);
  
private:
  void send(uint8_t, uint8_t);
  void write4bits(uint8_t);
  void pulseEnable();
  void waitBusy();

  uint8_t _rs_pin; // LOW: command.  HIGH: character.
  uint8_t _enable_pin; // activated by a HIGH pulse.
  uint8_t _busy_pin;
  uint8_t _data_pins[4];

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;
  uint8_t _initialized;
  uint8_t _currline;
  uint8_t _numlines;
};

#endif

The OLEDFourBit.cpp file…

#include "OLEDFourBit.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "WProgram.h"

OLEDFourBit::OLEDFourBit(uint8_t rs, uint8_t enable,
			     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(rs, enable, d4, d5, d6, d7);
}

void OLEDFourBit::init(uint8_t rs, uint8_t enable,
			 uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  _rs_pin = rs;
  _enable_pin = enable;
  _busy_pin = d7;
  
  _data_pins[0] = d4;
  _data_pins[1] = d5;
  _data_pins[2] = d6;
  _data_pins[3] = d7; 


  pinMode(_rs_pin, OUTPUT);
  pinMode(_enable_pin, OUTPUT);
  
  _displayfunction = LCD_FUNCTIONSET | LCD_4BITMODE;
   
  begin(20, 4);  
}

void OLEDFourBit::begin(uint8_t cols, uint8_t lines) {
  _numlines = lines;
  _currline = 0;
  
  pinMode(_rs_pin, OUTPUT);
  pinMode(_enable_pin, OUTPUT);
  
  digitalWrite(_rs_pin, LOW);
  digitalWrite(_enable_pin, LOW);
  
  // SEE PAGE 20 of NHD-0420DZW-AY5 
  delayMicroseconds(50000); // wait 50 ms just to be sure tha the lcd is initialized
  
  // Now we pull both RS and R/W low to begin commands
  
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], LOW);
  }

  delayMicroseconds(100000);
  write4bits(0x03);
  delayMicroseconds(100000);
  write4bits(0x02);
  delayMicroseconds(10000);
  write4bits(0x02);
  delayMicroseconds(10000);
  write4bits(0x08);
   
  
  //command(0x28);
  delayMicroseconds(10000);
  
  command(0x08);	// Display off
  delayMicroseconds(10000);
	
  command(0x01);	// display clear
  delayMicroseconds(10000);

  command(0x06);	// Entry Mode Set:
  delayMicroseconds(10000);

	
  command(0x02);	// Home
  delayMicroseconds(10000);

  command(0x0C);	// display on/ cursor on/ cursor blink
  delayMicroseconds(10000);
	
	
}

/********** high level commands, for the user! */
void OLEDFourBit::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
}

void OLEDFourBit::home()
{
  command(LCD_RETURNHOME);  // set cursor position to zero
}

void OLEDFourBit::setCursor(uint8_t col, uint8_t row)
{
  uint8_t row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
  if ( row >= _numlines ) {
    row = 0;  //write to first line if out off bounds
  }
  
  command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

// Turn the display on/off (quickly)
void OLEDFourBit::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void OLEDFourBit::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void OLEDFourBit::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void OLEDFourBit::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void OLEDFourBit::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void OLEDFourBit::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void OLEDFourBit::scrollDisplayLeft(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void OLEDFourBit::scrollDisplayRight(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void OLEDFourBit::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void OLEDFourBit::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void OLEDFourBit::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void OLEDFourBit::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void OLEDFourBit::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
}

/*********** mid level commands, for sending data/cmds */

inline void OLEDFourBit::command(uint8_t value) {
  send(value, LOW);
  waitBusy();
}

inline void OLEDFourBit::write(uint8_t value) {
  send(value, HIGH);
  waitBusy();
}

/************ low level data pushing commands **********/

// write either command or data
void OLEDFourBit::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);
  
  write4bits(value>>4);
  write4bits(value);
}

void OLEDFourBit::pulseEnable(void) {
  digitalWrite(_enable_pin, HIGH);
  delayMicroseconds(100);    // enable pulse must be >450ns
  digitalWrite(_enable_pin, LOW);
}

void OLEDFourBit::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }
  delayMicroseconds(100);
  pulseEnable();
}

void OLEDFourBit::waitBusy(void) {
  delayMicroseconds(5000);
}

Well that was nice. I just typed a long post on how the new library doesn’t have a “print” function and so more stuff needs to change. Specifically the new OLED library has a “write” function, which I’ll guess sends ASCII characters to the LCD but doesn’t convert a numeric int or long or byte or … to an ASCII char, like print and lcd.print do. So before we go down that road (again) let’s do a simple “Hello World” test to see if the new library compiles and if anything intelligible can be displayed on this LCD.

#include "OLEDFourBit.h"

// initialize the library with the numbers of the interface pins
// with the OLED library this also seems to do the init and begin calls
OLEDFourBit OLEDFourBit(12, 11, 5, 4, 9, 8);


void setup()
{
  OLEDFourBit.clear();
  delay(5);
  OLEDFourBit.setCursor(0, 0);
  delay(5);
  OLEDFourBit.write("    Shot Timer 1    ");
  delay(5);
  OLEDFourBit.setCursor(0, 1);
  delay(5);
  OLEDFourBit.write("    Initializing    ");
  delay(3000);
}

void loop()
{
  OLEDFourBit.clear();
  delay(1000);
  OLEDFourBit.setCursor(0, 0);
  OLEDFourBit.write("Line 1");
  OLEDFourBit.write(" cursor check");
  OLEDFourBit.setCursor(0, 1);
  OLEDFourBit.write("    Line 2");
  OLEDFourBit.setCursor(0, 2);
  OLEDFourBit.write("         Line 3");
  OLEDFourBit.setCursor(0, 3);
  OLEDFourBit.write("              Line 4");
  delay(5000);
}

Assuming the above works then we’ll need to change all the lcd.print(XYZ) to OLEDFourBit.write(XYZ) and convert all non-char XYZs to chars before “writing” them. I was planning on using the PString library for a similar use in the UI code, but IIRC we had problems with that. So if the above works, we’ll need to do a PString test to get that working … or find another method.

EDIT : Give the above a try but I’m not hopeful. The more I look at the code, the more I think a “write” sends a single character. To send a array of chars, we’ll need to come up with our own “print” function. That’s not too hard though. :liar:

When the above fails, try this.

#include "OLEDFourBit.h"

// initialize the library with the numbers of the interface pins
// with the OLED library this also seems to do the init and begin calls
OLEDFourBit OLEDFourBit(12, 11, 5, 4, 9, 8);

char buf[21];

void setup()
{
  OLEDFourBit.clear();
  delay(5);
  OLEDFourBit.setCursor(0, 0);
  delay(5);
  buf = ("    Shot Timer 1    ");
  writeString();
  delay(5);
  OLEDFourBit.setCursor(0, 1);
  delay(5);
  buf = ("    Initializing    ");
  writeString();
  delay(3000);
}

void loop()
{
  OLEDFourBit.clear();
  delay(1000);
  OLEDFourBit.setCursor(0, 0);
  buf = ("Line 1");
  writeString();
  buf = (" cursor check");
  writeString();
  OLEDFourBit.setCursor(0, 1);
  buf = ("    Line 2");
  writeString();
  OLEDFourBit.setCursor(0, 2);
  buf = ("         Line 3");
  writeString();
  OLEDFourBit.setCursor(0, 3);
  buf = ("              Line 4");
  writeString();
  delay(5000);
}

void writeString()
{
  int i = 0;
  while (buf(i) != '\0')
  {
   OLEDFourBit.write(buf(i));
   i++;
  }
}

Then try this …

#include "OLEDFourBit.h"

// initialize the library with the numbers of the interface pins
// with the OLED library this also seems to do the init and begin calls
OLEDFourBit OLEDFourBit(12, 11, 5, 4, 9, 8);

char buf[21];
unsigned int count = 0;

void setup()
{
  OLEDFourBit.clear();
  delay(5);
  OLEDFourBit.setCursor(0, 0);
  delay(5);
  buf = ("    Incrementing    ");
  writeString();
  delay(5);
  OLEDFourBit.setCursor(0, 1);
  delay(5);
  buf = ("     count test     ");
  writeString();
  delay(3000);
}

void loop()
{
  OLEDFourBit.clear();
  delay(5);
  OLEDFourBit.setCursor(0, 0);
  buf = ("Count = ");
  writeString();
  PString(buf, 20, count);
  writeString();
  count++;
  delay(1000);
}

void writeString()
{
  int i = 0;
  while (buf(i) != '\0')
  {
   OLEDFourBit.write(buf(i));
   i++;
  }
}

Here’s a minor mystery. The Beer Fridge Guy says he had to “modify” the LCD library to get his display to work. His new library above looks to me to add or replace certain functions, and with new names. I don’t know how or if the 2 can be used together … so I didn’t above. But if I look at the whole project code he put on Google here :

http://code.google.com/p/uberfridge/sou … inting.pde

… I don’t see any of the new functions used. Indeed he seems to use the same lcd.print function. So I’m confused as to what we have to do.