//////////////////////////////////////////////////////////////////
//                                                              //
//          Arduino UNO based Locomotive brake valves           //  
//                                                              //
//////////////////////////////////////////////////////////////////
/*--------------------------------------------------------
  "THE BEER-WARE LICENSE" (Revision 42):
  Alex Kostyuk wrote this code. As long as you retain this 
  notice you can do whatever you want with this stuff. 
  If we meet some day, and you think this stuff is worth it, 
  you can buy me a beer in return.
----------------------------------------------------------*/
// Revision 0.2.0  29.08.2017

#define BAUD_RATE 115200

/****************** System ******************/
void setup(void);
void loop(void);
void serialEvent();

/******** Main discrete input task ********/
#define BITS_MAXIMUM   15
void bits_init(void);
void discrete_task( void);

/****************** Utilities ******************/
const char hex_table[] = "0123456789ABCDEF";

/******** Serial communication ********/
#define STX           '{'
#define ETX           '}'
#define RBUF_LEN       162 
char rbuf[RBUF_LEN];
int  rcnt;


void setup() 
{
    digitalWrite(13, HIGH);  
    bits_init();
    Serial.begin(BAUD_RATE);
}  
  
void loop() 
{
    discrete_task();
}

void serialEvent() 
{
    int ndx;  
    while (Serial.available()) 
    {
        char ch = (char)Serial.read();
        switch(ch)
        {
            case STX:
                rcnt = 0;
            break;

            case ETX:
                rbuf[rcnt] = 0x00;
                Serial.write('!');
            break;

            default:
                if(rcnt<RBUF_LEN)
                    rbuf[rcnt++] = ch;
        }
    }
}  

/*****************************************
           Discrete inputs
*****************************************/
#define FLAG_INVERTED   (1<<0)
#define FLAG_ENABLED    (1<<1)

enum {
   TOGGLE_SWITCH_0 = 0,
   TOGGLE_SWITCH_1,
   ITEMS_MAX
};

enum {
  PUSH_BUTTON = 0,
  PUSH_BUTTON_X,
  TOGGLE_SWITCH,
  ROTARY_SWITCH,
  ROTARY_SWITCH_X,
  ENCODER_A,
  ENCODER_B,
  SHIFT
};


typedef struct {
short counter;
char signal;
short  max;
short critical_time;
short critical_timer;
char evcode;
char flag;
char mode;
char pin;
} BITS_DESCRIPTION;

BITS_DESCRIPTION  bits[BITS_MAXIMUM];

#define shift_calculate()  \
    do {\
    if(i>=0 && i<=6) /* 395 */\
    {\
       if(i > pos_395_prev)\
           shift = 16;\
       else\
           shift = 0;\
       pos_395_prev = i;\
    }\
    if(i>=7 && i<=13) /* 254 */\
    {\
       if(i > pos_254_prev)\
           shift = 100;\
       else\
           shift = 0;\
       pos_254_prev = i;\
    }\
    } while(0)

/*********************************************
          Main discrete task
**********************************************/
/* takes 448 uS  */
char prev_pos = 0x19;
void discrete_task( void)
{
    int bit,flag;
    int bit_state;
    int i,itmp;
    int shift;

    static int pos_254_prev = 0;
    static int pos_395_prev = 0;
    static char ini_flag = 0;


    if(ini_flag == 0)
    {
        for(i=0; i<7; i++)
        {
            if(digitalRead(bits[i].pin) == 0)
                pos_395_prev = i;
        }

        for(; i<14; i++)
        {
            if(digitalRead(bits[i].pin) == 0)
                pos_254_prev = i;
        }

        ini_flag = 1;
    }


    for(i=0;i<BITS_MAXIMUM;i++)
    {
       if(bits[i].flag & FLAG_ENABLED)
       {
           if(bits[i].critical_timer)
           {
               if(--bits[i].critical_timer == 0)
               {
                   Serial.write(STX);
                   Serial.write('1');
                   itmp = i + 128;
                   Serial.write(hex_table[itmp >> 4]);
                   Serial.write(hex_table[itmp & 0x0F]);
                   Serial.write(ETX);
               }
           }
           
           bit_state = digitalRead(bits[i].pin);
               
           flag = bits[i].flag;
           bit = (flag & FLAG_INVERTED) ^ bit_state;
           if(bit)
           {
               if(bits[i].counter < bits[i].max)
               {
                   if(++(bits[i].counter) >= bits[i].max)
                   {
                       if(bits[i].signal == 0)
                       {
                           //front __0__/--1--
                           
                           if(bits[i].critical_time)
                              bits[i].critical_timer = bits[i].critical_time;  
                           
                            if(bits[i].mode == ROTARY_SWITCH)
                            {
                                shift_calculate(); /* direction ccw: shift=0, cw: shift = 1*/
                                
                                Serial.write(STX);
                                Serial.write('1'); 
                                itmp = i + shift;
                                Serial.write(hex_table[itmp >> 4]);
                                Serial.write(hex_table[itmp & 0x0F]);
                                Serial.write(ETX);
                            }
  
                            else if(bits[i].mode == PUSH_BUTTON)
                            {
                                shift_calculate(); /* direction ccw: shift=0, cw: shift = 1*/
                                
                                Serial.write(STX);
                                Serial.write('8');
                                itmp = i + shift;
                                Serial.write(hex_table[itmp >> 4]);
                                Serial.write(hex_table[itmp & 0x0F]);
                                Serial.write(ETX);
                            }
                        }
                        bits[i].signal = 1;
                    }
                }
            } 
            else
            {
                if(bits[i].counter > 0)
                {
                    if(--(bits[i].counter) <= 0)
                    {
                        if(bits[i].signal)
                        {
                            // edge  --1--\___0___
                            if(bits[i].mode == PUSH_BUTTON)
                            {
                                Serial.write(STX);
                                Serial.write('4');
                                itmp = i;
                                Serial.write(hex_table[itmp >> 4]);
                                Serial.write(hex_table[itmp & 0x0F]);
                                Serial.write(ETX);
                            }
                            bits[i].critical_timer = 0;
                        }   
                        bits[i].signal = 0;
                    }
                }
            }
        }
    }
}



/*********************************************
      Inputs setup macro definitions
**********************************************/
    
#define input_init(x,y,m)   do {\
bits[x].counter = 0;\
bits[x].signal = 0;\
bits[x].max = 100;\
bits[x].flag = (FLAG_ENABLED | FLAG_INVERTED);\
bits[x].pin = y;\
pinMode(y,INPUT_PULLUP);\
bits[x].mode = m;\
} while(0)


#define input_init_rotary_switch(x,y,m)   do {\
bits[x].counter = 0;\
bits[x].signal = 0;\
bits[x].max = 30;\
bits[x].flag = (FLAG_ENABLED | FLAG_INVERTED);\
bits[x].pin = y;\
pinMode(y,INPUT_PULLUP);\
bits[x].mode = m;\
} while(0)

#define input_init_fast(x,y,m)   do {\
bits[x].counter = 0;\
bits[x].signal = 0;\
bits[x].max = 3;\
bits[x].flag = (FLAG_ENABLED | FLAG_INVERTED);\
bits[x].pin = y;\
pinMode(y,INPUT_PULLUP);\
bits[x].mode = m;\
} while(0)
   
/*****************************************
           Inputs setup
*****************************************/
void bits_init(void)
{  

    memset(bits,0,sizeof(bits));

    input_init(0,  2,ROTARY_SWITCH);/*  Valve No 395 I     switching event  Key0  / Key100 */
    input_init(1,  3,ROTARY_SWITCH);/*  Valve No 395 II          -"-        Key1  / Key101 */
    input_init(2,  4,ROTARY_SWITCH);/*  Valve No 395 III         -"-        Key2  / Key102 */
    input_init(3,  5,ROTARY_SWITCH);/*  Valve No 395 IV          -"-        Key3  / Key103 */
    input_init(4, A0,ROTARY_SWITCH);/*  Valve No 395 Va(e)       -"-        Key4  / Key104 */
    input_init(5, A1,ROTARY_SWITCH);/*  Valve No 395 V           -"-        Key5  / Key105 */
    input_init(6, A2,ROTARY_SWITCH);/*  Valve No 395 VI          -"-        Key6  / Key106 */

    input_init(7,  6,ROTARY_SWITCH);/*  Valve No 254 I           -"-        Key7  / Key107 */
    input_init(8,  7,ROTARY_SWITCH);/*  Valve No 254 II          -"-        Key8  / Key108 */
    input_init(9,  8,ROTARY_SWITCH);/*  Valve No 254 III         -"-        Key9  / Key109 */
    input_init(10, 9,ROTARY_SWITCH);/*  Valve No 254 IV          -"-        Key10 / Key110 */
    input_init(11,10,ROTARY_SWITCH);/*  Valve No 254 V           -"-        Key11 / Key111 */
    input_init(12,11,ROTARY_SWITCH);/*  Valve No 254 VI          -"-        Key12 / Key112 */
    input_init(13,12,ROTARY_SWITCH);/*  Valve No 254 VI          -"-        Key13 / Key113 */
    input_init(14, 6,PUSH_BUTTON);  /*  Valve No 254 I    holding/releasing     Key14      */    
}