// wac 30 May 2017   changed DCC digital output switching to increase spped;
// wac amended 27 Feb 2018 ; added void 'set_fun' ; added to 'get_key' if (last_locoAdr != locoAdr && locoAdr > 0){set_fun();}
// wac added SoftwareSerial dcc(A3,A4) and new current sense void 10 July 2018
// wac aug 2018, changed if statements in get_key 
// wac sep 2018 added CV1 write - program loco numbers 
// PRESS 'A' AND HOLD AS POWER IS SWITCHED ON TO ACCESS CV1 WRITE OF LOCO ADDRESS
// Jan 2019 added option for momentary F2 F3, added '&& repeat_data' to loco and speed ops
// added '&& repeat_data' to function and turnout ops
// wac 1 Apr 2019 changed time interval for 'void live_locos' from 30 to 3000 ms 
// wac 2 May 2019 added to line 491 '&& e_stop == 0 ' 
// wac 11 May 2019 fixed bug in initial screen display
// extended delay between CV1 DCC packets from 5 to 10 msec
// 20 Sep 2019 amended clock and packets for CV write marked //**

#include <SoftwareSerial.h>
SoftwareSerial dcc(A3,A4);  // RX TX

#include "Keypad.h"
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns 
char keys[ROWS][COLS] = {
  {'1','2','3', 'A'},
  {'4','5','6', 'B'},
  {'7','8','9', 'C'},
  {'*','0','#', 'D'}
};
byte rowPins[ROWS] = {2,3,4,5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6,7,8,11}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
char key = keypad.getKey();

//D10 is Arduino pin for DCC out, connected to "DIRECTION" of LMD18200
#define AN_SPEED A7  // analog reading for Speed Pot
#define AN_CURRENT A0  // analog reading for current sense
#define DCC_PWM A2  // analog pin as digital, connected to "PWM" of LMD18200
#define DCC_BRAKE A5  // analog pin as digital, connected to "BRAKE" of LMD18200

//Timer frequency is 2MHz for ( /8 prescale from 16MHz )
#define TIMER_SHORT 0x8D  // 58usec pulse length 
#define TIMER_LONG  0x1B  // 116usec pulse length 

unsigned char last_timer=TIMER_SHORT;  // store last timer value
   
unsigned char flag=0;  // used for short or long pulse
unsigned char every_second_isr = 0;  // pulse up or down

// definitions for state machine 
#define PREAMBLE 0    
#define SEPERATOR 1
#define SENDBYTE  2

unsigned char state= PREAMBLE;
unsigned char preamble_count;
unsigned char index = 0;  // **
int preamable_type = 0;
int go_cv;
int cv_set;
unsigned char outbyte = 0;
unsigned char cbit = 0x80;
boolean changed_l = false;
boolean changed_f = false;
boolean changed_t = false;
boolean changed_s = false;
boolean repeat_data = false;
String send_string;
long t = 0;
long lastmillis;
long interval;

// variables for throttle / functions
int locoSpeed = 0;
int last_locoSpeed = 0;
int dir = 0;
int fl = 0;
int fun1 = 0;int fun2 = 0;int fun3 = 0;int fun4 = 0;int fun5 = 0;int fun6 = 0;int fun7 = 0;int fun8 = 0;  // functions
int tun1 = 0;int tun2 = 0;int tun3 = 0;int tun4 = 0;int tun5 = 0;int tun6 = 0;int tun7 = 0;int tun8 = 0;  // turnouts
int tun9 = 0;int tun10 = 0;int tun11 = 0;int tun12 = 0;int tun13 = 0;int tun14 = 0;int tun15 = 0;int tun16 = 0;  // turnouts
int teen = 0;
int last_locoAdr = 0;
int key_val;
int function = 0;
int last_function = 0;
int e_stop = 0;
int last_e_stop = 0;
unsigned char  data = 0, xdata;
long A;  // current measured
long Acv;  // current measured on CV1 writing
long Amax;  // max current
int Atrig; // when max current Amax is exceeded
int turn_a = 0;
int turn_b = 0;
int last_turn_a = 0;
int last_turn_b = 0;
int maxF =4;
int locoAdr = 9;   // this is the default address of the loco
int locoAdr2; // temp value for repeat commands
int sensorValue;
int z;

// buffer for command
struct Message {
   unsigned char data[7];
   unsigned char len;
} ;

#define MAXMSG 3
// for the time being, use only 2 messages - the idle msg, the loco Speed msg, function msg

struct Message msg[MAXMSG] = { 
    { { 0xFF,0, 0xFF, 0, 0, 0, 0}, 3},   // idle msg
    { { locoAdr, 0, 0,  0, 0, 0, 0}, 3},   // locoMsg with 128 speed steps 0x3f
    { { 16, 0, 0, 0, 0, 0, 0}, 3}  // ** added this message to handle preamble 
                                   // and idle message changes as required
  };               // loco msg must be filled later with speed and XOR data byte
                                        
int msgIndex=0;  
int byteIndex=0;

int la[20];// array to hold loco numbers
int sa[20];// array to hold speed values
int fda[20];// array to hold dir
int fla[20];// array to hold lights
int f1a[20];// array to hold fun1
int f2a[20];// array to hold fun2
int f3a[20];// array to hold fun3
int f4a[20];// array to hold fun4
int f5a[20];// array to hold fun5
int f6a[20];// array to hold fun6
int f7a[20];// array to hold fun7
int f8a[20];// array to hold fun8

int tun[20];// array to hold turnout values
unsigned char fun1_data[20];// array to hold group 1 Msg data for each loco
unsigned char fun2_data[20];// array to hold group 2 Msg data for each loco

//Setup Timer2.
//Configures the 8-Bit Timer2 to generate an interrupt at the specified frequency.
//Returns the time load value which must be loaded into TCNT2 inside your ISR routine.

void SetupTimer2(){
    DDRB = B00000100;   //  register B for digital pin 8 to 13, pin 10 as an OUTPUT
  //Timer2 Settings: Timer Prescaler /8, mode 0
  //Timmer clock = 16MHz/8 = 2MHz oder 0,5usec
  TCCR2A = 0;
  TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20; 

  //Timer2 Overflow Interrupt Enable   
  TIMSK2 = 1<<TOIE2;

  //load the timer for its first cycle
  TCNT2=TIMER_SHORT; 
}
//Timer2 overflow interrupt vector handler
ISR(TIMER2_OVF_vect) {
  //Capture the current timer value TCTN2. This is how much error we have
  //due to interrupt latency and the work in this function
  //Reload the timer and correct for latency.  
  // for more info, see http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
  unsigned char latency;
  // for every second interupt just toggle signal
  if (every_second_isr)  {
      PORTB = B00000100;   // using register control to speed up switching speed pin 10 switched HIGH (DIR)
     every_second_isr = 0;        
     // set timer to last value
     latency=TCNT2;
     TCNT2=latency+last_timer;  
  }  else  {  // != every second interrupt, advance bit or state
       PORTB = B00000000;  // pin 10 switched LOW (DIR)
     every_second_isr = 1; 
     switch(state)  {
       case PREAMBLE:
           flag=1; // short pulse
           preamble_count--;
           if (preamble_count == 0)  {  // advance to next state
              state = SEPERATOR;
              // get next message
              msgIndex++;
               if (msgIndex >= MAXMSG-1)  {  msgIndex = index;}  // ** 
              byteIndex = 0; //start msg with byte 0
           }
           break;
        case SEPERATOR:
           flag=0; // long pulse
           // then advance to next state
           state = SENDBYTE;
           // goto next byte ...
           cbit = 0x80;  // send this bit next time first         
           outbyte = msg[msgIndex].data[byteIndex];
           break;
        case SENDBYTE:
           if (outbyte & cbit)  { 
              flag = 1;  // send short pulse
           }  else  {
              flag = 0;  // send long pulse
           }
           cbit = cbit >> 1;
           if (cbit == 0)  {  // last bit sent, is there a next byte?
              byteIndex++;
              if (byteIndex >= msg[msgIndex].len)  {
                 // this was already the XOR byte then advance to preamble
                 state = PREAMBLE; 
               preamble_count = msg[2].data[0];  // **
               index = msg[2].data[1];           // **
              }  else  {
                 // send separtor and advance to next byte
                 state = SEPERATOR ;
              }
           }
           break;
     }   
     if (flag)  {  // if data==1 then short pulse
        latency=TCNT2;
        TCNT2=latency+TIMER_SHORT;
        last_timer=TIMER_SHORT;
     }  else  {   // long pulse
        latency=TCNT2;
        TCNT2=latency+TIMER_LONG; 
        last_timer=TIMER_LONG;
     }  
  }
}

void setup() {
   Serial.begin(9600);
   dcc.begin(9600);
  
   pinMode(DCC_PWM, OUTPUT); // A2 to PWM pin on LMD18200t
   pinMode(DCC_BRAKE, OUTPUT); // A5 to Brake pin on LMD18200t
   digitalWrite(DCC_PWM, HIGH);
   digitalWrite(DCC_BRAKE, LOW);
   
   Atrig = 0;
   
  //zero array
  for (int i = 0 ; i<=20 ; i++){
  la[i] = i; sa[i] = 0; fda[i] = 0; fla[i] = 0; f1a[i] = 0; f2a[i] = 0;
  f3a[i] = 0; f4a[i] = 0; f5a[i] = 0; f6a[i] = 0; f7a[i] = 0; f8a[i] = 0;
  tun[i] = 0; fun1_data[i]= 128 ;fun2_data[i]= 176;
 }
  t = millis();
  lastmillis = millis();
  interval = 3000;   // changed from 30 to 3000 1 April 2019 2019
  //Start the timer 
  SetupTimer2();
  delay(200);  
  
  go_cv = 0;
  get_key_start(); //select 'A' for CV1 setting
}

void amend_speed (struct Message & x) 
{ 
 x.data[0] = locoAdr;
 x.data[1] = 0x3f;  // locoMsg with 128 speed steps 0x3f
}

void amend_speed2 (struct Message & x) 
{ 
 x.data[0] = locoAdr2;
 x.data[1] = 0x3f;  // locoMsg with 128 speed steps 0x3f
}

void E_stop (struct Message & x) 
{ 
 x.data[0] = 0;
 x.data[1] = B01000001;  // e stop
}

void amend_group1 (struct Message & x) 
{ 
 x.data[0] = locoAdr;
 x.data[1] = 0x80;   // locoMsg with group one instruction 0x80
 x.data[3] = 0;
}

void amend_group1_2 (struct Message & x) 
{ 
 x.data[0] = locoAdr2;
 x.data[1] = 0x80;   // locoMsg with group one instruction 0x80
 x.data[3] = 0;
}

void amend_tun1 (struct Message & x) 
{ 
 x.data[0] = 0x81; // accessory decoder 0x80 & address 1
 x.data[1] = 0;
 x.data[3] = 0;
}

void amend_tun2 (struct Message & x) 
{ 
 x.data[0] = 0x82; // accessory decoder 0x80 & address 2
 x.data[1] = 0;
 x.data[3] = 0;
}

void amend_tun3 (struct Message & x) 
{ 
 x.data[0] = 0x83; // accessory decoder 0x83 & address 3
 x.data[1] = 0;
 x.data[3] = 0;
}

void amend_tun4 (struct Message & x) 
{ 
 x.data[0] = 0x84; // accessory decoder 0x84 & address 4
 x.data[1] = 0;
 x.data[3] = 0;
}

 void amend_len4 (struct Message & x) 
{ 
 x.len = 4; z = 4;
}

void amend_len3 (struct Message & x) 
{ 
 x.len = 3; z = 3;
}

void current(){
  sensorValue = analogRead(AN_CURRENT);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 1.1V) = 1.08mv per division
  // 0.1 ohm resistor on current sense gives 200mv at 2 Amps or, 100mv per Amp
  // 1.08 mv per div for Internal Ref of 1.1v  : 100/1.08 = 92.6 divisions per 1000mA or 1 div = 10.8mA
  // using voltage (0 - 5V) = 4.9mv per division
      Acv = 10.8 * sensorValue ;  // mA using internal reference for CV1 write mode
      A =  50 * sensorValue ;     // current measured while in operational mode
}

void loop() {
  Atrig = 0;
  if (go_cv == 1){
    analogReference(INTERNAL);
    Amax = 250; // max current while running CV1 routine and using Internal Ref
    amend_len3(msg[1]);
    
    if (Acv >= Amax){  // current limit
        Atrig = 1;
        }
    if (Atrig == 1){
        digitalWrite(DCC_PWM, LOW);  // logic control of DCC signal to low (off)
        digitalWrite(DCC_BRAKE, HIGH); // to brake, shorts out h-bridge o/p
       }
    get_key_cv1();
    return;
  }
  
  Amax = 2600; // max operational current
  idle();
  current();
  get_key();
  read_loco();
  if (changed_l && repeat_data)  {     // if loco number, direction or e_stop changes
       amend_len4(msg[1]);
       amend_speed(msg[1]);
       assemble_dcc_msg_speed(); 
       if (e_stop == 1){
          amend_len3(msg[1]);
          E_stop(msg[1]);
          assemble_dcc_msg_group1();   
       } 
       if (Atrig == 1){
        digitalWrite(DCC_PWM, LOW);  // logic control of DCC signal to low (off)
        digitalWrite(DCC_BRAKE, HIGH); // to brake, shorts out h-bridge o/p
       }
       send_data();   
  } 
  if (changed_s && repeat_data)  {     // if loco speed pot changes
       amend_len4(msg[1]);
       amend_speed(msg[1]);
       assemble_dcc_msg_speed();  
       send_speed_data(); 
  } 
  read_function();
  if (changed_f && repeat_data)  {    // if function keys pressed
       amend_len3(msg[1]);
       amend_group1(msg[1]);
       assemble_dcc_msg_group1(); 
       send_data(); 
       delay(100);
       if (f2a[locoAdr] == 1){   // momentary action on F2
        fun2 = 0;
       } 
       if (f3a[locoAdr] == 1){   // momentary action on F3
        fun3 = 0;
       } 
  }
  
  read_turnout();
  if (changed_t && repeat_data){     // if turnout keys pressed
       amend_len3(msg[1]);
       assemble_dcc_msg_tun();  
       if (turn_a == 1) { 
       send_turnout_a();
       delay(10);
       send_turnout_a();
       delay(10);
       send_turnout_a();
       delay(10);
       send_turnout_a();
       }
       if (turn_b == 1) {    
       send_turnout_b();
       delay(10);
       send_turnout_b();
       delay(10);
       send_turnout_b();
       delay(10);
       send_turnout_b();
       }
       //send_data4();
      }
 
 if (e_stop == 0){
      repeat();
      idle();
      }
}
void repeat(){
    t = millis() - lastmillis;  
    if (t >= interval){
      repeat_data = false;
        amend_len4(msg[1]);
        for (int i = 1 ; i<=19 ; i++){
        data = 0;
        locoAdr2 = i;
        switch (fda[i]) {
        case 0 : data = 0; break;
        case 1 : data = 0x80; break; 
                        }
        if (sa[i] >= 1) {                
        data |= sa[i];  // Dir and speed data 
            amend_speed2(msg[1]);
            assemble_dcc_msg_speed();
            delay(30);  
                        }
        }
        amend_len3(msg[1]);
        for (int i = 1 ; i<=19 ; i++){   
        data = fun1_data[i];  // Front light F0 and F1 to F4 data
            locoAdr2 = i;
              if (data >= 129) { 
            amend_group1_2(msg[1]);
            assemble_dcc_msg_group1();
            delay(30);
                        }
        data = fun2_data[i];  // F5 to F8 data
             if (data >= 177) { 
            amend_group1_2(msg[1]);
            assemble_dcc_msg_group1();
            delay(30); 
                      }
        }
        lastmillis = millis();
        repeat_data = true;
    }
  }

void read_loco()  {
   changed_l = false;
   changed_s = false;
   data = 0;
  if (last_e_stop != e_stop){
      last_e_stop = e_stop;
   if (e_stop == 1){// emergency stop
        turn_a = 0;turn_b = 0;
        changed_l = true;
        return changed_l;
        }
   if (e_stop == 0){ // remove emergency stop
         locoAdr = last_locoAdr;
         send_data();
        }
       }
  if (A >= Amax){  // current limit
        Atrig = 1;
        changed_l = true;
        return changed_l;
        }
   if (last_locoAdr != locoAdr && locoAdr > 0){
    last_locoAdr = locoAdr;la[locoAdr] = locoAdr;locoSpeed = sa[locoAdr];
    changed_l = true;
    } 
   if (fda[locoAdr] != dir){fda[locoAdr] = dir;
    changed_l = true;
    }
   if (fda[locoAdr] == 0){data = 0;}
   if (fda[locoAdr] == 1){data = 0x80;} 

 if (locoAdr > 0 && e_stop == 0){  
      locoSpeed = (127L * analogRead(AN_SPEED))/1023;
   if (last_locoSpeed < locoSpeed -1 || last_locoSpeed > locoSpeed +1) {
      last_locoSpeed = locoSpeed; sa[locoAdr] = locoSpeed;
      changed_s = true;
       }
     }
    data |= sa[locoAdr];
    return changed_l;changed_s;
}

 void set_fun(){
    dir = fda[locoAdr]; fl = fla[locoAdr]; fun1 = f1a[locoAdr];fun2 = f2a[locoAdr];fun3 = f3a[locoAdr];
    fun4 = f4a[locoAdr];fun5 = f5a[locoAdr];fun6 = f6a[locoAdr];fun7 = f7a[locoAdr];fun8 = f8a[locoAdr]; 
 }
 
void read_function()  {
   delay(20);
   changed_f = false; 
   
 if (fla[locoAdr]  != fl){
    fla[locoAdr] = fl;
    changed_f = true;
   }
 if (fla[locoAdr] == 0) {
    data = 0x80;
    fun1_data[locoAdr] = data;
   } //head lights off
   
 if (fla[locoAdr] == 1) {
    data = 0x90;
    fun1_data[locoAdr] = data;
   } //head lights on
   
if (last_function != function){
    last_function = function;
    changed_f = true;
   }
    if (f1a[locoAdr] != fun1){
    f1a[locoAdr] = fun1;
    changed_f = true;
    }
    if (f1a[locoAdr] == 0) {
    data |= 0;  // f1 off
    fun1_data[locoAdr] = data;
    }
    if (f1a[locoAdr] == 1) {
    data |= 0x01; // f1 on
    fun1_data[locoAdr] = data;
    }
    if (f2a[locoAdr] != fun2){
    f2a[locoAdr] = fun2;
    changed_f = true;
    }
    if (f2a[locoAdr] == 0) {
    data |= 0;
    fun1_data[locoAdr] = data;
    }
    if (f2a[locoAdr] == 1) {
    data |= 0x02; // f2  on
    fun1_data[locoAdr] = data;
    }
    if (f3a[locoAdr] != fun3){
    f3a[locoAdr] = fun3;
    changed_f = true;
    }
    if (f3a[locoAdr] == 0) {
    data |= 0; 
    fun1_data[locoAdr] = data; 
    }
    if (f3a[locoAdr] == 1) {
    data |= 0x04; // f3  on
    fun1_data[locoAdr] = data;
    }
    if (f4a[locoAdr] != fun4){
    f4a[locoAdr] = fun4;
    changed_f = true;
    }
    if (f4a[locoAdr] == 0) {
    data |= 0;
    fun1_data[locoAdr] = data;
    }
    if (f4a[locoAdr] == 1) {
    data |= 0x08; // f4  on
    fun1_data[locoAdr] = data;
    }
    
 if (key_val >= 500 && key_val <= 800){ 
    data = 0xB0;              // locoMsg with group two instruction               
    if (f5a[locoAdr] != fun5){
    f5a[locoAdr] = fun5;
    changed_f = true;
    }
    if (f5a[locoAdr] == 0) {
    data |= 0;              // locoMsg with group two instruction
    fun2_data[locoAdr] = data;
    }
    if (f5a[locoAdr] == 1) {
    data |= 0x01; // f5  on
    fun2_data[locoAdr] = data;
    }
    if (f6a[locoAdr] != fun6){
    f6a[locoAdr] = fun6;
    changed_f = true;
    }
    if (f6a[locoAdr] == 0) {
    data |= 0;
    fun2_data[locoAdr] = data;
    }
    if (f6a[locoAdr] == 1) {
    data |= 0x02; // f6  on
    fun2_data[locoAdr] = data;
    }
    if (f7a[locoAdr] != fun7){
    f7a[locoAdr] = fun7;
    changed_f = true;
    }
    if (f7a[locoAdr] == 0) {
    data |= 0;
    fun2_data[locoAdr] = data;
    }
    if (f7a[locoAdr] == 1) {
    data |= 0x04; // f7  on
    fun2_data[locoAdr] = data;
    }
    if (f8a[locoAdr] != fun8){
    f8a[locoAdr] = fun8;
    changed_f = true;
    }
    if (f8a[locoAdr] == 0) {
    data |= 0;
    fun2_data[locoAdr] = data;
    }
    if (f8a[locoAdr] == 1) {
    data |= 0x08; // f8  on
    fun2_data[locoAdr] = data;
    } 
  }
    return changed_f;
}
void read_turnout()  {
   delay(20);
   changed_t = false;  
   
  if (key_val >= 101 && key_val <= 404 && turn_a == 1){
    data = 0xf8;amend_tun1(msg[1]);  
  }
  if (key_val >= 505 && key_val <= 808 && turn_a == 1){
    data = 0xf8;amend_tun2(msg[1]);  
  }
  if (key_val >= 101 && key_val <= 404 && turn_b == 1){
    data = 0xf8;amend_tun3(msg[1]);   
  }
  if (key_val >= 505 && key_val <= 808 && turn_b == 1){
    data = 0xf8;amend_tun4(msg[1]);}   
  
  if (tun[1] != tun1){tun[1] = tun1;changed_t = true;}
  
  if (key_val == 101 && turn_a == 1){
      if (tun1 == 1 ){data |= 0;}
      if (tun1 == 0 ){data |= 0x01;} 
   }

   if (tun[2] != tun2){tun[2] = tun2;changed_t = true;}
   
   if (key_val == 202 && turn_a == 1){
      if (tun2 == 1 ){data |= 0x02;}
      if (tun2 == 0 ){data |= 0x03;}
    }

    if (tun[3] != tun3){tun[3] = tun3;changed_t = true;}
    
    if (key_val == 303 && turn_a == 1){
      if (tun3 == 1 ){data |= 0x04;}
      if (tun3 == 0 ){data |= 0x05;} 
    }

    if (tun[4] != tun4){tun[4] = tun4;changed_t = true;}
    
    if (key_val == 404 && turn_a == 1){
        if (tun4 == 1 ){data |= 0x06;}
        if (tun4 == 0 ){data |= 0x07;}
    }

    if (tun[5] != tun5){tun[5] = tun5;changed_t = true;}
    
    if (key_val == 505 && turn_a == 1){
        if (tun5 == 1 ){data |= 0;}
        if (tun5 == 0 ){data |= 0x01;}
    }

    if (tun[6] != tun6){tun[6] = tun6;changed_t = true;}
    
    if (key_val == 606 && turn_a == 1){
        if (tun6 == 1 ){data |= 0x02;}
        if (tun6 == 0 ){data |= 0x03;}
    }

    if (tun[7] != tun7){tun[7] = tun7;changed_t = true;}
    
    if (key_val == 707 && turn_a == 1){
        if (tun7 == 1 ){data |= 0x04;}
        if (tun7 == 0 ){data |= 0x05;}
    }

    if (tun[8] != tun8){tun[8] = tun8;changed_t = true;}
    
    if (key_val == 808 && turn_a == 1){
        if (tun8 == 1 ){data |= 0x06;}
        if (tun8 == 0 ){data |= 0x07;}
    }

   if (tun[9] != tun9){tun[9] = tun9;changed_t = true;}
      
   if (key_val == 101 && turn_b == 1){
        if (tun9 == 1 ){data |= 0;}
        if (tun9 == 0 ){data |= 0x01;}
    }

    if (tun[10] != tun10){tun[10] = tun10;changed_t = true;}
    
    if (key_val == 202 && turn_b == 1){
        if (tun10 == 1 ){data |= 0x02;}
        if (tun10 == 0 ){data |= 0x03;}
    }

    if (tun[11] != tun11){tun[11] = tun11;changed_t = true;}
    
    if (key_val == 303 && turn_b == 1){
        if (tun11 == 1 ){data |= 0x04;}
        if (tun11 == 0 ){data |= 0x05;}
    }

    if (tun[12] != tun12){tun[12] = tun12;changed_t = true;}
    
    if (key_val == 404 && turn_b == 1){
        if (tun12 == 1 ){data |= 0x06;}
        if (tun12 == 0 ){data |= 0x07;}
    }

    if (tun[13] != tun13){tun[13] = tun13;changed_t = true;}
    
    if (key_val == 505 && turn_b == 1){
        if (tun13 == 1 ){data |= 0;}
        if (tun13 == 0 ){data |= 0x01;}
    }

    if (tun[14] != tun14){tun[14] = tun14;changed_t = true;}
    
    if (key_val == 606 && turn_b == 1){
        if (tun14 == 1 ){data |= 0x02;}
        if (tun14 == 0 ){data |= 0x03;}
    }

    if (tun[15] != tun15){tun[15] = tun15;changed_t = true;}
    
    if (key_val == 707 && turn_b == 1){
        if (tun15 == 1 ){data |= 0x04;}
        if (tun15 == 0 ){data |= 0x05;}
    }

    if (tun[16] != tun16){tun[16] = tun16;changed_t = true;}
    
    if (key_val == 808 && turn_b == 1){
        if (tun16 == 1 ){data |= 0x06;}
        if (tun16 == 0 ){data |= 0x07;}
    }
    return changed_t;
}

void idle(){
  amend_len3(msg[1]);
  noInterrupts();
  msg[1].data[0] = 0xFF;
  msg[1].data[1] = 0;
  msg[1].data[2] = 0xFF;
  interrupts();
   }
   
void assemble_dcc_msg_speed() {
   // add XOR byte 
   noInterrupts();  // make sure that only "matching" parts of the message are used in ISR
   msg[2].data[0] = 14; // 14 x '1's  // **
   msg[2].data[1] = 0;  // **
   msg[1].data[2] = data;
   xdata = (msg[1].data[0] ^ msg[1].data[1]) ^ data;
   msg[1].data[3] = xdata; 
   interrupts();
}

void assemble_dcc_msg_group1() {
   // add XOR byte  
   noInterrupts();  // make sure that only "matching" parts of the message are used in ISR
   msg[2].data[0] = 14; // 14 x '1's  // **
   msg[2].data[1] = 0;  // **
   msg[1].data[1] = data;
   xdata = (msg[1].data[0] ^ data);
   msg[1].data[2] = xdata; 
   interrupts();
}

void assemble_dcc_msg_tun() {
   // add XOR byte  
   noInterrupts();  // make sure that only "matching" parts of the message are used in ISR
   msg[2].data[0] = 14; // 14 x '1's  // **
   msg[2].data[1] = 0; // **
   msg[1].data[1] = data;
   xdata = (msg[1].data[0] ^ data);
   msg[1].data[2] = xdata; 
   interrupts();
}

void get_key(){
  char key = keypad.getKey();
  if (key == NO_KEY){
    return;
  }
  delay(200);
   if (key != NO_KEY){
    key_val = key - '0';
    //Serial.println(key_val);
    switch (key_val)
    { 
    case -13: teen ^= 10; locoAdr = teen; locoAdr = constrain(locoAdr, 1, 10);return;
    case 17: turn_a = 0 ; turn_b = 0 ; function ^= 1;return;
    case 18: function = 0; turn_b = 0; turn_a = 1; send_turnout_a();return;
    case 19: function = 0; turn_a = 0; turn_b = 1; send_turnout_b();return;
    case 20: e_stop ^= 1;return;
    }
 if (key_val >= 1 && key_val <= 9 && function == 0 && turn_a == 0 && turn_b == 0){
      locoAdr = key_val + teen;
    }
  
  switch (key_val)
  {
    case -6: dir ^= 1;return;
    case 0:  fl ^= 1;return;
  }

 if (key_val >= 1 && key_val <= 9 && function == 1){
      key_val = key_val * 100;
    }
 if (function == 1){
  switch (key_val)
  {
    case 100: fun1 ^= 1;return;
    case 200: fun2 ^= 1;return;
    case 300: fun3 ^= 1;return;
    case 400: fun4 ^= 1;return;
    case 500: fun5 ^= 1;return;
    case 600: fun6 ^= 1;return;
    case 700: fun7 ^= 1;return;
    case 800: fun8 ^= 1;return;
  } }
if (turn_a == 1){
 if (key_val >= 1 && key_val <= 9 ){
      key_val = key_val * 101;
    }
    switch (key_val)
    {
    case 101: tun1 ^= 1;return;
    case 202: tun2 ^= 1;return;
    case 303: tun3 ^= 1;return;
    case 404: tun4 ^= 1;return;
    case 505: tun5 ^= 1;return;
    case 606: tun6 ^= 1;return;
    case 707: tun7 ^= 1;return;
    case 808: tun8 ^= 1;return;
    }
   }

if (turn_b ==1){
 if (key_val >= 1 && key_val <= 9 ){
      key_val = key_val * 101;
    }
    switch (key_val)
    {
    case 101: tun9 ^= 1;return;
    case 202: tun10 ^= 1;return;
    case 303: tun11 ^= 1;return;
    case 404: tun12 ^= 1;return;
    case 505: tun13 ^= 1;return;
    case 606: tun14 ^= 1;return;
    case 707: tun15 ^= 1;return;
    case 808: tun16 ^= 1;return;
    }
   }
   if (last_locoAdr != locoAdr && locoAdr > 0){
      set_fun();
    }
  }
}

void send_data(){
  send_string = ("AA,"+ String(locoAdr) + "," + String(fda[locoAdr]) + "," + String(fla[locoAdr]) + "," + String(f1a[locoAdr]) + "," + String(f2a[locoAdr]) + "," + String(f3a[locoAdr]) + ",");
  send_string = send_string + (String(f4a[locoAdr]) + "," + String(f5a[locoAdr]) + "," + String(f6a[locoAdr]) + "," + String(f7a[locoAdr]) + "," + String(f8a[locoAdr]) + ",");
  send_string = send_string + (String(sa[locoAdr]) + "," + String(function) + "," + String(e_stop) + "," + String(A) + "," +String(Amax) + "," + String(Atrig) + "," + '\n');
  dcc.println(send_string);
  //Serial.println(send_string);
}

void send_turnout_a(){
  send_string = ("AA,"+ String(locoAdr) + "," + String(fda[locoAdr]) + "," + String(fla[locoAdr]) + "," + String(tun1) + "," + String(tun2) + "," + String(tun3) + ",");
  send_string = send_string + (String(tun4) + "," + String(tun5) + "," + String(tun6) + "," + String(tun7) + "," + String(tun8) + ",");
  send_string = send_string + (String(sa[locoAdr]) + "," + String(3) + "," + String(e_stop) + "," + String(A) + "," +String(Amax) + "," + String(Atrig) + "," + '\n');
  dcc.println(send_string);
  //Serial.println(send_string);
}

void send_turnout_b(){
  send_string = ("AA,"+ String(locoAdr) + "," + String(fda[locoAdr]) + "," + String(fla[locoAdr]) + "," + String(tun9) + "," + String(tun10) + "," + String(tun11) + ",");
  send_string = send_string + (String(tun12) + "," + String(tun13) + "," + String(tun14) + "," + String(tun15) + "," + String(tun16) + ",");
  send_string = send_string + (String(sa[locoAdr]) + "," + String(4) + "," + String(e_stop) + "," + String(A) + "," +String(Amax) + "," + String(Atrig) + "," + '\n');
  dcc.println(send_string);
  //Serial.println(send_string);
}

void send_speed_data(){
  send_string = ("AA,"+ String(locoAdr) + "," + String(fda[locoAdr]) + "," + String(fla[locoAdr]) + "," + String(f1a[locoAdr]) + "," + String(f2a[locoAdr]) + "," + String(f3a[locoAdr]) + ",");
  send_string = send_string + (String(f4a[locoAdr]) + "," + String(f5a[locoAdr]) + "," + String(f6a[locoAdr]) + "," + String(f7a[locoAdr]) + "," + String(f8a[locoAdr]) + ",");
  send_string = send_string + (String(sa[locoAdr]) + "," + String(5) + "," + String(e_stop) + "," + String(A) + "," +String(Amax) + "," + String(Atrig) + "," + '\n');
  dcc.println(send_string);
  //Serial.println(send_string);
}

void send_data2() {
  for (int i = 1 ; i<=19 ; i++){
        Serial.print(la[i],DEC);
        Serial.print(" ");
        Serial.print(fda[i],DEC);
        Serial.print(" ");
        Serial.print(fla[i],DEC);
        Serial.print(" ");
        Serial.print(f1a[i],DEC);
        Serial.print(" ");
        Serial.print(f2a[i],DEC);
        Serial.print(" ");
        Serial.print(f3a[i],DEC);
        Serial.print(" ");
        Serial.print(f4a[i],DEC);
        Serial.print(" ");
        Serial.print(f5a[i],DEC);
        Serial.print(" ");
        Serial.print(f6a[i],DEC);
        Serial.print(" ");
        Serial.print(f7a[i],DEC);
        Serial.print(" ");
        Serial.print(f8a[i],DEC);
        Serial.print(" ");
        Serial.print(sa[i],DEC);
        Serial.print(" ");
        Serial.println(fun1_data[i]);
        Serial.print(" ");
        Serial.println(fun2_data[i]);
        Serial.print(" ");
        Serial.println(""); 
    }
}

void send_data4(){
  for (int i = 1 ; i<= z ; i++){
   Serial.println(msg[1].data[i-1], BIN);dcc.print(" , ");dcc.print(msg[1].data[i-1], HEX);dcc.print("\n");
  } 
   Serial.println();
}

//CV1 coding

void cv1_prog(){
for (int i = 0 ; i<=5 ; i++){
  reset_packet();
  delay(5);
 }
for (int i = 0 ; i<=8 ; i++){
  page_preset_packet();
  delay(5);
 }

for (int i = 0 ; i<=10 ; i++){
  reset_packet();
  delay(5);
 } 
for (int i = 0 ; i<=6 ; i++){
  cv1_write_packet();
  delay(5);
 } 
for (int i = 0 ; i<=11 ; i++){
  reset_packet();
  delay(5);
 } 
}

void reset_packet() {
   amend_len3(msg[1]);
   noInterrupts();
   msg[2].data[0] = 24; // 14 x '1's // **
   msg[2].data[1] = 1; // **
   msg[1].data[0] = B00000000;
   msg[1].data[1] = B00000000;
   msg[1].data[2] = B00000000; 
   interrupts();
}
void page_preset_packet() {
   amend_len3(msg[1]);
   noInterrupts();
   msg[2].data[0] = 24; // 24 x '1's // **
   msg[2].data[1] = 1; // **
   msg[1].data[0] = B01111101;
   msg[1].data[1] = B00000001;
   msg[1].data[2] = B01111100; 
   interrupts();
}
void cv1_write_packet() { 
   amend_len3(msg[1]);
   noInterrupts();
   msg[2].data[0] = 24; // 24 x '1's // **
   msg[2].data[1] = 1; // **
   msg[1].data[0] = B01111000; // Address only mode
   msg[1].data[1] = B00000000 | locoAdr;
   msg[1].data[2] = (msg[1].data[0] ^ msg[1].data[1]);
   interrupts();
}

void get_key_start(){
  char key = keypad.getKey();
  delay(4000);
  if (key == NO_KEY){
    send_data();
    return;
  }
  delay(200);
   if (key != NO_KEY){
    key_val = key - '0';
    switch (key_val)
    {
    case 17: go_cv = 1; // hold 'A' for 1 second for CV1 set routine
    cv_set = 0;
    send_data_cv1();
    return; 
    } 
  }
}

void get_key_cv1(){
  A = 0;
  char key = keypad.getKey();
  if (key == NO_KEY){
    return;
  }
  delay(200);
   if (key != NO_KEY){
    key_val = key - '0';
    switch (key_val)
    {
    case -13: teen ^= 10; locoAdr = teen; locoAdr = constrain(locoAdr, 1, 10);
    cv_set = 0;
    send_data_cv1();
    return;
    case 17:
    digitalWrite(DCC_PWM, HIGH); // turn on DCC o/p
    digitalWrite(DCC_BRAKE, LOW);
    
    cv1_prog(); // press 'A' key to set CV1 to selected locoAdr
    
    current();
    digitalWrite(DCC_PWM, LOW);  // turn off DCC o/p
    digitalWrite(DCC_BRAKE, HIGH);
    if (Acv > 15){ 
    cv_set = 1;
    send_data_cv1();
    }
    return; 
    } 
    if (key_val >= 1 && key_val <= 9){
      locoAdr = key_val + teen;
      } 
     cv_set = 0;
     send_data_cv1();
  }
}

void send_data_cv1(){
  send_string = ("BB,"+ String(locoAdr) + "," + String(Acv) + "," +String(Amax) + "," + String(Atrig) + "," + String(cv_set) + "," + '\n');
  dcc.println(send_string);
  }