It/Hardware/Arduino Bell System
Source code for the arduino bell system using the DS3231 RTC module and doorbells.
/** * An arduino program to ring the doorbells at MSG on the bell schedule. * Written by Micah Henney. Last updated February 2, 2024. * * Made for use with DS3231 RTC module. A similar program exists for the DS1302/DS1307 * but that module keeps less accurate time due to an external temperature crystal. * * The RTC module should be connected through I2C using the default arduino I2C pins SDA and SCL. * Run Vcc on 5V and GND on GND. Do not connect SQW and 32K. * To connect the bell system transmitter, take apart one of the doorbell buttons and solder pins * to the positve and negative battery terminals, as well as the top left pin of the button. * Connect the postive battery terminal to Vin (works on 5V, but made for 12V so Vin allows it to * receive more power if available), the negative terminal to GND, and the button lead to pin 8 * (defined below PIN_TRIG). The arduino will pull the button lead LOW to trigger the transmitter. * * To set the schedule times, change the below lines mondayRingtimes and ringtimes. They are split * because VTE has a different schedule on Monday. CNX has the same ringtimes every day, so each * has the same values. Format is timeInDay(HourAs24, Minute, Second) * * To change the schedule ring times, the code must be reuploaded. Setting the current time can be * done through Serial. To change the schedule, change the times below, plug in the arduino and * upload the code. Then set the current time. To set the current time, plug in the arduino and * connect to the serial monitor. The board will print out the current set time, then how long it * will wait before ringing. If it is correct, no additional steps are required. If not, type the * current time in the serial monitor input in the format YYMMDDwHHMMSSx (the x at the end is * required, and all numbers except day of week must be 2 digit. Day of week Monday = 1) For * example, if the current time is Thursday, February 15 2024 at 2:17:00 PM, type "2402154141700x" * and enter (meaning year 2024, month 02, day 15, day of week 4, hour 14, minute 17, second 00) * Reset the board and confirm that it now shows the correct time. * * To pair the button transmitter with the alarm component of the doorbell, press the tone selector * on the doorbell until the desired tone plays. Hold the volume button for 4 seconds until the blue * light turns solidly on, then press the transmitter. The doorbell light will blink and turn off. * Pairing is successful. * * When not connected to serial, the arduino onboard LED will blink once for every 10 minutes * remaining until the next ringtime, up to a maximum of 20 blinks. It will sleep for about 8 * seconds between each blink sequence. */ #include <DS3231-RTC.h> #include <Adafruit_SleepyDog.h> RTClib Rtc; DS3231 ll_rtc; #define countof(a) (sizeof(a) / sizeof(a[0])) #define timeInDay(h, m, s) ((unsigned long)(h) * 60 * 60 + (m) * 60 + (s)) #define PIN_TRIG 8 unsigned long mondayRingtimes[] = {timeInDay(7, 45, 30), timeInDay(8, 0, 0), timeInDay(9, 10, 0), timeInDay(9, 15, 0), timeInDay(10, 23, 0), timeInDay(10, 55, 0), timeInDay(11, 0, 0), timeInDay(12, 10, 0), timeInDay(12, 55, 0), timeInDay(14, 0, 0), timeInDay(14, 30, 0), timeInDay(14, 35, 0), timeInDay(15, 40, 0), timeInDay(15, 55, 0), timeInDay(17, 0, 0)}; // {timeInDay(7, 15, 0), timeInDay(9, 50, 0), timeInDay(9, 55, 0), timeInDay(11, 10, 0), timeInDay(11, 45, 0), timeInDay(13, 0, 0), timeInDay(13, 5, 0), timeInDay(13, 35, 0), timeInDay(13, 40, 0), timeInDay(15, 35, 0)}; unsigned long ringtimes[] = {timeInDay(8, 0, 0), timeInDay(9, 10, 0), timeInDay(9, 15, 0), timeInDay(10, 23, 0), timeInDay(10, 55, 0), timeInDay(11, 0, 0), timeInDay(12, 10, 0), timeInDay(12, 55, 0), timeInDay(14, 0, 0), timeInDay(14, 30, 0), timeInDay(14, 35, 0), timeInDay(15, 40, 0), timeInDay(15, 55, 0), timeInDay(17, 0, 0)}; // {timeInDay(7, 15, 0), timeInDay(9, 50, 0), timeInDay(9, 55, 0), timeInDay(11, 10, 0), timeInDay(11, 45, 0), timeInDay(13, 0, 0), timeInDay(13, 5, 0), timeInDay(13, 35, 0), timeInDay(13, 40, 0), timeInDay(16, 25, 0)}; DateTime next; bool timeJustSet = false; void setup() { pinMode(PIN_TRIG, OUTPUT); digitalWrite(PIN_TRIG, HIGH); delay(100); Serial.begin(9600); Wire.begin(); delay(500); DateTime now = Rtc.now(); DateTime test = DateTime(now.getUnixTime()); Serial.print("Current time: "); printDateTime(now); Serial.println(); delay(500); getNext(now); } void getNext(DateTime now) { unsigned long cmp = timeInDay(now.getHour(), now.getMinute(), now.getSecond()); if (now.getWeekDay() == 1) // Monday { Serial.println("Monday"); if (cmp > mondayRingtimes[countof(mondayRingtimes) - 1]) { // It's Monday night. Next ringtime is Tuesday morning next = timeOnDay(now, 2, ringtimes[0]); return; } else { // Use the next Monday ringtime for (int i = 0; i < countof(mondayRingtimes); i++) { if (cmp < mondayRingtimes[i]) { next = timeOnDay(now, 1, mondayRingtimes[i]); return; } } Serial.println("This should never happen x1"); } } else { if (now.getWeekDay() == 5) // Friday { Serial.println("Friday"); if (cmp > ringtimes[countof(ringtimes) - 1]) { next = timeOnDay(now, 1, mondayRingtimes[0]); return; } else { for (int i = 0; i < countof(ringtimes); i++) { if (cmp < ringtimes[i]) { next = timeOnDay(now, 5, ringtimes[i]); return; } } Serial.println("This should never happen x2"); } } else { // It's a Tuesday through Thursday. We can add one day and do tomorrow if not sometime today if (cmp > ringtimes[countof(ringtimes) - 1]) { // Ring tomorrow morning next = timeOnDay(now, now.getWeekDay() + 1, ringtimes[0]); return; } else { // Get next ringtime today for (int i = 0; i < countof(ringtimes); i++) { if (cmp < ringtimes[i]) { next = timeOnDay(now, now.getWeekDay(), ringtimes[i]); return; } } Serial.println("This should never happen x3"); } } } } DateTime timeOnDay(DateTime reference, uint8_t dow, unsigned long time) { if (reference.getWeekDay() == dow) { return DateTime(reference.getYear(), reference.getMonth(), reference.getDay(), time / 60 / 60, (time % 3600) / 60, time % 60, dow); } else { // Number of days until dow long addDays = (dow + 7 - reference.getWeekDay()) % 7; long diff = (addDays * 60 * 60 * 24); // Subtract seconds between 1970 and 2000 // For some reason constructor assumes from Y2K DateTime sometimeInDay = DateTime(diff + reference.getY2kTime()); DateTime ret = DateTime(sometimeInDay.getYear(), sometimeInDay.getMonth(), sometimeInDay.getDay(), time / 60 / 60, (time % 360) / 60, time % 60, dow); return ret; } } void printDateTime(DateTime dt) { char datestring[50]; dt.show_DateTime(datestring, sizeof(datestring)); Serial.print(datestring); } void loop() { DateTime now = Rtc.now(); if(timeJustSet) { getNext(now); timeJustSet = false; delay(1000); return; } setTime(); long timeToWait = next.getUnixTime() - now.getUnixTime(); if (timeToWait < 0) { Serial.println("Ring"); // Blink the on board LED as well digitalWrite(13, HIGH); digitalWrite(PIN_TRIG, LOW); delay(2000); digitalWrite(13, LOW); digitalWrite(PIN_TRIG, HIGH); getNext(now); Serial.print("Next ringtime: "); printDateTime(next); Serial.println(); } else if (timeToWait < 10) { delay(50); // Delay for 1/20th of a second } else { // Blink onboard led once per 10 minutes remaining long minutesToWait = timeToWait / 60; Serial.print("Minutes to wait: "); Serial.print(minutesToWait); Serial.println(); if (!Serial) { for (int i = 0; i < min((minutesToWait / 10) + 1, 20); i++) { digitalWrite(13, HIGH); delay(200); digitalWrite(13, LOW); delay(200); } Watchdog.sleep(); } else { delay(2000); } } } void parseSetTimeInput(byte &year, byte &month, byte &date, byte &dOW, byte &hour, byte &minute, byte &second) { // Call this if you notice something coming in on // the serial port. The stuff coming in should be in // the order YYMMDDwHHMMSS, with an 'x' at the end. boolean gotString = false; char inChar; byte temp1, temp2; char inString[20]; byte j = 0; while (!gotString) { if (Serial.available()) { inChar = Serial.read(); inString[j] = inChar; j += 1; if (inChar == 'x') { gotString = true; } } else return; } Serial.println(inString); // Read year first temp1 = (byte)inString[0] - 48; temp2 = (byte)inString[1] - 48; year = temp1 * 10 + temp2; // now month temp1 = (byte)inString[2] - 48; temp2 = (byte)inString[3] - 48; month = temp1 * 10 + temp2; // now date temp1 = (byte)inString[4] - 48; temp2 = (byte)inString[5] - 48; date = temp1 * 10 + temp2; // now Day of Week dOW = (byte)inString[6] - 48; // now hour temp1 = (byte)inString[7] - 48; temp2 = (byte)inString[8] - 48; hour = temp1 * 10 + temp2; // now minute temp1 = (byte)inString[9] - 48; temp2 = (byte)inString[10] - 48; minute = temp1 * 10 + temp2; // now second temp1 = (byte)inString[11] - 48; temp2 = (byte)inString[12] - 48; second = temp1 * 10 + temp2; } void setTime() { if (!Serial) return; if (Serial.available()) { byte year; byte month; byte date; byte dOW; byte hour; byte minute; byte second; parseSetTimeInput(year, month, date, dOW, hour, minute, second); ll_rtc.setClockMode(false); // set to 24h // setClockMode(true); // set to 12h ll_rtc.setYear(year); ll_rtc.setMonth(month); ll_rtc.setDate(date); ll_rtc.setDoW(dOW); ll_rtc.setHour(hour); ll_rtc.setMinute(minute); ll_rtc.setSecond(second); timeJustSet = true; } }