/**
 * Author : Anatole SCHRAMM-HENRY
 * Created the : 13/06/2021
 * This TEST program aims to verify all the basic functions of the gateway by displaying some output to a serial console :
 * - Testing that both NRF work correctly
 * - Testing the LED status lights
 * - Testing the NRF IRQs detection with the PCF8574
 */

#include <SPI.h>
#include <ESP8266WiFi.h>
#include <PCF8574.h>
#include "RF24.h"
#include "definition.h"


#define NRF_1_CE 15 //chip enable
#define NRF_2_CE 16 //chip enable

#define NRF_1_CS 5 //chip select
#define NRF_2_CS 4 //chip select

#define CHAN 108   //0-125
#define RECV_CHECK 100 //We test every 100 ms if we received something
#define WIFI_CHECK_TIMEOUT 20000

DataPacket p;

RF24 NRF_1(NRF_1_CE, NRF_1_CS);
RF24 NRF_2(NRF_2_CE, NRF_2_CS);
PCF8574 PCF(0x20);

const uint8_t ADDR[] = "WEST1", ADDR2[] = "ABCDE";
uint64_t timeStamp(0), recvSlot(0);
uint32_t freeMem(0);
uint16_t biggestContigMemBlock(0);
uint8_t frag(0);
volatile boolean IRQFlag(false);

IRAM_ATTR void NRFIRQsHandler(void *p)
{
  Serial.println("NRF IRQs detected !");
  *(boolean *)p = true;
}

void setup() {
  //We do not need to read on the serial bus
  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
  delay(1000);
  Serial.println("\nSetup begin");
  //We set the RXD0 as a GPIO
  pinMode(D9, FUNCTION_3);
  pinMode(D9, INPUT_PULLUP);
  attachInterruptArg(D9,&(NRFIRQsHandler), (void *)&IRQFlag, FALLING);
  //To prevent wear and tear on the flash mem
  WiFi.persistent(false);
  WiFi.disconnect(true);
  WiFi.softAPdisconnect(true);
  
  Serial.printf("\nNRF 1 %s\n",NRF_1.begin() ? "started" : "error");
  if(!NRF_1.isChipConnected())
    Serial.println("NRF 1 is missing");
  else
    Serial.println("NRF 1 is detected");
    
  NRF_1.setChannel(CHAN); 
  NRF_1.setPALevel(RF24_PA_MIN);
  NRF_1.setDataRate(RF24_250KBPS);
  NRF_1.setRetries(8,15);
  NRF_1.openReadingPipe(1, ADDR);
  NRF_1.startListening();

  Serial.printf("NRF 2 %s\n",NRF_2.begin() ? "started" : "error");
  if(!NRF_2.isChipConnected())
    Serial.println("NRF 2 is missing");
  else
    Serial.println("NRF 2 is detected");
    
  NRF_2.setChannel(CHAN); 
  NRF_2.setPALevel(RF24_PA_MIN);
  NRF_2.setDataRate(RF24_250KBPS);
  NRF_2.setRetries(8,15);
  NRF_2.openReadingPipe(1, ADDR);
  NRF_2.startListening();

  memset(&p, 0, sizeof p);

  //Setting the I2C pins and the PCF8574
  Wire.begin(0,2);
  Serial.printf("PCF %s\n", PCF.begin() ? "found" : "not found");
  PCF.pinMode(PCF8574::P2, OUTPUT);
  PCF.pinMode(PCF8574::P3, OUTPUT);
  PCF.pinMode(PCF8574::P4, OUTPUT);
  Serial.println("End setup");
}

void loop() 
{
  if(millis() - recvSlot > RECV_CHECK)
  {
    //We check if we got and IRQ from one, both NRFs
    if(IRQFlag)
    {
      bool tx_ok, tx_fail, rx_ready;
      //We read the PCFs IO to check which NRF raised the IRQ :
      if(!PCF.digitalRead(PCF8574::P1)) //IRQs are active low
      {
        Serial.printf("NRF 1 triggered the IRQs\n Checking why :\n");
        NRF_1.whatHappened(tx_ok, tx_fail, rx_ready);
        if(tx_ok)
        {
          Serial.println("NRF 1 TX_OK");
        }
        if(tx_fail)
        {
          Serial.println("NRF 1 TX_FAIL");
        }
        if(rx_ready)
        {
          Serial.printf("NRF 1 Received %u bytes with sig(%s) : \n",NRF_1.getPayloadSize(), NRF_1.testRPD()?"good":"bad");
          NRF_1.read(&p, sizeof(p));
          debugStruct(&p);
        }
      }
      if(!PCF.digitalRead(PCF8574::P0)) //IRQs are active low
      {
        Serial.printf("NRF 2 triggered the IRQs\n Checking why :\n");
        NRF_2.whatHappened(tx_ok, tx_fail, rx_ready);
        if(tx_ok)
        {
          Serial.println("NRF 2 TX_OK");
        }
        if(tx_fail)
        {
          Serial.println("NRF 2 TX_FAIL");
        }
        if(rx_ready)
        {
          Serial.printf("NRF 2 Received %u bytes with sig(%s) : \n",NRF_2.getPayloadSize(), NRF_2.testRPD()?"good":"bad");
          NRF_2.read(&p, sizeof(p));
          debugStruct(&p);
        }
      }

      //We check if the RX fifo the NRFs are full
      //If yes, it means we have a big problem since packets can be thrown away without notice
      //Could be due to missed IRQs (very bad)
      if(NRF_1.rxFifoFull())
      {
        Serial.println("NRF 1 FIFO FULL !!!!!!!!!!!!");
        NRF_1.flush_rx();
      }
      if(NRF_2.rxFifoFull())
      {
        Serial.println("NRF 2 FIFO FULL !!!!!!!!!!!!");
        NRF_2.flush_rx();
      }
      IRQFlag = false;
    }
    recvSlot = millis();
  }

  //Here we check if we are still connected
  if(millis() - timeStamp > WIFI_CHECK_TIMEOUT)
  {
    PCF.togglePin(PCF8574::P2);
    PCF.togglePin(PCF8574::P3);
    PCF.togglePin(PCF8574::P4);
    ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag);
    printf("Memory Info :\n - Free Mem > %u\n - Heap frag > %u\n - Max block > %u\n", freeMem, frag, biggestContigMemBlock);
    timeStamp = millis();
  }
}

void debugStruct(DataPacket *p)
{
  Serial.println("##############DATA##############");
  Serial.print("ID : ");
  Serial.println(p->id);

  Serial.print("HEADER : ");
  Serial.println(p->header);
  
  Serial.printf("BATT :  %.5f V\n", p->battery);
  
  Serial.print("LDR : ");
  Serial.println(p->ldr);

  Serial.print("BMP TEMP : ");
  Serial.print(p->bmpTemp);
  Serial.println(" *C");
  
  Serial.print("BMP PRESS : ");
  Serial.print(p->bmpPress);
  Serial.println(" Pa");

  Serial.print("HUM : ");
  Serial.print(p->humidity);
  Serial.println(" %");
  
  Serial.print("COM HUM : ");
  Serial.print(p->compensatedHumidity);
  Serial.println(" %");
  
  Serial.print("HTU TEMP : ");
  Serial.print(p->htuTemp);
  Serial.println(" *C");
}
