Introduction: Automatic Medication Dispenser

This project is for use in the medical field, where elderly patients must have a reliable way to have medication portioned and dispensed. This device allows medication to be portioned up to 9 days in advance, and be automatically dispensed at a desired time. The lid is also lockable with the RFID tag, making sure that only the caregiver can access the medication.

Supplies

There are the required materials to construct this project:

  • Arduino UNO
  • Motor Driver Module
  • SG90 9G Servo
  • Stepper Motor
  • DS1302 RTC Module
  • Various jumper wires
  • IIC 1602 LCD
  • 3D printer access
  • Legs such as wooden dowels
  • RFID Module and Tag
  • Two pushbuttons
  • Soldering iron
  • Breadboard
  • Super glue
  • Wood screws
  • Unfinished Wood Box with Hinged Lid
  • Double sided tape

Step 1: Modifying the Box

The box will first have to be modified. There are multiple holes that must be drilled. The first hole will be on the front of the box, where the control panel box is printed. The second hole is in the rear of the box, for the USB cable to pass through. The last hole is on the bottom of the box, where the medication will fall through once dispensed. Lastly, the legs must be attached to the bottom. I used rubber feet I found around my house for legs, but wooden dowels can also be used.

Step 2: 3D Printed Parts

There are many 3D printed parts needed for this project.

They are:

  • Carousel that holds medication
  • Base for carousel
  • Funnel for the medication
  • Arm for servo motor to lock the lid
  • Base for servo motor
  • Latch for servo arm
  • Control panel
  • Cup for the medication to be dispensed into

The base for the carousel is adhered to the box with double sided tape. The base for the servo motor and the latch for the arm are both screwed into the box with short wood screws. The control panel box is glued to the front of the box with super glue, after the components have been inserted.

Step 3: Electronics

The electronics now need to be placed into the box. First, the stepper motor is attached to the carousel base with M3 bolts and nuts. The servo is then super glued to its base. Then, the motor controller, Arduino, breadboard, RFID module, and RTC module are all attached to the box with double sided tape. The LCD is inserted into the hole in the control box. There is some soldering that is required. For the push buttons, the jumper cables must be soldered to the spade connectors. For the RFID reader, the pins must be soldered to the board.

Step 4: Code

Below is the commented code:

Libraries for the Servo, LCD, RTC, RFID, and Stepper motor are included in this code.

///////////////// Libraries and Variables
#include <Wire.h>
#include <Servo.h> //Arduino standard library
#include <LiquidCrystal_I2C.h>
#include <virtuabotixRTC.h>
virtuabotixRTC myRTC(2, 3, 4);
// Define pins
#define servopin 8
const int buttonup = 6;
const int buttondown = 7;
int hr=0;
int minn=0;
int sel=0;
int stateup=0;
int statedown=0;
int statesel=0;
int wait = 0;
int locker = 0;

// Set up servo
Servo servo;
int angle = 180;
#include <StepperAK.h> //use modified stepper library with 1000/0100/0010/0001 magnet firing sequence. Put library in your library folder.
#define gearratio 64 //1:64 gear ratio
const int stepsPerRevolution = 2048;  //the Arduino Kit motor is geared down. By experiment I determined that 2048 steps turn the shaft one round.
int steps = 0;
  
LiquidCrystal_I2C lcd(0x27,16,2);                                    

// instantiate a 4-wire stepper on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, A0,A1,A2,A3);            

 
#include <SPI.h>
#include <MFRC522.h>
 
#define SS_PIN 10
#define RST_PIN 9
MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance.
int deg = 10;
 
void setup() 
{
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  // The line below this is used to set the current time. It only has to be done once, and afterwards the code 
  // must be uploaded again with it commented.
  //myRTC.setDS1302Time(40, 55, 11, 1, 7, 12, 2020);
  pinMode(buttonup, INPUT_PULLUP);
  pinMode(buttondown, INPUT_PULLUP);
  Serial.begin(9600);   // Initiate a serial communication
  SPI.begin();      // Initiate  SPI bus
  mfrc522.PCD_Init();   // Initiate MFRC522
  myStepper.setSpeed(0.15*gearratio);//the motor appears to be geared down 1/64, meaning that the speed needs to be set 64x.
  // initialize the serial port:
  servo.attach(servopin);


}
void loop(){
  ///////////////// LCD Code
  // Constantly updates the display with current time and dispensing time.
  lcd.clear();
  myRTC.updateTime();
  lcd.setCursor(0,0);
  lcd.print("Time:");
  lcd.setCursor(6,0);
  lcd.print(myRTC.hours);
  lcd.print(":");
  lcd.print(myRTC.minutes);
  lcd.setCursor(0,1);
  lcd.print("Dispense:");
  lcd.setCursor(10,1);
  lcd.print(hr);
  lcd.print(":");
  lcd.print(minn);

  
  ///////////////// Read Button States
  // Reads the states of the buttons to change the dispensing time.
  stateup = digitalRead(buttonup);
  statedown = digitalRead(buttondown);
  delay(100);

  
  ///////////////// Dispensing Logic
  // If the current time is the same as the selected dispensing time, turn the stepper motor.
  // Every 9 times the device dispenses, the motor turns an extra distance to ensure a full rotation is made.
  if (myRTC.hours == hr && myRTC.minutes == minn && steps <9){
  myStepper.step(227);
  steps = steps +1;
  delay(60100);
  myRTC.updateTime();
  }
    else if (myRTC.hours == hr && myRTC.minutes == minn && steps ==9){
    myStepper.step(232);
    steps = 0;
    delay(60100);
    myRTC.updateTime();

    
  ///////////////// Changing Dispensing Time
  // Change the dispensing time based on which button is pressed. 
  // Time returns to zero when hours gets to 24 and minutes gets to 60.
  }
  if (stateup == LOW && hr<23){
  hr=hr+1;
  delay(50);
  
  }
  else if (stateup == LOW && hr ==23){
    hr=0;
    delay(50);
  }
  if (statedown == LOW && minn <59){
    minn=minn+1;
    delay(50);
  }
  else if (statedown == LOW && minn ==59){
    minn=0;
    delay(50);
  }
 
  
  ///////////////// RFID Code
  // Reads RFID tag when it is presented.
  if ( ! mfrc522.PICC_IsNewCardPresent()) 
  {
    return;
  }
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) 
  {
    return;
  }

  String content= "";
  byte letter;
  for (byte i = 0; i < mfrc522.uid.size; i++) 
  {
     //Serial.println(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
     //Serial.println(mfrc522.uid.uidByte[i], HEX);
     content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
     content.concat(String(mfrc522.uid.uidByte[i], HEX));
     locker = 1;
  }
  content.toUpperCase();

  
  ///////////////// LOCK CODE
  // When the correct RFID tag is read, move servo to open position when it is closed, 
  // and move servo to closed position when it is open.
  while (locker == 1){
  if (content.substring(1) == "3B 21 D6 22"){ //change here the UID of the card/cards that you want to give access
  {
    switch (deg){
     case 180:
      servo.write(deg);
      deg = 10;
      locker = 0;
      Serial.print("moving");
      delay(1000);
      break;
     case 10:
      servo.write(deg);
      deg = 180;
      locker=0;
      delay(1000);
      break;
    }
  }  

    }
  

 else   {
    Serial.println(" Access denied");
    delay(1000);
  }
  }

  

} <br>

Step 5: Final Set Up

The last step is to ready the project for use. First upload the code with the time setting line uncommented, to upload the current time to the RTC. Then comment the code out, and reupload the code. This will ensure that if the device is unplugged, it will still retain the correct time. Now all you have to do is place the medication in the slots, place the cup under the dispensing hole, and set a dispensing time. The device will reliably dispense at the same time every day.