Digital Pins not outputting enough voltage [solved]

Hi. I have written a program that controls 4 100W light bulbs via relays attached to my Arduino (pins 2-5). I have a debounced button, finite stage machines, some timing, and sequences of lights on/off which are read from sequentially named (01.txt, 02.txt, etc.) text files on an SD card. This allows visual programming of sequences win Notepad outside of the Arduino environment.

I am having the curious and undesired result of only getting 5V output from pins #4 and #5 - so my relays are not firing on pins #2 and #3. When I substitute LEDs for relays, the LEDs on pins #2 AND #3 only light up dimly, and are reading ~1 and less V, while #4 and #5 are bright. My relays are 5V/0.30A so current is not the problem. My Arduino is powered by 12V DC, 1.25A. The relays are getting 5V in parallel from the Arduino VCC / GND out.

To test that electronic connections, I wrote a simple “blink 2, blink 3, blink 4, blink 5” sketch just using delays, and that manages to fire the relays and light up the light bulbs just fine. There is something in this program that is the culprit, can anyone help?

By the way, If this looks familiar to anyone I had posted asking for help in the Communication forum a few weeks ago; since then I have managed to get the program to do what it should, save for this latest issue. Thanks for having a look.

Here is my program (in two parts):

#include <Bounce.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <FiniteStateMachine.h>

int REDled = 8;
int GREENled = 9;

//Sequence
int SEQUENCEbutton = 6;
unsigned long SequenceLastFired;

//Variables for Timing
unsigned long StartupEnteredTime;
unsigned long ArmedEnteredTime;
unsigned long FiringEnteredTime;
unsigned long PausingEnteredTime;

unsigned long StartFiring;

//random intermission parameters
long randomIntermissionLengthMin = 2000;
long randomIntermissionLengthMax = 8000;
int randomValveMin = 100;
int randomValveMax = 200;
int timeBetweenBlastsMin = 50;
int timeBetweenBlastsMax = 5000;


//state machine declarations
State Idle = State (DSIdle); //idle state
State Startup = State (DSStartupStart,DSStartupUpd,DSStartupExit); //startup state
State Armed = State (DSArmedStart,DSArmedUpd,DSArmedExit); //Armed state
State Firing = State (DSFiringStart,DSFiringUpd,DSFiringExit); //cannons firing state
State Pausing = State (DSPausingStart,DSPausingUpd,DSPausingExit);//pause state
FSM DSStateMachine = FSM(Idle); //initialize DS State Machine, start in state: Idle


//sequence reset time, during which sequence button will not work after pressed (should be eventually set to 15 minutes)
int SequenceResetTime = 5000;

//button debouncing
Bounce SEQUENCEbouncer = Bounce (SEQUENCEbutton,3);

//SD card setup
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;


//Text files on card names and quantity
//int offset = 2;
int fileCount = 7; // count + 1

//struct for variables in text file
struct Event {
 long eventTimestamp;
 byte address;
} ;

//LED or Cannon Pins
int outputPins[4] = {2,3,4,5};
int sequenceSize;
long millisSinceStart = 0;
int currentEventPtr = 0;

char name[] = "00.txt";
uint8_t fileCounter = 0;

Event sequence[150];

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s)) 
void error_P(const char* str) {
  PgmPrint("mystery error: ");
  if (card.errorCode()) 
  {
    PgmPrint("SD error: ");
   }
  while(1);
}

//====================SETUP CODE TO RUN ONCE=====================

void setup () {
 
  pinMode (SEQUENCEbutton, INPUT);
  for (int i = 2; i < 6; i++) 
  {
     pinMode(outputPins[i], OUTPUT); 
  }

  if (!card.init(SPI_HALF_SPEED)) error("card.init failed");
  if (!volume.init(&card)) error("volume.init failed");
  if (!root.openRoot(&volume)) error("openRoot failed");
  
 for (int i = 2; i < 6; i++)  {
     digitalWrite(i, LOW); 
     delay(10);
  }
 Serial.begin(9600);
 //REMOVED loadSequence(); 
 randomSeed(analogRead(0));
 }

//===================MAIN PROGRAM LOOP CODE=====================
void loop (){
  
      if (millis() > 3000 && DSStateMachine.isInState(Idle)){
        {
        DSStateMachine.transitionTo(Startup);
        }
      
   }       
DSStateMachine.update();   
}

dir_t dir;

//==================STATE MACHINES==============================
//Idle------------------------------/
void DSIdle (){
  Serial.println("In State = Idle");
  for (int i = 2; i < 6; i++)  
  {
     digitalWrite(i, LOW); 
  }
  digitalWrite(GREENled, LOW);
  digitalWrite(REDled, HIGH);
}

//Startup----------------------------/
void DSStartupStart (){
  Serial.println("In State = Startup");
  for (int i = 2; i < 6; i++)  
  {
     digitalWrite(i, LOW); 
  }
  digitalWrite(GREENled, LOW);
  digitalWrite(REDled, HIGH);
  StartupEnteredTime = millis();
  Serial.println(StartupEnteredTime);
}
  
void DSStartupUpd (){
    
  if (millis() - StartupEnteredTime > 2000)
    {
    DSStateMachine.transitionTo(Armed);
    }  
}

void DSStartupExit ()
{
}

//Armed------------------------------/
 void DSArmedStart (){
   for (int i = 2; i < 6; i++)  
  {
     digitalWrite(i, LOW); 
  }
  Serial.println("In State = Armed");
  digitalWrite(GREENled, HIGH);
  digitalWrite(REDled, LOW);
  ArmedEnteredTime = millis();

 }
 
 void DSArmedUpd (){
  
 SEQUENCEbouncer.update();
 int SEQUENCEbuttonState = SEQUENCEbouncer.read();  

 if (SEQUENCEbuttonState == HIGH)
 {
 DSStateMachine.transitionTo(Firing);
 }
}

void DSArmedExit ()
{
}

//FIRING---------------------------/
void DSFiringStart()
{  
  Serial.println("In State = Firing");
  loadSequence();
}

void DSFiringUpd()
{
  StartFiring = millis();
  //Serial.println(StartFiring);
  //delay(1000);
  //Serial.println("timestamp");
 // Serial.println(sequence[currentEventPtr].eventTimestamp);
  
  if(StartFiring > (sequence[currentEventPtr].eventTimestamp))
  {
      loadBinaryCannonSequences(sequence[currentEventPtr].address);    
      currentEventPtr++;
      if(currentEventPtr >= sequenceSize)
      {
        currentEventPtr = 0;
        file.close();
        for (int i = 2; i < 6; i++) {
           digitalWrite(i, LOW); 
           delay(10);
        SequenceLastFired = millis();
        DSStateMachine.transitionTo(Pausing);
        }
      }
      //  loadSequence();
  }
}
     
void DSFiringExit()
{
}
 
//PAUSING------------------------------/  

void DSPausingStart(){
  Serial.println("In State = Pausing");
  digitalWrite(REDled, HIGH);
  digitalWrite(GREENled,LOW);
  PausingEnteredTime = millis();
 
}

void DSPausingUpd(){
  
  ///*
  //--------Random intermission code for TOP MAST only (PIN 2)-----------
  //--can be removed if not desired, decomment exclusion characters above and below.
  unsigned long randomIntermissionLength = random(randomIntermissionLengthMin, randomIntermissionLengthMax);
   
  do
  {
    delay(random(timeBetweenBlastsMin, timeBetweenBlastsMax));   
    digitalWrite(2, HIGH);
    delay(random(randomValveMin, randomValveMax));
    digitalWrite(2, LOW);
    delay(random(timeBetweenBlastsMin, timeBetweenBlastsMax));
  }while (millis() - PausingEnteredTime < SequenceResetTime);
  
  //--------------------------------------
  //*/
  
  if (millis() - PausingEnteredTime > SequenceResetTime){
   DSStateMachine.immediateTransitionTo(Armed);    
  }
}
  
void DSPausingExit()
{
}

//PART ONE OF CODE

(part two of program continued)

//PART TWO OF PROGRAM CODE
//=======================LOAD SEQUENCE CODE===============================
void loadSequence ()
{
   
  for (int i = 2; i < 6; i++) {
     digitalWrite(i, LOW); 
     delay(10);
     }
  sequenceSize = 0;
  if (file.open(&root, name, O_READ)) {
     
  } else{
    
    fileCounter = 0;
    name[0] = fileCounter/10 + '0';
    name[1] = fileCounter%10 + '0';
    if (!file.open(&root, name, O_READ)){
      error("file.open failed");
      
    }else{
         
    }
  }
  
  fileCounter++; // sequential
  
  name[0] = fileCounter/10 + '0';
  name[1] = fileCounter%10 + '0';    
  
  int n;
  char buf[32];  
  int sequenceIndex = 0;
  int index = 0;
  char timestampString[10] = {0,0,0,0,0,0,0,0,0,0};
  boolean isByteString = false;
  byte currentByte = B00000000;
 
 //WAS COMMENTED OUT 
  Serial.println(name);
  
  // READ THROUGH EACH CHARACTER IN THE FILE
  while ((n = file.read(buf, sizeof(buf))) > 0) {
    
    // LOOP THROUGH BUFFER
    for (int i = 0; i < n; i++)
    {   
      // DELIMITER HIT - ADD PREVIOUS TO ARRAY
      if(buf[i] == ',' || buf[i] == '\n')
      { 
        // TIMESTAMP        
        if(isByteString == false) 
        {                  
          index++;
          timestampString[index] = '\0'; 
          long ts = atoi(timestampString);
          sequence[sequenceIndex].eventTimestamp = ts;
          char timestampString[10] = {0,0,0,0,0,0,0,0,0,0};
          index = 7;
          
          
        }
        // BYTE      
        if(isByteString == true)
        {
          sequence[sequenceIndex].address = currentByte;
          currentByte = B00000000;
          index = 0;                
        }
        isByteString = !isByteString; // SWITCHES BACK AND FORTH BETWEEN TIMESTAMP AND BYTE              
      } 
      // END - DELIMITER CODE
      
      if(buf[i] == '\n'){
        sequenceSize++;
        sequenceIndex++;
      }

      // BUILD DATA TYPES     
      if(buf[i] != ',' && buf[i] != '\n' && buf[i] != '\r'  && buf[i] != ' '){
        
        // TIMESTAMP
        if(isByteString == false)
        {
            timestampString[index] = char(buf[i]);
            index++;
        }          
        
        // BYTE
        if(isByteString == true)
        {
          if(char(buf[i]) == '1'){
            currentByte = currentByte | (1 << index);
          }
  
          if(char(buf[i]) == '0'){
            currentByte = currentByte | (0 << index);
          }
          index--;     
          
        }
       
      } // END - BUILD DATA TYPES    
    } // END - LOOP THROUGH BUFFER   
  } // END - LOOP THROUGH FILE

  resetMillis();  
  currentEventPtr = 0;
 
 
}
  
//==================THIS CODE FIRES THE CANNONS BASED ON TEXT FILES=====
 void loadBinaryCannonSequences(byte BinaryPinSequence) {
  int i=0;
  int pinState; 
  for (i=7; i>=0; i--)  {
    if ( BinaryPinSequence & (1<<i) ) {
      pinState= 1;
    }
    else {	
      pinState= 0;
    }
    
    digitalWrite(i, pinState);
  }
  
}


//==========================this code resets the millis() timer

extern volatile unsigned long timer0_millis;
void resetMillis()
{
 cli();  // disable interrupts
 timer0_millis = 0;
 sei();  // enable interrupts
}

Here is an example text file, where the first number is a time, and the second is a binary number representing the LEDs:

1000,00111100
5000,00100000
7000,00010000
10000,00010000
12000,00001000
14000,00010000
15000,00100000
16000,00111100
17000,00000000

My relays are 5V/0.30A so current is not the problem. My Arduino is powered by 12V DC, 1.25A.

Well unless you are using switching transistors between the arduino output pins and the relay coils, then 300 ma is way too much current to try and draw from a output pin and can or already has caused damage. 30ma not 300ma is a good MAXIMUM output pin current to abide by.

Lefty

Good point - but if the pins were fried on the Ardunio, wouldn't it not be able to fire with my simple program (which fires them and the light bulbs fine)?

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT); 
  pinMode(4, OUTPUT); 
  pinMode(5, OUTPUT); 
}

void loop() {
  digitalWrite(2, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(2, LOW);    // set the LED off
  delay(1000);              // wait for a second
    digitalWrite(3, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(3, LOW);    // set the LED off
  delay(1000); 
    digitalWrite(4, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(4, LOW);    // set the LED off
  delay(1000); 
    digitalWrite(5, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(5, LOW);    // set the LED off
  delay(1000); 
}

geek_tk: Good point - but if the pins were fried on the Ardunio, wouldn't it not be able to fire with my simple program (which fires them and the light bulbs fine)?

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT); 
  pinMode(4, OUTPUT); 
  pinMode(5, OUTPUT); 
}

void loop() {   digitalWrite(2, HIGH);   // set the LED on   delay(1000);              // wait for a second   digitalWrite(2, LOW);    // set the LED off   delay(1000);              // wait for a second     digitalWrite(3, HIGH);   // set the LED on   delay(1000);              // wait for a second   digitalWrite(3, LOW);    // set the LED off   delay(1000);     digitalWrite(4, HIGH);   // set the LED on   delay(1000);              // wait for a second   digitalWrite(4, LOW);    // set the LED off   delay(1000);     digitalWrite(5, HIGH);   // set the LED on   delay(1000);              // wait for a second   digitalWrite(5, LOW);    // set the LED off   delay(1000); }

First fix your electrical problem. Drawing 300ma from an output pin is a VERY big problem, so don't try and over think it, just look up proper relay coil interfacing with simple a cheap npn transistor and a series base resistor.

Lefty

My relays have base resistors and NPN transistors on a custom PCB I made, so I don't think that is the issue. As I said, when I run that very simple code I posted in my reply, I get all four relays and lights to fire just fine. The issue is somewhere in my program.

I'd suspect your physical wiring, re-check it, including the transistor orientation etc.

Try substitution and elimination, try putting all transistor/relay combos on the pins that work, the ones that don't.

Give us a drawing or photo of what you've done.

Dave

pinMode (SEQUENCEbutton, INPUT);
for (int i = 2; i < 6; i++)
{
pinMode(outputPins*, OUTPUT);*

  • }[/quote]*
    Your array goes from 0 to 3. You are accessing 2-5…

[quote author=James C4S link=topic=116366.msg875897#msg875897 date=1343537919]

pinMode (SEQUENCEbutton, INPUT);
for (int i = 2; i < 6; i++)
{
pinMode(outputPins*, OUTPUT);*

  • }[/quote]*
    Your array goes from 0 to 3. You are accessing 2-5…
    [/quote]
    Duh! I am having a braindead night from too many hours of staring at this screen…should I just replace this with stating the individual pins as outputs (not utilize an array?)

James C4S you are the MAN! This fixed it. Amazing how something so simple was the hangup and how it blew right by me. If I had the means to beam you a cold beverage of your choice through the intertubes, I would!

should I just replace this with stating the individual pins as outputs (not utilize an array?)

No. Just use the correct array index and loop index.

  for(int i=0; i<3; i++)
  {
     pinMode(outputPins[i], OUTPUT);
  }

PaulS:

should I just replace this with stating the individual pins as outputs (not utilize an array?)

No. Just use the correct array index and loop index.

  for(int i=0; i<3; i++)

{
     pinMode(outputPins[i], OUTPUT);
  }

Maybe I haven;t had enough coffee yet but how does the above not come out to:
i = 0
i = 1
i = 2

I want pins 2,3,4, and 5 declared as output.

Maybe I haven;t had enough coffee yet but how does the above not come out to:
i = 0
i = 1
i = 2

I want pins 2,3,4, and 5 declared as output.

It does work out so that i is 0, 1, 2. The < should have been <=, so i gets to be 3, too.

The pin numbers are in the array, so outputPins[0], outputPins[1], outputPins[2], and outputPins[3] get set as OUTPUT.

Presumably, somewhere you have declared outputPins to be an array, containing the values 2, 3, 4, and 5.