Alarm timer for Fire station radios

I am building a timer to switch the audio between the two radios for the station. we use an alert receiver and a base radio. the alert receiver has a programmable function to where I can provide a LOW to a analog input to the arduino. The base radio can also provide a low too. I am using a relay shield with 4 SPDT relays with everything off the audio from the alert radio is feed thru the NC contacts and out to the speaker PA for the station. that way if the arduino was to lose power the audio from the alert receiver is still feed to the PA system.

the alret reciever is configured so that it unmutes when ever it receives a call for 2 minutes it also provides the LOW for the same duration of time. the base radio provided a LOW any time is recievs a signal. the Base radio audio is feed to teh NO contacts of the first set of relays.

the arduino is an UNO with a Tiny RTC and 16X2 LCD configured for I2C. teh clock is running correctly with the sketch I have built thus far. the relay board is 4 SPDT relays.

first event I want to create is that the arduino switches the first set of relays tot eh base radio at 07:30 which would be a digitalWrite pin X HIGH
next event would be to digitalWrite the same pin LOW at 20:00

at anytime if the alert receiver was to activate I would want to digitalWrite pin X LOW so that audio from the alert receiver would be heard and the call would go out. after the alert receiver resets in 120 secs that LOW it provides to an analog input would go away and the relays would return to their previous state.

next would be to have a button function something along the lines of 1 press the system mutes fors for 1 hours, DOUBLE press it mutes until the the next day at 07:30 and TRIPLE press which would unmute the audio from the radio. confusing?

with each state I would need the LCD to display the state of the system, so between 07:30-20:00 it would say UNMUTED and the audio pa would hear the base radio. between 20:00 and 07:30 the display would display MUTED. if SINGLE press it would read MUTED 1 HR, DOUBLE PRESS MUTED, TRIPLE PRESS UNMUTED.

last little bit is I would need the clock to automatically adjust for daylight saving time.

The second set of contacts is not yet used but the plan is if we need to add a 3rd priority radio to listen too that as long as the system was muted, the station alert receiver was activated and the Base radio was not receiving anything audio would be derived from the radio on relays 3 and 4.

here is the sketch so far, just the clock. your thoughts on everything.

// Date and time functions using a DS1307 RTC connected via I2C and Wire lib

#include <Wire.h>
#include "RTClib.h"
#include <TimeAlarms.h>
#include <Timezone.h>
#include <Time.h>
#include <LiquidCrystal_I2C.h>
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
#define RELAY1 4 //
#define RELAY2 5 //
#define RELAY3 6 //
#define RELAY4 7 //
void setup () {
  Serial.begin(57600);
#ifdef AVR
  Wire.begin();
#else
  Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
#endif
  rtc.begin();

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
lcd.begin(16,2);   // initialize the lcd for 16 chars 2 lines, turn on backlight
lcd.backlight();
lcd.clear();
}
void loop () {
    DateTime now = rtc.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
 lcd.setCursor(0, 0);
 lcd.print(now.month(), DEC);  //We print the month
 lcd.print('/');
 lcd.print(now.day(), DEC); //We print the day
 lcd.print('/');
 lcd.print(now.year(), DEC);   //We print the year
 lcd.setCursor(0, 1);
 lcd.print(now.hour(), DEC);  //We print the hour
 lcd.print(':');
 lcd.setCursor(3, 1);
 lcd.print(now.minute(), DEC);  //We print the minutes
 lcd.print(':');
 lcd.setCursor(6, 1);
 lcd.print(now.second(), DEC);  //We print the seconds
   delay(10);
}

ok so this is as far as I have got.
it is a bit of a mess I know I am trying to get things to function and then clean it up.

I keep getting hung up when the i am asked to declare a button. I though I did this but I guess not.
I cut a pasted much of it. where am I going wrong.

TNX Jim

/*----------------------------------------------------------------------*
 * Timezone library example sketch.                                     *
 * Self-adjusting clock for multiple time zones.                        *
 * Jack Christensen Mar 2012                                            *
 *                                                                      *
 * Sources for DST rule information:                                    *
 * http://www.timeanddate.com/worldclock/                               *
 * http://home.tiscali.nl/~t876506/TZworld.html                         *
 *                                                                      *
 * This work is licensed under the Creative Commons Attribution-        *
 * ShareAlike 3.0 Unported License. To view a copy of this license,     *
 * visit http://creativecommons.org/licenses/by-sa/3.0/ or send a       *
 * letter to Creative Commons, 171 Second Street, Suite 300,            *
 * San Francisco, California, 94105, USA.                               *
 *----------------------------------------------------------------------*/
#include <ButtonV2.h>
#include <Time.h>        //http://www.arduino.cc/playground/Code/Time
#include <Timezone.h>    //https://github.com/JChristensen/Timezone
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
#include <Wire.h>
ButtonV2 Button1;
#define RELAY1 4 //
#define RELAY2 5 //
#define RELAY3 6 //
#define RELAY4 7 //
const byte buttonPin = A2;    // Input pin for switching RELAY1 and RELAY2
TimeChangeRule usPDT = {"PDT", Second, dowSunday, Mar, 2, -420}; //US Pacific Time Zone Daylight Savings
TimeChangeRule usPST = {"PST", First, dowSunday, Nov, 2, -480};  //US Pacific Time Zone Daylight Savings
Timezone usPT(usPDT, usPST);
TimeChangeRule *tcr; //pointer to the time change rule, use to get the TZ abbrev
time_t utc;

void setup(void)
{
    Serial.begin(115200);
    Wire.begin();
    setTime(usPT.toUTC(compileTime()));
    lcd.begin(16, 2);  // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("PCFD Quick Call");
  delay(3000);
  lcd.clear();
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  Button1.SetStateAndTime(LOW);
}

void loop(void)
{
    utc = now();
    printTime(usPT.toLocal(utc, &tcr));
    delay(10);

   if ((hour() > 7) && (hour() < 20)) //set RELAY1 and RELAY2 on and off times.
    {
       digitalWrite (RELAY1,HIGH);
       digitalWrite (RELAY2, HIGH);
       lcd.setCursor(10, 1);
       lcd.print("UNMUTE");
    }
 else {
       digitalWrite (RELAY1,LOW);
       digitalWrite (RELAY2, LOW);
       lcd.setCursor(10, 1);
       lcd.print("MUTE");
}
{
pinMode(buttonPin, INPUT_PULLUP); 
byte Button = button.CheckButton(buttonPin);
  switch (Button){
    case PRESSED:
    // create 1 hour timer for this function
      digitalWrite(RELAY1, LOW);
      digitalWrite(RELAY2, LOW);
      lcd.setCursor(10, 1);
      lcd.print("MUTE Hr");
      break;
    case DOUBLE_PRESSED:
      digitalWrite(RELAY1, LOW);
      digitalWrite(RELAY2, LOW);
      lcd.setCursor(10, 1);
      lcd.print("MUTE");
      break;
    case HELD:
      digitalWrite(RELAY1, LOW);
      digitalWrite(RELAY2, LOW);
      lcd.setCursor(10, 1);
      lcd.print("UNMUTE");
      break;
}
}
}
time_t compileTime(void) //Function to return the compile date and time as a time_t value
{
#define FUDGE 25        //fudge factor to allow for compile time (seconds, YMMV)
    char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
    char chMon[3], *m;
    int d, y;
    tmElements_t tm;
    time_t t;
    strncpy(chMon, compDate, 3);
    chMon[3] = '\0';
    m = strstr(months, chMon);
    tm.Month = ((m - months) / 3 + 1);

    tm.Day = atoi(compDate + 4);
    tm.Year = atoi(compDate + 7) - 1970;
    tm.Hour = atoi(compTime);
    tm.Minute = atoi(compTime + 3);
    tm.Second = atoi(compTime + 6);
    t = makeTime(tm);
    return t + FUDGE;        //add fudge factor to allow for compile time
}

void printTime(time_t t)//Function to print time 
{
   lcd.setCursor(0, 1);
   lcd.print(hour(t));
   lcd.print(":");
   int vala = (minute(t));
   if (vala < 10) lcd.print('0');
    lcd.print(vala, DEC);
    lcd.print(":");
    int valb = (second(t));
    if (valb < 10) lcd.print('0');
    lcd.print(valb, DEC);
    lcd.setCursor(0, 0);
    lcd.print(monthShortStr(month(t)));
    lcd.print("/");
    lcd.print(day(t));
    lcd.print("/");
    lcd.print(year(t));
    lcd.print(' ');
 
 
 
}

You need to learn about "states". The idea is that you have a variable (or several) that keeps track of the state of the system - is it dayTime or nightTime ('D' or 'N') and all the other things.

Then, rather than repeat everything millions of times between 0700 and 2000 you would do something like

if (dayState == 'D' && relayState == 'F') { // assumes F is off and N in on
  relayState = 'N';    // this means they will only be turned on once.
  // turn relays on
}

Now if you go through the whole process and think of it in those terms you should be able to write down the day as a series of steps - one on each line, and then develop your program to match.

Have a look at planning and implementing a program

...R

I am a pedant. Just as capital letters in code make it easier to read, so do capitals in posts.

Weedpharma

ok i made some changes to the sketch to hopefully avoid doing things less
but is still get the same error for the button, not sure why similar sketch is identical ( I think and I dont have any problems

#include <ButtonV2.h>
#include <Time.h>        //http://www.arduino.cc/playground/Code/Time
#include <Timezone.h>    //https://github.com/JChristensen/Timezone
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
#include <Wire.h>
ButtonV2 Button1;
#define RELAY1 4 //
#define RELAY2 5 //
#define RELAY3 6 //
#define RELAY4 7 //
const byte buttonPin = A2;    // Input pin for switching RELAY1 and RELAY2
TimeChangeRule usPDT = {"PDT", Second, dowSunday, Mar, 2, -420}; //US Pacific Time Zone Daylight Savings
TimeChangeRule usPST = {"PST", First, dowSunday, Nov, 2, -480};  //US Pacific Time Zone Daylight Savings
Timezone usPT(usPDT, usPST);
TimeChangeRule *tcr; //pointer to the time change rule, use to get the TZ abbrev
time_t utc;

void setup(void)
{
  Serial.begin(57600);
  Wire.begin();
  setTime(usPT.toUTC(compileTime()));
  lcd.begin(16, 2);  // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("PCFD Quick Call");
  delay(3000);
  lcd.clear();
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  Button1.SetStateAndTime(LOW);
}
time_t compileTime(void) //Function to return the compile date and time as a time_t value
{
#define FUDGE 25        //fudge factor to allow for compile time (seconds, YMMV)
    char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
    char chMon[3], *m;
    int d, y;
    tmElements_t tm;
    time_t t;
    strncpy(chMon, compDate, 3);
    chMon[3] = '\0';
    m = strstr(months, chMon);
    tm.Month = ((m - months) / 3 + 1);

    tm.Day = atoi(compDate + 4);
    tm.Year = atoi(compDate + 7) - 1970;
    tm.Hour = atoi(compTime);
    tm.Minute = atoi(compTime + 3);
    tm.Second = atoi(compTime + 6);
    t = makeTime(tm);
    return t + FUDGE;        //add fudge factor to allow for compile time
}
void printTime(time_t t)//Function to print time 
{
   lcd.setCursor(0, 1);
   int vala = (hour(t));
   if (vala < 10) lcd.print('0');
   lcd.print(vala, DEC);
   lcd.print(":");
   int valb = (minute(t));
   if (valb < 10) lcd.print('0');
    lcd.print(valb, DEC);
    lcd.print(":");
    int valc = (second(t));
    if (valc < 10) lcd.print('0');
    lcd.print(valc, DEC);
    lcd.setCursor(0, 0);
    lcd.print(monthShortStr(month(t)));
    lcd.print("/");
    lcd.print(day(t));
    lcd.print("/");
    lcd.print(year(t));
    lcd.print(' ');
}
void muteunmute(){
if ((hour() > 7) && (hour() < 20)) //set RELAY1 and RELAY2 on and off times.
    {
       digitalWrite (RELAY1,HIGH);
       digitalWrite (RELAY2, HIGH);
       lcd.setCursor(10, 1);
       lcd.print("UNMUTE");
    }
 else {
       digitalWrite (RELAY1,LOW);
       digitalWrite (RELAY2, LOW);
       lcd.setCursor(10, 1);
       lcd.print("MUTE");
}
}
void useradjust(){
  pinMode(buttonPin, INPUT_PULLUP); 
 
byte Button = button.CheckButton(buttonPin);
  switch (Button){
    case PRESSED:
    // create 1 hour timer for this function
      digitalWrite(RELAY1, LOW);
      digitalWrite(RELAY2, LOW);
      lcd.setCursor(10, 1);
      lcd.print("MUTE Hr");
      delay(6000000);  // create 1 hour timer for this function
      break;
    case DOUBLE_PRESSED:
      digitalWrite(RELAY1, LOW);
      digitalWrite(RELAY2, LOW);
      lcd.setCursor(10, 1);
      lcd.print("MUTE");
         //create timer unitl 0700 the next day in this function
      break;
    case HELD:
      digitalWrite(RELAY1, LOW);
      digitalWrite(RELAY2, LOW);
      lcd.setCursor(10, 1);
      lcd.print("UNMUTE");
      // do this until 2000 hrs
      break;
}
}
void loop()
{
    utc = now();
    printTime(usPT.toLocal(utc, &tcr));
    muteunmute();
    useradjust();
    delay(10);  
}

You can't use delay() in a program like that. The Arduino can do nothing during a delay().

It looks like you did not read Reply #2 or study the code in planning and implementing a program.

...R

yep your right i caught that after I posted it.

I made some progress…it gets time from the RTC, switchs the relays on and off at the correct times and the alarm input seems to work, not the most convention way of doing it thou. I went from what I know from another project and applied it here.

I did reading you post on planning an implementing a program and I am still trying to grasp it all.

So here is what does work
As far as I can tell the time change rule is not being used. I am not sure how to make this work. I have tried a few things but no luck

For the button function I have changed it to be included in the if else statement. my though is that since this is being used to control the relays for routing audio and that should be part of the statment. in the comments in the sketch I have put what I need done for each case. one press needs a 60 minute timer on that digitalWrite, double press needs to remain in that state until 0700 the following day and Held hold that state until 2000.

I have never used a timer library let alone incorporate it into a function like that.

I know I am using the wrong terminology here but correct me and i will get it right

TNX Jim

#include <Wire.h>
#include "RTClib.h"
#include <DS1307RTC.h>
#include <TimeAlarms.h>
#include <Timezone.h>
#include <Time.h>
#include <ButtonV2.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
ButtonV2 Button1;
RTC_DS1307 rtc;
TimeChangeRule usPDT = {"PDT", Second, dowSunday, Mar, 2, -420}; //US Pacific Time Zone
TimeChangeRule usPST = {"PST", First, dowSunday, Nov, 2, -480}; //US Pacific Time Zone
Timezone usPT(usPDT, usPST);
TimeChangeRule *tcr;//pointer to the time change rule, use to get TZ abbrev
time_t utc, local;

#define RELAY1 4 //
#define RELAY2 5 //
#define RELAY3 6 //
#define RELAY4 7 //

int alarmPin1 = A1;
const byte buttonPin1 = A0;    // Input pin for switching RELAY1 and RELAY2

void setup(void)
{
  Serial.begin(57600);
  Wire.begin();
  rtc.begin();
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  lcd.begin(16, 2);  // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("PCFD Quick Call");
  delay(3000);
  lcd.clear();
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);

  Button1.SetStateAndTime(LOW);
}
void adjust(byte buttonPin, ButtonV2 &button)  //
{
  utc = now();  // not used???
  local = usPT.toLocal(utc, &tcr);// not used 
  DateTime now = rtc.now();

  lcd.setCursor(0, 0);
  lcd.print(now.month(), DEC);  //Print the month
  lcd.print('/');
  lcd.print(now.day(), DEC); //Print the day
  lcd.print('/');
  lcd.print(now.year(), DEC);   //Print the year
  lcd.setCursor(0, 1);
  int valc = now.hour();
  if (valc < 10) lcd.print('0');
  lcd.print(valc, DEC); //Print the hour
  lcd.print(':');
  lcd.setCursor(3, 1);
  int vala = now.minute();
  if (vala < 10) lcd.print('0');
  lcd.print(vala, DEC);  //Print the minutes
  lcd.print(':');
  lcd.setCursor(6, 1);
  int valb = now.second();
  if (valb < 10) lcd.print('0');
  lcd.print(valb, DEC);  //Print the seconds

  int val = map(analogRead(alarmPin1), 0, 1, 0, 1); //turn off RELAY 1 and RELAY 2 to get audion from Quick Call

  if ((val - 0) < 1)  {
    digitalWrite (RELAY1, LOW);
    digitalWrite (RELAY2, LOW);
    lcd.setCursor(10, 1);
    lcd.print("ALARM ");  //Alarm mode
  }
  else if (((now.hour() > 7) && (now.hour() < 20)) && ((val - 0) > 1)) //set RELAY1 and RELAY2 on and off times.
  {
    digitalWrite (RELAY1, HIGH);
    digitalWrite (RELAY2, HIGH);
    lcd.setCursor(10, 1);
    lcd.print("UNMUTE");  //Day mode
  }
  else if (((now.hour() < 7) && (now.hour() > 20)) && ((val - 0) > 1))
  {
    digitalWrite (RELAY1, LOW);
    digitalWrite (RELAY2, LOW);
    lcd.setCursor(10, 1);
    lcd.print("MUTE  ");  //Night mode
  }
  else {
    pinMode(buttonPin1, INPUT_PULLUP);
    byte Button = button.CheckButton(buttonPin1);
    switch (Button) {
      case PRESSED:
        // create 1 hour timer for this function
        digitalWrite(RELAY1, LOW);
        digitalWrite(RELAY2, LOW);
        lcd.setCursor(10, 1);
        lcd.print("MUTEhr");
        break;
      case DOUBLE_PRESSED:
        //do this unitl 0700 the next day in this function
        digitalWrite(RELAY1, LOW);
        digitalWrite(RELAY2, LOW);
        lcd.setCursor(10, 1);
        lcd.print("MUTE  ");
        break;
      case HELD:
        // do this until 2000 hrs then mute
        digitalWrite(RELAY1, LOW);
        digitalWrite(RELAY2, LOW);
        lcd.setCursor(10, 1);
        lcd.print("UNMUTE");
        break;
    }
  }
}
void loop()
{
  adjust(buttonPin1, Button1);
}

The way you have written the code in Reply #6 you might as well just have put everything in loop().

If this was my project I would separate it into several separate functions. One of them would read buttons. Another would read the RTC. Another would prepare messages for the LCD. Another would display the messages. Etc.

The idea is that what goes on inside a function does not need to be known to any other function. The only thing they share is the data - which might be a message stored in a variable to be displayed on the LCD. Or the most recent time from the RTC. This allows each function to be short and simple and capable of being tested separately from the rest of the project. This is the general approach in Planning and Implementing a Program

In your case I would like to have a variable that represents whether the relays are on or off - perhaps relayState. Then the function to operate the relays would be as simple as

void switchRelays() {
   digitalWrite(relay1Pin, relayState);
   digitalWrite(relay2pin, relayState);
}

Then you would have another function (perhaps updateRelayState() ) that changes the value of relayState depending on time and buttons. The function for the buttons would record their state and the function updateRelayState() would take that into account in order to decide how to set the value of the variable relayState.

The code in loop() would just be

void loop() {
   readButtons();
   readRTC();
   updateRelayState();
   switchRelays();
}

In this model of your project the important logic would exist in the function updateRelayState()

What might not be obvious is that the Arduino runs throught these functions so quickly that you can assume they all happen at the same time. The order in which the functions are listed is largely irrelevant. Each function is independent of the others except to the extent that one uses data prepared by another.

…R