#include <SPI.h>
#include "RF24.h"
#include <EEPROM.h>

/*  Hardware connections from Arduino Nano to nRF24L01
 *   CE -> D9
 *   CSN -> D10
 *   SCK -> D13
 *   MOSI -> D11
 *   MISO -> D12
 *   IRQ -> N/C
 *   
 *   Set up both Nano + RF24 the same way. Make the transmitter radio 1 and the receiver radio 0
 *   On the receiver, setup a relay driven by D8
 */

//    ****************    User configuration ***************
//  set this as radio 0 or 1
bool radioNumber = 1;

/*  Hardware configuration */
RF24 radio(9,10);

//  Network topology
uint8_t addresses[][6] = {"1Node","2Node"};

//  Role management
typedef enum { role_xmit = 1, role_recv=0 } role_e;
const char* role_friendly_name[] = { "Receive", "Transmit" };
role_e role = role_recv; 

//    State management
typedef enum { state_free_serial = 0, state_obtain_code = 1, state_idle = 2 } state_t;
state_t currentState = state_free_serial;


//  EEPROM
#define ID_WAS_WRITTEN          0x1D
#define ID_ADDR                 1

#define TRANSMITTER_ID          0x74 // 't'
#define RECEIVER_ID             0x72 // 'r'

#define RELAY_PIN 8
#define RELAY_SET_CMD           0x666        // code the transmitter sends to toggle relay     
#define RELAY_SET_RESPONSE      0x333       //  success code returned by receiver
#define RELAY_NOT_SET_RESPONSE  0x111       //  no success code returned by receiver

//  "Button" - we're not using a physical button here
//  instead we are looking for a "B" on the serial input
//  to the transmitter
bool buttonPressed = false;

void setup() {
    Serial.begin(9600);
    Serial.println( F("RF24 remote relay test") );
    
    //  read the EEPROM first and see if we have a hardware ID
    if( EEPROM.read(0) == ID_WAS_WRITTEN ) {
        //  we can extract the ID and set a default role
        radioNumber = (EEPROM.read(ID_ADDR) == TRANSMITTER_ID)?1:0;
        role = (radioNumber == 1 )?role_xmit:role_recv;
        
        Serial.print( F("EEPROM Radio # ") );
        Serial.println(radioNumber);
    }
    else {
    	Serial.println( F("*** PRESS 'T' to begin transmitting to the other node") );
    }
    
    //  configure our radio
    radio.begin();
    radio.setPALevel(RF24_PA_MIN);
    
    if( radioNumber ) {
        radio.openWritingPipe(addresses[1]);
        radio.openReadingPipe(1,addresses[0]);
    }
    else {
        radio.openWritingPipe(addresses[0]);
        radio.openReadingPipe(1,addresses[1]);
    }
    if( role == role_recv ) {
        digitalWrite(RELAY_PIN, LOW);
        pinMode(RELAY_PIN, OUTPUT);
    }
    radio.startListening();
}

void loop() {
    if( role == role_xmit ) {
        uint16_t start_time = micros();
        radio.stopListening();
        if( buttonPressed ) {
            buttonPressed = false;
            uint16_t sendData = RELAY_SET_CMD;
            Serial.println(F("Now sending button press"));
            if (!radio.write( &sendData, sizeof(uint16_t) )){
                Serial.println(F("*** ERROR: failed to send"));
            }
            radio.startListening();
            unsigned long started_waiting_at = micros();               // Set up a timeout period, get the current microseconds
            boolean timeout = false;
            while( !radio.available() ) {
                if (micros() - started_waiting_at > 200000 ){            // If waited longer than 200ms, indicate timeout and exit while loop
                    timeout = true;
                    break;
                }
            }
            if( timeout ) {
                Serial.println(F("*** WARN: response timed out"));
            }
            else {
                uint16_t response;
                radio.read( &response, sizeof(uint16_t));
                
                Serial.print(F("Received response: 0x"));
                Serial.println(response, HEX);
            }
        }
    }   // xmit role
    //  if we are the receiver
    else {
        uint16_t code;
        if( radio.available() ) {
            while( radio.available() ) {
                radio.read( &code, sizeof(uint16_t) );
            }
            radio.stopListening();
            if(code == RELAY_SET_CMD ) {
                digitalWrite(RELAY_PIN, !digitalRead(RELAY_PIN));
                code = RELAY_SET_RESPONSE;
            }
            else {
                code = RELAY_NOT_SET_RESPONSE;
            }
            radio.write( &code, sizeof(uint16_t) );
            radio.startListening();
            Serial.print(F("Sent response "));
            Serial.println(code,HEX);
        }
    }   // rcv role
    //  each pass check if user wants to switch
    if( Serial.available() ) {
        if( currentState == state_obtain_code ) {
            uint8_t code = Serial.read();
            //  mark that we've written an ID
            EEPROM.write(0,0x1D);
            EEPROM.write(ID_ADDR, code);
            currentState == state_idle;
        }
        else {
            char c = toupper(Serial.read());
            if( c == 'T' && role == role_recv ) {
                Serial.println(F("*** CHANGING TO XMIT ROLE ***"));
                role = role_xmit;
            }
            else if( c== 'R' && role == role_xmit ) {
                Serial.println(F("*** CHANGING TO RCV ROLE ***"));
                role = role_recv;
                radio.startListening();
            }
            else if( c == 'B' && role == role_xmit ) {
                Serial.println(F("*** BUTTON PRESSED ***"));
                buttonPressed = true;
            }
            else if( c == 'S' ) {
                //  next pass through serial read loop will 
                //  obtain the code to use
                Serial.println("Type 't' for radio1/xmit or 'r' for radio0/receive");
                currentState = state_obtain_code;
            }
        }
        
    }
}