Components and supplies
Wire, Hook Up
DS1307 64 x 8, Serial, I²C Real-Time Clock
Arduino UNO
Apps and platforms
Arduino IDE
Project description
Code
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
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
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
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
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
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
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
Arduino REM_SYS Asynchronous Timed Reminders
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
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
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
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
Arduino REM_SYS Asynchronous Timed Reminders
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
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
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
Only logged in users can leave comments