/* PCRD Japan */

/*
 * SD card attached to SPI bus as follows:
 ** SDcard01B/CMD      MOSI - pin PB3
 ** SDcard01B/DATA0    MISO - pin PB4
 ** SDcard01B/CLK      CLK  - pin PB5
 ** SDcard01B/CD/DATA3 CS   - pin PD4
 
 ** ADCmonoSPI/CONV - pin PD6
 ** ADCmonoSPI/SDO  - pin PD5
 ** ADCmonoSPI/SCK -  pin PD7  shared with LED4
*/

#include <SD.h>

const String filename = "log.csv ";  // filename for logfile

const int ADreset=3; // PD3
const int eint=2;     // PD2
const int LED1=8;     // PB0
const int LED2=9;     // PB1
const int LED3=10;    // PB2
const int LED4=7;     // PD7
const int chipSelect = 4;     // CS is PD4
const int CONV = 6;     // CONV is PD6
const int SDO = 5;     // SDO is PD5
const int ADSCK = 7;     // SCK is PD7

const int CHANNELS=512;  // Number of channels

//unsigned int channelT[CHANNELS];    // recordig buffer
unsigned char channelA[CHANNELS];    // recordig buffer
boolean rise=false;           // flag fo recording time
char inChar;                  // input character from GPS
String dataString = "";       // concantenated string with NMEA messages and measured values
int coll = 0;                 // collons counter in NMEA messages
unsigned int num = 0;         // measurements counter
unsigned int count;           // measurements per 10 s

// 1x 100 us per 10 s UTC synchronised without FIX; 40 configuration bytes
const char cmd[40]={0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x80, 0x96, 0x98, 0x00, 0xE0, 0xC8, 0x10, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0xC6, 0x51};

// airborne <1g; 40 configuration bytes
const char cmd2[44]={0xB5, 0x62 ,0x06 ,0x24 ,0x24 ,0x00 ,0xFF ,0xFF ,0x06 ,0x03 ,0x00 ,0x00 ,0x00 ,0x00 ,0x10 ,0x27 ,0x00 ,0x00 ,0x05 ,0x00 ,0xFA ,0x00 ,0xFA ,0x00 ,0x64 ,0x00 ,0x2C ,0x01 ,0x00 ,0x3C ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x52 ,0xE8};

// configure GPS
void setupGPS()
{ 
   for (int n=0;n<40;n++) Serial.write(cmd[n]); 
   for (int n=0;n<44;n++) Serial.write(cmd2[n]); 
}

void errorLED()
{
  while(true) 
  {
    digitalWrite(LED4, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(100);               // wait for a second
    digitalWrite(LED4, LOW);    // turn the LED off by making the voltage LOW
    delay(100);               // wait for a second   
  }
}

// function for reading $GPRMC NMEA message
void ReadGPRMC()
{
  // $GPRMC,091451.00,A,4915.64143,N,01441.50397,E,0.053,,090215,,,A*74
  coll = 0;
  while(1)    // wait for $GPRMC
  {
    // get incoming byte:
    while (!Serial.available());
    if (Serial.read() != '$') continue;
    while (!Serial.available());
    if (Serial.read() != 'G') continue;
    while (!Serial.available());
    if (Serial.read() != 'P') continue;
    while (!Serial.available());
    if (Serial.read() != 'R') continue;
    while (!Serial.available());
    if (Serial.read() != 'M') continue;
    while (!Serial.available());
    if (Serial.read() != 'C') continue;
    while (!Serial.available());
    if (Serial.read() != ',') continue;
    break;
  }  
  do
  {
    while (!Serial.available());
    inChar = (char)Serial.read();
    if (inChar == ',') coll++;
    dataString += inChar;
  }
  while (coll < 9);  // read only 9 coma separated values
}

// function for reading $GPGGA NMEA message
void ReadGPGGA()
{
  // $GPGGA,091451.00,4915.64143,N,01441.50397,E,1,09,0.90,443.2,M,44.0,M,,*50
  coll = 0;
  while(1)  // wait for $GPGGA
  {
    while (!Serial.available());
    if (Serial.read() != '$') continue;
    while (!Serial.available());
    if (Serial.read() != 'G') continue;
    while (!Serial.available());
    if (Serial.read() != 'P') continue;
    while (!Serial.available());
    if (Serial.read() != 'G') continue;
    while (!Serial.available());
    if (Serial.read() != 'G') continue;
    while (!Serial.available());
    if (Serial.read() != 'A') continue;
    while (!Serial.available());
    if (Serial.read() != ',') continue;
    break;
  }  
  do
  {
    while (!Serial.available());
    inChar = (char)Serial.read();
    if (inChar == ',') coll++;
    if (coll > 4) dataString += inChar;  // skip first 5 coma separated values
  }
  while (coll < 12); // read only 7 coma separated values
}

void isr()        // interrupt service routine driven from 1PPS from GPS
{
  {
    rise=true;
  }

}

void record()
{
  for (int c=67; c<CHANNELS; c++)
  {
    if (channelA[c]>0)
    {
      digitalWrite(LED4, HIGH);   // LED 16-64
      break;      
    }
  }

  for (int c=33; c<64; c++)
  {
    if (channelA[c]>0)
    {
      digitalWrite(LED3, HIGH);   // LED 9-16
      break;      
    }
  }

  for (int c=17; c<32; c++)
  {
    if (channelA[c]>0)
    {
      digitalWrite(LED2, HIGH);   // LED 5-8
      break;      
    }
  }

  for (int c=0; c<16; c++)
  {
    if (channelA[c]>0)
    {
      digitalWrite(LED1, HIGH);   // LED 0-4
      break;      
    }
  }
     
  dataString = "";        // make a string for assembling the data to log
  //*
  ReadGPRMC();            // read NMEA sentences from GPS
  ReadGPGGA();
  //*/
  // make a string for assembling the data to log:
  dataString += String(num++);
  //dataString += ","; 
  //!!! Serial.print(dataString);
  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  digitalWrite(chipSelect, HIGH);   
  char fileNameCharArray[filename.length()];
  filename.toCharArray(fileNameCharArray, filename.length());
  
  File dataFile;

  dataFile = SD.open(fileNameCharArray, FILE_WRITE);
  if (dataFile) 
  {
    dataFile.print(dataString);
    dataFile.close();
  }  
  else 
  {
    errorLED();
  } 

  for (int i=0; i<(CHANNELS/32); i++)
  {
    dataString = "";
    for (int n=0; n<32; n++)
    {
      dataString += ","; 
      dataString += String(channelA[(i*32)+n]);
    };
    
    dataFile = SD.open(fileNameCharArray, FILE_WRITE);
    if (dataFile) 
    {
      dataFile.print(dataString);
      dataFile.close();
    }  
    else 
    {
      errorLED();
    } 
  }

  dataString = ","; 
  dataString += String(count);
  dataFile = SD.open(fileNameCharArray, FILE_WRITE);
  if (dataFile) 
  {
    dataFile.println(dataString);
    dataFile.close();
  }  
  else 
  {
    errorLED();
  } 

  
  digitalWrite(chipSelect, LOW);   

  /*!!! control print
  //TODO print to I2C display
  Serial.print(":");       
  Serial.print(count);       
  Serial.print("*");       
  for(int j=0;j<36;j++) {Serial.print(channelA[j]); Serial.print(' ');}
  Serial.println();
  //*/

  for (int n=0; n<CHANNELS; n++) // clear recording buffer
  {
    channelA[n]=0;
  }  

  digitalWrite(LED1, LOW);   // LED OFF
  digitalWrite(LED2, LOW);   // LED OFF
  digitalWrite(LED3, LOW);   // LED OFF
  digitalWrite(LED4, LOW);   // LED OFF

}

void setup() 
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {;}
  //Serial.println("#cvak");

  pinMode(ADreset, OUTPUT);
  pinMode(eint, INPUT);
  pinMode(SDO, INPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(CONV, OUTPUT);
  //pinMode(SCK, OUTPUT);

  setupGPS();

  //Serial.print("#Initializing SD card...");  // inserting a SD Card always reset the processor and call setup
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) 
  {
    //Serial.println("Card failed, or not present");
    // don't do anything more:
    errorLED();
    return;
  }
  //Serial.println("card initialized.");

  noInterrupts();          // disable all interrupts
  attachInterrupt(0, isr, RISING);  // initialise interrupt from rising edge of 1PPS

  for (int n=0; n<CHANNELS; n++) // clear recoding buffer
  {
    channelA[n]=0; 
  }

  interrupts();             // enable all interrupts
  
  //Serial.println("#Hmmm");
}

void loop() 
{
  unsigned int val;
  unsigned int treshold = 1;
  
  count = 0;
  while (true)
  {
    count++;
    digitalWrite(ADSCK, HIGH);  
    digitalWrite(CONV, HIGH);  // start AD conversion
    digitalWrite(ADreset, HIGH);  // reset Peack Detector
    digitalWrite(CONV, LOW);   // start SPI
    digitalWrite(ADreset, LOW);  // start Peack Detector
    val=0;
    for (int p=0;p<9;p++)
    {
      digitalWrite(ADSCK, LOW);  // 1 CLK
      digitalWrite(ADSCK, HIGH);  
      val= (val<<1)|digitalRead(SDO);
    }
    digitalWrite(ADSCK, LOW);  // 1 CLK
    
    if (channelA[val] < 255) channelA[val]++;

    if (rise)  // recording time is now
    {
        record();  // make record
        digitalWrite(ADreset, HIGH);  // reset Peack Detector
        rise = false;
        count = 0;
        digitalWrite(ADreset, LOW);  // start Peack Detector
        continue;  // skip this interrupted impuls
    }
  }
}