#define circuit_C2    INPUT_PULLUP   // switch type reqires no other components beyond the button switch

#define debounce             10      // number of millisecs to wait for a switch to settle once activated
#define switched           true      // signifies switch has been pressed/switch cycle complete

#define button_switch        1       // differentiates switch type

#define num_switches         4       // number of button switches connected

int relay_1 = 4;
int relay_2 = 7;
int relay_3 = 8;
int relay_4 = 12;

int led_1 = 3;
int led_2 = 5;
int led_3 = 9;
int led_4 = 11;


// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// %       Switch to Pin Macro Definition List         %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// associate button swithes each with a digital pin.
// add further definitions here if adding more switches
// note that digital pins allocated are arbitary, any
// available pin will do, or remove any not required.
#define button_switch_1      2
#define button_switch_2      6
#define button_switch_3      10
#define button_switch_4      13


// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// %                   Switch Control Sruct(ure) Declaration                 %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// now set up the configuration data for each individual
// switch to be used by the switch read function and to
// generally define the nature of the switch.
struct switch_control {
 // int  switch_type;               // type of switch connected.NOT used in this sketch example - future use
  int  switch_pin;                // digital input pin assigned to the switch
  int  circuit_type;              // the type of circuit wired to the switch
  bool switch_on;                 // the value relating to what "on" means, set up in the setup() function
  bool switch_pending;            // records if switch in transition or not
  long unsigned int elapse_timer; // records debounce timer count when associated switch is in transition
 } switches[num_switches] = {
  button_switch_1, circuit_C2, LOW, false, 0,
  button_switch_2, circuit_C2, LOW, false, 0,
  button_switch_3, circuit_C2, LOW, false, 0,
  button_switch_4, circuit_C2, LOW, false, 0,

};
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void setup() {

  pinMode(relay_1, OUTPUT);
  pinMode(relay_2, OUTPUT);
  pinMode(relay_3, OUTPUT);
  pinMode(relay_4, OUTPUT);
  
  pinMode(led_1, OUTPUT);
  pinMode(led_2, OUTPUT);
  pinMode(led_3, OUTPUT);
  pinMode(led_4, OUTPUT);



for(int i=0; i<255; i++){
    analogWrite(led_1, i);
    analogWrite(led_2, i);
    analogWrite(led_3, i);
    analogWrite(led_4, i);
}

  delay(500);

  digitalWrite(led_1, LOW);
  digitalWrite(led_2, LOW);
  digitalWrite(led_3, LOW);
  digitalWrite(led_4, LOW);

  // initialise digital input switch pins
  for (int sw = 0; sw < num_switches; sw++) {
    // define the switch circuit type - circuit_type is:
    pinMode(switches[sw].switch_pin, switches[sw].circuit_type);
    // establish 'meaning' for switch on/off depending on circuit type
    if (switches[sw].circuit_type == circuit_C2) {
      // switch is NOT configured with a pull down switch resistor
      switches[sw].switch_on   = LOW;    // switch pin goes LOW when switch pressed, ie on
    }
  }
  //Serial.begin(115200); // dont forget to set your serial monitor speed to whatever is set here
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void loop() {


  // poll each connected switch in turn and, if switched, process its associated purpose
  for (int sw = 0; sw < num_switches; sw++) {
    if (read_switch(sw) == switched) {
      // this switch (sw) has been pressed, so process via a case switch
//      Serial.print("\nbutton switch ");
//      Serial.print(sw + 1);
//      Serial.print(" on digital pin ");
      byte button_pin = switches[sw].switch_pin;
 //     Serial.println(button_pin);
      // move to switch's associated code section
      switch (button_pin)
      {
        case button_switch_1:
          if (digitalRead(relay_1)==LOW){      
            analogWrite(led_1, 100);
            digitalWrite(relay_1, HIGH);}
          else
            if (digitalRead(relay_1)==HIGH){      
              digitalWrite(led_1, LOW);
              digitalWrite(relay_1, LOW);}

  //        Serial.println("case statement 1 entered");
          break;
        case button_switch_2:
                  if (digitalRead(relay_2)==LOW){      
            digitalWrite(led_2, HIGH);
            digitalWrite(relay_2, HIGH);}
          else
            if (digitalRead(relay_2)==HIGH){      
              digitalWrite(led_2, LOW);
              digitalWrite(relay_2, LOW);}

  //        Serial.println("case statement 2 entered"); 
          break;
        case button_switch_3:
                  if (digitalRead(relay_3)==LOW){      
            digitalWrite(led_3, HIGH);
            digitalWrite(relay_3, HIGH);}
          else
            if (digitalRead(relay_3)==HIGH){      
              digitalWrite(led_3, LOW);
              digitalWrite(relay_3, LOW);}

  //        Serial.println("case statement 3 entered");
          break;
          case button_switch_4:
                  if (digitalRead(relay_4)==LOW){      
            digitalWrite(led_4, HIGH);
            digitalWrite(relay_4, HIGH);}
          else
            if (digitalRead(relay_4)==HIGH){      
              digitalWrite(led_4, LOW);
              digitalWrite(relay_4, LOW);}
              
  //        Serial.println("case statement 4 entered");
          break;
        
        default:
          // spurious switch index!  Should never arise as this is controlled
          // by the for loop within defined upper bound
          break;
      }
      Serial.flush();  // flush out the output buffer
    }
  }
 
}

//
// generic button switch read function.
// reading is controlled by:
//   a. the function parameter which indicates which switch
//      is to be polled, and
//   b. the switch control struct(ure), referenced by a).
//
// note that this function works in a nonexclusive way
// and incorporates debounce code
//
bool read_switch(int sw) {
  int switch_pin_reading;
  switch_pin_reading = digitalRead(switches[sw].switch_pin);
  if (switch_pin_reading == switches[sw].switch_on) {
    // switch is pressed (ON), so start/restart debounce process
    switches[sw].switch_pending = true;
    switches[sw].elapse_timer = millis();    // start elapse timing
    return !switched;                        // now waiting for debounce to conclude
  }
  if (switches[sw].switch_pending && switch_pin_reading == !switches[sw].switch_on) {
    // switch was pressed, now released (OFF), so check if debounce time elapsed
    if (millis() - switches[sw].elapse_timer > debounce) {
      // dounce time elapsed, so switch press cycle complete
      switches[sw].switch_pending = false;
      return switched;
    }
  }
  return !switched;
}