/* Code to receive time signal from a GPS card and sent it via SPI 0.2 2013-12-27 Andrew Amos Add sending on I2C 1.0 2014-09-19 Andrew Amos Pull up hall effect inputs 1.1 2014-10-07 Andrew Amos Change Echo test to be called GPS test. This echoes output directly from / to the GPS unit on the USB bus. Add new Echo test, which disables GPS output and echoes USB input on the I2C bus. 1.1.1 2014-01-20 Andrew Amos Fix bugs in Echo test. */ #include // See http://arduino.cc/en/Reference/SoftwareSerial ///* Seems to give better results with SoftwareSerial.h changed to: #define SS_MAX_RX_BUFF 256 // RX buffer size //*/ #include #include // include LCD library #include // See http://playground.arduino.cc/Code/Time //#include #include #define TIME_MSG_LEN 10 // time sync to PC is HEADER followed by Unix time_t as ten ASCII digits TinyGPS gps; #define RXPIN 2 #define TXPIN 3 SoftwareSerial nss(RXPIN, TXPIN); #define CHECK_RATE 1000 int GPSyear; byte GPSmonth, GPSday, GPShour, GPSminute, GPSsecond; byte GPShundredth; unsigned long fix_age; long previousMillis = 0; LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 ); //Pins for the freetronics 16x2 LCD shield. LCD: ( RS, E, LCD-D4, LCD-D5, LCD-D6, LCD-D7 ) byte prevGPSminute = 0; // Push button defines #define BUTTON_ADC_PIN A0 // A0 is the button ADC input #define BUTTON_NONE 0 // #define BUTTON_RIGHT 1 // #define BUTTON_UP 2 // #define BUTTON_DOWN 3 // #define BUTTON_LEFT 4 // #define BUTTON_SELECT 5 // byte buttonJustPressed = false; //this will be true after a ReadButtons() call if triggered byte buttonJustReleased = false; //this will be true after a ReadButtons() call if triggered byte buttonWas = BUTTON_NONE; //used by ReadButtons() for detection of button events byte testState = 0; #define STATE_NORMAL 0 #define STATE_ECHO 1 #define STATE_GPS 2 #define HAND_MINUTE A2 #define HAND_HOUR A3 int prevMinute; byte hand_processed; /*-------------------------------------------------------------------------------------- setup() Called by the Arduino framework once, before the main loop begins --------------------------------------------------------------------------------------*/ void setup() { // button adc input pinMode( BUTTON_ADC_PIN, INPUT ); //ensure A0 is an input digitalWrite( BUTTON_ADC_PIN, LOW ); //ensure pullup is off on A0 // set up the LCD number of columns and rows: lcd.begin( 16, 2 ); #ifdef SPI // SPI Setup digitalWrite(SS, HIGH); // ensure SS stays high for now SPI.begin (); SPI.setClockDivider(SPI_CLOCK_DIV8); #endif Wire.begin(); // Serial setup Serial.begin(9600); nss.begin(9600); Serial.print("Ready to begin\n"); // Hall effect hand sensors setup pinMode( HAND_MINUTE, INPUT_PULLUP ); // is an input, pulled up - active low pinMode( HAND_HOUR, INPUT_PULLUP ); // is an input, pulled up - active low prevMinute = minute(); hand_processed = false; } byte displayed_no_serial = false; void loop() { byte button; //get the latest button pressed, also the buttonJustPressed, buttonJustReleased flags button = ReadButtons(); processButtons(button); #define TESTER #ifndef TESTER // Echo data received from USB port back to GPS if(Serial.available() ) { while(Serial.available()) { char c = Serial.read() ; nss.print(c); } } #endif while (testState != STATE_ECHO && nss.available()) { unsigned long currentMillis; displayed_no_serial = false; char c = nss.read(); if (testState == STATE_GPS) { Serial.print(c); } if (gps.encode(c)) { // process new gps info here gps.crack_datetime(&GPSyear, &GPSmonth, &GPSday, &GPShour, &GPSminute, &GPSsecond, &GPShundredth, &fix_age); currentMillis = millis(); /* Items inside this loop will only run every CHECK_RATE milliseconds */ if(currentMillis - previousMillis > CHECK_RATE) { if (fix_age == TinyGPS::GPS_INVALID_AGE) { // Display Invalid Serial.print("\nGPS_INVALID_AGE"); displayString("INVALID AGE", 11, 0, 0, ' '); Serial.print("\n"); } else if (fix_age > 1000) { // Display Age Serial.print("\nGPS Old Fix - "); Serial.print(fix_age); displayString("AGE", 3, 0, 0, ' '); Serial.print("\n"); } else { // Valid date/ time // Display date / time byte col = 5; byte row = 0; displayInt((int)GPSday, 2, col, row, '-'); col +=3; displayInt((int)GPSmonth, 2, col, row, '-'); col +=3; displayInt((int)GPSyear, 4, col, row, ' '); row = 1; col = 5; displayInt((int)GPShour, 2, col, row, ':'); col +=3; displayInt((int)GPSminute, 2, col, row, ':'); col +=3; displayInt((int)GPSsecond, 2, col, row, ' '); // Send data #define UNIX_TIME #ifdef UNIX_TIME time_t GPSTime; // value of time received from the GPS tmElements_t tm; tm.Year = GPSyear - 1970; tm.Month = GPSmonth; tm.Day = GPSday; tm.Hour = GPShour; tm.Minute = GPSminute; tm.Second = GPSsecond; GPSTime = makeTime(tm); Serial.print("G"); Serial.print(GPSTime); Serial.print("\n"); #ifdef SPI // SPI Transfer char c; // enable Slave Select digitalWrite(SS, LOW); // SS is pin 10 int div = 1; int rem = GPSTime; for (byte x = len; x > 1; x--) div *= 10; while (div > 0) { int digit = rem / div; SPI.transfer (digit); rem = rem - digit * div; div = div / 10; } SPI.transfer ("\n"); // disable Slave Select digitalWrite(SS, HIGH); #endif if (GPSminute != prevGPSminute) { // Only send every minute prevGPSminute = GPSminute; Wire.beginTransmission(4); // transmit to device #4 Wire.write('G'); // flag this is a GPS string Serial.print("\nG"); time_t div = 1000000000; time_t rem = GPSTime; // for (byte x = TIME_MSG_LEN; x > 1; x--) // div *= 10; while (div > 0) { int digit = rem / div; char c = digit + '0'; // Convert to character Wire.write(c); Serial.print(digit); rem = rem - digit * div; div = div / 10; } Wire.write('\n'); Serial.print("\n"); Wire.endTransmission(); // stop transmitting } //#define TEST1 #ifdef TEST1 tmElements_t tm1; breakTime(GPSTime, tm1); Serial.print("\nG"); Serial.print(tm1.Year + 1970); Serial.print("-"); Serial.print(tm1.Month); Serial.print("-"); Serial.print(tm1.Day); #endif #else Serial.print("\nG"); Serial.print(GPSyear); Serial.print("-"); Serial.print(GPSmonth); Serial.print("-"); Serial.print(GPSday); Serial.print(" "); Serial.print(GPShour); Serial.print(":"); Serial.print(GPSminute); Serial.print(":"); Serial.print(GPSsecond); #endif } previousMillis = millis(); } } } // Hand position detection // if (prevMinute != minute()) // { // prevMinute = minute(); // Is the hand in position? if (digitalRead(HAND_MINUTE) == LOW) { // Minute hand in position if (!hand_processed) { if (digitalRead(HAND_HOUR) == LOW) { // Hour hand in position too Wire.beginTransmission(4); // transmit to device #4 Wire.write('H'); Wire.write('\n'); Serial.print("\nH\n"); Wire.endTransmission(); // stop transmitting } else { // Minute hand only Wire.beginTransmission(4); // transmit to device #4 Wire.write('M'); Wire.write('\n'); Serial.print("\nM\n"); Wire.endTransmission(); // stop transmitting } } hand_processed = true; } else { hand_processed = false; } // } if(Serial.available() ) { // Processes messages received on the USB port processSyncMessage(); } } /*-------------------------------------------------------------------------------------- displayInt() Prints out the desired digits, trailed by the specified seperator theInt - the integer value to be displayed len - number of digits to display col - column to start displaying the integer row - row in which to display sep - separator character --------------------------------------------------------------------------------------*/ void displayInt(int theInt, byte len, byte col, byte row, char sep) { int div = 1; int rem = theInt; for (byte x = len; x > 1; x--) div *= 10; while (div > 0) { int digit = rem / div; lcd.setCursor(col++, row); lcd.print(digit); rem = rem - digit * div; div = div / 10; } lcd.setCursor(col, row); lcd.print(sep); } /*-------------------------------------------------------------------------------------- displayString() Prints out the desired string theString - the string to be displayed len - number of characters to display col - column to start displaying the integer row - row in which to display sep - separator character --------------------------------------------------------------------------------------*/ void displayString(char *theString, byte len, byte col, byte row, char sep) { for (byte x = 0; x < len; x++) { lcd.setCursor(col++, row); lcd.print(theString[x]); } if (sep != '\0') { lcd.setCursor(col, row); lcd.print(sep); } } /*-------------------------------------------------------------------------------------- ReadButtons() Detect the button pressed and return the value Uses global values buttonWas, buttonJustPressed, buttonJustReleased. --------------------------------------------------------------------------------------*/ long lastDebounceTime = 0; // the last time the output pin was toggled byte lastButtonState = BUTTON_NONE; // the previous reading from the input pin #define BUTTON_DEBOUNCE_TIME 50 byte ReadButtons() { // Based on LCD_Test code by Mark Bramwell, July 2010 // and http://arduino.cc/en/Tutorial/Debounce byte button; int adc_key_in = 0; adc_key_in = analogRead(BUTTON_ADC_PIN); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) button = BUTTON_NONE; // We make this the 1st option for speed reasons since it will be the most likely result else if (adc_key_in < 50) button = BUTTON_RIGHT; else if (adc_key_in < 195) button = BUTTON_UP; else if (adc_key_in < 380) button = BUTTON_DOWN; else if (adc_key_in < 555) button = BUTTON_LEFT; else if (adc_key_in < 790) button = BUTTON_SELECT; else button = BUTTON_NONE; // when all others fail, return this... // If the switch changed, due to noise or pressing: if (button != lastButtonState) { // reset the debouncing timer lastDebounceTime = millis(); } if ((millis() - lastDebounceTime) > BUTTON_DEBOUNCE_TIME) { // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current state: if (buttonWas != button) { //handle button flags for just pressed and just released events if( ( buttonWas == BUTTON_NONE ) && ( button != BUTTON_NONE ) ) { //the button was just pressed, set buttonJustPressed, this can optionally be used to trigger a once-off action for a button press event //it's the duty of the receiver to clear these flags if it wants to detect a new button change event buttonJustPressed = true; buttonJustReleased = false; } if( ( buttonWas != BUTTON_NONE ) && ( button == BUTTON_NONE ) ) { buttonJustPressed = false; buttonJustReleased = true; } //save the latest button value, for change event detection next time round buttonWas = button; } } lastButtonState = button; return(buttonWas); } /*-------------------------------------------------------------------------------------- processButtons() At the blink rate, look at the button just pressed, and act accordingly. --------------------------------------------------------------------------------------*/ long lastButtonTime = 0; // the last time the button state was processed (for repeating) #define BUTTON_REPEAT_TIME 500 void processButtons(byte button) { if ( buttonJustPressed || (button != BUTTON_NONE && (millis() - lastButtonTime) > BUTTON_REPEAT_TIME) ) { lastButtonTime = millis(); switch( button ) { case BUTTON_NONE: { break; } case BUTTON_RIGHT: { break; } case BUTTON_UP: { break; } case BUTTON_DOWN: { break; } case BUTTON_LEFT: { break; } case BUTTON_SELECT: { // toggle test state if (testState == STATE_NORMAL) { testState = STATE_ECHO; displayString("Echo", 4, 0, 0, ' '); } else if (testState == STATE_ECHO) { testState = STATE_GPS; displayString("GPS ", 4, 0, 0, ' '); } else { testState = STATE_NORMAL; displayString(" ", 4, 0, 0, ' '); } break; } default: { break; } } buttonJustPressed = false; } } /*-------------------------------------------------------------------------------------- processSyncMessage() Processes messages received on the USB port --------------------------------------------------------------------------------------*/ void processSyncMessage() { while(Serial.available()) { char c = Serial.read() ; if (testState == STATE_ECHO) { Serial.print(c); Wire.beginTransmission(4); // transmit to device #4 Wire.write(c); Wire.endTransmission(); // stop transmitting } else if (testState == STATE_GPS) nss.print(c); // else do nothing } }