// Logowanie danych i obsługa ADK, część 1. z 8
#include <SD.h> // musi być dołączony przed AndroidAccessory.h
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>
  static byte badPacket[21] = {0x7E ,0x00 ,0x11 ,0x10 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 , 
  0x00 ,0xFF ,0xFF ,0xFF ,0xFE ,0x00 ,0x00 ,0x42 ,0x41 ,0x44 ,0x2D };
  byte OutPacketBuffer[80]; 
  byte incomingBuffer[80];
  int incomingByteLen;
  byte incomingPacketChecksum;
  byte sourceADR[10];     // przechowanie adresu źródłowego
  byte incomeData [64];   // przechowanie wyodrębnionych danych 
  int dataLength;         // długość odebranych danych
  byte SDinBuffer[34];    // bufor SD ma 34 bajty na pomieszczenie całego wiersza
  byte SDoutBuffer[34] = 
  {'S','1',':',0xFF,':',0xFF,':',':','S','2',':',0xFF,':',0xFF,':',':','S','3',':',0xFF,':',0xFF,':',':','L','1',
  ':',0xFF,':',0xFF,':',':', 0x0A,0x0D}; // szablon stosowany do logowania danych                                   
  int ERRcount = 0;           
  int IcomingTime  = 0; 
  boolean Blink  = LOW; // przechowywanie stanu migania diody
  bool lastReply = false; // false - zła odpowiedź, true - dobra
  boolean ADKisConnected = false; // reszta kodu nie musi odczytywać portu USB
  boolean LogFileSyncADK = false; 
  File LogFile;
  AndroidAccessory ADK("Producent",
  		"Model",
		"Opis",
		"1.0",
		"http://twojastrona.pl",
        "0000000012345678");
// Logowanie danych i obsługa ADK, część 2. z 8
void setup(){
  Serial.begin(115200);   // port szeregowy do monitorowania
  Serial3.begin(115200);  // port szeregowy do XBee  
  pinMode(2, INPUT);      // pin do wykrywania karty SD, można podłączyć przerwanie w razie potrzeby
  digitalWrite (2, HIGH); // zmiana stanu pinu do wykrywania układu na wysoki
  pinMode(13, OUTPUT);    // użycie diody na płycie do diagnostyki
  pinMode(53, OUTPUT);    // zapewnienie, że SPI nie wejdzie w tryb slave
  pinMode(10, OUTPUT);    // pin CS dla karty SD
  while (!SD.begin(10)) { // oczekiwanie na dostępność karty SD
    digitalWrite (13, (Blink = !Blink)); // stałe miganie podczas oczekiwania na kartę
  }
  delay (100);
  for (int i = 0 ; i <= 10 ; i++) {
    digitalWrite (13, (Blink = !Blink));
    delay (100);
  } // szybkie miganie, oznaczające inicjalizację karty SD
  if (SD.exists("sensor.log")) {
    for (int i = 0; i <= 4 ; i++) {
    digitalWrite (13, (Blink = !Blink));
    delay (300);
   } // krótkie wolne miganie, oznaczające, że plik został znaleziony
  }
  else{
    LogFile = SD.open("sensor.log", FILE_WRITE);
    LogFile.close();
  } // utworzenie pliku logu, jeżeli nie został znaleziony
 ADK.begin(); // inicjalizacja połączenia z Androidem
} // koniec setup
// Logowanie danych i obsługa ADK, część 3. z 8
void loop(){
  if (digitalRead(2) == HIGH){
  digitalWrite (13, HIGH); 
  if (IcomingTime >= 25){
    if (lastReply){
    SendOK();
    }
    else{
    Serial3.write (badPacket,21);
   } 
   IcomingTime = 0; 
  }
  HandleADK();
  CheckForIncoming();
  delay (50); 
  IcomingTime++;                  
  } // koniec if (digitalRead(2) == HIGH)
  else{
  IcomingTime = 1000; 
  bool lastReply = false; // żądanie ustawienia nowego pakietu jeżeli węzeł czeka na odpowiedź
  digitalWrite (13, (Blink = !Blink));  // miganie, jeżeli karta SD jest niedostępna
  delay (100);
  } // koniec instrukcji else dla if (digitalRead(2) == HIGH)
} // koniec loop
// Logowanie danych i obsługa ADK, część 4. z 8
void CheckForIncoming(){
  incomingBuffer[0] =  0; // wyzerowanie pierwszego bajtu bufora wejściowego
  if (Serial3.available() && 0x7E == (incomingBuffer[0] = Serial3.read())){
    incomingBuffer[1] = Serial3.read(); // pobranie długości pakietu
    incomingBuffer[2] = Serial3.read();   
    incomingByteLen = incomingBuffer[1] + incomingBuffer[2]; // obliczenie długości pakietu
    incomingPacketChecksum = 0; // wyzerowanie sumy kontrolnej
    for (int i  = 3; i <= incomingByteLen + 3; i++){
      incomingBuffer[i]  = Serial3.read();  // odebranie pakietu
      incomingPacketChecksum += incomingBuffer[i]; // obliczenie sumy kontrolnej
    }
    incomingPacketChecksum = (0xFF - incomingPacketChecksum); // zakończenie obliczenia sumy kontrolnej
    incomingByteLen += 3;
    if (incomingByteLen > 0 && incomingPacketChecksum == 
      incomingBuffer[incomingByteLen+1]){
      Serial3.flush(); // wysłanie bufora danych
      ReadPacket(); // odczyt i obróbka danych
      SendOK(); // odpowiedź do pierwotnego nadawcy
    }   
    else { // jeżeli suma jest błędna, wyczyszczenie i wysłanie odpowiedzi o złym pakiecie
      ERRcount++; // zwiększenie licznika błędów
      for (int i = 0; i <= 80; i++){  // usunięcie pakietu z bufora wejściowego
        incomingBuffer[i] = 0;
      }
      Serial3.flush(); // wyczyszczenie połączenia szeregowego
      delay (100);
      // reset połączenia szeregowego, jeżeli wystąpiło zbyt dużo błędów
      if (ERRcount == 10) {   
        Serial3.end();   // całkowite zamknięcie połączenia szeregowego
        for (int i = 0; i <= 10; i++) {  // miganie diody do weryfikacji
          digitalWrite (13, (Blink = !Blink));
          delay (50);
          ERRcount = 0; // reset licznika błędów
        }
        Serial3.begin(115200); // ponowne otwarcie połączenia szeregowego
        delay (30);
      }
      Serial3.write (badPacket,21); // wysłanie odpowiedzi o złym pakiecie
      lastReply = false; // ustawienie flagi o ostatniej odpowiedzi o złym pakiecie
    }  // koniec instrukcji else dot. błędnego pakietu
  } // koniec if (Serial3.available() && 0x7E 
} // koniec void CheckIncoming()
// Logowanie danych i obsługa ADK, część 5. z 8
void ReadPacket(){
  IcomingTime  = 0;  // reset czasu odebrania dobrego pakietu        
  switch (incomingBuffer[3]){  // sprawdzenie typu pakietu i wysłanie odpowiedzi
    case 0x90:  // pakiet danych
      dataLength = 0;
      for (int i = 4; i <= 13; i++){  // pobranie obu adresów urządzenia źródłowego
        sourceADR[i-4] = incomingBuffer[i];
     }
     dataLength = incomingByteLen - 15;  // zmniejszenie do długości danych, aby je pobrać
     for (int i = 0; i <= dataLength; i++){
       incomeData [i] = incomingBuffer[i+15]; // przetwarzanie danych z pakietu 
     }
     if (dataLength == 4) { // wysłanie danych do funkcji przygotowującej, jeżeli długość jest właściwa
       PrepareDataForSD();
     }
     break;
   case 0x8B:  // jeżeli to pakiet odpowiedzi na transmisję, wykonaj czynność
     Serial.println ("Status transmisji");
     break;
   case 0x88: // wyświetlenie informacji o odpowiedzi na polecenie
      Serial.print("Odpowiedź na polecenie:");
      Serial.print (incomingBuffer[8], HEX);
      Serial.println (incomingBuffer[9],HEX);
      break;
   default: // informacja o pakiecie nieznanego typu
     Serial.println ("blad: nieznany typ pakietu");
  } // koniec Switch Case
} // koniec void ReadPacket
// Logowanie danych i obsługa ADK, część 6. z 8
void PrepareDataForSD(){
  switch (incomeData[1]){
    case '1':
      SDoutBuffer[3] =  incomeData[2];
      SDoutBuffer[5] =  incomeData[3];
      break;
    case '2':
      SDoutBuffer[11] =  incomeData[2];
      SDoutBuffer[13] =  incomeData[3];
      break;
    case '3':
      SDoutBuffer[19] =  incomeData[2];
      SDoutBuffer[21] =  incomeData[3];
      // dane z lokalnego czujnika mogą być odczytane i dodane do bufora SD na pozycji L1
      LogFile = SD.open("sensor.log", FILE_WRITE);  // otwarcie pliku do zapisu   
      LogFile.seek(LogFile.size()); // odszukanie końca pliku w celu dołączenia danych
      if (LogFile) {
        LogFile.write (SDoutBuffer,34);
        Serial.write (SDoutBuffer,34);
        if (ADKisConnected && LogFileSyncADK){
          ADK.write (SDoutBuffer,34); 
        }
      } // koniec if (LogFile)
      LogFile.close();
      SDoutBuffer[3]   =  0xFF;  // reset bufora SD
      SDoutBuffer[5]   =  0xFF;
      SDoutBuffer[11] =  0xFF;
      SDoutBuffer[13] =  0xFF;
      SDoutBuffer[19] =  0xFF;
      SDoutBuffer[21] =  0xFF;
      break;
  } // koniec switch
} // koniec void PrepareDataForSD()
// Logowanie danych i obsługa ADK, część 7. z 8
void SendOK(){
  delay (50);
  byte length = 2; 
  byte Outdata[2] = {'O', 'K'};
  OutPacketBuffer[17 + length] = 0; // wyzerowanie bajtu sumy kontrolnej
  OutPacketBuffer[0] = 0x7E;        // bajt startowy
  OutPacketBuffer[1] = 0;           // pierwszy bajt długości wyzerowany z bieżącymi ograniczeniami
  OutPacketBuffer[3] = 0x10;        // typ ramki, żądanie transmisji
  OutPacketBuffer[4] =  0;          // identyfikator ramki
  for (int i = 5; i <= 14; i++){    // dodanie adresów
    OutPacketBuffer[i] = sourceADR[i-5];
  }
  OutPacketBuffer[15] = 0 ;          // ustawienie obu opcji
  OutPacketBuffer[16] = 0 ;
  for (int i = 0; i < length; i++){
    OutPacketBuffer[i + 17] =  Outdata [i];  // dodanie danych do pakietu
  }
  OutPacketBuffer[2] = 14 + length;      // ustawienie młodszego bajtu długości
  for (int i = 0; i <  OutPacketBuffer[2]; i++){   // początek obliczeń sumy kontrolnej
    OutPacketBuffer[17+ length] =  OutPacketBuffer[17+ length] + OutPacketBuffer[i+3];
  }
  // umieszczenie sumy kontrolnej i ukończenie pakietu
  OutPacketBuffer[17+ length]= 0xFF - OutPacketBuffer[17+ length]; 
  Serial3.write(OutPacketBuffer, (18 + length));
  lastReply = true;
} // koniec void SendOK()
// Logowanie danych i obsługa ADK, część 8. z 8
void HandleADK(){
  if (ADK.isConnected()) {
    delay (100);
    ADKisConnected = true;
    if (ADK.available() > 0){    // sprawdzenie przychodzących danych
      switch (ADK.read()){
        case 'a': {
          Serial.println('a');
          File LogFile = SD.open("sensor.log");      
          if (LogFile) {
            while (LogFile.available()) { // odczyt bajtów do bufora 
              for (int i = 0; i < 34; i ++){
                SDinBuffer[i] = LogFile.read();
              }  
             ADK.write (SDinBuffer, 34);
            } // koniec while (LogFile.available())
            LogFileSyncADK = true;
            LogFile.close(); 
          }  // koniec if (LogFile)
          break;
        } // koniec case case 'a': 
        case 'b':
          LogFileSyncADK = false;
          break;
        case 'c':
          SD.remove("sensor.log");
          break;
      }//koniec switch (ADK.read())
    } //koniec if (ADK.available() > 0)
  }  //koniec if (acc.isConnected()) 
  else{
     ADKisConnected = false;
     LogFileSyncADK = false; 
  }
}// koniec HandleADK()
