/****************************************************************
      DCF39/DCF49 PACKET PARSER / ANALYZER v1.3 2020.04.01
/****************************************************************/

#include <LiquidCrystal.h>
#include <TimerOne.h>

LiquidCrystal lcd(4,5,6,7,8,9,10);

boolean  byteEnd;
byte     ti;
int      x;
byte     y;
char     c[33];

byte     incomingByte;
byte     buff[256];
byte     addr;
boolean  dataBurstEnd;
byte     realLength;
boolean  parityBit;
byte     headParityChk;
byte     bodyParityChk;
byte     frameSq;
byte     LField1;
boolean  lostData;

volatile int idleTime;

void setup()
{
        lcd.begin(16, 2);
        lcd.cursor();
        lcd.setCursor(0, 0);
        
        Timer1.initialize();
        Timer1.attachInterrupt(timer_interrupt, 500);
        
        Serial.begin(19200);
        
        frameSq = 0;
}

void timer_interrupt(void)
{
        static byte rSq;
        
        ti++;
        if(rSq == 0 && digitalRead(19) == 0)
        {
                rSq = 1;
                ti = 0;
        }
        if(rSq == 1 && ti == 5)
        {
                if(digitalRead(19) == 0)
                {
                        rSq = 2;
                        ti = 0;
                }
                else
                {
                        rSq = 0;
                }
        }
        else if(rSq == 2 && ti == 10)
        {
                bitWrite(incomingByte, 0, digitalRead(19));
        }
        else if(rSq == 2 && ti == 20)
        {
                bitWrite(incomingByte, 1, digitalRead(19));
        }
        else if(rSq == 2 && ti == 30)
        {
                bitWrite(incomingByte, 2, digitalRead(19));
        }
        else if(rSq == 2 && ti == 40)
        {
                bitWrite(incomingByte, 3, digitalRead(19));
        }
        else if(rSq == 2 && ti == 50)
        {
                bitWrite(incomingByte, 4, digitalRead(19));
        }
        else if(rSq == 2 && ti == 60)
        {
                bitWrite(incomingByte, 5, digitalRead(19));
        }
        else if(rSq == 2 && ti == 70)
        {
                bitWrite(incomingByte, 6, digitalRead(19));
        }
        else if(rSq == 2 && ti == 80)
        {
                bitWrite(incomingByte, 7, digitalRead(19));
        }
        else if(rSq == 2 && ti == 90)
        {
                parityBit = digitalRead(19);
                byteEnd = true;
        }
        if(rSq == 2 && ti == 100)
        {
                rSq = 0;
        }
        
        if(digitalRead(19) == 0)
        {
                idleTime = 0;
        }
        else
        {
                idleTime++;
        }
}

//DISPLAYS CHARACTER
void lcdOut()
{
        int i;

        char ch = incomingByte;
        
        //ASCII CODE FILTER
        if(ch < 32 || ch > 126)
        {
                ch = ' ';
        }
        
        lcd.print(ch);
        c[16 * y + x] = ch;
        x++;
        if(x == 16 && y == 0)
        {
                x = 0; y = 1;
        }  
        else if(x == 16 && y == 1)
        {
                for(i = 0; i < 16; i++)
                {
                        c[i] = c[16 + i];
                }
                for(i = 0; i < 16; i++)
                {
                        c[16 + i] = '\0';
                }
                lcd.clear();
                lcd.noCursor();
                for(i = 0; i < 16; i++)
                {
                        lcd.print(c[i]);
                }
                lcd.cursor();
                x = 0; y = 1;
        }
        lcd.setCursor(x, y);
}

//CLEAR BUFFER
void bufClear()
{
        int i;
        for(i = 0; i < 256; i++)
        {
                buff[i] = '\0';
        }
}

void checkAndStore()
{
        byte paritySum;
        byte LField2;
        
        boolean bit7 = incomingByte & B10000000;
        boolean bit6 = incomingByte & B01000000;
        boolean bit5 = incomingByte & B00100000;
        boolean bit4 = incomingByte & B00010000;
        boolean bit3 = incomingByte & B00001000;
        boolean bit2 = incomingByte & B00000100;
        boolean bit1 = incomingByte & B00000010;
        boolean bit0 = incomingByte & B00000001;
        
        Serial.print(bit7);
        Serial.print(bit6);
        Serial.print(bit5);
        Serial.print(bit4);
        Serial.print(bit3);
        Serial.print(bit2);
        Serial.print(bit1);
        Serial.print(bit0);
        
        Serial.print(' ');
        Serial.print(' ');
        Serial.print(parityBit);
        
        //Even Parity Check
        paritySum = 0;
        
        if(bit7){ paritySum++; }
        if(bit6){ paritySum++; }
        if(bit5){ paritySum++; }
        if(bit4){ paritySum++; }
        if(bit3){ paritySum++; }
        if(bit2){ paritySum++; }
        if(bit1){ paritySum++; }
        if(bit0){ paritySum++; }
        if(parityBit){ paritySum++; }
        
        if(paritySum % 2 == 0)
        {
                if(frameSq == 0 && headParityChk < 4)
                {
                        headParityChk++;
                }
                else if(frameSq == 1)
                {
                        bodyParityChk++;
                }
        }
        else
        {
                if(frameSq == 0)
                {
                        headParityChk = 0;
                }
        }
        
        Serial.print(' ');
        Serial.print(' ');
        if(paritySum % 2 == 0)
        {
                Serial.print("OK");
        }
        else
        {
                Serial.print("ER");
        }
        
        Serial.print(' ');
        Serial.print(' ');
        if(incomingByte < 16) {
                Serial.print(0, HEX);
        }
        Serial.print(incomingByte, HEX);

        char ch = incomingByte;
        
        //ASCII CODE FILTER
        if(ch < 32 || ch > 126)
        {
                ch = ' ';
        }
        
        Serial.print(' ');
        Serial.print(' ');
        Serial.println(ch);
        
        if(frameSq == 0)
        {
                buff[addr] = incomingByte;
                
                //SEEK HEADER
                if(buff[addr - 3] == 0x68 && buff[addr] == 0x68)
                {
                        LField1 = buff[addr - 2];
                        LField2 = buff[addr - 1];
                        if(LField1 == LField2)
                        {
                                if(headParityChk == 4)
                                {
                                        bufClear();
                                        addr = 0;
                                        idleTime = 0;
                                        bodyParityChk = 0;
                                        lostData = false;
                                        frameSq = 1;
                                }
                                else
                                {
                                        addr++;
                                }
                        }
                        else
                        {
                                addr++;
                        }
                }
                else
                {
                        addr++;
                }
        }
        else if(frameSq == 1) //BODY
        {
                buff[addr] = incomingByte;
                
                if(addr == LField1 + 1)
                {
                        frameSq = 2;
                }
                else
                {
                        addr++;
                }
        }
}

void parsePk()
{
        int  i;
        
        byte      CField;
        byte      AField;
        byte      CIField;
        byte      checkSum;
        byte      endMarker;
        int       year;
        byte      month;
        byte      day;
        byte      dOWeek;
        byte      hour;
        boolean   summer;
        byte      minute;
        byte      second;
        
        CField     = buff[0x00];
        AField     = buff[0x01];
        CIField    = buff[0x02];
        if(lostData)
        {
                checkSum   = buff[addr - 2];
                endMarker  = buff[addr - 1];
                realLength = addr - 2;
        }
        else
        {
                checkSum   = buff[LField1];
                endMarker  = buff[LField1 + 1];
                realLength = LField1;
        }
        
        Serial.println();
        
        //-- -------------------------------------------- --//
        
        if(AField == 0x00 && CIField == 0x00)
        {
                Serial.println("TIME");
                
                //PARSE TIME
                year   = buff[9] & B01111111;
                year   = year + 2000;
                month  = buff[8] & B00001111;
                day    = buff[7] & B00011111;
                dOWeek = buff[7] & B11100000;
                dOWeek = dOWeek >> 5;
                hour   = buff[6] & B00011111;
                summer = buff[6] & B10000000;
                minute = buff[5] & B00111111;
                second = buff[4] & B11111100;
                second = second >> 2;
                
                Serial.print(year); 
                Serial.print(',');
                if(month < 10)
                {
                        Serial.print('0');
                }
                Serial.print(month); 
                Serial.print(',');
                if(day < 10)
                {
                        Serial.print('0');
                }
                Serial.print(day); 
                switch(dOWeek)
                {
                        case 1:
                                Serial.print(" Mon "); break;
                        case 2:
                                Serial.print(" Tue "); break;
                        case 3:
                                Serial.print(" Wed "); break;
                        case 4:
                                Serial.print(" Thu "); break;
                        case 5:
                                Serial.print(" Fri "); break;
                        case 6:
                                Serial.print(" Sat "); break;
                        case 7:
                                Serial.print(" Sun "); break;
                }
                if(hour < 10)
                {
                        Serial.print('0');
                }
                Serial.print(hour); 
                Serial.print(':');
                if(minute < 10)
                {
                        Serial.print('0');
                }
                Serial.print(minute); 
                Serial.print(':');
                if(second < 10)
                {
                        Serial.print('0');
                }
                Serial.print(second); 
                if(summer)
                {
                        Serial.println(" Summer Time");
                }
                else
                {
                        Serial.println(" Standard Time");
                }
        }
        else if(AField == 0xFF && CIField == 0xFF)
        {
                Serial.println("BROADCAST");
        }
        else
        {
                Serial.println("COMMAND");
        }
        
        //-- -------------------------------------------- --//
        
        Serial.print("L Field: ");
        Serial.print(LField1);
        Serial.print('d');
        
        Serial.print(" / Real L: ");
        Serial.print(realLength);
        Serial.print('d');
        if(lostData)
        {
                Serial.print('*');
        }
        Serial.println();
        
        //-- -------------------------------------------- --//
        
        Serial.print("C Field: ");
        if(CField < 16) {
                Serial.print(0, HEX);
        }
        Serial.print(CField, HEX);
        
        byte telegramNumber = CField & B11110000;
        telegramNumber = telegramNumber >> 4;
        
        Serial.print(" / Telegram # ");
        Serial.println(telegramNumber);
        
        //-- -------------------------------------------- --//
        
        Serial.print("A Field: ");
        if(AField < 16) {
                Serial.print(0, HEX);
        }
        Serial.println(AField, HEX);
        
        //-- -------------------------------------------- --//
        
        Serial.print("CI Field: ");
        if(CIField < 16) {
                Serial.print(0, HEX);
        }
        Serial.println(CIField, HEX);
        
        //-- -------------------------------------------- --//
        
        Serial.print("Payload: ");
        for(i = 3; i < realLength; i++)
        {
                if(buff[i] < 16) {
                        Serial.print(0, HEX);
                }
                Serial.print(buff[i], HEX);
                Serial.print(' ');
        }
        Serial.print("(");
        for(i = 3; i < realLength; i++)
        {
                //ASCII CODE FILTER
                if(char(buff[i]) < 32 || char(buff[i]) > 126)
                {
                        Serial.print(' ');
                }
                else
                {
                        Serial.print(char(buff[i]));
                }
        }
        Serial.println(')');
        
        //-- -------------------------------------------- --//
        
        Serial.print("Check Sum: ");
        if(checkSum < 16) {
                Serial.print(0, HEX);
        }
        Serial.print(checkSum, HEX);
        Serial.print(' ');
        
        //CHECK SUM CALC
        uint8_t checkSumCalc;
        
        checkSumCalc += CField;
        checkSumCalc += AField;
        checkSumCalc += CIField;
        for(i = 3; i < realLength; i++)
        {
                checkSumCalc += buff[i];
        }
        
        if(checkSumCalc < 16) {
                Serial.print(0, HEX);
        }
        Serial.print(checkSumCalc, HEX);
        Serial.print(' ');
        if(checkSumCalc == checkSum)
        {
                Serial.println("OK");
        }
        else
        {
                Serial.println("ER");
        }
        
        //-- -------------------------------------------- --//
        
        Serial.print("End Marker: ");
        if(endMarker < 16) {
                Serial.print(0, HEX);
        }
        Serial.print(endMarker, HEX);
        Serial.print(' ');
        if(endMarker == 0x16)
        {
                Serial.println("OK");
        }
        else
        {
                Serial.println("ER");
        }
        
        //-- -------------------------------------------- --//
        
        Serial.print("Parity: ");
        Serial.print(4 + bodyParityChk);
        Serial.print("/");
        Serial.print(4 + realLength + 2);
        Serial.print(' ');
        if(bodyParityChk == realLength + 2)
        {
                Serial.println("OK");
        }
        else
        {
                Serial.println("ER");
        }
        
        //-- -------------------------------------------- --//
        
        Serial.println();
        
        bufClear();
        addr = 0;
        headParityChk = 0;
        dataBurstEnd = true;
        frameSq = 0;
}

void loop()
{
        int i;
        
        if(byteEnd)
        {
                lcdOut();
                checkAndStore();
                dataBurstEnd = false;
                byteEnd = false;
        }
        if(frameSq == 0 && idleTime > 100)
        {
                if(dataBurstEnd == false)
                {
                        Serial.println();
                        dataBurstEnd = true;
                }
        }
        else if(frameSq == 1 && idleTime > 100)
        {
                lostData = true;
                parsePk();
        }
        else if(frameSq == 2)
        {
                parsePk();
        }
}