/* 
 *  TCO PULT V0.22
 *  
 *  Ovladaní příslušenství pomocí pultu s tlačítky připojeného na XpressNet
 *  
 *  by BorgMcz  site: http://www.dccmm.cz
 *  license GPLv2
 *  
 *  Library: Arduino RBD Button Library Example v2.1.0, by Alex Taujenis - https://github.com/alextaujenis/RBD_Button
 *           Arduino Timer Library v1.2.0, by Alex Taujenis - https://github.com/alextaujenis/RBD_Timer
 *           XpressNet slave, pgahtow.de by Philipp Gahtow - http://sourceforge.net/projects/pgahtow/files/Arduino%20%28v1.0%29%20libaries/XpressNet.zip
 *           
 *  V0.1 - test and debug default function  
 *  V0.2 - new definition this data
 *  V0.21 - funkce malloc a zjednoduseni cyklu
 *  V0.22 - add config delay time, memory optimilization
 *  
 */
#define waiting 200         // doba sepnuti vystupu

// buttons number  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60
// realy pins     54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 17, 16, 15, 14,  3,  4,  5,  6,  7,  8, 22, 24, 26, 28, 30, 32, 36, 34, 44, 42, 40, 38, 46, 48, 50, 52, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, 31, 29, 27, 25, 23, 21, 20   // realne piny arduina zmena mozna v "buttonPins"
byte addresses[]={ 1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,  7,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30};  // adresa dekoderu
  boolean turn[]={ 1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0};  // realce vyhybky rovne/odbocka
     byte mode[]={ 5,  5,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, 60, 60};  // mod reakce (pulzni nebo stiskovej)
// 0-podle delky drzeni, 1-10 pulzni ( x*0.2sec) [10*0.2sec = 2sec], vstupy 51 až 60 možno pulz v rozsahu 1-255 v rezimu multitask

#include <RBD_Timer.h>
#include <RBD_Button.h>

#define NO_OBJECTS 60   // realny pocet tlacitek ktera se pouziji, musi se pocitat i vynechana
RBD::Button *bouncer = (RBD::Button*)malloc(NO_OBJECTS * sizeof(RBD::Button));
byte buttonPins[NO_OBJECTS] = {54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,17,16,15,14,3,4,5,6,7,8,22,24,26,28,30,32,36,34,44,42,40,38,46,48,50,52,53,51,49,47,45,43,41,39,37,35,33,31,29,27,25,23,21,20};

#include <XpressNet.h> 
XpressNetClass XpressNet;
// XpressNet address: must be in range of 1-31; must be unique. Note that some IDs
// are currently used by default, like 2 for a LH90 or LH100 out of the box, or 30
// for PC interface devices like the XnTCP.
#define XNetAddress 26    // Adresse im XpressNet
#define XNetSRPin 2       // Max485 Busdriver Send/Receive-PIN

// rucni zapis, tlacitka ovladani napajeni
RBD::Button buttonOn(10);   // power ON
RBD::Button buttonOff(11);  // power OFF
RBD::Button buttonEme(9);   // power Emergency

byte StateOld;
boolean ledState = 0; 
unsigned long previousMillis = 0;        		// will store last time LED was updated
unsigned long nextMillisButton[10];      		// will store next time button reaction
boolean changeButton[10]={0,0,0,0,0,0,0,0,0,0}; // state control time to button reaction 
#define interval 500                     		// interval snimani stavu DCC

const byte LEDC = 12;                 // pin cervene ledky
const byte LEDZ = 13;                 // pin zelene ledky

// #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define SerialDEBUG 0  //For Serial Port Debugging (Arduino Mega only) , 0 znamena vypnuti debugingu
#define WaitingDEBUG 0  //Waiting for
//#endif 

#if SerialDEBUG                // nastaveni vystupu pro debuging
  unsigned int printRam = 8;
  #include <SoftwareSerial.h>
  SoftwareSerial Debug(0, 1); // RX, TX
#endif

void setup() {
  for(byte i = 0; i < NO_OBJECTS; i++){
    bouncer[i] = RBD::Button(buttonPins[i]);
  }
  pinMode(LEDC,OUTPUT);       // nastaveni vystupu ledek a test
  pinMode(LEDZ,OUTPUT);
  digitalWrite(LEDC,HIGH);
  digitalWrite(LEDZ,HIGH);
  delay(800);
  digitalWrite(LEDC,LOW);
  digitalWrite(LEDZ,LOW);  
  
  #if SerialDEBUG
    Debug.begin(115200); 
    Debug.println("Start TCO");
  #endif

  XpressNet.start(XNetAddress, XNetSRPin);    //inicializace XNet-Bus
}

byte XAddress;

void loop() {

//zde toto se bude opakovat podle poctu tlacitek max. 60
  XpressNet.receive();        //permernet update the library
  #if SerialDEBUG 
    unsigned long timerMicros = micros();   // zacatek mereni smycky
  #endif 
  for (byte x = 0; x < 50; x++) {
		#if WaitingDEBUG
			delay(500); 
			Debug.println(x);
		#endif
    if(bouncer[x].onPressed()) {
      XAddress = (addresses[x] - 1);
      if(turn[x] == 1) {
        sendGfunction();
        if (mode[x] != 0) {
          if (mode[x] > 10) {mode[x]=10;}
          delay((waiting*mode[x])); 
          sendGfunctionOff();
        } else {
            while (bouncer[x].onReleased()==false) {}
            sendGfunctionOff();
          }
      } else {
        sendRfunction();
        if (mode[x] != 0) {
          if (mode[x] > 10) {mode[x]=10;}
          delay((waiting*mode[x]));
          sendRfunctionOff();
        } else {
            while (bouncer[x].onReleased()==false) {}
            sendRfunctionOff();
    } } }
  }
// cyklus pro poslednich 10 tlacitek 
  byte y = 0;
  for (byte x = 50; x < 60; x++) {
		#if WaitingDEBUG
			delay(500); 
			Debug.print(x); Debug.print(" : "); Debug.println(y);
		#endif
    XAddress = (addresses[x] - 1);
    if (changeButton[y]) {							// bezi cas u tohoto tlacitka?
	   if (nextMillisButton[y] <= millis()) {       // vyprsel cas?
	      if(turn[x] == 1) {						// jaky smer tlacitka to byl?
	         sendGfunctionOff();
		  } else { sendRfunctionOff(); }
		  changeButton[y] = false;
		  bouncer[x].onPressed();					// smazani priznaku
	   } 
	} else {
      if(bouncer[x].onPressed()) {
        if(turn[x] == 1) {
          sendGfunction();
          if (mode[x] != 0) {
            nextMillisButton[y] = ((mode[x] * waiting) + millis());
			changeButton[y] = true;
          } else {
              while (bouncer[x].onReleased()==false) {}
              sendGfunctionOff();
            }
        } else {
          sendRfunction();
          if (mode[x] != 0) {
            nextMillisButton[y] = ((mode[x] * waiting) + millis());
			changeButton[y] = true;
          } else {
              while (bouncer[x].onReleased()==false) {}
              sendRfunctionOff();
      } } } }
	  y++;
  }
// snimani tlacitek ovladani stavu DCC
    if(buttonOn.onPressed()) {
      XpressNet.setPower(csNormal);
      #if SerialDEBUG 
          Debug.println("Button 100 Normal run"); 
      #endif
      }

    if(buttonOff.onPressed()) {
      XpressNet.setPower(csTrackVoltageOff);
        #if SerialDEBUG 
          Debug.println("Button 101 Track Voltage Off"); 
        #endif      
      }

    if(buttonEme.onPressed()) {
      XpressNet.setPower(csEmergencyStop);
        #if SerialDEBUG 
          Debug.println("Button 102 Emergency Stop"); 
        #endif      
      }
  #if SerialDEBUG 
    timerMicros = (micros()-timerMicros);   // konec mereni casu smycky
  #endif 

  unsigned long currentMillis = millis();
    if(currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;
      notifyXNetPower (XpressNet.getPower());
      #if SerialDEBUG 
          if(printRam >= 10) {             // pri preteceni vypise volnou ram a delku smycky
          printRam = 0; 
          Debug.print("RAM: ");
          Debug.println(freeRam());        // volna ram
          Debug.print(timerMicros);        // cas smycky jednoho snimani tlacitek
          Debug.println(" smycka");  
          } else {
             printRam++; }  
      #endif 
    }
}

//--------------------------------------------------------------------------------------------
void notifyXNetPower (uint8_t State){
  #if SerialDEBUG 
    if (State != StateOld) {
       Debug.print("Power: "); Debug.println(State);
       StateOld = State;
      }
  #endif
    switch (State) {
     case csNormal: 
//           Debug.println("ON");
          digitalWrite(LEDC,LOW);     // sviti zelena
          digitalWrite(LEDZ,HIGH);        
       break;

     case csTrackVoltageOff: 
//           Debug.println("OFF");
          digitalWrite(LEDC,HIGH);    // sviti cervena
          digitalWrite(LEDZ,LOW);   
       break;

     case csEmergencyStop: 
//           Debug.println("EmStop");
          ledState = !ledState;       // blika zelena
          digitalWrite(LEDC,LOW);
          digitalWrite(LEDZ,ledState);          
       break;

     case csShortCircuit: 
//            Debug.println("SHORT");
          ledState = !ledState;       // blika cervena
          digitalWrite(LEDZ,LOW);
          digitalWrite(LEDC,ledState); 
       break;

     case csServiceMode: 
//            Debug.println("PROG"); 
          digitalWrite(LEDZ,LOW);     // sviti cervena
          digitalWrite(LEDC,HIGH);
       break;

     case 255: 
//            Debug.println("ERROR"); 
          ledState = !ledState;         // blikaji obe
          digitalWrite(LEDZ,ledState);     
          digitalWrite(LEDC,ledState);
       break;       

     default:
//            Debug.println("OTHER");
          ledState = !ledState;       // blika cervena
          digitalWrite(LEDZ,LOW);
          digitalWrite(LEDC,ledState); 
       break;
    }

}


void sendGfunction() {
  XpressNet.setTrntPos(0, XAddress, B1000);
  #if SerialDEBUG 
    Debug.print("Button Green ON, Address: "); Debug.println(XAddress);
  #endif 
}
void sendGfunctionOff() {
  XpressNet.setTrntPos(0, XAddress, B0000);  
  #if SerialDEBUG 
    Debug.println("Button Green OFF");
  #endif
}

void sendRfunction() {
  XpressNet.setTrntPos(0, XAddress, B1001);
  #if SerialDEBUG 
    Debug.print("Button Red ON, Address: "); Debug.println(XAddress);
  #endif  
}
void sendRfunctionOff() {
  XpressNet.setTrntPos(0, XAddress, B0001);
  #if SerialDEBUG 
    Debug.println("Button Red OFF");
  #endif  
}


//--------------------------------------------------------------------------------------------  
//--------------------------------------------------------------------------------------------
#if SerialDEBUG
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif



