Arduino Micro - driving relays for seat warmer

I'm looking for some additional help with a project I'm working on, the gist of it is that I want to use an Arduino Micro to activate a pair of seat warmers.

I have an Arduino Micro, I have a 5v 2 relay module (with optocouplers), and 2 momentary switches that I need to use for the project (they belong to the car, so the whole plan is to use these rear window defrost switches to power seat warmers instead, so it will look completely stock.

The problem is that everything is working, but not all the time. Sometimes it takes several button pushes for it to register, on either side. Is this a limitation of the Micro?

I'm basically just using the edge detect code, duplicated to run two buttons (driver and passenger) - here's the lite version of the code (remarks and output stripped):

const int driverButtonPin = 5;
const int passengerButtonPin = 6;
const int driverRelayPin = 7;
const int passengerRelayPin = 8;

int driverButtonPushCounter = 0;
int passengerButtonPushCounter = 0;
int driverButtonState = 0;
int passengerButtonState = 0;
int driverLastButtonState = 0;
int passengerLastButtonState = 0;

void setup() {
pinMode(driverButtonPin, INPUT);
pinMode(passengerButtonPin, INPUT);
pinMode(driverRelayPin, OUTPUT);
pinMode(passengerRelayPin, OUTPUT);
Serial.begin(9600);
}

void loop() {
driverButtonState = digitalRead(driverButtonPin);
if (driverButtonState != driverLastButtonState) {
if (driverButtonState == HIGH) {
driverButtonPushCounter++;
}
}
driverLastButtonState = driverButtonState;
passengerButtonState = digitalRead(passengerButtonPin);
if (passengerButtonState != passengerLastButtonState) {
if (passengerButtonState == HIGH) {
passengerButtonPushCounter++;
}
}
passengerLastButtonState = passengerButtonState;

if (driverButtonPushCounter % 2 == 0) {
digitalWrite(driverRelayPin, HIGH);
} else {
digitalWrite(driverRelayPin, LOW);
}

if (passengerButtonPushCounter % 2 == 0) {
digitalWrite(passengerRelayPin, HIGH);
} else {
digitalWrite(passengerRelayPin, LOW);
}
}

How are these wired up?

pinMode(driverButtonPin, INPUT);
pinMode(passengerButtonPin, INPUT);

Each of the input pins is connected to output of the switch and also to ground through 10k ohm resistors.

I'm also discovering that if I engage both relays at the same time when I'm not plugged into USB and plugged in using 12vdc on the RAW input, the board resets.

Pretty sure the code works, because if I comment out either set of code, it seems to work just fine. Is it just a limitation of the board? Like it's only 'listening' for the edge detect half the time, because I'm doing two of them?

It looks like you're trying to activate the relay when the pushbutton is released then pressed then released. It's not a typical strategy in electronics; I'm guessing in another life you've done some software UI programming. But, back to the point, I suspect your problem is that you're not debouncing your switches.

Definitely more programming than prototyping. :slight_smile:

But this is just the canned edge detect code included in the Arduino programming environment, modified to add a second button and to activate a relay instead of an LED. Counter Starts at 0, if it's not divisible by 2 then relay is on - button starts off (open), so when it goes from low to high then back to low (pressed and released) the counter should increment.

What do I need to do to debounce the switch?

Sorry, didn't see the hyperlinks. Thank you.

Is there a better way to toggle a relay with a momentary pushbutton? I just found the first thing I could find that worked.

One question you need to consider, is this switch a "momentary pushbutton" ?

And if you are getting resets when you switch both on at once, that tends to suggest that your power supply is overloaded, at least temporarily.

Richter12x2:
Each of the input pins is connected to output of the switch and also to ground through 10k ohm resistors.

For protection against noise, i would recommend wiring the buttons to use pull-up resistors (1K) to +5V (Arduino supply voltage) like this:

// Per.

I finally have it working, despite one minor setback - the Micro I'm using doesn't have enough VCC to hold both relays on, unless I'm plugged into BOTH 12v on RAW and have the micro-usb plugged in. That's easy enough though.

The debounce code from earlier was the trick, but it didn't work as advertised. Here's the code from the example:

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // set initial LED state
  digitalWrite(ledPin, ledState);
}

void loop() {
  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button 
  // (i.e. the input went from LOW to HIGH),  and you've waited 
  // long enough since the last press to ignore any noise:  

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {

It didn't look right to me, and after adding some debug outs into the code and watching with the serial monitor, it turns out to be accurate.

In the line "lastDebounceTime = millis();" you set the debounce timer to current time. Then in the very next sentence, you're asking what Current Time - lastDebounceTime is. Well guess what? It's 0. Every time.

But if you put the comparison (millis - lastDebounceTime > delay) bit at the beginning, then execute your code, then set the lastDebounceTime with an 'else' statement, then it works very well.

Here's the complete working code, in case it helps someone else in the future:

/*
Modified to toggle relay on and off on alternating button pushes

Original Text Follows
  State change detection (edge detection)
 	
 Often, you don't need to know the state of a digital input all the time,
 but you just need to know when the input changes from one state to another.
 For example, you want to know when a button goes from OFF to ON.  This is called
 state change detection, or edge detection.
 
 This example shows how to detect when a button or button changes from off to on
 and on to off.
 	
 The circuit:
 * pushbutton attached to pin 5 from +5V
 * 10K resistor attached to pin 5 from ground
 * LED attached from pin 13 to ground (or use the built-in LED on
   most Arduino boards)
 
 created  27 Sep 2005
 modified 30 Aug 2011
 by Tom Igoe

This example code is in the public domain.
 	
 http://arduino.cc/en/Tutorial/ButtonStateChange
 
 */

// this constant won't change:
const int driverButtonPin = 5;     // the pin that the pushbutton is attached to
const int passengerButtonPin = 6;
const int driverRelayPin = 7;       // the pin that the LED is attached to
const int passengerRelayPin = 8;

// Variables will change:
int driverButtonPushCounter = 0;   // counter for the number of button presses
int passengerButtonPushCounter = 0;
int driverButtonState = 0;         // current state of the button
int passengerButtonState = 0;
int driverLastButtonState = 0;     // previous state of the button
int passengerLastButtonState = 0;

long driverDebounceTime = 0; //debounce timer
long passengerDebounceTime = 0;
long debounceDelay = 50; //increase if flickers

void setup() {
  // initialize the button pin as a input:
  pinMode(driverButtonPin, INPUT);
  pinMode(passengerButtonPin, INPUT);
  // initialize the LED as an output:
  pinMode(driverRelayPin, OUTPUT);
  pinMode(passengerRelayPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  int driverReading = digitalRead(driverButtonPin);
  if (driverReading != driverLastButtonState) {
    if ((millis() - driverDebounceTime) > debounceDelay) {
      Serial.println("Step1");
      driverButtonState = driverReading;
      // compare the buttonState to its previous state
      if (driverButtonState != driverLastButtonState) {
        Serial.println("Step2");
        // if the state has changed, increment the counter
        if (driverButtonState == HIGH) {
          Serial.println("Step3");
          // if the current state is HIGH then the button
          // wend from off to on:
          driverButtonPushCounter++;
          Serial.println("on");
          Serial.print("driver:  ");
          Serial.println(driverButtonPushCounter);
        } 
        else {
          // if the current state is LOW then the button
          // wend from on to off:
          Serial.println("off"); 
        }
      }
    }
 }
 else {
   driverDebounceTime = millis();  
   }
  // read the pushbutton input pin:
  int passengerReading = digitalRead(passengerButtonPin);
  if (passengerReading != passengerLastButtonState) {
    if ((millis() - passengerDebounceTime) > debounceDelay) {
      Serial.println("Step1");
      passengerButtonState = passengerReading;
      // compare the buttonState to its previous state
      if (passengerButtonState != passengerLastButtonState) {
        Serial.println("Step2");
        // if the state has changed, increment the counter
        if (passengerButtonState == HIGH) {
          Serial.println("Step3");
          // if the current state is HIGH then the button
          // wend from off to on:
          passengerButtonPushCounter++;
          Serial.println("on");
          Serial.print("passenger:  ");
          Serial.println(passengerButtonPushCounter);
        } 
        else {
          // if the current state is LOW then the button
          // wend from on to off:
          Serial.println("off"); 
        }
      }
    }
 }
 else {
   passengerDebounceTime = millis();  
   } // save the current state as the last state, 
  //for next time through the loop
  driverLastButtonState = driverButtonState;
  passengerLastButtonState = passengerButtonState;

  
  // turns on the LED every four button pushes by 
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of 
  // the division of two numbers:
  if (driverButtonPushCounter % 2 == 0) {
    digitalWrite(driverRelayPin, HIGH);
  } else {
   digitalWrite(driverRelayPin, LOW);
  }
   
    if (passengerButtonPushCounter % 2 == 0) {
    digitalWrite(passengerRelayPin, HIGH);
  } else {
   digitalWrite(passengerRelayPin, LOW);
  }
}

the Micro I'm using doesn't have enough VCC to hold both relays on, unless I'm plugged into BOTH 12v on RAW and have the micro-usb plugged in.

There are issues with this. You really should not be trying to run the arduino from two different power sources at the same time.

If your relay module really does have optocouplers, then the arduino should not be overloaded driving it. How are you obtaining the relay activating coil current ? I suspect you have wired up the relays wrongly somehow.

It's possible it's wired incorrectly, but also was the only way to get it to work.

Raw voltage into Arduino is 12VDC (from car)

Relay board expects 5VDC, so Relay board VCC (factory installed) is connected to Arduino VCC (converted 5VDC).
Ground from Relay board is connected to Ground on Arduino
Jumper to VCC-JD (relay coils) is connected to VCC, as was preinstalled.

Bad arrangement - the relays are pulling too much current for the tiny regulator
on the Micro.

You must use a separate 7805 regulator on a heatsink (or DC-DC converter) to provide
DC power for the relay windings.

Your real problem is failing to get a 12V operated relay in the first place - then it could
be driven from the car's 12V directly, simple.

What current do the heater(s) take? Can a logic-level MOSFET be used instead (even
simpler if you choose a big enough one).

MarkT:
Your real problem is failing to get a 12V operated relay in the first place - then it could
be driven from the car's 12V directly, simple.

If you check my other topic posted (Arduino Uno, Relay shield - relays not picking - Motors, Mechanics, Power and CNC - Arduino Forum), I actually did do that first, with a 12v relay board - however, the optocouplers on the 12v relay board ALSO require 12v (or at least 9) to activate, which means you'll be grounding > 5VDC through the actuating pin on the Arduino, which I was told will burn it up, by you, it appears. :smiley: . Hence, 5v relay board

What current do the heater(s) take? Can a logic-level MOSFET be used instead (even
simpler if you choose a big enough one).

The load of the heaters doesn't go through the board, only through the switched side of the relay - hot wire in from 12vdc to one post of the relay then out to the heaters on the other. The only load on the Arduino is the supposedly tiny relay coil activation current. (and the pulse of the input buttons).
I'm sure there are many other ways it could be done, if I were an EE. Technically, you never NEED an Arduino, if you design all of your circuits specifically for their intended purpose. I could use a pair of relays connected to the button in push pull so that the second relay holds and releases the first on button presses, but that leads to a block of 4 relays to handle two seats. I could also have used the original latching timer relays, each of which is more expensive than an Arduino. I'm pretty sure I could have also used SCRs, but the time it would take me to learn enough electronics design to properly create a circuit that I'd trust enough to leave in my wife's car unattended was deemed to be greater than the amount of time it would take to get the solution working with an Arduino.

See http://arduino-info.wikispaces.com/ArduinoPower#OI for help on wiring it using the optical isolation. With that scheme you're powering the relays with the car's 12V and it only needs ~2ma from the Arduino, per relay.

I've read through the article before, and just read it again. Removing the jumper and adding power via VCC-JD doesn't actually solve the problem that the relays themselves are still 5v actuated, and 12v would likely damage them, so I still need a regulated 5vdc signal to power the coil side of the relays, and don't have access to one in 12v car without having to design a new regulated circuit.

This would however be useful IF the 12v relay boards (supposed to be for Arduino, but evidently not without an additional power stage) used 5v opto-couplers. However, since the 12v relay boards use 12v optocouplers, then the arduino can't switch the optocouplers on the 12v boards without additional circuitry (tested) and I can't use 12v to drive the relay coils on the 5vdc relay boards.

So I'm going to go back to the question - other than 'you shouldn't have to do that', what is the issue with using 12vdc into RAW and then plugging in the microusb cable? It seems like the Arduino is designed to operate like that, otherwise you would never be able to use USB communication when connected to an outside power source.

Would you be willing to remove/change the current limiting resistor to the optocoupler so it will work with 5V? The resistor shown in your eBay link (other thread) shows "103" (10K) so just by sticking another 10K resistor on top of it / in parallel you should get enough current to activate the optocoupler at 5V. You could test this non-destructively just by holding the resistor across the SMD resistor's contacts.

Regarding 12V into "RAW", the regulator is still regulating that to 5V so it's not going to solve your problem. What you would need is an external 5V regulator -- one that can handle the current to drive the relays -- and then connect that to the 5V on the micro. With respect to the "you can't do that" part it would work with boards like an Uno but I'm not sure the micro has the same protection.

Well like I said, it works fine as long as the USB is connected - only when it's on 12v power does it restart the board once both relays are latched.

I haven't tried powering it with ONLY the USB cable yet