Project showcase
Task Manager

Task Manager

Make your kids help with your house chores! A scoreboard with all home chores for them to log using RFID cards! Lots of fun!

  • 997 views
  • 0 comments
  • 6 respects

Components and supplies

Necessary tools and machines

Apps and online services

About this project

In order to make my kids participate at home, doing chores and helping my pregnant wife while I am absent, I decided to find a way to get them motivated. This task manager allows user to store the chores done at home and give them points that are summed up and shown on a specific screen.

Using an RFID receiver and cards each user can store daily his/her chores by selecting it at the validate screen and touching their cards against the box.

I am using an RTC in order to upgrade the system and give the date and time of when the tasks were completed. (Implemented but not in use).

Feel free to upgrade and expand it!!

Have fun!

Code and schematics are provided. Libraries used are stated in the code. Remember to have them installed. Due to compatibility issues with newer versions of some of those libraries I am specifying which versions I have used for this project.

Code

Arduino CodeArduino
Soul of your project!
/* 
 *  Working on Mega2560 R3.
 *  Mega AVR (Wire library) - v1.8.1
 *  Adafruit_SSD1306 by Adafruit - v1.2.9
 *  Mega AVR (EEPROM) - v1.8.1
 *  Encoder by Paul Stoffregen - v1.4.1
 *  DS1307RTC by Michael Margolis - v1.4.0
 *  MFRC522 by GithubCommunity - v1.4.4
 *  
 *  PINOUT
   *  OLED
   *    GND - GND
   *    VCC - 5V
   *    SCL - SCL (21)
   *    SDA - SDA (20)
   *  RTC
   *    GND - GND
   *    VCC - 5V
   *    SCL - SCL (21)
   *    SDA - SDA (20)
   *  ENCODER
   *    GND - GND
   *     +  - 5V
   *    CLK - 2
   *    DT  - 3
   *  RFID
   *    3.3V - 3.3V
   *    RST  - 9
   *    GND  - GND (different from the others)
   *    MISO - 50
   *    MOSI - 51
   *    SCK  - 52
   *    SDA  - 53
 *    
*/
//**************************************** INCLUDE ****************************************
#include <Wire.h>                                      // WIRE library - used for chosing pins for the encoder.
#include <Adafruit_SSD1306.h>                          // OLED library
#include <EEPROM.h>                                    // Current state of tasks is persisted by saving/restoring to EEPROM memory
#include <Encoder.h>                                   // Encoder library
#include <DS1307RTC.h>                                 // Used to retrieve time information from Real-Time Clock (RTC) module https://github.com/PaulStoffregen/DS1307RTC
#include <MFRC522.h>                                   // RFID reader library

//**************************************** PIN SET ****************************************
Adafruit_SSD1306 display (128, 64, & Wire, -1);        // Resolution 128x64, -1 = no reset pin.(SDA - 20(SDA) SCL - 21(SCL))
Encoder myEnc(2, 3);                                   // Comments on Encoder_Mega_Test. Pins CLK - 2, DT - 3.
MFRC522 mfrc522(53, 9);                                // Create an instance of the MFRC522 class. The parameters specified in the constructor are (SS pin, Reset pin)

//**************************************** INTEGERS, BYTE, LONG ****************************************
byte button;                                           // for use of the button
byte arrowpos = 0;                                     // Arrow position - used to save the arrow position to be used in different screens
long oldPosition  = -999;                              // Initialize the encoder position.
int menuIndex = 0;                                     // 0 means it will start on main menu. It will be used to track which submenu you want to go in.
int listItems = 2;                                     // Number of index on *listsubMenu - change to fit the size of your array
const char ver[6]= "V0.1";                             // Version number - To help you keep track of your project
int userID2;
int selectedTask = 0;
byte userTemp;

//**************************************** MENULISTSCHARS ****************************************
char *score[] = {"1 Place:", "2 Place:", "3 Place:", "4 Place:", "5 Place:"};
char *listMenu[] = {"Validate Tasks", "Scoredboard"}; // Names of your menus used on main menu.

//**************************************** STRUCTURES ****************************************
struct task {                                          // This data structure encapsulates all the properties associated with an individual task
  char taskName[16];                                   // The short, "friendly" name for this task as will appear on the display
  int repeatEachXDays;                                 // Regularity, in days, with which this task is repeated. 1=Daily, 7=Weekly etc.
  unsigned long lastCompletedTime;                     // Timestamp at which this task was last completed
  int lastCompletedBy;                                 // ID of the person who last completed this task
};

struct user {                                          // This data structure represents a user
  int userID;                                          // Unique ID - matches that stored in the "lastCompletedBy" field of a task
  char userName[8];                                    // Display name of this user
  byte rfidUID[4];                                     // Unique identifier of the RFID tag this user uses 
  int score;
};

int struct_cmp_by_score(const void *a, const void *b) { 
    struct user *ia = (struct user *)a;
    struct user *ib = (struct user *)b;
    return (int)(ia->score - ib->score);               // float comparison: returns negative if b > a and positive if a > b. 
}; 

//**************************************** CONSTANTS ****************************************
const byte eepromSignature = 2;                       // This byte defines whether the data stored on EEPROM is valid. Valid values range from 0-254 and reflect the version of the dataset - it should be changed every time the structure of the underlying data is updated (tasks added/removed etc.)
const int numTasks = 12;                              // The total number of different tasks available to perform - change to match the amount of tasks
const int numUsers = 5;                               // The total number of users known to the system

//**************************************** GLOBALS ****************************************
user userList[numUsers] = {                           // The dictionary of users
  { 0, "      ", {0x12, 0x34, 0x56, 0x78}, 0},        // Dummy user! Don't change
  { 1, "User1 ", {0xC2, 0x52, 0xD3, 0x04}, 0},        // Change to your card!
  { 2, "User2 ", {0x40, 0x7B, 0x79, 0x19}, 0},        // Change to your card!
  { 3, "User3 ", {0x04, 0x03, 0x96, 0x92}, 0},        // Change to your card!
  { 4, "User4 ", {0x3D, 0x3F, 0x77, 0x62}, 0},        // Change to your card!     
};

task taskList[numTasks] = {                           // The taskList is an array of all the task information. 
  { "Task1          ", 7, "0", 0 },                   // Tasks are initialised with a lastCompletedTime=0 and lastCompletedBy=-1, 
  { "Task2          ", 3, "0", 0 },                   // but these values will be overwritted with data from EEPROM if valid stored data exists. 
  { "Task3          ", 3, "0", 0 },                   // If this list is changed, be sure to also update the EEPROM signature value above 
  { "Task4          ", 3, "0", 0 },                   // otherwise the program will attempt to restore mismatched stored data from EEPROM.   
  { "Task5          ", 1, "0", 0 }, 
  { "Task6          ", 1, "0", 0 },
  { "Task7          ", 3, "0", 0 }, 
  { "Task8          ", 7, "0", 0 },
  { "Task9          ", 1, "0", 0 },
  { "Task10         ", 30, "0",0 },
  { "Task11         ", 7, "0", 0 },
  { "Task12         ", 7, "0", 0 },  
};

//**************************************** NORMALIZE ****************************************
long normalize(long value, long radix) {               // to wrap your encoder count around your amount of submenus.
  long rval = value % radix;
  while(rval < 0) { 
    rval += abs(radix); 
  } 
  return rval;
  
};

//**************************************** COMPLETETASK ****************************************
void completeTask(int taskId, unsigned long completedTime, int completedBy) { // Completes the provided task id 
  taskList[taskId].lastCompletedTime = completedTime;
  taskList[taskId].lastCompletedBy = completedBy;
}

//**************************************** SETUP ****************************************
void setup() {
  Serial.begin(9600);                                  // Starting serial for debbuging purposes.
  
  Serial.print(F("Loading OLED..."));                  // Initializing OLED
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.display();
  display.clearDisplay();
  Serial.println(F("done."));

  Serial.print(F("Loading saved data..."));            // Load the stored data
  restoreFromEEPROM();
  Serial.println(F("done."));  
  delay(100);

  tmElements_t tm;                                     // Initializing RTC
  if(RTC.read(tm)) {
    Serial.print(F("Initializng RTC... "));
    char timeString[16];
    memset(timeString, 0, sizeof timeString);
    snprintf(timeString, 16, "%i/%i/%i %i:%02i", tm.Day, tm.Month, 1970+tm.Year-2000, tm.Hour, tm.Minute);
    Serial.println(timeString);
  }
  else {
    if(RTC.chipPresent()) {
      Serial.println("FAILED. The DS1307 is not initialised. Please run the SetTime sketch to set correct time.");
    } 
    else {
      Serial.println("FAILED. Cannot connect to DS1307. Please check your wiring.");
    }
  }

  Serial.print(F("Initialising SPI Communication...")); // Initiate RFID
  SPI.begin();                                          // Initiate SPI bus. Wire library.
  Serial.println(F("done."));
    
  Serial.print(F("Initialising RFID reader..."));       // Initiate RFID
  mfrc522.PCD_Init();
  Serial.println(F("done."));
  
}

//**************************************** LOOP ****************************************
void loop() {

  switch (menuIndex)  {
    case 0: 
      mainMenu();                                      // Main menu
      selectEncoder();
      selectButton();
      break;
    case 1: 
      validateTaskMenu();                              // Screen to validate tasks with RFID badges
      selectEncoder();
      selectButton();
      break;
    case 2:
      resultsTask();                                   // Screen to show users scores
      selectEncoder();
      selectButton();
      break;
  }
  display.clearDisplay();
  delay(150);
}

//**************************************** MAIN MENU ****************************************
void mainMenu() {                                      // Case 0: mainMenu

  listItems =2;
  
  display.setTextSize(2);                              // Set text size of Main Menu to 2
  display.setTextColor(WHITE);                         // To make the text show up on the screen.

  display.setCursor(30, 0);                             // Screen title position
  display.print("Menu");                               // Screen title
  display.setTextSize(1);                              // Set text size of Version and subMenus to 1
  display.setCursor(100, 56);                          // Program version position
  display.println(ver);                                // Program version
//-----------------------------------------------------// Above line static info. Below line variable info.

  for (int i = 1; i <= 2; i++){                        // Loop to show all Menus
    display.setCursor(10, (i*10)+6);
    display.println(listMenu[i-1]);                    // -1 to reduce the arrowpos() number to 4
  }
  if (arrowpos == 2) {                                 // Loop to position arrow in front of Menus
    display.setCursor(2, 16);                                   
    display.println(">");
  } else {
  display.setCursor(2, (arrowpos * 10) + 16);          // Arrow will move up/down on screen.
  display.println(">");
  }
  display.display();
  
}

//**************************************** VALIDATETASKMENU ****************************************
void validateTaskMenu() {                              // Case 1: validateTasks
  listItems = 12;
  arrowpos=2;

  display.clearDisplay();

  display.setTextSize(2);    
  display.setCursor(22, 0);
  display.print("Chores");
//-----------------------------------------------------// Above line static info. Below line variable info.
  display.setCursor(0, 16);
  display.setTextSize(1);
  display.setTextColor(BLACK, WHITE);                  // Draw 'inverse' text 
  display.print("<  ");
  display.setCursor(18, 16);
  display.print(taskList[selectedTask].taskName);
  display.setCursor(108, 16);
  display.println("  >");
  display.setCursor(0, 26);
  display.setTextColor(WHITE);  
  display.println("Approximate ID:");
  char timeString1[16];
  while (! mfrc522.PICC_ReadCardSerial() && mfrc522.PICC_IsNewCardPresent()) {
//    Serial.print("UID tag : ");                        // Show UID on serial monitor
    int userID = GetUserFromRFIDTag(mfrc522.uid.uidByte); // Get ID from RFID card
    if(userID != -1){
      tmElements_t tm;
      if(RTC.read(tm)) {
        memset(timeString1, 16, sizeof timeString1);
        snprintf(timeString1, 16, "%i.%i.%i %i:%02i", tm.Day, tm.Month, 1970+tm.Year-2000, tm.Hour, tm.Minute);
        time_t theTime = makeTime(tm);
        completeTask(selectedTask, theTime, userID);
//        Serial.println(userList[userID].userName);
//        Serial.println(userID);
        if (digitalRead(button) == HIGH){
          if (userID != userID2){
            userList[userID].score = userList[userID].score+1;
            userID2 = userID;
            saveToEEPROM(); 
            break;
          }
        }
      }
    }
  }

  display.setCursor(30, 36);
  display.setTextSize(2);
  display.println(userList[userID2].userName);

  display.display();

}

//**************************************** RESULTSTASK ****************************************
void resultsTask() {                                   // Case 4: resultsTask
  arrowpos = 2;

  sortScoreList();
  
  display.clearDisplay();

  display.setCursor(0, 0);
  display.println("Scoreboard:");
//-----------------------------------------------------// Above line static info. Below line variable info.
  
  for(int i=0; i<numUsers; i++) {
    display.setCursor(10, (i*10)+16);
    display.print(score[i]);
    display.setCursor(60, ((numUsers-i)*10)+6);        // To print in highest-lowest score.
    display.print(userList[i].userName);
    display.print(" - ");
    display.println(userList[i].score);
  }
  
  display.display();

}

//**************************************** RFIDGET ****************************************
int GetUserFromRFIDTag(byte RFID[]){
  // Loop over all the users
  for(int i=0; i<numUsers; i++) {
    if(memcmp(userList[i].rfidUID, RFID, sizeof userList[i].rfidUID) == 0) {
      return userList[i].userID;
    }
  }

//  Serial.print(F("Unknown RFID card detected: "));
  for(byte i=0; i<4; i++) {
//    Serial.print(RFID[i]<0x10 ? " 0" : " ");
//    Serial.print(RFID[i], HEX);
  }
  
}

//**************************************** SELECTENCODER ****************************************
void selectEncoder() {
  
//  Serial.print("Arrowpos: ");
//  Serial.println(arrowpos);
//  Serial.print("menuIndex: ");
//  Serial.println(menuIndex);
  Serial.print("Button: ");
  Serial.println(button);
  
  long encoderCount = myEnc.read()>>2;
  encoderCount = normalize(encoderCount, listItems);   // Number of lines to display affects the encoder count
  if (encoderCount != oldPosition) {
    oldPosition = encoderCount;
    arrowpos = encoderCount;                           // Positions the arrow on each menu
//    Serial.print("Encoder: ");
//    Serial.println(encoderCount);
    selectedTask = encoderCount;
  }
  
}

//**************************************** SELECTBUTTON ****************************************
void selectButton() {
  button = digitalRead(4);                            // Select task button

  if (menuIndex == 2) {                               // Menu2()
    if (button == LOW && arrowpos == 2) {             // Goes back to mainMenu()
      menuIndex = 0;   
    }
  }
  if (menuIndex == 1) {                               // Menu1()
    if (button == LOW && arrowpos == 2) {             // Goes to subMenu1()
      menuIndex = 0;   
    }
  }
  if (menuIndex == 0) {                               // mainMenu()
    if (button == LOW && arrowpos == 0) {             // Goes to Menu1()
      menuIndex = 1;
    }
    if (button == LOW && arrowpos == 1) {             // Goes to Menu2()
      menuIndex = 2;  
    }
    if (button == LOW && arrowpos == 2) {             // Goes back to mainMenu()
      menuIndex = 0;  
    }
  }

}

//**************************************** SORTSCORELIST ****************************************
void sortScoreList(void) { 
    qsort(userList, numUsers, sizeof(struct user), struct_cmp_by_score); // sort array using qsort functions

} 

//**************************************** SAVETOEEPROM ****************************************
void saveToEEPROM() {
  // First, overwrite the first byte of EEPROM as a checkbyte
  EEPROM.write(0, 255);
  // Write the tasklist to EEPROM, starting at the second byte
  EEPROM.put(1, userList);
//  Serial.println("Task list saved to EEPROM!");
  // Now restore the checkbyte
  EEPROM.write(0,eepromSignature);
}

//**************************************** RESTOREFROMEEPROM **************************************** 
void restoreFromEEPROM() {                        //Restores the saved state of items in the tasklist from EEPROM
  // Read the value of the checkbyte
  int checkByte = EEPROM.read(0);
  // If the signature matches the current version number
  if(checkByte == eepromSignature) {
    // Restore the tasklist
    EEPROM.get(1, userList);
  }
}

Schematics

Schematic
Taskmanager bb paphyy1t1d

Comments

Similar projects you might like

Microscope Manager

Project showcase by Team Borislav Chakarov

  • 4,189 views
  • 1 comment
  • 11 respects

GPS Tracking System for Vehicles

Project showcase by Sustainable Living Lab

  • 15,278 views
  • 3 comments
  • 58 respects

Integrated Solar ChargeController, Inverter, PowerBank, Lamp

Project tutorial by Shahariar

  • 11,565 views
  • 26 comments
  • 42 respects

Sorting Resistors the Lazy Way

Project tutorial by Federico Vivaldi

  • 6,798 views
  • 17 comments
  • 49 respects

Amazing 6WD Off-Road Robot | Arduino RC Robot

Project tutorial by Jithin Sanal

  • 5,944 views
  • 1 comment
  • 46 respects

Play with Fire Over Wi-Fi (ESP8266, NeoPixels & Android App)

Project tutorial by ElectroPeak

  • 5,554 views
  • 5 comments
  • 36 respects
Add projectSign up / Login