Thursday, July 28, 2011

Pill Reminder

Remembering to take a vitamin daily is simple enough. Remember to take a vitamin every three days is nearly impossible (for me). I wanted a solution which will remind me to take the pill and require zero effort. This small project holds two bottles in a fairly nice looking box and flashes red until you take the pill. The act of picking up the bottle (the pill ingestion is assumed) is the entire interface.


I had a prototype which worked great for several months but I decided to simplify the design. The prototype was a Arduino UNO, real time clock, breadboard, battery backup. It was smart enough to remember the state between power outages, but that doesn't happen often enough to warrant the added complexity in hardware and code. Notice the entire thing was built without turning on a soldering iron...


The build itself is fairly straight forward. Here is the parts list:
  • 2 picture frames from Target
  • Prototino (quickly becoming my favorite clone)
  • 5 LEDs
  • 2 photoresistors
  • 7 resistors (current limiting for LEDs and for the sensor voltage divider)
  • 1 2.1mm barrel jack
  • Miscellaneous: Wire, glue, screws, foam, velcro, etc.
The frames are the ideal size to comfortably hold two bottles. The glass has been removed and replaced by the cardboard backing with two circles drilled out. The opening is then covered with colored foam from a craft store. The frame on the right is the bottom (you can see the rubber feet).

Holes for the 3 LEDs where drilled and the bottle sensor were built. The sensor is simply a LED pointed at a a photoresistor (voltage divider) shrouded in a black plastic tube. It works great with the analog input of the Arduino. The power jack has been installed in the back left of the bottom frame below.  Ignore the AA battery box, I couldn't remove it easily because of glue from the prototype.

In the updated design, everything is mounted on the underside of the lid (top frame).  Here are snapshots from the build.








The Prototino from SpikenzieLabs is perfect. There is no USB which saves a few bucks but it still uses a crystal so time is kept fairly well. It turns out the crystal isn't really all that necessary.



That is the finished project. It looks nice enough to sit in the dining room where the flashing red LEDs are noticed. Normally (as in not time to take a pill), none of the red notify LEDs are flashing. It looks exactly as above. When it is time to take a pill, the red LED will flash rapidly. When a bottle is picked up (assuming the LED is flashing), the LED becomes solid and the other notify led will go out (even if it was flashing). The goal is to prevent the user from picking up both bottles and putting them back in the wrong order.

Writing the code for this project is really the hard part.

I wrote the code with three states in mind.
  1. Not time to take a pill (nothing flashing).
  2. Time to take a pill, bottle present (red notify LED flashing, sensor LED is on)
  3. Time to take a pill, bottle missing (red notify LED solid, other notify LED off, sensor LED on)
I used the "time" library to keep track of seconds. It starts at 1 and counts forever. Normally, the unix epoch time would be updated from an external source (like a real time clock or GPS), but I don't need that. The timing is actually very simple. The code (today hard coded) contains the number of seconds between when the pills should be taken. In this example, it is every 3 days for the left bottle and  every 1 day for the right bottle. These pills are typically taken around dinner time. But, I didn't want the delay to be exact. If I didn't take the pill until 11 at night, I still wanted the led flashing the next day in time for dinner. I decided a 6 hour grace period is about right. This means that if you take the pill at 7 at night, it will start flashing the next day at 1 in the afternoon.  And, if you took it at 11 at night, it will be flashing 5 which is still in time for dinner. Sure, it isn't perfect. It is very simple. Everything resets back to zero (as if the pills were both just taken) when the power is disconnected. I would rather miss a dose than take too much.  A little bit of debounce code was required because sometimes (as the bottle was removed) it would think it was removed and immediately replaced. The delay() function is not used.
/*

Two Pill Bottles
Ryan Blace
July 2011

*/

#include <Time.h>  

const int BottleCount = 2;
const int PowerLed = 9;
const int SensorLed[] = {10,11};
const int SensorRec[] = {A0,A1};
const int NotifyLed[] = {12,13};
const unsigned long RxSeconds[] = {86400, 259200};  //how many seconds to delay after a pill has been taken (86400 is a day, 259200 is 3 days)
const unsigned long GracePeriod = 21600; // If the bottle is put back at 10pm, it will start flashing the next day at 4pm (21600 is 6 hours)
const int SensorThreshold = 750;
const int FlashFrequency = 150;

unsigned long LastFlash[] = {0,0};
boolean NotifyLedState[] = {HIGH, HIGH};
unsigned long BottleLastTaken[] = {0,0};
int CurrentState[] = {0,0};
int BottlePosition;
int Debounce;

// 0->not time to take a pill (led off)
// 1->time to take pill, bottle not moved (led flashing, sensor led on)
// 2->time to take pill, bottle missing (led solid, sensor led on)

void setup(void) {
  pinMode(PowerLed, OUTPUT);
  for (BottlePosition = 0; BottlePosition < BottleCount; BottlePosition++){
    pinMode(SensorLed[BottlePosition], OUTPUT);
    pinMode(SensorRec[BottlePosition], INPUT);
    pinMode(NotifyLed[BottlePosition], OUTPUT); 
    digitalWrite(NotifyLed[BottlePosition], NotifyLedState[BottlePosition]);
    digitalWrite(SensorLed[BottlePosition], HIGH);
  }
}  

void loop(void) {
  for (BottlePosition = 0; BottlePosition < BottleCount; BottlePosition++){
    switch (CurrentState[BottlePosition]) {
    case 0:
      if (now() > BottleLastTaken[BottlePosition] + (RxSeconds[BottlePosition] - GracePeriod)){
        digitalWrite(SensorLed[BottlePosition], LOW);    
        CurrentState[BottlePosition] = 1;
        LastFlash[BottlePosition] = millis();  
      }
      break;
    case 1:
      if (millis() - LastFlash[BottlePosition] > FlashFrequency){    // flash the led
        LastFlash[BottlePosition] = millis();
        NotifyLedState[BottlePosition] = !NotifyLedState[BottlePosition];
        digitalWrite(NotifyLed[BottlePosition], NotifyLedState[BottlePosition]);
      }
      (analogRead(SensorRec[BottlePosition]) > SensorThreshold) ? Debounce++ : Debounce = 0;
      if (Debounce > 50) {  // Bottle is missing
        for (int x = 0; x < BottleCount; x++){  // turn off all the notify leds
          digitalWrite(NotifyLed[x], HIGH);
        }
        CurrentState[BottlePosition] = 2;
        digitalWrite(NotifyLed[BottlePosition], LOW);
        while (analogRead(SensorRec[BottlePosition]) > SensorThreshold) {
        } // pause everything while a bottle is missing
      }
      break;
    case 2:
      if (analogRead(SensorRec[BottlePosition]) < SensorThreshold) {  // Bottle is back!
        CurrentState[BottlePosition] = 3;
        digitalWrite(SensorLed[BottlePosition], HIGH);
        digitalWrite(NotifyLed[BottlePosition], HIGH);
        BottleLastTaken[BottlePosition] = now();
        CurrentState[BottlePosition] = 0;
      }
    }  
  }
}

Future improvement include:

  • Figure out an easy way to program when to take the pills. (DIP switches, USB, Mind Reader)
  • Maybe teach it to always make the bottles start to flash together (if they are within a day of each other)
  • Add the RTC back and get into sub 24 hour schedules (three times a day, etc)
  • Add an audible notification
  • Add more bottles
  • Add a display (TAKE WITH FOOD flashing when picking up a bottle with that warning)
  • Some sort of weight sensor to tell when a bottle is getting close to empty
  • Automatically reorder medication via twitter
  • Teach it to make breakfast and hide your medication in the pancakes
Thanks.

1 comment:

  1. nice project..can you help me to do pill counter with timer??
    thanks..

    ReplyDelete