


/* Bakalarska prace Sulc Patrik 221272
 * GPS lokator pro IoT
 * VUT FEKT 2022/2023
 * sulcanepatrik@gmail.com
 * Release Notes pro plugin  https://ww1.microchip.com/downloads/en/DeviceDoc/release_notes_lorawan_v01.10.00_beta.pdf
 * v Archivu je potreba stahnout MPLAB 3.40 a XC8 1.38
 * https://www.microchip.com/en-us/tools-resources/archives/mplab-ecosystem 
 * v tom samem archivu jsou i pluginy do MCC, tam najit LoRaWAN Library v01.10.00, nebo odkaz niz
 * LoRaWAN plugin pro MCC https://www.microchip.com/mymicrochip/filehandler.aspx?ddocname=en593782
 * Datasheet pro LoRa plugin https://ww1.microchip.com/downloads/en/DeviceDoc/40001864B.pdf
 */



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "softwareSPI.h"
#include "mcc_generated_files/mcc.h"

#define debug_Initialize    EUSART2_Initialize
#define debug_Read          EUSART2_Read
#define debug_Write         EUSART2_Write
#define GNSS_Initialize     EUSART1_Initialize
#define GNSS_Read           EUSART1_Read
#define GNSS_Write          EUSART1_Write



    //pripojovaci metoda pro LoRaWAN| OTAA nebo ABP
#define ACTIVATION_METHOD OTAA



#define debug ((bool)0)
#define GNSS  ((bool)1)

#define LF 0x0A

    //ypravy pro nastaveni GNSS
#define NMEA_MSG_OFF        "$PSTMNMEAONOFF,0*4F\r\n"       //vypnout NMEA zprravy
#define NMEA_MSG_ON         "$PSTMNMEAONOFF,1*4E\r\n"       //zapnout NMEA zprravy
#define SAVE_PAR            "$PSTMSAVEPAR*58\r\n"           //ulozit zmenene parametry
#define NMEA_RMC            "$PSTMSETPAR,1201,40*1D\r\n"    //zustanou zapnute pouze --RMC zpravy
#define GNSS_SUSPEND        "$PSTMGPSSUSPEND*14\r\n"        //pripravi GNSS na vypnuti
//#define GNSS_SUSPEND_RES    "$PSTMGPSSUSPENDED*15\r\n"      //odpoved na GNSS_SUSPEND
//#define GNSS_LOWPOWER1      "$PSTMLOWPOWERONOFF,1,1,0,0,0,0,3,7,0,10,0,0,5,0*6F\r\n"
//#define GNSS_LOWPOWER2      "$PSTMCFGLPA,1,3,0,1,8,60,180,0,0,15,9,700,0*17\r\n"

//Struktura pro --RMC zpravu
struct RMC{
    char status[1];
    char time_stamp[10];
    char lat[10];
    char N_S[1];
    char lon[11];
    char E_W[1];
//    signed double speed;
//    signed double trackGood;
//    signed long date;
//    double magVar;
//    char magVarDir;
//    char mode;  
//    signed int checksum;
}RMC_MSG;
    

// ABP Activation Method Keys
uint8_t deviceEui[8] =      { 0x00, 0x04, 0xA3, 0x0B, 0x01, 0x07, 0x22, 0x37};
uint8_t nwkSessionKey[16] = { 0xBA, 0xE7, 0x8B, 0x4E, 0x4F, 0xBC, 0x9C, 0x3D, 0x47, 0x80, 0x2A, 0xA5, 0x33, 0x24, 0x2C, 0x3E};
uint8_t appSessionKey[16] = { 0xFB, 0x9D, 0xDA, 0x2C, 0xD1, 0x36, 0x2E, 0x9D, 0x7D, 0x15, 0x65, 0x13, 0x99, 0x50, 0xD8, 0x15};
uint32_t deviceAddr = 0x260B8853;


// OTAA Activation Method Keys
uint8_t deviceEUI[8] = {0x00, 0x04, 0xA3, 0x0B, 0x01, 0x07, 0x22, 0x37};
uint8_t applicationEUI[8] = {0x25, 0xA3, 0xB8, 0x65, 0xC7, 0x57, 0xF1, 0x23};
uint8_t applicationKey[16] = {0x7E, 0x0F, 0xCC, 0xDA, 0x76, 0x3C, 0xC9, 0xD6, 0xA7, 0xA1, 0xCB, 0x2A, 0xF4, 0xAD, 0x72, 0xDB};

LorawanError_t LORAWAN_JOIN();
LorawanError_t lorawanErrorCode;

unsigned int send_counter = 1;
bool sekunda, recivedF, decodedF = 0;

bool send = 0;
bool resend = 0;
bool send_uBat = 0;
bool resend_uBat = 0;
bool move = 1;
unsigned char in_buffer[100];
unsigned char UBat_buff[6];
unsigned char s_5counter, s_GNSScounter, s_180counter, decode_status =0;
unsigned char WDT_counter = 0;
bool GNSS_Init_F=0;
adc_result_t  U_Bat = 0;    //3.3/1024 = 3.223 mV -> Vout/Vin = 0.647 -> Napeti Baterie = (U_Bat * 3.223 mV)/0.647 = U_Bat * 0,004 981 V


unsigned char channel = 1;      //kanal pro LoRa send  
unsigned char start_channel;

bool destination = 0;   //debug or GNSS, cil pro printf()


void RxData(uint8_t* pData, uint8_t dataLength, OpStatus_t status)
{
}
void RxJoinResponse(bool status)
{
}
/*Funkce pro prime zapisovani do vystupnich reg. UART1 a UART2
 Nahrazena printf                                               */
void sendString(bool destination, const char *str);

/*Vyctou zachytny registr UART, pokud doslo k Overrun err, znovu povoli prijem  */
unsigned char GNSS_checkReceivedChar();
unsigned char debug_checkReceivedChar();
/*Vycte zpravu z UART, Origin - ktery UART, buffer ukazatel kam bude zpravu ukladat, size - velikost buffer
 vrati 1 pokud nacte '\n', jinak 0              */
bool read_MSG(bool origin, char *buffer, unsigned char size);
/*vytiskne na debug error hlasku od LoRa*/
void LoraErrorCodePrint(LorawanError_t lorawanErrorCode);
/*Dekoduje --RMC zpravu na adrese in_str, length - velikost n_str */
unsigned char decode_MSG(const char *in_str, unsigned char length);
void mc3630_init(void);

//funkce pro printf
void putch(char data);


void GNSS_init(void);
void GNSS_RXEnables(void);
void GNSS_RXDisables(void);

void main(void)
{
    // Initialize the device  
    SYSTEM_Initialize();
    OSCCONbits.IDLEN = 0;       //SLEEP() -> sleep
    GNSS_RXDisables();
//    __delay_ms(20);
    destination = debug;
//    printf("GNSS RX Dis\r\n");
//    printf("Inicializace OK\r\n");

//    mc3630_init();
    // <editor-fold defaultstate="collapsed" desc="MCC E/D code">
    // If using interrupts in PIC18 High/Low Priority Mode you need to enable
   // the Global High and Low Interrupts
    // If using interrupts in PIC Mid-Range Compatibility Mode you need to
   // enable the Global and Peripheral Interrupts
    // Use the following macros to:
    // Enable high priority global interrupts
    //INTERRUPT_GlobalInterruptHighEnable();
    // Enable low priority global interrupts.
    //INTERRUPT_GlobalInterruptLowEnable();
    // Disable high priority global interrupts
    //INTERRUPT_GlobalInterruptHighDisable();
    // Disable low priority global interrupts.
    //INTERRUPT_GlobalInterruptLowDisable();
    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();
    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();
    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();
   // Disable the Peripheral Interrupts
   //INTERRUPT_PeripheralInterruptDisable();
    // </editor-fold>
    
    //inicializace LoRaWAN na fr. 868 MHz
    RADIO_ReceiveStop();
    LORAWAN_Reset(ISM_EU868);
    LORAWAN_Init(RxData, RxJoinResponse);
 
    LORAWAN_SetTxPower(10);    
    LoraErrorCodePrint(LORAWAN_JOIN());
    
        
   while (1){
        LORAWAN_Mainloop();
        
        //pocitadlo sekund, f_sekunda se nastavuje v ISR TMR0 (odvozeno od ms)
        if(f_sekunda == 1){
            f_sekunda = 0;
            s_5counter++;
            s_GNSScounter++;
            s_180counter++;
        }
        
        if(s_5counter==5){
            s_5counter=0;
            //inicializace GNSS, probehne jednou po 5 s po spusteni MCU
            if(!GNSS_Init_F){
                GNSS_Init_F = 1;
                GNSS_init();
                __delay_ms(10);
                GNSS_RXEnables();       
                in_buffer[0]='\0';
//                destination = debug;
//                printf("GNSS Init\r\n");
                __delay_ms(10);
            }
            //nastavy opetovny pokus o odeslani zpravy
            if(resend){
                resend = 0;
                send = 1;
            }
            if(resend_uBat){
                resend_uBat = 0;
                send_uBat = 1;
            }
        }
        
        if(s_GNSScounter==60){
            s_GNSScounter=0;
            //send a start_chanel pro stale odesilani kazdou minutu
            send = 1;
            start_channel = channel;
//            destination = debug;
//            printf("2Minuta\r\n");
        }
        if(s_180counter == 180){
            //pro 3 min odesilani napeti
            s_180counter = 0;
            BAT_TR_SetHigh();
            __delay_ms(1);
            U_Bat = ADC_GetConversion(A_BAT);
            BAT_TR_SetLow();
//            destination = debug;
//            printf("UBat %u\r\n",U_Bat);
            send_uBat = 1;
        }



        //pro odesilani polohy
        if(send){
//            destination = debug;
//            printf("SENDING\r\n");
            lorawanErrorCode = LORAWAN_Send(UNCNF, channel, &RMC_MSG, sizeof(RMC_MSG));
//            LoraErrorCodePrint(lorawanErrorCode);
            switch(lorawanErrorCode)
            {
                case 0:
                    send = 0;
                    break;
                case 1: // try to join
                    send = 0;
                    resend = 1;
                    LoraErrorCodePrint(LORAWAN_JOIN());
                    break;
                case 6: // rejoin
                    send = 0;
                    resend = 1;
                    LORAWAN_Reset(ISM_EU868);
                    LORAWAN_Init(RxData, RxJoinResponse);
                    LoraErrorCodePrint(LORAWAN_JOIN());
                    break;
                case 9: // resend payload in other channel
                    if (channel>2) channel = 0;
                    channel++;
                    if(channel == start_channel){
                        send = 0;
                        resend = 1;
//                        printf("Tried All channels\r\n");
                    }
//                    printf("channel %u\r\n",channel);
                    break;
                default:
                    send = 0;
                    resend = 1;
                    break;
            }
        }
        //pro odeslani napeti
        if(send_uBat){
//            destination = debug;
//            printf("SENDING UBAT: %u\r\n",U_Bat);
            //int na string, samotny int neslo poslat (prijimaji se 0)
            sprintf(UBat_buff,"%u",U_Bat);
            lorawanErrorCode = LORAWAN_Send(UNCNF, 4, UBat_buff, sizeof(UBat_buff));
//            lorawanErrorCode = LORAWAN_Send(UNCNF, 4, U_Bat, 2);
//            LoraErrorCodePrint(lorawanErrorCode);
            switch(lorawanErrorCode)
            {
                case 0:
                    send_uBat = 0;
                    break;
                case 1: // try to join
                    send_uBat = 0;
                    resend_uBat = 1;
//                    LoraErrorCodePrint(LORAWAN_JOIN());
                    break;
                case 6: // rejoin
                    send_uBat = 0;
                    resend_uBat = 1;
                    LORAWAN_Reset(ISM_EU868);
                    LORAWAN_Init(RxData, RxJoinResponse);
//                    LoraErrorCodePrint(LORAWAN_JOIN());
                    break;
                case 9: // resend payload in other channel
//                    printf("Channel 4 not available\r\n");
                    break;
                default:
                    send_uBat = 0;
                    resend_uBat = 1;
                    break;
            }          
        }

        //periodicke cteni RX UART od GNSS, pokud '\n' dekoduje spravu
        recivedF = read_MSG(GNSS, in_buffer, sizeof(in_buffer));
        if(recivedF){
            recivedF = 0;
//            destination = debug;
//            printf(in_buffer);
//            __delay_ms(1);
            decodedF = decode_MSG(in_buffer,sizeof(in_buffer));
        }

    
        //uspani
        if(!move_F&&!send&&!send_uBat&&!resend&&!resend_uBat&&0){
//            destination = debug;        
//            printf("Low Power: ");
            GNSS_RESET_SetLow();
            SW_EN_SetLow();
            WDTCONbits.SWDTEN = 1;  //enable WDT
            SLEEP();
            if(!RCONbits.NOT_TO){
                WDT_counter++;
                if(WDT_counter >= 1){
                    WDTCONbits.SWDTEN = 0;  //dis WDT
                    SW_EN_SetHigh();
                    WDT_counter = 0;
                    send_uBat = 1;
                }
            }else{
                WDTCONbits.SWDTEN = 0;  //dis WDT
                SW_EN_SetHigh();
                GNSS_RESET_SetHigh();
                send = 1;
                WDT_counter = 0;
           }
        }
   //konec main while 
   }   
//konec MAIN
}







//pripojeni k lora, metoda podle def ACTIVATION_METHOD
LorawanError_t LORAWAN_JOIN()   // try to join LORA WAN
{
    LorawanError_t err = 0;
    if (ACTIVATION_METHOD)
    {
        // ABP Activation Method
        LORAWAN_SetNetworkSessionKey(nwkSessionKey);
        LORAWAN_SetApplicationSessionKey(appSessionKey);
        LORAWAN_SetDeviceAddress(deviceAddr);
        err = LORAWAN_Join(ABP);
    } else
    {
        // OTAA Activation Method
        LORAWAN_SetApplicationEui(applicationEUI);
        LORAWAN_SetDeviceEui(deviceEUI);
        LORAWAN_SetApplicationKey(applicationKey);
        err = LORAWAN_Join(OTAA);
    }
    return err;
}


//void sendString(bool destination, const char *str)
//{
//    if(destination == GNSS){
//        while(!TXSTA1bits.TRMT);
//        while(*str)
//        {
//            GNSS_Write(*str);
//            str++;
//        }
//    }else if(destination == debug){
//        while(!TXSTA2bits.TRMT);
//        while(*str)
//        {
//            debug_Write(*str);
//            str++;
//        }
//    }else{
//    }
//}


unsigned char debug_checkReceivedChar()
{
    if(RCSTA2bits.OERR){
        // EUSART2 FIFO Overun - restart
        RCSTA2bits.CREN = 0; 
        RCSTA2bits.CREN = 1; 
    }
    return RCREG2;
}

unsigned char GNSS_checkReceivedChar()
{
    if(RCSTA1bits.OERR){
        // EUSART1 FIFO Overun - restart
        RCSTA1bits.CREN = 0; 
        RCSTA1bits.CREN = 1; 
    }
    return RCREG1;
}

bool read_MSG(bool origin, char *buffer, unsigned char size)
{
    static unsigned char UART_REC, pos = 0;
    //GNSS
    if(origin){
        //je neco v RCREG
        if(PIR1bits.RC1IF){
//            printf("RX\r\n");
            //omezeni buffrem
            if(pos<size){
                //dokud neni RCREG prazdny
                while(PIR1bits.RC1IF){
                    UART_REC = GNSS_checkReceivedChar();
                    if(UART_REC != '\n'){
                        buffer[pos] = UART_REC;
                        pos++;
                    }else{
                        buffer[pos++] = '\0';
                        pos=0;
                        return 1;
                    }
                }
            }else{
//                destination = debug;
//                printf("RX MSG too big\r\n");
                pos=0;
            }
        }
    }
    //debug
    //stejne hajo UART1
    else{
        if(PIR3bits.RC2IF){
            if(pos<size){
                while(PIR3bits.RC2IF){
                    UART_REC = debug_checkReceivedChar();
                    if(UART_REC != '\n'){
                        buffer[pos] = UART_REC;
                        pos++;
                    }else{
                        buffer[pos++] = '\0';
                        pos=0;
                        return 1;
                    }
                }
            }else{
                destination = debug;
//                printf("RX MSG too big\r\n");
                pos=0;
            }
        }
    }
    return 0;
}   



void GNSS_init(void)
{
    destination = GNSS;
    printf(NMEA_MSG_OFF);
    printf(NMEA_RMC);
    printf(SAVE_PAR);
    printf(NMEA_MSG_ON);
}




void LoraErrorCodePrint(LorawanError_t lorawanErrorCode)
{
    destination = debug;
    switch (lorawanErrorCode)
    {
        case 0:
            printf("OK\r\r\n");
            break;
        case 1:
            printf("Network not joined\r\n");
            break;
        case 2:
            printf("MAC state not ready for transmission\r\n");
            break;
        case 3:
            printf("Invalid parameter\r\n");
            break;
        case 4:
            printf("Keys not initialized\r\n");
            break;
        case 5:
            printf("Silent immediately active\r\n");
            break;
        case 6:
            printf("Frame counter error -> rejoin needed\r\n");
            break;
        case 7:
            printf("Invalid buffer length\r\n");
            break;
        case 8:
            printf("MAC paused\r\n");
            break;
        case 9:
            printf("No channels found\r\n");     //no_free_ch
            break;
        case 10:
            printf("Invalid class\r\n");
            break;
        case 11:
            printf("MCAST-PARAM-ERROR\r\n");
            break;
        case 12:
            printf("MCAST-MSG-ERROR\r\n");
            break;
    }
}

void GNSS_RXEnables(void){
    RCSTA1bits.CREN = ENABLED;
}
void GNSS_RXDisables(void){
    RCSTA1bits.CREN = DISABLED;
}

//jsouli vyzadovany dalsi parametrz, odkomentovat tu a ve struct
unsigned char decode_MSG(const char *in_str, unsigned char length){
    unsigned char par_counter = 0;
    unsigned char char_count=0;
    unsigned char buffer[15];

//    destination = debug;
//    printf("Dekoduji... \r\n");
    for(unsigned char j=0;j<length;j++){
        if(in_str[j]==','||in_str[j]=='*'){
            buffer[char_count++] = '\0';
            switch(par_counter){
                // <editor-fold defaultstate="collapsed" desc="Case">
            case 0:
                //MSG ID
                
                break;
            case 1:
                //time
                memcpy(RMC_MSG.time_stamp, buffer, char_count-1);
//                RMC_MSG.time_stamp = atof(buffer);
                break;
            case 2:
                //status
                memcpy(RMC_MSG.status, buffer, char_count-1);
//                RMC_MSG.status = buffer[0];
                break;

            case 3:
                //lat
                memcpy(RMC_MSG.lat, buffer, char_count - 1);
//                RMC_MSG.lat = atof(buffer);
                break;
            case 4:
                //N/S
                memcpy(RMC_MSG.N_S, buffer, char_count-1);
//                RMC_MSG.N_S = buffer[0];
                break;
            case 5:
                //<Long>
                memcpy(RMC_MSG.lon, buffer, char_count-1);
//                RMC_MSG.lon = atof(buffer);
                break;

            case 6:
                //<E/W>
                memcpy(RMC_MSG.E_W, buffer, char_count-1);
//                RMC_MSG.E_W = buffer[0];
                break;
            case 7:
                //<Speed>
//                RMC_MSG.speed = atof(buffer);
                break;
            case 8:
                //<Trackgood>
//                RMC_MSG.trackGood = atof(buffer);
                break;
            case 9:
                //<Date>
//                printf("\r\n%s %d\r\n",buffer, atol(buffer));
//                RMC_MSG.date = atol(buffer);
                break;
            case 10:
                //<MagVar>
//                RMC_MSG.magVar = atof(buffer);
                break;
            case 11:
                //<MagVarDir>
//                RMC_MSG.magVarDir = buffer[0];
                break;

            case 12:
                //<mode>
//                RMC_MSG.mode = buffer[0];
                break;
            case 13:
                //<checksum>
//                RMC_MSG.checksum = (int)strtol(buffer, NULL, 16);
//                printf("Case 13   ");
                return 0;
//                break;
            default:
//                printf("Moc parametru\r\n");
                return 2;
            }
            // </editor-fold>   
            
        par_counter++;
        char_count = 0;

        }else{
            buffer[char_count++] = in_str[j];
        }
        if(in_str[j+1]=='\0'){
            buffer[char_count++] = '\0';
//            RMC_MSG.checksum = (int)strtol(buffer, NULL, 16);
            return 1;
        }
    }   
//    printf("konec buffru   ");
    return 4;
}




void putch(char data){
    if(destination){
        //gnss
        while(!PIR1bits.TX1IF)  // check buffer
        continue;     // wait till ready
        TXREG1 = data;   // send data
    }else{
        //debug
        while(!PIR3bits.TX2IF)  // check buffer
        continue;     // wait till ready
        TXREG2 = data;   // send data
    }
}


void mc3630_init(void){
    sSPICS = 1;
    sSPISCK = 1;
    sSPIMOSI = 1;
    sendSPIWord(0x10,0x01,write);
    sendSPIWord(0x24,0x40,write);
    __delay_ms(1);
    while(!sendSPIWord(0x18,0x00,read));
    do{
        sendSPIWord(0x0D,0x80,write);
    }
    while(sendSPIWord(0x0D,0x00,read)!=0x80);
    sendSPIWord(0x0F,0x42,write);
    sendSPIWord(0x10,0x01,write);
    __delay_ms(10);
    sendSPIWord(0x20,0x01,write);
    sendSPIWord(0x21,0x80,write);
    sendSPIWord(0x28,0x00,write);
    sendSPIWord(0x1A,0x00,write);
    
}