Introduction: Did We Feed the Pets? Take Our Medicine?

This is a drop-dead simple project to solve a problem that most probably don't have: Making sure the pets are fed (or any other routine things), and not under/double fed, when multiple people are responsible, and are doing so on different schedules.This could easily be modified to 3, 4, etc. per day.

My bride would leave me paper notes, or nothing, and the cats & dogs always looked hungry to me. Did they eat? Well, they'd scarf down a second breakfast with no complaints, so how am I to know? This could be any responsibility multiple people try to make sure happens. One practical use I thought of was taking medicine.

So I made an over-engineered portable device that would tell you if they had been fed that morning/afternoon, allowing whoever did so to indicate it for the next person to check. Our Post-It consumption is bound to decrease.

If you want to do it the way I did, you need:

  1. A medicine bottle big enough to hold the battery you choose. I chose the 18650. The project now takes 0.13µA when sleeping (with 1MHz Internal clock), so you can use anything you like and it will still last! It takes around 4-5mA while checking or switching. The timer in code (waitToSleepMillis) allows this for 5 seconds before going to sleep.
  2. A holder for #1.
  3. An ATTiny45 (or any µC that has 4 GPIO pins). Note that I have "sleep" routines here specifically for the AVR line. If you choose another line, you'll have to modify the sleep() sub for your platform.
  4. A way to burn the ATTiny using "Arduino as ISP". There are thousands of tutorials on either doing this ad-hoc or building a burner out there on the Inter-Webs. My video on making a permanent one is here.
  5. 2x LEDs of different colors (one for AM, one for PM). I used 5mm because I have hundreds.
  6. 2x 330 Ohm resistors, or whatever suits your LEDs. Mind you, I'm driving the uC with less than 5Vdc. On my rig, the resistors above dragged the voltage to the LEDs down by ~half.
  7. 2x momentary SPST switches.
  8. I used a tiny scrap of Perf Board just big enough for the uC.
  9. Glue Gun to secure components to the medicine bottle lid, and to keep the exposed resistor leads separated.
  10. uh... Wires & Solder? :)

Step 1: Starting With the Board for the Microcontroller

I took the scrap mentioned above, and put not just an 8-pin socket on it, but 2 rows of header pins (2 x 4). As you can see from the pic above, there is a 1:1 relationship in the wiring. So top right pin of uC (Vcc) is wired to the top right header pin, etc.

The rationale is this: There is very little pin sticking through the board from a socket. The pins are much shorter than the chip's. By wiring each pin to a header pin, I only used a single wire, and the header pins could handle more than one wire soldered to them easily. I just wanted things clean & simple, as this won't be winning any awards.

In addition, back in soldering classes during my Vo-Tech years, when cavemen rode dinosaurs, we were taught to always create a strong mechanical connection before soldering-never depending on the solder for connectivity. If we did that, or cold/incomplete solder joints, we had to take them apart the hard way and do it again correctly. I cringe when I see the vast majority of videos out there where 2 wires are joined by holding them near each other and tossing a blob of solder on them. I cringe, but at the voltages/capacitance we're talking about for these videos, it hardly matters. And honestly, I'm really glad that there are videos of any kind with tinkering out there to inspire the 'throw away' generation.

That said, when you put the 'short' end of the pin headers through the perf board, it leaves the longer end(s) sticking up, making room for all the mechanical connections needed.

NOTE: I did not wire every one of the 8 possible pins from the socket to the headers. Rather, only the ones needed.

Step 2: The Container (medicine Bottle), Switches & LEDs

As you can see, the lid is a decent size. I didn't want it (the project) to get lost. I drilled 4 imperfect holes by eye in the top. The one in the center would be the "They Ate!" selector, the 2 "AM" & "PM" LEDs are next to each other at the top, and the 2nd switch, which would be labeled in Sharpie "Check".

The concept here is that I couldn't see the need to have the status LED ("AM" or "PM") lit 24/7. Why waste battery? So the "Check" button simply connects the GND to the LEDs, with the uC deciding which has Vcc sent to it.

I gave up on making 2 tiny holes for the switches, and just reamed out a single hole for each with reckless abandon. I put a glob of glue in the hole, pressed each switch in, and waited for it to cool. Very scientific.

A glue gun (the Duct Tape of Small Projects) helped me secure all 4 to the lid. I later used glue for things like the pin/wire areas on the board to avoid shorts while changing battery, and putting a drop of glue on the metal 'rivets' on the ends of the 18650 holder so they wouldn't short as well.

You can never use too much hot glue, nor have a project that can't be better with some.

Step 3: Connect Board to Lid [Components]

Using a WW2 color scheme, I chose all wires the same color. This being over-engineered, I used resistors of a far higher wattage than necessary, as they were at the top of the bin. Hey, over-engineered doesn't have to mean non-lazy.

I wired mine like this:

Step 4: Schematic

Step 5: ATTiny/Arduino Source Code

Debouncing was new to me (in code), and I found a lot of attempts at making it work (the code will not work as expected without this added complexity), but after 3 tries I found an approach that was simple & worked here. I had already prototyped with INPUT_PULLUP and my "They Ate!" button pulling PB3 down (I always prefer this to setting a pin PULLUP). I point this out as the tutorial at the link has the switch pulling up, but I preferred down. It is also for a single LED, and I needed 2 LEDs alternating state (one on, one off).

So I adapted the code at the link to suit. Either go to the tutorial or ask me if you want it explained.

NOTE: The following has been stripped of comments regarding additional ways to do things. I will attach the full 'sketch' at the end, which is 100% the same functionality as this:

#include 
#include

const int SW = 3; // "They Ate!"
const int SW_CHECK = 0; // "Check"
const int LED1 = 1; // doesn't matter which is which
const int LED2 = 2; // doesn't matter which is which
int swState = HIGH; // SW needs debouncing...
int ledState = HIGH; // "
int lastButtonState = LOW; // "
unsigned long lastDebounceTime = 0; // the last time the select pin was toggled
unsigned long debounceDelay = 50; // the debounce time
unsigned long lastChangeTime = 0; // the last time the select pin was toggled
unsigned long lastCheckedTime = 0; // the last time the check pin was pressed
unsigned long waitToSleepMillis = 5000; // how long (secs x 1000) since last 'selection' to wait before sleeping

void setup() {
pinMode(SW, INPUT_PULLUP);
pinMode(SW_CHECK, INPUT_PULLUP);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
/**
*
*/
void loop() {
int reading = digitalRead(SW);
if (reading != lastButtonState) { // switching button takes SW LOW
lastDebounceTime = millis(); // reset the debouncing timer
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != swState) {
swState = reading;
if (swState == LOW) { // "They Ate!" button (SW) has been depressed
ledState = !ledState;
lastChangeTime = millis(); // start sleep timer
}
}
}
digitalWrite(LED1, ledState);
digitalWrite(LED2, !ledState);
lastButtonState = reading;
if ((millis() - lastCheckedTime) > waitToSleepMillis) { // check sleep timer
sleep(); // time to sleep (see below)
}
if (digitalRead(SW_CHECK) == LOW) {
lastCheckedTime = millis(); // reset sleep timer (via "Check" button)
}
} ISR(PCINT0_vect) {
// This is called when the interrupt occurs (in this case on pin 0)
}void sleep() {
digitalWrite(LED1, LOW); // I had to do this, or they would stay on-even during sleep! why?
digitalWrite(LED2, LOW); // I had to do this, or they would stay on-even during sleep! why?
GIMSK |= _BV(PCIE); // Enable Pin Change Interrupts
PCMSK |= _BV(PCINT0); // Use PB0 (pin 5) as interrupt pin
ADCSRA &= ~_BV(ADEN); // ADC off
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // replaces above statement
sleep_enable(); // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
sei(); // Enable interrupts
sleep_cpu(); // sleep
cli(); // Disable interrupts
PCMSK &= ~_BV(PCINT0); // Turn off PB0 as interrupt pin
sleep_disable(); // Clear SE bit
/*
ADCSRA |= _BV(ADEN); // ADC on (if needed)
*/
sei(); // Enable interrupts
} // sleep

PB0: When taken LOW, pulls out of sleep mode.

PB1 & PB2: LEDs. For 2, it doesn't matter which, but if you are making a medicine reminder with 3, 4 or more, you will need to keep them straight in code.

PB3: When driven LOW it instructs the chip to alternate PB1's & PB2's states.

The debouncing ended up being very needed.

The more observant of you will notice the lines:

digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);

If these are not there, the chip goes to sleep, but drops from ~4.7v to ~3.4v because the LEDs stay on! With these lines, the sleep current goes way down to 0.13uA. As it works fine, I haven't looked into it.

If it works, should I fix it?

Here is a link to the big ATTiny25/45/85 Datasheet (PDF)

Here is a link to a TON of ATTiny85 Documents (Click the Documents tab on the page).

From these you can navigate to the others, but except for size, it all applies to the line. I think the ATTiny line is the most interesting line to work with for small projects!

Step 6: Test, Add Battery, and Win Some Arguments!

This is the finished project. I show above the lid after I marked it up.

No LEDs will light unless you are holding the "Check" button down. You can change the state (which LED is lit) with the "They Ate!" button even if the "Check" button is not depressed (no GND to LEDs).

You can also amaze your friends and confuse your enemies by holding down the "Check" button and pressing the "They Ate!" button and create a light show.

I hope someone out there sees a more practical use for something like this than making sure your pets haven't eaten twice or starved. It would be a shame to go to my grave with the sad realization that it was truly useless.

Compared to an analog latching circuit, was this over-engineered?

Yes. Intentionally, unnecessarily so.

Step 7: Battery Status Updates

I sure wish I had made sure the 18650 was 100% charged when I started...

2022-06-27 - Coming up on 4 years.

Interested in hearing from those using the ATTiny line, or the deep sleep modes, like this. What are your findings?