Introduction: Daily Running Tracker
It's been a while since I made an instructable so I'd like to share something fun that I made recently. During lockdown I made my own yearly "everyday calendar" inspired by Simon Giertz's own idea (no instructable but photo attached for those interested). My better half wanted something similar to track her running (but on a 7 day weekly basis rather than a year). It also had to keep track of how many miles she ran not just a single light indicating a task had been done or not. Additionally, I wanted it to sing and dance and do lots of shiny LED business when she exceeded her target. The idea for the daily running tracker was born, so in summary it must:
- Log miles ran every day of the week
- Compare 'miles ran' against a set goal for that day
- Reward the user with a celebratory song and some flashy lights
- Be small enough to fit on a desk
- Remember everything when the power was turned off (comit everything to non-volatile memory)
For this project I used:
- A 3D printer (for the enclosure designed in FreeCAD)
- M3 screws, and brass M3 inserts
- A soldering iron
- Jumper wires or wires of some kind
- x9 momentary push buttons
- x7 WS2812 addressable LED's
- x7 SSD1306 OLED 128x32 pixel Displays
- A 4 Ohm speaker
- The DY-SV8F playback module
- An Arduino Mega
- DC barrel input
- The TCA9548A multiplexer
- 13. Arduino Mega prototyping board
Step 1: Operation
The user does a short push to enter the distance ran (Distance input mode) for the particular button pressed for the day in question (the DAY button), then uses an UP and DOWN button to increment the miles in 0.01 increments. A long push for more than 3 seconds will allow the user to enter the target/goal (target input mode) for that particular day, and UP and DOWN is used again to set the goal in 0.01 mile increments. A short push of the DAY button will confirm the entry of either the goal or target depending on which mode the unit is place in.
A long press of the push button allows the targets to be reset by clearing the EEPROM memory. When pushed for more than 3 seconds the displays will ask if the user if they are sure and display UP = YES, and DOWN = NO, to confirm their entry. A short press on the UP button will write a bunch of 0's to the EEPROM and the display will show "ALL MEMORY ERASED". I think an encoder would of been a nicer idea but I wanted to use up some push buttons I had in stock.
By default the LED for each day is red until the distance ran for that day exceeds the target set, in which case it turns to green. When the user enters distance input mode the LED turns light blue to indicate the unit is waiting for the user to input the miles, and deep blue when target input mode is selected. For confirmation of a target/distance when the user pushes the DAY button again to exit the input goal/target mode the LED with perform a colour cascade as an acknowledgement it has received the command. Finally when the user reaches the goal it will flash wildy different colours.
My original idea was to use the I2C bus to string multiple OLED's together, however I found out that the SSD1306 OLED's are typically hard coded with the same I2C address. That is why I had to use the TCA9548A multiplexer to allow separate addressing of individual displays. This displays a little running man and some chevrons to indicate the user is in distance input mode, and a crosshair with chevrons to indicate target input mode. I used the purple one picture above. I have included a fritzing diagram above which illustrates an example of how to connect 4 of these sorts of OLED's to an Arduino UNO, although it is the same for 7 OLED's and the MEGA.
I wanted a module which would handle all the play back of music segments. I wanted it to be simple to use/implement and play at decent quality and volume. The DY-SV8F module pictured above seemed an obvious choice. It plays up to 255 MP3 or WAV files, 10 files which are directly triggered by a logic LOW signal at one of it's input pins. A microSD card was loaded with 7 music MP3 files each about 20 seconds long (cut down and faded in/out using Audacity). Each sound file must be labelled using a 5 bit nomenclature, i.e. for input 1, the first file must be named 00001.wav, the second 00002.wav, the third 00003.wav and so on. The Arduino GPIO pulls the pin in question low which triggers the song when the user hits their goal. The module apparently has a 3W amplifier, and without much thought I soldered a 4 Ohm speaker (recycled from a toy) onto the SPEAKER output and it works fine.
Step 2: Code
As far as C coding goes I get by. After finishing this code on an Arduino nano I realised of the repetitive code took up alot of space. What I should of done is put certain operations into Void functions and written the code once, but instead I found what worked for one day and simply copy and pasted it 7 times! It used 103% of the Arduino's memory so that is why I simply used a Mega, because I was too lazy to reduce the already working code. Meh...I was running out of GPIO's anyway, and the Mega has more than enough so I may of had to anyway with hind sight.
The code generally has the following functions
- Celebrate! -flash the LED's and make a pretty pattern
- Acknowledge - flash the LED's in a colour cascade
- Write to EEPROM
- Read from EEPROM
- Monitor the push buttons
- Display stuff on the OLED's
Reading the EEPROM is done very simply by storing the numbers as seperate integers. For example the distance 10.12 miles is made up of the numbers 10 and 12 seperated by a decimal. I would simply store them in seperate address spaces one for each part of the number. The decimal number in this example 12 would be read from the EEPROM and cast as a floating point number so it could be divided by 100 to give 0.12, added to the first number 10 then this gives 10.12...easy. Writing is a little different to separe the number 10 you simply typecast 10.12 as an integer which the arduino code parses as simply 10. Take this number 10 away from 10.12 and then you are left with 0.12 which you simply float, multiply by 100 to get 12, and then write to the address space in question. All this is done with the basic commands included with Arduino. As long as the user runs < 254.99 miles then this will be adequet.
I used this library to do the multiplexing, which is available directly through the libraries manager of the arduino IDE. to use it you simply call the address you want (corresponding to the silk screen on the TCA9548A) and it sends all of the I2C commands to that "channel". The library of the OLED is just the Adafruit GFX library that everyone uses to control these modules so you don't need to do anything different to normal when controlling the OLED. When opening each channel the channel should be closed after the command is sent if you want to then address another channel otherwise the same command can go to two addresses at once.
To display anything other than text you will need to store it as a byte array. Luckily there is LCDassistant which converts a simple black and white bitmap into a byte array you can simply copy and paste into your arduino code. I made the running man and the bullseye in Inkscape to get the dimensions correct before using LCD assistant. Adafruit has a short tutorial on using this. However they don't explain that the OLED displays a BLACK background so if you want to display something it will be WHITE ON BLACK. I have included my bitmaps to illustrate what I mean, the colours will be inverted.
Any of the WS2812 libraries can be used but again Adafruit libraries are very well supported and easy to use so I have used those. Most of the code happens in FOR loops and iterates through the colours an arbitrary number of times. I tried to make it cycle through as many colour permeantations as possible to make it more shiny!
Most of the action happens in WHILE loops. First an IF statement tests if the DAY button is pushed and the code changes a BOOL to true. The WHILE loop continues to run and tests if the UP or DOWN button is pushed which either adds +0.01 or subtracts -0.01 from ther distance input and continues until the BOOL is false, which only happens on a second confirmation push of the DAY button which causes the WHILE loop to exit. A function is called to write the new distance value to EEPROM
I have attached my bulky code for those who are interested!
Step 3: Make an Enclosure
The design was then working on the breadboard, but before I could permenantly solder everything together I needed an enclosure to put everything in. I measured everything and printed little jigs to test fit each component to see if the dimensions would fit when installed into the front of the enclosure. The OLED's are a friction fit so they can be gently pushed in and they are held in place. The speaker is glued onto a small circular lip on the inside with a dab of superglue, and the push buttons are panel mount so are attached with lock nuts. Holes are in each corner to allow brass threaded inserts to be added later
Much to my annoyance the WS2812 module I used is a SparkFun module so all the dimensions were unhelpfully in Mils not Millimeters (thanks guys). SparkFun makes the Gerbers available but again very unhelpfully these are EAGLE files...guys if you are reading this...try out KiCAD it's free by the way! Fortunately EasyEDA allows you to read EAGLE files so if you don't have Autodesk use EasyEDA to get the dimensions from a .BRD file. For the metrically impaired I have uploaded a picture of the metric equivalent. Once I had the dimensions I made little pegs for the boards to stand on, and secured with a dab of hot glue.
For the bottom part I made little pegs to mount the arduino and the sound module to the bottom, and a hole for a switch and a DC barrel jack.
Step 4: Cram Everything In
After disassembling the breadboard I soldered all the connections onto a prototype board (shield) which just plugs into the Mega once you solder header pins onto the through holes. The ones I used are from Keyestudio which break out all the pins onto through holes in the middle of the prototyping area. They were a bit expensive at £15 for ten but they made a good job. I used a piece of stripboard to make a 5V rail and 0V rail (GND) as all the buttons, OLED's, LED's, TCA9548A and sound module all shared these power rails. You then carefully cram everything into the enclosure so it looks like the rats nest in the pictures above.
Step 5: Admire Your Project
It won't replace my partners smartwatch anytime soon, but it was a lot of fun making this. I particularly had fun choosing which songs to use. For Sunday I used the Charlie Sheen WINNING Meme done by schmoyoho, and a few other well known songs like Kool & the Gang, and the proclaimers.