Archive for the ‘openaccess’ Category

OpenAccess eeprom functions

July 1, 2016

I defined a lot of commands about how to store and retrieve stuff from the onboard eeprom.  Here they are with hopefully enough explanation.

//EEPROM stuff
//if I have time I should figure out why it errors out reading over 512 entries (should get 2048)
int entry_size = 64;
byte hash[32];
//store card id (currently a long, might not need to be)
char name[31];
byte priv;
byte command[64]; //add time zone

Initialization stuff, and questions about my sanity.  I defined 64 bytes of ‘command’ data, but never got around to using it (I could have made the define byte command[entry_size]).  The length is because that’s the same amount of space I use for a given user (and I can just index +1 to get that index user) .  You can see that the hash is 32 bits, and I have one priv byte which leaves an arbitrary 31 bytes to store the human readable name while keeping a nice round 64 byte per entry number.  The intent was to use this space for nonvolatile things (which could be stored in the 2560’s eeprom, but I decided to make it so the micro-controller board is fungible.  The only part of that intent that remains is the comment that references a time zone.  It would be easy to add the ability to peek and poke that byte (er… signed whatever-the-hell it would have to be to be a time zone) and have the date retrieval function use that offset.

// dumps the command area of the EEPROM
void SCmd_read_CMD()//debug, dumps command memory
{
read_EE_command();//updates working memory from EEPROM
for (int i = 0; i < 64; i++)
{
Serial.print(command[i], HEX);
}
Serial.println();
}

The command area of the eeprom stores nonvolatile things, but I never defined what.  This function reads it all out into working memory and prints it out for the benefit of the person or script that requested it (this function is triggered by a serial command).

void read_EE_command()//populate working memory from EEPROM
{
//for(int i = 0;i<64;i++)
//{
// command[i] = 0;
//}
for(int i = 0;i<64;i++)
{
command[i] = EEPROM1024.read(i);
}
}

This is the function that actually reads the command section of the eeprom and puts it into working memory.  There is a commented out section that blanks the working memory first, I don’t think that needs to still be there.

void write_EE_cmd()//populate EEPROM from working memory
{
for(int i = 0;i<64;i++)
{
EEPROM1024.write(i,command[i]);
}
}

This writes the volatile command memory to long term storage, doing the opposite of the above function.

void blank_EE_cmd()//debug

{
for(int i = 0;i<64;i++)
{
command[i] = 0;
EEPROM1024.write(i,0);
}
}

When testing code I end up filling the eeprom and need to blank parts of it to test other code, this function probably need not live in a running system.  I would recommend putting in a bunch of debug stuff, but marking it as such so it can be removed later.

void read_EE_name(int index)
{
for(int i = 0;i<31;i++)
{
name[i] = 0;
}
for (int i = 0; i < 31; i++)
{
name[i] = EEPROM1024.read(i+((index+1) * entry_size));//keeps command byte unwritten
}
return;
}

This code reads the name at a given index to working memory.  This is a common part of this code, I move stuff to and from working memory in order to check on it, change it, and then put it back.  This keeps reads and writes to the memory to a minimum and there’s less of a chance that the memory will be saved in an undesirable state.

void read_EE_hash(int index)
{
//for(int i = 0;i<64;i++)
//{
// EEPROM1024.write(i,command[i]);
//}
index +=1;//keeps command byte unwritten
int start_address = (index) * entry_size;
int end_address = start_address + entry_size;

for (int i = 0; i < 32; i++)
{
hash[i] = EEPROM1024.read(((start_address) + 32)+i);
}
return;
}

This code reads a hash at a given index to working memory.  This code is framed a bit differently than the function above, but it accomplishes the same task.  This looks older and more complicated, and should probably be condensed to look like the name retrieval function.

void dump_EE(int start_address, int end_address)//debug
{
//byte data[64];
for (int address = start_address; address < end_address; address++)
{
char data;
data = EEPROM1024.read(address);
Serial.print(address);
Serial.print(” :”);
Serial.print(data, HEX);
Serial.print(” :”);
Serial.println(data);
}
//Serial.println(“DONE”);
return;
}

This is another debug statement to dump eeprom data.  I mostly store my data as ascii so it prints the hex and ascii.  This is how I checked up on the memory during development, and if the intent is to run this board as a dumb I/O terminal it could be useful to leave in or as a method of backing up all the raw data.  If there is a problem this could be helped in debugging it since it does not try to be smart and just gives you all the addresses you ask for.  As always there’s some cruft left from previous formatting of the data, I’d remove the first one but leave the second one as it would be nice to be able to uncomment that later if something starts locking up.

void serial_user_print(int index)//debug? if in final make serial prints logging
{
read_EE_name(index);
read_EE_priv(index);
read_EE_hash(index);
Serial.print(index,DEC);
Serial.print(“;”);
for (int j = 0; j < (sizeof(name)/sizeof(name[0])); j++)
{
if(name[j] != 0)
Serial.print(name[j]);
}
Serial.print(“;”);
Serial.print(priv,HEX);
Serial.print(“;”);
for (int j = 0; j < (sizeof(hash)/sizeof(hash[0])); j++)
{
Serial.print((char)hash[j]);
}
Serial.println();
}

This prints out all the information about a user at a given address.  A more verbose version could be written to decode the priv bits as well.  This is a smarter version of the dump function which gives one index’s user data in a concise format.  It is only sent to the serial port, not to the printer (as pointed out by the comment).

// removes the user with <hash>
//format: RMV_USRH;<hash>
void SCmd_remove_user_hash()
{
char *arg;
arg = SCmd.next();
remove_user_hash(arg);
Serial.println(“user removed”);
}
void remove_user_hash(char hashd[32])////////////////////////////////////////////////////////////////////////////////////////////////untested
{
for(int i = 0;i<num_users;i++)
{
boolean succeed = 1;
read_EE_hash(i);
for (int j = 0; j < (sizeof(hash)/sizeof(hash[0])); j++)
{
if(hash[j] != hashd[j])
{
succeed == 0;
j+=((sizeof(hash)/sizeof(hash[0])));//break
}
}
if(succeed)
{
remove_user(i);
Serial.println(“user removed”);
i+=num_users;//break
}
}
}

This is supposed to be a serial wrapper and the associated function to remove a user with a given hash.  It would search out a perfect match for a given hash then blow away the first user with that hash.  It looks like it actually prints “user removed” twice, once from each function which should be corrected (remove the first instance, leave the one in the internal function and make it log that to the printer as well as the serial port).  There is no output if the hash isn’t found (this should be corrected).  Notice how I break the loop? Classic me. Use at your own peril.

// removes the user with <name>
//format: RMV_USRN;<name>
void SCmd_remove_user_name()
{
char *arg;
arg = SCmd.next();
remove_user_name(arg);
Serial.println(“user removed”);
}
void remove_user_name(char named[31])///////////////////////////////////////////////////////////////////////////////////////////////untested
{
for(int i = 0;i<num_users;i++)
{
boolean succeed = 1;
read_EE_name(i);
for (int j = 0; j < (sizeof(name)/sizeof(name[0])); j++)
{
if((name[j] != 0 || named[j] != 0) && name[j] != named[j])
{
succeed == 0;
j+=((sizeof(name)/sizeof(name[0])));//break
}
}
if(succeed)
{
remove_user(i);
Serial.println(“user removed”);
i+=num_users;//break
}
}
}

This is supposed to be a serial wrapper and the associated function to remove a user with a given human readable name.  It would search out a perfect match for a given name, then blow away the first user with that hash.  It looks like it actually prints “user removed” twice, once from each function which should be corrected (remove the first instance, leave the one in the internal function and make it log that to the printer as well as the serial port).  There is no output if the hash isn’t found (this should be corrected).  Notice how I break the loop? Classic me. Use at your own peril.

// removes the user at <index>
//format: RMV_USR;<index>
void SCmd_remove_user()
{
char *arg;
arg = SCmd.next();
remove_user(atoi(arg));
Serial.println(“user removed”);
}
void remove_user(int i)
{
logging(logDate());
logging(” – “);
loggingln(“removing user”);
serial_user_print(i);
String name = “Free”;
byte priv= 0;
//gonna need to add a blank cardid ince that gets implemented
String hash = “00000000000000000000000000000000”;
write_EE(i, name, priv, hash);
logging(logDate());
logging(” – “);
loggingln(“user removed”);
}

This will remove a user at a given index, log it properly, blanking the hash, priv byte, and changing the name to ‘Free’.  This is the way I think it should be done, finding the user you want to delete by reading through the entries and deciding to eliminate it by index, the above functions were an attempt to automate that (which are a bit unfinished).  This sorta creates a sacred cow with that name, but I’m not worried about it.  I could also use “-1” but “Free” reads better when you dump the data.  Also, there’s no spellchecker in my IDE, so that comment typo was only found here in wordpress.

void populate_blank_users()//debug
{
for (int i = 0;i<num_users;i++)
remove_user(i);
}

Want to remove everyone? Probably do if you’re debugging a bunch.  This just loops through everyone and does that.

// adds a user at a given index
//format: ADD_USR;<index>;<name>;<priv byte>;<hash>
void SCmd_add_user()//rename to SCmd_add_user_index() once above function is done
{
char *arg;
arg = SCmd.next();
int index = atoi(arg);
arg = SCmd.next();
String name = arg;
arg = SCmd.next();
byte priv= atoi(arg);
arg = SCmd.next();
String hash = arg;
write_EE(index, name, priv, hash);
}

This adds a user to a given index, be warned you need the desired name, precalculated hash and priv byte to use this.  It’s mostly useful for restoring after a backup, although I could create a standalone tool to calculate those based on the card ID, PIN, and asking what privileges the user should have.  I already have those functions, they just need to be tweaked to reply to some user input.

//create a function like SCmd_add_user that adds a user at the first free index

I could have sworn I did this but I guess it’s left as an exercise for the reader.  It’s like the ‘search and destroy’ commands, but it’s a ‘search and implant’ which uses the functionality already in the user add functionality if you use they keypad to add the user.

// reads the user at <index>
//format: READ_USR;<index>
void SCmd_read_user()
{
char *arg;
arg = SCmd.next();
serial_user_print(atoi(arg));
//Serial.println(“DONE”);
}

This reads a user at a given index, this is a serial wrapper for a function already made modular for other reasons.

// dumps all users present
//format: DUMP_USR
void SCmd_dump_users()
{
for (int i=0;i<num_users;i++)
{
if(user_present(i))
{
Serial.println(i);
serial_user_print(i);
}
}
//Serial.println(“DONE”);
}

This dumps all present users (use the raw read if you want all the data).  This uses the user_present function, I thought that used the name being ‘Free’ but I just checked and it uses priv byte not equal to zero.  Whatever gets the job done, this actually returns whether the user can open the door and that effectively limits the other functions because I use the door open timer to allow for the triggering of the other functions.

void dump_all_EE() //debug stuff
{
for (int address = 0; address < 131072; address++)
{
char data;
data = EEPROM1024.read(address);
Serial.print(address);
Serial.print(” :”);
Serial.println(data, HEX);
Serial.print(address);
Serial.print(” :”);
Serial.println((char)data);
//Serial.print(” :”);
//Serial.println(data);
}
//Serial.println(“DONE”);
}

Dump all the eeprom!  Hardcoded endstop, don’t remember why but there it is.

void write_EE(int index, String name, byte priv, String hash)
{
index +=1;
char data[64] = {
};
for(int i = 0;i<31;i++)
{
if(name[i] == 0)
{
i+=31;//break
}
else
{
data[i] = name[i];
}
}
data[31] = priv;
for(int i = 0;i<32;i++)
{
data[i+32] = hash[i];
}

int start_address = (index) * entry_size;
int end_address = start_address + entry_size;

//Serial.println(“Writing data”);
for (int address = start_address; address < end_address; address++)
{
EEPROM1024.write(address, data[address % entry_size]);
}
//Serial.println();
}

Here is how I write the eeprom for a user index.  This is used when I dump working memory back to a user slot.  I have the serial wrapper for it, or it is called after adding a user.

This may seem like a lot, and you may not need much of it but I think it’s nice to have.  This is part of the OpenAccess project.

 

Advertisements

OpenAccess Exit button/relay

July 1, 2016

This has to do with the exit button/relay.  I call these functions from elsewhere, but basically it sets the timeout for activating the exit relay.  I also use this as a timeout to enter commands to the system (enter or leave modes, like adding users)

//exit button stuff
const int exit_button = 48; //digital pin the exit button is attached to
const int exit_relay = 0; //relay the door solenoid is on
const unsigned long exit_time = 3000; //time to keep the door open in thousandths of a second
boolean exiting = 0; //whether or not the door should be open (and timer running)
unsigned long exit_start_time = 0; //used for count down timer

That is the initialization; sets the button, relay, timeout, and two variables so it can let the rest of the code execute while it’s open.

void exit()
{
logging(logDate());
logging(” – “);
loggingln(“exit relay open”);
exiting = 1;
exit_start_time = current_time;
digitalWrite(relaysToPins[exit_relay], HIGH);
}

That starts the ‘exiting’ procedure, starts the timer rolling and opens the door.

void exit_timeout()
{
logging(logDate());
logging(” – “);
loggingln(“exit relay closed”);
LCD_default();
RFID_reset();
exiting = 0; //no longer in exiting mode
//exit_start_time = 0; //not needed
digitalWrite(relaysToPins[exit_relay], LOW); //close door
}

That stops the exit procedure, pretty simple really.

if (!digitalRead(exit_button)) //exit button pressed
{
//Serial.println(“exiting”);
exit();
}
//Serial.println(“past exiting”);

That checks to see if the exit button was pressed, and starts the exit procedure.

if (exiting)
{
if (current_time – exit_start_time > exit_time) //exit time out loop
{
exit_timeout();
}
}

That checks to see if the door should be re-locked (you can of course extend the timer with more code that backs up the start time to cheat this check)

That’s all there is to the exit button and exit relay code.  It can also be called like any other relay from the I/O section, but it operates on its own unlike the others.  This is part of the OpenAccess project.

OpenAccess Keypad functions

July 1, 2016

char keypad_matrix()

{
//Serial.println(“keypad_matrix”);
if(Serial3.available())
{
char result = Serial3.read();//Read key press
return result;
}
else
{
return ‘ ‘;//equivalent to -1, a null result
}
//delay(10);
}

This code polls for keypresses.  If it returns anything then the keypresses are acted on.  Stuff like this is why it is so important to keep the code looping and not blocking.  If there are multiple keypresses buffered this will grab one and leave the rest for next loop.

void enter_pin(char c)
{
//Serial.print(“Pin Button = “);
//Serial.println(reader2);
LCD_disp(“*”);
pin[digit] = c – ‘0’;//stores as numbers rather than characters
digit++;
}

Here is how a pin is incremented, it is one of the few places I do not overwrite the entire LCD line at once.

void backspace_pin()
{
pin[digit] = 15;
//Serial.println(“backspace”);
LCD_displn(” “,1);
LCD_displn(“Pin:”,1);
if(digit >0)
{
digit–;
pin[digit] = 15;
}
for (int i = 0;i<digit;i++)//repopulates asterisks
{
LCD_disp(“*”);
}
//for(int i=0;i<(sizeof(pin)/sizeof(pin[0]));i++)
//{
// Serial.print(pin[i]);
//}
}

Removing a digit is tricky, I have to set it to an impossible to type character and rewrite the line of asterisks because I don’t support arbitrary cursor positioning.

 

void clear_pin()
{
digit = 0;
for(int i=0;i<(sizeof(pin)/sizeof(pin[0]));i++)
{
pin[i] = 15;
}
}

Clearing a pin is easy, set back to unprintable.  These steps are done in the key taking process which is longer and in the main logic code.  This is part of the greater OpenAccess project.

OpenAccess LCD functions

July 1, 2016

Here is the set of variables related to the LCD, the backlight and contrast values are important.  ‘printname’ is not here for the LCD, it actually is used when returning strings over the serial port.  ‘transmit’ is what I use to send commands to the LCD.

//LCD stuff

int back_val = 255;
int cont_val = 90;
String transmit = “”;
String printname = “”;

This is my initializer for the LCD (the LCD is on Serial3), it sets the contrast and backlight value to the values in memory (could use the command bytes) so that whatever state the LCD is in, it gets set up right.

void LCD_init()
{
Serial3.begin(115200);
LCD_clear();

transmit = “CONTRAST;”;
transmit += cont_val;
Serial3.println(transmit);

transmit = “BACKLIGHT;”;
transmit += back_val;
Serial3.println(transmit);
}

These are the commands to display things on the LCD manually (like using the Serial print and println commands).  These are used internal to the code all the time, there are wrappers though that let them get used by the serial port commands.

void LCD_displn(String prints,int line)//displays on a given line, does not emulate CR LF
{
transmit = “DISPLN;”;
transmit += line;
transmit += “;”;
transmit += prints;
Serial3.println(transmit);
}
void LCD_disp(String prints)
{
transmit = “DISP;”;
transmit += prints;
Serial3.println(transmit);
}

These set the backlight and contrast from the serial console on the OpenAccess

// sets contrast
//format: CONTRAST;<analog value, 0-255 or nothing for default>
void SCmd_contrast()
{

transmit = “CONTRAST;”;
char *arg;
//Serial.println(“contrast”);
arg = SCmd.next();
if (arg != NULL)
{
transmit += arg;
}
else
{
transmit += cont_val;
}
Serial3.println(transmit);
}
// sets backlight
//format: BACKLIGHT;<analog value, 0-255 or nothing for default>
void SCmd_backlight()
{

transmit = “BACKLIGHT;”;
char *arg;
//Serial.println(“backlight”);
arg = SCmd.next();
if (arg != NULL)
{
transmit += arg;
}
else
{
transmit += back_val;
}
Serial3.println(transmit);
}

This is my clear command (it is a 2 line LCD)  This is modular so you can fill in your own code to clear whatever display you use:

inline void LCD_clear()
{
LCD_displn(” “,0);
LCD_displn(” “,1);
}

This is how you can manually overwrite the display from the console:

// uses display
//format: DISP;<0=line 1, 1=line 2 (or no argument to clear the display)>;<string (or no argument to clear the line)>
void SCmd_display()
{
char *arg;
arg = SCmd.next();
if (arg != NULL)
{
if (atoi(arg) == 0)
{
arg = SCmd.next();
if (arg != NULL)
{
LCD_displn(arg,0);
}
else
{
LCD_displn(” “,0);
}
}
else if (atoi(arg) == 1)
{
arg = SCmd.next();
if (arg != NULL)
{
LCD_displn(arg,1);
}
else
{
LCD_displn(” “,1);
}
}
}
else
{
LCD_clear();
}
}

 

This runs every loop that nothing interesting is happening and chooses the splash screen:

void LCD_default()
{
LCD_clear();
if(doorbell)
{
LCD_displn(“Welcome: press “,0);
LCD_displn(“\”#\” to enter “,1);
}
else if(doorhold)
{
LCD_displn(“Door held: press”,0);
LCD_displn(“\”#\” to enter “,1);
}
else
{
LCD_displn(“i3 Detroit “,0);
LCD_displn(“Hackerspace “,1);
}
}

That’s my LCD functions, you can see that I compartmentalized it a lot so it is easy to change the protocol or library of your display. This is part of the greater OpenAccess project.

OpenAccess RFID functions

July 1, 2016

Here are the wiegand readers:

(my other comments on wiegand can be found here)

//RFID reader stuff
#include <WIEGAND26.h> // Wiegand 26 reader format library
byte reader1Pins[]={
19,18}; // Reader 1 connected to pins 19,18
byte reader2Pins[]={
2,3}; // Reader2 connected to pins 2,3
long reader1 = 0;
int reader1Count = 0;
long reader2 = 0;
int reader2Count = 0;
const byte reader2buzz = 6; //pin for the buzzer on Reader2
const byte reader2led = 4; //pin for led1 on Reader2
const byte reader2led2 = 5; //pin for led2 on Reader2
const byte reader1buzz = 7; //pin for the buzzer on Reader1
const byte reader1led = 8; //pin for led1 on Reader1
const byte reader1led2 = 17; //pin for led2 on Reader1
WIEGAND26 wiegand26;
//Get the interrupt number of a given digital pin on an Arduino MEGA.
const byte pinToInterrupt[22] = {
-1,-1,0,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5,4,3,2};

This declares all the pins used , it gives a map from pin number to interrupt numbers (I got that from Mike and I like the idea), and the global variables checked by the wiegand functions.  The pins used for the RFID stuff are defined in a function:

void RFID_init()
{
pinMode(reader2buzz, OUTPUT);
pinMode(reader2led, OUTPUT);
pinMode(reader2led2, OUTPUT);
digitalWrite(reader2led, 1); //active low
digitalWrite(reader2led2, 1); //active low
digitalWrite(reader2buzz, 1); //active low
attachInterrupt(pinToInterrupt[reader2Pins[1]], callReader2One, CHANGE);
attachInterrupt(pinToInterrupt[reader2Pins[0]], callReader2Zero, CHANGE);
wiegand26.initReaderTwo();
pinMode(reader1buzz, OUTPUT);
pinMode(reader1led, OUTPUT);
pinMode(reader1led2, OUTPUT);
digitalWrite(reader1led, 1); //active low
digitalWrite(reader1led2, 1); //active low
digitalWrite(reader1buzz, 1); //active low
attachInterrupt(pinToInterrupt[reader1Pins[1]], callReader1One, CHANGE);
attachInterrupt(pinToInterrupt[reader1Pins[0]], callReader1Zero, CHANGE);
wiegand26.initReaderOne();
}

I broke up all the init functions and stuff like that because as I built this code I changed hardware so often that knowing where to edit the code is very valuable.  The RFID readers have timeouts for when to give up after they’ve accepted a card:

//RFID timer stuff
const unsigned long RFID_time = 30000; //timeout for the RFID scan in thousandths of a second
unsigned long RFID_start_time = 0; //used for the count down timer

These two functions that reset some global variables in the case an rfid entry attempt is not successful (garbage collection).

void RFID_reset()
{
clear_pin();
//RFID_start_time = 0; //not needed
tag_read = 0;
digit = 0;
new_tag_entry = 0;
}
void RFID_error_out()
{
can_add = 0; //reset user capabilities
can_doorhold = 0; //reset user capabilities
can_doorbell = 0; //reset user capabilities
RFID_reset();
beep_repeat(2,200,3);//delay timer
LCD_default();
}

The readers can beep, that function is defined here (0 beeps both readers if you didn’t figure it out):

void beep_reader(int reader,int d)//blocking
{
switch(reader){
case 0:
{
digitalWrite(reader2led, 0);
digitalWrite(reader2buzz, 0);
digitalWrite(reader1led, 0);
digitalWrite(reader1buzz, 0);
delay(d);
digitalWrite(reader2led, 1);
digitalWrite(reader2buzz, 1);
digitalWrite(reader1led, 1);
digitalWrite(reader1buzz, 1);
}
case 1:
{
digitalWrite(reader1led, 0);
digitalWrite(reader1buzz, 0);
delay(d);
digitalWrite(reader1led, 1);
digitalWrite(reader1buzz, 1);
}
case 2:
{
digitalWrite(reader2led, 0);
digitalWrite(reader2buzz, 0);
delay(d);
digitalWrite(reader2led, 1);
digitalWrite(reader2buzz, 1);
}
default:
{
}
}
}

That beep code is yet again wrapped up here to add a duration for repeated beeps:

void beep_repeat(int reader,int time,int number)//blocking
{
/*Serial.print(“beep repeat”);
Serial.print(” “);
Serial.print(reader);
Serial.print(” “);
Serial.print(time);
Serial.print(” “);
Serial.println(number);*/
for(int i = 0;i<number;i++)
{
beep_reader(reader,time);
delay(time);
}
}

This is how I call it from the serial port:

// beeps reader<reader number> for <duration> in delay(arg) format
//format: BEEP;<reader number>;<duration>
void SCmd_beep()
{
Serial.println(“beepcommand”);
char *arg;
arg = SCmd.next();
if (arg != NULL)
{
if(atoi(arg) == 1)
{
arg = SCmd.next();
if (arg != NULL)
{
beep_reader(1,atoi(arg));
}
}
else if(atoi(arg) == 2)
{
arg = SCmd.next();
if (arg != NULL)
{
beep_reader(2,atoi(arg));
}
}
else if(atoi(arg) == 0)
{
arg = SCmd.next();
if (arg != NULL)
{
beep_reader(0,atoi(arg));
}
}
}
}

This stuff is at the bottom of the code, it is needed because the wiegand26 library is not written as well as it could be:

void callReader1Zero(){
wiegand26.reader1Zero();
}
void callReader1One(){
wiegand26.reader1One();
}
void callReader2Zero(){
wiegand26.reader2Zero();
}
void callReader2One(){
wiegand26.reader2One();
}

This is part of the greater OpenAccess project.

OpenAccess RTC functions

July 1, 2016

These declares are for the i2c eeprom and realtime clock:

//I2C stuff
#include <Wire.h> // Needed for I2C Connection to the DS1307 date/time chip
#include <DS1307.h> // DS1307 RTC Clock/Date/Time chip library
#include <E24C1024.h> // Needed for the onboard EEPROM
uint8_t second, minute, hour, dayOfWeek, dayOfMonth, month, year; // Global RTC clock variables. Can be set using DS1307.getDate function.
DS1307 ; // RTC Instance

Those are used in their own functions, this one takes commands from the serial and does RTC things with them:

// uses RTC
//format: RTC;<0=write, 1=read>;<second, 0-59 (write only)>;<minute,0-59 (write only)>;<hour, 1-23 (write only)>;<dayOfWeek, 1-7 (write only)>;<dayOfMonth, 1-28/29/30/31 (write only)>;<month, 1-12 (write only)>;<year, 0-99 (write only)>
void SCmd_rtc()
{
char *arg;
//Serial.println(“rtc”);
arg = SCmd.next();
if (arg != NULL)
{
if(atoi(arg))
{
//Serial.println(“read rtc”);
//logDate();
Serial.println(logDate());//should be logging?
}
else
{
//Serial.println(“write rtc”);
arg = SCmd.next();
if (arg != NULL)
{
second=atoi(arg);
}
else
{
//Serial.println(“No second argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
minute=atoi(arg);
}
else
{
//Serial.println(“No minute argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
hour=atoi(arg);
}
else
{
//Serial.println(“No hour argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
dayOfWeek=atoi(arg);
}
else
{
//Serial.println(“No dow argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
dayOfMonth=atoi(arg);
}
else
{
//Serial.println(“No dom argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
month=atoi(arg);
}
else
{
//Serial.println(“No month argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
year=atoi(arg);
}
else
{
//Serial.println(“No year argument”);
}
ds1307.setDateDs1307(second,minute,hour,dayOfWeek,dayOfMonth,month,year);
/* Sets the date/time (needed once at commissioning)
uint8_t second // 0-59
uint8_t minute // 0-59
uint8_t hour // 1-23
uint8_t dayOfWeek // 1-7
uint8_t dayOfMonth // 1-28/29/30/31
uint8_t month // 1-12
uint8_t year // 0-99
*/
}
}
else
{
//Serial.println(“No arguments”);
}
}

This next one returns the date in a print/logable form and is used all over the code to timestamp things:

String logDate()
{
String date;
ds1307.getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
date+=”20″;//hack
date+=year;
date+=”-“;
if(month < 10)
{
date+=”0″;
}
date+=month;
date+=”-“;
if(dayOfMonth < 10)
{
date+=”0″;
}
date+=dayOfMonth;
date+=’T’;
if(hour < 10)
{
date+=”0″;
}
date+=hour;
date+=”:”;
if(minute < 10)
{
date+=”0″;
}
date+=minute;
date+=”:”;
if(second < 10)
{
date+=”0″;
}
date+=second;
date+=” “;
switch(dayOfWeek){
case 1:
{
date+=”SUN”;
break;
}
case 2:
{
date+=”MON”;
break;
}
case 3:
{
date+=”TUE”;
break;
}
case 4:
{
date+=”WED”;
break;
}
case 5:
{
date+=”THU”;
break;
}
case 6:
{
date+=”FRI”;
break;
}
case 7:
{
date+=”SAT”;
break;
}
}
return date;
}

That’s it, that’s what the RTC is for: timestamps on things. This is part of the greater OpenAccess project.

OpenAccess temperature functions

July 1, 2016

The thought behind the temperature sensor was to be able to set the contrast of the LCD based on it, I didn’t know how to put the logic in the uart lcd so I made it externally controllable with the thought to add logic later.  The function to check the temperature can be called by the serial port and reports back that way and to the dot matrix printer.  Here is the temperature sensor setup for checking outside temperature:

// Interface to DS1822 chip, record data to EEPROM
#include <OneWire.h>
OneWire ds(30); // define the pin the interface is on */
float current_temperature;

Here is my function for getting the temperature:

float getTemp()
{
byte data[9];
byte addr[8];
int temp;

while ( !ds.search(addr))
{
//Serial.print(“No more addresses.\n”);
ds.reset_search();
}
ds.reset();
ds.select(addr);
ds.write(0x44,1);
delay(600);
byte present = ds.reset();
ds.select(addr);
ds.write(0xBE);
for (byte i = 0; i < 9; i++)
{
data[i] = ds.read();
}
boolean negative = (data[1] & 1<<5);
data[1] <<=4;
data[1] >>=4;
temp = data[1];
temp <<= 8; /* shift left eight bits */
temp += data[0]; /* add second byte of temperature */
float tempFloat = (float)temp/16.0;
if(negative)
{
tempFloat = -tempFloat;
}
return tempFloat;
}

This temperature is used in the function another wrapper function to get the temperature and log it:

// logs temperature
void check_temp()
{
char temp[8];
current_temperature = getTemp();//stored for calculation of contrast value later
ftoa(temp,current_temperature,2);
//dtostrf(voltage,8,2,volt); should work?
printname = “Temperature outside: “;
printname += temp;

logging(logDate());
logging(” – “);
loggingln(printname);
}

That variable “current_temperature” is used nowhere else and need not be declared globally.  This is part of the greater OpenAccess project.

OpenAccess printer and log functions

July 1, 2016

This is one of many sub-articles to describe the function of my OpenAccess.  I started putting it in one article, but it is really not useful like that.

The dot matrix printer uses these pins:

//dot matrix printer stuff
const int dataPins[] = {
41,40,43,42,45,44,47,46};
const int strobePin = 39;//active low
const int busyPin = 49;//active low

It is initialized here:

void printer_init()
{
for (int i = 0; i < sizeof(dataPins) / sizeof(dataPins[0]); ++i)
pinMode(dataPins[i], OUTPUT);
pinMode(strobePin, OUTPUT);
pinMode(busyPin, INPUT);
digitalWrite(strobePin, HIGH);
}

The printer is actually called in this printing function here, which is blocking:

void printing(char buffer)//blocking? only if BusyPin is ever high?
{
//Write out our buffer
for (int j = 0; j < sizeof(dataPins) / sizeof(dataPins[0]); ++j)
digitalWrite(dataPins[j], (buffer & (1 << j)));
//buffer = 0x00;

//Block and wait for the printer to be ready
while (digitalRead(busyPin));

//Tell the printer to write out the data
digitalWrite(strobePin, LOW);
delay(10);
digitalWrite(strobePin, HIGH);
delay(10);//not needed?
//Reset the data lines
for (int j = 0; j < sizeof(dataPins) / sizeof(dataPins[0]); ++j)
digitalWrite(dataPins[j], LOW);
}

That function is called in these ones that act like serial print() and println() statements:

void printer(String printable)
{
//if (printable.length() != 0)
//{
for(int i=0;i<printable.length();i++)//change to not call .length every time
{
printing(printable[i]);
}
//}
//else {
//Serial.println(“No arguments”);
//}
}
void printerln(String printable)
{
printer(printable);
printing(0x0d);//CR
printing(0x0a);//LF
}

Those functions are called at the same time as serial’s print functions as log functions so the computer and printer can log stuff together:

void logging(String tolog)//wrapper
{
Serial.print(tolog);
printer(tolog);
}
void loggingln(String tolog)//wrapper
{
Serial.println(tolog);
printerln(tolog);
}

Those functions are called absolutely everywhere, very useful for logging things verbosely.

// uses printer
//format: PRINT;<string>
void SCmd_print()
{
char *arg;
//Serial.println(“print”);
arg = SCmd.next();
if (arg != NULL)
{
printerln(arg);
}
}

That function is the serial wrapper that just uses the printer, great for testing or taking manual control of the system.

This is a derivative of the dot matrix printer driver I used before and part of the OpenAccess project.

OpenAccess privlige functions

July 1, 2016

 

#define PRIV_ACCESS 0 //allowed to enter space
#define PRIV_ADDUSER 1 //allowed to add or remove users
#define PRIV_DOORBELL 2 //allowed to set doorbell mode
#define PRIV_DOORHOLD 3 //allowed to set door hold mode

This has to do with the privilege bits, it defines what a given key has the ability to do.  They are read by:

void read_EE_priv(int index)
{
priv = EEPROM1024.read(((index+1) * entry_size) + 31);//keeps command byte unwritten
return;
}

This reads the given index’s privilege bits into the working global memory (yeah, global variables, sue me).  This is called whenever a user is to be put in global memory or checking to see if the user is present:

boolean user_present(int i)
{
read_EE_priv(i);
if(priv != 0)
{
return 1;
}
else
{
return 0;
}
}

Or if the user is allowed to enter the space:

boolean can_enter(int i)
{
read_EE_priv(i);
if((priv & bit(PRIV_ACCESS)) == bit(PRIV_ACCESS))
{
return 1;
}
else
{
return 0;
}
}

This sets bits to be used later:

read_EE_name(i);
read_EE_priv(i);
read_EE_hash(i);
//Serial.print(“win: “);
//serial_user_print(i);
if((priv & bit(PRIV_ADDUSER)) == bit(PRIV_ADDUSER))
{
can_add = 1;
//Serial.println(“can add”);
}
if((priv & bit(PRIV_DOORBELL)) == bit(PRIV_DOORBELL))
{
can_doorbell = 1;
//Serial.println(“can doorbell”);
}
if((priv & bit(PRIV_DOORHOLD)) == bit(PRIV_DOORHOLD))
{
can_doorhold = 1;
//Serial.println(“can door hold”);
}

I could have done these all at once, or I could have done them all separately, but this is how it ended up.  the “can_***” are also global variables, those get set after a user is made active and before those checks are made (to allow the actions restricted by the privilege bits).  The functions it allows/disallows are things that we thought not all users should have:

  • add user: to learn new keys to the system
  • doorbell: put the door in a mode where pressing a button opens the door with no key for parties and such (6 hour default)
  • door hold: means that you do not need to use a key while loading stuff from your car (10 minute default)

Those last two functions are defined here (add user will get its own article):

//doorbell mode stuff
boolean doorbell = 0; //in doorbell mode?
boolean can_doorbell = 0; //can a given user enter doorbell mode?
unsigned long doorbell_start = 0; //used for count down timer
unsigned long doorbell_dur = (1000*60*60*6); //timeout for doorbell in hours (default 6)

 

//door hold mode stuff
boolean doorhold = 0; //in door hold mode?
boolean can_doorhold = 0; //can a given user enter door hold mode?
unsigned long doorhold_start = 0; //used for count down timer
unsigned long doorhold_dur = (1000*60*10); //timeout for door hold in minutes

The way those timeout is here:

if(doorbell) //doorbell timer loop
{
if (current_time – doorbell_start > doorbell_dur)
{
doorbell = 0;
}
}
//Serial.println(“past doorbell”);
if(doorhold) //door hold timer loop
{
if (current_time – doorhold_start > doorhold_dur)
{
doorhold = 0;
}
}
//Serial.println(“past doorhold”);

The way those modes are activated and deactivated:

void exit_doorbell_mode()
{
logging(logDate());
logging(” – “);
loggingln(“exit doorbell mode”);
LCD_clear();
LCD_displn(“doorbell mode”,0);
LCD_displn(“deactivated”,1);
doorbell = 0;
//doorbell_start = 0;
//can_doorbell = 0;
}
void enter_doorbell_mode()
{
logging(logDate());
logging(” – “);
loggingln(“enter doorbell mode”);
LCD_clear();
LCD_displn(“doorbell mode”,0);
LCD_displn(“activated”,1);
doorbell =1;
doorbell_start = current_time;
//can_doorbell = 0;
}
void exit_doorhold_mode()
{
logging(logDate());
logging(” – “);
loggingln(“exit door hold mode”);
LCD_clear();
LCD_displn(“door hold mode”,0);
LCD_displn(“deactivated”,1);
doorhold = 0;
//doorhold_start = 0;
//can_doorhold = 0;
}
void enter_doorhold_mode()
{
logging(logDate());
logging(” – “);
loggingln(“enter door hold mode”);
LCD_clear();
LCD_displn(“door hold mode”,0);
LCD_displn(“activated”,1);
doorhold =1;
doorhold_start = current_time;
//can_doorhold = 0;
}

The evaluation of keypresses to get into and out of those modes will be discussed elsewhere.  This is part of the greater OpenAccess project.

OpenAccess main logic

July 1, 2016

OK, the big one to wrap your head around.  I’ll try to pseudocode it for you

  • initialize all the things
  • log the battery, temp, time, date, reboot status
  • start loop
    • check for serial commands, execute if any
    • set the current time
    • check for an exit button press, open if there is one
    • check battery if needed
    • toggle relays if needed
    • reset doorbell if needed
    • reset door hold if needed
    • if tag read
      • if timeout reached, exit tag reading
      • if keypad button pressed
        • if escape, backspace
        • if enter
          • if we’re reading a new tag, go to that procedure
          • else, go to auth procedure
        • if a number add it to the pin (variable, 4-8 numbers)
    • if button pressed
      • if the we are ‘exiting’ (authed)
        • if the user can hold the door and the door hold button is pressed, toggle that state (exiting other states)
        • if the user can use doorbell mode and the doorbell mode button is pressed, toggle that state (exiting other states)
        • if the user can add a key and the add a key button is pressed, go into that mode
        • if the enter button is pressed and the door is in a doorbell or door hold state
          • open the door
    • if the pin entered is too big, overflow it
    • if the exit relay should be closed, do it
    • if a key has been read, go into that mode
  • restart loop

There’s the main loop, checks basically everything and is written to fall through as fast as possible.

The associated functions are here:

void enter_new_tag_entry_mode()
{
exit_timeout();
logging(logDate());
logging(” – “);
loggingln(“new member attempt”);
LCD_clear();
LCD_displn(“New member:”,0);
LCD_displn(“swipe new tag”,1);
new_tag_entry = 1;
}

This one sets up the code for a new tag (after the existing user with those privliges has swiped and pressed the required key).

void reset_tag_reading(int reader_num)
{
//Serial.print(“tag reader “);
//Serial.println(reader_num,HEX);
if(reader_num == 1)
{
reader1 = reader1 >> 1;//fix for extra zero
tag = reader1;
reader1Count = 0;
reader1 = 0;
}
else if(reader_num == 2)
{
reader2 = reader2 >> 1;//fix for extra zero
tag = reader2;
reader2Count = 0;
reader2 = 0;
}
printname = “”;
printname += tag;
logging(logDate());
logging(” – “);
logging(printname);
loggingln(” tag read”);
LCD_clear();
LCD_displn(“Please Enter”,0);
LCD_displn(“Pin:”,1);
tag_read = 1;
clear_pin();//not needed?
RFID_start_time = current_time;
}

So, this clears the display and starts reading a tag again if the pin has not been completely entered before another tag is swiped.

void new_user_entry()
{
//Serial.print(“Tag = “);
//Serial.println(tag,DEC);
//Serial.print(“Pin = “);
//for(int i=0;i<digit;i++)
//{
// Serial.print(pin[i]);
//}
if (digit<4) //too short of a pin
{
clear_pin();
int d = 300;
LCD_clear();
LCD_displn(“pin too short”,0);
beep_repeat(2,200,2);
reader2 = tag;//hack
reset_tag_reading(2);//hack
RFID_start_time = millis();
}
else
{
String toHash = “”;
toHash += tag;
//put in raw tag to eeprom
for(int i=0;i<(sizeof(pin)/sizeof(pin[0]));i++)
{
toHash += pin[i];
}
clear_pin();
char hashed[32];
toHash.toCharArray(hashed,32);
char *md5str = MD5::make_digest(MD5::make_hash(hashed), 16);
for (int i=0;i<num_users;i++)
{
if(!user_present(i))
{
String name = “New Member “;
name += logDate();
priv= 1;
//put in raw tag to eeprom
write_EE(i, name, priv, md5str);
break;
}
}
//Serial.println(md5str);
logging(logDate());
logging(” – “);
logging(“new user “);
logging(md5str);
loggingln(” added”);
LCD_clear();
LCD_displn(“Welcome”,0);
LCD_displn(“New member”,1);

for(int i=0;i<(sizeof(hashed)/sizeof(hashed[0]));i++) //also should not be needed
{
hashed[i] = 0;
}
new_tag_entry = 0;
toHash = “”; //not needed?
RFID_reset();
exit();
}
}

This code creates a new user entry with a 4 digit minimum pin and hashes the pin and tag together to store that.

void current_user_entry()
{
//possibly add a time based lack of pin requirement, for example
//store a list of user IDs that have no pin entry allowed, reset
//it at midnight (or noon) and check the card id against all of those before hashing, etc…

//Serial.print(“Tag = “);
//Serial.println(tag,DEC);
//Serial.print(“Pin = “);
//for(int i=0;i<digit;i++)
//{
// Serial.print(pin[i]);
//}
String toHash;
toHash += tag;
for(int i=0;i<(sizeof(pin)/sizeof(pin[0]));i++)
{
toHash += pin[i];
}
clear_pin();
char hashed[32];
toHash.toCharArray(hashed,32);
char *md5str = MD5::make_digest(MD5::make_hash(hashed), 16);
if(authenticate(md5str) == 1)
{
logging(logDate());
logging(” – “);
printname = “”;
for (int j = 0; j < (sizeof(name)/sizeof(name[0])); j++)
{
if(name[j] != 0)
{
printname += name[j];
//Serial.println(name[j],HEX);
}
}
logging(printname);
loggingln(” carded in”);
LCD_clear();
LCD_displn(“Welcome Member:”,0);
LCD_displn(printname,1);
exit();
}
else
{
logging(logDate());
logging(” – “);
//logging(md5str);//
//logging(” – “);//
loggingln(“card entry failed”);
LCD_clear();
LCD_displn(“Invalid entry”,0);
RFID_error_out();
}
//Serial.println(md5str);
//Serial.println();
//Serial.println(test);
for(int i=0;i<(sizeof(hashed)/sizeof(hashed[0]));i++)//should not be needed
{
hashed[i] = 0;
}
toHash = “”; //also not needed?
RFID_reset();
}

This function hashes the pin and card id and checks them against the internal list of users, it then allows or disallows entry.

boolean authenticate(char *md5str)
{

byte hard_hash[5][33] = {
{/*”e0bdc8c7b2e2d33e7889858a30aad569″*/
}
,{
}
,{
}
,{
}
,{
}
};/////////5 hashes
boolean succeed = 1;
for (int k=0;k<5;k++)
{
for (int j=0;j<32;j++)
{
if(hard_hash[k][j] != md5str[j])
{
//Serial.print(“fail: “);
//Serial.println(k,DEC);
//Serial.println(j,DEC);
//Serial.println(hash[j],HEX);
//Serial.println((md5str[j]),HEX);
succeed = 0;
j+=32;
}
}
if(succeed)
{
printname = “Failsafe”;
for (int j = 0; j < printname.length(); j++)///////////////////////////////////////////////////////////////////////untested
{
if(printname[j] != 0)
{
name[j] = printname[j];
//Serial.println(name[j],HEX);
}
}

logging(logDate());
logging(” – “);
loggingln(“Failsafe used”);
return 1;
}
}

for (int i=0;i<num_users;i++)
{
if(can_enter(i))
{
read_EE_hash(i);
succeed = 1;
for (int j=0;j<32;j++)
{
if(hash[j] != (md5str[j]))
{
//Serial.print(“fail: “);
//Serial.println(i,DEC);
//Serial.println(j,DEC);
//Serial.println(hash[j],HEX);
//Serial.println((md5str[j]),HEX);
succeed = 0;
break;
}
}
if(succeed == 1)
{
read_EE_name(i);
read_EE_priv(i);
read_EE_hash(i);
//Serial.print(“win: “);
//serial_user_print(i);
if((priv & bit(PRIV_ADDUSER)) == bit(PRIV_ADDUSER))
{
can_add = 1;
//Serial.println(“can add”);
}
if((priv & bit(PRIV_DOORBELL)) == bit(PRIV_DOORBELL))
{
can_doorbell = 1;
//Serial.println(“can doorbell”);
}
if((priv & bit(PRIV_DOORHOLD)) == bit(PRIV_DOORHOLD))
{
can_doorhold = 1;
//Serial.println(“can door hold”);
}
return 1;
}
}
}
return 0;
}

The hash authentication function has an override built in to allow for hardcoded hashes in the program (not the eeprom) which amounts to a firmware level backdoor.  And everyone knows I’m all about that.  This function goes on to check all the spots in memory and report if the user has certain abilities.  When this function is done the ram is populated with the user info of the person authenticated.

That’s the salient parts of the main code.  I hope that’s enough to get some of you started in the OpenAccess project.