/* 
 *  TCO PULT V0.21
 *  
 *  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
 *           
 */
#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"

unsigned int 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
          boolean mode[]={ 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,  0,  0,  0,  0};  // mod reakce (pulzni nebo stiskovej)
// 1-pulzni    0-podle delky drzeni

#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));
unsigned int 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

unsigned int StateOld;
boolean ledState = 0; 
unsigned long previousMillis = 0;        // will store last time LED was updated
#define interval 500            // interval snimani stavu DCC

const int LEDC = 12;                 // pin cervene ledky
const int 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
//#endif 

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

void setup() {
  for(int 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 (int x = 0; x < NO_OBJECTS; x++) {
    if(bouncer[x].onPressed()) {
      XAddress = (addresses[x] - 1);
      if(turn[x] == 1) {
        sendGfunction();
        if (mode[x] != 0) {
          delay(waiting); 
          sendGfunctionOff();
        } else {
            while (bouncer[x].onReleased()==false) {}
            sendGfunctionOff();
          }
      } else {
        sendRfunction();
        if (mode[x] != 0) {
          delay(waiting); 
          sendRfunctionOff();
        } else {
            while (bouncer[x].onReleased()==false) {}
            sendRfunctionOff();
    } } }
  } 

// 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



