It/Hardware/Arduino Bell System
Jump to navigation
Jump to search
New Code
Developed by Walter DeMoss
Old Code
Developed by Micah Henney
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; } }