Indipendently control Relays

Hi

I’ve made a simple and small sketch for a sprinkler control system

/*

 Udp NTP Client
 
 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket 
 For more on NTP time servers and the messages needed to communicate with them, 
 see http://en.wikipedia.org/wiki/Network_Time_Protocol
 
 created 4 Sep 2010 
 by Michael Margolis
 modified 17 Sep 2010
 by Tom Igoe
 
 This code is in the public domain.

 */

#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>
#include <TimeAlarms.h>

//CONFIG RELAY BOARD
#define RELAY_ON 0
#define RELAY_OFF 1
#define Relay_1  3 // Valve 1
#define Relay_2  5 // Valve 2
#define Relay_3  6 // Valve 3
#define Relay_4  8 // Valve 4
#define Relay_5  9 // Valve 5
#define Relay_6  4 // Valve 6
#define Relay_7  7 // Valve 7
#define Relay_M  2 // Master Valve


// Configurazione di rete e NTP
byte mac[] = { 0x90, 0xA2, 0xDA, 0x06, 0x00, 0x5A }; //Mac Adress Arduino
unsigned int localPort = 8888;      // Porta UDP
IPAddress timeServer(199,167,198,163); // pool.ntp.org NTP server
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
EthernetUDP Udp;
int timeZoneHour = +2; // Timezone (+1 inverno - +2 Estate)
long timeZoneOffset = (timeZoneHour * -1) * 60 * 60 ;
int NTP_Update_Interval = 86400; // Intervallo update NTP in secondi (86400 = una volta al giorno)

// Config DURATA
int var;
int attive; // Numero Stazioni attive (per il calcolo della pausa)
int durata1 = 10; //in secondi
int durata2 = 10; //in secondi
int durata3 = 10; //in secondi
int durata4 = 10; //in secondi
int durata5 = 10; //in secondi
int durata6 = 10; //in secondi
int durata7 = 0; //in secondi
long intervallo = 1200; //pause time

// Config PARTENZA e FINE
int orapartenza = 19;
int minutopartenza = 31;
int orafine = 19;
int minutofine = 51; //Dare tempo di fare un ulteriore giro se necessario

// Void Setup - Esecuzione solo all'avvio
void setup() 
{
  digitalWrite(Relay_1, RELAY_OFF);
  digitalWrite(Relay_2, RELAY_OFF);
  digitalWrite(Relay_3, RELAY_OFF);
  digitalWrite(Relay_4, RELAY_OFF);
  digitalWrite(Relay_5, RELAY_OFF);
  digitalWrite(Relay_6, RELAY_OFF);
  digitalWrite(Relay_7, RELAY_OFF);
  digitalWrite(Relay_M, RELAY_OFF);
  Serial.begin(9600);
  Serial.println("Starting");
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for(;;);
  }
  Udp.begin(localPort);
  Serial.print("Got IP:");
  Serial.println(Ethernet.localIP());
  setSyncProvider(getNTPTime);
  Serial.println("Looking for a time");
  while(timeStatus()== timeNotSet);
  Serial.println("Got a Time");
  setSyncInterval(NTP_Update_Interval);
  // SVEGLIE  
  Alarm.alarmRepeat(orapartenza,minutopartenza,0, MorningAlarm);  // Partenza
  Alarm.alarmRepeat(orafine,minutofine,0,EveningAlarm);  // Fine
  //LED
  pinMode(Relay_1, OUTPUT);   
  pinMode(Relay_2, OUTPUT);  
  pinMode(Relay_3, OUTPUT);
  pinMode(Relay_4, OUTPUT);
  pinMode(Relay_5, OUTPUT);   
  pinMode(Relay_6, OUTPUT);  
  pinMode(Relay_7, OUTPUT);
  pinMode(Relay_M, OUTPUT);   
}


//void loop - Programma sempre in esecuzione
void loop(){
if (var == 1)
{
   Azione();
}
else
{
  stampaorario();
  Alarm.delay(1000);
}
}

// Printdigits - Mette lo 0 davanti alle cifre
void printDigits(int digits){
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

//Accende i LED
void Azione(){
  // MASTER
  if ( durata1 > 0 || durata2 > 0 || durata3 > 0 || durata4 > 0 || durata5 > 0 || durata6 > 0 || durata7 > 0 )
  {  
  Serial.print("Partenza Stazione MASTER: ");
  stampaorario();
  digitalWrite(Relay_M, RELAY_ON);
  Alarm.delay(1000);
  attive++;
  }
  // Stazione 1
  if ( durata1 > 0 ){   
  Serial.print("Partenza Stazione 1: ");
  stampaorario();
  digitalWrite(Relay_1, RELAY_ON); 
  Alarm.delay(durata1*1000);
  digitalWrite(Relay_1, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  //Stazione 2
  if ( durata2 > 0 ){ 
  Serial.print("Partenza Stazione 2: ");
  stampaorario();
  digitalWrite(Relay_2, RELAY_ON);
  Alarm.delay(durata2*1000);
  digitalWrite(Relay_2, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  //Stazione 3
  if ( durata3 > 0 ){ 
  Serial.print("Partenza Stazione 3: ");
  stampaorario();
  digitalWrite(Relay_3, RELAY_ON);
  Alarm.delay(durata3*1000);
  digitalWrite(Relay_3, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  // Stazione 4
  if ( durata4 > 0 ){ 
  Serial.print("Partenza Stazione 4: ");
  stampaorario();
  digitalWrite(Relay_4, RELAY_ON); 
  Alarm.delay(durata4*1000);
  digitalWrite(Relay_4, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  // Stazione 5
  if ( durata5 > 0 ){ 
  Serial.print("Partenza Stazione 5: ");
  stampaorario();
  digitalWrite(Relay_5, RELAY_ON); 
  Alarm.delay(durata5*1000);
  digitalWrite(Relay_5, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  // Stazione 6
  if ( durata6 > 0 ){ 
  Serial.print("Partenza Stazione 6: ");
  stampaorario();
  digitalWrite(Relay_6, RELAY_ON); 
  Alarm.delay(durata6*1000);
  digitalWrite(Relay_6, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  // Stazione 7
  if ( durata7 > 0 ){ 
  Serial.print("Partenza Stazione 7: ");
  stampaorario();
  digitalWrite(Relay_7, RELAY_ON); 
  Alarm.delay(durata7*1000);
  digitalWrite(Relay_7, RELAY_OFF);
  Alarm.delay(1000);
  attive++;
  }
  // Fine MASTER e Pausa
  if ( durata1 > 0 || durata2 > 0 || durata3 > 0 || durata4 > 0 || durata5 > 0 || durata6 > 0 || durata7 > 0 ) {
  digitalWrite(Relay_M, RELAY_OFF);
  Serial.print("Inizio Pausa: ");
  stampaorario();
  Serial.print("Durata Pausa: ");
  Serial.print(intervallo-durata1-durata2-durata3-durata4-durata5-durata6-durata7-attive);
  Serial.print(" Secondi ");
  Serial.println();  
  Alarm.delay(intervallo*1000-durata1*1000-durata2*1000-durata3*1000-durata4*1000-durata5*1000-durata6*1000-durata7*1000-1000*attive);
  attive=0;
  }
}

// Stampaorario - Stampa l'ora come voglio io
void stampaorario() {
  printDigits(hour());
  Serial.print(":");
  printDigits(minute());
  Serial.print(":");
  printDigits(second());
  Serial.print(" ");
  printDigits(day());
  Serial.print("/");
  printDigits(month());
  Serial.print("/");
  Serial.print(year()); 
  Serial.println();
} 

// functions to be called when an alarm triggers:
void MorningAlarm(){
  var = 1;    
}

void EveningAlarm(){
  var = 0;           
}

//Codice NTP
unsigned long getNTPTime()
{
  Serial.println("In getNTPTime");
  sendNTPpacket(timeServer);
  Alarm.delay(1000); 
  if ( Udp.parsePacket() ) {  
    Serial.println("Got Time Packet");
    Udp.read(packetBuffer,NTP_PACKET_SIZE);
 
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    unsigned long secsSince1900 = highWord << 16 | lowWord;  

    const unsigned long seventyYears = 2208988800UL + timeZoneOffset;      
    unsigned long epoch = secsSince1900 - seventyYears;  
  
    return epoch;    
  }
  Serial.println("No Time Packet Found");
  return 0;
}
// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

It simply takes the time from an NTP server, and when the morning alarm starts the sequence it activates the various relays and then pause for a given time, after the pause it restarts the sequence until the evening alarm stops everything for the night.

The main problem is that I have the same pause time for every station. I would like to have the ability to control every relay indipendently. Since I’m a programming newbie, what’s the easiest way to achieve that?

If you need any explanation for the code (most of the comments are in Italian :slight_smile: ), just ask.

Thank you

Have you tried changing the durata variables?

Can you translate the code? I think the variable names are as important as the comments to understand what is going on. :expressionless:

#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>
#include <TimeAlarms.h>

//RELAY BOARD CONFIGURATION
#define RELAY_ON 0
#define RELAY_OFF 1
#define Relay_1  3 // Relay 1
#define Relay_2  5 // Relay 2
#define Relay_3  6 // Relay 3
#define Relay_4  8 // Relay 4
#define Relay_5  9 // Relay 5
#define Relay_6  4 // Relay 6
#define Relay_7  7 // Relay 7
#define Relay_M  2 // Relay 8 (Master Valve)


// Network and NTP Configuration
byte mac[] = { 0x90, 0xA2, 0xDA, 0x06, 0x00, 0x5A }; //Mac Adress Arduino
unsigned int localPort = 8888;      // UDP Port
IPAddress timeServer(199,167,198,163); // pool.ntp.org NTP server
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
EthernetUDP Udp;
int timeZoneHour = +2; // Timezone 
long timeZoneOffset = (timeZoneHour * -1) * 60 * 60 ;
int NTP_Update_Interval = 86400; // NTP Update Interval

// Watering Program
int execution; // 
int active_stations; // Active Watering stations (used to calculate the pause period)
int period1 = 10; // Station 1 Watering period (in seconds)
int period2 = 20; // Station 2 Watering period (in seconds)
int period3 = 20; // Station 3 Watering period (in seconds)
int period4 = 20; // Station 4 Watering period (in seconds)
int period5 = 10; // Station 5 Watering period (in seconds)
int period6 = 10; // Station 6 Watering period (in seconds)
int period7 = 10; // Station 7 Watering period (in seconds)
long pause_time = 120; // Pause Time (in seconds)

// Start and Stop Configuration
int orapartenza = 8; // Start time (Morning Alarm) - Hour
int minutopartenza = 0; // Start time (Morning Alarm) - Minute
int orafine = 19; // Stop time (Evening Alarm) - Hour
int minutofine = 0; // Stop time (Evening Alarm) - Minute 

// Void Setup - Executed only once
void setup() 
{
  digitalWrite(Relay_1, RELAY_OFF);
  digitalWrite(Relay_2, RELAY_OFF);
  digitalWrite(Relay_3, RELAY_OFF);
  digitalWrite(Relay_4, RELAY_OFF);
  digitalWrite(Relay_5, RELAY_OFF);
  digitalWrite(Relay_6, RELAY_OFF);
  digitalWrite(Relay_7, RELAY_OFF);
  digitalWrite(Relay_M, RELAY_OFF);
  Serial.begin(9600);
  Serial.println("Starting");
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for(;;);
  }
  Udp.begin(localPort);
  Serial.print("Got IP:");
  Serial.println(Ethernet.localIP());
  setSyncProvider(getNTPTime);
  Serial.println("Looking for a time");
  while(timeStatus()== timeNotSet);
  Serial.println("Got a Time");
  setSyncInterval(NTP_Update_Interval);
  // ALARM
  Alarm.alarmRepeat(orapartenza,minutopartenza,0, MorningAlarm);  // START - Morning Alarm
  Alarm.alarmRepeat(orafine,minutofine,0,EveningAlarm);  // STOP - Evening Alarm
  //RELAY
  pinMode(Relay_1, OUTPUT);   
  pinMode(Relay_2, OUTPUT);  
  pinMode(Relay_3, OUTPUT);
  pinMode(Relay_4, OUTPUT);
  pinMode(Relay_5, OUTPUT);   
  pinMode(Relay_6, OUTPUT);  
  pinMode(Relay_7, OUTPUT);
  pinMode(Relay_M, OUTPUT);   
}


//void loop
void loop(){
if (execution == 1)
{
   Azione();
}
else
{
  print_time();
  Alarm.delay(1000);
}
}

// Printdigits (only used for serial debug)
void printDigits(int digits){
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

//Accende i LED
void Azione(){
  // MASTER
  if ( period1 > 0 || period2 > 0 || period3 > 0 || period4 > 0 || period5 > 0 || period6 > 0 || period7 > 0 )
  {  
  Serial.print("MASTER VALVE START: ");
  print_time();
  digitalWrite(Relay_M, RELAY_ON);
  Alarm.delay(1000);
  active_stations++;
  }
  // Stazione 1
  if ( period1 > 0 ){   
  Serial.print("RELAY 1 START: ");
  print_time();
  digitalWrite(Relay_1, RELAY_ON); 
  Alarm.delay(period1*1000);
  digitalWrite(Relay_1, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  //Stazione 2
  if ( period2 > 0 ){ 
  Serial.print("RELAY 2 START: ");
  print_time();
  digitalWrite(Relay_2, RELAY_ON);
  Alarm.delay(period2*1000);
  digitalWrite(Relay_2, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  //Stazione 3
  if ( period3 > 0 ){ 
  Serial.print("RELAY 3 START: ");
  print_time();
  digitalWrite(Relay_3, RELAY_ON);
  Alarm.delay(period3*1000);
  digitalWrite(Relay_3, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  // Stazione 4
  if ( period4 > 0 ){ 
  Serial.print("RELAY 4 START: ");
  print_time();
  digitalWrite(Relay_4, RELAY_ON); 
  Alarm.delay(period4*1000);
  digitalWrite(Relay_4, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  // Stazione 5
  if ( period5 > 0 ){ 
  Serial.print("RELAY 5 START: ");
  print_time();
  digitalWrite(Relay_5, RELAY_ON); 
  Alarm.delay(period5*1000);
  digitalWrite(Relay_5, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  // Stazione 6
  if ( period6 > 0 ){ 
  Serial.print("RELAY 6 START: ");
  print_time();
  digitalWrite(Relay_6, RELAY_ON); 
  Alarm.delay(period6*1000);
  digitalWrite(Relay_6, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  // Stazione 7
  if ( period7 > 0 ){ 
  Serial.print("RELAY 7 START: ");
  print_time();
  digitalWrite(Relay_7, RELAY_ON); 
  Alarm.delay(period7*1000);
  digitalWrite(Relay_7, RELAY_OFF);
  Alarm.delay(1000);
  active_stations++;
  }
  // Fine MASTER e Pausa
  if ( period1 > 0 || period2 > 0 || period3 > 0 || period4 > 0 || period5 > 0 || period6 > 0 || period7 > 0 ) {
  digitalWrite(Relay_M, RELAY_OFF);
  Serial.print("Begin pause: ");
  print_time();
  Serial.print("Pause duration: ");
  Serial.print(pause_time-period1-period2-period3-period4-period5-period6-period7-active_stations);
  Serial.print(" Seconds ");
  Serial.println();  
  Alarm.delay(pause_time*1000-period1*1000-period2*1000-period3*1000-period4*1000-period5*1000-period6*1000-period7*1000-1000*active_stations);
  active_stations=0;
  }
}

// print_time - Used to print time (only used in serial debug)
void print_time() {
  printDigits(hour());
  Serial.print(":");
  printDigits(minute());
  Serial.print(":");
  printDigits(second());
  Serial.print(" ");
  printDigits(day());
  Serial.print("/");
  printDigits(month());
  Serial.print("/");
  Serial.print(year()); 
  Serial.println();
} 

// functions to be called when an alarm triggers:
void MorningAlarm(){
  execution = 1;    
}

void EveningAlarm(){
  execution = 0;           
}

//NTP Code
unsigned long getNTPTime()
{
  Serial.println("In getNTPTime");
  sendNTPpacket(timeServer);
  Alarm.delay(1000); 
  if ( Udp.parsePacket() ) {  
    Serial.println("Got Time Packet");
    Udp.read(packetBuffer,NTP_PACKET_SIZE);
 
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    unsigned long secsSince1900 = highWord << 16 | lowWord;  

    const unsigned long seventyYears = 2208988800UL + timeZoneOffset;      
    unsigned long epoch = secsSince1900 - seventyYears;  
  
    return epoch;    
  }
  Serial.println("No Time Packet Found");
  return 0;
}
// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

Here you are… i know i can change period1, period2, period3 etc. to change the watering time (in the code I posted now station 2, 3 and 4 have 20 seconds of activation, all the other stations have 10 seconds), but the pause will always be the same for every valve.

I would like to really have an independent control of the valves (for example: valve 1 waters 10 seconds and pause for 20 minutes, valve 2 waters 20 seconds and pause for 3 hours etc. etc.) and independent morning and evening alarm for each valve.

I don’t want anybody to write me the code XD , i’m just asking for suggestions on how you would do it.

wait... I think I found something useful

http://arduino.cc/playground/Code/MultiBlink

I think I should use this kind of approach.

I think you need to first look at the control of one valve the way you want it to and then multiply by the number of valves.

millis will be running, so you can use that to time your intervals.

if (valve == ON && before - millis() > interval_on) {
    valve = OFF; 
    before = millis(); }

if (valve == OFF && before - millis() > interval_off) {
    valve = ON; 
    before = millis(); }

This can be made cleaner by arranging the data in an array for example:

struct valves[7] {
int pin_number;
int time_on;
int time_off;
}

for (int i = 0 ; i< 7; i++) {
   if (valve[i] == ON && before[i] - millis() > time_on[i]) {
      valve[i] = OFF; 
      before[i] = millis(); }

   if (valve[i] == OFF && before[i] - millis() > time_off[i]) {
      valve[i] = ON; 
      before[i] = millis(); }
}

Something like this, the code is full of errors, but could be a possibility. :slight_smile:

the code is full of errors

Why not fix them?

valves[ i ] is a struct. It is not going to equal ON or OFF. time_on is a member of a struct, not an array element.

If you are going to create a an array of structs, why not make before a member of that struct, too?