//*******************************************************************************
//   MSP430FR5969 I2C Master for SGP30 Sensor Communication
//*******************************************************************************

#include "intrinsics.h"
#include "msp430fr5969.h"
#include <msp430.h> 
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

// I2C address for SGP30
#define SLAVE_ADDR  0x58

// SGP30 Commands
#define INIT_AIR_QUALITY       0x2003
#define MEASURE_AIR_QUALITY    0x2008
#define MEASURE_TEST           0x2032

// Buffer size for SGP30 data (CO2eq, TVOC, and CRCs)
#define MAX_BUFFER_SIZE 6

// Timeout and Success Constants
#define ERROR_TIMEOUT 1
#define SUCCESS 0

// I2C State Machine
typedef enum I2C_ModeEnum {
    IDLE_MODE,
    NACK_MODE,
    TX_REG_ADDRESS_MODE,
    RX_REG_ADDRESS_MODE,
    TX_DATA_MODE,
    RX_DATA_MODE,
    SWITCH_TO_RX_MODE,
    SWITHC_TO_TX_MODE,
    TIMEOUT_MODE
} I2C_Mode;

// Global Variables for I2C communication
volatile I2C_Mode MasterMode = IDLE_MODE;
/* The Register Address/Command to use*/
uint8_t TransmitRegAddr = 0;
uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t TransmitIndex = 0, ReceiveIndex = 0, TXByteCtr = 0, RXByteCtr = 0;

// Function Prototypes
void UART_Init(void);
void initGPIO(void);
void initClockTo16MHz(void);
int initI2C();
// uint8_t calculateCRC(uint8_t data[], uint8_t length);
void initSGP30();
void readSGP30Data();
void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count);
I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t *data, uint8_t count);
I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t count);

void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count) {
    uint8_t i;
    for (i = 0; i < count; i++) {
        dest[i] = source[i];
    }
}

// I2C Write function
I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t *data, uint8_t count) {
    MasterMode = TX_REG_ADDRESS_MODE;
    CopyArray(data, TransmitBuffer, count);

    TXByteCtr = count;
    UCB0I2CSA = dev_addr;
    UCB0CTLW0 |= UCTR; // put into TX mode (WRITE)
    UCB0CTLW0 |= UCTXSTT;

    // Continuously try to send START condition until it is successfully sent
    // while (UCB0CTLW0 & UCTXSTT) {
    //     UCB0CTLW0 |= UCTXSTT; // Send START condition
    // }

    // Wait for stop condition
    while (UCB0CTLW0 & UCTXSTP);

    return MasterMode;
}

// I2C Read function
I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t count) {
    // MasterMode = RX_DATA_MODE;
    MasterMode = TX_REG_ADDRESS_MODE;
    RXByteCtr = count;
    UCB0I2CSA = dev_addr;
    UCB0CTLW0 &= ~UCTR;
    UCB0CTLW0 |= UCTXSTT;

    // Wait for stop condition
    while (UCB0CTLW0 & UCTXSTP);

    // Check if RXByteCtr was decremented to confirm data reception
    if (RXByteCtr != 0) {
        MasterMode = TIMEOUT_MODE;
    }

    return MasterMode;
}

// uint8_t calculateCRC(uint8_t data[], uint8_t length) {
//     uint8_t crc = 0xFF;
//     uint8_t i, j;
//     for (i = 0; i < length; i++) {
//         crc ^= data[i];
//         for (j = 0; j < 8; j++) {
//             crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : crc << 1;
//         }
//     }
//     return crc;
// }

// UART initialization
void UART_Init(void) {
    // Configure UART pins
    P2SEL1 |= BIT0 | BIT1; // Set P2.0 and P2.1 to UART mode
    P2SEL0 &= ~(BIT0 | BIT1);
    printf("UART pins configured.\n");

    // Configure UART
    UCA0CTLW0 |= UCSWRST; // Put UART in reset mode
    UCA0CTLW0 |= UCSSEL__SMCLK; // Select SMCLK as clock source
    printf("UART control configured.\n");

    // Baud rate configuration for 9600 baud (assuming 16MHz clock)
    UCA0BRW = 104; // 16MHz / 9600 = ~104
    UCA0MCTLW = 0xD600; // Set modulation parameters for 9600 baud
    printf("UART baud rate set.\n");

    UCA0CTLW0 &= ~UCSWRST; // Initialize the UART
    printf("UART initialized.\n");
}

// GPIO Initialization
void initGPIO(void) {
    // Set P1.0 as output for LED
    P1DIR |= BIT0;
    P1OUT &= ~BIT0; // Turn off LED

    // Additional GPIO configurations can be added here
    printf("GPIO initialized.\n");
}

// Clock Initialization to 16MHz
void initClockTo16MHz(void) {
    CSCTL0_H = CSKEY_H;              // Unlock CS registers
    CSCTL1 = DCOFSEL_4 | DCORSEL;    // Set DCO to 16MHz
    CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK; // Set ACLK to VLO, SMCLK and MCLK to DCO
    CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1; // Set all dividers to 1
    CSCTL0_H = 0;                    // Lock CS registers
    printf("Clock set to 16MHz.\n");
}

int initI2C() {
    // Set pins for I2C functionality (e.g., P1.6 for SDA and P1.7 for SCL)
    P1SEL0 |= BIT6 | BIT7;      // Set P1.6 and P1.7 to I2C mode
    P1SEL1 &= ~(BIT6 | BIT7);

    UCB0CTLW0 = UCSWRST;                      // Enable SW reset
    UCB0CTLW0 |= UCMODE_3;                    // I2C mode
    UCB0CTLW0 |= UCMST;                       // set as master
    UCB0CTLW0 |= UCSSEL__SMCLK;               // select SMCLK
    UCB0CTLW0 |=UCSYNC;                       // synchronous mode
    UCB0BRW = 160;                            // fSCL = SMCLK/10 = 100kHz (assuming SMCLK = 1MHz)
    UCB0I2CSA = SLAVE_ADDR;                   // Set Slave Address = 0x58
    UCB0CTLW1 = UCASTP_2;                     // auto STOP mode

    P1SEL1 &= ~BIT6;
    P1SEL0 |= BIT6;                           // P1.6 = SDA
    P1SEL1 &= ~BIT7;
    P1SEL0 |= BIT7;                           // P1.7 = SCL
    printf("I2C pins configured.\n");

    PM5CTL0 &= ~LOCKLPM5;                     // turn on I/O
    UCB0CTLW0 &= ~UCSWRST;                    // Clear SW reset, resume operation
    UCB0IE |= UCTXIE0;                        // local enable for TX0
    __enable_interrupt();                     // enable maskables
    UCB0IE |= UCNACKIE;                       // Enable NACK interrupt (optional)
    printf("I2C configured.\n");

    if ((UCB0CTLW0 & UCSWRST) == 0) {
        P1OUT |= BIT0;  // Assuming P1.0 is connected to the LED for success indication
        printf("I2C initialization successful.\n");
        return 0; // Success
    } else {
        P1OUT &= ~BIT0; // Turn off LED for failure indication
        printf("I2C initialization failed.\n");
        return 1; // Failure
    }
}

// SGP30 Initialization
void initSGP30() {
    uint8_t command[2] = { INIT_AIR_QUALITY >> 8, INIT_AIR_QUALITY & 0xFF };
    I2C_Master_WriteReg(SLAVE_ADDR, command, 2);
    printf("SGP30 initialization command sent.\n");
}

// Read SGP30 Data
void readSGP30Data() {
    uint8_t command[2] = { MEASURE_AIR_QUALITY >> 8, MEASURE_AIR_QUALITY & 0xFF };
    I2C_Master_WriteReg(SLAVE_ADDR, command, 2);
    printf("Data request command sent to SGP30.\n");

    I2C_Master_ReadReg(SLAVE_ADDR, 6);

    // Parse the data and validate CRCs
    uint16_t CO2eq = (ReceiveBuffer[0] << 8) | ReceiveBuffer[1];
    // uint8_t CO2_CRC = ReceiveBuffer[2];
    uint16_t TVOC = (ReceiveBuffer[3] << 8) | ReceiveBuffer[4];
    // uint8_t TVOC_CRC = ReceiveBuffer[5];

    // if (calculateCRC(ReceiveBuffer, 2) != CO2_CRC) {
    //     printf("Error: CO2 CRC mismatch.\n");
    //     return;
    // }
    // if (calculateCRC(ReceiveBuffer + 3, 2) != TVOC_CRC) {
    //     printf("Error: TVOC CRC mismatch.\n");
    //     return;
    // }

    printf("CO2eq: %u ppm\n", CO2eq);
    printf("TVOC: %u ppb\n", TVOC);
}

// Main Function
int main(void) {
    WDTCTL = WDTPW | WDTHOLD; // Stop the watchdog timer
    __delay_cycles(100000);    // Short delay for stability

    initGPIO(); // Initialize GPIO

    //initClockTo16MHz(); // Initialize the clock to 16MHz
    UART_Init(); // Initialize UART for debugging

    if (initI2C() != 0) {
        printf("I2C Initialization failed, exiting.\n");
        return 1; // Exit if I2C initialization fails
    }

    initSGP30(); // Start SGP30 sensor initialization
    __delay_cycles(16000000); // Wait for sensor readiness (about 1 second at 16MHz)

    // Wait 15 seconds for SGP30 to complete initial calibration
    // printf("Waiting 15 seconds for SGP30 calibration...\n");
    // __delay_cycles(240000000); // 15-second delay at 16MHz
    
    while (1) {
        readSGP30Data(); // Read and display data from SGP30
        __delay_cycles(1600000); // Delay between readings (about 100ms at 16MHz)
    }
}

//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  //Must read from UCB0RXBUF
  uint8_t rx_val = 0;
  switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
  {
    case USCI_NONE:          break;         // Vector 0: No interrupts
    case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
    case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
      break;
    case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
    case USCI_I2C_UCSTPIFG:  break;         // Vector 8: STPIFG
    case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
    case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
    case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
    case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
    case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
    case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
    case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
        rx_val = UCB0RXBUF;
        if (RXByteCtr)
        {
          ReceiveBuffer[ReceiveIndex++] = rx_val;
          RXByteCtr--;
        }

        if (RXByteCtr == 1)
        {
          UCB0CTLW0 |= UCTXSTP;
        }
        else if (RXByteCtr == 0)
        {
          UCB0IE &= ~UCRXIE;
          MasterMode = IDLE_MODE;
          __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
        }
        break;
    case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
        switch (MasterMode)
        {
          case TX_REG_ADDRESS_MODE:
              UCB0TXBUF = TransmitRegAddr;
              if (RXByteCtr)
                  MasterMode = SWITCH_TO_RX_MODE;   // Need to start receiving now
              else
                  MasterMode = TX_DATA_MODE;        // Continue to transmision with the data in Transmit Buffer
              break;

          case SWITCH_TO_RX_MODE:
              UCB0IE |= UCRXIE;              // Enable RX interrupt
              UCB0IE &= ~UCTXIE;             // Disable TX interrupt
              UCB0CTLW0 &= ~UCTR;            // Switch to receiver
              MasterMode = RX_DATA_MODE;    // State state is to receive data
              UCB0CTLW0 |= UCTXSTT;          // Send repeated start
              if (RXByteCtr == 1)
              {
                  //Must send stop since this is the N-1 byte
                  while((UCB0CTLW0 & UCTXSTT));
                  UCB0CTLW0 |= UCTXSTP;      // Send stop condition
              }
              break;

          case TX_DATA_MODE:
              if (TXByteCtr)
              {
                  UCB0TXBUF = TransmitBuffer[TransmitIndex++];
                  TXByteCtr--;
              }
              else
              {
                  //Done with transmission
                  UCB0CTLW0 |= UCTXSTP;     // Send stop condition
                  MasterMode = IDLE_MODE;
                  UCB0IE &= ~UCTXIE;                       // disable TX interrupt
                  __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
              }
              break;

          default:
              __no_operation();
              break;
        }
        break;
    default: break;
  }
}