1 | #include <SPI.h> //Library for Adafruit communication to display
|
2 | #include <Wire.h> //I2C communication library
|
3 | #include "ds3231.h" //Real Time Clock library
|
4 | #include "DHT.h" //DHT Sensor Library
|
5 | #include <Adafruit_GFX.h> //Graphics library
|
6 | #include <Adafruit_PCD8544.h> //Display library
|
7 |
|
8 | //PCD pin setting
|
9 | Adafruit_PCD8544 display = Adafruit_PCD8544(2, 3, 4, 5, 6);
|
10 |
|
11 | //DHT setting
|
12 | #define DHTPIN 9
|
13 | #define DHTTYPE DHT11
|
14 | DHT dht(DHTPIN, DHTTYPE);
|
15 |
|
16 | uint8_t secset = 0; //Index for second RTC setting
|
17 | uint8_t minset = 1; //Index for minute RTC setting
|
18 | uint8_t hourset = 2; //Index for hour RTC setting
|
19 | uint8_t wdayset = 3; //Index for weekday RTC setting
|
20 | uint8_t mdayset = 4; //Index for date RTC setting
|
21 | uint8_t monset = 5; //Index for month RTC setting
|
22 | uint8_t yearset = 6; //Index for year RTC setting
|
23 |
|
24 | //Alarm 1 time variables
|
25 | uint8_t wake_HOUR_1 = 0;
|
26 | uint8_t wake_MINUTE_1 = 0;
|
27 | uint8_t wake_SECOND_1 = 0;
|
28 | uint8_t wake_SET_1 = 1; //Default alarm to ON in case of power failure or reset
|
29 |
|
30 | //Alarm 2 time variables
|
31 | uint8_t wake_HOUR_2 = 0;
|
32 | uint8_t wake_MINUTE_2 = 0;
|
33 | uint8_t wake_SECOND_2 = 0;
|
34 | uint8_t wake_SET_2 = 1; //Default alarm to ON in case of power failure or reset
|
35 |
|
36 | unsigned long prev, interval = 100; //Variables for display/clock update rate
|
37 | byte flash = 0; //Flag for display flashing - toggle once per update interval
|
38 | byte mode = 0; //Mode for time and date setting
|
39 | int tempset; //Temporary variable for setting time/date
|
40 | int beepcount = 0; //Variable for number of 100ms intervals since alarm started sounding
|
41 |
|
42 | void setup()
|
43 | {
|
44 | Serial.begin(9600); //Initialize serial port, if needed (not used)
|
45 |
|
46 | Wire.begin(); //Initialize I2C communication library
|
47 |
|
48 | dht.begin();
|
49 |
|
50 | DS3231_init(0x00); //Initialize Real Time Clock for 1Hz square wave output (no RTC alarms on output pin)
|
51 |
|
52 | pinMode(7, INPUT); //Set pin for time/date mode button to input
|
53 | digitalWrite(7, HIGH); //Turn on pullup resistors
|
54 |
|
55 | pinMode(8, INPUT); //Set pin for time/date set button to input
|
56 | digitalWrite(8, HIGH); //Turn on pullup resistors
|
57 |
|
58 | pinMode(10, OUTPUT); //Set pin for alarm 1 relay
|
59 | digitalWrite(10, LOW); //Initialize alarm 1 relay to off-state
|
60 |
|
61 | pinMode(11, OUTPUT); //Set pin for alarm 2 relay
|
62 | digitalWrite(11, LOW); //Initialize alarm 2 relay to off-state
|
63 |
|
64 | display.begin(); // Initialize display
|
65 | display.setTextSize(1); //Set default font size to the smalles
|
66 | display.setTextColor(BLACK); //Set font to display color on black background
|
67 | }
|
68 |
|
69 | void loop()
|
70 | {
|
71 | unsigned long now = millis();
|
72 | struct ts t; //Time-structure for retrieving and storing time and date data RTC
|
73 |
|
74 | //Draw and update display every refresh period (100ms)
|
75 | if ((now - prev > interval)) { //Determine whether to start a time and screen update
|
76 | if (flash == 0) {
|
77 | flash = 1; //Toggle flash flag for cursor blinking later
|
78 | } else {
|
79 | flash = 0;
|
80 | }
|
81 |
|
82 | DS3231_get(&t); //Get time and date and save in t structure
|
83 |
|
84 | get_alarm_1(); //Retrieve current alarm setting
|
85 |
|
86 | get_alarm_2(); //Retrieve current alarm setting
|
87 |
|
88 | digitalWrite(10, LOW); //Turn off external alarm for flashing
|
89 |
|
90 | display.clearDisplay(); //Clear display buffer from last refresh
|
91 |
|
92 | //Alarm 1 trigger
|
93 |
|
94 | if (wake_SET_1 && DS3231_triggered_a1()) { //Display/sound alarm if enabled and triggered
|
95 | beepcount = beepcount + 1;
|
96 | if (beepcount <= 600) { //Sound alarm for 60 seconds
|
97 | if (!flash) { //Flash display and sound interrupted beeper
|
98 | digitalWrite(10, HIGH); //Flash external alarm if alarm triggered, regardless of mode
|
99 | }
|
100 | }
|
101 | else {
|
102 | beepcount = 0; //If alarm has sounded for 1 minute, reset alarm timer -counter and -flag
|
103 | DS3231_clear_a1f();
|
104 | }
|
105 | }
|
106 |
|
107 | //Alarm 2 trigger
|
108 |
|
109 | if (wake_SET_2 && DS3231_triggered_a2()) { //Display/sound alarm if enabled and triggered
|
110 | beepcount = beepcount + 1;
|
111 | if (beepcount <= 600) { //Sound alarm for 60 seconds
|
112 | if (!flash) { //Flash display and sound interrupted beeper
|
113 | digitalWrite(10, LOW); //Flash external alarm if alarm triggered, regardless of mode
|
114 | }
|
115 | }
|
116 | else {
|
117 | beepcount = 0; //If alarm has sounded for 1 minute, reset alarm timer -counter and -flag
|
118 | DS3231_clear_a2f();
|
119 | }
|
120 | }
|
121 |
|
122 | //DISPLAY TIME, ALARM, TEMPERATURE AND HUMIDITY
|
123 |
|
124 | //Display time
|
125 |
|
126 | if (mode <= 9) {
|
127 |
|
128 | display.setCursor(16, 0); //Position text cursor for time display
|
129 |
|
130 | if (t.hour < 10) {
|
131 | display.print(" "); //Add leading space if single-digit hour
|
132 | }
|
133 | display.print(t.hour);
|
134 |
|
135 | display.print(":"); //Display hour-minute separator
|
136 |
|
137 | if (t.min < 10) {
|
138 | display.print("0"); //Add leading zero if single-digit minute
|
139 | }
|
140 | display.print(t.min); //Display retrieved minutes
|
141 |
|
142 | display.print(":"); //Display minute-seconds separator
|
143 |
|
144 | if (t.sec < 10) {
|
145 | display.print("0"); //Add leading zero for single-digit seconds
|
146 | }
|
147 | display.print(t.sec); //Display retrieved seconds
|
148 |
|
149 | //Display Alarm
|
150 |
|
151 | if (mode > 9) {
|
152 |
|
153 | display.setCursor(0, 9); //Position text cursor for alarm 1 display
|
154 |
|
155 | if (wake_HOUR_1 < 10) {
|
156 | display.print(" "); //Add leading space if single-digit hour
|
157 | }
|
158 | display.print(wake_HOUR_1);
|
159 |
|
160 | display.print(":"); //Display hour-minute separator
|
161 |
|
162 | if (wake_MINUTE_1 < 10) {
|
163 | display.print("0"); //Add leading zero if single-digit minute
|
164 | }
|
165 | display.print(wake_MINUTE_1); //Display retrieved minutes
|
166 |
|
167 | display.setCursor(40, 9); //Position text cursor for alarm 2 display
|
168 |
|
169 | if (wake_HOUR_2 < 10) {
|
170 | display.print(" "); //Add leading space if single-digit hour
|
171 | }
|
172 | display.print(wake_HOUR_2);
|
173 |
|
174 | display.print(":"); //Display hour-minute separator
|
175 |
|
176 | if (wake_MINUTE_2 < 10) {
|
177 | display.print("0"); //Add leading zero if single-digit minute
|
178 | }
|
179 | display.print(wake_MINUTE_2); //Display retrieved minutes
|
180 | }
|
181 |
|
182 | // Reading DHT sensor
|
183 | float hum = dht.readHumidity();
|
184 | float tmp = dht.readTemperature(); // Read temperature as Celsius (the default)
|
185 | float fh = dht.readTemperature(true); // Read temperature as Fahrenheit (isFahrenheit = true)
|
186 |
|
187 | // Check if any reads failed and exit early (to try again).
|
188 | if (isnan(hum) || isnan(tmp) || isnan(fh)) {
|
189 | Serial.println("Fehler: Klimasensor");
|
190 | display.setCursor(1, 20);
|
191 | display.print("Fehler: Klimasensor");
|
192 | return;
|
193 | }
|
194 |
|
195 | //Display temperature and humidity
|
196 |
|
197 | display.setCursor(1, 20);
|
198 | display.print("Tmp.:");
|
199 | display.print(tmp, 1);
|
200 | display.print((char)247);
|
201 | display.print("C");
|
202 | display.setCursor(1, 28);
|
203 | display.print("Hum.:");
|
204 | display.print(hum, 0);
|
205 | display.print("%");
|
206 |
|
207 | }
|
208 |
|
209 | //Time, alarm, temperature and humidity settings button processing and settings cursor flashing
|
210 | //Digital and analog time/date display updates with new settings at 5Hz as settings are changed
|
211 |
|
212 | switch (mode)
|
213 | {
|
214 | case 0: break;
|
215 |
|
216 | case 1: //Hour setting
|
217 | if (flash) {
|
218 | display.drawRect(15, 0, 12, 8, BLACK); //Flash cursor (5Hz)
|
219 | }
|
220 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
221 | tempset = t.hour; //Get the current hour and save in temporary variable
|
222 | tempset = tempset + 1; //Increment the hour at 5Hz rate
|
223 | if (tempset > 23) {
|
224 | tempset = 0; //Roll over hour after 23rd hour (setting done in 24-hour mode)
|
225 | }
|
226 | t.hour = tempset; //After each update, write the hour to the time structure
|
227 | set_rtc_field(t, hourset); //Write the set field only to the RTC after each update
|
228 | }
|
229 | break;
|
230 |
|
231 | case 2: //Minute setting
|
232 | if (flash) {
|
233 | display.drawRect(33, 0, 12, 8, BLACK); //Flash cursor (5Hz)
|
234 | }
|
235 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
236 | tempset = t.min; //Get the current minute and save in temporary variable
|
237 | tempset = tempset + 1; //Increment the minute at 5Hz rate
|
238 | if (tempset > 59) {
|
239 | tempset = 0; //Roll over minute to zero after 59th minute
|
240 | }
|
241 | t.min = tempset; //After each update, write the minute to the time structure
|
242 | set_rtc_field(t, minset); //Write the set field only to the RTC after each update
|
243 | }
|
244 | break;
|
245 |
|
246 | //Set clock + 1 minute, then press and hold to freeze second setting.
|
247 | //Release button at 00 seconds to synchronize clock to external time source.
|
248 |
|
249 | case 3: //Second synchronization
|
250 | if (flash) {
|
251 | display.drawRect(51, 0, 12, 8, BLACK); //Flash cursor (5Hz)
|
252 | }
|
253 | if (!digitalRead(9) && (!flash)) { //Reset second to zero at 5Hz rate if button held down
|
254 | t.sec = 0; //After each update, write the zeroed second to the time structure
|
255 | set_rtc_field(t, secset); //Write the set field only to the RTC after each update
|
256 | }
|
257 | break;
|
258 |
|
259 | case 4: //Alarm 1 hour setting
|
260 | if (flash) {
|
261 | display.drawRect(0, 9, 12, 8, BLACK); //Flash cursor (5Hz)
|
262 | }
|
263 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
264 | tempset = wake_HOUR_1; //Get the current hour and save in temporary variable
|
265 | tempset = tempset + 1; //Increment the hour at 5Hz rate
|
266 | if (tempset > 23) {
|
267 | tempset = 0; //Roll over hour after 23rd hour (setting done in 24-hour mode)
|
268 | }
|
269 | wake_HOUR_1 = tempset; //Write the hour to the alarm variable after each update
|
270 | set_alarm_1(); //Write the alarm setting to the RTC after each update
|
271 | }
|
272 | break;
|
273 |
|
274 | case 5: //Alarm 1 minute setting
|
275 | if (flash) {
|
276 | display.drawRect(18, 9, 12, 8, BLACK); //Flash cursor (5Hz)
|
277 | }
|
278 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
279 | tempset = wake_MINUTE_1; //Get the current minute and save in temporary variable
|
280 | tempset = tempset + 1; //Increment the minute at 5Hz rate
|
281 | if (tempset > 59) {
|
282 | tempset = 0; //Roll over minute to zero after 59th minute
|
283 | }
|
284 | wake_MINUTE_1 = tempset; //Write the minute to the alarm variable after each update
|
285 | set_alarm_1(); //Write the alarm setting to the RTC after each update
|
286 | }
|
287 | break;
|
288 |
|
289 | case 6: //Alarm 1 enable/disable
|
290 | if (flash) {
|
291 | display.drawRect(0, 0, 6, 8, BLACK); //Display rectangle cursor every other display update (5Hz)
|
292 | }
|
293 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
294 | if (wake_SET_1) {
|
295 | wake_SET_1 = 0; //Toggle alarm on/of variable at 5 Hz
|
296 | } else {
|
297 | wake_SET_1 = 1;
|
298 | }
|
299 | }
|
300 | break;
|
301 |
|
302 | case 7: //Alarm 2 hour setting
|
303 | if (flash) {
|
304 | display.drawRect(40, 9, 12, 8, BLACK); //Flash cursor (5Hz)
|
305 | }
|
306 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
307 | tempset = wake_HOUR_2; //Get the current hour and save in temporary variable
|
308 | tempset = tempset + 1; //Increment the hour at 5Hz rate
|
309 | if (tempset > 23) {
|
310 | tempset = 0; //Roll over hour after 23rd hour (setting done in 24-hour mode)
|
311 | }
|
312 | wake_HOUR_2 = tempset; //After each update, write the hour back to the alarm variable
|
313 | set_alarm_2(); //Write the alarm setting back to the RTC after each update
|
314 | }
|
315 | break;
|
316 |
|
317 | case 8: //Alarm 2 minute setting
|
318 | if (flash) {
|
319 | display.drawRect(58, 9, 12, 8, BLACK); //Display rectangle cursor every other display update (5Hz blink)
|
320 | }
|
321 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
322 | tempset = wake_MINUTE_2; //Get the current minute and save in temporary variable
|
323 | tempset = tempset + 1; //Increment the minute at 5Hz rate
|
324 | if (tempset > 59) {
|
325 | tempset = 0; //Roll over minute to zero after 59th minute
|
326 | }
|
327 | wake_MINUTE_2 = tempset; //After each update, write the minute back to the alarm variable
|
328 | set_alarm_2(); //Write the alarm setting back to the RTC after each update
|
329 | }
|
330 |
|
331 | break;
|
332 |
|
333 | case 9: //Alarm 2 enable/disable
|
334 | if (flash) {
|
335 | display.drawRect(0, 0, 6, 8, BLACK); //Display rectangle cursor every other display update (5Hz blink)
|
336 | }
|
337 | if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
|
338 | if (wake_SET_2) {
|
339 | wake_SET_2 = 0; //Toggle alarm on/of variable at 5 Hz
|
340 | } else {
|
341 | wake_SET_2 = 1;
|
342 | }
|
343 | }
|
344 | break;
|
345 |
|
346 | prev = now; //Reset variable for display and time update rate
|
347 | display.display(); //Display the constructed frame buffer for this framecount
|
348 | }
|
349 |
|
350 | //Clock setting mode set - outside time/display update processing for faster button response
|
351 | if (!digitalRead(8)) { //Read setting mode button
|
352 | delay(25); //25ms debounce time
|
353 | if (!digitalRead(8)) { //Activate setting mode change after 100ms button press
|
354 | mode = mode + 1; //Increment the time setting mode on each button press
|
355 | if (mode > 9) {
|
356 | mode = 0; //Roll the mode setting after 9th mode
|
357 | }
|
358 | while (!digitalRead(8)) {} //Wait for button release
|
359 | }
|
360 | }
|
361 |
|
362 | if (!digitalRead(9)) { //Reset alarm flag if set button pressed
|
363 | delay(25); //25ms debounce time
|
364 | if (!digitalRead(9)) {
|
365 | DS3231_clear_a1f(); //Clear alarm flag if set button pressed - insures alarm reset when turning alarm on
|
366 | }
|
367 | }
|
368 | }
|
369 | }
|
370 | //Subroutine to adjust a single date/time field in the RTC
|
371 | void set_rtc_field(struct ts t, uint8_t index)
|
372 | {
|
373 | uint8_t century;
|
374 |
|
375 | if (t.year > 2000) {
|
376 | century = 0x80;
|
377 | t.year_s = t.year - 2000;
|
378 | } else {
|
379 | century = 0;
|
380 | t.year_s = t.year - 1900;
|
381 | }
|
382 |
|
383 | uint8_t TimeDate[7] = { t.sec, t.min, t.hour, t.wday, t.mday, t.mon, t.year_s };
|
384 |
|
385 | Wire.beginTransmission(DS3231_I2C_ADDR);
|
386 | Wire.write(index);
|
387 | TimeDate[index] = dectobcd(TimeDate[index]);
|
388 | if (index == 5) {
|
389 | TimeDate[5] += century;
|
390 | }
|
391 | Wire.write(TimeDate[index]);
|
392 | Wire.endTransmission();
|
393 |
|
394 | //Adjust the month setting, per data sheet, if the year is changed
|
395 | if (index == 6) {
|
396 | Wire.beginTransmission(DS3231_I2C_ADDR);
|
397 | Wire.write(5);
|
398 | TimeDate[5] = dectobcd(TimeDate[5]);
|
399 | TimeDate[5] += century;
|
400 | Wire.write(TimeDate[5]);
|
401 | Wire.endTransmission();
|
402 | }
|
403 | }
|
404 |
|
405 | //Subroutine to set alarm 1
|
406 | void set_alarm_1()
|
407 | {
|
408 |
|
409 | // flags define what calendar component to be checked against the current time in order
|
410 | // to trigger the alarm - see datasheet
|
411 | // A1M1 (seconds) (0 to enable, 1 to disable)
|
412 | // A1M2 (minutes) (0 to enable, 1 to disable)
|
413 | // A1M3 (hour) (0 to enable, 1 to disable)
|
414 | // A1M4 (day) (0 to enable, 1 to disable)
|
415 | // DY/DT (dayofweek == 1/dayofmonth == 0)
|
416 | byte flags[5] = { 0, 0, 0, 1, 1 }; //Set alarm to trigger every 24 hours on time match
|
417 |
|
418 | // set Alarm1
|
419 | DS3231_set_a1(0, wake_MINUTE_1, wake_HOUR_1, 0, flags); //Set alarm 1 RTC registers
|
420 |
|
421 | }
|
422 |
|
423 | //Subroutine to set alarm 2
|
424 | void set_alarm_2()
|
425 | {
|
426 |
|
427 | // flags define what calendar component to be checked against the current time in order
|
428 | // to trigger the alarm - see datasheet
|
429 | // A1M1 (seconds) (0 to enable, 1 to disable)
|
430 | // A1M2 (minutes) (0 to enable, 1 to disable)
|
431 | // A1M3 (hour) (0 to enable, 1 to disable)
|
432 | // A1M4 (day) (0 to enable, 1 to disable)
|
433 | // DY/DT (dayofweek == 1/dayofmonth == 0)
|
434 | byte flags[5] = { 0, 0, 0, 1, 1 }; //Set alarm to trigger every 24 hours on time match
|
435 |
|
436 | // set Alarm2
|
437 | DS3231_set_a2(0, wake_MINUTE_2, wake_HOUR_2, flags); //Set alarm 2 RTC registers
|
438 |
|
439 | }
|
440 |
|
441 | //Subroutine to get alarm 1
|
442 | void get_alarm_1()
|
443 | {
|
444 | uint8_t n[4];
|
445 | uint8_t t[4]; //second,minute,hour,day
|
446 | uint8_t f[5]; // flags
|
447 | uint8_t i;
|
448 |
|
449 | Wire.beginTransmission(DS3231_I2C_ADDR);
|
450 | Wire.write(DS3231_ALARM1_ADDR);
|
451 | Wire.endTransmission();
|
452 |
|
453 | Wire.requestFrom(DS3231_I2C_ADDR, 4);
|
454 |
|
455 | for (i = 0; i <= 3; i++) {
|
456 | n[i] = Wire.read();
|
457 | f[i] = (n[i] & 0x80) >> 7;
|
458 | t[i] = bcdtodec(n[i] & 0x7F);
|
459 | }
|
460 |
|
461 | f[4] = (n[3] & 0x40) >> 6;
|
462 | t[3] = bcdtodec(n[3] & 0x3F);
|
463 |
|
464 | wake_SECOND_1 = t[0];
|
465 | wake_MINUTE_1 = t[1];
|
466 | wake_HOUR_1 = t[2];
|
467 | }
|
468 |
|
469 | //Subroutine to get alarm 2
|
470 | void get_alarm_2()
|
471 | {
|
472 | uint8_t n[3];
|
473 | uint8_t t[3]; //second,minute,hour,day
|
474 | uint8_t f[4]; // flags
|
475 | uint8_t i;
|
476 |
|
477 | Wire.beginTransmission(DS3231_I2C_ADDR);
|
478 | Wire.write(DS3231_ALARM2_ADDR);
|
479 | Wire.endTransmission();
|
480 |
|
481 | Wire.requestFrom(DS3231_I2C_ADDR, 3);
|
482 |
|
483 | for (i = 0; i <= 2; i++) {
|
484 | n[i] = Wire.read();
|
485 | f[i] = (n[i] & 0x80) >> 7;
|
486 | t[i] = bcdtodec(n[i] & 0x7F);
|
487 | }
|
488 |
|
489 | f[3] = (n[2] & 0x40) >> 6;
|
490 | t[2] = bcdtodec(n[3] & 0x3F);
|
491 |
|
492 | wake_SECOND_2 = t[0];
|
493 | wake_MINUTE_2 = t[1];
|
494 | wake_HOUR_2 = t[2];
|
495 | }
|