Using an ATTiny85 and a 5x5 RGB LED display for a schoolbag patch
When the little one first started school, she got a new school bag and that had velcro on it so you could attach patches to it. Of course the manufaturer also sold those patches but although there were some cute ones among them and even some with LEDs, they were very simple and boring.
So, I came up with something interesting: A programmable, recharchable patch that could display simple animations. My choice fell on the ATTiny85 as the microprocessor because I have worked with that in the past, it's easy to use and its power consumption is relatively low.
To avoid the little one being overlooked when walking to school, I wanted the LEDs to be bright, unlike those lame flimsy botton battery ones you can buy online. My choice here fell on a 5x5 WS2812 Display, enough to display simple animations but still easy anough to create them and it also doesn't use too much power while still being very bright (in fact, I ended up reducing the brightness in the code).
I designed a simple PCB, re-designed it a few times, until I ended up with a version I could work with, using the first ones as a base plate to level the patch with the li-ion battery I included.
First, I thought about giving the little one this really nerdy looking patch just like that but my sister came up with the idea to sew a cute little case for it. Thus, that was the end product and the little one is happy.
The circuit is very simple, I won't even care to put it into a professional schematics diagram because it's clearer this way.
Components:
- A 250 mAh li-ion battery, enough for about three hours
(should at least have an under-voltage-protection it the charger is not in included into the circuit) - A li-ion charger curcuit to guard the battery from under and over charge and to control the charging process
- An ATTiny85 microprocessor controlling the animations
- A USB port to supply charging power
- A main switch, cutting the patch off from the charging board
- A pushbutton as input to change animations
- A capacitor (~10µF) to level out power spikes so the processor doesn't brown out
- A 5x5 WS2812 matrix as display
I also used a 10kΩ pullup-resistor for the pushbutton but it should work without it using the INPUT_PULLUP mode on the button pin. I also tried using a A2222 transistor to deactivate the power consumption of the WS2812 matrix display but that didn't work.
These are the ATTiny85 pins I use:
Pin2/PB4: Set to INPUT_PULLUP so the line is default high. When the button is pressed, it will connect to ground, thus the signal is low. This can also trigger a low-level interruptPin3/PB3: Connects to the one-wire input of the WS2812 displayPin4/GND: Connects to ground - after the power switschPin8/VCC: Connects to the protected positiv output of the charger board
In addition, I have connected these pins to a connector so I could flash the ATTiny85 easily:
Pin1: To programmer Arduino ISPPin10Pin4: To programmer Arduino ISPGNDPin5: To programmer Arduino ISPPin11Pin6: To programmer Arduino ISPPin12Pin7: To programmer Arduino ISPPin13Pin8: To programmer Arduino ISPVCC/5V
The code implements the following features:
- Auto-sleep-mode after a defined time (into low-power mode)
- Sleep-mode by long-pressing the pushbuttoncircuit
- Wake-up from sleep-mode with pushbutton via low-power interrupt
- A custom heart animation with red-pink color shifting
- A sine-wave rainbow animation
- A custom animation with a dog repeating a part of the animation
- Several hard-coded animations with slightly shifting color:
- A very simple Mario animation
- A simple pong animatino
- A passing car annimation
- A jumping dots animation
- Naruto run in different parts (not used)
- A running man annimation who falls and stands up again in different parts (not used)
All animations are defined in all four orientations: 0°, 90°, 180° and 270°. That way it is easy to change where the pushbutton for example points. The switching works by including the appropriate orientation file animations*.h at the beginning of the script.
I also created little JavaScript tools to help creating and editing animations:
- Editor: bjsblog.spdns.de/stuff/BjSMatrixEditor2.htm
- Rotater: bjsblog.spdns.de/stuff/BjSMatrixRotater.htm
On the editor site you can create animations frame by frame and pixel by pixel. The Data is stored as a 1 bit image in one uint32_t per frame and the code parses it like that and sends it to the 5x5 display
If you want to edit or add an animation, you should do so in the header files animations*.h. Follow the existing examples to include the animations into the program memory to avoid using RAM instead:
const uint32_t RUNNING_MAN[] PROGMEM = {
5, // <-- put number of frames here
0b0010000000001100110000100,
0b0010000000001100010001000,
0b0010000000001100011001000,
0b0010000000001100110010010,
0b0010000000001101110000100
};To use the animation you can edit the function void loopAnis(uint8_t aniSet) of the main ino file and insert a case for the animation:
void loopAnis(uint8_t aniSet) { switch (mode) {
case 0: loopHeart(); break;
case 1: loopDog(); break;
case 2: loopRainbow(); break;
case 3: loopAnis(0); break; // running
case 4: loopAnis(1); break; // mario
case 5: loopAnis(2); break; // pong
case 6: loopAnis(3); break; // car
case 7: loopAnis(4); break; // dots
default:
currMode = 0;
break;
}
...
switch (aniSet) {
...
case 17:
// Reset the current frame
currAniSelect = 0;
// Set the base address of the animation
currAniAddr = &RUNNING_MAN switch (mode) {
case 0: loopHeart(); break;
case 1: loopDog(); break;
case 2: loopRainbow(); break;
case 3: loopAnis(0); break; // running
case 4: loopAnis(1); break; // mario
case 5: loopAnis(2); break; // pong
case 6: loopAnis(3); break; // car
case 7: loopAnis(4); break; // dots
default:
currMode = 0;
break;
}
;
break;
...
}
...
}The rest is done automatically, looping through the frames and restarting it when the current frame has reached the frame count.
The last step is to adjust the modes in the function void loop() in the main ino file. Here you can set which mode plays which animation. You can edit this to your liking. The code starts with mode 0 and the mode switching works by increasing the mode by 1 each time the pushbutton is pressed until the mode is not inclused in the switch case which resets the mode back to 0. E.g. if you only want the heart and your animation, this would look like this:
void loop() {
...
switch (mode) {
case 0: loopHeart(); break; // the heart
case 1: loopAnis(17); break; // your animation
default: // This should not be touched
currMode = 0;
break;
}
}I use the following Arduino IDE settings:
- Board URL: drazzy.com/package_drazzy.com_index.json
- Additional library:
Adafruit NeoPixel - Board:
ATTinyCore/ATTiny25/45/85 (no bootloader)- Chip: ATTiny85
- Clock source:
16 MHz (PLL) - Timer 1 Clock:
CPU (CPU frequency) - LTO:
Enabled - millis()/micros():
Enabled - Save EEPROM:
EEPROM retained - B.O.D. Level:
B.O.D. Disabled (saves power) - Programmer:
Arduino as ISP
Do not forget to burn the bootloader on first use before flashing the program!
To flash the ATTiny, prepare an Arduino (Uno or Nano works best for me), connect the Pins 10 to 13 plus VCC and GND to the ATTiny and use the programmer to upload the script.
- I still couldn't find an easy solution to switch off the 5x5 WS2812 Matrix display when entering the sleep-mode. This results is a sugnificant power drain even when the ATTiny is in sleep-mode so the battery won't last long in this state - not even two days.
- There are different kinds of 5x5 WS2812 matrix displays and the code only supports one specific kind where the LEDS are placed in an alternating pattern and not line by line. Also, I found out that there are mirrored displays. Right now, I only solve this by mirroring the annimation data manually or not at all because it doesn't really matter if the animations are mirrored horizontally.
- I haven't implemented text scrolling yet and with the storage usage at around 90% I probably won't.




