direction commands updated to Up down RPM value

Dependencies:   MAX7219pot MCP23S17 mbed

Fork of POT_V_1_0 by Marine Electricals

main.cpp

Committer:
viewdeep51
Date:
2018-07-25
Revision:
5:f7b7ee0702cc
Parent:
4:710464cb44fc

File content as of revision 5:f7b7ee0702cc:

/*Board configuration
>>>>>place JP5 to E5V side to use external  5V supply
>>>>>Remove jumper link SB13,SB14 usb uart debug disabled
>>>>>Place jumper link SB62,SB63...uart PA2,PA3 enabled by these jumper links
*/

#include "mbed.h"
#include "pot.h"
#include "max7219.h"
#include "MCP23S17.h"

using namespace std;

//------------------------------------
// Hyperterminal configuration
// 9600 bauds, 8-bit data, no parity
//------------------------------------
Serial UART(SERIAL_TX, SERIAL_RX); 

//timer instances creation//
Ticker pot_HB;              
Ticker HB_Ticker;
Ticker flash_Ticker;
Ticker R_A_Ticker;
Ticker CMD_Ticker;
//Ticker DIR_Ticker;      //function changed to Up-Down Rpm counts
Ticker HB_fail;
Ticker RTC_ticker;
Ticker led_refresh;
Timeout timeout;

//SPI instances creation//
SPI _SPI2(SPI2_MOSI, SPI2_MISO, SPI2_SCK);  //for displays max7219
SPI _SPI3(SPI3_MOSI, SPI3_MISO, SPI3_SCK);  //for keyscan  mcp23s17

//class object instances for key I/O expander and segment driver//
MCP23S17 mcp23s17_1(1,_SPI3, SPI3_CS1,SPI3_RST);  //class MCP23S17 object instance for mcp23s17 IC-1 key detection
MCP23S17 mcp23s17_2(2,_SPI3, SPI3_CS2,SPI3_RST);  //class MCP23S17 object instance for mcp23s17 IC-2 key detection
Max7219 max7219(SPI2_MOSI, SPI2_MISO, SPI2_SCK,SPI2_CS1);  //object instance for seven segment drivers
max7219_configuration_t DEVICE1, DEVICE2;
//interrupts definations
InterruptIn int_A(INT_A);       //8commands
InterruptIn int_B(INT_B);       //4  3commands+1ack
InterruptIn int_C(INT_C);       //4(3 controls(INC+,DEC-,TEST)+1 req) enbaled for all

//chip select definations for max7219 & mcp23s17 as digitalout pins//
DigitalOut cs_disp1(SPI2_CS1);   
DigitalOut cs_disp2(SPI2_CS2);   //max7219
DigitalOut cs_key1(SPI3_CS1);    
DigitalOut cs_key2(SPI3_CS2);    //mcp23s17

DigitalOut Drive_En(TX_EN);      //uart driver enable

//discrete outputs definations buzzer and hooter
DigitalOut buzzer(BUZ);
DigitalOut hooter(HTR); 


int err_ALL,sync_count,Hrs_1,Hrs_2, Mins_1, Mins_2, current_cmd, current_status,current_status_dir;
int addr, req,ack, send_ADDR, rcvd_ADDR, ach_S,ach_M,test_cnt,req_ADDR,ack_ADDR, err_ID, err_Status;
int rpm_data[4] = {0,0,0,0};               //digits 0-3
int last_entry[4] = {0,0,0,0};          //digits 0-3


uint8_t spi_error,no_pot_connected,num_bytes,ticker_count,ticker_count_fast,norm_count,set_bit;


//int dir, last_dir;
int checksum_send_byte,checksum_rcvd_byte;

char pot_Addr,addr_ID;

float HB_timer,duty_cycle,sync_time;

bool device_ERR,pot_Error, norm_op;
bool toggle,test,toggle_fast,display_flash;
bool key_CMD,key_CTRL,key_SET;
bool HB_rcvd, HB_ack_rcvd, REQ_rcvd, BROADCAST_rcvd, ACK_rcvd,time_RCVD;

bool DIR_cmd,DIR_cmd_ack,DIR_cmd_ack_send;
bool RPM_cmd,RPM_cmd_ack,RPM_cmd_ack_send;

bool HB_send, CMD_send, BROADCAST_send, REQ_send, ACK_send;

bool ENTER_ACK_key,DIR_key,REQ_key,ACK_key,disp_clear;

bool switch_CH, pot_MCR_err, pot_ER_err, ACH_SW_over;
bool data_fail,disp_test;

int device_addr = -1;

char valid;
char valid_sentence[8] = {'A', 'A','A','A','A','A','A','A'};
char rx_buf[RX_BUFFER_SIZE+1];
char tx_buf[TX_BUFFER_SIZE+1];
char RTC_buffer[5] = {1,2,3,4,0};

DigitalOut LED_CMD_ACK(_CMD_ACK);                              //ack command in slave devices only
DigitalOut LED_CTRL[2] = {_REQ,_ACK};                             //control transfer
//DigitalOut LED_DIR[2] = {_AHEAD,_ASTERN};                         //dir command
DigitalOut LED_ACH[8] =  {_WH,_BRDG,_MCR,_ER,_WP,_WS,_OPS,_ASP};  //active channels

//DigitalOut myled(PB_13);  //for internal test LED1

PwmOut mypwm(_PWM);

struct _POT
{
    int slave_ID;
    int err_cnt;
    bool err_status;
}; 
struct _POT* POT=NULL;  

enum AddrID
{
    pot_Master = 1,
    pot_Submaster,
    pot_Slave_MCR,
    pot_Slave_ER,
    pot_Listner_WP,
    pot_Listner_WS,
    pot_Listner_OPS,
    pot_Listner_ASP = 8
};

enum LED_STATUS
{ 
    OFF = 0,
    ON,
    _FLASH = 2
};          
LED_STATUS led_cmd_ack;   //only in slave devices
LED_STATUS led_ctrl[2];
//LED_STATUS led_dir[2];
LED_STATUS led_ach[8];

enum KEY
{
        ZERO = 0,
        ONE,
        TWO,
        THREE,
        FOUR,
        FIVE,
        SIX,
        SEVEN,
        EIGHT,
        NINE,
        FALSE0 = 10
};
enum KEY num_KEY; 

enum CTRL
{
    REQ_ = 1,
    ACK_ = 2,
    FALSE1= 3
    
};
enum CTRL ctrl_KEY; 

enum UP_DOWN
{
    UP_key=1,
    DOWN_key,
    FALSE2 = 3
};
enum UP_DOWN up_down_KEY;

/*
enum direction_KEY
{
    AHEAD = 1,
    ASTERN,
    FALSE2 = 3
};
enum direction_KEY dir_KEY;
*/

enum display_KEY
{
    clr_ENTRY = 0,           //CE
    clr_ALL,                 //C, all data turns to zero
    ENTER_ACK,               //enter in case of master and ACK in case of slaves/repeaters
    FALSE3 = 3
};
enum display_KEY disp_KEY;

enum SET
{
    INC=0,
    DEC,
    TEST,
    FALSE4 = 4
};
enum SET set_KEY; 

enum CHANNEL
{ 
    ch_A=1,
    ch_B,
    ch_C,
    ch_D=4
};          //keyscan channelA,B,C,Dof MCP23s17 1& 2 respectively
enum CHANNEL ch;


/**************************************************************************************
 EOT RTC display update FUNCTIONS
 **************************************************************************************/
void RTC_display(char* digits)
{
    for(uint8_t i = 0; i <= 3; i++)              //to write all the four digits
        {
            if(i == 1)
              RTC_buffer[i] = RTC_buffer[i]|0b10000000;   // to inc
            else
                __nop();
            max7219.write_digit(1, i+1, *(digits+i));   //device 2, digit 1-4 ,data 0x01
        }  
}

/**************************************************************************************
EOT RTC read FUNCTION
 **************************************************************************************/
void read_RTC()
{
    time_t seconds = time(NULL);
    strftime(RTC_buffer, 5, "%H%M\n", localtime(&seconds));
    wait_ms(5);
    RTC_display(RTC_buffer);
    wait_ms(1);
}

/**************************************************************************************
EOT CHECK LED STATUS FUNCTION
 **************************************************************************************/
void update_led()
{
    if(++ticker_count == 25)                //25x20 = 500ms
    {
        ticker_count = 0;
        toggle = !toggle;                   //normal cmd flash
    }
    else
        __nop();
        
    if(++ticker_count_fast == 10)          //20x10 =200
    {
        ticker_count_fast = 0;
        toggle_fast = !toggle_fast;        //error flashing
    }
    else
        __nop();
    
    if(test)
    {
        LED_CMD_ACK = ON;                  //in slave only
       
        for(uint8_t i=0;i<=1;i++)          //control
        {
                LED_CTRL[i] = ON;
        }
        
        /*
        for(uint8_t i=0;i<=1;i++)          // dir control
        {
                LED_DIR[i] = ON;                                  /////////////////working here
        }
        */
        
        for(uint8_t i=0;i<=7;i++)          //active channel
        {
                LED_ACH[i] = ON;                  //duty cycle 10%
        }
        max7219.set_display_test();                  //all on
        wait_us(1);
        disp_test = true;
    }
    else
    {
            if(disp_test)
                {
                    disp_test = false;
                    max7219.clear_display_test();                //normal operation
                }
            else
                __nop();
            
            if(led_cmd_ack == ON)          //status update LED_CMD_ACK
                LED_CMD_ACK = ON;
            else
                __nop();
                
            if(led_cmd_ack == OFF)
                LED_CMD_ACK = OFF;
            else
                __nop();
                
            if(led_cmd_ack == _FLASH)
            {
                if(toggle)
                    LED_CMD_ACK= ON;
                else
                    LED_CMD_ACK = OFF;
            }
            else
                __nop();
            
        for(uint8_t i=0;i<=1;i++)          //status update LED_CTRL 
        {
            if(led_ctrl[i] == ON)
                LED_CTRL[i] = ON;
            else
                __nop();
                
            if(led_ctrl[i] == OFF)
                LED_CTRL[i] = OFF;                  //duty cycle 10%
            else
                __nop();
                
            if(led_ctrl[i] == _FLASH)
            {
                 if(toggle)
                    LED_CTRL[i]= ON;
                else
                    LED_CTRL[i] = OFF;
            }
            else
                __nop();
        }
        /*
        for(uint8_t i=0;i<=1;i++)          //status update LED_CTRL 
        {
            if(led_dir[i] == ON)
                LED_DIR[i] = ON;
            else
                __nop();
                
            if(led_dir[i] == OFF)
                LED_DIR[i] = OFF;                  //duty cycle 10%
            else
                __nop();
                
            if(led_dir[i] == _FLASH)
            {
                 if(toggle)
                    LED_DIR[i]= ON;
                else
                    LED_DIR[i] = OFF;
            }
            else
                __nop();
        }
        */
        
        for(uint8_t i=0;i<=7;i++)          //status update LED_ACH
        {
            if(led_ach[i] == ON)
                LED_ACH[i] = ON;                  //duty cycle 10%
            else
                __nop();
                
            if(led_ach[i] == OFF)
                LED_ACH[i] = OFF;                  //duty cycle 10
            else
                __nop();
                
            if(led_ach[i] == _FLASH)
            {
                if(toggle_fast)
                    LED_ACH[i]= ON;
                else
                    LED_ACH[i] = OFF;
            }
            else
                __nop();
        }
    }
    if(display_flash)                               //if command executed or command received
    {
        if(toggle)
            max7219.enable_device(2);                //enable device 1 or normal mode
        else
            max7219.disable_device(2);              //shutdown mode
    }
    else if(!device_ERR)                           //specific device is not in error device 
       max7219.enable_device(2);               //enable device 1 or normal mode;
    else
        __nop();
}

/**************************************************************************************
  POT State Machines FUNCTIONS
 **************************************************************************************/
void HB_State_Machine()                       //run at an interval of 10ms,event driven start stop
{
    static int hb_state_cnt = 0;
    if(HB_send)
    {
        HB_send = false;
        hb_state_cnt = 0;
    }
    else
        __nop();
        
    if(HB_ack_rcvd)
    {
        HB_ack_rcvd = false;
        HB_Ticker.detach();
        POT[device_addr].err_cnt = 0;
        if(POT[device_addr].err_status)                //if that specific device is in error before but revert back to healthy state
        {
            POT[device_addr].err_status = false;
            err_Status = 0;
            err_ID = POT[device_addr].slave_ID;
            
            set_bit =  (uint8_t)1<<(err_ID-1);
            err_ALL = err_ALL^set_bit;                  //bitwise XOR
            
            BROADCAST_send = true;                      // to tell all device that error diminished
            switch_CH = true;                           // to tell specific device abot active channels and rpm data                                
            if(POT[device_addr].slave_ID == pot_Slave_MCR)
            {
                pot_MCR_err = false;
                if(pot_ER_err)
                {
                    ach_S = pot_Slave_MCR;
                    switch_CH = true;                         //automatic takeover if ER is already faulty
                }
                else
                    __nop();
            }
            else
                __nop();
                
            if(POT[device_addr].slave_ID == pot_Slave_ER)
            {
                pot_ER_err = false;
                if(pot_MCR_err)
                {
                    ach_S = pot_Slave_ER;
                    switch_CH = true;                         //automatic takeover if ER is already faulty
                }
                else
                    __nop();
            }
            else
                __nop();
        }
        else
        {
            POT[device_addr].err_status = false;
            hb_state_cnt = 0;
            err_Status = 0;
            
            if((POT[device_addr].slave_ID == ach_S)&&(led_ach[send_ADDR - 1] != ON))
                led_ach[send_ADDR - 1] = ON;
            else if((POT[device_addr].slave_ID != ach_S)&&(led_ach[send_ADDR - 1] != OFF))
                led_ach[send_ADDR - 1] = OFF; 
            else
                __nop();  
                
            if((addr_ID == ach_M)&&(led_ach[ach_M-1] != ON))
                led_ach[ach_M-1] = ON;
            else if((addr_ID != ach_M)&&(led_ach[ach_M-1] != OFF))
                led_ach[ach_M-1] = OFF;
            else
                __nop();
        }
    }
    else
    {
        if(++hb_state_cnt >= 5)             //if HB_response not arrived till 50ms
        {
            hb_state_cnt=0;
            HB_Ticker.detach();
            
            if((++POT[device_addr].err_cnt >= 5)&&(!POT[device_addr].err_status))  //specific device  in error state after 05 hb response missing
            {
                POT[device_addr].err_cnt = 0;
                led_ach[send_ADDR - 1] = _FLASH;
                err_ID = POT[device_addr].slave_ID; 
                
                set_bit =  (uint8_t)1<<(err_ID-1); 
                err_ALL = err_ALL|set_bit;                //bitwise OR
                
                POT[device_addr].err_status = true;
                err_Status = 1;
                BROADCAST_send = true;                //broadcast needed to be sent
                if(POT[device_addr].slave_ID == pot_Slave_MCR)  //means MCR fail
                {   
                    pot_MCR_err = true;
                    if(ach_S == pot_Slave_MCR)
                    {
                        if(!pot_ER_err)                   //automatic changeover if ER healthy
                        {
                            ach_S = pot_Slave_ER;
                            switch_CH = true;                  //transfer all slaves the new active slave
                        }
                        else
                        {
                            pot_Error = true;
                            ach_S = 9;
                            switch_CH = true;        //both start flashing for ER nd MCR in all slaves
                        }
                    }
                    else 
                        __nop();
                }
                else 
                    __nop();
                   
                if(POT[device_addr].slave_ID == pot_Slave_ER)
                {
                    pot_ER_err = true;
                    if(ach_S == pot_Slave_ER)
                    {
                        if(!pot_MCR_err)                   //automatic changeover if MCR healthy
                        {
                            ach_S = pot_Slave_MCR;
                            switch_CH = true;
                        }
                        else
                        {
                            pot_Error = true;
                            ach_S = 9;
                            switch_CH = true;    //both start flashing for all MCR nd ER
                        } 
                    }
                    else
                        __nop();
                }
                else
                    __nop();
            }
            else
                __nop();
        }
        else                        
            __nop();
    }    
}

void CMD_State_Machine()                //function run periodcally in all connected devices on bus
{
    static int cmd_state_cnt=0;
        
    if(RPM_cmd_ack)                        //PEOTX,CMD received in Master/SUB_Master, non Active Slave & Listner
    {
        for(uint8_t i = 0; i <=3; i++)
        {
            last_entry[i] = rpm_data[i];           //data saved to last entery
        }
        for(uint8_t i = 5; i <=8; i++)
        {
            max7219.write_digit(1,i,last_entry[i-5]);     //last entery displayed
        }
        if(addr_ID == ach_M)
        {
            mcp23s17_1.gpintena(0xff);      //ACK key interrupts disabled
            wait_us(1);
            mcp23s17_1.gpintenb(0xff);      //ACK key interrupts disabled
            wait_us(1);
            disp_clear = true;
        }
        else if(addr_ID == ach_S)
        {
            int_B.rise(NULL);             //all key functions/interrupt disabled for PORTA,B
            wait_us(1);
            mcp23s17_1.gpintenb(0x00);    //interrupts disabled
            wait_us(1); 
        }
        else
            __nop();
            
        led_cmd_ack = ON;
        current_status =  1;              //display steady
        cmd_state_cnt = 0;
        RPM_cmd = false; 
        RPM_cmd_ack = false;
        display_flash = false;
        buzzer = OFF;
        hooter = OFF;
        CMD_Ticker.detach();                        //cmd state machine ticker stopped in all devices
    }
    else if(++cmd_state_cnt >= 12)                       //Hotter ON after 3sec
    {
        if((hooter == OFF)&&(addr_ID == ach_S))              //if hooter still off
        {
            cmd_state_cnt = 0;
            hooter = ON;
       /* if(++cmd_state_cnt >= 96)                     //still command ack not recived for 24 sec
        {
            LED_CMD[cmd].OFF();                //command suspended
            buzzer = OFF;
            hooter = OFF;
            int_A.rise(NULL);
            int_A.rise(NULL);
            CMD_ticker.detach();           //timer stoped in all connected devices if ack not received in 3sec
        } */
        }
        else
            __nop();
    }
    else
        __nop();
}

void R_A_Statemachine()
{
    static int state_cnt = 0;
    if(++state_cnt >= 60)             //REQ not acknowledged for 30 sec, leds turn off, timer stopped
    {
        state_cnt = 0;
        REQ_rcvd = false;            //clear any pending request
        R_A_Ticker.detach();
    /*  
        if(addr_ID != ach_M)      // TODO         
        {
            int_B.rise(NULL);
            mcp23s17_1.gpintenb(0x00);      //portb of mcp1 keys disabled related to ACK
            wait_us(10);
        }
        else
            __nop();
     */
            
        led_ctrl[REQ_-1] = OFF;
        led_ctrl[ACK_-1] = OFF;
    }
    else
        __nop();
        
    if(ACK_rcvd)               //active channel switchover true only after acknowledge receive
        {
            ACK_rcvd = false;
            led_ctrl[REQ_-1] = OFF;
            if(addr_ID == pot_Master)
            {
                if(ack_ADDR == 3 || ack_ADDR == 4)
                {
                    if(ach_S == pot_Slave_MCR)
                        ach_S = pot_Slave_ER;
                    else if(ach_S == pot_Slave_ER)
                        ach_S = pot_Slave_MCR;
                }
                else
                    __nop();
                    
                if(ack_ADDR == 2 || ack_ADDR == 1)       
                {
                    if(ach_M == pot_Master)
                        ach_M = pot_Submaster;
                    else if(ach_M == pot_Submaster)
                        ach_M = pot_Master;
                }
                else
                    __nop();
                    
                switch_CH = true;
                state_cnt = 0;
                R_A_Ticker.detach();
            }
            else
            {
                state_cnt = 0;
                R_A_Ticker.detach();
            }
        }
        else
            __nop();
}

//Direction function changed to RPM Up-Down Counter so Dir_state_machine function not required
/*                
void DIR_state_machine(void)
{
    static int dir_state_cnt = 0;
    if(DIR_cmd_ack)
    {
        DIR_cmd =  false;
        DIR_cmd_ack = false;
        current_status_dir = 1;             //steady
        led_dir[dir-1] = ON;                //led direction acknowledged turn steady
        buzzer = OFF;                             //buzzer Off
        hooter = OFF;
        DIR_Ticker.detach();
    }
    else
    {
        if((++dir_state_cnt >= 12) && (hooter == OFF))          //direction command not ack for 03 sec
            {
                hooter = ON;
                dir_state_cnt = 0;
            }
        else
            __nop();
    } 
}
*/

void HB_fail_state_machine()
{
    static uint8_t fail_count = 0;
    if(HB_rcvd)
    {
        HB_rcvd = false;
        device_ERR = false;
    }
    else if(++fail_count >=3)             // if max 07 number of connected eots 7x3=21, it takes 150x21=3.150 sec to alarm
    {
        fail_count = 0;
        //led_dir[dir-1] = OFF;               //current command if any turned OFF
        led_cmd_ack = OFF;
        display_flash = false;
        device_ERR = true;
        max7219.disable_device(2);              //shutdown mode
        wait_ms(2);
        if(addr_ID != ach_M)
        {
            for(uint8_t i = 0; i <=7; i++)
            {
                led_ach[i] = OFF;             //all other active chnnels leds or device errors
            }
            led_ach[addr_ID - 1] = _FLASH;    //specific device is in error flshes only
        }
        HB_fail.detach();
    }
}

void NUMERIC_handler(uint8_t key_ID)                             //function sacnning all the 0-9 and up-down number keys
{  
    uint8_t num;
    num = key_ID;
    if(key_ID != FALSE0)
        {                                                                                  
        if(((ach_M == pot_Master) && (addr_ID == pot_Master)) || ((ach_M == pot_Submaster) && (addr_ID == pot_Submaster)))                               //for Master
            {
                if(disp_clear)                   //if last command has been acknowledged
                {
                    for (uint8_t j = 0; j < 4; j++)
                    {
                        rpm_data[j] = 0;          //clear rpm_data to enter new values
                    } 
                    disp_clear = false;
                } 
                else
                    __nop();
                    
                for (uint8_t j = 0; j < 4; j++)
                {
                        rpm_data[j] = rpm_data[j + 1];   //at every key press array shift to left
                }
                
                rpm_data[3] = num;   //new value be updated at place of digit 4
                
                for (uint8_t j = 1; j <= 4; j++)
                {
                    max7219.write_digit(2,j,rpm_data[j-1]);
                }                                     
            }
        else
            __nop();
        }
    else
        __nop();
}


void CTRL_handler(uint8_t CTRL_ID)                        //handle  control transfer 
{
    if(CTRL_ID != FALSE1)
    {
        if((addr_ID == pot_Slave_MCR) || (addr_ID == pot_Slave_ER))
        {
            if((CTRL_ID == REQ_)&&(!REQ_rcvd))  //if no request has been received
            {   
                req = REQ_;                     //req data updated 0 here, same int values as in enum
                REQ_key = true;
                led_ctrl[REQ_-1] = _FLASH;                //req led flash starts
                R_A_Ticker.attach(&R_A_Statemachine,0.5);
            }
            else
                __nop();
                
            if((CTRL_ID == ACK_)&& (REQ_rcvd))
            {
                REQ_rcvd = false;
                ack = ACK_;                     //ack data updated 2 here
                ACK_key = true;
                int_B.rise(NULL);               //interrupt disabled
                wait_us(10);
                mcp23s17_1.gpintenb(0x00);      //ACK key interrupts disabled
                wait_us(10);
                led_ctrl[ACK_-1] = OFF;                  //ack led turns off
                R_A_Ticker.detach();
            }
            else
                __nop();
        }
    }
    else 
        __nop(); //then do nothing
}

void UP_DOWN_handler(uint8_t key_ID)
{
    if(key_ID != FALSE2)
        {                                                                                  
        if(((ach_M == pot_Master) && (addr_ID == pot_Master)) || ((ach_M == pot_Submaster) && (addr_ID == pot_Submaster)))                               //for Master
            {    
                if(key_ID == UP_key)
                {
                    rpm_data[3]++;
                    if(rpm_data[3] == 10)
                        {
                            rpm_data[3] = 0;
                            rpm_data[2]++;
                            if(rpm_data[2] == 10)
                            {
                                rpm_data[2] = 0;
                                rpm_data[1]++;
                                if(rpm_data[1] == 10)
                                {
                                    rpm_data[1] = 0;
                                    rpm_data[0]++;
                                    if(rpm_data[0] == 10)
                                        rpm_data[0] = 9;
                                    else
                                        __nop();
                                }
                                else
                                    __nop();
                            }
                            else
                                __nop();
                        }
                    else
                        __nop();
                }
                else if(key_ID == DOWN_key)
                    {
                        rpm_data[3]--;
                    if(rpm_data[3] == 0)
                        {
                            rpm_data[3] = 0;
                            rpm_data[2]--;
                            if(rpm_data[2] == 0)
                            {
                                rpm_data[2] = 0;
                                rpm_data[1]--;
                                if(rpm_data[1] == 0)
                                {
                                    rpm_data[1] = 0;
                                    rpm_data[0]--;
                                    if(rpm_data[0] == 0)
                                        rpm_data[0] = 0;
                                    else
                                        __nop();
                                }
                                else
                                    __nop();
                            }
                            else
                                __nop();
                        }
                    else
                        __nop();
                        
                    }
                else
                    __nop();
                                
                for (uint8_t j = 1; j <= 4; j++)
                {
                    max7219.write_digit(2,j,rpm_data[j-1]);    //update data to display
                }                                     
            }
        else
            __nop();
        }
    else
        __nop();
    
}

//functon not required as DiR keys change to UP-down RPM
/*
void DIR_handler(uint8_t DIR_ID)                        //handle  control transfer 
{
    if(DIR_ID != FALSE2)
    {
        if(addr_ID == ach_M)
        { 
            current_status_dir = 2;                  //flash
            led_dir[dir-1] = OFF;
            dir = DIR_ID;                     
            led_dir[dir-1] = _FLASH;                //led direction AHEAD start flashing
            buzzer = ON;                             //buzzer ON
            DIR_cmd = true;                          // dir cmd needs to be sent
        }
        else
            __nop();
            
        if (addr_ID == ach_S)
        { 
            if((dir == DIR_ID)&&(DIR_cmd))         //direction command is received                  
            {
                current_status_dir = 1;
                led_dir[dir-1] = OFF;
                dir = DIR_ID;                     
                led_dir[dir-1] = ON;                    //led direction AHEAD start flashing
                buzzer = OFF;                             //buzzer OFF
                DIR_key = true;                   //dir ack will be sent in response to HB
            }
            else
                __nop();
        } 
        else
                __nop();
    }
    else 
        __nop();                                //then do nothing
}
*/

void DISP_handler(uint8_t DISP_ID)                        //handle  control transfer 
{
    uint8_t disp_key;
    disp_key = DISP_ID;
    if(DISP_ID != FALSE3)
    {
        switch(disp_key)
        {
            case clr_ENTRY:                                      //CE,elete only last/4th digit and shift others digits towards right/4th digit
                if(addr_ID == ach_M)
                {
                    for (uint8_t j = 3; j > 0; j--)
                    {
                        rpm_data[j] = rpm_data[j - 1];   //at every key press array shift to left
                    }
                    rpm_data[0] = 0;
                
                    for (uint8_t j = 1; j <= 4; j++)
                    {
                        max7219.write_digit(2,j,rpm_data[j-1]);
                    }
                }
                else
                {
                    __nop();
                }
            break;
            
            case clr_ALL: 
                if(addr_ID == ach_M) 
                {                                    //C      delete all digits in DS1 and show last entered value                                 
                    for (uint8_t j = 0; j <=3; j++)
                    {
                        rpm_data[j] = 0;   //at every key press array shift to left
                    }
                    for (uint8_t j = 1; j <= 4; j++)      // all digits turns to zero
                    {
                        max7219.write_digit(2,j,rpm_data[j-1]);
                    }
                }
                else
                {
                    __nop();
                }  
            break;
            
            case ENTER_ACK:                                    //work as Enter in master and CMD ACK in slave
                if((addr_ID == ach_S)&&(RPM_cmd))                    //RPM command ack needs to be sent
                {
                    RPM_cmd = false;    
                    display_flash = false; 
                    buzzer = OFF;
                    int_B.rise(NULL);
                    wait_us(10);
                    mcp23s17_1.gpintenb(0x00);      //ACK key interrupts disabled
                    wait_us(10);
                    ENTER_ACK_key = true;           //flashing display1 turned steady                              //RPM command acknowledged by active slave
                } 
                else  if (addr_ID == ach_M)                                 //in case of active masters
                {
                    int RPM_set = 0;                                              //disp1 start flashing, and data sent over UART for all digits 1-4
                    RPM_set = (rpm_data[0]*1000) + (rpm_data[1]*100) + (rpm_data[2]*10) + rpm_data[3];
                    if(RPM_set>Max_RPM)
                    {
                        for (uint8_t j = 0; j <=3; j++)
                        {
                            rpm_data[j] = 0;   //at every key press array shift to left
                        }
                        for (uint8_t j = 1; j <= 4; j++)      // all digits turns to zero
                        {
                            max7219.write_digit(2,j,rpm_data[j-1]);
                        }  
                    }
                    else
                    {
                        display_flash = true;             //display starts flashing
                        mcp23s17_1.gpintena(0x00);      //ACK key interrupts disabled
                        wait_us(10);
                        mcp23s17_1.gpintenb(0x00);      //ACK key interrupts disabled
                        wait_us(10);
                        current_status =  2;              //flash
                        buzzer = ON;
                        RPM_cmd = true;                   //command need to be send
                    }
                }
                else
                {
                    __nop();
                }
            break;
            
            default:
                __nop();
            break;
        }
    }
    else 
        __nop(); //then do nothing
}

/******************************************************************
function to set brightness of leds and seven segment
******************************************************************/
void SET_handler(uint8_t Set_ID)
{
    if(Set_ID != FALSE2)
    {
        switch(Set_ID)
        {
            case INC:
                duty_cycle = mypwm.read();     //as duty cycle decreases creases brightnes increases
                if( duty_cycle > 0.02f)
                {
                    duty_cycle = duty_cycle-0.01f;
                    mypwm.write(duty_cycle);
                }
                else
                {
                    duty_cycle = 0.01f;
                }
                if(DEVICE1.intensity  < 14)
                {
                    DEVICE1.intensity ++;                               //intensity modified
                    DEVICE2.intensity ++;
                    max7219.set_intensity(DEVICE1);       //updating brightness value of DEVICE1
                    max7219.set_intensity(DEVICE2);       //updating brightness value of DEVICE2
                }
                else
                {
                    DEVICE1.intensity =15;
                    DEVICE2.intensity =15;
                    max7219.set_intensity(DEVICE1);       //updating brightness value of DEVICE1
                    max7219.set_intensity(DEVICE2);       //updating brightness value of DEVICE2
                }
            break;
        
            case DEC:
                duty_cycle = mypwm.read();
                if( duty_cycle < 0.98f)          //as duty cycle increases brightnes decreases
                {
                    duty_cycle = duty_cycle+0.01f;
                    mypwm.write(duty_cycle);
                }
                else
                {
                    duty_cycle = 0.99f;
                }
                if(DEVICE1.intensity  > 2)
                {
                    DEVICE1.intensity --;                              //intensity modified
                    DEVICE2.intensity --;
                    max7219.set_intensity(DEVICE1);       //updating brightness value of DEVICE1
                    max7219.set_intensity(DEVICE2);       //updating brightness value of DEVICE2
                }
                else
                {
                    DEVICE1.intensity =1;
                    DEVICE2.intensity =1;                                //minimum brightnes
                    max7219.set_intensity(DEVICE1);       //updating brightness value of DEVICE1
                    max7219.set_intensity(DEVICE2);       //updating brightness value of DEVICE2
                }
            break;
        
            case TEST:
                test_cnt++;
                if(test_cnt <= 1)
                test = true;
                else if(test_cnt >= 2)
                {
                    test = false;
                    test_cnt = 0;
                }
                else
                    __nop();
            break;
            
            default:
                __nop();
            break;
        }
    }
    else
        __nop();
}
/**************************************************************************************
 POT SPI Data Read KEY FUNCTIONS
 **************************************************************************************/
unsigned char read_KEY_SPI(int CH)
{
    char _key;
    switch(CH)
    {
        case 1:
            wait_ms(1);
            _key = mcp23s17_1.intcapa();                   // read(INTCAPA_ADDR);
            wait_ms(1);
        break;
        
        case 2:
            wait_ms(1);
            _key=mcp23s17_1.intcapb();
            wait_ms(1);
        break;
        
        case 3:
            wait_ms(1);
            _key=mcp23s17_2.intcapa();
            wait_ms(1);
        break;
        case 4:
        wait_ms(1);
        _key = mcp23s17_2.gpiob(); //address read PORTB of chip2
        wait_ms(1);
        break;
        default:
        _key = 0;
        break;
    }
    return(_key);
}

void read_KEY_A()                      //function check status of PORTA of MCP23S17 (1) chip
{
    char key_status = 0;
    num_KEY = FALSE0;
    key_status = read_KEY_SPI(ch_A);    //to read portA of mcp23s17(1)
    if(key_status != 0xff)                    //means some key must have been pressed
    {
        switch(key_status)
        {
            case 0xfe:               //0xfe  GPA0
                num_KEY = ZERO;
            break;
            case 0xfd:               //0xfd  GPA1
                num_KEY = ONE;
            break;
            case 0xfb:               //0xfb  GPA2
                num_KEY = TWO;
            break;
            case 0xf7:               //0xf7  GPA3
                num_KEY = THREE;
            break;
            case 0xef:               //0xef  GPA4
                num_KEY = FOUR;
            break;
            case 0xdf:               //0xdf  GPA5
                num_KEY = FIVE;
            break;
            case 0xbf:               //0xbf  GPA6
                num_KEY = SIX;
            break;
            case 0x7f:               //0x7f  GPA7
                num_KEY = SEVEN;
            break;
            default:
                num_KEY = FALSE0;
            break;
        }
        NUMERIC_handler(num_KEY);
    }
}

void read_KEY_B()
{
    char key_status = 0;
    num_KEY = FALSE0;
    ctrl_KEY = FALSE1;
    //dir_KEY = FALSE2;  /not required
    up_down_KEY = FALSE2;
    disp_KEY = FALSE3;
    
    key_status = read_KEY_SPI(ch_B);   //to read portBof mcp23s17(1)
    if(key_status != 0xff)
    {   
        switch(key_status)
        {
            case 0xfe:               //0xfe  GPA0
                num_KEY = EIGHT;
                NUMERIC_handler(num_KEY);
            break;
            case 0xfd:               //0xfd  GPA1
                num_KEY = NINE;
                NUMERIC_handler(num_KEY);
            break;
            case 0xfb:                      //0xfb  GPA2
                up_down_KEY = UP_key;             //dir_KEY = AHEAD; /not required now
                UP_DOWN_handler(up_down_KEY);   //DIR_handler(dir_KEY); /not required now                                            
            break;
            case 0xf7:                          //0xf7  GPA3
                up_down_KEY = DOWN_key;             //dir_KEY = ASTERN;  /not required now
                UP_DOWN_handler(up_down_KEY);     //DIR_handler(dir_KEY); /not required now
            break;
            case 0xef:                     //0xef  GPA4
                ctrl_KEY = ACK_;
                CTRL_handler(ctrl_KEY);
            break;
            case 0xdf:                     //0xdf  GPA5
                disp_KEY = clr_ENTRY;               //CE
                DISP_handler(disp_KEY);
            break;
            case 0xbf:                     //0xbf  GPA6
                disp_KEY = clr_ALL;             //C
                DISP_handler(disp_KEY);
            break;
            case 0x7f:                    //0x7f  GPA7
                disp_KEY = ENTER_ACK;
                DISP_handler(disp_KEY);
            break;
            default:
                num_KEY = FALSE0;
                ctrl_KEY = FALSE1;
                //dir_KEY = FALSE2;
                up_down_KEY = FALSE2;
                disp_KEY = FALSE3;
            break;
        }
    }
}
void read_KEY_C()
{
    char key_status = 0;
    ctrl_KEY = FALSE1;
    set_KEY = FALSE4;
    key_status = read_KEY_SPI(ch_C);   //to read portA of mcp23s17(2)
    if(key_status != 0xff)
    {
        if(key_status==0xfe)
        {
            ctrl_KEY  = REQ_;                    //control transfer REQ button pressed
            CTRL_handler(ctrl_KEY);
        }
        else
        {
            if(key_status==0xfd)
                set_KEY = INC;                     //setting key INC
            else if(key_status==0xfb)
                set_KEY = DEC;                      //setting key DEC
            else if(key_status==0xf7)
                set_KEY = TEST;                       //setting key TEST
            else 
            {
                set_KEY = FALSE4;                       //setting key TEST
                ctrl_KEY = FALSE1;
            }
            
            SET_handler(set_KEY);
        }
    }
}


/**************************************************************************************
POT Serial Data Out FUNCTIONS
 **************************************************************************************/
uint8_t  get_checksum(const char *sentence)
{
    const char *n = sentence + 1; // Plus one, skip '$'
    uint8_t chk = 0;

    /* While current char isn't '*' or sentence ending (newline) */
    while (('*' != *n) &&(NMEA_END_CHAR_1 != *n))
    {
        if ('\0' == *n || n - sentence > NMEA_MAX_LENGTH) 
        {
            /* Sentence too long or short */
            chk = 0;
        }
        chk ^= (uint8_t) *n;
        n++;
    }
    return chk;
}

void resp_HB(uint8_t seq)      //presentees(submaster,slaves,listeners will respond back to the HB along with REQ and CMD ack status
{
   switch(seq)
    {
        case 0:
            sprintf(tx_buf,"$PPOTA,%u,%c*", addr_ID,valid);
            checksum_send_byte = get_checksum(tx_buf);
            sprintf(tx_buf,"$PPOTA,%u,%c*%u",addr_ID,valid,checksum_send_byte);
        break;
        case 1:
            sprintf(tx_buf,"$PPOTR,%u,%u,%c*", addr_ID, REQ_,'A');
            checksum_send_byte = get_checksum(tx_buf);
            sprintf(tx_buf,"$PPOTR,%u,%u,%c*%u",addr_ID,REQ_,'A',checksum_send_byte);
        break;
        case 2:
            sprintf(tx_buf,"$PPOTK,%u,%u,%c*", addr_ID, ACK_,'A');
            checksum_send_byte = get_checksum(tx_buf);
            sprintf(tx_buf,"$PPOTK,%u,%u,%c*%u",addr_ID, ACK_,'A',checksum_send_byte);
        break;
        default:
            __nop(); //sprintf(tx_buf,"$PPOTA,%u,%c", addr_ID, valid);
        break;
    }                
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}

void send_ack_CMD(uint8_t cmd_t)
{
    sprintf(tx_buf,"$PPOTX,%u,%c*", addr_ID,'A');
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTX,%u,%c*%u",addr_ID,'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}

void send_RPM(uint8_t _adr)
{
    sprintf(tx_buf,"$PPOTC,%u,%u,%u,%u,%u,%c*", _adr, rpm_data[0],rpm_data[1],rpm_data[2],rpm_data[3],'A');
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTC,%u,%u,%u,%u,%u,%c*%u",_adr, rpm_data[0],rpm_data[1],rpm_data[2],rpm_data[3],'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}

/*
void send_DIR(uint8_t _adr)             //addr 1-8 of leds,error=1 or 0(no error)
{
    sprintf(tx_buf,"$PPOTD,%u,%u,%c*",_adr,dir,'A');             // dir is 1 for ahead, 2 for astern
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTD,%u,%u,%c*%u",_adr,dir,'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}
*/

void send_HB(uint8_t _adr)                   //master will send the heartbeat to all presentees at periodic intervals
{      
    sprintf(tx_buf,"$PPOTH,%u,%c*", _adr,'A');
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTH,%u,%c*%u",_adr,'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}

void send_BROADCAST()                        //addr 1-8 of leds,error=1 or 0(no error)
{
    sprintf(tx_buf,"$PPOTB,%u,%u,%c*",err_ALL,err_Status,'A');
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTB,%u,%u,%c*%u",err_ALL,err_Status,'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
    BROADCAST_send=false;
}
 /** Function will send ac-M, ac-S, rpm-data(04digits), cmd_state)**/
void send_ACH()                 
{
    sprintf(tx_buf,"$PPOTS,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%c*", ach_M, ach_S, rpm_data[0],rpm_data[1],rpm_data[2],rpm_data[3],last_entry[0],last_entry[1],last_entry[2],last_entry[3],current_status,'A');
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTS,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%c,*%u", ach_M, ach_S, rpm_data[0],rpm_data[1],rpm_data[2],rpm_data[3],last_entry[0],last_entry[1],last_entry[2],last_entry[3],current_status,'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}

void ask_TIME()
{
    Hrs_1 = Hrs_2 = Mins_1 = Mins_2 = 0;
    sprintf(tx_buf,"$PPOTQ,%u,%u,%u,%u,%c*", Hrs_1,Hrs_2, Mins_1, Mins_2,'A');    //querry to CPU for time
    checksum_send_byte = get_checksum(tx_buf);                
    sprintf(tx_buf,"$PPOTQ,%u,%u,%u,%u,%c*%u", Hrs_1,Hrs_2, Mins_1, Mins_2,'A',checksum_send_byte);
    Drive_En = 1;              
    UART.printf("%s\r\n",tx_buf);
    wait_ms(2);
    Drive_En = 0;
}

/**************************************************************************************
 EOT Serial Data In FUNCTIONS
 **************************************************************************************/
void rcvd_HB()      //all devices check for HB  along with req response for all presentees
{
    if(rcvd_ADDR == addr_ID)             //addr_ID  self address received
    {   
        HB_rcvd = true;       
        if(addr_ID > pot_Slave_ER)
        {                                //all listners replied with
            resp_HB(0);                  //normal HB response
        }
        else                             //in case of active slave
        {
            if(ENTER_ACK_key)               //RPM ACK key is detected at active slave only
            {
                send_ack_CMD(1);  
                RPM_cmd_ack = true;  
                ENTER_ACK_key = false;
            }
            /*
            else if(DIR_key)                //dir ack key detected at active slave
            {
                DIR_key = false;
                DIR_cmd_ack_send = true;
                send_ack_CMD(2);
            }
            */
            else if(REQ_key)
            {
                REQ_key = false;
                resp_HB(1);                 //req and ack status data in req=1 and ack=2 if true
            }
            else if(ACK_key)
            {
                ACK_key = false;
                resp_HB(2);                 //req and ack status data in req=1 and ack=2 if true
            }
            else
                resp_HB(0);                 //req and ack status data in req=1 and ack=2 if true
        }
        HB_fail.attach(&HB_fail_state_machine,1);
    }
    else
      __nop(); 
}

void resp_rcvd_HB()                  //master and active/passive slaves check HB response along with req/ack commands
{ 
    static uint8_t checksum_flt[8] = {0,0,0,0,0,0,0,0}; 
    static bool checksum_err[8] = {false,false,false,false,false,false,false,false};
    if(addr_ID == pot_Master)                                       //only master will be able to monitor status of all eots connected
    { 
        if(send_ADDR == rcvd_ADDR)              //valid broadcast not received  
        {
            if(valid_sentence[rcvd_ADDR] == 'A')
            {
                HB_ack_rcvd = true;
                checksum_err[rcvd_ADDR] = false;
                checksum_flt[rcvd_ADDR] = 0;
            } 
            else if((valid_sentence[rcvd_ADDR] == 'V')&&(++checksum_flt[rcvd_ADDR]<=3))
            {
                HB_ack_rcvd = true;
                BROADCAST_send = true;    //again broadcast
                switch_CH = true;         //send active channels again
            }
            else if((valid_sentence[rcvd_ADDR] == 'V')&&(checksum_flt[rcvd_ADDR]>3)&&(!checksum_err[rcvd_ADDR]))
            {
               HB_ack_rcvd = false;
               checksum_err[rcvd_ADDR] = true;
            } 
            else
                __nop();
            
        }
        else
            HB_ack_rcvd = false;     
    }
    else
        __nop();
}

void rcvd_ACH()
{
    led_ach[ach_M-1] = ON;
    if(current_status == 2)
    {
        led_cmd_ack = _FLASH;
        max7219.enable_device(2);
        display_flash = true; 
    }
    else if(current_status == 1)
    {
        led_cmd_ack = ON;
        max7219.enable_device(2);
        display_flash = false;
    }
    else
    {
        led_cmd_ack = OFF;
        max7219.enable_device(2);
    }
    /*
    if(current_status_dir == 2)
    {
        led_dir[dir-1] = _FLASH;
    }
    else if(current_status_dir == 1)
    {
        led_dir[dir-1] = ON;
    }
    else
    {
        led_dir[dir-1] = OFF;
    }
    */
    for(uint8_t i = 1; i <= 4 ; i++)    //current cmd information
    {
        max7219.write_digit(2,i,rpm_data[i-1]);     //device 2 digits 1-4                  //data 0 means left most digit will be blank bcoz no decode mode is used
    }
    
    for(uint8_t i = 5; i <=8; i++)     //last command executed
    {
        max7219.write_digit(1,i,last_entry[i-5]);     //last cmd 0000
    }
        
    if(ach_S != 9)
    {
        if(ach_S == pot_Slave_MCR)
        {
            led_ach[pot_Slave_MCR -1] = ON;
            if(led_ach[pot_Slave_ER -1] == ON)
                led_ach[pot_Slave_ER -1] = OFF;    //ER off
            else
                __nop();
        }
        else if(ach_S == pot_Slave_ER)
        {
            led_ach[pot_Slave_ER -1] = ON;    //ER ON 
            if(led_ach[pot_Slave_MCR -1] == ON)
                led_ach[pot_Slave_MCR -1] = OFF;    //ER off
            else
                __nop();   
        }
        else
            __nop();
            
        if((RPM_cmd || DIR_cmd)&& (addr_ID == ach_S))      // cmd ack not received before its failure control shifted to another slave
        {                                                //all key functions enabled for PORTB in active slave only        
            int_B.rise(&read_KEY_B);
            wait_us(10);
            mcp23s17_1.gpintenb(0xff);    //ff
            wait_us(10);
        }
        else
            __nop();
    }
    else
    {
        led_ach[pot_Slave_MCR-1] = _FLASH;
        led_ach[pot_Slave_ER-1]  = _FLASH;
    }  
}

void rcvd_rpm_CMD()                         //all devices check for received command
{
    led_cmd_ack = _FLASH;                   //ack cmd led start flashing
    for(uint8_t i = 0; i <= 3 ; i++)
    {
        max7219.write_digit(2,i+1,rpm_data[i]);     //device 2 digits 1-4                  //data 0 means left most digit will be blank bcoz no decode mode is used
    }  
    wait_us(10);                                      
    buzzer = ON;
    display_flash = true;                     //display start flashing                                                         //buzzer on                                                     
    
    if(ach_S == addr_ID)
    {   
        int_B.rise(&read_KEY_B);
        wait_us(10);     
        mcp23s17_1.gpintenb(0xff);    //ff
        wait_us(10);                                              //portc is always enbaled in all cases and PORTA is enabled only for active master
    }
    else 
    {
        __nop();
    }
    CMD_Ticker.attach(&CMD_State_Machine,0.25);                                 //state machine timer started in all connected devices except  master
}
/*
void rcvd_dir_CMD()         //all devices check for received command
{
    DIR_cmd =  true;
    led_dir[last_dir-1] = OFF;
    led_dir[dir-1] = _FLASH;                                                    //led flash started
    buzzer = ON;                                                                 //buzzer on
    last_dir = dir;                                                        //HB-cmd ack pending with "A"  
    if(ach_S == addr_ID)
    {       
        int_B.rise(&read_KEY_B);                      //enable portb interupts
        mcp23s17_1.gpintenb(0xff);    //ff
        wait_us(10); 
    }
    DIR_Ticker.attach(&DIR_state_machine,0.25);
}
*/

void rcvd_BROADCAST()
{
    for(uint8_t i = 0 ; i <= 7 ; i++)
    {
        if(((uint8_t)1<<i) & (uint8_t)err_ALL)
        {
            led_ach[i] = _FLASH;
        }
        else if(led_ach[i] != ON)
        {
            led_ach[i]= OFF;
        }
        else
            __nop();
    } 
}

void rcvd_TIME()
{
    uint8_t Hrs,Mins;
    struct tm t;
    Hrs = 10*Hrs_1+Hrs_2;
    Mins = 10*Mins_1+Mins_2;
    t.tm_year = 2018-1900; 
    t.tm_mon = 5;
    t.tm_mday = 16;
    t.tm_hour = Hrs;
    t.tm_min = Mins;
    t.tm_sec = 51;
    set_time((time_t)mktime(&t));
    wait_ms(1);
    read_RTC();
    wait_ms(1);
    time_RCVD = true;
}

void at_timeout()
{
    timeout.detach();
    data_fail = true;
}

void read_SERIAL()
{  
    char rcvd_char;
    char rcvd_valid;
    //int cmd_t;
    uint8_t checksum_calculated;
    timeout.attach(&at_timeout,0.100);    //100ms timeout
    data_fail = false;
    while(UART.readable() && (num_bytes < RX_BUFFER_SIZE)&&(!data_fail))
    {
        rcvd_char = UART.getc();
        if(rcvd_char == '$')             //first character
            num_bytes = 0;
        else
            __nop();
            
        rx_buf[num_bytes] = rcvd_char;
        num_bytes++;
        if(rcvd_char == '\n')             //last character received
        {
            timeout.detach();
            strtok(rx_buf,",");
            if(strcmp(rx_buf,"$PPOTH") == 0)                      //* present submaster,Slaves or lsitners receive HB *// 
            {
                sscanf(strtok (NULL,","),"%u", &rcvd_ADDR);        //address of connected device
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTH,%u,%c*",rcvd_ADDR,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                {
                    rcvd_HB();               //valid sentence received   
                }                   
                else
                    __nop();              //valid sentence receivedrcvd_HB();                                   //set flag when Heartbeat received
            }
            else if(strcmp(rx_buf,"$PPOTA") == 0)                  //* Master,submaster and Slaves receive  heartbeat resp *//
            {
                sscanf(strtok (NULL,","),"%u", &rcvd_ADDR);
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTA,%u,%c*",rcvd_ADDR,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                {
                    valid_sentence[rcvd_ADDR] = rcvd_valid;
                    resp_rcvd_HB();               //valid sentence received  
                }                    
                else
                    __nop();
            }
            else if(strcmp(rx_buf,"$PPOTB") == 0)                 //* submaster,Slaves or listener receive broadcast *//
            {
                sscanf(strtok (NULL,","), "%u", &err_ALL);       //device in error with addr_ID =  err_ID
                sscanf(strtok (NULL,","), "%u",&err_Status);
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTB,%u,%u,%c*",err_ALL,err_Status,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                {               //valid sentence received  
                    valid = 'A';
                    rcvd_BROADCAST();   
                }                 
                else
                    valid = 'V';              //not valid 
            }
            else if(strcmp(rx_buf,"$PPOTC") == 0)                 //* submaster,Slaves or listener receive CMD *//
            {
                sscanf(strtok (NULL,","), "%u", &rcvd_ADDR);       //cmd extracted
                sscanf(strtok (NULL,","), "%u", &rpm_data[0]);     //rpm data digit 1 and soon
                sscanf(strtok (NULL,","), "%u", &rpm_data[1]);       
                sscanf(strtok (NULL,","), "%u", &rpm_data[2]);
                sscanf(strtok (NULL,","), "%u",&rpm_data[3]);
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTC,%u,%u,%u,%u,%u,%c*",rcvd_ADDR,rpm_data[0],rpm_data[1],rpm_data[2],rpm_data[3],rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                   {
                    valid = 'A';             
                    RPM_cmd = true;                                    // in all devices who receive RPM command
                    rcvd_rpm_CMD();
                   }
                else
                    valid = 'V';
            }
            /*
            else if(strcmp(rx_buf,"$PPOTD") == 0)                      //present submaster,Slaves or lsitners receive HB 
            {
                sscanf(strtok (NULL,","), "%u", &rcvd_ADDR);        //address of connected device
                sscanf(strtok (NULL,","), "%u",&dir);        // 1 for ahead and 2 for astern
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTD,%u,%u,%c*",rcvd_ADDR,dir,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                {
                    valid = 'A';
                    DIR_cmd = true;
                    rcvd_dir_CMD(); 
                }
                else
                    valid = 'V';                                   //set flag when Heartbeat received
            }
            */
            else if(strcmp(rx_buf,"$PPOTX") == 0)                //* Master,submaster,Slave or listners receive CMD acknowledge *//
            {
                sscanf(strtok (NULL,","), "%u", &rcvd_ADDR);       //address extracted
                //sscanf(strtok (NULL,","), "%u",&cmd_t);
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTX,%u,%c*",rcvd_ADDR,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                    {
                        valid = 'A';
                        RPM_cmd_ack = true;       
                    }                      
                else
                     valid = 'V';               // not valid sentence received;       
            }
            else if(strcmp(rx_buf,"$PPOTS") == 0)                 //*active channel status recieved *//
            {
                sscanf(strtok (NULL,","), "%u", &ach_M);       //cmd extracted
                sscanf(strtok (NULL,","), "%u", &ach_S);
                sscanf(strtok (NULL,","), "%u", &rpm_data[0]);
                sscanf(strtok (NULL,","), "%u", &rpm_data[1]);
                sscanf(strtok (NULL,","), "%u", &rpm_data[2]);
                sscanf(strtok (NULL,","), "%u", &rpm_data[3]);
                sscanf(strtok (NULL,","), "%u", &last_entry[0]);
                sscanf(strtok (NULL,","), "%u", &last_entry[1]);
                sscanf(strtok (NULL,","), "%u", &last_entry[2]);
                sscanf(strtok (NULL,","), "%u", &last_entry[3]);
                //sscanf(strtok (NULL,","), "%u", &dir);
                sscanf(strtok (NULL,","), "%u", &current_status);
                //sscanf(strtok (NULL,","),"%u", &current_status_dir);
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTS,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%c*",ach_M,ach_S,rpm_data[0],rpm_data[1],rpm_data[2],rpm_data[3],last_entry[0],last_entry[1],last_entry[2],last_entry[3],current_status,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                    {
                        valid = 'A';              //valid sentence received
                        rcvd_ACH(); 
                    }                      
                else
                    valid = 'V';  
            }
            else if(strcmp(rx_buf,"$PPOTR") == 0)               //* control transfer request arrived*//
            {
                sscanf(strtok (NULL,","),"%u", &rcvd_ADDR);
                sscanf(strtok (NULL,","),"%u", &req);    //e.g $R req or $K ack 
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                if(addr_ID <= pot_Slave_ER)
                {
                    sprintf(rx_buf,"$PPOTR,%u,%u,%c*",rcvd_ADDR,req,rcvd_valid);
                    checksum_calculated = get_checksum(rx_buf);
                    if(checksum_calculated == checksum_rcvd_byte)
                        { 
                            valid = 'A';                  
                            REQ_rcvd = true;
                            req_ADDR = rcvd_ADDR;
                            if(((rcvd_ADDR == pot_Master) && (addr_ID == pot_Submaster)) || ((rcvd_ADDR == pot_Submaster) && (addr_ID == pot_Master)))
                            {
                                int_B.rise(&read_KEY_B);        //to enable ACK key interrupt
                                wait_us(10);
                                mcp23s17_1.gpintenb(0xff);      // portb of mcp1 ACK key enabled
                                wait_us(10);
                                led_ctrl[ACK_-1] = _FLASH;        //ack led blink
                            }
                            else 
                                __nop();
                                
                            if(((rcvd_ADDR == pot_Slave_MCR) && (addr_ID == pot_Slave_ER)) || ((rcvd_ADDR == pot_Slave_ER) && (addr_ID == pot_Slave_MCR)))
                            {
                                int_B.rise(&read_KEY_B);        //to enable ACK key interrupt
                                wait_us(10);
                                mcp23s17_1.gpintenb(0xff);      //portb of mcp1 ACK key enabled
                                wait_us(10);
                                led_ctrl[ACK_-1] = _FLASH;       //ack led blink
                            }
                            else
                                __nop();
                    
                            R_A_Ticker.attach(&R_A_Statemachine,0.5);
                        }
                    else
                        valid = 'V'; 
                }
                else
                    __nop();
            }
            else if(strcmp(rx_buf,"$PPOTK") == 0)               //* Master,submaster,Slave or listners REQ acknowledge *//
            {
                sscanf(strtok (NULL,","), "%u", &rcvd_ADDR);
                sscanf(strtok (NULL,","),"%u", &ack);    //e.g $R req or $K ack
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid); 
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                
                if(addr_ID <= pot_Slave_ER)
                {
                    sprintf(rx_buf,"$PPOTK,%u,%u,%c*",rcvd_ADDR,ack,rcvd_valid);
                    checksum_calculated = get_checksum(rx_buf);
                    if(checksum_calculated == checksum_rcvd_byte)
                        { 
                            valid = 'A';
                            ack_ADDR = rcvd_ADDR;
                            ACK_rcvd = true;
                            //ACH_SW_over = true;
                        
                            if(((rcvd_ADDR == pot_Master) && (addr_ID == pot_Submaster)) || ((rcvd_ADDR == pot_Submaster) && (addr_ID == pot_Master)))
                            {
                                 led_ctrl[ACK_-1] = OFF;
                                 //R_A_Ticker.detach();
                            }
                            else if(((rcvd_ADDR == pot_Slave_ER) && (addr_ID == pot_Slave_MCR)) || ((rcvd_ADDR == pot_Slave_MCR) && (addr_ID == pot_Slave_ER)))
                            {
                                led_ctrl[ACK_-1] = OFF;
                                //R_A_Ticker.detach();
                            }
                            else
                                __nop();
                        }
                    else
                        valid = 'V';
                }                    
                else
                    __nop();
            }
            else if(strcmp(rx_buf,"$PPOTT") == 0)                 //* submaster,Slaves or listener receive CMD *//
            {
                sscanf(strtok (NULL,","), "%u", &Hrs_1);       //cmd extracted
                sscanf(strtok (NULL,","), "%u", &Hrs_2); 
                sscanf(strtok (NULL,","), "%u", &Mins_1);
                sscanf(strtok (NULL,","),"%u", &Mins_2);
                sscanf(strtok (NULL,"*"),"%c", &rcvd_valid);        //sentence validity char
                sscanf(strtok (NULL,","),"%u", &checksum_rcvd_byte);
                sprintf(rx_buf,"$PPOTT,%u,%u,%u,%u,%c*",Hrs_1,Hrs_2,Mins_1,Mins_2,rcvd_valid);
                checksum_calculated = get_checksum(rx_buf);
                if(checksum_calculated == checksum_rcvd_byte)
                {
                    valid = 'A';
                    rcvd_TIME();
                }                     
                else
                    valid = 'V';
            }
            else
                __nop();
        }
        else
            __nop();
    }
}

/********END of FUNCTION****************/

/**************************************************************************************
EOT Handler FUNCTIONS
 **************************************************************************************/
void HB_handler()                     //function run periodically within active master only
{   
    if(++sync_count >= sync_time)
    {
        time_RCVD = false;
        norm_op = false;
        sync_count = 0;
        ask_TIME();                         //master will send querry for GPS time
    }
    else if(time_RCVD || norm_op)                     //as all receive time update normal operation starts
    {
        if(ach_M == pot_Master)
        {   
            device_addr++;
            if(device_addr == no_pot_connected)    //max no-pot-conneted = 7
                device_addr = 0;
        }
        else
            __nop();
        /*else if(ach_M == eot_Submaster)
        {   
            if(device_addr==2)
            {
                device_addr=3;
            }
            else
            {
                device_addr++;
                if(device_addr>NO_MAX_EOT)
                device_addr=1;
            }
        }
                        */
        
        send_ADDR = POT[device_addr].slave_ID;
        
        if(RPM_cmd)
        {
            send_RPM(send_ADDR);
            RPM_cmd =  false;                                                      //cmd to be send with current running address and cmd value
            wait_ms(2);
            //HB_Ticker.attach(&HB_State_Machine,0.010);
            CMD_Ticker.attach(&CMD_State_Machine,0.25);                   //CMD_state_machine ticker started              
        }
        /*
        else if(DIR_cmd)
        {
            send_DIR(send_ADDR);
            DIR_cmd =  false;                                                      //cmd to be send with current running address and cmd value
            wait_ms(2);
            //HB_Ticker.attach(&HB_State_Machine,0.010);
            //DIR_Ticker.attach(&DIR_state_machine,0.25);                   //CMD_state_machine ticker started              
        }
        */
        else  if(BROADCAST_send)
        {
            send_BROADCAST();         //broadcast needed err_ID nd err_Status
            BROADCAST_send = false;
            wait_ms(2);
        }
        else if(switch_CH)                            //active channel switch over happened
        {
            send_ACH();                              //active channel changeover
            switch_CH = false;
            wait_ms(2);
        }
        
        else
        {    
            send_HB(send_ADDR);   //hb is to be going on
            HB_Ticker.attach(&HB_State_Machine,0.015);                          //HB_state_machine timer started
        }
    }
    else if(++norm_count >= 5)
    {
        norm_count = 0;
        if(!time_RCVD)
            norm_op = true;
        else
            __nop();
    }
    else
        __nop();
}

/**************************************************************************************
POT config and address Read FUNCTIONS
 **************************************************************************************/
void read_POT_ADDR()                     //function give present eots and no. of connected eots
{   
    pot_Addr = read_KEY_SPI(ch_D);
    if(pot_Addr==0xff)   //if 0xff it means no addressing done
        pot_Error=true;
    else if(pot_Addr!=0xff)
    {
        for( uint8_t i=0;i<=7;i++)
        {
            if(((1 << i) & pot_Addr)== 0)      // to check which bit of eot_Addr register is '0'
            {
                if(i==0)                       // bit 0 means master present and soon
                {   
                    addr_ID = pot_Master;
                    ach_M = pot_Master;
                    for(i = 1 ; i <= 7 ; i++)
                    {
                        if((((uint8_t)1<<i) & pot_Addr) == 0)
                        {
                            switch(i)
                            {
                                case 1:
                                    POT[no_pot_connected].slave_ID = pot_Submaster;
                                    POT[no_pot_connected].err_cnt=0;
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                                case 2:
                                    ach_S = pot_Slave_MCR; 
                                    POT[no_pot_connected].slave_ID = pot_Slave_MCR;
                                    POT[no_pot_connected].err_cnt=0;
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                                case 3:
                                    if(ach_S == pot_Slave_MCR)                 //if MCR ispresent
                                        __nop();
                                    else
                                    {                                            //if MCR is not present
                                        ach_S = pot_Slave_ER;
                                    }                                     
                                    POT[no_pot_connected].slave_ID = pot_Slave_ER;
                                    POT[no_pot_connected].err_cnt = 0;
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                                case 4:
                                    POT[no_pot_connected].slave_ID = pot_Listner_WP;
                                    POT[no_pot_connected].err_cnt = 0;
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                                case 5:
                                    POT[no_pot_connected].slave_ID = pot_Listner_WS;
                                    POT[no_pot_connected].err_cnt=0;
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                                case 6:
                                    POT[no_pot_connected].slave_ID = pot_Listner_OPS;
                                    POT[no_pot_connected].err_cnt=0;
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                                case 7:
                                    POT[no_pot_connected].slave_ID = pot_Listner_ASP;
                                    POT[no_pot_connected].err_cnt=0;       
                                    POT[no_pot_connected].err_status=false;
                                    no_pot_connected++;
                                break;
                            }
                        }
                    }
                    if(no_pot_connected <=0)
                        pot_Error = true;
                    else
                        pot_Error = false;  
                }
                else
                {
                    switch(i)
                    {
                        case 1:
                        {
                            addr_ID = pot_Submaster;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        case 2:
                        {
                            addr_ID = pot_Slave_MCR;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        case 3:
                        {
                            addr_ID = pot_Slave_ER;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        case 4:
                        {
                            addr_ID = pot_Listner_WP;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        case 5:
                        {
                            addr_ID = pot_Listner_WS;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        case 6:
                        {
                            addr_ID = pot_Listner_OPS;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        case 7:
                        {
                            addr_ID = pot_Listner_ASP;
                            ach_S = pot_Slave_MCR;
                            led_ach[ach_S-1] = ON;
                            break;
                        }
                        default:
                            pot_Error = true;
                    }   
                }
            }
        }
    }
}

void interrupt_init()
{
    int_A.mode(PullUp);
    wait_ms(1);
    int_B.mode(PullUp);
    wait_ms(1);
    int_C.mode(PullUp);
    wait_ms(1); 
}

void Uart_init()
{                                    
  UART.baud(9600);
  UART.format(8,SerialBase::None,1);
  wait_ms(1);
}

void Led_init()
{   
    
    LED_CMD_ACK = ON;                  
    wait_ms(1);
       
    for(uint8_t i=0;i<=1;i++)             //ctrl led intialisation
    {
        LED_CTRL[i] = ON;
        wait_ms(1);
    }
    /*
    for(uint8_t i=0;i<=1;i++)             //ctrl led intialisation
    {
        LED_DIR[i] = ON;
        wait_ms(1);
    }
    */
    for(uint8_t i=0; i<=7;i++)            //ach led initialisation
    {
        LED_ACH[i] = ON ; 
        wait_ms(1);
    } 
    
    wait(1);
    
    LED_CMD_ACK = OFF;
    led_cmd_ack = OFF;
    wait_ms(1);
    
    for(uint8_t i=0;i<=1;i++)         //ctrl led turned off
    {
        LED_CTRL[i] = OFF;
        led_ctrl[i] = OFF;
        wait_ms(1);
    }
    /*
    for(uint8_t i=0;i<=1;i++)             //ctrl led intialisation
    {
        LED_DIR[i] = OFF;
        led_dir[i] = OFF;
        wait_ms(1);
    }
    */
    for(uint8_t i=0; i<=7;i++)         //ach led turned off
    {
        LED_ACH[i] = OFF; 
        led_ach[i] = OFF;
        wait_ms(1);
    } 
}

void pwm_init()
{
    mypwm.period_ms(10);           //initialy 20ms
    wait_us(1);
    mypwm.write(.10);      //10%duty cycle more bright with less duty cycle
    wait_us(1);
}

void mcp23s17_init()
{
    //device1 configuration
    mcp23s17_1.reset();           //reset on power-up
    wait_ms(1);
    mcp23s17_2.reset();           //reset on power-up
    wait_ms(1);
    mcp23s17_1.iocon(0x2a);
    wait_ms(1);
    mcp23s17_2.iocon(0x2a);
    wait_ms(1);
    mcp23s17_1.iodira(0xff);     //set 8-bits PORTA as inputs
    wait_ms(1);
    mcp23s17_1.iodirb(0xff);     //set 8-bits PORTB as inputs
    wait_ms(1);
    mcp23s17_1.ipola(0x00);
    wait_ms(1);
    mcp23s17_1.ipolb(0x00);
    wait_ms(1);
    mcp23s17_1.gpintena(0x00);   //initially disabled
    wait_ms(1);
    mcp23s17_1.gpintenb(0x00);    
    wait_ms(1);
    mcp23s17_1.defvala(0xff);
    wait_ms(1);
    mcp23s17_1.defvalb(0xff);
    wait_ms(1);
    mcp23s17_1.intcona(0x00);     //bank0,mirror disabled, SEQOP disabled, slew rate disabled,HAEN enabled,open drain enabled,interrupt active high,not allowed
    wait_ms(1);
    mcp23s17_1.intconb(0x00);
    wait_ms(1);
    mcp23s17_1.gppua(0xff);
    wait_ms(1);
    mcp23s17_1.gppub(0xff);
    wait_ms(1);
    
    //device2 configuration
   
    mcp23s17_2.iodira(0xff);       //set 8-bits PORTA as inputs
    wait_ms(1);
    mcp23s17_2.iodirb(0xff);       //set 8-bits PORTb as inputs
    wait_ms(1);
    mcp23s17_2.ipola(0x00);
    wait_ms(1);
    mcp23s17_2.ipolb(0x00);
    wait_ms(1);
    mcp23s17_2.gpintena(0xff);     //initially 0xff will be always enabled req/inc/dec/test
    wait_ms(1);
    mcp23s17_2.gpintenb(0x00);     //interrupts disabled for address PORT
    wait_ms(1);
    mcp23s17_2.defvala(0xff);
    wait_ms(1);
    mcp23s17_2.defvalb(0xff);
    wait_ms(1);
    mcp23s17_2.intcona(0x00);    //bank0,mirror disabled, SEQOP disabled, slew rate disabled,HAEN enabled,open drain enabled,interrupt active high,not allowed
    wait_ms(1);
    mcp23s17_2.intconb(0x00);
    wait_ms(1);
    mcp23s17_2.gppua(0xff);
    wait_ms(1);
    mcp23s17_2.gppub(0xff);
    wait_ms(1);
    mcp23s17_1.intcapa();    //dummy read performed to clear any interrupt on power up
    wait_ms(1);
    mcp23s17_1.intcapb();
    wait_ms(1);
    mcp23s17_2.intcapa();
    wait_ms(1);
    
}

void Spi3_init()              //for mcp23s17
{
  _SPI3.format(8,0);         //8bit,mode 0
  _SPI3.frequency(1000000);  //1mhz   
  wait_ms(1);
}

void RTC_init()
{
    read_RTC();
    wait_ms(1);
    RTC_display(RTC_buffer);
    wait_ms(1);
}

void SetDateTime
(    int           year = 0
    ,int           mon = 0
    ,int           day = 0
    ,int           hour = 0
    ,int           min = 0
    ,int           sec = 0
)
{
    struct     tm Clock;
    Clock.tm_year = year - 1900;
    Clock.tm_mon  = mon;
    Clock.tm_mday = day;
    Clock.tm_hour = hour;
    Clock.tm_min  = min;
    Clock.tm_sec  = sec;
    time_t epoch = mktime(&Clock);
    set_time(epoch);                              //time set
}

void max7219_init()
{
    max7219.set_num_devices(2);                 //total 02 no. of devices
    DEVICE1.device_number = 0x01;
    DEVICE1.decode_mode = 0xff;                 //bcd decode mode
    DEVICE1.intensity = 0x01;                   //Max7219::MAX7219_INTENSITY_8,
    DEVICE1.scan_limit = 0x07;                  // 1-4 digits RTC,5-8 digits last cmd RPM
    wait_ms(1);
    DEVICE2.device_number = 0x02;               //
    DEVICE2.decode_mode = 0xff;                 //bcd decode mode
    DEVICE2.intensity = 0x01;                   //Max7219::MAX7219_INTENSITY_8,     //intensity moderate
    DEVICE2.scan_limit = 0x03;                  //1-4 digits current
    wait_ms(1);

    max7219.init_device(DEVICE1);               //DEVICE 1 configured as per above settings
    wait_ms(1);
    max7219.enable_device(1);                   //normal operation
    wait_ms(1);
    max7219.init_device(DEVICE2);               //DEVICE 2 configured as per above settings
    wait_ms(1);
    max7219.enable_device(2);                   //normal operation
    wait_ms(1);
    max7219.set_display_test();                  //all on
    wait(1);
    max7219.clear_display_test();                //normal operation
    wait_ms(1);
    for(uint8_t i=1; i<=4; i++)  
    {
        max7219.write_digit(1, i, 0x00);   //device 1 RTC, digit 1-4 ,data 0x01
        wait_us(100);
    }
    wait_ms(100);
    
    for(uint8_t i=4; i<=8; i++)
    {
        max7219.write_digit(1, i, 0x00);   //device 1 last cmd, digit 4-8 ,data 0x01
        wait_us(100);
    }
    wait_ms(100);
    for(uint8_t i=1; i<=4;i++)
    {
        max7219.write_digit(2, i, 0x00);   //device 2 current CMD, digits 1-4 ,data 0x01
    }
    wait_ms(100);
}

void Spi2_init()             // for max7219
{
       _SPI2.format(16,0);         //16bit,mode 0
       _SPI2.frequency(1000000);  //1mhz
       wait_ms(1);
}

void Discrete_init()
{
     buzzer = ON;
     hooter = ON;             //on
     wait(1);   // one second delay
     buzzer = OFF;
     hooter = OFF;              //off
}

void Parameters_init()
{ 
    /*intialization of all bool data types indicating different error flags */     
    pot_Error = false;
    device_ERR = false;  
    pot_MCR_err = false;
    pot_ER_err = false;
    test = false; 
    toggle = false;
    toggle_fast = false;  
    display_flash = false;   
    norm_op = false;                    
    
    /*intialization of all bool data types indicating flag on key press interruption */
    key_CMD = false;
    key_CTRL = false;
    key_SET = false;
    REQ_key = false;
    ACK_key = false;
    RPM_cmd = false;              //if true RPM command is raised
    DIR_cmd = false;              //if true DIR command is raised
    ENTER_ACK_key = false;
    disp_clear = false;
    
    /*intialization of all bool data types indicating flags while serial receive */ 
    RPM_cmd_ack = false;
    HB_ack_rcvd = false;
    BROADCAST_rcvd = false;
    HB_rcvd = false;
    REQ_rcvd = false;
    ACK_rcvd = false;
    time_RCVD = false;
    data_fail = false;
    
    /*intialization of all bool data types indicating flags while serial transmit */ 
    BROADCAST_send = false;
    RPM_cmd_ack_send = false;
    DIR_cmd_ack_send = false;
    REQ_send = false;
    ACK_send = false;
    HB_send = false;
    
    /*intialization of all bool data types indicating active channel changeover*/
    test=false;
    disp_test = false;
    ACH_SW_over = switch_CH = false;
    
    display_flash = false;
    
    /*intialization of all int  data types*/
    addr = req = ack = ach_S = ach_M = duty_cycle = sync_count = 0;
    pot_Addr = addr_ID = send_ADDR = rcvd_ADDR = req_ADDR = ack_ADDR = 0;
    test_cnt = err_ID = err_Status = ticker_count = ticker_count_fast  = current_status = current_status_dir= 0;
    spi_error = set_bit = err_ALL = no_pot_connected = norm_count = Hrs_1 = Hrs_2 = Mins_1 = Mins_2 = 0;
    checksum_send_byte = checksum_rcvd_byte = 0;
    
    //dir = last_dir = 0;
    HB_timer =0;
    sync_time = 0;
    num_bytes = 0;

    device_addr = -1;
    valid = 'A';
    
    POT = new  _POT[NO_MAX_POT];
}

void POT_INIT()
{   
    Parameters_init();
    wait_ms(1);
    
    Discrete_init();
    wait_ms(1);
    
    Spi2_init();
    wait_ms(1);
    
    max7219_init();
    wait_ms(1);
    
    SetDateTime(2017,11,16,16,45,40); // set default date and time
    wait_ms(1);
    
    RTC_init();
    wait_ms(1);
    
    Spi3_init();
    wait_ms(1);
    
    mcp23s17_init();
    wait_ms(1);
    
    pwm_init();
    wait_ms(1);
    
    Led_init();
    wait_ms(1);
    
    Uart_init(); 
    wait_ms(1);
    
    read_POT_ADDR();
    wait_ms(1);
    
    interrupt_init();
    wait_ms(1);
    
    if((addr_ID == pot_Master)&&(!pot_Error))                         //if addres of master eot found, by default master
    {
        wait(10);                             //wait for 10sec,as MCU take time to read time from GPS
        led_ach[ach_M-1] = ON;   
        int_A.rise(&read_KEY_A);              //all key functions enabled for PORTA,B,C        
        int_B.rise(&read_KEY_B);                
        int_C.rise(&read_KEY_C); 
        wait_ms(2);
        mcp23s17_1.gpintena(0xff);    //interrupts enabled
        wait_us(100);
        mcp23s17_1.gpintenb(0xff);    //ff
        wait_us(100);
        HB_timer = 1.0/no_pot_connected;
        sync_time = 3600/HB_timer;                //   3600.0/HB_timer; required
        pot_HB.attach(&HB_handler,HB_timer);    //
        wait_ms(1);
        UART.attach( &read_SERIAL);    //receive interrupt enabled
        wait_ms(1);
        ask_TIME();                    //send enquiry to Central power unit for time setting
        wait_ms(2);
        send_ACH();
        wait_ms(2);
    }
    else if(pot_Submaster <= addr_ID <= pot_Listner_ASP)
    {
        wait(1);
        led_ach[0] = ON;           //by default master led turns on
        int_A.rise(NULL);          //all key functions/interrupt disabled for PORTA,B       
        int_B.rise(NULL);
        int_C.rise(&read_KEY_C);   //only PORTC needs to be intialized in other all cases,inc,dec,tst,req
        wait_ms(1);
        UART.attach( &read_SERIAL);    //receive interrupt enabled
        wait_ms(1);
    }
    else if(pot_Error)
    {
        int_A.rise(NULL);
        int_B.rise(NULL);
        int_C.rise(NULL);
        pot_Error = true;
        //return(-1);
    }   
    else
    {
        __nop();
    }
}

int main()
{
    POT_INIT();
    
    led_refresh.attach(&update_led , 0.020);   //refresh rate 20ms
    wait_ms(2);
    
    RTC_ticker.attach(&read_RTC , 59);   //after 60 sec
    wait_ms(2);
    
    while(1) 
    {
    }
}