Triggering a sequence of events with millis()

I googled it for 2 days, all the examples teach how to control 2 things independently.
I need to control 3 water pumps (via relays) in sequence. It’s trivial with delay(), but I can’t get it to work with millis()

The event sequence I’m looking for:

A) If water level sensor HIGH:

  1. Pump 1 ON for 30 sec
  2. Pump 1 OFF for 5 sec
  3. Pump 2 ON for 30 sec
  4. Pump 2 OFF for 5 sec
  5. Pump 3 ON for 30 sec
  6. Pump 3 OFF for 5 sec
  7. Increase counter by 1 and display current counter value on LCD.

B) If water level sensor LOW: do nothing

C) Reset counter after 24 hours

I figured out the WaterLevel part, my homemade sensor is working nicely, I also know how to display stuff on LCD. I’m using LEDs instead of relays and I can’t get that millis() part to work.
Also, I don;t know where to put “counter = counter+1” part within my loop.
I have no C++ experience whatsoever, so please forgive me the obvious mistakes.
I want my 3 pumps to work in sequence to reduce the current on wires and fuses.
That “5 sec” delay is optional and probably not neccessery, but I wanted to be sure I’m not overstressing my wires.

My code so far (not working as expected):

#define Pump1 8
#define Pump2 9
#define Pump3 10
#define WaterSensor 2

int Pump1State = LOW;
int Pump2State = LOW;
int Pump3State = LOW;

unsigned long CurrentMillis = 0;
unsigned long StoredMillis1 = 0;
unsigned long StoredMillis2 = 0;
unsigned long StoredMillis3 = 0;

const unsigned long interval = 1000;

int DifPump1 = 0;
int DifPump2 = 0;
int DifPump3 = 0;

int counter = 0; // How many times the water tank was overflooded

void setup() {
 pinMode(Pump1, OUTPUT);      
 pinMode(Pump2, OUTPUT); 
 pinMode(Pump3, OUTPUT);
 pinMode(WaterSensor, INPUT_PULLUP); // HIGH means overflooded
 Serial.begin(9600);  
 }



void loop() {

 // CurrentMillis - milliseconds elapsed since restart;
 unsigned long CurrentMillis = millis();
 
 // WaterSensor state stored as WaterLevel;
 int WaterLevel = digitalRead(WaterSensor);

 digitalWrite(Pump1, Pump1State);
 digitalWrite(Pump2, Pump2State);
 digitalWrite(Pump3, Pump3State);

 Serial.println(DifPump1); // for testing
 
 DifPump1 = CurrentMillis - StoredMillis1; // To make it easier
 DifPump2 = CurrentMillis - StoredMillis2;
 DifPump3 = CurrentMillis - StoredMillis3;
 
 // If too much water in the tank
 if (WaterLevel == HIGH) { 

   // Start 3 pumps in sequence
   
     // Pump 1  
     
     if (DifPump1 >= 0.1*interval && DifPump1 < interval) {
     //Store Pump1 Millis
     StoredMillis1 = CurrentMillis;
     //Pump 1 ON
     Pump1State = HIGH;
     Pump2State = LOW;
     Pump3State = LOW;
     }
     if (DifPump1 >= interval && DifPump1 < 1.1*interval) {
     //Store Pump1 Millis
     StoredMillis1 = CurrentMillis;
     //Pump 1 OFF
     Pump1State = LOW;
     Pump2State = LOW;
     Pump3State = LOW;
     }
   
     // Pump 2    
     
     if (DifPump2 >= 1.1*interval && DifPump2 < 2*interval) {
     //Store Pump2 Millis
     StoredMillis2 = CurrentMillis;
     //Pump 2 ON
     Pump1State = LOW;
     Pump2State = HIGH;
     Pump3State = LOW;
     }
     if (DifPump2 >= 2*interval && DifPump2 < 2.1*interval) {
     //Store Pump2 Millis
     StoredMillis2 = CurrentMillis;
     //Pump 2 OFF
     Pump1State = LOW;
     Pump2State = LOW;
     Pump3State = LOW;
     }

     // Pump 3    
   
     if (DifPump3 >= 2.1*interval && DifPump3 < 3*interval) {
     //Store Pump3 Millis
     StoredMillis3 = CurrentMillis;
     //Pump 3 ON
     Pump1State = LOW;
     Pump2State = LOW;
     Pump3State = HIGH;
     }
     if (DifPump3 >= 3*interval && DifPump3 < 3.1*interval) {
     //Store Pump3 Millis
     StoredMillis3 = CurrentMillis;
     //Pump 3 OFF
     Pump1State = LOW;
     Pump2State = LOW;
     Pump3State = LOW;
     }

   }
 else { //No need to run pumps

 Pump1State = LOW;
 Pump2State = LOW;
 Pump3State = LOW;
        }
}

first error I see is simple.

you measure,
set the output
do the math.

more correctly, it is

measure
do the math
set the output.

digitalWirte() being at the end of loop, not at the beginning.

try :
if ( WaterLevel == 0 ) {
StoredMillis1 = CurrentMillis;
StoredMillis2 = CurrentMillis;
StoredMillis3= CurrentMillis ;
}

as long as the water level is low, the value is 0
as soon as the water level rises to 1
only then does the timing come into play.

===========

your first if()

basically says if ( value is between 0.1 and 1, then re-set to zero)

your second says

if (greater than 1 )

but it can never get there. see the first if()

nesting if() statement might be in order. and then only re-set the counter after the second action times out.

if ( pump1 == LOW ){ // once the pump goes high, this is not looked at again.
if (DifPump1 >= 0.1*interval && DifPump1 < interval) {
Pump1State = HIGH;
Pump2State = LOW;
Pump3State = LOW;
}
}

if (pump1 == HIGH){
if (DifPump1 >= interval && DifPump1 < 1.1*interval) {
Pump1State = LOW;
Pump2State = LOW;
Pump3State = LOW;
StoredMillis1 = CurrentMillis // reset the counter
}
}

============

in order to better help, I would offer that if could explain what your project is
and what you want to achieve it would help us understand it better.

it looks like you want to run pump1, and pump2, and pump3
all at the same time.

reading your words, I assumed…
run pump1 for 30 seconds
pause for 5 sec
run pump2 for 30seconds
pause for 5 sec
run pump3 for 30seconds
shut everything off

but that is not want your code shows.

in re-reading your sketch

if pump1 < interval
Pump1State = HIGH;
Pump2State = LOW;
Pump3State = LOW;

if pump2 < interval
Pump1State = LOW;
Pump2State = HIGH;
Pump3State = LOW;

if pump3 < interval
Pump1State = LOW;
Pump2State = LOW;
Pump3State = HIGH

pump3 would cancel out all the other commands

You show a typical state machine use case :slight_smile:

In Idle state you wait for the water level sensor, then activate pump 1 and proceed to the next state. In subsequent states you wait for the interval time elapse. The entire machine can be implemented by a switch statement. The states can become an enum with named constants, for increased readability.

dave-in-nj:
your first if()

basically says if ( value is between 0.1 and 1, then re-set to zero)

your second says

if (greater than 1 )

but it can never get there. see the first if()

I removed StoredMillis2 and StoredMillis3, moved StoredMillis1 = CurrentMillis; to WaterSensor == LOW section and I changed the timing section:

// If too much water in the tank
  if (WaterLevel == HIGH) { 

    // Start 3 pumps in sequence
    
      // Pump 1  
      
      if (DifPump1 >= 0 && DifPump1 < interval) {
      //Pump 1 ON
      Pump1State = HIGH;
      Pump2State = LOW;
      Pump3State = LOW;
      }
          
      // Pump 2    
      
      if (DifPump1 >= 1*interval && DifPump1 < 2*interval) {
      //Pump 2 ON
      Pump1State = LOW;
      Pump2State = HIGH;
      Pump3State = LOW;
      }

      // Pump 3    
    
      if (DifPump1 >= 2*interval && DifPump1 < 3*interval) {
      //Pump 3 ON
      Pump1State = LOW;
      Pump2State = LOW;
      Pump3State = HIGH;
      }

      // All Pumps OFF and counter + 1

      if (DifPump1 >= 3*interval) {
      //Pump 3 OFF
      Pump1State = LOW;
      Pump2State = LOW;
      Pump3State = LOW;
      counter = counter + 1;
      StoredMillis1 = CurrentMillis;
            }
    }
    
  else { //No need to run pumps

  Pump1State = LOW;
  Pump2State = LOW;
  Pump3State = LOW;

  StoredMillis1 = CurrentMillis; 
      }

digitalWrite(Pump1, Pump1State);
digitalWrite(Pump2, Pump2State);
digitalWrite(Pump3, Pump3State);

The timing part works as I wanted, thank you very much.
However, I need help with the counter part - right now counter = counter + 1 only happens when all three pumps finish their timed cycles. When the loop gets interupted by WaterLevel == LOW, counter does not increase.

Ideally, I would like the counter = counter + 1 part to trigger when WaterLevel == HIGH, but just once every “tank-emptying cycle”.

nesting if() statement might be in order. and then only re-set the counter after the second action times out.

if ( pump1 == LOW ){ // once the pump goes high, this is not looked at again.
if (DifPump1 >= 0.1*interval && DifPump1 < interval) {
Pump1State = HIGH;
Pump2State = LOW;
Pump3State = LOW;
}
}

if (pump1 == HIGH){
if (DifPump1 >= interval && DifPump1 < 1.1*interval) {
Pump1State = LOW;
Pump2State = LOW;
Pump3State = LOW;
StoredMillis1 = CurrentMillis // reset the counter
}
}

I don’t understand that, you wrote ** StoredMillis1 = CurrentMillis // reset the counter**, but I don’t see the counter integer within your code. If you could modify my code to include your counter = counter + 1 idea, it will be easier for me to understand.

============

in order to better help, I would offer that if could explain what your project is
and what you want to achieve it would help us understand it better.

it looks like you want to run pump1, and pump2, and pump3
all at the same time.

reading your words, I assumed…
run pump1 for 30 seconds
pause for 5 sec
run pump2 for 30seconds
pause for 5 sec
run pump3 for 30seconds
shut everything off

but that is not want your code shows.

The project needs to run 3 pumps in order to empty a tank. All 3 Pumps have to work in sequence (they can’t work in the same time). They can all run with a fixed pumping time. Pump sequence needs to be triggered and stopped by WaterSensor. I need to count how many times pump sequence fired and put it on the LCD. The counter should reset every morning, but that can be done with Arduino reset.

DrDiettrich:
You show a typical state machine use case :slight_smile:

A Finite State Machine is one good option.
I see a list of commands:

struct commandSTRUCT
{
  int command;              // set pin, or increase counter, or do something else
  int pin;                  // arduino pin         
  int value;                // value HIGH or LOW for arduino pin
  unsigned long interval;   // how long to wait until next command in ms
} command;

command commandList[] =
{
  { SET_PIN, pump1Pin, HIGH, 30000},
  { SET_PIN, pump1Pin, LOW,   5000},
  { SET_PIN, pump2Pin, HIGH, 30000},
};

It will require a lot of code, but once it works, it is easy to add things and anything can be done at any moment.

@kokosgt, if you want to use a Finite State Machine or a list with commands then you have to rewrite your sketch. I don't want to help to fix your sketch, because I don't want to go where you are going ::slight_smile:

is the threshold of high/low set by one sensor ?
in many arrangements, like a sump, there is a high and a low switch
in a fish tank, there is a level that is desired.

you do not show the whole loop(),
but since you say it is working I assume you have the millis() set properly with StoredMillis1 and CurrentMillis;
these are only snippets where I added some lines

// If too much water in the tank
if (WaterLevel == HIGH) {

  if (sequence_started = 0) { // here is a flag that is set once for each pumping cycle
    sequence_started = 1 ; // makes it so counter does not up-tick on every time through the loop.
    sequenceCounter++;  // here is your counter
  }
  // Start 3 pumps in sequence

in your declaration:

int sequence_started, sequnneceCounter ;

in your setup()

sequence_started = 0 ;

add into your loop() :

  // All Pumps OFF and counter + 1

  if (DifPump1 >= 3 * interval) {
    //Pump 3 OFF
    Pump1State = LOW;
    Pump2State = LOW;
    Pump3State = LOW;
    counter = counter + 1;
    StoredMillis1 = CurrentMillis;
    sequence_started = 0;  // shows the end of the sequence
  }
}  =============   end WaterLevel =========== // might be better after else()

else { //No need to run pumps

you could put the counter at the end, or at the beginning, your choice.

the question of resetting the counter…
if you just reset the Arduino, then setup() will reset the counter
if you have a switch, you can press it and reset the counter.

if you want to know the last few days of running

if (resetSwitch = 1 ){
day3 = day2 ;
day2 = day1;
day1 = yesterday;
yesterday = sequenceCounter ;
}

this is all very basic programming. like doing addition when you could simplify by doing multiplication.
but understanding the fundamentals is part of learning.

notes:
if water level is high, 3-pump cycle runs to end
if water level is low at the end, the system is back to a ready state
if the water level is high at the end, then the counter will toggle by 1 and the 3-pump cycle will start again.

if you use the crude day counter, you might want to use debounce if it acts flakey.
or you could put in another flag into your WaterLevel() statement so that once you re-set, you cannot erase the last days.

They can all run with a fixed pumping time. ---- easy to do with delay

Pump sequence needs to be triggered and stopped by WaterSensor. ---- until a pin needs to be watched all the time

If you keep the pump data (pin #, state) in an array then which pump should run can be in a single variable that only gets changed with proper turning off the last chosen pump. Perhaps an index >=3 means no pump running. Then the water level can simply pass a signal for which pump to stop or start.
That would NOT be done with one big logic-knot code but rather a few if(trigger) sections inside of void loop(), one for each sensor/button (Inputs) and one for the motors since 1 motor runs at a time (Output), and one for watching time and signalling process changes (Process).
It takes less code to do as triggered tasks than as a mono-block of logic-to-cover-every-situation. You break things down to parts and let them work between themselves using variables to pass signals and data, you can get rid of a lot of knotty logic.

Koepel:
A Finite State Machine is one good option.
I see a list of commands:

struct commandSTRUCT

{
  int command;              // set pin, or increase counter, or do something else
  int pin;                  // arduino pin       
  int value;                // value HIGH or LOW for arduino pin
  unsigned long interval;  // how long to wait until next command in ms
} command;

command commandList =
{
  { SET_PIN, pump1Pin, HIGH, 30000},
  { SET_PIN, pump1Pin, LOW,  5000},
  { SET_PIN, pump2Pin, HIGH, 30000},
};




It will require a lot of code, but once it works, it is easy to add things and anything can be done at any moment.

@kokosgt, if you want to use a Finite State Machine or a list with commands then you have to rewrite your sketch. I don't want to help to fix your sketch, because I don't want to go where you are going ::)

You actually make FSM harder (and bigger code) than it needs to be right from the start.

Cripes, even the pin # is an int! That's a habit most PC programmers do without thinking and newbies pick up from examples, but hello, most Arduinos have limited RAM and none have > 256 pins.

GoForSmoke:
It takes less code to do as triggered tasks

Your solution is somewhat like a minimalistic command list with just the pin and the state. Can you put the delay time in there as well ? :wink:

GoForSmoke:
Cripes, even the pin # is an int!

I can make it a uint8_t ;D
I was thinking (too) big, to be able to add other commands in the future. It is better to start small, of course.

Do you agree with DrDiettrich and me that a large block with if-statements is going down the rabbit hole. To make a clear and straightforward sketch then something with a Finite State Machine or array with pin definitions or a list of commands is a good solution ?

Koepel:
A Finite State Machine is one good option.
I see a list of commands:

struct commandSTRUCT

{
 int command;              // set pin, or increase counter, or do something else
 int pin;                  // arduino pin        
 int value;                // value HIGH or LOW for arduino pin
 unsigned long interval;   // how long to wait until next command in ms
} command;

command commandList =
{
 { SET_PIN, pump1Pin, HIGH, 30000},
 { SET_PIN, pump1Pin, LOW,   5000},
 { SET_PIN, pump2Pin, HIGH, 30000},
};




I will require a lot of code, but once it works, it is easy to add things and anything can be done at any moment.

@kokosgt, when you want to use a Finite State Machine or a list with commands then you have to rewrite your sketch. I don't want to help to fix your sketch, because I don't want to go where you are going ::)

IMHO, it goes to learning.
This is all very basic programming. like doing addition when you could simplify by doing multiplication.
but understanding the fundamentals is part of learning.
learning and using blinkWithoutDelay in it's many-many forms helps one to understand how you can do things with a timer.
I would have done this differently, but, I would also with BWD, just because it is such a simple sketch.
What Koepel has started is working, in my book, that is a win.
IMHO, I hate to abandon a learning experience as uncompleted.
Koepel, as others have mentioned, a Finite State Machine, would be something you will want to use as your skills grow.
this sketch could be a great opportunity to use a Finite State Machine due to the simplicity of the sketch.

Koepel:
Your solution is somewhat like a minimalistic command list with just the pin and the state. Can you put the delay time in there as well ? :wink:
I can make it a uint8_t ;D
I was thinking (too) big, to be able to add other commands in the future. It is better to start small, of course.

Do you agree with DrDiettrich and me that a large block with if-statements is going down the rabbit hole. To make a clear and straightforward sketch then something with a Finite State Machine or array with pin definitions or a list of commands is a good solution ?

With int as opposed to byte (Arduino type byte is uint8_t, only easier to type);
I was pointing out a very common PC coder habit I call automatic-int. After some time, typing int gets to be some kind of default arm-hand reflex when thinking small numbers. I know, I had to break myself of doing that. Step 1 is knowing there is a problem....

Yes, one large logic-knot code is doing it the hard way -- even worse if the code needs to flex or add much at all, that's the road to massive debug sessions. I know because I used to write that way until I found/made ways out over a few years in the early-mid 80's. See reply 8 to this thread.

IMO the whole command engine thing is way bigger than this project needs. If it was a finished, debugged thing the OP could adapt then yeah sure, jump on that!

if this is not a beginners sketch, then the FSM is not too steep of a gradient

As I mentioned, the variation of blink without delay can be useful and simple.

apologies for my one line if() statements.

[ EDIT : this will run all pumps once and not stop if the water level falls below the switch ]
[ EDIT : this is very crude, to show the example. seconds would be 5000, not 5, etc
one could put if (duration > 5000) && (duration < 35000) { pump1State = HIGH ; }

if (WaterLevel == HIGH) {   startPumps=1; }   

duration = ( millis() - then ) /1000 ;  // to get seconds

if (startPumps = 1 )   [ EDIT :  should be startPumps == 1 , thanks AWOL ]
  {  // starts pumping sequence
  
  if (sequence_started = 0)   [ EDIT :  should be  " == "  , thanks AWOL ]
    {   
    counter++;   // increments counter
    then = millis(); // sets the start time to zero the first time through
    started_sequence = 1 ;
    }
  
  if (duration > 5  ) { pump1State = HIGH  }
  if (duration > 35 ) { pump1State = LOW  }
  if (duration > 40 ) { pump2State = HIGH  }
  if (duration > 70 ) { pump2State = LOW  }
  if (duration > 75 ) { pump3State = HIGH  }

  if (duration > 105 )
  {
    pump3State = LOW ;
    started_sequence = 0 ;
    startPumps = 0 ;  // runs all pumps though the sequence
    then = millis();
  }
} // ===========  end of startPumps ==========
else{ then = millis() ; }  // keeps then at 0

digitalWrite (pump1, pump1State)
digitalWrite (pump2, pump2State)
digitalWrite (pump3, pump3State)

if (startPumps = 1 )Oops

Thank you guys, after another few hours of analysing your suggestions and a lot of trial and error, I have managed to get all of it working as I wanted.

I have another question though - can I power all 3 relays and 4-digit LED seven segment display with 1A 9V adapter plugged in barrel jack? i was looking for “current consumption in idle state” information in relay data sheet, but I couldn’t find it.

I’m using three JQC-3FF 5V relays in Normally Open mode, plugged directly to Arduino pins:

Thanks to short work cycles and delays between pumps, a maximum of 1 relay will be online at any given time, so I’m not worried about too much amps under load. However, all 3 relays will be on standby 24/7, so they will use some current.

The circuit has been in idle mode for a few hours now and although the Arduino voltage regulator is getting quite hot, it seems to be working fine. Relays are powered from Arduino 5V pin.

Is it OK or should I power these from an external power supply?
I only have 9V adapters, should I build a voltage divider from resistors to reduce it from 9V to 5V?

My full code (working):

#include "TM1637.h" 
const int pinClk = 13; 
const int pinDta = 12; 
TM1637 led;

#define Pump1 8
#define Pump2 9
#define Pump3 10
#define led1 5
#define led2 6
#define led3 7
#define WaterSensor 2

int Pump1State = LOW;
int Pump2State = LOW;
int Pump3State = LOW;

unsigned long CurrentMillis = 0;
unsigned long StoredMillis1 = 0;
unsigned long ResetMillis = 0;

const unsigned long interval = 30000;
unsigned long DifPump1 = 0;
int counter = 0; // How many times the water tank was overflooded?
boolean counterSwitch = false; // Trigger for counter=counter+1 event

void setup() {
  pinMode(Pump1, OUTPUT);      
  pinMode(Pump2, OUTPUT); 
  pinMode(Pump3, OUTPUT);
  pinMode(led1, OUTPUT);      
  pinMode(led2, OUTPUT); 
  pinMode(led3, OUTPUT);
  pinMode(WaterSensor, INPUT_PULLUP); // HIGH means overflooded
  Serial.begin(9600);
  }

void loop() {

  // CurrentMillis - gets updated with millis() every cycle
    
  unsigned long CurrentMillis = millis();
  
  // WaterSensor state stored as WaterLevel;
  int WaterLevel = digitalRead(WaterSensor);

  DifPump1 = CurrentMillis - StoredMillis1;

  if (millis() >= 86400000) { // 24h Reset
    
    software_Reset();
    }
  
  // If too much water in the tank
  if (WaterLevel == HIGH) { 

    if (counterSwitch == true) {
    counter = counter + 1;
    counterSwitch = false;
    }
      
    // Start 3 pumps in sequence
    
      // Pump 1 ON 
      
      if (DifPump1 >= 0 && DifPump1 < 2*interval) {
      Pump1State = HIGH;
      Pump2State = LOW;
      Pump3State = LOW;
      }

      // Pump 1 OFF 

      if (DifPump1 >= 2*interval && DifPump1 < 2.1*interval) {
      Pump1State = LOW;
      Pump2State = LOW;
      Pump3State = LOW;
      }
          
      // Pump 2 ON    
      
      if (DifPump1 >= 2.1*interval && DifPump1 < 4*interval) {
      Pump1State = LOW;
      Pump2State = HIGH;
      Pump3State = LOW;
      }

      // Pump 2 OFF    
      
      if (DifPump1 >= 4*interval && DifPump1 < 4.1*interval) {
      Pump1State = LOW;
      Pump2State = LOW;
      Pump3State = LOW;
      }

      // Pump 3 ON
    
      if (DifPump1 >= 4.1*interval && DifPump1 < 5*interval) {
      Pump1State = LOW;
      Pump2State = LOW;
      Pump3State = HIGH;
      }

      // Pump 3 OFF

      if (DifPump1 >= 5*interval) {
      Pump1State = LOW;
      Pump2State = LOW;
      Pump3State = LOW;
      // StoredMillis1 = CurrentMillis;
      }
    }
    
  else { //No need to run pumps

  Pump1State = LOW;
  Pump2State = LOW;
  Pump3State = LOW;
  counterSwitch = true;
  StoredMillis1 = CurrentMillis;
  }

 
digitalWrite(led1, Pump1State);
digitalWrite(led2, Pump2State);
digitalWrite(led3, Pump3State);
digitalWrite(Pump1, Pump1State);
digitalWrite(Pump2, Pump2State);
digitalWrite(Pump3, Pump3State);



Serial.println(DifPump1);
led.DigitDisplayWrite(pinClk, pinDta, counter);
}
 
void software_Reset() // Restarts program from beginning but does not reset the peripherals and registers
  {
  asm volatile ("  jmp 0");  
  }

Don't draw more than 200mA through the AVR chip pins. It is probably a bad idea to draw much more through that barrel jack if it has a 7805 regulator (mine do) as the more current drawn through that, the hotter it gets.

I have no idea how much juice your relays pull, you'd better find out how much any one can because it might be high.

kokosgt:
Thank you guys, after another few hours of analysing your suggestions and a lot of trial and error, I have managed to get all of it working as I wanted.

I have another question though - can I power all 3 relays and 4-digit LED seven segment display with 1A 9V adapter plugged in barrel jack? i was looking for "current consumption in idle state" information in relay data sheet, but I couldn't find it.

I'm using three JQC-3FF 5V relays in Normally Open mode, plugged directly to Arduino pins:
https://www.generationrobots.com/media/JQC-3FF-v1.pdf

The circuit has been in idle mode for a few hours now and although the Arduino voltage regulator is getting quite hot, it seems to be working fine. Relays are powered from Arduino 5V pin.

Is it OK or should I power these from an external power supply?
I only have 9V adapters, should I build a voltage divider from resistors to reduce it from 9V to 5V?

Use an external power supply. a cell phone charger would output 5v.
voltage dividers are not good for power, they just heat resistors.

the relay above shows 77 ohms for the coil.
if you only have 9v adapters, then get a power supply

EBAY power supply

This one has 700mA max output.

another option is to get a switching power supply.
you can get these in fixed output voltages or adjustable.
you can get cheap, under $1 each, bare bones
or can get with an LED display for quite a bit more.

switching power supply

I ordered both, thank you!