// Arduino Based Seismograph
// Using MPU6050 to detect seismic activity and log to SD card with RTC for timestamps
// Adjustable (via potentiometer) sensitivity to trigger alarm (LED and buzzer)
// Timeset via ~ command (enter ~HHMMSS, time is set and new file created)
// Select one of the sample rates below to implement filter and record rate to suit

#define SAMPLES_PER_SECOND 42
//#define SAMPLES_PER_SECOND 100
//#define SAMPLES_PER_SECOND 200

// Error flash codes:
// 1: RTC not found
// 2: card not found
// 3: can't open file (could be card full or too many directory entries)
// 4: can't write header (could be card full)
// 5: can't write data (could be card full)

//comment out to turn off debugging output
#define SERIAL_DEBUG

// RTCC stuff
#include <EEPROM.h>
#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

// SD card stuff
#include <SPI.h>
#include "SdFat.h"

static const uint8_t SD_CS_PIN = SS;
SdFat sd;
SdFile file;
unsigned long synctime;
byte lasthour;            //to detect day rollover at midnight
bool timeset=false;       //flag set while time is being set
char buf[128];

// Accelerometer stuff
#include <Filters.h>
FilterOnePole XFHigh(HIGHPASS, 0.5), YFHigh(HIGHPASS, 0.5), ZFHigh(HIGHPASS, 0.5);

//register addresses for MPU6050
const int MPU_addr=0x69;  // I2C address of the MPU-6050
#define MPU6050_AUX_VDDIO          0x01   // R/W
#define MPU6050_SMPLRT_DIV         0x19   // R/W
#define MPU6050_CONFIG             0x1A   // R/W
#define MPU6050_GYRO_CONFIG        0x1B   // R/W
#define MPU6050_ACCEL_CONFIG       0x1C   // R/W
#define MPU6050_FF_THR             0x1D   // R/W
#define MPU6050_FF_DUR             0x1E   // R/W
#define MPU6050_MOT_THR            0x1F   // R/W
#define MPU6050_MOT_DUR            0x20   // R/W
#define MPU6050_ZRMOT_THR          0x21   // R/W
#define MPU6050_ZRMOT_DUR          0x22   // R/W
#define MPU6050_FIFO_EN            0x23   // R/W
#define MPU6050_I2C_MST_CTRL       0x24   // R/W
#define MPU6050_I2C_SLV0_ADDR      0x25   // R/W
#define MPU6050_I2C_SLV0_REG       0x26   // R/W
#define MPU6050_I2C_SLV0_CTRL      0x27   // R/W
#define MPU6050_I2C_SLV1_ADDR      0x28   // R/W
#define MPU6050_I2C_SLV1_REG       0x29   // R/W
#define MPU6050_I2C_SLV1_CTRL      0x2A   // R/W
#define MPU6050_I2C_SLV2_ADDR      0x2B   // R/W
#define MPU6050_I2C_SLV2_REG       0x2C   // R/W
#define MPU6050_I2C_SLV2_CTRL      0x2D   // R/W
#define MPU6050_I2C_SLV3_ADDR      0x2E   // R/W
#define MPU6050_I2C_SLV3_REG       0x2F   // R/W
#define MPU6050_I2C_SLV3_CTRL      0x30   // R/W
#define MPU6050_I2C_SLV4_ADDR      0x31   // R/W
#define MPU6050_I2C_SLV4_REG       0x32   // R/W
#define MPU6050_I2C_SLV4_DO        0x33   // R/W
#define MPU6050_I2C_SLV4_CTRL      0x34   // R/W
#define MPU6050_I2C_SLV4_DI        0x35   // R  
#define MPU6050_I2C_MST_STATUS     0x36   // R
#define MPU6050_INT_PIN_CFG        0x37   // R/W
#define MPU6050_INT_ENABLE         0x38   // R/W
#define MPU6050_INT_STATUS         0x3A   // R  
#define MPU6050_ACCEL_XOUT_H       0x3B   // R  
#define MPU6050_ACCEL_XOUT_L       0x3C   // R  
#define MPU6050_ACCEL_YOUT_H       0x3D   // R  
#define MPU6050_ACCEL_YOUT_L       0x3E   // R  
#define MPU6050_ACCEL_ZOUT_H       0x3F   // R  
#define MPU6050_ACCEL_ZOUT_L       0x40   // R  
#define MPU6050_TEMP_OUT_H         0x41   // R  
#define MPU6050_TEMP_OUT_L         0x42   // R  
#define MPU6050_GYRO_XOUT_H        0x43   // R  
#define MPU6050_GYRO_XOUT_L        0x44   // R  
#define MPU6050_GYRO_YOUT_H        0x45   // R  
#define MPU6050_GYRO_YOUT_L        0x46   // R  
#define MPU6050_GYRO_ZOUT_H        0x47   // R  
#define MPU6050_GYRO_ZOUT_L        0x48   // R  
#define MPU6050_EXT_SENS_DATA_00   0x49   // R  
#define MPU6050_EXT_SENS_DATA_01   0x4A   // R  
#define MPU6050_EXT_SENS_DATA_02   0x4B   // R  
#define MPU6050_EXT_SENS_DATA_03   0x4C   // R  
#define MPU6050_EXT_SENS_DATA_04   0x4D   // R  
#define MPU6050_EXT_SENS_DATA_05   0x4E   // R  
#define MPU6050_EXT_SENS_DATA_06   0x4F   // R  
#define MPU6050_EXT_SENS_DATA_07   0x50   // R  
#define MPU6050_EXT_SENS_DATA_08   0x51   // R  
#define MPU6050_EXT_SENS_DATA_09   0x52   // R  
#define MPU6050_EXT_SENS_DATA_10   0x53   // R  
#define MPU6050_EXT_SENS_DATA_11   0x54   // R  
#define MPU6050_EXT_SENS_DATA_     0x55   // R  
#define MPU6050_EXT_SENS_DATA_13   0x56   // R  
#define MPU6050_EXT_SENS_DATA_14   0x57   // R  
#define MPU6050_EXT_SENS_DATA_15   0x58   // R  
#define MPU6050_EXT_SENS_DATA_16   0x59   // R  
#define MPU6050_EXT_SENS_DATA_17   0x5A   // R  
#define MPU6050_EXT_SENS_DATA_18   0x5B   // R  
#define MPU6050_EXT_SENS_DATA_19   0x5C   // R  
#define MPU6050_EXT_SENS_DATA_20   0x5D   // R  
#define MPU6050_EXT_SENS_DATA_21   0x5E   // R  
#define MPU6050_EXT_SENS_DATA_22   0x5F   // R  
#define MPU6050_EXT_SENS_DATA_23   0x60   // R  
#define MPU6050_MOT_DETECT_STATUS  0x61   // R  
#define MPU6050_I2C_SLV0_DO        0x63   // R/W
#define MPU6050_I2C_SLV1_DO        0x64   // R/W
#define MPU6050_I2C_SLV2_DO        0x65   // R/W
#define MPU6050_I2C_SLV3_DO        0x66   // R/W
#define MPU6050_I2C_MST_DELAY_CTRL 0x67   // R/W
#define MPU6050_SIGNAL_PATH_RESET  0x68   // R/W
#define MPU6050_MOT_DETECT_CTRL    0x69   // R/W
#define MPU6050_USER_CTRL          0x6A   // R/W
#define MPU6050_PWR_MGMT_1         0x6B   // R/W
#define MPU6050_PWR_MGMT_2         0x6C   // R/W
#define MPU6050_FIFO_COUNTH        0x72   // R/W
#define MPU6050_FIFO_COUNTL        0x73   // R/W
#define MPU6050_FIFO_R_W           0x74   // R/W
#define MPU6050_WHO_AM_I           0x75   // R

#define MPU6050_D0 0
#define MPU6050_D1 1
#define MPU6050_D2 2
#define MPU6050_D3 3
#define MPU6050_D4 4
#define MPU6050_D5 5
#define MPU6050_D6 6
#define MPU6050_D7 7

#define MPU6050_DLPF_CFG0     MPU6050_D0
#define MPU6050_DLPF_CFG1     MPU6050_D1
#define MPU6050_DLPF_CFG2     MPU6050_D2
#define MPU6050_EXT_SYNC_SET0 MPU6050_D3
#define MPU6050_EXT_SYNC_SET1 MPU6050_D4
#define MPU6050_EXT_SYNC_SET2 MPU6050_D5

#define MPU6050_EXT_SYNC_SET_0 (0)
#define MPU6050_EXT_SYNC_SET_1 (bit(MPU6050_EXT_SYNC_SET0))
#define MPU6050_EXT_SYNC_SET_2 (bit(MPU6050_EXT_SYNC_SET1))
#define MPU6050_EXT_SYNC_SET_3 (bit(MPU6050_EXT_SYNC_SET1)|bit(MPU6050_EXT_SYNC_SET0))
#define MPU6050_EXT_SYNC_SET_4 (bit(MPU6050_EXT_SYNC_SET2))
#define MPU6050_EXT_SYNC_SET_5 (bit(MPU6050_EXT_SYNC_SET2)|bit(MPU6050_EXT_SYNC_SET0))
#define MPU6050_EXT_SYNC_SET_6 (bit(MPU6050_EXT_SYNC_SET2)|bit(MPU6050_EXT_SYNC_SET1))
#define MPU6050_EXT_SYNC_SET_7 (bit(MPU6050_EXT_SYNC_SET2)|bit(MPU6050_EXT_SYNC_SET1)|bit(MPU6050_EXT_SYNC_SET0))
 
#define MPU6050_EXT_SYNC_DISABLED     MPU6050_EXT_SYNC_SET_0
#define MPU6050_EXT_SYNC_TEMP_OUT_L   MPU6050_EXT_SYNC_SET_1
#define MPU6050_EXT_SYNC_GYRO_XOUT_L  MPU6050_EXT_SYNC_SET_2
#define MPU6050_EXT_SYNC_GYRO_YOUT_L  MPU6050_EXT_SYNC_SET_3
#define MPU6050_EXT_SYNC_GYRO_ZOUT_L  MPU6050_EXT_SYNC_SET_4
#define MPU6050_EXT_SYNC_ACCEL_XOUT_L MPU6050_EXT_SYNC_SET_5
#define MPU6050_EXT_SYNC_ACCEL_YOUT_L MPU6050_EXT_SYNC_SET_6
#define MPU6050_EXT_SYNC_ACCEL_ZOUT_L MPU6050_EXT_SYNC_SET_7

#define MPU6050_DLPF_CFG_0 (0)
#define MPU6050_DLPF_CFG_1 (bit(MPU6050_DLPF_CFG0))
#define MPU6050_DLPF_CFG_2 (bit(MPU6050_DLPF_CFG1))
#define MPU6050_DLPF_CFG_3 (bit(MPU6050_DLPF_CFG1)|bit(MPU6050_DLPF_CFG0))
#define MPU6050_DLPF_CFG_4 (bit(MPU6050_DLPF_CFG2))
#define MPU6050_DLPF_CFG_5 (bit(MPU6050_DLPF_CFG2)|bit(MPU6050_DLPF_CFG0))
#define MPU6050_DLPF_CFG_6 (bit(MPU6050_DLPF_CFG2)|bit(MPU6050_DLPF_CFG1))
#define MPU6050_DLPF_CFG_7 (bit(MPU6050_DLPF_CFG2)|bit(MPU6050_DLPF_CFG1)|bit(MPU6050_DLPF_CFG0))
 
#define MPU6050_DLPF_260HZ    MPU6050_DLPF_CFG_0
#define MPU6050_DLPF_184HZ    MPU6050_DLPF_CFG_1
#define MPU6050_DLPF_94HZ     MPU6050_DLPF_CFG_2
#define MPU6050_DLPF_44HZ     MPU6050_DLPF_CFG_3
#define MPU6050_DLPF_21HZ     MPU6050_DLPF_CFG_4
#define MPU6050_DLPF_10HZ     MPU6050_DLPF_CFG_5
#define MPU6050_DLPF_5HZ      MPU6050_DLPF_CFG_6
#define MPU6050_DLPF_RESERVED MPU6050_DLPF_CFG_7

#define MPU6050_ACCEL_HPF0 MPU6050_D0
#define MPU6050_ACCEL_HPF1 MPU6050_D1
#define MPU6050_ACCEL_HPF2 MPU6050_D2
#define MPU6050_AFS_SEL0   MPU6050_D3
#define MPU6050_AFS_SEL1   MPU6050_D4
#define MPU6050_ZA_ST      MPU6050_D5
#define MPU6050_YA_ST      MPU6050_D6
#define MPU6050_XA_ST      MPU6050_D7
 
#define MPU6050_ACCEL_HPF_0 (0)
#define MPU6050_ACCEL_HPF_1 (bit(MPU6050_ACCEL_HPF0))
#define MPU6050_ACCEL_HPF_2 (bit(MPU6050_ACCEL_HPF1))
#define MPU6050_ACCEL_HPF_3 (bit(MPU6050_ACCEL_HPF1)|bit(MPU6050_ACCEL_HPF0))
#define MPU6050_ACCEL_HPF_4 (bit(MPU6050_ACCEL_HPF2))
#define MPU6050_ACCEL_HPF_7 (bit(MPU6050_ACCEL_HPF2)|bit(MPU6050_ACCEL_HPF1)|bit(MPU6050_ACCEL_HPF0))
 
#define MPU6050_ACCEL_HPF_RESET  MPU6050_ACCEL_HPF_0
#define MPU6050_ACCEL_HPF_5HZ    MPU6050_ACCEL_HPF_1
#define MPU6050_ACCEL_HPF_2_5HZ  MPU6050_ACCEL_HPF_2
#define MPU6050_ACCEL_HPF_1_25HZ MPU6050_ACCEL_HPF_3
#define MPU6050_ACCEL_HPF_0_63HZ MPU6050_ACCEL_HPF_4
#define MPU6050_ACCEL_HPF_HOLD   MPU6050_ACCEL_HPF_7
 
#define MPU6050_AFS_SEL_0 (0)
#define MPU6050_AFS_SEL_1 (bit(MPU6050_AFS_SEL0))
#define MPU6050_AFS_SEL_2 (bit(MPU6050_AFS_SEL1))
#define MPU6050_AFS_SEL_3 (bit(MPU6050_AFS_SEL1)|bit(MPU6050_AFS_SEL0))
 
#define MPU6050_AFS_SEL_2G  MPU6050_AFS_SEL_0
#define MPU6050_AFS_SEL_4G  MPU6050_AFS_SEL_1
#define MPU6050_AFS_SEL_8G  MPU6050_AFS_SEL_2
#define MPU6050_AFS_SEL_16G MPU6050_AFS_SEL_3

//Adjustable defines for pins
#define ALARM_PIN 7
#define CARD_STATUS_PIN 5
#define SHUTDOWN_PIN 4

//sample rate dependent defines
#define WAV_HEADER_42_HZ "RIFF\x4\x0\x0\x0WAVEfmt \x10\x0\x0\x0\x1\x0\x4\x0\x2A\x0\x0\x0\x50\x1\x0\x0\x8\x0\x10\x0" "data\x0\x0\x0\x0"
#define WAV_HEADER_100_HZ "RIFF\x4\x0\x0\x0WAVEfmt \x10\x0\x0\x0\x1\x0\x4\x0\x64\x0\x0\x0\x20\x3\x0\x0\x8\x0\x10\x0" "data\x0\x0\x0\x0"
#define WAV_HEADER_200_HZ "RIFF\x4\x0\x0\x0WAVEfmt \x10\x0\x0\x0\x1\x0\x4\x0\xC8\x0\x0\x0\x40\x6\x0\x0\x8\x0\x10\x0" "data\x0\x0\x0\x0"

#if SAMPLES_PER_SECOND == 42
  #define WAV_HEADER WAV_HEADER_42_HZ
  #define LPF_IN_USE MPU6050_DLPF_21HZ
#endif
  
#if SAMPLES_PER_SECOND == 100
  #define WAV_HEADER WAV_HEADER_100_HZ
  #define LPF_IN_USE MPU6050_DLPF_44HZ
#endif

#if SAMPLES_PER_SECOND == 200
  #define WAV_HEADER WAV_HEADER_200_HZ
  #define LPF_IN_USE MPU6050_DLPF_94HZ
#endif

void dosync(){            //need to update filesize params before sync, then sync occurs
  unsigned long riffsize, datasize; //8 less than and 44 less than filesize respectively    
  byte d[4];              //32 bit sizes
  digitalWrite(CARD_STATUS_PIN,HIGH);   //flash LED during sync
  riffsize=file.fileSize()-8;
  datasize=file.fileSize()-44;
  d[0]=(byte)riffsize;
  d[1]=(byte)(riffsize>>8);
  d[2]=(byte)(riffsize>>16);
  d[3]=(byte)(riffsize>>24);
  file.seekSet(4);
  if( !file.write(d,4) ){
    Serial.println(F("Unable to write to SD card!"));
    file.close();
    errorflash(5);
  }
  d[0]=(byte)datasize;
  d[1]=(byte)(datasize>>8);
  d[2]=(byte)(datasize>>16);
  d[3]=(byte)(datasize>>24);
  file.seekSet(40);
  if( !file.write(d,4) ){
    Serial.println(F("Unable to write to SD card!"));
    file.close();
    errorflash(5);
  }
  file.seekEnd();             //back to end of file
  if(!file.sync()){Serial.println(F("Sync failed"));errorflash(5);}   //commit changes
  synctime=millis();          //set timer for next automatic sync
  if(!timeset){
    Serial.println(F("Card synced"));
  }
  digitalWrite(CARD_STATUS_PIN,LOW);
}

void openfile(){      //opens/creates a new file as needed (in a separate function as needed in loop and setup)
  char buf[80];                      //for filename
  file.close();                      //any previously opened file
  DateTime now(rtc.now().unixtime());
  sprintf(buf, "ArduinoSeismograph_%04d-%02d-%02d_%02d%02d%02d.wav", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
#ifdef SERIAL_DEBUG
  Serial.print(F("Opening file "));
  Serial.print(buf);
  Serial.print(F("\n"));
  Serial.flush();
#endif
  if( !file.open(buf, O_RDWR | O_CREAT | O_AT_END) ){   //try opening file
    Serial.println(F("Error opening file on SD card!"));
    errorflash(3);
  }
  if( !file.write(WAV_HEADER,44)){ //wav header, 4 channel, 42Hz, 16 bit
    Serial.println("Error writing header to SD card!");
    file.close();
    errorflash(4);
  }
  synctime=millis();                  //set synctime
}

char* ftoa(char* a, double f, int precision) {    //for converting floats to char arrays
  long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000};

  char *ret = a;
  long heiltal = (long)f;
  itoa(heiltal, a, 10);
  while (*a != '\0') a++;
  *a++ = '.';
  long desimal = abs((long)((f - heiltal) * p[precision]));
  itoa(desimal, a, 10);
  int len = strlen(a);
  if( len < precision ) {
    while( len++ < precision )
      *a++ = '0';
    itoa(desimal, a, 10);
  }
  return ret;
}

void setup() {
  Serial.begin(115200);
  pinMode(ALARM_PIN, OUTPUT);         //earthquake alarm pin
  digitalWrite(ALARM_PIN, LOW);
  pinMode(CARD_STATUS_PIN, OUTPUT);   //SD/RTC alarm pin- can be the same
  digitalWrite(CARD_STATUS_PIN, LOW);
  pinMode(A0, OUTPUT);      //trimpot is connected to these three pins
  pinMode(A1, INPUT);
  pinMode(A2, OUTPUT);
  digitalWrite(A0, LOW);
  digitalWrite(A2, LOW);    //set A2 high to turn on trimpot
  pinMode(SHUTDOWN_PIN,INPUT_PULLUP);   //pull to ground to effect shutdown

#ifdef SERIAL_DEBUG
  Serial.println(F("SILICON CHIP Seismograph v1.0"));
#endif
  while( !rtc.begin() ) {
#ifdef SERIAL_DEBUG
    Serial.println(F("Couldn't find RTC module"));
    errorflash(1);
#endif
  }

  if( rtc.lostPower() ) {
#ifdef SERIAL_DEBUG
    Serial.println(F("RTC lost power, setting time"));
    Serial.flush();
#endif
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  } else {
    uint32_t LastSetDateTimeUnix;
    EEPROM.get(0, LastSetDateTimeUnix);
    DateTime CompileDateTime(F(__DATE__), F(__TIME__));
    if( CompileDateTime.unixtime() != LastSetDateTimeUnix ) {
#ifdef SERIAL_DEBUG
      Serial.println(F("RTC time updated"));
      Serial.flush();
#endif
      LastSetDateTimeUnix = CompileDateTime.unixtime();
      EEPROM.put(0, LastSetDateTimeUnix);
      rtc.adjust(CompileDateTime + TimeSpan(20));
    } else {
      DateTime now = rtc.now();
      TimeSpan TimeSinceCompilation = now - CompileDateTime;
      if( TimeSinceCompilation.totalseconds() < 60+20 ) {
#ifdef SERIAL_DEBUG
        Serial.println(F("RTC time advanced to next minute"));
        Serial.flush();
#endif
        now = now + TimeSpan(60 - now.second());
        rtc.adjust(now);
      }
    }
  }

  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(MPU6050_CONFIG);
  Wire.write(LPF_IN_USE|MPU6050_EXT_SYNC_DISABLED);             //set low pass filter
  Wire.endTransmission(true);
  Wire.beginTransmission(MPU_addr);
  Wire.write(MPU6050_ACCEL_CONFIG);
  Wire.write(MPU6050_ACCEL_HPF_0_63HZ|MPU6050_AFS_SEL_4G);      //set hi pass and scale
  Wire.endTransmission(true);
  Wire.beginTransmission(MPU_addr);
  Wire.write(MPU6050_PWR_MGMT_1);  // PWR_MGMT_1 register
  Wire.write(0);                   // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

#ifdef SERIAL_DEBUG
  Serial.println(F("Initialising SD card"));
  Serial.flush();
#endif
  if( !sd.begin(SD_CS_PIN, SPI_FULL_SPEED) ) {
#ifdef SERIAL_DEBUG
    Serial.println(F("Error initialising SD card"));
    errorflash(2);
#endif
  }
  openfile();
}

int Pwave_alarm_time, Pwave_alarm_going, Swave_alarm_time, Swave_alarm_going, alarm_counter, alarm_delay = 10;
unsigned int sample_num, buflen;
//uint32_t last_time;
unsigned long last_micros;

void loop() {
  int16_t AcX,AcY,AcZ;
  unsigned short trimpot_val;
  float xy_vector_mag, z_vector_mag, scale_factor, xv, yv, zv;
  uint32_t time_now;
  byte d[8];        //sample data, 4 channels, 2 bytes per channel
  int xi,yi,zi,mi;  //16 bit int data for writing to file
  time_now = rtc.now().unixtime();

  digitalWrite(A2, HIGH);
  trimpot_val = analogRead(A1);
  digitalWrite(A2, LOW);

  //read the acceleration values
  Wire.beginTransmission(MPU_addr);
  Wire.write(MPU6050_ACCEL_XOUT_H);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,6,true);
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  //scale to 'g'
  XFHigh.input(AcX / 8192.0);
  YFHigh.input(AcY / 8192.0);
  ZFHigh.input(AcZ / 8192.0 - 1.0);
  scale_factor = pow(2, trimpot_val / 100.0);
  xy_vector_mag = sqrt(XFHigh.output() * XFHigh.output() + YFHigh.output() * YFHigh.output()) * scale_factor;
  z_vector_mag = abs(ZFHigh.output() * scale_factor);
  xv = XFHigh.output(); yv = YFHigh.output(); zv = ZFHigh.output();
  DateTime now(time_now);
  if(now.hour()<lasthour){      //if hour has rolled over, start new file, gives a new file at midnight
    openfile();
  }
  lasthour=now.hour();
  //scale back to ints (values have been filtered)
  xi=xv*8192;
  yi=yv*8192;
  zi=zv*8192;
  mi=sqrt(xv * xv + yv * yv + zv * zv)*8192;  
  d[0]=(byte)xi;
  d[1]=(byte)(xi>>8);
  d[2]=(byte)yi;
  d[3]=(byte)(yi>>8);
  d[4]=(byte)zi;
  d[5]=(byte)(zi>>8);
  d[6]=(byte)mi;
  d[7]=(byte)(mi>>8);  
  if( !file.write(d,8) ){
    Serial.println("Unable to write to SD card!");
    file.close();
    errorflash(5);
  }
  else
  {
    if(!timeset){     //display data
      Serial.print(F("|XY|=")); Serial.print(xy_vector_mag); Serial.print(F(",|Z|=")); Serial.println(z_vector_mag);
    }
  }
  if(millis()-synctime>60000L){     //sync file every minute
    dosync();
  }

  if(digitalRead(SHUTDOWN_PIN)==LOW){
    dosync();         //update filesizes in riff header
    file.close();
    digitalWrite(CARD_STATUS_PIN,HIGH);   //indicate with LED solid on
    Serial.println(F("File Closed, logging stopped"));
    while(1){}        //can't do anything so idle    
  }

  if( alarm_delay ) {
    --alarm_delay;
  } else {
    if( xy_vector_mag >= 1 )        //check for P-wave 
      ++Pwave_alarm_time;
    else if( Pwave_alarm_time > 0 )
      --Pwave_alarm_time;
    if( z_vector_mag >= 1 )         //check for S-wave
      ++Swave_alarm_time;
    else if( Swave_alarm_time > 0 )
      --Swave_alarm_time;
  }

  if( Pwave_alarm_time > 5 ) {
    if( !Pwave_alarm_going )
      alarm_counter = 0;
    Pwave_alarm_going = 80;
  } else if( Swave_alarm_time > 5 ) {
    Swave_alarm_going = 80;
  }
  if( Pwave_alarm_going || Swave_alarm_going ) {    //output alarm status
    digitalWrite(ALARM_PIN, alarm_counter < 10 || Swave_alarm_going ? HIGH : LOW);
    if( ++alarm_counter == 20 )
      alarm_counter = 0;
    if( Pwave_alarm_going )
      Pwave_alarm_going--;
    if( Swave_alarm_going )
      Swave_alarm_going--;
  } else {
    digitalWrite(ALARM_PIN, LOW);
  }

  //check if timesetting is necessary
  dotimeset();

  if( ++sample_num == SAMPLES_PER_SECOND ) {      //manage sample rate
    sample_num = 0;
    time_now = rtc.now().unixtime();
    while( rtc.now().unixtime() == time_now )
      ;
    last_micros = micros();
  } else {
    unsigned long next_micros = last_micros + sample_num * 1000000UL / SAMPLES_PER_SECOND;      //will this glitch when micros() rolls over?
    while( micros() < next_micros )
      ;
  }
}

void errorflash(int n){     //flashes the LED n times, then pauses to indicate an error, needs reset to recover, which these faults would need anyway
  int i;
  while(1){
    for(i=0;i<n;i++){
      digitalWrite(CARD_STATUS_PIN,HIGH);   //flash LED
      delay(150);
      digitalWrite(CARD_STATUS_PIN,LOW);
      delay(150);
    }
    delay(500);
    digitalWrite(ALARM_PIN, HIGH);          //brief chirp on siren to let you know there's a problem
    delay(50);     
    digitalWrite(ALARM_PIN, LOW);   
  }
}

void dotimeset(){         //checks for user input on Serial port and updates time if requested
  int d;
  static char t[13];
  if(Serial.available()){
    d=Serial.read();
    if((d>'9')&&(timeset)){
      Serial.println(F("Setting cancelled"));
      timeset=false;      
    }
    if(d=='~'){       //enter timeset mode, suppresses serial data  
      timeset=true;
      t[0]=0;t[1]=0;t[2]=0;t[3]=0;t[4]=0;t[5]=0;t[6]=0;t[7]=0;t[8]=0;t[9]=0;t[10]=0;t[11]=0;t[12]=0;
      Serial.println(F("Enter YYMMDDHHMMSS"));      
    }
    if((d>='0')&&(d<='9')){
      t[strlen(t)]=d;
      Serial.write(d);
      if(strlen(t)>11){
        DateTime set;
        rtc.adjust(DateTime(t[0]*10+t[1]-528, t[2]*10+t[3]-528, t[4]*10+t[5]-528, t[6]*10+t[7]-528, t[8]*10+t[9]-528, t[10]*10+t[11]-528));
        set = rtc.now();
        sprintf(buf,"\nTime set to %04d-%02d-%02d_%02d%02d%02d", set.year(), set.month(), set.day(), set.hour(), set.minute(), set.second());
        Serial.println(buf);
        timeset=false;
        openfile();
      }
    }    
  }
}

