// Include Wire Library for I2C
#include <Wire.h>
// Include Adafruit Graphics & OLED libraries
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
const int height = 64; // oled height
const int width = 128; // oled width
// Reset pin not used but needed for library
#define OLED_RESET 3
Adafruit_SSD1306 display(width, height, &Wire, OLED_RESET);
const int buzzerPin = 13;
const int buttonPin = 18;
// student id's
const char wyatt_id[9] = {'1','0','2','9','1','2','0','1','3'};
const char william_id[9] = {'1','0','3','0','6','7','9','9','8'};
const char ziping_id[9] = {'1','0','2','8','7','6','4','2','3'};
unsigned long pressedTime = 0;
unsigned long releasedTime = 0;
int currentState;
int lastState = HIGH;
volatile int state = LOW; // state of the button
bool begin_timer = false; // used for enabling countdown time when button pressed
int seconds = 0; // incremented every second when button clicked
unsigned int ms_ctr = 0; // millisecond counter for timer interrupt
unsigned int ms_ctr2 = 0; // millisecond counter for timer interrupt
String str_id = ""; // string we add to when we click button
int count = -1; // increments for every button click
// williams bitmap image
const unsigned char willsbitmap [] PROGMEM = {
// '299-2994088_free-clipart-of-padlock-unlocked-silhou-lock-and-unlock-symbol, 50x50px
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xf0, 0x7f,
0xff, 0xc0, 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xc7, 0x0f, 0xff, 0xc0,
0xff, 0xff, 0xff, 0x8f, 0xcf, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x9f, 0xc7, 0xff, 0xc0, 0xff, 0xff,
0xff, 0x9f, 0xe7, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x1f, 0xe7, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x1f,
0xe7, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x1f, 0xe7, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x1f, 0xe7, 0xff,
0xc0, 0xff, 0xff, 0xff, 0x1f, 0xe7, 0xff, 0xc0, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xc0, 0xff,
0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00,
0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff,
0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0,
0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8,
0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// store student id in char array
char getWyattID[10];
char getWilliamID[10];
char getZipingID[10];
// store student id in string - easier to check for equivalence
String str_getWyattID;
String str_getWilliamID;
String str_getZipingID;
bool loggedIn_wyatt = false;
bool loggedIn_william = false;
bool loggedIn_ziping = false;
bool timedOut = false;
// Strings and boolean used for enabling the system
String en = "";
String en1 = "EEE20003";
bool systemSignedIn = false;
int pos = 0; // used for indexing in eeprom
int menu; // used to store menu choice
bool selectedMenuItem = false;
const int PIN_TRIG = 3; // Define sensor pin
const int PIN_ECHO = 2;
int distance;
bool saveSensorData = false;
int sec = 0; // used to increment seconds in second timer interrupt
void setup()
{
Serial.begin(115200);
Serial.println();
pinMode(buzzerPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(PIN_TRIG, OUTPUT); // Set up the mode of pin
pinMode(PIN_ECHO, INPUT);
// initialize OLED with I2C addr 0x3C
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
bool writeWyattID = true;
bool writeWilliamID = true;
bool writeZipingID = true;
// if one value in EEPROM where ID's are stored is not equal to 255
// indicates data is present and don't overwrite it
for(int i = 0; i < 9; i++)
{
if(EEPROM.read(i) != 255)
{
writeWyattID = false;
}
}
for(int i = 9; i < 18; i++)
{
if(EEPROM.read(i) != 255)
{
writeWilliamID = false;
}
}
for(int i = 18; i < 27; i++)
{
if(EEPROM.read(i) != 255)
{
writeZipingID = false;
}
}
// if student id's not stores in EEPROM, then write id's into EEPROM
if(writeWyattID)
{
Serial.println("No data for Wyatt");
for(int i = 0; i < 9; i++)
{
EEPROM.write(i, wyatt_id[i]);
}
}
if(writeWilliamID)
{
Serial.println("No data for William");
for(int i = 9; i < 18; i++)
{
EEPROM.write(i, william_id[i-9]);
}
}
if(writeZipingID)
{
Serial.println("No data for Ziping");
for(int i = 18; i < 27; i++)
{
EEPROM.write(i, ziping_id[i-18]);
}
}
// store id's into char array
for(int i = 0; i < 9; i++)
{
getWyattID[i] = EEPROM.read(i);
getWilliamID[i] = EEPROM.read(i+9);
getZipingID[i] = EEPROM.read(i+18);
}
getWyattID[9] = '\0';
getWilliamID[9] = '\0';
getZipingID[9] = '\0';
// store id's into strings - makes it easier to check for equivalence
str_getWyattID = String(getWyattID);
str_getWilliamID = String(getWilliamID);
str_getZipingID = String(getZipingID);
init_timer1_ovf_interrupt(); // initialise timer 1
attachInterrupt(5, readButton, RISING); // attach interrupt to button
cli();
TCCR2B = 0x04; //divide timer1 timebase by 256, overfl. occurs every 4.1 ms
TIMSK2 = 0x01; //enable timer1 overflow interrupt
sei();
}
void loop()
{
if(systemSignedIn == false)
{
enable();
}
if(loggedIn_wyatt == false && loggedIn_william == false && loggedIn_ziping == false) // if no one logged in
{
display.clearDisplay();
PauseSignIn();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(width/2-50,height/2-10);
display.print("Enter Student ID:");
display.setCursor(width/2-30,height/2);
display.print(str_id);
display.setCursor(0, height-10);
display.print("Digit: ");
display.print(String(count));
// if button clicked
if(state)
{
processButton();
begin_timer = true;
state = LOW;
}
if(str_id.length() == 9)
{
// wyatt logs in - display animation
if(str_id == str_getWyattID)
{
display.clearDisplay();
granted_sound();
for(int i = 0; i < 12; i += 3)
{
display.drawRect(i,i,width-(i+i),height-(i+i), 1);
display.display();
delay(300);
}
display_msg("Welcome Wyatt!", 2000);
loggedIn_wyatt = true;
loggedIn_william = false;
loggedIn_ziping = false;
str_id = "";
}
// william logs in - display animation
else if(str_id == str_getWilliamID)
{
display.clearDisplay();
display.drawBitmap(40, -10, willsbitmap, 50, 50, WHITE);
delay(500);
display_msg("Welcome William!", 2000);
loggedIn_wyatt = false;
loggedIn_william = true;
loggedIn_ziping = false;
str_id = "";
}
// ziping logs in - display animation
else if(str_id == str_getZipingID)
{
display.clearDisplay();
granted_sound();
for(int i = 0; i < 12; i += 3)
{
display.drawRect(i,i,width-(i+i),height-(i+i), 1);
display.display();
delay(300);
}
display_msg("Welcome Ziping!", 2000);
loggedIn_wyatt = false;
loggedIn_william = false;
loggedIn_ziping = true;
str_id = "";
}
else
{
accessDenied();
}
}
}
if(loggedIn_wyatt && selectedMenuItem == false)
{
menu = readMenuInput();
chooseMenuOption("Wyatt", 0);
}
if(loggedIn_william && selectedMenuItem == false)
{
menu = readMenuInput();
chooseMenuOption("William", 8);
}
if(loggedIn_ziping && selectedMenuItem == false)
{
menu = readMenuInput();
chooseMenuOption("Ziping", 18);
}
if(menu == 1) // change id
{
display.clearDisplay();
display.setCursor(width/2-50,height/2-10);
display.setTextColor(WHITE);
display.print("Enter New ID:");
display.setCursor(width/2-30,height/2);
display.print(str_id);
display.setCursor(0, height-10);
display.print("Digit: ");
display.print(String(count));
if(state)
{
processButton();
begin_timer = true;
state = LOW;
}
if(str_id.length() == 9)
{
if(str_id == str_getWyattID || str_id == str_getWilliamID || str_id == str_getZipingID)
{
display.clearDisplay();
display.setCursor(width/2-30, height/2 - 10);
display.print("ID Already");
display.setCursor(width/2-20, height/2);
display.print("Taken");
str_id = "";
count = -1;
menu = 1;
display.display();
delay(2000);
}
else
{
char newid[10];
str_id.toCharArray(newid, 10);
for(int i = 0; i < 9; i++)
{
EEPROM.write(i+pos, newid[i]); // general code block - updates anyones id in eeprom
}
if(loggedIn_wyatt)
{
Serial.println("Wyatt's ID Updated.");
for(int i = 0; i < 9; i++)
{
getWyattID[i] = EEPROM.read(i);
}
getWyattID[9] = '\0';
str_getWyattID = String(getWyattID);
Serial.print("New Student ID: ");
Serial.println(str_getWyattID);
}
else if(loggedIn_william)
{
Serial.println("Williams's ID Updated.");
for(int i = 0; i < 9; i++)
{
getWilliamID[i] = EEPROM.read(i+pos);
}
getWilliamID[9] = '\0';
str_getWilliamID = String(getWilliamID);
Serial.print("New Student ID: ");
Serial.println(str_getWilliamID);
}
else if(loggedIn_ziping)
{
Serial.println("Ziping's ID Updated.");
for(int i = 0; i < 9; i++)
{
getZipingID[i] = EEPROM.read(i+pos);
}
getZipingID[9] = '\0';
str_getZipingID = String(getZipingID);
Serial.print("New Student ID: ");
Serial.println(str_getZipingID);
}
selectedMenuItem = false;
str_id = "";
menu = 0;
}
}
}
else if(menu == 2) // write sensor data to eeprom
{
saveSensorData = true;
menu = 0;
selectedMenuItem = false;
}
else if(menu == 3) // logout
{
display.clearDisplay();
display.setTextSize(2);
display.setCursor(width/2-40, height/2-10);
display.print("Goodbye");
display.display();
delay(2000);
loggedIn_wyatt = false;
loggedIn_william = false;
loggedIn_ziping = false;
selectedMenuItem = false;
menu = 0;
saveSensorData = false;
}
display.display();
}
// Function displays a custom message to the screen of the oled
void display_msg(char msg[], long _delay)
{
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(width/2-45,height/2);
display.print(msg);
display.display();
delay(_delay);
display.clearDisplay();
}
// If system password is incorrect, access is denied.
void accessDenied()
{
// error sound
denied_sound();
String access = "Access";
String denied = "Denied";
display.clearDisplay();
display.drawLine(0,0,width, height, 1);
display.drawLine(width, 0, 0, height, 1);
display.setCursor(width/2-(access.length()*2.5), 10);
display.print(access);
display.setCursor(width/2-(access.length()*2.5), height-15);
display.print(denied);
display.display();
delay(2000);
display.clearDisplay();
str_id = "";
}
void denied_sound()
{
tone(buzzerPin, 100);
delay(100);
noTone(buzzerPin);
delay(100);
tone(buzzerPin, 100);
delay(100);
noTone(buzzerPin);
}
void granted_sound()
{
tone(buzzerPin, 800);
delay(100);
noTone(buzzerPin);
}
// Debounces the button and changes the state of it
void readButton()
{
static unsigned int long lastMillis = 0;
unsigned long newMillis = millis();
if(newMillis - lastMillis < 100) // debounce
{
}
else
{
if(!timedOut)
{
lastMillis = newMillis;
state = !state;
}
}
}
// This function increments a counter each time the button is pressed (state flipped)
void processButton()
{
if(Serial.available() == 0)
{
// current state of button
currentState = digitalRead(buttonPin);
if(lastState == HIGH && currentState == LOW) // button is pressed
{
pressedTime = millis();
}
else if(lastState == LOW && currentState == HIGH) // button is released
{
releasedTime = millis();
long pressDuration = releasedTime - pressedTime;
if(pressDuration > 100 && pressDuration < 800)
{
count++;
if(count > 9)
count = -1;
}
}
// save the last state
lastState = currentState;
}
}
// This function sets the properties for the timer 1 overflow properties
void init_timer1_ovf_interrupt(void)
{
TCCR1B = 0x04; //divide timer1 timebase by 256, overfl. occurs every 4.1 ms
TIMSK1 = 0x01; //enable timer1 overflow interrupt
// asm("SEI"); //enable global interrupt
sei();
}
// This ISR begins incrementing a counter when the button is clicked once - resets to 0 after specified time
ISR(TIMER1_OVF_vect)
{
if(begin_timer)
{
ms_ctr = ms_ctr + 1;
if(ms_ctr == 154) // 1000ms
{
ms_ctr = 0; //reset ms counter
seconds = seconds + 1; //increment second counter
if(seconds == 5)
{
if(count == -1)
{
seconds = 0;
}
else
{
str_id += String(count);
seconds = 0;
count = -1;
}
}
}
}
}
// This ISR serves to read the sensor input every second or so.
// If user indicates they want to store sensor data in EEPROM
// then that data is also stored once every second or so.
ISR(TIMER2_OVF_vect)
{
ms_ctr2 = ms_ctr2 + 1;
if(ms_ctr2 == 154) // 1000ms
{
ms_ctr2 = 0; //reset ms counter
sec += 1;
}
if(saveSensorData)
{
if(sec == 5)
{
int dis = readDistance();
EEPROM.put(28,dis); // Save data to EEPROM location 27
int get_data;
EEPROM.get(28, get_data); // Read the 28th position in the EEPROM
Serial.print("Addr_28 = ");
Serial.println(get_data); // Display the read values on the serial monitor
}
}
if(sec == 5)
{
distance = readDistance();
sec = 0;
}
}
// williams code
//////////////////////////////////////////////////////////////
void enable()
{
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(width/2-15,height/2-10);
display.print("Enter");
display.setCursor(width/5,height/2);
display.print("System Enabler: ");
display.display();
while(en == "")
{
en = (Serial.readStringUntil('\n'));
en.trim();
Serial.flush();
}
if (en.equals(en1) == true)
{
systemSignedIn = true;
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(width/4+10,height/2-5);
display.print("Welcome");
display.display();
delay(2000);
display.clearDisplay();
}
else if(en.equals(en1) == false)
{
systemSignedIn = false;
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(width/4-20,height/2-5);
display.print("Operation failed");
display.display();
delay(2000);
en = "";
enable();
}
}
////////////////////////////////////////////////////////
void createMenu()
{
display.setCursor(5, height-40);
display.print("1. Modify Student ID");
display.setCursor(5, height-30);
display.print("2. Store Sensor Data");
display.setCursor(5, height - 20);
display.print("3. Logout");
}
// This function reads the distance value from the ultrasonic sensor
int readDistance()
{
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
// Read the result:
int duration = pulseIn(PIN_ECHO, HIGH);
int dis = duration / 58;
return dis;
}
// This function displays hello on the oled whenever the distance value
// from the ultrasonic sensor is below 200. But only when a student
// is entering their student id.
void PauseSignIn()
{
if(distance > 200)
{
display.setCursor(width/2-20, 5);
display.print("Paused");
timedOut = true;
}
else
{
timedOut = false;
}
}
// This function reads the menu input from the Serial input
int readMenuInput()
{
String getinput = "";
while(Serial.available())
{
if(Serial.available() > 0)
{
char c = Serial.read();
getinput += c;
delay(3);
}
}
int menu = getinput.toInt();
return menu;
}
// This function access a particular menu option and sets the corresponding properties
void chooseMenuOption(char name[], int eeprom_index)
{
if(menu == 1 || menu == 2 || menu == 3)
{
selectedMenuItem = true;
}
else
{
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(10,10);
display.print("Welcome ");
display.print(name);
createMenu();
pos = eeprom_index;
}
}