Limited Programming Experience - Updated (check newest post)

Hello all, this is my first post and my first Arduino project.
I don’t have a lot of programming experience but that does not stop me from hacking away at something until I learn the right way to get it done.

I have a vehicle for which I like to install my own hardware, add features, etc.
Sometime last year, I added heated seats with a Rostra kit, which works fine and all.
Recently, I bought a replacement A/C panel that includes the controls for the heated seats (but because my vehicle did not come with heated seats, it is not a plug and play install) for which I either need the control module ($500+) or use an Arduino.

On the A/C Panel, two buttons (one for the driver and one for the passenger) for control along with three leds each (six total) for high, medium and low to indicate selection. I can provide a diagram of this as soon as I figure out how to extract it from my vehicle service manual.

Anyways, a bit of brainstorming tells me that I needed a way to keep count of how many presses were done on that button, be aware of which side was pushed (driver or passenger), use relays or a relay shield, etc.

This is what I came up with:

/*
  Heated Seat Switching
  
  This sketch handles the logic required to control a 4-stage heated seat module.
  The stages are HIGH, MEDIUM, LOW and OFF. OFF is the default-start state.
  Indication of stages is done via LEDs for HIGH, MEDIUM and LOW.
  
  INPUT is handled by pushbutton(s).
  INPUT is done on pins 2 and 3 (interrupt capable pins).
  OUTPUT is done on pins 4, 5, 6 and 7.

*/
int inputPin0 = 2; // Driver Side Button
int inputPin1 = 3; // Passenger Side Button

int ledPin = 13; // OnBoard LED

// Driver Side
int outputPin0 = 4; // HIGH Relay
int outputPin1 = 5; // MEDIUM Relay
int outputPin2 = 6; // LOW Relay

// Passenger Side
int outputPin3 = 7; // HIGH Relay
int outputPin4 = 8; // MEDIUM Relay
int outputPin5 = 9; // LOW Relay

int outputPin6 = 10; // ON Status Pin - HIGH on all states but OFF

int interruptTimeMills = 300; // Time allowed for Button Debounce
int powerOnTimeMills = 1050; // Time allowed for vehicle power stabilization
int powerOffTimeMills = 320; // Power off time - delay

volatile int pressedCountDrv = 0; // Driver Side Button Pressed Count
volatile int pressedCountPas = 0; // Passenger Side Button Pressed Count

void setup() {
  pinMode(ledPin, OUTPUT); // System On
  digitalWrite(ledPin, LOW);
  
  pinMode(inputPin0, INPUT); // Driver Side Button
  digitalWrite(inputPin0, HIGH); //Pull-Up
   
  pinMode(inputPin1, INPUT); // Passenger Side Button  
  digitalWrite(inputPin1, HIGH); //Pull-Up
   
  pinMode(outputPin0, OUTPUT); // HIGH Relay - Driver Side
  digitalWrite(outputPin0, LOW);
   
  pinMode(outputPin1, OUTPUT); // MEDIUM Relay - Driver Side
  digitalWrite(outputPin1, LOW);
   
  pinMode(outputPin2, OUTPUT); // LOW Relay - Driver Side
  digitalWrite(outputPin2, LOW);
   
  pinMode(outputPin3, OUTPUT); // HIGH Relay - Passenger Side
  digitalWrite(outputPin3, LOW);
   
  pinMode(outputPin4, OUTPUT); // Medium Relay - Passenger Side
  digitalWrite(outputPin4, LOW);
   
  pinMode(outputPin5, OUTPUT); // Low Relay - Passenger Side
  digitalWrite(outputPin5, LOW);
   
  pinMode(outputPin6, OUTPUT); // ON Status Pin - HIGH on all states but OFF
  digitalWrite(outputPin6, LOW);
   
  attachInterrupt(0, buttonPressedDrv, FALLING); // Driver Side Button Interrupt
  attachInterrupt(1, buttonPressedPas, FALLING); // Passenger Side Button Interrupt
   
  Serial.begin(9600);
}

void loop() {
  if (pressedCountDrv != 0 || pressedCountPas != 0){
    digitalWrite(ledPin, HIGH);
    
    delay(powerOnTimeMills);
    
    digitalWrite(outputPin6, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
    
    delay(powerOffTimeMills);
    
    digitalWrite(outputPin6, LOW);
  }
}

void buttonPressedDrv(){
  static unsigned long lastInterruptTime = 0;
  unsigned long interruptTime = millis();
  if (interruptTime - lastInterruptTime > interruptTimeMills){
    Serial.println("Driver Side Button Pressed!");
    
    noInterrupts();
    pressedCountDrv++;
    interrupts();
    
    if (pressedCountDrv == 4 || pressedCountDrv > 4){
    pressedCountDrv = 0;
  }
    enableHeat(pressedCountDrv, 0); // enable heat, passing heat level and seat (driver).
  }
  lastInterruptTime = interruptTime;
}

void buttonPressedPas(){
  static unsigned long lastInterruptTime = 0;
  unsigned long interruptTime = millis();
  if (interruptTime - lastInterruptTime > interruptTimeMills){
    Serial.println("Passenger Side Button Pressed!");
    
    noInterrupts();
    pressedCountPas++;
    interrupts();
    
    if (pressedCountPas == 4 || pressedCountPas > 4){
    pressedCountPas = 0;
  }
    enableHeat(pressedCountPas, 1); // enable heat, passing heat level and seat (passenger).
  }
  lastInterruptTime = interruptTime;
}
// turns on heat, sets the level of heat, from the side requested
void enableHeat(int level, int side){
  if (side == 0){
    Serial.println("Driver Side Heat:");
    switch(level){
    case 0:
      Serial.println("OFF");
      disableHeat(side);
      break;
    case 1:
      Serial.println("HIGH");
      digitalWrite(outputPin1, LOW);
      digitalWrite(outputPin2, LOW);
      digitalWrite(outputPin0, HIGH);
      break;
    case 2:
      Serial.println("MEDIUM");
      digitalWrite(outputPin0, LOW);
      digitalWrite(outputPin2, LOW);
      digitalWrite(outputPin1, HIGH);
      break;
    case 3:
      Serial.println("LOW");
      digitalWrite(outputPin0, LOW);
      digitalWrite(outputPin1, LOW);
      digitalWrite(outputPin2, HIGH);
      break;
    }
  }
  if (side == 1){
    Serial.println("Passenger Side Heat:");
    switch(level){
    case 0:
      Serial.println("OFF");
      disableHeat(side);
      break;
    case 1:
      Serial.println("HIGH");
      digitalWrite(outputPin4, LOW);
      digitalWrite(outputPin5, LOW);
      digitalWrite(outputPin3, HIGH);
      break;
    case 2:
      Serial.println("MEDIUM");
      digitalWrite(outputPin3, LOW);
      digitalWrite(outputPin5, LOW);
      digitalWrite(outputPin4, HIGH);
      break;
    case 3:
      Serial.println("LOW");
      digitalWrite(outputPin3, LOW);
      digitalWrite(outputPin4, LOW);
      digitalWrite(outputPin5, HIGH);
      break;
    }
  }
}
// turns off heat only on the side requested
void disableHeat(int side){
  switch(side){
    case 0:
      Serial.println("Driver Side Off");
      delay(powerOffTimeMills);
      digitalWrite(outputPin0, LOW);
      digitalWrite(outputPin1, LOW);
      digitalWrite(outputPin2, LOW);
      break;
    case 1:
      Serial.println("Passenger Side Off");
      delay(powerOffTimeMills);
      digitalWrite(outputPin3, LOW);
      digitalWrite(outputPin4, LOW);
      digitalWrite(outputPin5, LOW);
      break;
  }
}


So, I am asking experienced Arduino members for their opinions about this code and potential ways to reduce the amount of lines of code while keeping the Arduino working quickly and waking up when needed.
I would later like to add features like an auto shutoff after a certain amount of time and a fallback feature that reduces the heat from high to medium and then to low after a certain amount of time.

What do you guys think and how should I approach this?
Is there anything I forgot to comment for clarity?

Note: Sketch is not final, there are a few things I need to add to reflect real world conditions (wiring in car, relay needed or not, pins to power led in the a/c panel, etc.

  attachInterrupt(0, buttonPressedDrv, FALLING); // Driver Side Button Interrupt
  attachInterrupt(1, buttonPressedPas, FALLING); // Passenger Side Button Interrupt

You really do NOT need interrupts for human-pressed switched. It REALLY is not necessary to respond within nanoseconds to turn the heat up or down.

I was thinking interrupts to avoid putting too much code on loop. I did try running the buttons on the loop function but a few presses were being lost if both driver side and passenger side were being pressed right after another. A few other things were not working then (one of them being the counter for each side) so I relied on the interrupt because of how quickly it responds.

Also, my end goal is to keep track of the amount of presses as quickly as possible, not how quickly I need the heat to go up or down. I actually need a delayed response there to not overload any circuitry on my 12V line in the car. Not sure how to go about that yet but I am currently working on that.

Using interrupts actually adds work for the Arduino because it has to do the background stuff to manage the interrupt as well as the stuff you program.

Look at the the demo several things at a time and the Thread planning and implementing a program

If you do want to use an interrupt it should be as short as possible. For example

void buttonPressedDrv(){
   pressedCountDrv++;
   drvBtnPressed = true;
}

The main program can check the variable drvBtnPressed to see if a button was pressed, and if it was it should reset the variable to false and then act on the new data.

One problem with a system that relies on counting button presses is providing feedback to the user so s/he knows what is happening.

...R

Oh! Got it, I think I understand what I need to change and improve upon on my code.

It's a bit late here so I hope to post my changes sometime tomorrow and if possible if you guys could look at it once again to see if I am on the right track.

I am loving what I can do with my Arduino and I hope to understand it as quick as possible and get a few projects under my belt.

Edit: Had some time to look at the car and its wiring. Things will actually be a lot simpler than how I have this set up so I will be doing a rewrite to reflect that. The way my car works is pretty simple. If you want something activated, then you ground it (except for a few things here and there, and even those you ground to activate). So, if I want the LED for HIGH, MEDIUM and/or LOW I simply ground that wire.

Will be back once I have the code running and tested and the wiring for the heating portion of the install.

The last few days I have been waiting for a few parts to arrive in the mail (will drive my circuit instead of relays), so I have been putting a bit of time into my code.

As of right now I have the part that handles the button presses (two buttons, one pin always grounded via a 10k resistor - this is because my buttons in the car are 12v when pushed, so a voltage divider to 5v will be needed there. The resistor is to keep it from floating if it does.) and turns on the onboard LED whenever any of the LEDs are on.

This is what I have so far:

/*
  Heated Seat Switching
  
  This sketch handles the logic required to control a 4-stage heated seat module.
  The stages are HIGH, MEDIUM, LOW and OFF. OFF is the default-start state.
  Indication of stages is done via LEDs for HIGH, MEDIUM and LOW.
  Vehicle wiring is ground based and built-in LEDs turn on when grounded.
  
  INPUT:
  Pin 2 & 3
  
  OUTPUT:
  Pin 4, 5 and 6 for Driver side.
  Pin 7, 8 and 9 for Passenger side.
*/

// Dash Buttons to monitor
const int buttonPin[] = {2, 3};

// Output to ULN2003A
const int statusPin[] = {4, 5, 6, 7, 8, 9};

// On-Board LED (on Arduino)
const int onBoardLedPin = 13;

// Timers
const int buttonInterval = 100; // number of millisecs between button readings
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked

// Push Counters/State Change
int buttonPushCounter[] = {0, 0};
int buttonState[] = {0, 0};
int lastButtonState[] = {0, 0};

void setup() {
  
  // Initializing On-Board LED as an output
  pinMode(onBoardLedPin, OUTPUT);
  
  // Initializing Buttons as inputs
  for (int x = 0; x < 2; x++){
  pinMode(buttonPin[x], INPUT);
  }
  
  // Initializing Status Pins as outputs
  for (int x = 0; x < 6; x++){
  pinMode(statusPin[x], OUTPUT);
  }
  
  // capture the latest value of millis()
  currentMillis = millis();
}

void loop() {
  // Reset counters if above a set limit
  resetPushCounter();
  // Lets read the state of each button
  queryButtonState();
  // Turn this on if any part of out system is on
  powerOnIndicator();
}

void resetPushCounter(){
  for (int x = 0; x < 2; x++){
    if (buttonPushCounter[x] == 4){
      buttonPushCounter[x] = 0;
    }
  }
}

void queryButtonState(){
  if (millis() - previousButtonMillis >= buttonInterval) {
    for (int x = 0; x < 2; x++){
      buttonState[x] = digitalRead(buttonPin[x]);
      
      if (buttonState[x] != lastButtonState[x]){
        if (buttonState[x] == HIGH && buttonPin[x] == 2){
          buttonPushCounter[0]++;
        }
        if (buttonState[x] == HIGH && buttonPin[x] == 3){
          buttonPushCounter[1]++;
        }
      }
    }
    previousButtonMillis += buttonInterval;
  }
}

void powerOnIndicator(){
  if (buttonPushCounter[0] != 0 || buttonPushCounter[1] != 0){
    digitalWrite(onBoardLedPin, HIGH);
  } else{
    digitalWrite(onBoardLedPin, LOW);
  }
}

A heck of a lot less lines if I compare this portion of the code with my previous code and should be a lot easier to read and come back to if I need to change/improve something.
Another mention is that I am no longer using interrupts and that unless they are needed when I install it in the vehicle, I will keep it that way.

I think this code is much better, but if you guys see any areas where it can be improved, I am open to suggestions.
I’m thinking of reducing a number of those for loops to make the arduino work a bit faster there, but if it is still counting each button press without a hitch, then I don’t think there is really any need to remove them just yet.

Had the time to write some more code and so I did;
Granted, I removed the debounce part of the code until I can get some more research done and implement it either via hardware or software or both.
For now, here is the code I have come up with:

/*
  Heated Seat Switching
  
  This sketch handles the logic required to control a 4-stage heated seat module.
  The stages are HIGH, MEDIUM, LOW and OFF. OFF is the default-start state.
  Indication of stages is done via LEDs for HIGH, MEDIUM and LOW.
  Vehicle wiring is ground based and built-in LEDs turn on when grounded.
  
  INPUT:
  Pin 2 & 3
  
  OUTPUT:
  Pin 4, 5 and 6 for Driver side.
  Pin 7, 8 and 9 for Passenger side.
*/

// Dash Buttons to monitor
const int buttonPin[] = {2, 3};

// Output to ULN2003A
const int statusPin[] = {4, 5, 6, 7, 8, 9};

// On-Board LED (on Arduino)
const int onBoardLedPin = 13;

// Push Counters/State Change
int buttonPushCounter[] = {0, 0};
int buttonState[] = {0, 0};
int lastButtonState[] = {0, 0};

// Timers
const int buttonInterval = 5; // number of millisecs between button readings

void setup() {
  
  // Initializing On-Board LED as an output
  pinMode(onBoardLedPin, OUTPUT);
  
  // Initializing Buttons as inputs
  for (int x = 0; x < 2; x++){
  pinMode(buttonPin[x], INPUT);
  }  
  
  // Initializing Status Pins as outputs
  for (int x = 0; x < 6; x++){
  pinMode(statusPin[x], OUTPUT);
  }
}

void loop() {
  // Reset counters if above a set limit
  resetPushCounter();
  // Lets read the state of each button
  queryButtonState();
  // Now we can get some heat going
  heatStatus();
}

void resetPushCounter(){
  for (int x = 0; x < 2; x++){
    if (buttonPushCounter[x] == 4){
      buttonPushCounter[x] = 0;
    }
  }
}

void queryButtonState(){
  for (int x = 0; x < 2; x++){
    buttonState[x] = digitalRead(buttonPin[x]);
    
  if (buttonState[x] != lastButtonState[x]){
    
      if (buttonState[x] == HIGH && buttonPin[x] == 2){
        buttonPushCounter[0]++;
      }
      if (buttonState[x] == HIGH && buttonPin[x] == 3){
        buttonPushCounter[1]++;
      }
    }
    lastButtonState[x] = buttonState[x];
  }
}

void heatStatus(){
  if (buttonPushCounter[0] != 0 || buttonPushCounter[1] != 0){
    powerOn();
  }
  else{
    powerOff();
  }
}

void powerOn(){
  digitalWrite(onBoardLedPin, HIGH);
  enableHeat();
}

void powerOff(){
  disableHeat();
  digitalWrite(onBoardLedPin, LOW);
}

void disableHeat(){
  for (int x = 0; x < 6; x++){
  digitalWrite(statusPin[x], LOW);
  }
}

void enableHeat(){
  for (int x = 0; x < 2; x++){
    heatLevel(buttonPushCounter[x], x);
  }
}

void heatLevel(int level, int side){
  if (side == 0){
    switch(level){
      case 0:
        digitalWrite(statusPin[0], LOW);
        digitalWrite(statusPin[1], LOW);
        digitalWrite(statusPin[2], LOW);
        break;
      case 1:
        digitalWrite(statusPin[1], LOW);
        digitalWrite(statusPin[2], LOW);
        digitalWrite(statusPin[0], HIGH);
        break;
      case 2:
        digitalWrite(statusPin[0], LOW);
        digitalWrite(statusPin[2], LOW);
        digitalWrite(statusPin[1], HIGH);
        break;
      case 3:
        digitalWrite(statusPin[0], LOW);
        digitalWrite(statusPin[1], LOW);
        digitalWrite(statusPin[2], HIGH);
        break;
      }
  }
  if (side == 1){
  switch(level){
    case 0:
        digitalWrite(statusPin[3], LOW);
        digitalWrite(statusPin[4], LOW);
        digitalWrite(statusPin[5], LOW);
      break;
    case 1:
      digitalWrite(statusPin[4], LOW);
      digitalWrite(statusPin[5], LOW);
      digitalWrite(statusPin[3], HIGH);
      break;
    case 2:
      digitalWrite(statusPin[3], LOW);
      digitalWrite(statusPin[5], LOW);
      digitalWrite(statusPin[4], HIGH);
      break;
    case 3:
      digitalWrite(statusPin[3], LOW);
      digitalWrite(statusPin[4], LOW);
      digitalWrite(statusPin[5], HIGH);
      break;
    }
  }
}

Seems to be working (except for bounce and debounce of course), would love to shrink the case and switch statements a bit, can’t think of a simpler way at the moment. But for now, I will go back to researching more debouncing techniques.

What do you guys think of the code so far?

Another one? Why the heck not?

So after a bit of bouncing on my bed, I have added a simple debounce to my sketch.
What do you guys think of the code now?

/*
  Heated Seat Switching
  
  This sketch handles the logic required to control a 4-stage heated seat module.
  The stages are HIGH, MEDIUM, LOW and OFF. OFF is the default-start state.
  Indication of stages is done via LEDs for HIGH, MEDIUM and LOW.
  Vehicle wiring is ground based and built-in LEDs turn on when grounded.
  
  INPUT:
  Pin 2 & 3
  
  OUTPUT:
  Pin 4, 5 and 6 for Driver side.
  Pin 7, 8 and 9 for Passenger side.
*/

// Dash Buttons to monitor
const int buttonPin[] = {2, 3};

// Output to ULN2003A
const int statusPin[] = {4, 5, 6, 7, 8, 9};

// On-Board LED (on Arduino)
const int onBoardLedPin = 13;

// Push Counters/State Change
int buttonPushCounter[] = {0, 0};
int buttonState[] = {0, 0};
int lastButtonState[] = {0, 0};

// Timers
long lastDebounceTime = 0; 
long debounceDelay = 5; // number of millisecs between button readings

void setup() {
  
  // Initializing On-Board LED as an output
  pinMode(onBoardLedPin, OUTPUT);
  
  // Initializing Buttons as inputs
  for (int x = 0; x < 2; x++){
  pinMode(buttonPin[x], INPUT);
  }  
  
  // Initializing Status Pins as outputs
  for (int x = 0; x < 6; x++){
  pinMode(statusPin[x], OUTPUT);
  }
}

void loop() {
  // Reset counters if above a set limit
  resetPushCounter();
  // Lets read the state of each button
  queryButtonState();
  // Now we can get some heat going
  toggleHeat();
}

void resetPushCounter(){
  for (int x = 0; x < 2; x++){
    if (buttonPushCounter[x] == 4){
      buttonPushCounter[x] = 0;
    }
  }
}

void queryButtonState(){
  for (int x = 0; x < 2; x++){
    buttonState[x] = digitalRead(buttonPin[x]);
    if ((millis() - lastDebounceTime) > debounceDelay){
    
      if (buttonState[x] != lastButtonState[x]){
        
          if (buttonState[x] == HIGH && buttonPin[x] == 2){
            buttonPushCounter[0]++;
          }
          if (buttonState[x] == HIGH && buttonPin[x] == 3){
            buttonPushCounter[1]++;
          }
        }
      lastButtonState[x] = buttonState[x];
      lastDebounceTime = millis();
    }
  }
}

void toggleHeat(){
  if (buttonPushCounter[0] != 0 || buttonPushCounter[1] != 0){
    powerOn();
  }
  else{
    powerOff();
  }
}

void powerOn(){
  digitalWrite(onBoardLedPin, HIGH);
  enableHeat();
}

void powerOff(){
  disableHeat();
  digitalWrite(onBoardLedPin, LOW);
}

void disableHeat(){
  for (int x = 0; x < 6; x++){
  digitalWrite(statusPin[x], LOW);
  }
}

void enableHeat(){
  for (int x = 0; x < 2; x++){
    heatLevel(buttonPushCounter[x], x);
  }
}

void heatLevel(int level, int side){
  if (side == 0){
    switch(level){
      case 0:
        digitalWrite(statusPin[0], LOW);
        digitalWrite(statusPin[1], LOW);
        digitalWrite(statusPin[2], LOW);
        break;
      case 1:
        digitalWrite(statusPin[1], LOW);
        digitalWrite(statusPin[2], LOW);
        digitalWrite(statusPin[0], HIGH);
        break;
      case 2:
        digitalWrite(statusPin[0], LOW);
        digitalWrite(statusPin[2], LOW);
        digitalWrite(statusPin[1], HIGH);
        break;
      case 3:
        digitalWrite(statusPin[0], LOW);
        digitalWrite(statusPin[1], LOW);
        digitalWrite(statusPin[2], HIGH);
        break;
      }
  }
  if (side == 1){
  switch(level){
    case 0:
        digitalWrite(statusPin[3], LOW);
        digitalWrite(statusPin[4], LOW);
        digitalWrite(statusPin[5], LOW);
      break;
    case 1:
      digitalWrite(statusPin[4], LOW);
      digitalWrite(statusPin[5], LOW);
      digitalWrite(statusPin[3], HIGH);
      break;
    case 2:
      digitalWrite(statusPin[3], LOW);
      digitalWrite(statusPin[5], LOW);
      digitalWrite(statusPin[4], HIGH);
      break;
    case 3:
      digitalWrite(statusPin[3], LOW);
      digitalWrite(statusPin[4], LOW);
      digitalWrite(statusPin[5], HIGH);
      break;
    }
  }
}
  // Now we can get some heat going
  heatStatus();

So, the verb in this name is heat. That means that the noun is status. Heating the status doesn't make sense. Who cares if the status is hot or cold?

PaulS:   // Now we can get some heat going   heatStatus();

So, the verb in this name is heat. That means that the noun is status. Heating the status doesn't make sense. Who cares if the status is hot or cold?

Really? What do you suggest calling it?

Oh wait, I know. Check post above for edit.

avluis: What do you guys think of the code now?

That's easy to answer - but I need one more piece of information.

Does the code work?

If it does it is good. If it doesn't it's not so good.

The Arduino is a great system for learning-by-doing

...R

Robin2: That's easy to answer - but I need one more piece of information.

Does the code work?

If it does it is good. If it doesn't it's not so good.

The Arduino is a great system for learning-by-doing

...R

Yep! I had a bit of time today to run it over and over. Resetting it via the onboard button. Unplugging it, etc. So far it all seems to work as intended.

I have learned quite a bit from my UNO, electronics-wise. Loving what I can do with it. So I can't wait to think of another project and get started on that.

Thanks for the help so far.

I will keep on improving this code (I would love to shrink down the switch(case) a bit, but for now, it does what it needs to do.

avluis:
I would love to shrink down the switch(case) a bit,

What about this

void heatLevel(int level, int side){
	if (side == 0) {
		lowPin = 0;
	}
	else {
		lowPin = 3
	}
        highPin = lowPin + 2;
	
	for (n = lowPin; n <= highPin; n++) {
		digitalWrite(statusPin[n], LOW);
		if (level > 0) {
			digitalWrite(statusPin[highPin], HIGH);
		}
	}
	
}

…R

@Robin2

Thanks for that input. That bit of code got me thinking, and so I came up with this:

void heatLevel(int level, int side){
  if (side == 0){
    if (level != 0){
      for (int n = 0; n < 3; n++){
        digitalWrite(statusPin[n], LOW);
        if (level > 0){
          digitalWrite(statusPin[level] - 1, HIGH);
        }
      }
    } else {
      for (int n = 0; n < 3; n++){
      digitalWrite(statusPin[n], LOW);
      }
    }
  } else {
    if (level != 0){
      for (int n = 0; n < 3; n++){
        digitalWrite(statusPin[n] + 3, LOW);
        if (level > 0){
          digitalWrite(statusPin[level] + 2, HIGH);
        }
      }
    } else {
      for (int n = 0; n < 3; n++){
      digitalWrite(statusPin[n] + 3, LOW);
      }
    }
  }
}

And here is what the sketch looks like now:

/*
  Heated Seat Switching
  
  This sketch handles the logic required to control a 4-stage heated seat module.
  The stages are HIGH, MEDIUM, LOW and OFF. OFF is the default-start state.
  Indication of stages is done via LEDs for HIGH, MEDIUM and LOW.
  Vehicle wiring is ground based and built-in LEDs turn on when grounded.
  
  INPUT:
  Pin 2 & 3
  
  OUTPUT:
  Pin 4, 5 and 6 for Driver side.
  Pin 7, 8 and 9 for Passenger side.
*/

// Dash Buttons to monitor
const int buttonPin[] = {2, 3};

// Output to ULN2003A
const int statusPin[] = {4, 5, 6, 7, 8, 9};

// On-Board LED (on Arduino)
const int onBoardLedPin = 13;

// Push Counters/State Change
int buttonPushCounter[] = {0, 0};
int buttonState[] = {0, 0};
int lastButtonState[] = {0, 0};

// Timers
long lastDebounceTime = 0;
// adjust this if getting button bounce
long debounceDelay = 6;

void setup() {
  
  // Initializing On-Board LED as an output
  pinMode(onBoardLedPin, OUTPUT);
  
  // Initializing Buttons as inputs
  for (int x = 0; x < 2; x++){
  pinMode(buttonPin[x], INPUT);
  }  
  
  // Initializing Status Pins as outputs
  for (int x = 0; x < 6; x++){
  pinMode(statusPin[x], OUTPUT);
  }
}

void loop() {
  // Lets read the state of each button
  queryButtonState();
  // Reset counters if above a set limit
  resetPushCounter();
  // Now we can get some heat going
  toggleHeat();
}

void resetPushCounter(){
  for (int x = 0; x < 2; x++){
    if (buttonPushCounter[x] == 4){
      buttonPushCounter[x] = 0;
    }
  }
}

void queryButtonState(){
  for (int x = 0; x < 2; x++){
    buttonState[x] = digitalRead(buttonPin[x]);
    if ((millis() - lastDebounceTime) > debounceDelay){
    
      if (buttonState[x] != lastButtonState[x]){
        
          if (buttonState[x] == HIGH && buttonPin[x] == 2){
            buttonPushCounter[0]++;
          }
          if (buttonState[x] == HIGH && buttonPin[x] == 3){
            buttonPushCounter[1]++;
          }
        }
      lastButtonState[x] = buttonState[x];
      lastDebounceTime = millis();
    }
  }
}

void toggleHeat(){
  if (buttonPushCounter[0] != 0 || buttonPushCounter[1] != 0){
    powerOn();
  }
  else{
    powerOff();
  }
}

void powerOn(){
  digitalWrite(onBoardLedPin, HIGH);
  enableHeat();
}

void powerOff(){
  disableHeat();
  digitalWrite(onBoardLedPin, LOW);
}

void disableHeat(){
  for (int x = 0; x < 6; x++){
  digitalWrite(statusPin[x], LOW);
  }
}

void enableHeat(){
  for (int x = 0; x < 2; x++){
    heatLevel(buttonPushCounter[x], x);
  }
}

void heatLevel(int level, int side){
  if (side == 0){
    if (level != 0){
      for (int n = 0; n < 3; n++){
        digitalWrite(statusPin[n], LOW);
        if (level > 0){
          digitalWrite(statusPin[level] - 1, HIGH);
        }
      }
    } else {
      for (int n = 0; n < 3; n++){
      digitalWrite(statusPin[n], LOW);
      }
    }
  } else {
    if (level != 0){
      for (int n = 0; n < 3; n++){
        digitalWrite(statusPin[n] + 3, LOW);
        if (level > 0){
          digitalWrite(statusPin[level] + 2, HIGH);
        }
      }
    } else {
      for (int n = 0; n < 3; n++){
      digitalWrite(statusPin[n] + 3, LOW);
      }
    }
  }
}

So, while making use of nested for-if statements is not always the best way to go about (due to speed concerns or some other technical reason), it solves my issue with the staggering amount of lines to mess up in a switch statement, specially the size mine was.

I also moved the call to function resetPushCounter() under loop() so as to have my counter update at the right time (where it was before, it was switching so fast that it was switching on my next pin before it had time to reset). Where it is now makes it work properly with the updated heatLevel() function.

I will be testing this as much as I can to make sure it is as reliable as the switch statements, but so far it is working perfectly. There are a few things I can probably rewrite to make it easier to read as well.

So thanks again!
If you see any other areas to improve this code (e.g.; is it better to use byte here instead of int, etc), I am all ears.

avluis: @Robin2

Thanks for that input. That bit of code got me thinking, and so I came up with this:

Looks a lot longer than mine - or did I omit something?

...R

Robin2: Looks a lot longer than mine - or did I omit something?

...R

It just required a few additional lines so that the pins would turn off when level was 0 on an individual side, then separating the sides themselves. That got it working across both sides. I sort of preferred it that way (where I could tell what side belong to what) and just went with that.

I'm planning on simplifying this function in the future since it can be written in a much simpler way (driver side = 0, any time heatLevel is > 2, side = 1 (passenger side)) but I need to modify a few other functions to account for that. That should reduce the overall size even further but it won't be as simple as it is now.

avluis: It just required a few additional lines so that the pins would turn off when level was 0 on an individual side,

I didn't know that level was intended to select a PIN. I still think it could be much shorter than your version - but if it works, it works. If it's not broke don't fix it :)

...R

If it's not broke don't fix it

If it's not broke, take it apart and see if you can modify it so it works better :art:

Robin2: I didn't know that level was intended to select a PIN. I still think it could be much shorter than your version - but if it works, it works. If it's not broke don't fix it :)

...R

LarryD: If it's not broke, take it apart and see if you can modify it so it works better :art:

While both of these statements are true (in an everyday situation), I would say when it comes to programming to always have a mindset of improvement. To take the time required to either improve my current programming skills (techniques) but to not shoot too far out there and get stuck guessing.

For now, this code can be considered prototype, as it works beautifully at the moment. I do have to go over several parts of it with the serial debugger and make sure it is not stepping out of line nor working harder than it should - but at the moment, it does the job and it does it quite well.

And yes, as I learn to program better, gain experience with more coding and research, coming back to prior code and improving it has never hurt.

And that, I look forward to.

Yes, I often go back an see if I can improve, document and rearrange things.