HELP - program inconsistency

I'm trying to turn on a relay and an led at the same time in my aquarium controller code and can't seem to get it to work. I'm using the arduino MEGA R3 running arduino 1.0. The LED is on pin 36 and the relay is on pin 52. When I test all the relays using the blink function, everything works fine, but when I combine it with my code it works intermittently. Once I get it working, I add whatever changes later in the program to another led/relay pair, then it stops working. So it's not a connection problem because I didn't touch anything. It shouldn't be in the code because the changes were farther down in the loop. I though it was a current draw problem, so I removed the led part of the code and the same thing happend (worked next compile, added the changes to later part of code, then failed next recompile).

The only thing I haven't tried is a temperature problem so I have it powered off right now.I also have an ethernet shield and RTC module hooked up if that matters. Using pins 20-21 for serial comm
Here's the code:

//**   AUTO-TOP-OFF  ***************************  Water is added from Kalk reactor   **************************

  int ato_level = rangefinderDist(SRF04_US);
  int kalk_level = rangefinderDist(Parallax);
    Serial.print(ato_level); 
    Serial.print("   "); 
    Serial.println(kalk_level);
    delay(100);

  //Serial.print(hour); Serial.println(ato_hour);

  if ((ato_level > ato_max &&  hour >= ato_hour)||(ato_level > ato_max && ato_day!=day && hour>=24-ato_hour)) {
    ato_day = day;
    //while (abs(ato_prev-ato_level) <= 3) { // look for change in water level
    // add counter in case of fault or for loop x numebr of times
    // add part that only adds h2o if reading is different that 1 from previous reading
    if (hour < (24-ato_freq)){ // limiting the freqency of ato fill ups
      ato_hour = hour+ato_freq;
      //Serial.println(ato_hour);
    }
    else   {
      ato_hour = 24-hour+ato_freq;
    }
    Serial.print("Next ato hour - "); 
    Serial.print(ato_hour); 
    Serial.print("  Day - ");  
    Serial.println(day);

    //digitalWrite(ato, HIGH);   // TURN ON ATO
    digitalWrite(ato, LOW);
    delay(100);
    digitalWrite(ato, HIGH);
//    delay(100);
//    digitalWrite(ato_light, HIGH);
    delay(ato_time);

    Serial.print("Water level - "); 
    Serial.println(ato_level);
    //Serial.println("add water");
    Serial.print(hour); 
    Serial.print(':'); 
    Serial.print(minute); 
    Serial.print(':'); 
    Serial.println(second);
    //}
    ato_prev  = ato_level;
  }
  //digitalWrite(ato, LOW); // TURN OFF ATO
  digitalWrite(ato,LOW);
  digitalWrite(ato_light, LOW);



  //**   Refill Kalk resevouir  *************************************************************************

  if ((kalk_level > kalk_max &&  hour >= kalk_hour)||(kalk_level > kalk_max && kalk_day!=day && hour>=24-kalk_hour)) {
    kalk_day = day;
    //while (abs(kalk_prev-kalk_level) <= 3) { // look for change in water level
    // add counter in case of fault or for loop x numebr of times
    // limiting the freqency of kalk fill ups
    if (hour < (24-kalk_freq)){
      kalk_hour = hour+kalk_freq;
      //Serial.println(kalk_hour);
    }
    else   {
      kalk_hour = 24-hour+kalk_freq;
    }
    Serial.print("Next kalk fill up hour - "); 
    Serial.print(kalk_hour); 
    Serial.print("  Day - "); 
    Serial.println(day);

    //digitalWrite(kalk, HIGH);   // TURN ON kalk
    digitalWrite(kalk, LOW);
    delay(100);
    digitalWrite(kalk, HIGH);
//    delay(100);
//    digitalWrite(kalk_light, HIGH);
    delay(kalk_time);

    Serial.print("Kalk Reactor level - ");
    Serial.println(kalk_level);
    //Serial.println("add water");
    Serial.print(hour); 
    Serial.print(':'); 
    Serial.print(minute); 
    Serial.print(':'); 
    Serial.println(second);
    //}
    kalk_prev  = kalk_level;
  }
  //digitalWrite(kalk, LOW); // TURN OFF ATO
  digitalWrite(kalk,LOW);
  digitalWrite(kalk_light, LOW);

Test code, works every time:

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  for(int pin = 48; pin<53; pin++){
  pinMode(pin, OUTPUT);
  }
pinMode(13, OUTPUT);  
}

void loop() {
  for(int pin = 48; pin<53; pin++){
  digitalWrite(pin, HIGH);
  }
digitalWrite(13, HIGH);// set the LED on
  delay(1000);              // wait for a second
    for(int pin = 48; pin<53; pin++){
  digitalWrite(pin, LOW);    // set the LED off
    }
  digitalWrite(13, LOW);
  delay(1000);              // wait for a second
}

How are you connecting the relays?

pins 48-52 are connected the the 5 relays all have common ground. There's a cable that goes between the relay box and the box for the board. No power is going through the relay atm, using the led on the relay to judge functionality.

So the relays are (normally) connected direct to the IO pins? No drive transistors? Do you have any back-emf absorbing diodes attached?

Nope, very straightforward setup. Just tried another cable, same results.

Well, unless they're solid-state relays, there's a good chance you may have fried your Arduino. This could be for one of two reasons:

  1. The relays draw too much current. The outputs are limited to 40mA each. If you try and draw more than that you could kill the output driver.
  2. The relays are inductive devices. When they switch they generate high negative voltages. These are often fatal to delicate little microcontrollers.

You should always connect your relays up like this:

The transistor can handle a much higher current than the Arduino, and the diode will effectively short circuit the negative voltages, removing them from harms way.

They are SS relays and the board still turns them on using the blink function I posted. When I combine it with my controller program, I get intermittent results.

For reasons I can't explain, everything works. I had switched the indicator light and relay pins, it worked, switched them back to the way they are supposed to go and it works. Gremlins?

I'd say it's probably either electrical interference, or a logic fault in your sketch.

Since I can't see either the hardware or software, it's impossible to guess.

You don't seem to have done anything that would be expected to cure the problem, so most likely you haven't cured it - it's just hiding. Unrepeatable problems are the hardest ones to solve.

So I think I solved it. The board was sending power to the relay pin, but not enough for the relay to switch (10mA threshold). I checked it my replacing the relay with an led, it lit up, but only barley. I had the problem on pins 51 and 50. Pin 49 works fine, but it could be that the pin 49 relay has a lower threshold than the ones attached to 50 and 51.

What really baffles me is that when I run my modified blink code, all the relays switch simultaneously, but I can't get them to switch individually in my aquarium controller code. Can anybody explain that?

Read this before posting a programming question

Hint: your code please.

Stopped working today :frowning:

The code is lengthy, but here it is:

/*
AQUARIUM CONTROLER MAIN FUNCTION
 */
#include <Wire.h>
#include <OneWire.h>
#include <DS1307new.h>
#include <LiquidTWI.h>

#define ato_dist_in 2       // Pin to receive echo pulse
#define ato_dist 3     // Pin to send trigger pulse
#define kalk_dist 53  // kalk US level

#define kalk 49        // refill kalk resevoir
#define fan 26         // Fan for cooling
#define light_A 240     // Main light
#define light_B 250    // Main light
#define light_fan 260   // Light fan
#define cal_doser 24   // Calcium dosing pump
#define alk_doser 25   // Alkalinity dosin pump
#define ato 52         // dispence h2o from kalk resevouir
#define skimmer 300     // Skimmer
#define heater 310      // Heater
#define return_pump 320 // Return Pump

// BUTTONS!!
#define feed 400      // Heater
#define lcd_on 410      // Heater
#define ato_light 36
#define kalk_light 38

LiquidTWI lcd(0); // sets LCD address

char SRF04_US = 'S'; //SRF04 ultra sonic rangefinder
char Parallax = 'P'; // Parallax ultra sonic rangefinder

int DS18S20_Pin = 5; //DS18S20 Signal pin on digital 5
OneWire ds(DS18S20_Pin); //Temperature chip i/o

int light_on_time = 8;
int light_off_time = 20;
int light_on = 0; // set to 1 for lights to turn off when program run

int cal_doser_duration = 10000; // SP3000 dosing pump in ms, SP300 pump faster than BRS
int cal_doser_time = 7; // hour that doser will turn
//int cal_doser_hour = 0; // tracks the previous cal dosing hour
int cal_doser_day = 0; // day doser on
//int alk_doser_duration = 10; // BRS dosing pump in minutes
int alk_doser_on_time = 23; // hour the doser will turn on
int alk_doser_off_time = 10; // The minuite the doser will turn off
//int alk_doser_hour = 25; // tracks the previous alk dosing hour
int alk_on = 0; // tracks when alk running

int ato_max = 20; // water level
int ato_time = 12000; // time water is turned on in ms
int ato_level; // US measurement
int ato_hour = 0; // set to 0 for first time through compatibility
int ato_day = 0; // Day the ato was turned on
int ato_freq = 5; // ato can only run every __ hours maximum
int ato_prev = ato_max; // previous value

int kalk_max = 4; // water level at which to fill kalk reactor in inches
int kalk_time = 15000; // time water is turned on in ms
int kalk_level; // US measurement
int kalk_hour = 0; // set to 0 for first time through compatibility
int kalk_day = 0; // Day the water was added to kalk reactor
int kalk_freq = 5; // kalk can only run every __ hours maximum
int kalk_prev = kalk_max; // previous value

int hour;
int minute;
int second;
int day;
//int day_prev;


void setup(){
  delay(1000); // give time to open serial monitor
  Serial.begin(9600);
//  Serial.println("hellow world");
  Wire.begin();
//  Serial.println("hellow world2");
//  lcd.begin(20, 4);
//  lcd.print("TEST");
//  Serial.println("hellow world3");
  pinMode(ato_dist_in, INPUT);
  pinMode(ato_dist, OUTPUT);
  pinMode(kalk, OUTPUT);
  pinMode(kalk_dist, INPUT);
  pinMode(fan, OUTPUT);
  pinMode(light_A, OUTPUT);
  pinMode(light_B, OUTPUT);
  pinMode(light_fan, OUTPUT);
  pinMode(cal_doser, OUTPUT);
  pinMode(alk_doser, OUTPUT);
  pinMode(ato, OUTPUT);
  pinMode(skimmer, OUTPUT);
  pinMode(heater, OUTPUT);
  pinMode(return_pump, OUTPUT);

  pinMode(feed, INPUT);
  pinMode(lcd_on, INPUT);
  pinMode(ato_light, OUTPUT);
  pinMode(kalk_light, OUTPUT);

  // Set clock
  /* 
   RTC.stopClock();
   RTC.fillByHMS(22,59,45); // (hour, minute, second)
   RTC.fillByYMD(2012,4,10); // (year, month, day)
   RTC.setTime();
   RTC.startClock();
   // */  //
}
//**  MAIN  *************************************************************************************
void loop(){
  //delay(1000);
  //**   GET CURRENT TIME   *************************************************************************************
  RTC.getTime();
  hour = RTC.hour;
  minute = RTC.minute;
  second = RTC.second;
  day = RTC.day;
  //day_prev = day - 1;
  // if (day ~= 1) day_prev = day - 1; // used for ato logic
  //else day_prev = 32; // doesn't matter so long as day~=day_prev
//  Serial.println();
//  Serial.print(hour); Serial.print(':'); Serial.print(minute); Serial.print(':'); Serial.println(second);

  // Serial.print(day);


  //**   Temperature Control  ***********************************************************************************
  float temperature = getTemp();
  //  Serial.println(temperature);
  if (temperature >= 79) {
    //    Serial.print("Fan on time - ");
    //    Serial.print(hour); Serial.print(':'); Serial.print(minute); Serial.print(':'); Serial.println(second);
    Serial.print("Temperature - ");
    Serial.println(temperature);
    delay(100);

    digitalWrite(fan, HIGH);
  }
  else {
    digitalWrite(fan, LOW);
  }

  //**   AUTO-TOP-OFF  ***************************  Water is added from Kalk reactor   **************************

  int ato_level = rangefinderDist(SRF04_US);
  int kalk_level = rangefinderDist(Parallax);
  Serial.print(ato_level); 
  Serial.print("   "); 
  Serial.println(kalk_level);
  delay(100);

  //Serial.print(hour); Serial.println(ato_hour);

  if ((ato_level > ato_max && ato_day!=day && hour >= ato_hour && abs(ato_level-ato_prev)<2)||(ato_level > ato_max && ato_day!=day && hour>=24-ato_hour && abs(ato_level-ato_prev)<2)) {
    ato_day = day;
    //while (abs(ato_prev-ato_level) <= 3) { // look for change in water level
    // add counter in case of fault or for loop x numebr of times
    // add part that only adds h2o if reading is different that 1 from previous reading
    if (hour <= (24-ato_freq)){ // limiting the freqency of ato fill ups
      ato_hour = hour+ato_freq;
      //Serial.println(ato_hour);
    }
    else   {
      ato_hour = 24-hour+ato_freq;
    }
    Serial.print("Next ato hour - "); 
    Serial.print(ato_hour); 
    Serial.print("  Day - ");  
    Serial.println(day);

    //digitalWrite(ato, HIGH);   // TURN ON ATO
    digitalWrite(ato, LOW);
    //    delay(100);
    //    delay(100);
    digitalWrite(ato_light, HIGH);
    delay(2000);
    digitalWrite(ato_light, LOW);
    
    delay(100);
    digitalWrite(ato, HIGH);
    delay(ato_time);

    Serial.print("Water level - "); 
    Serial.println(ato_level);
    //Serial.println("add water");
    Serial.print(hour); 
    Serial.print(':'); 
    Serial.print(minute); 
    Serial.print(':'); 
    Serial.println(second);
    //}
    ato_prev  = ato_level;
  }
  //digitalWrite(ato, LOW); // TURN OFF ATO
  digitalWrite(ato,LOW);
//  digitalWrite(ato_light, LOW);



  //**   Refill Kalk resevouir  *************************************************************************

  if ((kalk_level > kalk_max && ato_day!=day && hour >= kalk_hour && abs(kalk_level-kalk_prev)<2)||(kalk_level > kalk_max && kalk_day!=day && hour>=24-kalk_hour && abs(kalk_level-kalk_prev)<2)) {
    kalk_day = day;
    //while (abs(kalk_prev-kalk_level) <= 3) { // look for change in water level
    // add counter in case of fault or for loop x numebr of times
    // limiting the freqency of kalk fill ups
    if (hour <= (24-kalk_freq)){
      kalk_hour = hour+kalk_freq;
      //Serial.println(kalk_hour);
    }
    else   {
      kalk_hour = 24-hour+kalk_freq;
    }
    Serial.print("Next kalk fill up hour - "); 
    Serial.print(kalk_hour); 
    Serial.print("  Day - "); 
    Serial.println(day);

    //digitalWrite(kalk, HIGH);   // TURN ON kalk
    digitalWrite(kalk, LOW);
    delay(100);
    digitalWrite(kalk_light, HIGH);
    delay(2000);
    digitalWrite(kalk_light, LOW);
    
    delay(100);
    digitalWrite(kalk, HIGH);
    delay(kalk_time);

    Serial.print("Kalk Reactor level - ");
    Serial.println(kalk_level);
    //Serial.println("add water");
    Serial.print(hour); 
    Serial.print(':'); 
    Serial.print(minute); 
    Serial.print(':'); 
    Serial.println(second);
    //}
    kalk_prev  = kalk_level;
  }
  //digitalWrite(kalk, LOW); // TURN OFF ATO
  digitalWrite(kalk,LOW);
//  digitalWrite(kalk_light, LOW);
  


  //**   Dosign Pumps  **************************************************************************************
    if (hour == cal_doser_time && cal_doser_day!=day) {
      digitalWrite(cal_doser, HIGH);
      delay(cal_doser_duration);
      cal_doser_day = day;
      digitalWrite(cal_doser, LOW);
  
    }
    if (hour == alk_doser_on_time && alk_on == 0 && minute<alk_doser_off_time){
      digitalWrite(alk_doser, HIGH);
      alk_on = 1; // makes this part run once
    }
    if (hour == alk_doser_on_time && minute>=alk_doser_off_time){
      digitalWrite(alk_doser, LOW);
      alk_on = 0;
      //alk_doser_hour = 1; // Dosed this hour
    }

} // END LOOP!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Subfunctions

// Subfunctions

//** ULTRA-SONIC RANGEFINDER DISTANCE   ***********************************************************************
int rangefinderDist(char US_type) {
  int distance=0;
  int distance2 = 0;
  int USPin;
  int USPin_in;
  if (US_type=='S'){ 
    USPin = ato_dist;
    USPin_in = ato_dist_in;
  }
  if (US_type=='P'){ 
    USPin = kalk_dist;  
    USPin_in = kalk_dist;
  }
  for(int counter = 0; counter<50; counter++)  {
    if (US_type=='S'){
      digitalWrite(USPin, LOW);                   // Set the trigger pin to low for 2uS
      delayMicroseconds(2);
      digitalWrite(USPin, HIGH);                  // Send a 10uS high to trigger ranging
      delayMicroseconds(10);
      digitalWrite(USPin, LOW);                   // Send pin low again
      int distance2 = pulseIn(USPin_in, HIGH);  // Read in times pulse
      distance2 = distance2/54;
      distance = distance2 + distance;
      delay(20);
    }
    if (US_type=='P'){
      pinMode(USPin, OUTPUT);
      digitalWrite(USPin, LOW);
      delayMicroseconds(2);
      digitalWrite(USPin, HIGH);
      delayMicroseconds(5);
      digitalWrite(USPin, LOW);
      pinMode(USPin_in, INPUT);
      int distance2 = pulseIn(USPin_in, HIGH);
      distance2 = distance2/74/2;
      distance = distance2 + distance;
      delay(20);
    }
  }
  distance = distance/50;
  return distance;
}

//** Temperature Subfunction   ***********************************************************************

float getTemp(){
  //returns the temperature from one DS18S20 in DEG Celsius
  byte data[12];
  byte addr[8];
  if ( !ds.search(addr)) {
    //no more sensors on chain, reset search
    ds.reset_search();
    return -1000;
  }
  if ( OneWire::crc8( addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return -1000;
  }
  if ( addr[0] != 0x10 && addr[0] != 0x28) {
    Serial.print("Device is not recognized");
    return -1000;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1); // start conversion, with parasite power on at the end

  byte present = ds.reset();
  ds.select(addr);  
  ds.write(0xBE); // Read Scratchpad

  for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = ds.read();
  }

  ds.reset_search();

  byte MSB = data[1];
  byte LSB = data[0];

  float tempRead = ((MSB << 8) | LSB); //using two's compliment
  float TemperatureSum = tempRead / 16; // in celcius
  TemperatureSum = 1.8*TemperatureSum+32;

  return TemperatureSum;

}

parker21:
I had the problem on pins 51 and 50. Pin 49 works fine, but it could be that the pin 49 relay has a lower threshold than the ones attached to 50 and 51.

Can you explain the problem a bit more clearly? I don't see a reference to pin 51 in your code.

The problem is that the relay isn't switching when I power up the pin. It works fine when I use the blink function, but not in the aquarium controller program. I keep trying different pin/relay combos. "kalk" used to be pin 51.

#define kalk 49
#define ato 52

Link to the datasheet for the relay please?

parker21:
So I think I solved it. The board was sending power to the relay pin, but not enough for the relay to switch (10mA threshold). I checked it my replacing the relay with an led, it lit up, but only barley. I had the problem on pins 51 and 50. Pin 49 works fine, but it could be that the pin 49 relay has a lower threshold than the ones attached to 50 and 51.

Hi Parker21
I've had problems on the pins above 50. Cannot tell now if I achtually misread the pin and used the grd next to it, but I shy away from using pins in that end.

You really need to consider adding transistorrelay ddrivers to your design. While 40 mA is given as max per pin there is an overall max the chip can provide. You're likely exceeding it as it looks like you are using a number of relays.
Also, what is your power supply rated at? Given you are looking to source a fair bit of current it should be on the high side of the voltage range, and capable of delivering the current you need.

@Nick: Using crydom cwd2410 SSR
http://www.crydom.com/en/Products/Catalog/c_w24.pdf

@ Carsten: Now I'm having the problem with pin 49 as well...

@kbwaldron: I think you may be right about exceeding the max current the chip can put out. Power supply is 12V 600mA. Yes, I have a lot of relays, but most of the time only 1-2 are active at the same time. The only time 3 relays will be on is if the fan is running, alk dosing pump, and either the auto top off (ato) or kalk reservoir pump is on.

What is really bizarre is that I can turn on all 5 relays on the same power center on simultaneously with the blink program. But if I try and turn on 1 in the program, I cant seem to pull it off consistently. The board sends power to the pin, but not enough to switch the relay. I checked this with an led and the light barley came on. So why does it not send full power in the aquarium program, but it does in the blink program? The blink program should have a high power draw bc I'm turing on 5 at once.

It seems like a power issue to me atm. Maybe I'm running something in the background in the aquarium controller program that is taking some of the available power? I'm running a rtc module, but it stays hooked up for both tests. Could it be the serial communication? Could it be overall code length? There has to be something different in terms of power draw between the blink and controller programs to explain the phenomenon

It's difficult to help troubleshoot your code when your pin references in your forum posts don't match those in the code. Also you have writes to some of your problem pins commented out.

I would go back to first principals of debugging. Comment out everything then enable only one relay's code. If that works, go to the next, and so on until something doesn't work. Uncomment only small amounts of code so you can determine where the issue is. Your blink test only turns on the relays for one second. Try ten and see how they work. Relays take a while to come up to full current draw, not usually that look, but that test cycle is short.