Creating an access Schedule

Im trying to make an easy to store on SD access schedule

Example:
for the cleaners - they come on Mondays between 8am and 11am
the office workers can access weekdays between 6am and 6pm
the production people have same access as workers + Sat and Sun
the security people have 24hr access

Ive tried a few different methods… which are really really easy to hard code but i cant figure out how i could make it changeable without re-flashing.

I have an idea in my head of 21bytes… in a 2D array
1 bit per hour = 3bytes per day x 7 days (starting from Sunday)

S  M  T  W   T   F   S
1  4  7  10  13  16  19 (0000 to 0700)
2  5  8  11  14  17  20 (0800 to 1300)
3  6  9  12  15  18  21 (1400 to 2300)

so the cleaners will have all zeros except for the first 3 bits in byte 5

so say its monday at 12pm
when my RFID reader detects the cleaners tag it will check Time.weekday() and check Time.hour()
from this it knows what bit to look for - the 5th bit in byte 5 - because its still a zero it wont let them in, it will send me an email and i can fire them for being late again :slight_smile:

So my question is… is this a good way to do it? and whats an efficient way to actually code it?

The rest of my code is based on 3 databases. One keeps usernumber, username, welcome message, and will hold the access schedule. there is another database which keeps a tally of what finger print ‘address’ correlates to which user, and one that pretty much does the same for RFID tags. these are all stored on an SD card at the moment but i want to put them in FRAM when the shield is available.

1 Like

@Hootie81, your approach if fine assuming weekly repetition. Using bits vs bytes will save on the array size but you will have to use bit-level operations to get to the data you need. These are the classics:

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))

By storing your hours in a single unsigned int (4 bytes), the hour is your “bit” to read using the above defines. So you only need a single dimension array like:

unsigned int weekDay[7];

Then, the day of the week forms your array index. :smile:

1 Like

So as a scratch up to read the schedule… bit numbers are 0 to 7 yes? 7 being MSB???

boolean checkSchedule(void) { 
       
    // Time.weekday() returns a number as below
    //   Sunday = 1 so its in byte 1 or 8  or 15
    //   Monday = 2 so its in byte 2 or 9  or 16
    //  Tuesday = 3 so its in byte 3 or 10 or 17
    //Wednesday = 4 so its in byte 4 or 11 or 18
    // Thursday = 5 so its in byte 5 or 12 or 19
    //   Friday = 6 so its in byte 6 or 13 or 20
    // Saturday = 7 so its in byte 7 or 14 or 21
    
    uint8_t B; //stores byte the pointer
    uint8_t t = Time.hour();
    //Check the hour, then change it to set the bit number 
    if ( 0 <= t < 7 ) B=0;// its in byte 1 for the day 
    if ( 7 <= t < 15) { B=7; t -= 7;} // its in byte 2, t sets back to bit 1
    if (15 <= t < 24) { B=14; t -= 14;}// its in byte 3, f sets back to bit 1    
    
    B += Time.weekday(); // B = B + Weekday number as above
    B--; //because the pointer starts at 0 not 1
    
    uint8_t b; //holds the byte to check 
    b = Username.Schedule[B]; 
     
    if (bitRead( b , t )) return true;
    return false;
    
    
    
}

@hootie, Time.now() returns 24hr time so 0-23 hrs. The bits also go from bit 0 to 23. So right off the bat, Time.now() gives you the correct bit “pointer”! Time.weekday() returns the day value starting at 1. So your index to the weekDay[] array is Time.weekday() - 1 so it goes from 0 to 6 (7 days).

So to access any give hour in a weekday:

// Read the weekday/hour schedule
unsigned int scheduled = bitRead(weekDay[Time.weekday() - 1], time.hour());

if (scheduled)
  // That hour has been scheduled (bit = 1)
else
  // No schedule for that hour (bit = 0)

The same idea applies to writing the bits in the schedule. :smile:

Not sure if anyone is interested but someone may find it useful… A fairly simple menu on the serial port to setup an array to be used for a weekly access schedule. the array can then be checked with the checkSchedule function.

i have a stuct that holds username data, one element is: Username.Schedule that is a byte array of 21. i chose this because its really easy to store on an SD card with the arduino EDB library.

This is added to your code to call the menu function, i have it in the add user and update user menu’s:

while (!addSchedule()) SPARK_WLAN_Loop(); 

it will continue looping until the function is finished and returns a 1 for done or -1 for canceled.

and to check the schedule i load the Username,Schedule into RAM (this is done when the fingerprint reader has checked the print and determined whos it is)

boolean a = checkSchedule(); 

if its true the the bit was set and the user has access if not then they are not allowed in

the functions are below

    void printSchedule(uint8_t days) {
               
            Serial.println("Day    Time  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 00");
    
            int a;
            uint8_t day = 0;
            while (day <= 6) {
                day++;
                a = days;
                if (bitRead(a, day - 1)) {
        
                    if (day == 1) Serial.print("   Sunday :    ");
                    if (day == 2) Serial.print("   Monday :    ");
                    if (day == 3) Serial.print("  Tuesday :    ");
                    if (day == 4) Serial.print("Wednesday :    ");
                    if (day == 5) Serial.print(" Thursday :    ");
                    if (day == 6) Serial.print("   Friday :    ");
                    if (day == 7) Serial.print(" Saturday :    ");
        
                    int j;
                    int k;
                    for (j = 0; j <= 7; j++) {
                        k = Username.Schedule[day - 1];
                        Serial.print(bitRead(k, j));
                        Serial.print("  ");
                    }
                    for (j = 0; j <= 7; j++) {
                        k = Username.Schedule[day + 6];
                        Serial.print(bitRead(k, j));
                        Serial.print("  ");
                    }
                    for (j = 0; j <= 7; j++) {
                        k = Username.Schedule[day + 13];
                        Serial.print(bitRead(k, j));
                        Serial.print("  ");
                    }
                    Serial.println("   ");
        
                }
            }
            return;
        }
        
    boolean checkSchedule(void) {

    if (Time.now() <= 1400000000) return true; //current time not set, so we let them in
   
    uint8_t B; //stores byte the pointer

    uint8_t t = Time.hour();

    //Check the hour, see if its in the 1st 2nd or 3rd byte, then change it to set the bit 
    if (t >= 0 && t < 8) B = 0; // its in the 1st byte for the day 
    if (t >= 8 && t < 16) {
        B = 7;
        t -= 8;
    } // its in the 2nd byte for the day, t sets back to bit 0
    if (t >= 16 && t < 24) {
        B = 14;
        t -= 16;
    }// its in the 3rd byte for the day, t sets back to bit 0    

    B += (Time.weekday() - 1); // B = B + Weekday number - 1 so it starts a 0 instead of 1

    uint8_t b = Username.Schedule[B]; //holds the byte to check so we don't mess up the stored schedule

    if (bitRead(b, t)) return true;
    return false;
}    
    
        
        int addSchedule() {
        
            uint8_t weekday;
            uint8_t daystoset = 0;
            char s[2];
        
            char numascii[3];
            int starttime;
            int finishtime;
        
            Serial.println("Choose a Weekday:        Or Group:");
            Serial.println("  1. Sunday                a. Weekdays");
            Serial.println("  2. Monday                b. Weekends");
            Serial.println("  3. Tuesday               c. Full Week");
            Serial.println("  4. Wednesday");
            Serial.println("  5. Thursday         Press d when your Done");
            Serial.println("  6. Friday");
            Serial.println("  7. Saturday          Or press q to Quit");
            readString(s, 2);
        
            if (s[0] == 'd') return 1;
            if (s[0] == 'q') return -1;
            if (s[0] == 'a') daystoset = 0b00111110;
            if (s[0] == 'b') daystoset = 0b01000001;
            if (s[0] == 'c') daystoset = 0b01111111;
        
            weekday = atoi(s);
            if (weekday >= 1 && weekday <= 7) bitSet(daystoset, weekday - 1);
        
            printSchedule(daystoset);
        
            Serial.println("Choose a Method");
            Serial.println("  1. Add Access");
            Serial.println("  2. Remove Access");
            Serial.println("  3. Back");
            readString(s, 2);
            uint8_t access = atoi(s);
            if (access < 1 || access > 2) return 0;
        
            Serial.print("Enter start time ie 21 for 9pm : ");
            readString(numascii, 3);
            starttime = atoi(numascii);
            Serial.println(starttime);
            if (starttime >= 24 || starttime < 0) {
                Serial.println("Start time must be between 0 and 23");
                return 0;
            }
        
            Serial.print("Enter End time ie 22 for 10pm (24 for Midnight): ");
            readString(numascii, 3);
            finishtime = atoi(numascii);
            Serial.println(finishtime);
            if (starttime >= finishtime) {
                Serial.println("Start time must be before End time");
                return 0;
            }
            if (finishtime > 24 || finishtime < 0) {
                Serial.println("End time must be between 1 and 24");
                return 0;
            }
        
            Serial.print("Set time from ");
            Serial.print(starttime);
            Serial.print(":00 to ");
            Serial.print(finishtime);
            Serial.print(":00 as ");
            if (access == 1) Serial.println("Access Allowed");
            if (access == 2) Serial.println("Access Denied");
        
            Serial.print(" Press y to continue");
            char c = readSelection();
            if (c != 'y') return 0;
            Serial.print("Saving schedule...");
        
            int a;
            uint8_t day = 0;
            uint8_t starttime1;
            while (day <= 6) {
                day++;
                starttime1 = starttime; //because we need to reset it after incrementing.
                a = daystoset;
                if (bitRead(a, day - 1)) {
        
                    while (starttime1 <= 7 && starttime1 < finishtime) {
                        if (access == 1) bitSet(Username.Schedule[day - 1], starttime1);
                        if (access == 2) bitClear(Username.Schedule[day - 1], starttime1);
        
                        starttime1++;
                    }
                    while (starttime1 <= 15 && starttime1 < finishtime) {
                        if (access == 1) bitSet(Username.Schedule[day + 6], starttime1 - 8);
                        if (access == 2) bitClear(Username.Schedule[day + 6], starttime1 - 8);
        
                        starttime1++;
                    }
                    while (starttime1 <= 23 && starttime1 < finishtime) {
                        if (access == 1) bitSet(Username.Schedule[day + 13], starttime1 - 16);
                        if (access == 2) bitClear(Username.Schedule[day + 13], starttime1 - 16);
        
                        starttime1++;
                    }
                }
            }
        
        
            Serial.println("Done!");
            return 0;
        
        }
4 Likes