User Tools

Site Tools


arduino:art-clockbell

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
arduino:art-clockbell [2019/03/15 16:06]
Ilias Iliopoulos Added pictures
arduino:art-clockbell [2024/02/02 21:47] (current)
Ilias Iliopoulos
Line 1: Line 1:
 ====== Arduino Art - Town Hall Bell clock - Real Time clock with LCD display and Bell functionality ====== ====== Arduino Art - Town Hall Bell clock - Real Time clock with LCD display and Bell functionality ======
 +
 +{{htmlmetatags>​metatag-keywords=(arduino,​ nano,art, bell clock, RTC, ) 
 +metatag-description=(Arduino Art - Town Hall Bell clock - Real Time clock with LCD display and Bell functionality)
 +}}
  
 ===== Introduction ===== ===== Introduction =====
Line 11: Line 15:
  
 {{arduino:​20190228_arduino_bell_board.jpg?​600}} {{arduino:​20190228_arduino_bell_board.jpg?​600}}
 +
 {{arduino:​20190314_111606.jpg?​200}} {{arduino:​20190314_111606.jpg?​200}}
 {{arduino:​20190314_111611.jpg?​200}} {{arduino:​20190314_111611.jpg?​200}}
Line 42: Line 47:
 ===== Schematic ===== ===== Schematic =====
  
-{{arduino:20190228_arduino_bell_schematic.jpg?1000}}+{{arduino:arduino_bell_schematic.jpg?1000}}
  
 //Handmade schematic?//​ [[wiki:​handmade_schematics|See why..]] //Handmade schematic?//​ [[wiki:​handmade_schematics|See why..]]
Line 77: Line 82:
 When the time is exactly on the hour, the system produces a number of "​hour"​ pulses. Especially for hours after 12 (p.m for USA), 12 is subtracted from the 24hour number. So at 13:00 the bell sounds once, at 14:00 the bells sound twice, etc. When the time is exactly on the hour, the system produces a number of "​hour"​ pulses. Especially for hours after 12 (p.m for USA), 12 is subtracted from the 24hour number. So at 13:00 the bell sounds once, at 14:00 the bells sound twice, etc.
  
-The value of TIME_BELL_ON defines the time the relay will be at the ON state. Because we want to send a short pulse to the actuator, such as a mini solenoid electromagnet, ​300 milliseconds are OK. The value of TIME_BELL_ON is multiplied by 100 to produce the number of milliseconds.+The value of TIME_BELL_ON defines the time the relay will be at the ON state. Because we want to send a short pulse to the actuator, such as a mini solenoid electromagnet, ​100 milliseconds are OK. The value of TIME_BELL_ON is multiplied by 100 to produce the number of milliseconds.
  
-The value of TIME_BELL_OFF defines the time the relay will be at the ON state. We want the bell to reverberate for a while, so the value of 30 produces ​3000 milliseconds which is 3 seconds.+The value of TIME_BELL_OFF defines the time the relay will be at the ON state. We want the bell to reverberate for a while, so the value of 35 produces ​3500 milliseconds which is 3.5 seconds.
  
 <​code>​ <​code>​
Line 141: Line 146:
 ===== Source Code ===== ===== Source Code =====
  
-<file c++ arduino_art_clock_bell.ino>​ +The source code of the project [[arduino:art-clockbell-code|can be found here]].
-/* +
- * 10/9/2017 = 27/9/2019 +
- * CLOCK BELL project +
- *  +
- * Maintains time, synchronized with a Real Time Clock (RTC) and  +
- * rings a bell on the hour and on half hours +
- *  +
- * Modification so that triggered events may last not seconds but deci seconds +
- *  +
- * Example for ringing ​ hour 02:00:00 and 14:00:00 (num_bells = 2) +
- ​* ​      ​______________ ​           ______________ +
- ​* ​      ​I ​           I            I            I +
- ​* ​      ​I ​           I            I            I +
- * ______I ​           I____________I ​           I____________ +
- *  +
- ​* ​      <​--bell_on--><​---bell_off-><​--bell_on--><​--bell_off->​ +
- ​* ​      <​------------------ ring_on = true ----------------->​ +
- ​* ​       +
- ​* ​ Daylight Saving DST is still not supported ​     +
- ​* ​       +
- ​* ​ To display characters on the LCD display I use the libary ​     +
- ​* ​ LiquidCrystal I2C version 1.2 Author Frank de Brabander +
- ​* ​ Maintainer Marco Schwartz Websitehttps://​github.com/​marcoschwartz/​LiquidCrystal_I2C +
- ​* ​ which I downloaded from https://​www.arduinolibraries.info/​libraries/​liquid-crystal-i2-c ​     +
- ​* ​ WARNING: This library uses the pins of the PCF8574 driver differently than other libraries  +
- ​* ​ I have used in the past. I had to modify my wiring between PCF8574 and the LCD display.  +
- ​* ​ Currently, you can buy LCD displays which include a PCF8574 module at almost the same  +
- ​* ​ price as the LCD itself. Prefer this to save a lot of wiring. If the library does not work +
- ​* ​ properly, try the library LiquidCrystal_PCF8574 by Mattias Hertel +
- ​* ​      ​http://​www.mathertel.de/​Arduino/​LiquidCrystal_PCF8574.aspx +
- ​* ​       +
- ​* ​      +
- ​* ​ 27/2/2019 Added NightMode, meaning that if active, the bell will not ring      +
- ​* ​ on late night hours (actually on selected hours, but NightMode was a catchy phrase!!). +
- ​* ​  +
- ​* ​ When the last character of the first line shows a Lock symbol, this means that  +
- ​* ​ clock is in silent mode and will not ring on silent hours. +
- ​* ​ If the bell character is shown, the bell will sound for all hours +
- ​* ​  +
- ​* ​ When the last character of the second line shows a music/note symbol +
- ​* ​ this means that this is not a silent hour. If it is blank, it is a silent hour. +
- ​* ​  +
- ​* ​  +
- */ +
- +
-// To use an I2C LCD 2 lines by 16 columns, at I2C address 0x38  +
-#include <​Wire.h>​  +
-#include <​LiquidCrystal_I2C.h>​ +
- +
-#define LCD_ADDRESS 0x38 +
- +
-#include "​RTClib.h" ​ //RTClib by NeiroN +
-//DS1307 rtc; +
-DS3231 rtc; +
- +
-DateTime now; +
- +
-#define LED_PIN ​ LED_BUILTIN // LED that flashes every sec. If you want to  use another pin rather than the built-in led, set it here +
-#define BELL_PIN 7          // Output that controls the bell +
-#define BELL_HALF_PIN 7     // Output that controls the half hour bell. Can be same as hour pin +
- +
- +
-// Input switches +
-#define SET_HOURS_PIN 6      // Pressing the button increases hours by one +
-#define SET_MINUTES_PIN 5    // Pressing the button once increases minutes by one +
-#define SET_SECONDS_PIN 4   // Pressing the button once sets seconds to zero +
- +
-// The name NIGHT_MODE_PIN was probably taken by some library and caused error +
-// Had to name it NIGHT_PIN +
-#define NIGHT_PIN 3  // If set to LOW, by connecting the two pin headers, night mode will be inactive +
- +
-#define DEBOUNCING_TIME 2    // Debouncing time for reading switches in msec +
- +
-#define CLOCK_CORRECTION 0  // msec If crystal gains or lags, we can fix the clock making it run faster (negative value) or slower (positive value) +
-                     +
-/* +
- * One ring is composed ​of several bells. +
- * Each bell has a bell_on time of duration SEC_BELL_ON (in sec) +
- * and a bell_off time of duration SEC_BELL_OFF (in sec) +
- *  +
- */ +
-#define TIME_BELL_ON 1  // times 100 milliseconds. The max achieved time is 25,5 sec for a value of 255 +
-#define TIME_BELL_OFF 35  // times 100 milliseconds. The max achieved time is 25,5 sec for a value of 255                          +
- +
-// Define ​the silent hours, i.e. those that the bell will not ring +
-int silent_hours[] = { 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7 }; +
- +
-// Definition of a custom bell, speaker and sound images +
-// from https://​lastminuteengineers.com/​arduino-1602-character-lcd-tutorial/​ +
-byte Bell[8] = { +
-0b00100, +
-0b01110, +
-0b01110, +
-0b01110, +
-0b11111, +
-0b00000, +
-0b00100, +
-0b00000 +
-}; +
- +
-byte Speaker[8] = { +
-0b00001, +
-0b00011, +
-0b01111, +
-0b01111, +
-0b01111, +
-0b00011, +
-0b00001, +
-0b00000 +
-}; +
- +
- +
-byte Sound[8] = { +
-0b00001, +
-0b00011, +
-0b00101, +
-0b01001, +
-0b01001, +
-0b01011, +
-0b11011, +
-0b11000 +
-}; +
- +
-byte Lock[8] = { +
-0b01110, +
-0b10001, +
-0b10001, +
-0b11111, +
-0b11011, +
-0b11011, +
-0b11111, +
-0b00000 +
-}; +
- +
-// Volatile variables because they can be used in the interrupt service routine +
- +
-// Declares the time variables and set to initial value.  +
-volatile int hours = 00;  +
-volatile int minutes = 00; +
-volatile int seconds = 05;   +
-volatile int deci_seconds = 00; +
- +
-// In order to set the RTC, I need those +
-volatile int myyear; +
-volatile int mymonth; +
-volatile int myday; +
- +
-volatile unsigned long current_time;​ +
-volatile unsigned long previous_time = millis(); +
-volatile unsigned long deci_previous_time = millis(); +
- +
-// Variables to represent the timing of each hit of the hammer to the bell +
-// Values are in seconds +
-volatile int t_bell_on = 0; +
-volatile int t_bell_off = 0; +
- +
-// Boolean to monitor if a ringing process (several bells) is active +
-boolean ring_on = false; +
- +
-// Indicates that ringing is taking place for the half hour +
-// Used only to select the bell pin +
-boolean ring_30min = false; +
- +
-boolean bell_on = false; // Indicates that we are at the HIGH period of the bell pulse +
-boolean bell_off = false;// Indicates that we are at the LOW period of the bell pulse +
- +
-int num_bells = 0; +
- +
-int correction = CLOCK_CORRECTION;​ +
-int interval = 100 + correction;​ +
- +
-boolean night_mode = true; // Indicates if night mode is set, by reading the input pin +
- +
-LiquidCrystal_I2C lcd(LCD_ADDRESS,​16,​2); ​  +
-    +
-void setup() +
-+
- +
-   +
-  lcd.init();​ +
-  lcd.backlight();​ +
-  +
-  pinMode(LED_PIN,​ OUTPUT); +
-  pinMode(BELL_PIN,​ OUTPUT); +
-  digitalWrite(BELL_PIN,​ LOW); +
-  pinMode(BELL_HALF_PIN,​ OUTPUT); +
-  digitalWrite(BELL_HALF_PIN,​ LOW);   +
-   +
-  pinMode(SET_HOURS_PIN,​ INPUT_PULLUP);​ +
-  pinMode(SET_MINUTES_PIN,​ INPUT_PULLUP);​ +
-  pinMode(SET_SECONDS_PIN,​ INPUT_PULLUP);​ +
- +
-  pinMode(NIGHT_PIN,​ INPUT_PULLUP);​ +
- +
-  lcd.createChar(0,​ Bell); +
-  lcd.createChar(1,​ Speaker); +
-  lcd.createChar(2,​ Sound); +
-  lcd.createChar(3,​ Lock); +
- +
-  sync_from_RTC();​ +
- +
-  Serial.begin(9600);​ +
- +
-+
- +
-// Runs every interval (100msec = 1ds) +
-void every_interval() ​      +
-{  +
-  // Handle the bell timing +
-  // Decrement all bell timers. We do not care if they are active or not +
-  if (t_bell_on > 0) +
-    t_bell_on --; +
- +
-  if (t_bell_off > 0) +
-    t_bell_off --;   +
- +
-  deci_seconds ++; +
-  if (deci_seconds >= 10) {    +
-    deci_seconds = 0;        +
-    every_second(); ​     +
-  }    +
-  return; +
-+
- +
- +
-void every_second() +
-+
-    seconds ++;         +
-    if (seconds >= 60) {  +
-      seconds = 0;     +
-      minutes ++;            +
-      if (minutes >= 60) { +
-        minutes = 0; +
-        hours ++;        +
-        if (hours >= 24) { +
-          hours = 0; +
-        } +
-      }    +
-    }  +
-   // ​ IT IS WRONG TO PLACE HERE show_time_on_lcd();​ . IT DISTURBS TIMING +
-   // ​ IT STEELS TIME (approx 4msec) FROM TIME_BELL_ON +
-   // ​ LCD handling must be placed in loop2() ​       +
-+
- +
- +
-// Presents the time on the LCD +
-void show_time_on_lcd() +
-+
- +
-  // print date on line 1 of LCD +
-  lcd.setCursor(2,​0);​ +
-  if (myday < 10) +
-    lcd.print("​0"​); ​  +
-  lcd.print(myday);​ +
-  lcd.print("​."​);​ +
-  if (mymonth < 10); +
-    lcd.print("​0"​);​ +
-  lcd.print(mymonth);​ +
-  lcd.print("​."​);​ +
-  lcd.print(myyear);​ +
- +
-  // print hour on line 2 of LCD    +
-  lcd.setCursor(3,​1);​ +
-  if (hours < 10) +
-    lcd.print("​0"​);​ +
-  lcd.print(hours);​ +
-  lcd.print(":"​);​ +
-  if (minutes < 10) +
-    lcd.print("​0"​);​ +
-  lcd.print(minutes);​ +
-  lcd.print(":"​);​ +
-  if (seconds < 10) +
-    lcd.print("​0"​);​ +
-  lcd.print(seconds);​  +
- +
-  // Prints an indication for Night Mode +
-  lcd.setCursor(15,​0);​ +
-  if (night_mode) +
-    lcd.write(byte(3));​ // Lock symbol +
-  else +
-    lcd.write(byte(0)); ​ // Bell symbol +
- +
-  // Prints an indication if the hour is in the list of silent hours +
-  lcd.setCursor(15,​1);​ +
-  if (silent_hour(hours)) +
-    lcd.print("​ "​); ​  +
-  else +
-    lcd.write(byte(2)); ​  // Sound symbol  +
-     +
-  return;  +
-+
- +
-/* +
- * Do not use loop() for user code, because it handles timing. +
- * Use loop2() instead +
- */ +
-void loop() +
-+
-  current_time = millis(); +
-  if (current_time - previous_time >= interval) { +
-    every_interval();​  +
-    previous_time = current_time; ​  +
-  } +
-    +
-  // do the normal work +
-  loop2(); ​   +
-+
- +
- +
- +
-// User code goes here +
-void loop2() +
-+
-  // Synchronize with RTC every minute at xx:​50:​00:​00 +
-  // Close to the end of the minute to avoid interacting with  +
-  // actions that normally occur at xx:​00:​00:​00 +
-  if (seconds == 50 && deci_seconds == 0) { +
-    sync_from_RTC();​ +
-     +
-  } +
-   +
-  if (ring_on) +
-    process_ringing();​ +
-  else { +
-    if (seconds == 0  && deci_seconds == 0) { +
-      // Addition for night mode  +
-      if (minutes == 0) +
-         if (!night_mode) +
-           ​ring(hours);​ +
-         ​else ​ if (!silent_hour(hours)) +
-            ring(hours);​ +
-         else { +
-            // be silent ​             +
-         } +
-           +
-      else { +
-        if (minutes == 30) +
-          if (!night_mode) +
-            ring_half();​ +
-          else  if (!silent_hour(hours)) +
-            ring_half();​ +
-         else { +
-            // be silent ​             +
-         }  +
-               +
-      }      +
-    }         +
-  } +
- +
-  // LCD handling +
-  static int previous_seconds;​ +
-  if (seconds != previous_seconds) { +
-    show_time_on_lcd();​ +
-    previous_seconds = seconds; +
-  } +
- +
-  // Set time manually via switches +
-  read_switches();​ +
-   +
-  return; +
-+
- +
-void read_switches()  +
-+
-  if (digitalRead(SET_HOURS_PIN) == LOW) { +
-    delay(DEBOUNCING_TIME);​ +
-    if (digitalRead(SET_HOURS_PIN) == LOW) { +
-      hours = (hours + 1) % 24; +
-    } +
-    while (digitalRead(SET_HOURS_PIN) == LOW){  +
-    } +
-    set_RTC();​ +
-  }   +
-   +
-  if (digitalRead(SET_MINUTES_PIN) == LOW) { +
-    delay(DEBOUNCING_TIME);​ +
-    if (digitalRead(SET_MINUTES_PIN) == LOW) { +
-      minutes = (minutes + 1) % 60; +
-    }   +
-    while (digitalRead(SET_MINUTES_PIN) == LOW) {   +
-    } +
-    set_RTC();​ +
-  }   +
- +
-  if (digitalRead(SET_SECONDS_PIN) == LOW) { +
-    delay(DEBOUNCING_TIME);​ +
-    if (digitalRead(SET_SECONDS_PIN) == LOW) { +
-      seconds = 00;  +
-      deci_seconds = 0;  +
-    }   +
-    while (digitalRead(SET_SECONDS_PIN) == LOW) {   +
-    } +
-    set_RTC();​ +
-  }  +
- +
-  if (digitalRead(NIGHT_PIN) == LOW) { +
-     ​night_mode = false; // Headers are connected, bells ring all round the clock     +
-  } else { +
-    night_mode = true; +
-  } +
-  return; ​   +
-+
- +
-void set_RTC() +
-+
-   +
-  rtc.adjust(DateTime(myyear,​ mymonth, myday, hours, minutes, seconds));​ +
-  return; +
-+
- +
-void start_bell() +
-+
-  int bell_pin; +
-  if (ring_30min) +
-    bell_pin = BELL_HALF_PIN;​ +
-  else +
-    bell_pin = BELL_PIN; ​  +
-  digitalWrite(bell_pin,​ HIGH); +
-  digitalWrite(LED_PIN,​ HIGH); +
-  bell_on = true; +
-  bell_off = false; +
-  t_bell_on = TIME_BELL_ON;​  +
-  return; +
-+
- +
-void stop_bell() +
-+
-  int bell_pin; +
-  if (ring_30min) +
-    bell_pin = BELL_HALF_PIN;​ +
-  else +
-    bell_pin = BELL_PIN; ​  +
-  digitalWrite(bell_pin,​ LOW); +
-  digitalWrite(LED_PIN,​ LOW); +
-  bell_on = false; +
-  bell_off = true; +
-  t_bell_off = TIME_BELL_OFF;​  +
-  return; +
-+
- +
-/* Rings the bell, depending on the hour provided. +
-   The hours variable works on the 24 hour format +
-   but we want to ring the bell as per the 12 hour +
-   +
-   Skips ringing if previous hours is same. +
-   This condition may happen if we have a ring and after some time, +
-   the RTC synchronizes the internal clock and we go back to xx:00:00 +
-   This functionality is only for full hours. Half hours and 01:00:00 are  +
-   ​totally separated +
-    +
-*/  +
-void ring(int num_hours) +
-+
-  static int previous_hours = 0; +
- +
-  // protect from setting back hours via switches or sync with RTC +
-  if (num_hours == previous_hours)  +
-    return; +
-     +
-  previous_hours = num_hours; ​  +
-  ring_on = true; +
-  if (num_hours > 12) +
-    num_bells = num_hours - 12; +
-  else if (num_hours == 0)  +
-    num_bells = 12; +
-  else   +
-    num_bells = num_hours;​ +
-  start_bell(); ​  +
-  return; ​  +
-+
- +
-// Ring at half hour +
-void ring_half() +
-+
-  // Is there a way to protect from syncing back to xx:30:00 ???? +
-   +
-  ring_30min = true;  +
-  ring_on = true; +
-  num_bells = 1; +
-  start_bell();​ +
-  return; +
-+
- +
-void process_ringing() +
-+
-  if (bell_on && t_bell_on <= 0) +
-    stop_bell();​ +
-   +
-  if (bell_off && t_bell_off <= 0) { +
-    num_bells --; +
-    if (num_bells > 0) +
-      start_bell();​ +
-    else { +
-      bell_off = false; +
-      ring_on = false;  +
-      ring_30min = false; ​   +
-    } +
-  } +
-  return; +
-+
- +
-void send_time_to_serial() +
-+
-  Serial.print("​Time:​ ");  +
-  if (hours , 10) +
-    Serial.print("​0"​);​ +
-  Serial.print(hours);​ +
-  Serial.print(":"​);​ +
-  if (minutes < 10) +
-    Serial.print("​0"​);​ +
-  Serial.print(minutes);​ +
-  Serial.print(":"​);​ +
-  if (seconds < 10) +
-    Serial.print("​0"​);​ +
-  Serial.println(seconds); ​  +
-+
- +
-// Presents variables on LCD for debugging +
-void show_state_on_lcd() +
-+
-  // Clear line 1 +
-  lcd.setCursor(0,​1);​ +
-  lcd.print(" ​               "); +
-   +
-  lcd.setCursor(0,​1);​ +
-  lcd.print("​R:"​);​ +
-    if (ring_on) { +
-      lcd.print("​ON"​);​ +
-      lcd.print("​ B="​);​ +
-      lcd.print(num_bells);​ +
-      lcd.print("​ TB="​);​ +
-      lcd.print(t_bell_on);​ +
-      lcd.print("​ TBO="​);​ +
-      lcd.print(t_bell_off); ​      +
-       +
-    }   +
-    else +
-      lcd.print("​OFF"​);​ +
-   +
-  +
-+
- +
-void sync_from_RTC() +
-+
-  now = rtc.now();​ +
-   +
-  myyear = now.year();​ +
-  mymonth = now.month();​ +
-  myday = now.day();​ +
-  hours = now.hour();​ +
-  minutes = now.minute();​ +
-  seconds = now.second(); ​  +
-+
- +
-// checks if an int value is included in an array of ints +
-boolean in_int_array(int value, const int *arr, int num_elements) { +
- +
-  for (int i=0; i< num_elements ; ++i ) { +
-    if (value == arr[i]) +
-      return true;        +
-  } +
-  return false; ​  +
-+
- +
-// check if a given hour is silent +
-boolean silent_hour(int this_hour) { +
- +
-  if (in_int_array(this_hour,​ silent_hours,​ sizeof(silent_hours)/​sizeof(int))) +
-    return true; +
-  else +
-    return false; ​  +
-}+
  
-</​file>​+~~DISQUS~~
arduino/art-clockbell.1552658768.txt.gz · Last modified: 2019/03/15 16:06 by Ilias Iliopoulos