Introduction: Goal / Habit / Medication Tracker Using ATTINY85
People tend to forget if they had that day's medication or not and this often leads to an overdose or totally skipping it. Both are dangerous in most cases.
This was the problem I was aiming to solve, but as I made it, I realised that I could use it also to track my daily habits or goals.
It will be a small device with 7 LED's that signify the 7 days of the week. The LED's turn on one after the other when a button is pressed. With the help of the potentiometer, one can track more than one goal. Just turn the pot to the required range and the LED's light up to show the count of that particular goal.
After completing the day's said task, one has to press the button and an additional LED lights up. Then turn the potentiometer to another goal (the LED's count changes ) and when that task's done, update it by pressing the button again. As mentioned above, this can also be used to track medicines one takes at different times of the day.
Step 1: Getting Things Ready
Parts list:
- ATTINY85
- 74HC595 (Shift Register 8-Bit)
- Pushbutton
- Potentiometer 10K
- LED (7)
- 0.1 uF capacitor (3)
- 10uF capacitor
- 1K resistor (2)
- 10K resistor
- Perforated board
- 8 pin IC socket
Apart from this, you'll need a programmer to program the ATTINY85, wires, solder, Hot glue (not mandatory) and power source.
Note: Please do check out the 'Going Further' step for other design improvements and parts.
ATTINY85 programmer
You can either buy programmers like these or make one on your own if you have an Arduino lying around. Follow this guide here .
I made my own shield sort of programmer using the above guide, which I will be using here.
Step 2: A Bit of Theory
I recommend you read the following instructable to get a good picture of what we are trying to do.
https://www.instructables.com/id/Getting-more-IO-pins-on-ATTiny-with-Shift-Register/
With the shift register, one can control 7 LED's (or more using cascading) using just 3 pins. If you don't want to use another IC then try Charlieplexing but you'll only be able to control 6 LEDs (you can take a day off work :P) using the 3 pins.
I've simplified the circuit and the code as much as possible. I've added decoupling capacitors (for each IC) and a debouncing circuit for the pushbutton - They are very essential and should not be avoided.
- If you plan to change the order of pins follow this guide : https://github.com/SpenceKonde/ATTinyCore
- I also used the same ATTiny Core to program the Attiny85
- I used this debouncing network: https://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-i/
- Recommended read for beginners: https://www.autodesk.com/products/eagle/blog/what-are-decoupling-capacitors/
Attachments
Step 3: Making It
- Put the LED's in and place the components at the required positions on the board (I've left space at the bottom of the perforated board for future upgrades).
- In order to ensure that the part stays in place when we turn it face down (for soldering), I used some hot glue to do that. I find this easier than bending all the leads.
- Solder the components onto the board.
- Now make connections using solder and wires.
- Verify all connections with a multimeter
- Remove the hot glue
- Program the ATTINY85 with the code given below
- Board: ATtiny25/45/85
- Processor: ATtiny85
- Clock: Internal 8Mhz
- Programmer: Arduino as ISP
- Test it
POSSIBLE USES :
- Daily goal tracker
- Habits tracker
- Medication tracking
- Days left tracker (invert LED sequence in code)
- Timer (alter the code to use the button for starting an internal timer)
- Temperature intensity meter (using the inbuilt temperature sensor in the ATtiny85 - check this )
-------------------------------------------------------------------------------------------------------------------------------------------------
CODE
int latchPin = 2; //Pin connected to pin 12 (RCK) of 74HC595<br>int clockPin = 0; //Pin connected to pin 11 (SCK) of 74HC595 int dataPin = 1; //Pin connected to pin 14 (SER) of 74HC595 int buttonPin=4; // for the pushbutton int potPin=3; // for the potentiometer int val=0; // to store the state of the button int analog=0; // to store the value from ADC //holders for infromation you're going to pass to shifting function byte data; byte dataArray[10]; void setup() { //set pins to output because they are addressed in the main loop pinMode(latchPin, OUTPUT); pinMode(buttonPin,INPUT); pinMode(potPin,INPUT); //predefined array to store the different LED configurations // => 0 = LED ON // => 1 = LED OFF // IGNORE MSB VALUE AS WE ONLY HAVE 7 LED'S dataArray[0] = 0xFF; //0b11111111 => All off dataArray[1] = 0xBF; //0b10111111 dataArray[2] = 0x9F; //0b10011111 dataArray[3] = 0x8F; //0b10001111 dataArray[4] = 0x87; //0b10000111 dataArray[5] = 0x83; //0b10000011 dataArray[6] = 0x81; //0b10000001 dataArray[7] = 0x80; //0b10000000 => All on // THIS PART LIGHTS UP ALL THE LEDS IN A SEQUENCE AT STARTUP for (int j = 0; j < 8; j++) { data = dataArray[j]; //ground latchPin and hold low for as long as you are transmitting digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, data); digitalWrite(latchPin, 1); delay(50); // adjust as needed } } void loop() { val = digitalRead(buttonPin); // read button state analog=analogRead(potPin); // read potentiometer value // Variable to store the count of the different tasks //(can be increased as needed provided the potentiometer divisions are also increased ) static int count1=0; // Task1 static int count2=0; // Task2 static int count3=0; // Task3 // Potentiometer divisions: [0-100 | 100-600 | 600-1023 ] => 3 divisions => 3 tasks // TASK1 if (analog <= 100) { if (count1 < 8) { //load the LED sequence you want from array data = dataArray[count1]; //ground latchPin and hold low for as long as you are transmitting digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, data); //return the latch pin high at the end of transmission digitalWrite(latchPin, 1); //adjust delay to adjust switch response delay(50); } else { count1=0; // reset count when the all 7 LEDs are lit } if(val == 0){ count1++; // increment count when switch becomes low (or is pressed :- since its pulled up ) } } // TASK2 if (analog > 100 && analog <= 600 ) { if (count2 < 8) { //load the LED sequence you want from array data = dataArray[count2]; //ground latchPin and hold low for as long as you are transmitting digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, data); //return the latch pin high at the end of transmission digitalWrite(latchPin, 1); //adjust delay to adjust switch response delay(50); } else { count2=0; // reset count when the all 7 LEDs are lit } if(val == 0){ count2++; // increment count when switch becomes low (or is pressed :- since its pulled up ) } } //TASK3 if (analog > 600) { if (count3 < 8) { //load the LED sequence you want from array data = dataArray[count3]; //ground latchPin and hold low for as long as you are transmitting digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, data); //return the latch pin high at the end of transmission digitalWrite(latchPin, 1); //adjust delay to adjust switch response delay(50); } else { count3=0; // reset count when the all 7 LEDs are lit } if(val == 0){ count3++; // increment count when switch becomes low (or is pressed :- since its pulled up ) } } } //-------------------------------------------------------------------------------------------------------- // the heart of the program void shiftOut(int myDataPin, int myClockPin, byte myDataOut) { // This shifts 8 bits out MSB first, //on the rising edge of the clock, //clock idles low //internal function setup int i=0; int pinState; pinMode(myClockPin, OUTPUT); pinMode(myDataPin, OUTPUT); //clear everything out just in case to //prepare shift register for bit shifting digitalWrite(myDataPin, 0); digitalWrite(myClockPin, 0); //for each bit in the byte myDataOut� //NOTICE THAT WE ARE COUNTING DOWN in our for loop //This means that %00000001 or "1" will go through such //that it will be pin Q0 that lights. for (i=7; i>=0; i--) { digitalWrite(myClockPin, 0); //if the value passed to myDataOut and a bitmask result // true then... so if we are at i=6 and our value is // %11010100 it would the code compares it to %01000000 // and proceeds to set pinState to 1. if ( myDataOut & (1<<i) )="" {="" ="" pinstate="1;" ="" }="" else="" }<="" p=""></i)> //Sets the pin to HIGH or LOW depending on pinState digitalWrite(myDataPin, pinState); //register shifts bits on upstroke of clock pin digitalWrite(myClockPin, 1); //zero the data pin after shift to prevent bleed through digitalWrite(myDataPin, 0); } //stop shifting digitalWrite(myClockPin, 0); }
Attachments
Step 4: Going Further
Design Improvements
- LEDs should have been soldered a little higher so that it sticks out of the surface when put in an enclosure.
- I observed that different LEDs had different brightness even though they were all identical in make. Then I came across this . Hence its advisable to use resistors in series with each LED.
- Use bigger buttons
Going Further
- 3D print the enclosure - the enclosure can be 3D printed as shown in the image and the engraved text can be filled with paint.
- Powering it from a Li-ion battery - I've tested and it works fine with a Li-ion battery since the ATtiny can work from 2.7V to 5.5V and the SN74HC595 works from 2V to 6V. I also plan to add a Li-ion charging circuit later and make it a standalone device.
- Putting the device to sleep - It would be great if the device could be put to sleep after a period of inactivity and made to wake up when there is a button press. This would also help improve the battery life by a great margin.
- Keeping data safe - By writing the count values to the inbuilt EEPROM the data can be kept safe and retrieved even after power down. Data can also be logged to later check monthly stats.[the only limitation here is the 10,000 write/erase cycles of the EEPROM ]
Do post your questions and suggestions in the comments section and I will be more than happy to help you out!