Arduino Programmatic Timed Reminder Alerting Framework
Framework providing multiple concurrent elapsed and real-time reminder alerting to support applications with demanding timing needs.
Components and supplies
Arduino UNO
Wire, Hook Up
DS1307 64 x 8, Serial, I²C Real-Time Clock
Apps and platforms
Arduino IDE
Project description
Code
Timed Reminders - D20_TR_Segment.ino
c_cpp
Timed Reminder Processing segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// Scan the reminder list to process active reminder entries (ETR and RTR types). 4// 5void scan_R_list(int E_or_R) { 6 int R_entry, R_type, R_status; 7 // scan each possible reminder list entry and process if active according to reminder type 8 for (R_entry = 0; R_entry < max_R_list_entries; R_entry++) { 9 if (R_list[R_entry].R_type != inactive) { 10 // this reminder entry is active 11 R_type = R_list[R_entry].R_type; 12 R_status = 0; // assume the timed remider is not the final one, for now 13 // process ETR or RTR entries depending on value of the parameter 14 if (E_or_R == ETR && R_type >= ET_oneoff_type && R_type <= ET_repeat_duration_type) { 15 // process ETR reminder entry 16 if (R_list[R_entry].R_count_down > 0) { 17 R_list[R_entry].R_count_down--; // decrement countdown timer 18 } 19 if (R_list[R_entry].R_count_down == 0) { 20 // count down time elapsed so time to raise the reminder via the 21 // RQ for end user asynchronous processing 22 //R_status = 0; // assume the timed remider is not the final one, for now 23 switch (R_type) { 24 case ET_oneoff_type: 25 // 'oneoff' reminder type so remove this reminder as it has elapsed 26 R_list[R_entry].R_type = inactive; 27 R_status = final_alert; 28 break; 29 case ET_recurring_type: 30 // 'recurring' reminder type so reset counter for next wait cycle 31 R_list[R_entry].R_count_down = R_list[R_entry].R_freq; 32 break; 33 case ET_repeat_duration_type: 34 if (R_list[R_entry].R_duration == 0) 35 { // no further reminders are due of this reminder type 36 // ('remind_duration') time has now elapsed so remove this reminder 37 R_list[R_entry].R_type = inactive; 38 R_status = final_alert; 39 } else { 40 // 'repeat_duration' type, so reset counter for next cycle 41 if (R_list[R_entry].R_duration < R_list[R_entry].R_freq) { 42 // less time left than defined by R_freq, so take lesser value 43 R_list[R_entry].R_count_down = R_list[R_entry].R_duration; 44 R_list[R_entry].R_duration = 0; 45 } else { 46 // reduce end time by R_freq 47 if (R_list[R_entry].R_freq == 0) { 48 R_list[R_entry].R_count_down = R_list[R_entry].R_duration; 49 R_list[R_entry].R_duration = 0; 50 } else { 51 R_list[R_entry].R_duration = R_list[R_entry].R_duration - R_list[R_entry].R_freq; 52 R_list[R_entry].R_count_down = R_list[R_entry].R_freq; 53 } 54 } 55 } 56 break; 57 } 58 // assemble R_status to be a compound value to store whether this is a final alert 59 // for this reminder or not, together with the reminder type and subtype. 60 // These calues will be unpicked at set to global variables once the reminder 61 // has triggered and scan_RQ takes it off the RQ. This saves a little on space. 62 R_status = (R_status << 15) + (R_type << 8) + R_list[R_entry].R_subtype; 63 if (insert_into_RQ(R_status, 64 R_list[R_entry].R_user1, 65 R_list[R_entry].R_user2, 66 R_list[R_entry].R_user3, 67 R_list[R_entry].R_user4) == fail) { 68 if (diags_on) { 69 Serial.println(F("\ 70!!scan_list ETR insert failure!!")); 71 Serial.flush(); 72 } 73 } 74 } 75 } 76 else if (E_or_R == RTR && R_type >= RT_oneoff_type && R_type <= RT_repeat_duration_type) { 77 // process RTR reminder entry 78 if (R_list[R_entry].R_remind_at == Seconds_since_midnight) { 79 switch (R_type) { 80 case RT_oneoff_type: 81 R_list[R_entry].R_type = inactive; // reminder has now expired so clear it 82 R_status = final_alert; 83 break; 84 case RT_recurring_type: 85 // perform modulo 24 hours arithmetic on next reminder time (86400 = 24 hrs in seconds) 86 R_list[R_entry].R_remind_at = (R_list[R_entry].R_remind_at + R_list[R_entry].R_freq) % 86400; 87 break; 88 case RT_repeat_duration_type: 89 90 if (R_list[R_entry].R_duration == 0) { 91 // no further reminders are due of this reminder type 92 // ('remind_duration') time has now elapsed so remove this reminder 93 R_list[R_entry].R_type = inactive; 94 R_status = final_alert; 95 } else { 96 // 'repeat_duration' type, so reset counter for next cycle 97 if (R_list[R_entry].R_duration < R_list[R_entry].R_freq) { 98 // less time left than defined by R_freq, so take lesser value 99 R_list[R_entry].R_remind_at = (R_list[R_entry].R_remind_at + R_list[R_entry].R_duration) % 86400; 100 R_list[R_entry].R_duration = 0; 101 } else { 102 // reduce end time by R_freq 103 if (R_list[R_entry].R_freq == 0) { 104 R_list[R_entry].R_remind_at = (R_list[R_entry].R_remind_at + R_list[R_entry].R_duration) % 86400; 105 R_list[R_entry].R_duration = 0; 106 } else { 107 R_list[R_entry].R_duration = R_list[R_entry].R_duration - R_list[R_entry].R_freq; 108 R_list[R_entry].R_remind_at = (R_list[R_entry].R_remind_at + R_list[R_entry].R_freq) % 86400; 109 } 110 } 111 } 112 break; 113 } 114 R_status = (R_status << 15) + (R_type << 8) + R_list[R_entry].R_subtype; 115 if (insert_into_RQ(R_status, 116 R_list[R_entry].R_user1, 117 R_list[R_entry].R_user2, 118 R_list[R_entry].R_user3, 119 R_list[R_entry].R_user4) == fail) { 120 if (diags_on) { 121 Serial.println(F("\ 122!!scan_list RTR insert failure!!")); 123 Serial.flush(); 124 } 125 } 126 } 127 } 128 } 129 } 130} 131 132// 133// This routine drives the Real-Time scan process and is initiated via timer0 interrupts 134// 135int RTR_Processor() { 136 static int last_seconds = 60; // a start value that now_seconds cant achieve 137 static int now_second = 0; 138 if (rtc.isrunning()) { 139 DateTime now = rtc.now(); 140 now_second = now.second(); // get RTC second now 141 if (now_second != last_seconds) { 142 // must have moved on by at least 1 second so scan RT reminder list entries 143 last_seconds = now_second; // ready for next pass 144 Seconds_since_midnight = seconds_since_midnight(); // used in scan_R_list for RTR checking 145 scan_R_list(RTR); 146 return 1; // return that remind list was scanned this pass 147 } 148 } else { 149 Serial.println(F("!RTR_Processor - RTC not operating, terminating!")); 150 Serial.flush(); 151 exit(0); 152 } 153 return 0; // return that remind list not scanned this pass 154} 155 156// 157// create_ET_reminder() - routine creates a new Elapsed Time (ET) reminder in R_list if 158// there is space and parameters are valid! 159// 160// Paramerters in scope, by R_type: 161// one_off, R_type = 1 162// R_start_in: Yes 163// R_freq: n/a, ignored if set 164// R_duration: n/a, ignored if set 165// 166// recurring, R_type = 2 167// R_start_in: Yes 168// R_freq: Yes 169// R_duration: n/a, ignored if set 170// 171// for a duration, R_type = 3 172// R_start_in: Yes 173// R_freq: Yes 174// R_duration: Yes 175// 176// Note that: 177// 1. the parameters for start, frequency and for (duration) all follow same format: 178// hrs, mins, secs, subsecs 179// 2. the subsecs value is the number of cycles per second the remind list is scanned. 180// It is 0..'scans_per_sec'-1. 'scans_per_sec' is 'timer1_freq' / 'ETR_R_list_scan_freq'. 181// 182int create_ET_reminder(int R_type, int R_subtype, 183 long signed int R_start_in_hrs, long signed int R_start_in_mins, 184 long signed int R_start_in_secs, long signed int R_start_in_subsecs, 185 long signed int R_freq_hrs, long signed int R_freq_mins, 186 long signed int R_freq_secs, long signed int R_freq_subsecs, 187 long signed int R_duration_hrs, long signed int R_duration_mins, 188 long signed int R_duration_secs, long signed int R_duration_subsecs, 189 int R_user1, int R_user2, int R_user3, int R_user4) { 190 int R_entry, R_list_status; 191 long signed int R_start_in, R_freq, R_duration; 192 // start by validating the parameters 193 if (R_type < ET_oneoff_type || R_type > ET_repeat_duration_type) { 194 return invalid_R_type; 195 } 196 if (R_subtype < 0 || R_subtype > 255) { 197 return invalid_R_subtype; 198 } 199 // R_start_in is required for all R_types, so validate 200 if (R_start_in_mins < 0 || R_start_in_mins > 59) { 201 return invalid_start_in_mins; 202 } 203 if (R_start_in_secs < 0 || R_start_in_secs > 59) { 204 return invalid_start_in_secs; 205 } 206 if (R_start_in_subsecs < 0 || R_start_in_subsecs >= scans_per_sec) { 207 return invalid_start_in_subsecs; 208 } 209 // validate R_freq 210 if (R_type != ET_oneoff_type) { 211 // R_frequency is required for recurring_type or repeat_duration_type, so validate 212 // and set up the interval count 213 if (R_freq_mins < 0 || R_freq_mins > 59) { 214 return invalid_freq_mins; 215 } 216 if (R_freq_secs < 0 || R_freq_secs > 59) { 217 return invalid_freq_secs; 218 } 219 if (R_freq_subsecs < 0 || R_freq_subsecs >= scans_per_sec) { 220 return invalid_freq_subsecs; 221 } 222 R_freq_secs = R_freq_secs + timer_drift_adjustment * R_freq_hrs; // add the number of secs per hour adjustment 223 224 R_freq = R_freq_hrs * scans_per_hr + 225 R_freq_mins * scans_per_min + 226 R_freq_secs * scans_per_sec + 227 R_freq_subsecs; 228 if (R_type == ET_recurring_type && R_freq == 0) { 229 return invalid_freq; 230 } 231 } else { 232 R_freq = 0; 233 } 234 // validate R_duration time if R_type is repeat_duration_type 235 if (R_type == ET_repeat_duration_type) { 236 if (R_duration_mins < 0 || R_duration_mins > 59) { 237 return invalid_duration_mins; 238 } 239 if (R_duration_secs < 0 || R_duration_secs > 59) { 240 return invalid_duration_secs; 241 } 242 if ( R_duration_subsecs < 0 || R_duration_subsecs >= scans_per_sec) { 243 return invalid_duration_subsecs; 244 } 245 R_duration_secs = R_duration_secs + timer_drift_adjustment * R_duration_hrs; // add the number of secs per hour adjustment 246 R_duration = R_duration_hrs * scans_per_hr + 247 R_duration_mins * scans_per_min + 248 R_duration_secs * scans_per_sec + 249 R_duration_subsecs; 250 if (R_freq > R_duration) { 251 return invalid_freq; 252 } 253 } else { 254 R_duration = 0; 255 } 256 R_start_in_secs = R_start_in_secs + timer_drift_adjustment * R_start_in_hrs; // add the number of secs per hour adjustment 257 R_start_in = R_start_in_hrs * scans_per_hr + 258 R_start_in_mins * scans_per_min + 259 R_start_in_secs * scans_per_sec + 260 R_start_in_subsecs; 261 // now look for an empty slot... 262 R_entry = 0; // start with first reminder entry and scan until empty entry found, or not 263 do { 264 // R_list_status=R_list[R_entry].R_type; 265 if ( R_list[R_entry].R_type == inactive) { 266 // this entry is not used, so set up it... 267 R_list[R_entry].R_subtype = R_subtype; 268 R_list[R_entry].R_start_in = R_start_in; // used for all R_types 269 R_list[R_entry].R_freq = R_freq; // only used if R_type is recurring_type or repeat_duration_type 270 R_list[R_entry].R_duration = R_duration; // only used if R_type is repeat_duration_type 271 R_list[R_entry].R_count_down = R_start_in; 272 // End user variables held in a reminder entry 273 R_list[R_entry].R_user1 = R_user1; 274 R_list[R_entry].R_user2 = R_user2; 275 R_list[R_entry].R_user3 = R_user3; 276 R_list[R_entry].R_user4 = R_user4; 277 // set this reminder as active 278 noInterrupts(); 279 R_list[R_entry].R_type = R_type; // note, setting this last variable will trigger the timer scan 280 interrupts(); 281 return R_entry; // return with the entry number of the reminder 282 } 283 R_entry++; // look at next entry 284 } 285 while ( R_entry < max_R_list_entries); 286 return reminder_list_full; 287} 288// 289// create_RT_reminder() - routine creates a new Real-Time (RT)reminder in R_list if 290// there is space and parameters are valid! 291// 292// Paramerters in scope, by R_type: 293// one_off, R_type = 4 294// R_remind_at: Yes 295// R_freq: n/a, ignored if set 296// R_duration: n/a, ignored if set 297// 298// recurring, R_type = 5 299// R_remind_at: Yes 300// R_freq: Yes 301// R_duration: n/a, ignored if set 302// 303// for a duration, R_type = 6 304// R_remind_at: Yes 305// R_freq: Yes 306// R_duration: Yes 307// 308// Note that: 309// 1. the parameters for remind_at, frequency and for (duration) all follow same format: 310// hrs, mins, secs (no subsecs) 311// 312int create_RT_reminder(int R_type, int R_subtype, 313 long signed int R_remind_at_hrs, long signed int R_remind_at_mins, 314 long signed int R_remind_at_secs, 315 long signed int R_freq_hrs, long signed int R_freq_mins, 316 long signed int R_freq_secs, 317 long signed int R_duration_hrs, long signed int R_duration_mins, 318 long signed int R_duration_secs, 319 int R_user1, int R_user2, int R_user3, int R_user4) { 320 int R_entry; 321 long signed int R_remind_at, R_freq, R_duration; 322 if (RTC_enabled) { 323 // start by validating the parameters 324 if (R_type < RT_oneoff_type || R_type > RT_repeat_duration_type) { 325 return invalid_R_type; 326 } 327 if (R_subtype < 0 || R_subtype > 255) { 328 return invalid_R_subtype; 329 } 330 if (R_remind_at_hrs < 0 || R_remind_at_hrs > 23) { 331 return invalid_RT_hrs; 332 } 333 if (R_remind_at_mins < 0 || R_remind_at_mins > 59) { 334 return invalid_RT_mins; 335 } 336 if (R_remind_at_secs < 0 || R_remind_at_secs > 59) { 337 return invalid_RT_secs; 338 } 339 // validate R_freq 340 if (R_type != RT_oneoff_type) { 341 // R_frequency is required for recurring_type or repeat_duration_type, so validate 342 // and set up the interval count 343 if (R_freq_mins < 0 || R_freq_mins > 59) { 344 return invalid_freq_mins; 345 } 346 if (R_freq_secs < 0 || R_freq_secs > 59) { 347 return invalid_freq_secs; 348 } 349 R_freq = R_freq_hrs * 3600 + 350 R_freq_mins * 60 + 351 R_freq_secs; 352 if (R_type == RT_recurring_type && R_freq == 0) { 353 return invalid_freq; 354 } 355 } else { 356 R_freq = 0; 357 } 358 // validate R_duration time if R_type is repeat_duration_type 359 if (R_type == RT_repeat_duration_type) { 360 if (R_duration_mins < 0 || R_duration_mins > 59) { 361 return invalid_duration_mins; 362 } 363 if (R_duration_secs < 0 || R_duration_secs > 59) { 364 return invalid_duration_secs; 365 } 366 R_duration = R_duration_hrs * 3600 + 367 R_duration_mins * 60 + 368 R_duration_secs; 369 if (R_freq > R_duration) { 370 return invalid_freq; 371 } 372 } else { 373 R_duration = 0; 374 } 375 // now calculate the numer of seconds since last midnight of this real-time 376 R_remind_at = R_remind_at_hrs * 3600 + 377 R_remind_at_mins * 60 + 378 R_remind_at_secs ; 379 // now look for an empty slot... 380 R_entry = 0; // start with first reminder entry and scan until empty entry found, or not 381 do { 382 if ( R_list[R_entry].R_type == inactive) { 383 // this entry is not used, so set up it... 384 R_list[R_entry].R_subtype = R_subtype; 385 R_list[R_entry].R_remind_at = R_remind_at; // real-time of reminder in secs from last midnight 386 R_list[R_entry].R_freq = R_freq; // only used if R_type is recurring_type or repeat_duration_type 387 R_list[R_entry].R_duration = R_duration; // only used if R_type is repeat_duration_type 388 R_list[R_entry].R_count_down = 0; // not used for RTRs 389 // End user variables held in a reminder entry 390 R_list[R_entry].R_user1 = R_user1; 391 R_list[R_entry].R_user2 = R_user2; 392 R_list[R_entry].R_user3 = R_user3; 393 R_list[R_entry].R_user4 = R_user4; 394 // set this reminder as active 395 noInterrupts(); 396 R_list[R_entry].R_type = R_type; // note, setting this last value/field will trigger the scan process 397 interrupts(); 398 return R_entry; // return with the entry number of the reminder 399 } 400 R_entry++; // look at next entry 401 } 402 while ( R_entry < max_R_list_entries); 403 return reminder_list_full; 404 } 405 return RTC_not_configured; 406} 407 408// 409// Routine deletes a reminder entry of the specified parameters 410// That is, for the given R_id and R_id_subtype values 411// if 'lockout' true then interrupts are first disabled and then reenabled 412// otherwise. 413// if called from within a routine where interrupts are disabled, eg and 414// interrupt routine then 'lockout' should be set to false as interrupts 415// will already be disabled. 416// 417int delete_reminder(int R_type, int R_subtype) { 418 int R_entry; 419 noInterrupts(); 420 for (R_entry = 0; R_entry < max_R_list_entries; R_entry++) { 421 if ( R_list[R_entry].R_type != inactive) { 422 if (R_type == R_list[R_entry].R_type && R_subtype == R_list[R_entry].R_subtype) { 423 // match on this entry, so remove it, ie make inactive 424 R_list[R_entry].R_type = inactive; 425 interrupts(); 426 return success; 427 } 428 } 429 } 430 interrupts(); 431 return fail; 432} 433 434// 435// Print given Reminder details 436// 437void print_reminder(int R_entry) { 438 int R_type; 439 long unsigned int R_remind_at, hrs, mins, secs; 440 noInterrupts(); 441 if (R_list[R_entry].R_type != inactive) { 442 Serial.println(F("============ Reminder Parameters ================")); 443 Serial.print(F("Reminder entry no: ")); Serial.println(R_entry); 444 R_type = R_list[R_entry].R_type; 445 Serial.print(F("R_type: ")); Serial.print(R_type); 446 Serial.print(F(" R_subtype: ")); Serial.println(R_list[R_entry].R_subtype); 447 if (R_type >= ET_oneoff_type && R_type <= ET_repeat_duration_type) { 448 Serial.print(F("R_start_in: ")); Serial.print(R_list[R_entry].R_start_in); 449 Serial.print(F(" ")); 450 } else { 451 R_remind_at = R_list[R_entry].R_remind_at; 452 hrs = R_remind_at / 3600; 453 secs = R_remind_at % 3600; 454 mins = secs / 60; 455 secs = secs % 60; 456 Serial.print(F("R_remind_at: ")); 457 Serial.print(hrs); Serial.print(F(":")); 458 Serial.print(mins); Serial.print(F(":")); 459 Serial.print(secs); Serial.print(F(" (")); 460 Serial.print(R_remind_at); 461 Serial.println(F(")" )); 462 } 463 Serial.print(F("R_freq: ")); Serial.print(R_list[R_entry].R_freq); 464 Serial.print(F(" R_duration: ")); Serial.println(R_list[R_entry].R_duration); 465 Serial.print(F("R_count_down: ")); Serial.println(R_list[R_entry].R_count_down); 466 Serial.println(F("============== User Parameters ==================")); 467 Serial.print(F("R_user1: ")); Serial.println(R_list[R_entry].R_user1); 468 Serial.print(F("R_user2: ")); Serial.println(R_list[R_entry].R_user2); 469 Serial.print(F("R_user3: ")); Serial.println(R_list[R_entry].R_user3); 470 Serial.print(F("R_user4: ")); Serial.println(R_list[R_entry].R_user4); 471 } else { 472 Serial.print(F("\ 473\ 474** Reminder ")); 475 Serial.print(R_entry); 476 Serial.println(F(" is inactive **")); 477 } 478 Serial.flush(); 479 interrupts(); 480} 481
Configuration - C00_Configurations.ino
c_cpp
Configuration segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// **************************************************************************************************** 4// USER CONFIGURABLE PARAMETERS ARE REFERENCED HERE (see User Guide): 5// 6#define diags_on true // leave set to true whilst developing/testing 7 8#define RTC_enabled true // Real-time clock enabled if true, false, if not 9 10int ETR_timer_number = 0; // 0 for timer0, 2 for timer2 11 12#define ETR_R_list_scan_freq 100 //time (msecs) between scans of the ETR list (see User Guide) 13// 14// The timer_drift_adjustment variable allows the inaccuracy of timer0/2 to be 15// compensated for, but only as far as the drift per hour in seconds. 16// This is ONLY relevant for ETRs, see User Guide. 17long signed int timer_drift_adjustment = 1; // number of seconds (+/-) per hour to adjust elapsed times 18 19#define max_RQ_free_chain_blocks 16 // size of the RQ free chain in blocks 20 21#define max_R_list_entries 10 // number of reminder list entries 22// 23// END OF USER CONFIGURABLE DATA/PARAMETERS. 24// ******************************************************************************************************* 25// 26#define ETR_timer_freq 1000 // 1 msec timer interrupt frequency 27#define scans_per_sec ETR_timer_freq / ETR_R_list_scan_freq // number of R_list scans per second 28#define scans_per_min 60 * scans_per_sec // number of R_list scans per minute 29#define scans_per_hr 60 * scans_per_min // number of R_list scans per hour 30 31// 32// The timer_drift_adjustment variable allows the inaccuracy of timer1 to be 33// compensated for, but only as far as the drift per hour. See User Guide. 34// 35 36volatile long unsigned int Seconds_since_midnight; // used by RTR scan process 37 38// ************************************ 39// Reminder List variables/definitions: 40 41volatile boolean reminders_suspended = true; // allows reminder list scans to be halted/started 42 43#define success 1 44#define fail -1 45 46#define inactive 0 // if a reminder entry R_type is 0 it is inactive 47#define final_alert 1 48//Reminder types: 49// 1-3 are elapsed remider types and 50// 4-6 are real-time reminder times 51#define ET_oneoff_type 1 // Elapsed time (ETR) R_type values 52#define ET_recurring_type 2 // .... 53#define ET_repeat_duration_type 3 // .... 54 55#define RT_oneoff_type 4 // Real-time R_type (RTR) valiues 56#define RT_recurring_type 5 // .... 57#define RT_repeat_duration_type 6 // .... 58 59#define ETR 0 // ETR = Elapsed Time Reminder 60#define RTR 1 // RTR = Real Time Reminder 61 62// definitions used by the create reminder routines 63#define invalid_R_type -1 64#define invalid_R_subtype -2 65#define invalid_R_start_in -3 66#define invalid_start_in_mins -4 67#define invalid_start_in_secs -5 68#define invalid_start_in_subsecs -6 69#define invalid_duration_mins -7 70#define invalid_duration_secs -8 71#define invalid_duration_subsecs -9 72#define invalid_freq_mins -10 73#define invalid_freq_secs -11 74#define invalid_freq_subsecs -12 75#define invalid_freq -13 76#define invalid_RT_hrs -14 // pertinent to real-time reminders only 77#define invalid_RT_mins -15 // ... 78#define invalid_RT_secs -16 // ... 79#define reminder_list_full -99 80#define RTC_not_configured -100 81 82volatile struct Reminder_List { 83 // Reminder List variables for managing reminder entries 84 int R_type; // R_type = 0 if entry unused, 85 int R_subtype; // end user definable 86 union { 87 long unsigned int R_remind_at; // used for RTR processing 88 long unsigned int R_start_in; // used for ETR processing 89 }; 90 long unsigned int R_freq; 91 long unsigned int R_duration; 92 long unsigned int R_count_down; 93 // End user variables held in a reminder entry 94 int R_user1; 95 int R_user2; 96 int R_user3; 97 int R_user4; 98} R_list[max_R_list_entries]; 99 100// **************************************** 101// Reminder Queue variables 102#define free_chain_entry_size 6 103#define end_of_chain_value -1 104 105// When allocated to a triggered/elapsed reminder,a block from the free chain will be allocated 106// to the active RQ and be set up as follows: 107// word [0] - forward chain pointer, or end of chain value 108// word [1] - a compound integer that contains: 109// bit 15 - set to 1 if this entry represents the last reminder alert for the 110// triggered/elapsed reminder or, 111// set to 0 otherwise 112// bits 11-14 - not used 113// bits 8-10 - the reminder type that was triggered/elasped 114// bits 0-7 - the reminder subtype that was triggered/elapsed 115// words [2] to [5] - the end user parameters defined at reminder creation time 116 117volatile int RQ[max_RQ_free_chain_blocks][free_chain_entry_size]; 118 119// Pointers to manage the free blocks in the RQ 120volatile int start_of_free_RQ_chain; 121volatile int num_free_RQ_blocks; 122 123// Pointers to manage the allocated blocks in the RQ 124volatile int start_of_RQ_chain = -1; 125volatile int end_of_RQ_chain = -1; 126volatile int num_RQ_reminders = 0; 127 128// general values to manage RQ processes 129#define no_reminder_requests -1 130#define no_entry -1 131 132// The following R_xxx variables are set up by scan_RQ so that they can be referenced from the main 133// end user code following a timed reminder alert being taken off the queue. 134volatile int R_status; // used to indicate if the reminder alert is a final one or otherwise 135volatile int R_type; // used to indicate what the reminder type was that triggered/elapsed 136volatile int R_subtype; // used to indicate what the reminder subtype was that triggered/elapsed 137volatile int R_user1; // end user parameters defined at reminder creation time 138volatile int R_user2; // ditto 139volatile int R_user3; // ditto 140volatile int R_user4; // ditto 141
Setup() - H00_Setup.ino
c_cpp
Setup() segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// This framework is provided with two standing reminders that provide: 4// 1. a heart beat visual monitor which shows that the code is operating. 5// This is configured for pin 13 (on board LED), but do change if necessary. 6// 2. a midnight processor to allow daily processing/housekeeping to be 7// carried out as necessary ech midnight.a 8// 9int hb_intensity = 255; // start by setting to max output level 10#define heart_beat_pin LED_BUILTIN // digital pin for visible heart beat 11#define heart_beat 254 // ETR reminder sub_type for heart beat 12#define midnight 255 // RTR reminder sub_type for midnight processing 13 14// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15// H00_Setup [DECLARE] - insert any additional declaration requirements here. 16// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 17 18 19 20 21// 22void setup() 23{ 24 int result; 25 if (diags_on) { 26 Serial.begin(115200); 27 if (RTC_enabled) { 28 !rtc.begin(); 29 if (!rtc.isrunning()) 30 { 31 Serial.println(F("!setup() - RTC is not operating, terminating!")); 32 Serial.flush(); 33 exit(0); 34 } 35 } 36 } 37 if (RTC_enabled) { 38 // Set up 'today_day_number' to provide ready access to this value at any point 39 // in the program without the need for it to be recalculated. 40 // This will get incremented each midnight to stay in step with the current date. 41 DateTime now = rtc.now(); 42 today_day_number = day_number(now.day(), now.month(), now.year()); 43 today_day_of_week = (today_day_number + day_of_week_offset) % 7; // map to 0 (Sunday), 1 (Monday),.., 6 (Saturday) 44 } 45 // ********************************************************************************** 46 // set up the two standing reminders: 47 // 1. the heart beat visual monitor as an ETR, to show processes are operating, and 48 // 2. the midnight RTR each midnight to deal with any daily processing/housekeeping 49 // The alerts for each of the above remnders are dealt with in the main segment. 50 // ********************************************************************************** 51 int freq_secs, freq_subsecs; 52 pinMode(heart_beat_pin, OUTPUT); 53 // set the ETR reminder for the heart beat to be 1/2 second if possible, if not, 54 // set to 1 second. 55 freq_subsecs = scans_per_sec / 2; // take as 1/2 scan rate per second, if possible 56 if (freq_subsecs == 0) freq_secs = 1; else freq_secs = 0; 57 result = create_ET_reminder(ET_recurring_type, heart_beat, 58 0, 0, 0, 0, // start immediately 59 0, 0, freq_secs, freq_subsecs, // 1/2 or 1 second frequency 60 0, 0, 0, 0, // not used 61 0, 0, 0, 0); // not used 62 if (result < 0 && diags_on) { 63 Serial.print(F("setup() - error creating ETR for heart_beat, error value = ")); 64 Serial.println(result); 65 Serial.flush(); 66 } 67 if (RTC_enabled) { 68 // set up RTR for each midnight, so that any daily processing can be performed 69 result = create_RT_reminder(RT_recurring_type, midnight, 70 0, 0, 0, // start at midnight (00:00:00) 71 24, 0, 0, // repeat each midnight (24 hrs) 72 0, 0, 0, // not used 73 0, 0, 0, 0); // not used 74 if (result < 0 && diags_on) { 75 Serial.print(F("setup() error creating RTR for midnight, error value = ")); 76 Serial.println(result); 77 Serial.flush(); 78 } 79 } 80 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 81 // H00_Setup [INITIALISE] - insert any additional initialisation 82 // requirements here, but before the timers and remind queues are initialised 83 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 84 85 86 87 88 // ****************************************************************** 89 // create free chain and initialise timers (0 or 2 and 1) 90 // ****************************************************************** 91 create_RQ_free_chain(); // create free chain of reminder queue blocks 92 suspend_reminders(); // ensure timers do not action the reminder list yet 93 initialise_timers(); 94 resume_reminders(); // allow timer scans of the reminder list 95} 96
Date/Time User Functions - D15_DateTime_Segment.ino
c_cpp
Date/Time User Functions segment
1/* Ron D Bentley (UK) (c) 2020 2 This tab contains additional routines (and more) to support real-time reminders 3 these being in addition to the base framework which privides elapsed time reminder (ETR) 4 functionality. 5*/ 6//**************************************************************************************** 7// Date Functions 8//**************************************************************************************** 9// Define the start date (origin) for elapsed days calculations. 10// There is a constraint on the origin date: 11// 1. it MUST be of the form 01/01/LEAP_YEAR(or pseudo Leap year, eg divisible by 100) 12// 2. Also, if this is changed then change day_of_week_offset too. 13// 14int origin_day = 1; 15int origin_month = 1; 16int origin_year = 2020; 17 18int today_day_number; // used to keep a note of the day_number for today in midnight process 19int today_day_of_week; // used to keep a note of the day_of_week for today in midnight process 20 21#define day_of_week_offset 2 // offset value for referencing days_of_week array as 22// 1/1/2020 is a Wednesday, so offset provides 23// correcting adjustment for day_of_week function, 24// as day_number(1,1,2020) gives 1 25#define valid_date 1 26#define invalid_day -1 27#define invalid_month -2 28#define invalid_year -3 29 30#define Jan 1 31#define Feb 2 32#define Mar 3 33#define Apr 4 34#define May 5 35#define Jun 6 36#define Jul 7 37#define Aug 8 38#define Sep 9 39#define Oct 10 40#define Nov 11 41#define Dec 12 42 43#define Sunday 0 44#define Monday 1 45#define Tuesday 2 46#define Wednesday 3 47#define Thursday 4 48#define Friday 5 49#define Saturday 6 50 51char days_of_week[7][12] = 52{ 53 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 54}; 55byte days_in_month[13] = 56{ 57 0, // entry 0 not used 58 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // Jan, Feb, March, etc 59}; 60int accumulated_days_in_month[13] = 61{ 62 0, // entry 0 not used 63 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 // Jan, Feb, March, etc 64}; 65// 66// Checks the given day,month,year being a valid date 67// 68int check_date(int day, int month, int year) { 69 int Days_in_month; 70 if (year < origin_year) { 71 return invalid_year; 72 } 73 if (month < Jan || month > Dec) { 74 return invalid_month; 75 } 76 Days_in_month = days_in_month[month]; 77 if (month == Feb && leap_year(year)) { 78 Days_in_month++; // adjust for leap year 79 } 80 if (day < 1 || day > Days_in_month) { 81 return invalid_day; 82 } 83 return valid_date; 84} 85// 86// leap_year will return whether the given year is a leap year 87// and takes into account the leap year rule: 88// if divisible by 4 then it IS a leap year, UNLESS 89// divisible by 100 then it is NOT a leap year, UNLESS 90// divisible by 400 the it IS a leap year. 91// 92int leap_year(int year) { 93 if (year % 400 == 0) { 94 return true; 95 } 96 if (year % 100 == 0) { 97 return false; 98 } 99 if (year % 4 == 0) { 100 return true; 101 } 102 return false; 103} 104// 105// day_number will return the number of inclusive elapsed days from the 106// origin date of the given parameter date, taking ino account leap years. 107// 108int day_number(int day, int month, int year) { 109 int num_leaps, yr; 110 // start by counting the number of leap years from the origin year 111 // to the given year. 112 num_leaps = 0; 113 for (yr = origin_year; yr <= year; yr = yr + 4) { 114 if (leap_year(yr)) num_leaps++; 115 } 116 // final adjustment if given year being a leap 117 // year and given month is Jan or Feb. 118 if (month < Mar && leap_year(year)) { 119 num_leaps--; 120 } 121 // now calculate elapsed days... 122 return (year - origin_year) * 365 123 + accumulated_days_in_month[month] 124 + day 125 + num_leaps; 126} 127// 128// Given a day_number, function will determine the date as day, month, year. 129// 130void date_from_day_number(int day_number, int &day, int &month, int &year) { 131 int days_in_year, Days_in_month; 132 // determine what year the day_number falls in, count up from origin_year 133 // in full years as days. 134 year = origin_year; 135 if (leap_year(year)) { 136 days_in_year = 366; 137 } else { 138 days_in_year = 365; 139 } 140 do { 141 if (day_number > days_in_year) { 142 // move year on 143 day_number = day_number - days_in_year; 144 year++; 145 if (leap_year(year)) { 146 days_in_year = 366; 147 } else { 148 days_in_year = 365; 149 } 150 } 151 } 152 while (day_number > days_in_year); 153 // now step through days_in_month to determine month. What is 154 // left as the remainder is the day of the month, and we are done. 155 month = 1; 156 Days_in_month = days_in_month[1]; 157 while (month <= Dec && day_number > Days_in_month) { 158 if (month == Feb && leap_year(year)) { 159 Days_in_month = 29; 160 } else { 161 Days_in_month = days_in_month[month]; 162 } 163 day_number = day_number - Days_in_month; 164 month++; 165 if (month <= Dec) { // this set up for next while cycle test condition 166 Days_in_month = days_in_month[month]; 167 if (month == Feb && leap_year(year)) { 168 Days_in_month++; 169 } 170 } 171 } 172 day = day_number; // what is left in day_number is now that day of the month 173} 174// 175// returns the index for the day of the week in the days_of_week array 176// 177int day_of_week(int day, int month, int year) { 178 return (day_number(day, month, year) 179 + day_of_week_offset) % 7; // map to 0 (Sunday), 1 (Monday), ... , 6 (Saturday) 180} 181 182//****************************************************************************************** 183// date & time funcions required for other ETR/RTR code 184// DO NOT REMOVE 185//****************************************************************************************** 186// RTC libraries and declarations 187#include <RTClib.h> 188 189RTC_DS1307 rtc; 190 191void display_now_date_time() { 192 if (RTC_enabled) { 193 DateTime now = rtc.now(); 194 Serial.print(now.day()); 195 Serial.print(F("/")); 196 Serial.print(now.month()); 197 Serial.print(F("/")); 198 Serial.print(now.year()); 199 Serial.print(F(", ")); 200 Serial.print(days_of_week[now.dayOfTheWeek()]); 201 Serial.print(F(", ")); 202 Serial.print(now.hour()); 203 Serial.print(F(":")); 204 Serial.print(now.minute()); 205 Serial.print(F(":")); 206 Serial.println(now.second()); 207 Serial.flush(); 208 } 209} 210 211// 212// Function returns the number of seconds since midnight. 213long unsigned int seconds_since_midnight() { 214 long unsigned int secs, mins, hrs; 215 if (RTC_enabled) { 216 if (rtc.isrunning()) { 217 DateTime now = rtc.now(); 218 hrs = now.hour(); 219 mins = now.minute(); 220 secs = now.second(); 221 return hrs * 3600 + mins * 60 + secs; 222 } else { 223 Serial.println(F("!secs_since_midnight - RTC is not operating, terminating!")); 224 Serial.flush(); 225 exit(0); 226 } 227 } else return 0; // RTC not configured 228} 229
Introduction - A00_Remsys_v2_00.ino
c_cpp
Introduction segment
1/* version 1.00 rev 1, Ron D Bentley (Stafford UK) March 2020. 2 * version 1.00 rev 2, July 2020 - moved print strings to program storage space to reduce 3 * amount of dynamic storage used. Plus improved efficiency in 4 * heart beat coding. 5 * version 2.0, Aug 2020 - this version allows REM_SYS to be configured without the real-time clock 6 * module, so that ETR only applications can be designed. 7This framework provides a basis for the development of applications that would benefit from timed 8reminder alerting. Timed reminder alerts can be defined/created as elapsed time reminders (ETRs)or 9as real-time reminders (RTRs) linked to a real-time clock (RTC). Any number of reminders can be 10active at any one time, dependent on the size of the Reimder List initialised. 11____________________________________________________________________________________________________ 12Copyright (c) Ron D Bentley (UK) 13The extent this licence shall be limited to Non-profit Use. 14Permission is hereby granted, free of charge, for non-profit purposes to any person obtaining 15a copy of this software and associated documentation files (the "Software"), to deal in the 16Software without restriction, including without limitation the rights to use, copy, modify, merge, 17publish, distribute, sublicense copies of the Software, and to permit persons to whom the Software 18is furnished to do so, subject to the following conditions: 19 20The above copyright notice, acknowledgements and this permission notice shall be included in all 21copies or substantial portions of the Software. 22 23THE SOFTWARE IS LIMITED TO NON-PROFIT USE AND IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR 25A PARTICULAR PURPOSE AND NONINFRINGEMENT. 26IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 27WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 28SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29____________________________________________________________________________________________________ 30 31See User Guide for full capabilities and instructions for implementation. 32 33 */ 34
Main Loop - M00_Main_Segment.ino
c_cpp
Main Loop segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// main segment for end user code 4// insert end user code where indicated: 5// 1. if timed reminder alert processing is needed then within the 'do{if (scan_RQ){...}' 6// control block, otherwise. Use swich case on R_subtype to process alerts. 7// 2. for non-reminder requirements, place code within the 'do{if (scan_RQ)..else {..}' 8// control block 9 10void loop() { 11 do { 12 if (scan_RQ() != no_reminder_requests) { 13 // ******************************************************************************* 14 // the following global data variables are available at this point relating to the 15 // reminder triggered/alerted: 16 // 1. R_type - reminder type, ETRs: 1, 2, or 3, / RTRs: 4, 5, or 6 17 // 2. R_subtype - reminder subtype (0 <= R_subtype <= 255) 18 // 3. R_status - set to 1 if this is the FINAL reminder alert, 0 otherwise. 19 // Only relevant for oneoff and repeat_duration reminder types. 20 // 4. R_user1-R_user4 - user data values set up when the reminder was created 21 // 22 // The above variable are those defined when the timed reminder was created. 23 // These can be 'crafted' to control flow and decision processes. 24 // 25 // Insert end user code here to process timed reminder alerts by R_subtype. 26 // ******************************************************************************* 27 switch (R_subtype) { 28 case heart_beat: 29 // ******** provides a visual indication that the program is running 30 analogWrite(heart_beat_pin, hb_intensity); 31 // toggle heart beat output level for next pass 32 hb_intensity = 255 - hb_intensity; // produces 0 and 255 on alternate passes 33 break; 34 case midnight: 35 // ********* midnight processing. Insert any code relevant for daily housekeeping 36 // Note, this is only entered if the RTC is configured. 37 today_day_number++; // day_number for today, a new day 38 today_day_of_week = (today_day_of_week + 1) % 7; // next day of week value 39 break; 40 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 41 // M00_Main_Segment [ALERT PROCESSING], timer alert processing - 42 // Insert ETR/RTR alert switch case code handling here: 43 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 44 45 46 47 48 49 50 51 // default switch value 'catcher' 52 default: 53 Serial.print(F("!Spurious switch value=")); 54 Serial.println(R_subtype); 55 Serial.flush(); 56 display_now_date_time(); 57 break; 58 } 59 } else { 60 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 61 // M00_Main_Segment [GENERAL PROCESSING]. 62 // Reminder queue is currently empty, so do other things. 63 // Insert end user code here for processing non-reminder alerts 64 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 65 66 67 68 69 70 71 } 72 } while (true); 73} 74
Timers - D00_Timers_Segment.ino
c_cpp
Timer segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2 3// This segment comprises the timer setups and ISRs that underpin this framework. 4// They are used to provide the timimg for both ETRs and RTRs. 5// 6// timer0 and timer2 are used as the source timer for processing ETRs, and 7// timer1 is used as the source timer for processing RTRs. 8// 9// Note that the timer frequency for RTR processing is fixed (timer1). 10// However, the frequency for processing ETRs is end user configurable (see User Guide). 11// 12// ************************************************************************************** 13// timer initialisation routines for timer0, timer1 and timer2: 14// 15// ETR timer: 16// Set up timer0 interrupt for processimg ETRs for 1000 cycles per second, 17// 1kHz interrupts, ie for 1 millisecond interrupts 18void initialise_timer0() { 19 //set timer0 interrupt at 1kHz 20 noInterrupts(); 21 TCCR0A = 0; 22 TCCR0B = 0; 23 TCNT0 = 0; 24 // set compare match register for 1khz increments 25 OCR0A = 249; // (16*10^6) / (1000*64) - 1 (must be <256) 26 // turn on CTC mode 27 TCCR0A |= (1 << WGM01); 28 // Set CS01 and CS00 bits for 64 prescaler 29 TCCR0B |= (1 << CS01) | (1 << CS00); 30 // enable timer compare interrupt 31 TIMSK0 |= (1 << OCIE0A); 32 interrupts(); 33} 34 35// RTR timer: 36// Set up timer1 interrupt for processing RTRs for 2 cycles per second, 37// 2Hz interrupts, ie for 1/2 second interrupts 38// 39void initialise_timer1() { 40 noInterrupts(); 41 TCCR1A = 0; 42 TCCR1B = 0; 43 TCNT1 = 0; 44 // set Compare Match Register (CMR) for 1msec (2hz) increments 45 OCR1A = 31249; // (16,000,000)/(256*2)) - 1 46 // turn on CTC mode 47 TCCR1B |= (1 << WGM12); // WGM12 = 3 48 // Set CS11 bit for 256 prescaler 49 //TCCR1B |= (1 << CS11) | (1 << CS10); // CS11 = 1 and CS10 = 0 50 TCCR1B |= (1 << CS12); // CS12 = 2 51 // enable timer1 compare interrupt 52 TIMSK1 |= (1 << OCIE1A); // OCIE1A = 1 53 interrupts(); 54} 55// 56// ETR timer: 57// Alternative source timer for processimg ETRs for 1000 cycles per second, 58// 1kHz interrupts, ie for 1 millisecond interrupts. 59// This is provided if end user needs to use delay() function which makes 60// use of timer0, hence a possible conflict. End user encouraged not to 61// use delay()! 62void initialise_timer2() { 63 noInterrupts(); 64 // Clear registers 65 TCCR2A = 0; 66 TCCR2B = 0; 67 TCNT2 = 0; 68 // 1000 Hz (16000000/((124+1)*128)) 69 OCR2A = 124; 70 // CTC 71 TCCR2A |= (1 << WGM21); 72 // Prescaler 128 73 TCCR2B |= (1 << CS22) | (1 << CS20); 74 // Output Compare Match A Interrupt Enable 75 TIMSK2 |= (1 << OCIE2A); 76 interrupts(); 77} 78// 79// *************************************************************************** 80// timer0, 1 and 2 ISRs: 81// 82// timer0 is used as the timing source for ETR processing exclusively. 83// timer0 interrupt routine will be entered at ETR_timer_freq interrupt frequency 84// defined during the initialisation of timer0. 85// However, processing of the Remind List will only occur every R_List_scan_feq 'ticks' 86// of timer0. 87// 88ISR(TIMER0_COMPA_vect) { 89 static int timer0_tick = 0; // counts out the defined R_list scan frequency for ETRs. 90 if (!reminders_suspended) 91 { timer0_tick++; 92 if (timer0_tick == ETR_R_list_scan_freq) { 93 timer0_tick = 0; // reset for next timer1 interrupt 94 // scan ETR entries in reminder list 95 scan_R_list(ETR); 96 } 97 } 98} 99 100// Timer1 is used as the timing source for RTR processing exclusively. 101// Timer1 is initialised for 2 cycles per second (2hz) so is entered 102// every 1/2 second. 103// Note that this routne is only active if the RTC is enabled 104// 105ISR(TIMER1_COMPA_vect) { 106 if (!reminders_suspended) 107 { 108 // scan RTR entries in reminder list 109 interrupts(); // not normally the right thing, but this works and allows RTC lib to be accessed 110 RTR_Processor(); 111 } 112} 113 114// timer2 is an alternative timer interrupt routine to timer0. 115// It is offered to replace timer0 if end user wishes to use the delay() function! 116// It is used as the timing source for ETR processing exclusively. 117// timer2 interrupt routine will be entered at the ETR_timer_freq interrupt frequency 118// defined during the initialisation of timer2. 119// However, processing of the Remind List will only occur every R_List_scan_feq 'ticks' 120// of timer2. 121// 122ISR(TIMER2_COMPA_vect) { 123 static int timer2_tick = 0; // counts out the defined R_list scan frequency for ETRs. 124 if (!reminders_suspended) 125 { timer2_tick++; 126 if (timer2_tick == ETR_R_list_scan_freq) { 127 timer2_tick = 0; // reset for next timer1 interrupt 128 // scan ETR entries in reminder list 129 scan_R_list(ETR); 130 } 131 } 132} 133 134// The framework supports one of two timers for processing ETRs, either timer0 or timer2. 135// Both timers are initialised for 1khz (1000 cycles per second, or ETR_timer_freq). 136// If end user wishes to use delay() function, choose to initialise timer2 NOT timer0. 137// 138void initialise_timers() { 139 if (ETR_timer_number == 0) initialise_timer0(); 140 else initialise_timer2(); 141 if (RTC_enabled) {initialise_timer1();} // timer1 deals with the RTC 142} 143 144// 145// Routine sets the reminders_suspended flag to halt reminder list scans by ISR. 146// 147void suspend_reminders() { 148 noInterrupts(); 149 reminders_suspended = true; 150 interrupts(); 151} 152 153// 154// Routine clears the reminders_suspended flag to resume reminder list scans by ISR. 155// 156void resume_reminders() { 157 noInterrupts(); 158 reminders_suspended = false; 159 interrupts(); 160} 161
Queuing - D10_Queue_Segment.ino
c_cpp
Queue segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// This tab contains the routines to establish and maintain reminder 4// queue management. 5// 6// A free chain of blocks is set up that are then allocated to reminder alert requests 7// as they decome due. 8// 9// 'RQ' is used as the blocks of memory to be allocated/deallocated on request. 10// 'RQ' comprises a number of free blocks each of 5 integers, the first word of each block 11// contains a forward pointer to next block or end of chain value(free/used). 12 13// When a free block is allocated to a reminder request, it is inserted at the beginning of the 14// reminder queue with the first word of the block being a forward pointer to next allocated 15// block and the remainder of block containing user values defined as part of the remider set up. 16// 17// Note: 18// 1. The number of free blocks in the RQ free chain is defined by 'max_RQ_free_chain_blocks'. 19// 2. The implementation is as a queue, first in first out (FIFO). 20// 21 22// Set up the free RQ chain as defined by 'max_RQ_free__chain_blocks' 23// This is called just once per start/reset from te setup() process. 24// 25void create_RQ_free_chain() { 26 int ptr, last_RQ_block; 27 last_RQ_block = max_RQ_free_chain_blocks - 1; 28 for (ptr = 0; ptr < max_RQ_free_chain_blocks; ptr++) { 29 if (ptr == last_RQ_block) { 30 RQ[ptr][0] = end_of_chain_value; // set end of chain 31 } 32 else { 33 RQ[ptr][0] = ptr + 1; // set forward block pointer 34 } 35 } 36 num_free_RQ_blocks = max_RQ_free_chain_blocks; 37 start_of_free_RQ_chain = 0; 38} 39// 40// Allocates a block from the free chain, if one exists. 41// NOTE: assumes that interrupts are disabled by calling function(s). 42// 43int acquire_block_from_free_RQ() { 44 int block; 45 if (num_free_RQ_blocks > 0) { 46 // There is at least 1 free block left. 47 // Take the next block off the free chain. 48 num_free_RQ_blocks--; // reduce free blocks available. 49 block = start_of_free_RQ_chain; 50 start_of_free_RQ_chain = RQ[block][0]; // take the first free block off the free chain. 51 RQ[block][0] = end_of_chain_value; // set the forward pointer in this free block to out of range. 52 return block; // return with the 'address' of the free block provided to calling routine. 53 } 54 return fail; // no free blocks! Return error condition. 55} 56 57// 58// Returns the given block back to the free chain. 59// NOTE: assumes that reminders are disabled by calling function(s). 60// 61int relinquish_block_to_free_RQ(int block) { 62 if (num_free_RQ_blocks < max_RQ_free_chain_blocks) { 63 // there is space to add this block back to the free chain. 64 num_free_RQ_blocks++; // increase number of free blocks on free chain by 1 65 RQ[block][0] = start_of_free_RQ_chain; 66 start_of_free_RQ_chain = block; 67 return success; // relinquish was successful. 68 } 69 return fail; // free chain seems to be full of free blocks! No space to add another. 70} 71 72// 73// The function creates an entry in the interrupt queue for the given interrupt. 74// 75int insert_into_RQ(int R_status, int R_user1, int R_user2, int R_user3, int R_user4) { 76 int block; 77 block = acquire_block_from_free_RQ(); 78 if (block == fail) { 79 // no free block available! 80 return fail; 81 } 82 // we have a free block, so chain it into the reminder queue 83 // placing it at the end of the current queue. 84 if (num_RQ_reminders == 0) { 85 // queue is empty, so set start pointer 86 start_of_RQ_chain = block; 87 } else { 88 // at least on entry in RQ queue, so modify forward pointer of last block 89 RQ[end_of_RQ_chain][0] = block; 90 } 91 // now deal with rest of updates 92 num_RQ_reminders++; 93 end_of_RQ_chain = block; 94 RQ[block][0] = end_of_chain_value; // set new end of chain 95 RQ[block][1] = R_status; 96 RQ[block][2] = R_user1; 97 RQ[block][3] = R_user2; 98 RQ[block][4] = R_user3; 99 RQ[block][5] = R_user4; 100 return success; 101} 102 103// 104// See if there are any outstanding (unprocessed) reminder requests in the 105// reminder queue. If so, take the first one on the queue and return it to the 106// free chain. The answer to the function is either reminder found 107// or that no outstanding reminder requests exists. 108// 109// Note that this function is not entered via any interrupt routine, so we do need to 110// 111int scan_RQ() { 112 int RQ_block, interrupt; 113 //noInterrupts(); // ensure exclusive access of the RQ and its pointers 114 if (num_RQ_reminders == 0) { 115 // Clear down the end user reminder parameters, if no RQ entry to be processed. 116 R_status = 0; 117 R_type = 0; 118 R_subtype = 0; 119 R_user1 = 0; 120 R_user2 = 0; 121 R_user3 = 0; 122 R_user4 = 0; 123 //interrupts(); 124 return no_reminder_requests; 125 } 126 // take the first entry off the RQ as this is the next interrupt to be processed. 127 noInterrupts(); 128 num_RQ_reminders--; 129 RQ_block = start_of_RQ_chain; 130 start_of_RQ_chain = RQ[RQ_block][0]; // point to next interrupt to be processed, or end of chain 131 if (num_RQ_reminders == 0) { 132 // last used block to be relinquished, so adjust end of chain pointer 133 end_of_RQ_chain = end_of_chain_value; 134 } 135 // set up the interrupt parameters for this interrupt, 136 // solely for end user reference if required 137 R_status = RQ[RQ_block][1]; // active/inactive, R_type and R_subtype 138 R_type = (R_status & 0x7ff) >> 8; // the type of reminder 139 R_subtype = R_status & 0xff; // the reminder subtype value 140 R_status = bitRead(R_status, 15); // indicates if last reminder alert recieved, or not 141 R_user1 = RQ[RQ_block][2]; 142 R_user2 = RQ[RQ_block][3]; 143 R_user3 = RQ[RQ_block][4]; 144 R_user4 = RQ[RQ_block][5]; 145 relinquish_block_to_free_RQ(RQ_block); // put this block back on the free RQ chain 146 interrupts(); 147 return success; // return with the internal value of the interrupt handler that was assigned to this pin 148} 149// 150// Utility to print the reminder queue (RQ) 151// 152void print_RQ() { 153 int ptr, count, i; 154 if (diags_on) { 155 noInterrupts(); 156 count = num_RQ_reminders; 157 ptr = start_of_RQ_chain; 158 Serial.println(F("_______________________________")); 159 Serial.println(F(" REMINDER QUEUE")); 160 Serial.print(F(" Num in RQ = ")); 161 Serial.println(count); 162 Serial.print(F(" start of RQ chain = ")); 163 Serial.println(ptr); 164 Serial.print(F(" end of RQ chain = ")); 165 Serial.println(end_of_RQ_chain); 166 if (count > 0) { 167 do { 168 for (i = 0; i < free_chain_entry_size; i++) { 169 Serial.print(F("RQ[")); 170 Serial.print(ptr); 171 Serial.print(F("]")); 172 Serial.print(i); 173 Serial.print(F("] = ")); 174 Serial.println(RQ[ptr][i]); 175 } 176 Serial.print(F("\ 177")); 178 ptr = RQ[ptr][0]; // look at next entry/block 179 count --; 180 } 181 while ((ptr != end_of_chain_value) && (count > 0)); 182 } 183 Serial.println(F("")); 184 Serial.println(F("_______________________________\ 185")); 186 Serial.flush(); 187 interrupts(); 188 } 189} 190// 191// Utility to print the free chain 192// 193void print_free_chain() { 194 int ptr, count; 195 if (diags_on) { 196 noInterrupts(); 197 count = num_free_RQ_blocks; 198 ptr = start_of_free_RQ_chain; 199 Serial.println(F("")); 200 Serial.println(F("_______________________________")); 201 Serial.println(F(" FREE CHAIN")); 202 Serial.print(F(" Num in free chain = ")); 203 Serial.println(count); 204 Serial.print(F("start of free chain = ")); 205 Serial.println(ptr); 206 if (count > 0) { 207 do { 208 Serial.print(F("RQ[")); 209 Serial.print(ptr); 210 Serial.print(F("][0] = ")); 211 Serial.println(RQ[ptr][0]); 212 ptr = RQ[ptr][0]; // look at next entry/block 213 count --; 214 } 215 while ((ptr != end_of_chain_value) && (count > 0)); 216 } 217 Serial.println(F("_______________________________\ 218")); 219 Serial.flush(); 220 interrupts(); 221 } 222} 223
Timed Reminders - D20_TR_Segment.ino
c_cpp
Timed Reminder Processing segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// 4 Scan the reminder list to process active reminder entries (ETR and RTR types). 5// 6void 7 scan_R_list(int E_or_R) { 8 int R_entry, R_type, R_status; 9 // scan each 10 possible reminder list entry and process if active according to reminder type 11 12 for (R_entry = 0; R_entry < max_R_list_entries; R_entry++) { 13 if (R_list[R_entry].R_type 14 != inactive) { 15 // this reminder entry is active 16 R_type = R_list[R_entry].R_type; 17 18 R_status = 0; // assume the timed remider is not the final one, for now 19 20 // process ETR or RTR entries depending on value of the parameter 21 if 22 (E_or_R == ETR && R_type >= ET_oneoff_type && R_type <= ET_repeat_duration_type) 23 { 24 // process ETR reminder entry 25 if (R_list[R_entry].R_count_down 26 > 0) { 27 R_list[R_entry].R_count_down--; // decrement countdown timer 28 29 } 30 if (R_list[R_entry].R_count_down == 0) { 31 // count 32 down time elapsed so time to raise the reminder via the 33 // RQ for 34 end user asynchronous processing 35 //R_status = 0; // assume the 36 timed remider is not the final one, for now 37 switch (R_type) { 38 case 39 ET_oneoff_type: 40 // 'oneoff' reminder type so remove this reminder 41 as it has elapsed 42 R_list[R_entry].R_type = inactive; 43 R_status 44 = final_alert; 45 break; 46 case ET_recurring_type: 47 48 // 'recurring' reminder type so reset counter for next wait cycle 49 50 R_list[R_entry].R_count_down = R_list[R_entry].R_freq; 51 break; 52 53 case ET_repeat_duration_type: 54 if (R_list[R_entry].R_duration 55 == 0) 56 { // no further reminders are due of this reminder type 57 58 // ('remind_duration') time has now elapsed so remove this reminder 59 60 R_list[R_entry].R_type = inactive; 61 R_status = 62 final_alert; 63 } else { 64 // 'repeat_duration' 65 type, so reset counter for next cycle 66 if (R_list[R_entry].R_duration 67 < R_list[R_entry].R_freq) { 68 // less time left than defined 69 by R_freq, so take lesser value 70 R_list[R_entry].R_count_down 71 = R_list[R_entry].R_duration; 72 R_list[R_entry].R_duration = 73 0; 74 } else { 75 // reduce end time by R_freq 76 77 if (R_list[R_entry].R_freq == 0) { 78 R_list[R_entry].R_count_down 79 = R_list[R_entry].R_duration; 80 R_list[R_entry].R_duration 81 = 0; 82 } else { 83 R_list[R_entry].R_duration 84 = R_list[R_entry].R_duration - R_list[R_entry].R_freq; 85 R_list[R_entry].R_count_down 86 = R_list[R_entry].R_freq; 87 } 88 } 89 } 90 91 break; 92 } 93 // assemble R_status to be a compound 94 value to store whether this is a final alert 95 // for this reminder or 96 not, together with the reminder type and subtype. 97 // These calues will 98 be unpicked at set to global variables once the reminder 99 // has triggered 100 and scan_RQ takes it off the RQ. This saves a little on space. 101 R_status 102 = (R_status << 15) + (R_type << 8) + R_list[R_entry].R_subtype; 103 if 104 (insert_into_RQ(R_status, 105 R_list[R_entry].R_user1, 106 107 R_list[R_entry].R_user2, 108 R_list[R_entry].R_user3, 109 110 R_list[R_entry].R_user4) == fail) { 111 if 112 (diags_on) { 113 Serial.println(F("\ 114!!scan_list ETR insert failure!!")); 115 116 Serial.flush(); 117 } 118 } 119 } 120 } 121 122 else if (E_or_R == RTR && R_type >= RT_oneoff_type && R_type <= RT_repeat_duration_type) 123 { 124 // process RTR reminder entry 125 if (R_list[R_entry].R_remind_at 126 == Seconds_since_midnight) { 127 switch (R_type) { 128 case 129 RT_oneoff_type: 130 R_list[R_entry].R_type = inactive; // reminder 131 has now expired so clear it 132 R_status = final_alert; 133 break; 134 135 case RT_recurring_type: 136 // perform modulo 24 hours 137 arithmetic on next reminder time (86400 = 24 hrs in seconds) 138 R_list[R_entry].R_remind_at 139 = (R_list[R_entry].R_remind_at + R_list[R_entry].R_freq) % 86400; 140 break; 141 142 case RT_repeat_duration_type: 143 144 if (R_list[R_entry].R_duration 145 == 0) { 146 // no further reminders are due of this reminder type 147 148 // ('remind_duration') time has now elapsed so remove this reminder 149 150 R_list[R_entry].R_type = inactive; 151 R_status = 152 final_alert; 153 } else { 154 // 'repeat_duration' 155 type, so reset counter for next cycle 156 if (R_list[R_entry].R_duration 157 < R_list[R_entry].R_freq) { 158 // less time left than defined 159 by R_freq, so take lesser value 160 R_list[R_entry].R_remind_at 161 = (R_list[R_entry].R_remind_at + R_list[R_entry].R_duration) % 86400; 162 R_list[R_entry].R_duration 163 = 0; 164 } else { 165 // reduce end time by R_freq 166 167 if (R_list[R_entry].R_freq == 0) { 168 R_list[R_entry].R_remind_at 169 = (R_list[R_entry].R_remind_at + R_list[R_entry].R_duration) % 86400; 170 R_list[R_entry].R_duration 171 = 0; 172 } else { 173 R_list[R_entry].R_duration 174 = R_list[R_entry].R_duration - R_list[R_entry].R_freq; 175 R_list[R_entry].R_remind_at 176 = (R_list[R_entry].R_remind_at + R_list[R_entry].R_freq) % 86400; 177 } 178 179 } 180 } 181 break; 182 } 183 184 R_status = (R_status << 15) + (R_type << 8) + R_list[R_entry].R_subtype; 185 186 if (insert_into_RQ(R_status, 187 R_list[R_entry].R_user1, 188 189 R_list[R_entry].R_user2, 190 R_list[R_entry].R_user3, 191 192 R_list[R_entry].R_user4) == fail) { 193 if 194 (diags_on) { 195 Serial.println(F("\ 196!!scan_list RTR insert failure!!")); 197 198 Serial.flush(); 199 } 200 } 201 } 202 } 203 204 } 205 } 206} 207 208// 209// This routine drives the Real-Time scan process 210 and is initiated via timer0 interrupts 211// 212int RTR_Processor() { 213 static 214 int last_seconds = 60; // a start value that now_seconds cant achieve 215 static 216 int now_second = 0; 217 if (rtc.isrunning()) { 218 DateTime now = rtc.now(); 219 220 now_second = now.second(); // get RTC second now 221 if (now_second != 222 last_seconds) { 223 // must have moved on by at least 1 second so scan RT reminder 224 list entries 225 last_seconds = now_second; // ready for next pass 226 Seconds_since_midnight 227 = seconds_since_midnight(); // used in scan_R_list for RTR checking 228 scan_R_list(RTR); 229 230 return 1; // return that remind list was scanned this pass 231 232 } 233 } else { 234 Serial.println(F("!RTR_Processor - RTC not operating, 235 terminating!")); 236 Serial.flush(); 237 exit(0); 238 } 239 return 0; // 240 return that remind list not scanned this pass 241} 242 243// 244// create_ET_reminder() 245 - routine creates a new Elapsed Time (ET) reminder in R_list if 246// there is 247 space and parameters are valid! 248// 249// Paramerters in scope, by R_type: 250// 251 one_off, R_type = 1 252// R_start_in: Yes 253// R_freq: n/a, ignored if 254 set 255// R_duration: n/a, ignored if set 256// 257// recurring, R_type = 2 258// 259 R_start_in: Yes 260// R_freq: Yes 261// R_duration: n/a, ignored if set 262// 263// 264 for a duration, R_type = 3 265// R_start_in: Yes 266// R_freq: Yes 267// 268 R_duration: Yes 269// 270// Note that: 271// 1. the parameters for start, frequency 272 and for (duration) all follow same format: 273// hrs, mins, secs, subsecs 274// 275 2. the subsecs value is the number of cycles per second the remind list is scanned. 276// 277 It is 0..'scans_per_sec'-1. 'scans_per_sec' is 'timer1_freq' / 'ETR_R_list_scan_freq'. 278// 279int 280 create_ET_reminder(int R_type, int R_subtype, 281 long signed 282 int R_start_in_hrs, long signed int R_start_in_mins, 283 long 284 signed int R_start_in_secs, long signed int R_start_in_subsecs, 285 long 286 signed int R_freq_hrs, long signed int R_freq_mins, 287 long 288 signed int R_freq_secs, long signed int R_freq_subsecs, 289 long 290 signed int R_duration_hrs, long signed int R_duration_mins, 291 long 292 signed int R_duration_secs, long signed int R_duration_subsecs, 293 int 294 R_user1, int R_user2, int R_user3, int R_user4) { 295 int R_entry, R_list_status; 296 297 long signed int R_start_in, R_freq, R_duration; 298 // start by validating the 299 parameters 300 if (R_type < ET_oneoff_type || R_type > ET_repeat_duration_type) 301 { 302 return invalid_R_type; 303 } 304 if (R_subtype < 0 || R_subtype > 255) 305 { 306 return invalid_R_subtype; 307 } 308 // R_start_in is required for all 309 R_types, so validate 310 if (R_start_in_mins < 0 || R_start_in_mins > 59) { 311 312 return invalid_start_in_mins; 313 } 314 if (R_start_in_secs < 0 || R_start_in_secs 315 > 59) { 316 return invalid_start_in_secs; 317 } 318 if (R_start_in_subsecs 319 < 0 || R_start_in_subsecs >= scans_per_sec) { 320 return invalid_start_in_subsecs; 321 322 } 323 // validate R_freq 324 if (R_type != ET_oneoff_type) { 325 // R_frequency 326 is required for recurring_type or repeat_duration_type, so validate 327 // and 328 set up the interval count 329 if (R_freq_mins < 0 || R_freq_mins > 59) { 330 331 return invalid_freq_mins; 332 } 333 if (R_freq_secs < 0 || R_freq_secs 334 > 59) { 335 return invalid_freq_secs; 336 } 337 if (R_freq_subsecs < 338 0 || R_freq_subsecs >= scans_per_sec) { 339 return invalid_freq_subsecs; 340 341 } 342 R_freq_secs = R_freq_secs + timer_drift_adjustment * R_freq_hrs; // 343 add the number of secs per hour adjustment 344 345 R_freq = R_freq_hrs * scans_per_hr 346 + 347 R_freq_mins * scans_per_min + 348 R_freq_secs 349 * scans_per_sec + 350 R_freq_subsecs; 351 if (R_type == ET_recurring_type 352 && R_freq == 0) { 353 return invalid_freq; 354 } 355 } else { 356 R_freq 357 = 0; 358 } 359 // validate R_duration time if R_type is repeat_duration_type 360 361 if (R_type == ET_repeat_duration_type) { 362 if (R_duration_mins < 0 || R_duration_mins 363 > 59) { 364 return invalid_duration_mins; 365 } 366 if (R_duration_secs 367 < 0 || R_duration_secs > 59) { 368 return invalid_duration_secs; 369 } 370 371 if ( R_duration_subsecs < 0 || R_duration_subsecs >= scans_per_sec) { 372 return 373 invalid_duration_subsecs; 374 } 375 R_duration_secs = R_duration_secs + timer_drift_adjustment 376 * R_duration_hrs; // add the number of secs per hour adjustment 377 R_duration 378 = R_duration_hrs * scans_per_hr + 379 R_duration_mins * scans_per_min 380 + 381 R_duration_secs * scans_per_sec + 382 R_duration_subsecs; 383 384 if (R_freq > R_duration) { 385 return invalid_freq; 386 } 387 } else 388 { 389 R_duration = 0; 390 } 391 R_start_in_secs = R_start_in_secs + timer_drift_adjustment 392 * R_start_in_hrs; // add the number of secs per hour adjustment 393 R_start_in 394 = R_start_in_hrs * scans_per_hr + 395 R_start_in_mins * scans_per_min 396 + 397 R_start_in_secs * scans_per_sec + 398 R_start_in_subsecs; 399 400 // now look for an empty slot... 401 R_entry = 0; // start with first reminder 402 entry and scan until empty entry found, or not 403 do { 404 // R_list_status=R_list[R_entry].R_type; 405 406 if ( R_list[R_entry].R_type == inactive) { 407 // this entry is not used, 408 so set up it... 409 R_list[R_entry].R_subtype = R_subtype; 410 R_list[R_entry].R_start_in 411 = R_start_in; // used for all R_types 412 R_list[R_entry].R_freq = 413 R_freq; // only used if R_type is recurring_type or repeat_duration_type 414 415 R_list[R_entry].R_duration = R_duration; // only used if R_type is 416 repeat_duration_type 417 R_list[R_entry].R_count_down = R_start_in; 418 // 419 End user variables held in a reminder entry 420 R_list[R_entry].R_user1 = 421 R_user1; 422 R_list[R_entry].R_user2 = R_user2; 423 R_list[R_entry].R_user3 424 = R_user3; 425 R_list[R_entry].R_user4 = R_user4; 426 // set this reminder 427 as active 428 noInterrupts(); 429 R_list[R_entry].R_type = R_type; // 430 note, setting this last variable will trigger the timer scan 431 interrupts(); 432 433 return R_entry; // return with the entry number of the reminder 434 } 435 436 R_entry++; // look at next entry 437 } 438 while ( R_entry < max_R_list_entries); 439 440 return reminder_list_full; 441} 442// 443// create_RT_reminder() - routine creates 444 a new Real-Time (RT)reminder in R_list if 445// there is space and parameters are 446 valid! 447// 448// Paramerters in scope, by R_type: 449// one_off, R_type = 4 450// 451 R_remind_at: Yes 452// R_freq: n/a, ignored if set 453// R_duration: 454 n/a, ignored if set 455// 456// recurring, R_type = 5 457// R_remind_at: Yes 458// 459 R_freq: Yes 460// R_duration: n/a, ignored if set 461// 462// for a 463 duration, R_type = 6 464// R_remind_at: Yes 465// R_freq: Yes 466// R_duration: 467 Yes 468// 469// Note that: 470// 1. the parameters for remind_at, frequency 471 and for (duration) all follow same format: 472// hrs, mins, secs (no subsecs) 473// 474int 475 create_RT_reminder(int R_type, int R_subtype, 476 long signed 477 int R_remind_at_hrs, long signed int R_remind_at_mins, 478 long 479 signed int R_remind_at_secs, 480 long signed int R_freq_hrs, 481 long signed int R_freq_mins, 482 long signed int R_freq_secs, 483 484 long signed int R_duration_hrs, long signed int R_duration_mins, 485 486 long signed int R_duration_secs, 487 int 488 R_user1, int R_user2, int R_user3, int R_user4) { 489 int R_entry; 490 long signed 491 int R_remind_at, R_freq, R_duration; 492 if (RTC_enabled) { 493 // start by 494 validating the parameters 495 if (R_type < RT_oneoff_type || R_type > RT_repeat_duration_type) 496 { 497 return invalid_R_type; 498 } 499 if (R_subtype < 0 || R_subtype 500 > 255) { 501 return invalid_R_subtype; 502 } 503 if (R_remind_at_hrs 504 < 0 || R_remind_at_hrs > 23) { 505 return invalid_RT_hrs; 506 } 507 if 508 (R_remind_at_mins < 0 || R_remind_at_mins > 59) { 509 return invalid_RT_mins; 510 511 } 512 if (R_remind_at_secs < 0 || R_remind_at_secs > 59) { 513 return 514 invalid_RT_secs; 515 } 516 // validate R_freq 517 if (R_type != RT_oneoff_type) 518 { 519 // R_frequency is required for recurring_type or repeat_duration_type, 520 so validate 521 // and set up the interval count 522 if (R_freq_mins 523 < 0 || R_freq_mins > 59) { 524 return invalid_freq_mins; 525 } 526 if 527 (R_freq_secs < 0 || R_freq_secs > 59) { 528 return invalid_freq_secs; 529 530 } 531 R_freq = R_freq_hrs * 3600 + 532 R_freq_mins * 533 60 + 534 R_freq_secs; 535 if (R_type == RT_recurring_type && 536 R_freq == 0) { 537 return invalid_freq; 538 } 539 } else { 540 R_freq 541 = 0; 542 } 543 // validate R_duration time if R_type is repeat_duration_type 544 545 if (R_type == RT_repeat_duration_type) { 546 if (R_duration_mins < 0 || 547 R_duration_mins > 59) { 548 return invalid_duration_mins; 549 } 550 551 if (R_duration_secs < 0 || R_duration_secs > 59) { 552 return invalid_duration_secs; 553 554 } 555 R_duration = R_duration_hrs * 3600 + 556 R_duration_mins 557 * 60 + 558 R_duration_secs; 559 if (R_freq > R_duration) 560 { 561 return invalid_freq; 562 } 563 } else { 564 R_duration 565 = 0; 566 } 567 // now calculate the numer of seconds since last midnight of 568 this real-time 569 R_remind_at = R_remind_at_hrs * 3600 + 570 R_remind_at_mins 571 * 60 + 572 R_remind_at_secs ; 573 // now look for an empty 574 slot... 575 R_entry = 0; // start with first reminder entry and scan until empty 576 entry found, or not 577 do { 578 if ( R_list[R_entry].R_type == inactive) 579 { 580 // this entry is not used, so set up it... 581 R_list[R_entry].R_subtype 582 = R_subtype; 583 R_list[R_entry].R_remind_at = R_remind_at; // 584 real-time of reminder in secs from last midnight 585 R_list[R_entry].R_freq 586 = R_freq; // only used if R_type is recurring_type or repeat_duration_type 587 588 R_list[R_entry].R_duration = R_duration; // only used if R_type is 589 repeat_duration_type 590 R_list[R_entry].R_count_down = 0; // 591 not used for RTRs 592 // End user variables held in a reminder entry 593 594 R_list[R_entry].R_user1 = R_user1; 595 R_list[R_entry].R_user2 = 596 R_user2; 597 R_list[R_entry].R_user3 = R_user3; 598 R_list[R_entry].R_user4 599 = R_user4; 600 // set this reminder as active 601 noInterrupts(); 602 603 R_list[R_entry].R_type = R_type; // note, setting this last value/field 604 will trigger the scan process 605 interrupts(); 606 return R_entry; 607 // return with the entry number of the reminder 608 } 609 R_entry++; 610 // look at next entry 611 } 612 while ( R_entry < max_R_list_entries); 613 614 return reminder_list_full; 615 } 616 return RTC_not_configured; 617} 618 619// 620// 621 Routine deletes a reminder entry of the specified parameters 622// That is, for 623 the given R_id and R_id_subtype values 624// if 'lockout' true then interrupts 625 are first disabled and then reenabled 626// otherwise. 627// if called from within 628 a routine where interrupts are disabled, eg and 629// interrupt routine then 'lockout' 630 should be set to false as interrupts 631// will already be disabled. 632// 633int 634 delete_reminder(int R_type, int R_subtype) { 635 int R_entry; 636 noInterrupts(); 637 638 for (R_entry = 0; R_entry < max_R_list_entries; R_entry++) { 639 if ( R_list[R_entry].R_type 640 != inactive) { 641 if (R_type == R_list[R_entry].R_type && R_subtype == R_list[R_entry].R_subtype) 642 { 643 // match on this entry, so remove it, ie make inactive 644 R_list[R_entry].R_type 645 = inactive; 646 interrupts(); 647 return success; 648 } 649 } 650 651 } 652 interrupts(); 653 return fail; 654} 655 656// 657// Print given Reminder 658 details 659// 660void print_reminder(int R_entry) { 661 int R_type; 662 long unsigned 663 int R_remind_at, hrs, mins, secs; 664 noInterrupts(); 665 if (R_list[R_entry].R_type 666 != inactive) { 667 Serial.println(F("============ Reminder Parameters ================")); 668 669 Serial.print(F("Reminder entry no: ")); Serial.println(R_entry); 670 R_type 671 = R_list[R_entry].R_type; 672 Serial.print(F("R_type: ")); Serial.print(R_type); 673 674 Serial.print(F(" R_subtype: ")); Serial.println(R_list[R_entry].R_subtype); 675 676 if (R_type >= ET_oneoff_type && R_type <= ET_repeat_duration_type) { 677 Serial.print(F("R_start_in: 678 ")); Serial.print(R_list[R_entry].R_start_in); 679 Serial.print(F(" ")); 680 681 } else { 682 R_remind_at = R_list[R_entry].R_remind_at; 683 hrs = 684 R_remind_at / 3600; 685 secs = R_remind_at % 3600; 686 mins = secs / 60; 687 688 secs = secs % 60; 689 Serial.print(F("R_remind_at: ")); 690 Serial.print(hrs); 691 Serial.print(F(":")); 692 Serial.print(mins); Serial.print(F(":")); 693 694 Serial.print(secs); Serial.print(F(" (")); 695 Serial.print(R_remind_at); 696 697 Serial.println(F(")" )); 698 } 699 Serial.print(F("R_freq: ")); 700 Serial.print(R_list[R_entry].R_freq); 701 Serial.print(F(" R_duration: ")); 702 Serial.println(R_list[R_entry].R_duration); 703 Serial.print(F("R_count_down: 704 ")); Serial.println(R_list[R_entry].R_count_down); 705 Serial.println(F("============== 706 User Parameters ==================")); 707 Serial.print(F("R_user1: ")); Serial.println(R_list[R_entry].R_user1); 708 709 Serial.print(F("R_user2: ")); Serial.println(R_list[R_entry].R_user2); 710 711 Serial.print(F("R_user3: ")); Serial.println(R_list[R_entry].R_user3); 712 713 Serial.print(F("R_user4: ")); Serial.println(R_list[R_entry].R_user4); 714 715 } else { 716 Serial.print(F("\ 717\ 718** Reminder ")); 719 Serial.print(R_entry); 720 721 Serial.println(F(" is inactive **")); 722 } 723 Serial.flush(); 724 interrupts(); 725} 726
Introduction - A00_Remsys_v2_00.ino
c_cpp
Introduction segment
1/* version 1.00 rev 1, Ron D Bentley (Stafford UK) March 2020. 2 * 3 version 1.00 rev 2, July 2020 - moved print strings to program storage space 4 to reduce 5 * amount of dynamic storage used. 6 Plus improved efficiency in 7 * heart beat 8 coding. 9 * version 2.0, Aug 2020 - this version allows REM_SYS to be 10 configured without the real-time clock 11 * module, 12 so that ETR only applications can be designed. 13This framework provides a basis 14 for the development of applications that would benefit from timed 15reminder alerting. 16 Timed reminder alerts can be defined/created as elapsed time reminders (ETRs)or 17as 18 real-time reminders (RTRs) linked to a real-time clock (RTC). Any number of reminders 19 can be 20active at any one time, dependent on the size of the Reimder List initialised. 21____________________________________________________________________________________________________ 22Copyright 23 (c) Ron D Bentley (UK) 24The extent this licence shall be limited to Non-profit 25 Use. 26Permission is hereby granted, free of charge, for non-profit purposes to 27 any person obtaining 28a copy of this software and associated documentation files 29 (the "Software"), to deal in the 30Software without restriction, including without 31 limitation the rights to use, copy, modify, merge, 32publish, distribute, sublicense 33 copies of the Software, and to permit persons to whom the Software 34is furnished 35 to do so, subject to the following conditions: 36 37The above copyright notice, 38 acknowledgements and this permission notice shall be included in all 39copies 40 or substantial portions of the Software. 41 42THE SOFTWARE IS LIMITED TO NON-PROFIT 43 USE AND IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 44EXPRESS OR IMPLIED, 45 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR 46A 47 PARTICULAR PURPOSE AND NONINFRINGEMENT. 48IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 49 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 50WHETHER IN AN ACTION 51 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 52SOFTWARE 53 OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 54____________________________________________________________________________________________________ 55 56See 57 User Guide for full capabilities and instructions for implementation. 58 59 */ 60 61
Setup() - H00_Setup.ino
c_cpp
Setup() segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// 4 This framework is provided with two standing reminders that provide: 5// 1. a 6 heart beat visual monitor which shows that the code is operating. 7// This 8 is configured for pin 13 (on board LED), but do change if necessary. 9// 2. a 10 midnight processor to allow daily processing/housekeeping to be 11// carried 12 out as necessary ech midnight.a 13// 14int hb_intensity = 255; 15 // start by setting to max output level 16#define heart_beat_pin LED_BUILTIN 17 // digital pin for visible heart beat 18#define heart_beat 254 19 // ETR reminder sub_type for heart beat 20#define midnight 255 21 // RTR reminder sub_type for midnight processing 22 23// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 24// 25 H00_Setup [DECLARE] - insert any additional declaration requirements here. 26// 27 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 29 30 31 32// 33void 34 setup() 35{ 36 int result; 37 if (diags_on) { 38 Serial.begin(115200); 39 40 if (RTC_enabled) { 41 !rtc.begin(); 42 if (!rtc.isrunning()) 43 44 { 45 Serial.println(F("!setup() - RTC is not operating, terminating!")); 46 47 Serial.flush(); 48 exit(0); 49 } 50 } 51 } 52 if (RTC_enabled) 53 { 54 // Set up 'today_day_number' to provide ready access to this value at 55 any point 56 // in the program without the need for it to be recalculated. 57 58 // This will get incremented each midnight to stay in step with the current 59 date. 60 DateTime now = rtc.now(); 61 today_day_number = day_number(now.day(), 62 now.month(), now.year()); 63 today_day_of_week = (today_day_number + day_of_week_offset) 64 % 7; // map to 0 (Sunday), 1 (Monday),.., 6 (Saturday) 65 } 66 // ********************************************************************************** 67 68 // set up the two standing reminders: 69 // 1. the heart beat visual monitor 70 as an ETR, to show processes are operating, and 71 // 2. the midnight RTR each 72 midnight to deal with any daily processing/housekeeping 73 // The alerts for each 74 of the above remnders are dealt with in the main segment. 75 // ********************************************************************************** 76 77 int freq_secs, freq_subsecs; 78 pinMode(heart_beat_pin, OUTPUT); 79 // set 80 the ETR reminder for the heart beat to be 1/2 second if possible, if not, 81 // 82 set to 1 second. 83 freq_subsecs = scans_per_sec / 2; // 84 take as 1/2 scan rate per second, if possible 85 if (freq_subsecs == 0) freq_secs 86 = 1; else freq_secs = 0; 87 result = create_ET_reminder(ET_recurring_type, heart_beat, 88 89 0, 0, 0, 0, // start immediately 90 91 0, 0, freq_secs, freq_subsecs, // 1/2 or 1 second 92 frequency 93 0, 0, 0, 0, // not 94 used 95 0, 0, 0, 0); // not used 96 97 if (result < 0 && diags_on) { 98 Serial.print(F("setup() - error creating 99 ETR for heart_beat, error value = ")); 100 Serial.println(result); 101 Serial.flush(); 102 103 } 104 if (RTC_enabled) { 105 // set up RTR for each midnight, so that any 106 daily processing can be performed 107 result = create_RT_reminder(RT_recurring_type, 108 midnight, 109 0, 0, 0, // start at midnight 110 (00:00:00) 111 24, 0, 0, // repeat each midnight 112 (24 hrs) 113 0, 0, 0, // not used 114 0, 115 0, 0, 0); // not used 116 if (result < 0 && diags_on) { 117 Serial.print(F("setup() 118 error creating RTR for midnight, error value = ")); 119 Serial.println(result); 120 121 Serial.flush(); 122 } 123 } 124 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 125 126 // H00_Setup [INITIALISE] - insert any additional initialisation 127 // requirements 128 here, but before the timers and remind queues are initialised 129 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 130 131 132 133 134 135 // ****************************************************************** 136 // 137 create free chain and initialise timers (0 or 2 and 1) 138 // ****************************************************************** 139 140 create_RQ_free_chain(); // create free chain of reminder queue blocks 141 suspend_reminders(); 142 // ensure timers do not action the reminder list yet 143 initialise_timers(); 144 145 resume_reminders(); // allow timer scans of the reminder list 146} 147
Main Loop - M00_Main_Segment.ino
c_cpp
Main Loop segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// 4 main segment for end user code 5// insert end user code where indicated: 6// 7 1. if timed reminder alert processing is needed then within the 'do{if (scan_RQ){...}' 8// 9 control block, otherwise. Use swich case on R_subtype to process alerts. 10// 11 2. for non-reminder requirements, place code within the 'do{if (scan_RQ)..else 12 {..}' 13// control block 14 15void loop() { 16 do { 17 if (scan_RQ() 18 != no_reminder_requests) { 19 // ******************************************************************************* 20 21 // the following global data variables are available at this point relating 22 to the 23 // reminder triggered/alerted: 24 // 1. R_type - 25 reminder type, ETRs: 1, 2, or 3, / RTRs: 4, 5, or 6 26 // 2. R_subtype - 27 reminder subtype (0 <= R_subtype <= 255) 28 // 3. R_status - set 29 to 1 if this is the FINAL reminder alert, 0 otherwise. 30 // Only 31 relevant for oneoff and repeat_duration reminder types. 32 // 4. R_user1-R_user4 33 - user data values set up when the reminder was created 34 // 35 // 36 The above variable are those defined when the timed reminder was created. 37 38 // These can be 'crafted' to control flow and decision processes. 39 // 40 41 // Insert end user code here to process timed reminder alerts by R_subtype. 42 43 // ******************************************************************************* 44 45 switch (R_subtype) { 46 case heart_beat: 47 // ******** 48 provides a visual indication that the program is running 49 analogWrite(heart_beat_pin, 50 hb_intensity); 51 // toggle heart beat output level for next pass 52 53 hb_intensity = 255 - hb_intensity; // produces 0 and 255 on alternate 54 passes 55 break; 56 case midnight: 57 // ********* midnight 58 processing. Insert any code relevant for daily housekeeping 59 // Note, 60 this is only entered if the RTC is configured. 61 today_day_number++; 62 // day_number for today, a new day 63 today_day_of_week 64 = (today_day_of_week + 1) % 7; // next day of week value 65 break; 66 67 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 68 // 69 M00_Main_Segment [ALERT PROCESSING], timer alert processing - 70 // Insert 71 ETR/RTR alert switch case code handling here: 72 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 73 74 75 76 77 78 79 80 81 // default switch value 'catcher' 82 default: 83 Serial.print(F("!Spurious 84 switch value=")); 85 Serial.println(R_subtype); 86 Serial.flush(); 87 88 display_now_date_time(); 89 break; 90 } 91 } else 92 { 93 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 94 95 // M00_Main_Segment [GENERAL PROCESSING]. 96 // Reminder queue is 97 currently empty, so do other things. 98 // Insert end user code here for 99 processing non-reminder alerts 100 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 101 102 103 104 105 106 107 108 } 109 } while (true); 110} 111
Queuing - D10_Queue_Segment.ino
c_cpp
Queue segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// 4 This tab contains the routines to establish and maintain reminder 5// queue 6 management. 7// 8// A free chain of blocks is set up that are then allocated 9 to reminder alert requests 10// as they decome due. 11// 12// 'RQ' is used 13 as the blocks of memory to be allocated/deallocated on request. 14// 'RQ' comprises 15 a number of free blocks each of 5 integers, the first word of each block 16// contains 17 a forward pointer to next block or end of chain value(free/used). 18 19// When 20 a free block is allocated to a reminder request, it is inserted at the beginning 21 of the 22// reminder queue with the first word of the block being a forward pointer 23 to next allocated 24// block and the remainder of block containing user values 25 defined as part of the remider set up. 26// 27// Note: 28// 1. The number 29 of free blocks in the RQ free chain is defined by 'max_RQ_free_chain_blocks'. 30// 31 2. The implementation is as a queue, first in first out (FIFO). 32// 33 34// 35 Set up the free RQ chain as defined by 'max_RQ_free__chain_blocks' 36// This 37 is called just once per start/reset from te setup() process. 38// 39void create_RQ_free_chain() 40 { 41 int ptr, last_RQ_block; 42 last_RQ_block = max_RQ_free_chain_blocks - 43 1; 44 for (ptr = 0; ptr < max_RQ_free_chain_blocks; ptr++) { 45 if (ptr == 46 last_RQ_block) { 47 RQ[ptr][0] = end_of_chain_value; // set end of chain 48 49 } 50 else { 51 RQ[ptr][0] = ptr + 1; // set forward 52 block pointer 53 } 54 } 55 num_free_RQ_blocks = max_RQ_free_chain_blocks; 56 57 start_of_free_RQ_chain = 0; 58} 59// 60// Allocates a block from the free 61 chain, if one exists. 62// NOTE: assumes that interrupts are disabled by calling 63 function(s). 64// 65int acquire_block_from_free_RQ() { 66 int block; 67 if 68 (num_free_RQ_blocks > 0) { 69 // There is at least 1 free block left. 70 // 71 Take the next block off the free chain. 72 num_free_RQ_blocks--; // reduce 73 free blocks available. 74 block = start_of_free_RQ_chain; 75 start_of_free_RQ_chain 76 = RQ[block][0]; // take the first free block off the free chain. 77 RQ[block][0] 78 = end_of_chain_value; // set the forward pointer in this free block to out 79 of range. 80 return block; // return with the 'address' of the free 81 block provided to calling routine. 82 } 83 return fail; // no free 84 blocks! Return error condition. 85} 86 87// 88// Returns the given block back 89 to the free chain. 90// NOTE: assumes that reminders are disabled by calling function(s). 91// 92int 93 relinquish_block_to_free_RQ(int block) { 94 if (num_free_RQ_blocks < max_RQ_free_chain_blocks) 95 { 96 // there is space to add this block back to the free chain. 97 num_free_RQ_blocks++; 98 // increase number of free blocks on free chain by 1 99 RQ[block][0] = start_of_free_RQ_chain; 100 101 start_of_free_RQ_chain = block; 102 return success; // relinquish 103 was successful. 104 } 105 return fail; // free chain seems to be full 106 of free blocks! No space to add another. 107} 108 109// 110// The function creates 111 an entry in the interrupt queue for the given interrupt. 112// 113int insert_into_RQ(int 114 R_status, int R_user1, int R_user2, int R_user3, int R_user4) { 115 int block; 116 117 block = acquire_block_from_free_RQ(); 118 if (block == fail) { 119 // no 120 free block available! 121 return fail; 122 } 123 // we have a free block, 124 so chain it into the reminder queue 125 // placing it at the end of the current 126 queue. 127 if (num_RQ_reminders == 0) { 128 // queue is empty, so set start 129 pointer 130 start_of_RQ_chain = block; 131 } else { 132 // at least on 133 entry in RQ queue, so modify forward pointer of last block 134 RQ[end_of_RQ_chain][0] 135 = block; 136 } 137 // now deal with rest of updates 138 num_RQ_reminders++; 139 140 end_of_RQ_chain = block; 141 RQ[block][0] = end_of_chain_value; // set new end 142 of chain 143 RQ[block][1] = R_status; 144 RQ[block][2] = R_user1; 145 RQ[block][3] 146 = R_user2; 147 RQ[block][4] = R_user3; 148 RQ[block][5] = R_user4; 149 return 150 success; 151} 152 153// 154// See if there are any outstanding (unprocessed) reminder 155 requests in the 156// reminder queue. If so, take the first one on the queue and 157 return it to the 158// free chain. The answer to the function is either reminder 159 found 160// or that no outstanding reminder requests exists. 161// 162// Note 163 that this function is not entered via any interrupt routine, so we do need to 164// 165int 166 scan_RQ() { 167 int RQ_block, interrupt; 168 //noInterrupts(); // ensure exclusive 169 access of the RQ and its pointers 170 if (num_RQ_reminders == 0) { 171 // Clear 172 down the end user reminder parameters, if no RQ entry to be processed. 173 R_status 174 = 0; 175 R_type = 0; 176 R_subtype = 0; 177 R_user1 = 0; 178 R_user2 179 = 0; 180 R_user3 = 0; 181 R_user4 = 0; 182 //interrupts(); 183 return 184 no_reminder_requests; 185 } 186 // take the first entry off the RQ as this is 187 the next interrupt to be processed. 188 noInterrupts(); 189 num_RQ_reminders--; 190 191 RQ_block = start_of_RQ_chain; 192 start_of_RQ_chain = RQ[RQ_block][0]; // 193 point to next interrupt to be processed, or end of chain 194 if (num_RQ_reminders 195 == 0) { 196 // last used block to be relinquished, so adjust end of chain pointer 197 198 end_of_RQ_chain = end_of_chain_value; 199 } 200 // set up the interrupt parameters 201 for this interrupt, 202 // solely for end user reference if required 203 R_status 204 = RQ[RQ_block][1]; // active/inactive, R_type and R_subtype 205 R_type 206 = (R_status & 0x7ff) >> 8; // the type of reminder 207 R_subtype = R_status 208 & 0xff; // the reminder subtype value 209 R_status = bitRead(R_status, 210 15); // indicates if last reminder alert recieved, or not 211 R_user1 = RQ[RQ_block][2]; 212 213 R_user2 = RQ[RQ_block][3]; 214 R_user3 = RQ[RQ_block][4]; 215 R_user4 = 216 RQ[RQ_block][5]; 217 relinquish_block_to_free_RQ(RQ_block); // put this block 218 back on the free RQ chain 219 interrupts(); 220 return success; // return with 221 the internal value of the interrupt handler that was assigned to this pin 222} 223// 224// 225 Utility to print the reminder queue (RQ) 226// 227void print_RQ() { 228 int ptr, 229 count, i; 230 if (diags_on) { 231 noInterrupts(); 232 count = num_RQ_reminders; 233 234 ptr = start_of_RQ_chain; 235 Serial.println(F("_______________________________")); 236 237 Serial.println(F(" REMINDER QUEUE")); 238 Serial.print(F(" Num 239 in RQ = ")); 240 Serial.println(count); 241 Serial.print(F(" start of RQ 242 chain = ")); 243 Serial.println(ptr); 244 Serial.print(F(" end of RQ chain 245 = ")); 246 Serial.println(end_of_RQ_chain); 247 if (count > 0) { 248 do 249 { 250 for (i = 0; i < free_chain_entry_size; i++) { 251 Serial.print(F("RQ[")); 252 253 Serial.print(ptr); 254 Serial.print(F("]")); 255 Serial.print(i); 256 257 Serial.print(F("] = ")); 258 Serial.println(RQ[ptr][i]); 259 260 } 261 Serial.print(F("\ 262")); 263 ptr = RQ[ptr][0]; // look 264 at next entry/block 265 count --; 266 } 267 while ((ptr != end_of_chain_value) 268 && (count > 0)); 269 } 270 Serial.println(F("")); 271 Serial.println(F("_______________________________\ 272")); 273 274 Serial.flush(); 275 interrupts(); 276 } 277} 278// 279// Utility to print 280 the free chain 281// 282void print_free_chain() { 283 int ptr, count; 284 if (diags_on) 285 { 286 noInterrupts(); 287 count = num_free_RQ_blocks; 288 ptr = start_of_free_RQ_chain; 289 290 Serial.println(F("")); 291 Serial.println(F("_______________________________")); 292 293 Serial.println(F(" FREE CHAIN")); 294 Serial.print(F(" Num in 295 free chain = ")); 296 Serial.println(count); 297 Serial.print(F("start of 298 free chain = ")); 299 Serial.println(ptr); 300 if (count > 0) { 301 do 302 { 303 Serial.print(F("RQ[")); 304 Serial.print(ptr); 305 Serial.print(F("][0] 306 = ")); 307 Serial.println(RQ[ptr][0]); 308 ptr = RQ[ptr][0]; // look 309 at next entry/block 310 count --; 311 } 312 while ((ptr != end_of_chain_value) 313 && (count > 0)); 314 } 315 Serial.println(F("_______________________________\ 316")); 317 318 Serial.flush(); 319 interrupts(); 320 } 321} 322
Configuration - C00_Configurations.ino
c_cpp
Configuration segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// 4 **************************************************************************************************** 5// 6 USER CONFIGURABLE PARAMETERS ARE REFERENCED HERE (see User Guide): 7// 8#define 9 diags_on true // leave set to true whilst developing/testing 10 11#define 12 RTC_enabled true // Real-time clock enabled if true, false, 13 if not 14 15int ETR_timer_number = 0; // 0 for timer0, 2 for 16 timer2 17 18#define ETR_R_list_scan_freq 100 //time (msecs) between 19 scans of the ETR list (see User Guide) 20// 21// The timer_drift_adjustment variable 22 allows the inaccuracy of timer0/2 to be 23// compensated for, but only as far 24 as the drift per hour in seconds. 25// This is ONLY relevant for ETRs, see User 26 Guide. 27long signed int timer_drift_adjustment = 1; // number of seconds (+/-) 28 per hour to adjust elapsed times 29 30#define max_RQ_free_chain_blocks 16 31 // size of the RQ free chain in blocks 32 33#define max_R_list_entries 10 34 // number of reminder list entries 35// 36// END OF USER CONFIGURABLE DATA/PARAMETERS. 37// 38 ******************************************************************************************************* 39// 40#define 41 ETR_timer_freq 1000 // 1 msec timer interrupt 42 frequency 43#define scans_per_sec ETR_timer_freq / ETR_R_list_scan_freq // number 44 of R_list scans per second 45#define scans_per_min 60 * scans_per_sec 46 // number of R_list scans per minute 47#define scans_per_hr 60 48 * scans_per_min // number of R_list scans per hour 49 50// 51// The 52 timer_drift_adjustment variable allows the inaccuracy of timer1 to be 53// compensated 54 for, but only as far as the drift per hour. See User Guide. 55// 56 57volatile 58 long unsigned int Seconds_since_midnight; // used by RTR scan process 59 60// 61 ************************************ 62// Reminder List variables/definitions: 63 64volatile 65 boolean reminders_suspended = true; // allows reminder list scans to be halted/started 66 67#define 68 success 1 69#define fail -1 70 71#define 72 inactive 0 // if a reminder entry R_type is 0 it is inactive 73#define 74 final_alert 1 75//Reminder types: 76// 1-3 are elapsed remider 77 types and 78// 4-6 are real-time reminder times 79#define ET_oneoff_type 1 80 // Elapsed time (ETR) R_type values 81#define ET_recurring_type 2 // 82 .... 83#define ET_repeat_duration_type 3 // .... 84 85#define RT_oneoff_type 86 4 // Real-time R_type (RTR) valiues 87#define RT_recurring_type 88 5 // .... 89#define RT_repeat_duration_type 6 // .... 90 91 92#define ETR 0 // ETR = Elapsed Time Reminder 93#define 94 RTR 1 // RTR = Real Time Reminder 95 96// definitions 97 used by the create reminder routines 98#define invalid_R_type -1 99#define 100 invalid_R_subtype -2 101#define invalid_R_start_in -3 102#define 103 invalid_start_in_mins -4 104#define invalid_start_in_secs -5 105#define 106 invalid_start_in_subsecs -6 107#define invalid_duration_mins -7 108#define 109 invalid_duration_secs -8 110#define invalid_duration_subsecs -9 111#define 112 invalid_freq_mins -10 113#define invalid_freq_secs -11 114#define 115 invalid_freq_subsecs -12 116#define invalid_freq -13 117#define 118 invalid_RT_hrs -14 // pertinent to real-time reminders only 119#define 120 invalid_RT_mins -15 // ... 121#define invalid_RT_secs -16 // 122 ... 123#define reminder_list_full -99 124#define RTC_not_configured -100 125 126volatile 127 struct Reminder_List { 128 // Reminder List variables for managing reminder entries 129 130 int R_type; // R_type = 0 if entry unused, 131 int R_subtype; // end user 132 definable 133 union { 134 long unsigned int R_remind_at; // used for RTR processing 135 136 long unsigned int R_start_in; // used for ETR processing 137 }; 138 long 139 unsigned int R_freq; 140 long unsigned int R_duration; 141 long unsigned int R_count_down; 142 143 // End user variables held in a reminder entry 144 int R_user1; 145 int R_user2; 146 147 int R_user3; 148 int R_user4; 149} R_list[max_R_list_entries]; 150 151// **************************************** 152// 153 Reminder Queue variables 154#define free_chain_entry_size 6 155#define 156 end_of_chain_value -1 157 158// When allocated to a triggered/elapsed 159 reminder,a block from the free chain will be allocated 160// to the active RQ and 161 be set up as follows: 162// word [0] - forward chain pointer, or end of 163 chain value 164// word [1] - a compound integer that contains: 165// bit 166 15 - set to 1 if this entry represents the last reminder alert for the 167// 168 triggered/elapsed reminder or, 169// set 170 to 0 otherwise 171// bits 11-14 - not used 172// bits 173 8-10 - the reminder type that was triggered/elasped 174// bits 175 0-7 - the reminder subtype that was triggered/elapsed 176// words [2] to [5] 177 - the end user parameters defined at reminder creation time 178 179volatile int 180 RQ[max_RQ_free_chain_blocks][free_chain_entry_size]; 181 182// Pointers to manage 183 the free blocks in the RQ 184volatile int start_of_free_RQ_chain; 185volatile int 186 num_free_RQ_blocks; 187 188// Pointers to manage the allocated blocks in the RQ 189volatile 190 int start_of_RQ_chain = -1; 191volatile int end_of_RQ_chain = -1; 192volatile 193 int num_RQ_reminders = 0; 194 195// general values to manage RQ processes 196#define 197 no_reminder_requests -1 198#define no_entry -1 199 200// The following 201 R_xxx variables are set up by scan_RQ so that they can be referenced from the main 202// 203 end user code following a timed reminder alert being taken off the queue. 204volatile 205 int R_status; // used to indicate if the reminder alert is a final one or 206 otherwise 207volatile int R_type; // used to indicate what the reminder 208 type was that triggered/elapsed 209volatile int R_subtype; // used to indicate 210 what the reminder subtype was that triggered/elapsed 211volatile int R_user1; // 212 end user parameters defined at reminder creation time 213volatile int R_user2; 214 // ditto 215volatile int R_user3; // ditto 216volatile int R_user4; 217 // ditto 218
Date/Time User Functions - D15_DateTime_Segment.ino
c_cpp
Date/Time User Functions segment
1/* Ron D Bentley (UK) (c) 2020 2 This tab contains additional routines 3 (and more) to support real-time reminders 4 these being in addition to the base 5 framework which privides elapsed time reminder (ETR) 6 functionality. 7*/ 8//**************************************************************************************** 9// 10 Date Functions 11//**************************************************************************************** 12// 13 Define the start date (origin) for elapsed days calculations. 14// There is a constraint 15 on the origin date: 16// 1. it MUST be of the form 01/01/LEAP_YEAR(or pseudo 17 Leap year, eg divisible by 100) 18// 2. Also, if this is changed then change 19 day_of_week_offset too. 20// 21int origin_day = 1; 22int origin_month = 1; 23int 24 origin_year = 2020; 25 26int today_day_number; // used to keep a note of the 27 day_number for today in midnight process 28int today_day_of_week; // used to keep 29 a note of the day_of_week for today in midnight process 30 31#define day_of_week_offset 32 2 // offset value for referencing days_of_week array as 33// 1/1/2020 is a Wednesday, 34 so offset provides 35// correcting adjustment for day_of_week function, 36// as 37 day_number(1,1,2020) gives 1 38#define valid_date 1 39#define invalid_day 40 -1 41#define invalid_month -2 42#define invalid_year -3 43 44#define Jan 45 1 46#define Feb 2 47#define Mar 3 48#define 49 Apr 4 50#define May 5 51#define Jun 6 52#define 53 Jul 7 54#define Aug 8 55#define Sep 9 56#define 57 Oct 10 58#define Nov 11 59#define Dec 12 60 61#define 62 Sunday 0 63#define Monday 1 64#define Tuesday 2 65#define 66 Wednesday 3 67#define Thursday 4 68#define Friday 5 69#define 70 Saturday 6 71 72char days_of_week[7][12] = 73{ 74 "Sunday", "Monday", 75 "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 76}; 77byte 78 days_in_month[13] = 79{ 80 0, // entry 0 not used 81 31, 28, 31, 30, 31, 30, 82 31, 31, 30, 31, 30, 31 // Jan, Feb, March, etc 83}; 84int accumulated_days_in_month[13] 85 = 86{ 87 0, // entry 0 not used 88 0, 31, 59, 90, 120, 151, 181, 212, 243, 89 273, 304, 334 // Jan, Feb, March, etc 90}; 91// 92// Checks the given day,month,year 93 being a valid date 94// 95int check_date(int day, int month, int year) { 96 int 97 Days_in_month; 98 if (year < origin_year) { 99 return invalid_year; 100 } 101 102 if (month < Jan || month > Dec) { 103 return invalid_month; 104 } 105 Days_in_month 106 = days_in_month[month]; 107 if (month == Feb && leap_year(year)) { 108 Days_in_month++; 109 // adjust for leap year 110 } 111 if (day < 1 || day > Days_in_month) { 112 return 113 invalid_day; 114 } 115 return valid_date; 116} 117// 118// leap_year will return 119 whether the given year is a leap year 120// and takes into account the leap year 121 rule: 122// if divisible by 4 then it IS a leap year, UNLESS 123// divisible 124 by 100 then it is NOT a leap year, UNLESS 125// divisible by 400 the it IS a 126 leap year. 127// 128int leap_year(int year) { 129 if (year % 400 == 0) { 130 return 131 true; 132 } 133 if (year % 100 == 0) { 134 return false; 135 } 136 if (year 137 % 4 == 0) { 138 return true; 139 } 140 return false; 141} 142// 143// day_number 144 will return the number of inclusive elapsed days from the 145// origin date of the 146 given parameter date, taking ino account leap years. 147// 148int day_number(int 149 day, int month, int year) { 150 int num_leaps, yr; 151 // start by counting the 152 number of leap years from the origin year 153 // to the given year. 154 num_leaps 155 = 0; 156 for (yr = origin_year; yr <= year; yr = yr + 4) { 157 if (leap_year(yr)) 158 num_leaps++; 159 } 160 // final adjustment if given year being a leap 161 // 162 year and given month is Jan or Feb. 163 if (month < Mar && leap_year(year)) { 164 165 num_leaps--; 166 } 167 // now calculate elapsed days... 168 return (year 169 - origin_year) * 365 170 + accumulated_days_in_month[month] 171 + 172 day 173 + num_leaps; 174} 175// 176// Given a day_number, function will 177 determine the date as day, month, year. 178// 179void date_from_day_number(int day_number, 180 int &day, int &month, int &year) { 181 int days_in_year, Days_in_month; 182 // 183 determine what year the day_number falls in, count up from origin_year 184 // in 185 full years as days. 186 year = origin_year; 187 if (leap_year(year)) { 188 days_in_year 189 = 366; 190 } else { 191 days_in_year = 365; 192 } 193 do { 194 if (day_number 195 > days_in_year) { 196 // move year on 197 day_number = day_number - days_in_year; 198 199 year++; 200 if (leap_year(year)) { 201 days_in_year = 366; 202 203 } else { 204 days_in_year = 365; 205 } 206 } 207 } 208 while 209 (day_number > days_in_year); 210 // now step through days_in_month to determine 211 month. What is 212 // left as the remainder is the day of the month, and we are 213 done. 214 month = 1; 215 Days_in_month = days_in_month[1]; 216 while (month <= 217 Dec && day_number > Days_in_month) { 218 if (month == Feb && leap_year(year)) 219 { 220 Days_in_month = 29; 221 } else { 222 Days_in_month = days_in_month[month]; 223 224 } 225 day_number = day_number - Days_in_month; 226 month++; 227 if 228 (month <= Dec) { // this set up for next while cycle test condition 229 Days_in_month 230 = days_in_month[month]; 231 if (month == Feb && leap_year(year)) { 232 Days_in_month++; 233 234 } 235 } 236 } 237 day = day_number; // what is left in day_number is 238 now that day of the month 239} 240// 241// returns the index for the day of the 242 week in the days_of_week array 243// 244int day_of_week(int day, int month, int 245 year) { 246 return (day_number(day, month, year) 247 + day_of_week_offset) 248 % 7; // map to 0 (Sunday), 1 (Monday), ... , 6 (Saturday) 249} 250 251//****************************************************************************************** 252// 253 date & time funcions required for other ETR/RTR code 254// DO 255 NOT REMOVE 256//****************************************************************************************** 257// 258 RTC libraries and declarations 259#include <RTClib.h> 260 261RTC_DS1307 rtc; 262 263void 264 display_now_date_time() { 265 if (RTC_enabled) { 266 DateTime now = rtc.now(); 267 268 Serial.print(now.day()); 269 Serial.print(F("/")); 270 Serial.print(now.month()); 271 272 Serial.print(F("/")); 273 Serial.print(now.year()); 274 Serial.print(F(", 275 ")); 276 Serial.print(days_of_week[now.dayOfTheWeek()]); 277 Serial.print(F(", 278 ")); 279 Serial.print(now.hour()); 280 Serial.print(F(":")); 281 Serial.print(now.minute()); 282 283 Serial.print(F(":")); 284 Serial.println(now.second()); 285 Serial.flush(); 286 287 } 288} 289 290// 291// Function returns the number of seconds since midnight. 292long 293 unsigned int seconds_since_midnight() { 294 long unsigned int secs, mins, hrs; 295 296 if (RTC_enabled) { 297 if (rtc.isrunning()) { 298 DateTime now = rtc.now(); 299 300 hrs = now.hour(); 301 mins = now.minute(); 302 secs = now.second(); 303 304 return hrs * 3600 + mins * 60 + secs; 305 } else { 306 Serial.println(F("!secs_since_midnight 307 - RTC is not operating, terminating!")); 308 Serial.flush(); 309 exit(0); 310 311 } 312 } else return 0; // RTC not configured 313} 314
Timers - D00_Timers_Segment.ino
c_cpp
Timer segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2 3// This 4 segment comprises the timer setups and ISRs that underpin this framework. 5// 6 They are used to provide the timimg for both ETRs and RTRs. 7// 8// timer0 and 9 timer2 are used as the source timer for processing ETRs, and 10// timer1 is used 11 as the source timer for processing RTRs. 12// 13// Note that the timer frequency 14 for RTR processing is fixed (timer1). 15// However, the frequency for processing 16 ETRs is end user configurable (see User Guide). 17// 18// ************************************************************************************** 19// 20 timer initialisation routines for timer0, timer1 and timer2: 21// 22// ETR timer: 23// 24 Set up timer0 interrupt for processimg ETRs for 1000 cycles per second, 25// 1kHz 26 interrupts, ie for 1 millisecond interrupts 27void initialise_timer0() { 28 //set 29 timer0 interrupt at 1kHz 30 noInterrupts(); 31 TCCR0A = 0; 32 TCCR0B = 0; 33 34 TCNT0 = 0; 35 // set compare match register for 1khz increments 36 OCR0A 37 = 249; // (16*10^6) / (1000*64) - 1 (must be <256) 38 // turn on CTC 39 mode 40 TCCR0A |= (1 << WGM01); 41 // Set CS01 and CS00 bits for 64 prescaler 42 43 TCCR0B |= (1 << CS01) | (1 << CS00); 44 // enable timer compare interrupt 45 46 TIMSK0 |= (1 << OCIE0A); 47 interrupts(); 48} 49 50// RTR timer: 51// Set 52 up timer1 interrupt for processing RTRs for 2 cycles per second, 53// 2Hz interrupts, 54 ie for 1/2 second interrupts 55// 56void initialise_timer1() { 57 noInterrupts(); 58 59 TCCR1A = 0; 60 TCCR1B = 0; 61 TCNT1 = 0; 62 // set Compare Match Register 63 (CMR) for 1msec (2hz) increments 64 OCR1A = 31249; // 65 (16,000,000)/(256*2)) - 1 66 // turn on CTC mode 67 TCCR1B |= (1 << WGM12); 68 // WGM12 = 3 69 // Set CS11 bit for 256 prescaler 70 //TCCR1B 71 |= (1 << CS11) | (1 << CS10); // CS11 = 1 and CS10 = 0 72 TCCR1B |= (1 << CS12); 73 // CS12 = 2 74 // enable timer1 compare interrupt 75 TIMSK1 76 |= (1 << OCIE1A); // OCIE1A = 1 77 interrupts(); 78} 79// 80// 81 ETR timer: 82// Alternative source timer for processimg ETRs for 1000 cycles per 83 second, 84// 1kHz interrupts, ie for 1 millisecond interrupts. 85// This is provided 86 if end user needs to use delay() function which makes 87// use of timer0, hence 88 a possible conflict. End user encouraged not to 89// use delay()! 90void initialise_timer2() 91 { 92 noInterrupts(); 93 // Clear registers 94 TCCR2A = 0; 95 TCCR2B = 0; 96 97 TCNT2 = 0; 98 // 1000 Hz (16000000/((124+1)*128)) 99 OCR2A = 124; 100 // 101 CTC 102 TCCR2A |= (1 << WGM21); 103 // Prescaler 128 104 TCCR2B |= (1 << CS22) 105 | (1 << CS20); 106 // Output Compare Match A Interrupt Enable 107 TIMSK2 |= (1 108 << OCIE2A); 109 interrupts(); 110} 111// 112// *************************************************************************** 113// 114 timer0, 1 and 2 ISRs: 115// 116// timer0 is used as the timing source for ETR 117 processing exclusively. 118// timer0 interrupt routine will be entered at ETR_timer_freq 119 interrupt frequency 120// defined during the initialisation of timer0. 121// However, 122 processing of the Remind List will only occur every R_List_scan_feq 'ticks' 123// 124 of timer0. 125// 126ISR(TIMER0_COMPA_vect) { 127 static int timer0_tick = 0; 128 // counts out the defined R_list scan frequency for ETRs. 129 if (!reminders_suspended) 130 131 { timer0_tick++; 132 if (timer0_tick == ETR_R_list_scan_freq) { 133 timer0_tick 134 = 0; // reset for next timer1 interrupt 135 // scan ETR entries in 136 reminder list 137 scan_R_list(ETR); 138 } 139 } 140} 141 142// Timer1 143 is used as the timing source for RTR processing exclusively. 144// Timer1 is initialised 145 for 2 cycles per second (2hz) so is entered 146// every 1/2 second. 147// Note 148 that this routne is only active if the RTC is enabled 149// 150ISR(TIMER1_COMPA_vect) 151 { 152 if (!reminders_suspended) 153 { 154 // scan RTR entries in reminder list 155 156 interrupts(); // not normally the right thing, but this works and allows RTC 157 lib to be accessed 158 RTR_Processor(); 159 } 160} 161 162// timer2 is an alternative 163 timer interrupt routine to timer0. 164// It is offered to replace timer0 if end 165 user wishes to use the delay() function! 166// It is used as the timing source 167 for ETR processing exclusively. 168// timer2 interrupt routine will be entered 169 at the ETR_timer_freq interrupt frequency 170// defined during the initialisation 171 of timer2. 172// However, processing of the Remind List will only occur every R_List_scan_feq 173 'ticks' 174// of timer2. 175// 176ISR(TIMER2_COMPA_vect) { 177 static int timer2_tick 178 = 0; // counts out the defined R_list scan frequency for ETRs. 179 if (!reminders_suspended) 180 181 { timer2_tick++; 182 if (timer2_tick == ETR_R_list_scan_freq) { 183 timer2_tick 184 = 0; // reset for next timer1 interrupt 185 // scan ETR entries in 186 reminder list 187 scan_R_list(ETR); 188 } 189 } 190} 191 192// The framework 193 supports one of two timers for processing ETRs, either timer0 or timer2. 194// Both 195 timers are initialised for 1khz (1000 cycles per second, or ETR_timer_freq). 196// 197 If end user wishes to use delay() function, choose to initialise timer2 NOT timer0. 198// 199void 200 initialise_timers() { 201 if (ETR_timer_number == 0) initialise_timer0(); 202 else 203 initialise_timer2(); 204 if (RTC_enabled) {initialise_timer1();} // timer1 deals 205 with the RTC 206} 207 208// 209// Routine sets the reminders_suspended flag to 210 halt reminder list scans by ISR. 211// 212void suspend_reminders() { 213 noInterrupts(); 214 215 reminders_suspended = true; 216 interrupts(); 217} 218 219// 220// Routine clears 221 the reminders_suspended flag to resume reminder list scans by ISR. 222// 223void 224 resume_reminders() { 225 noInterrupts(); 226 reminders_suspended = false; 227 228 interrupts(); 229} 230
Downloadable files
REM_SYS Conceptual Framework Diagram
To assist in the understanding of the overall structure and concepts behind REM_SYS.
REM_SYS Conceptual Framework Diagram
REM_SYS Conceptual Framework Diagram
To assist in the understanding of the overall structure and concepts behind REM_SYS.
REM_SYS Conceptual Framework Diagram
Comments