Christmas Card with Lights and Sound

Table of contents

Christmas Card with Lights and Sound

Abstract

Article shows DIY way to make paper Christmas wish card improved by LED strips, bulbs, and speaker for playing sound. It is built as hand-soldered circuit without any PCB and utilizes AVR DD MCU, SPI Flash and op amp.

Hello Element14 Community!

In this article I would like to describe my DIY wish card. It is paper card with image on cover. You can write some message inside and then gave it to somebody you love or just to your friend. In my country these are usually given at birthdays and some special holidays like Christmas. Normally, you can buy high quality printed cards in the shop, but you can make your own yourself. I did so and even improve it by electronics! I improved printed Christmas tree on cover by LEDs and audio speaker for playing sound. It is flexible circuit without any PCB this time!

Side note: I am not sure what is correct English term for this. Most straightforward translation form my native language is wish card, but I found these under term “greeting card” or just card also.

See it in the action!

Sample Sponsors

At the beginning, I would like to thank STMicroelectronics for sending me free samples of TSB582 and TSV792 operational amplifiers, TDA2822D audio amplifier and L2720W13TR power amplifiers which I all tested when prototyping this project and TSB582 I used in final project.

image

Aesthetics

I am not very good in graphical design, so instead of drawing my own image, I searched for already existing templates with open licence. The template which I used is from My-Free-Printable-Cards.com. You can of course use whatever template you want. I chosen this Christmas tree because it perfectly fits for using LED strips and light bulbs. I did only one modification to template: I translated “Merry Christmas” to “Veselé Vánoce” which is equivalent in my native language (I want to give it to family member who do not speak English).

Similarly, I used sound with open licence. Sound which I used is freely available and comes from Pixabay. You can use any sound you want as long it is up to 95.1 seconds long. If you need longer, you will need to upgrade flash memory in the circuit as described below.

Electronics Circuit

The most interesting part of device is electronics in it, of course. It is my first design with speaker. Because I was not sure if it will work, instead of going blind shot way, I decided to make breadboard prototype first. So here is very similar “wish card” again:

Block diagram

Here is the blog diagram showing the architecture of electronics circuit:

image

Schematics

The schematics basically contains blocks from the block diagram.

image

Soldering

The same circuit after successful breadboard development I converted to the wish card. I used very strange technique with directly attaching wires to SMD pads. Chips are in SO8 and SOIC20 packages with 1.27 pitch, so they are not the smallest one and it is possible to attach wires to them. Even LEDs are SMD. Soldering wires to 0603 LED is horrible experience. Trust me guys, I wasted approx. 4 hours by soldering them. Do not go this way if you decide to make your own. After this mistake, I realized that it will be better to use THT resistors instead of trying to solder SMD resistors in the same way as I did with LEDs. Here are some photos from soldering time. Many parts of the connections shown below I later reworked (and improved by heat shrink tubes, and so on).

image

image

image

image

I tested all parts separately before connecting circuit together.

After connecting whole circuit together, I start attaching it to the card using sticky tape and parts around switch I attached using hot melt glue.

Battery contacts are made using ultra-sophisticated deinsulated wire (approx. 2cm of wire is formed to ball), then the contact is attached to the battery using sticky tape of course.

image

image

Assembly

Circuit is built in way that I printed template in greyscale (for saving toner) to strong paper (190 g/m2). I bend template for making A5 form factor and using sticky tape I attached electronics circuit to right places on paper. Later I improved mechanical stability of some parts like speaker and power switch by hot melt glue.

Complete assembled circuit looks as follows.

image

The last detail is covering LED strips to do not lit outside tree area. This I did using strong non-transparent paper.

image

Finally, the circuit was sealed by colour-printed standard paper cover. It is really important to complete and test circuit (including mechanical stress resistance) before gluying the last part because it is the only irreversible action. I glued it using hot melt glue. Also, if you test it too long, remember to replace battery before enclosing because at this step it become non-replaceable (or it is replaceable, but it is destructive process).

image

Now let’s speak about more details about electronic part selection and software running in the device.

Microcontroller

The main part of the circuit is AVR32DD20 microcontroller. I used this microcontroller because it was in my storage bin for a long time, and I think 8-bit AVR best fit this type of projects. It has hand soldering suitable package also. Memory requirements at the end are no so large, so AVR16DD20 is suitable also (but it is not available at Farnell and other distributors at the time of writing the text, so if you decide your own, you will still need to go AVR32DD20 way nowadays). The breadboard prototype I did with AVR128DB48 microcontroller on Curiosity Nano board which I received as part of previous Project14 reward. They are similar MCUs, but they come from different families (DB vs DD). MCU on breadboard has more peripherals than AVR32DD20 used in final design, so I basically limited myself to peripherals which are available on both MCUs and at the end I changed macros for changed pinout.

One thing which I worried before starting development was performance. 8-bit MCUs are not very performant and playing high quality audio requires generating 44100 samples per second. I hoped that 8-bit MCU clocked at around 24 MHz will sufficient, but I still had to do lot of optimizations.

Flash Memory

MCU has 32 KiB of flash memory which is insufficient for storing audio file (can be upgraded to 128 KiB, but this is still far insufficient). For this reason, I used external Winbond W25Q SPI Flash memory. Because MCU is not powerful enough for decompressing file, it requires audio file in un-compressed VAW format with 16-bit signed data and it must be mono. MCU at power-up reads the header and refuse to play incorrectly formatted file. You can use Audacity to convert audio. Flashing file to memory I did out of circuit using Raspberry Pi. I used flashrom open-source project for doing so. You should pad file by zeroes (not ones) to fill remaining space of flash memory. This is requirement caused by optimization. MCU do not check end of file on sample basis, but it checks it after completing playback of block of 256 samples. It basically overflows the file, but since it is zeroed, it is not problem. This optimization is required because comparing large 32-bit number (file size) requires several instructions on 8-bit CPU and this caused un-acceptable serious delay in sample processing. Pinout for flashing by Raspberry Pi is standard SPI0 connection with Chip Enable 0 connection according to https://pinout.xyz/. Flash memory Hold and WP pins should be tight to 3.3V. Commands for padding converted wav file and flashing it to 64 Mb flash using Raspberry Pi GPIO are following:

AUDIO_FILE=audio_file.wav
FLASH_Mb=64
TARGET_SIZE=$((FLASH_Mb * 1024 * 1024 / 8))

dd if=/dev/zero of="$AUDIO_FILE" oflag=append conv=notrunc bs=1 count=$(($TARGET_SIZE - $(stat -c %s "$AUDIO_FILE")))
flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=4096 -c "W25Q64BV/W25Q64CV/W25Q64FV" -v "$AUDIO_FILE"

Note the padding dd command is usually horribly slow (depends how much zeroes are needed to add). I did this step manually using hex editor, but if you do not know how to do it, you can just wait (up to about 4 minutes on Raspberry Pi 3).

The maximum duration of song can be computed as (FLSIZEMb * 1024 * 1024 / 8 - 44) / 44100 / 2 where FLSIZEMb is size of flash memory in Mb (the size written in Flash name W25Q64VF => 64Mb), 44 is size of WAV file header, 44100 is sample rate, 2 is number of bytes per sample (2 for 16-bit samples). For 64 Mb flash it is approx. 95.1 seconds, for 128 Mb flash it is approx. twice, ie. 190 seconds. Similarly, you can go lower for saving money on flash cost if you need only short songs. MCU do not worry about flash size. 128 Mib flash is the maximum supported size because 256 Mib requires different approach of accessing data after reaching end of 24-bit address space used by flash memory. If you need more than 190 seconds, you need to improve firmware flash memory driver by adding support for this.

Audio Amplifier

I did not use any special audio codec chip because most of them are in package which is nonsolderable without PCB. I tested some audio amplifiers and general-purpose operational amplifiers with high-current capability also. I ended up with ST TSB582 operational amplifier capable source and sink 200mA. I drive it with DAC referenced to 1.024V. I can change it to 2.048V, 2.5V and approx. 3.3V, but can’t change it down. With audio amplifiers it was much loud and I can’t easily drive it down below 1.024V (without reducing quality of course). With audio amplifiers there were also hearable loud noise, but it can be tuneable by some filters, I think. But instead, after several tests I ended up with high-current general purpose op amp. It is connected as unity gain amplifier. Speaker is connected through series high-pass capacitor and there is also snubber attached to output. This seems like minimalistic and reliable solution which requires almost no external passives. I never made any audio product before, so I tried several components, and many changes have minimal or no impact on result, but I ended up on recommendations from some internet sources and examples.

LED Strips and LEDs

Microcontroller drives two LED strips. I bought them few months ago for test when I have heard about them in local maker communities. They come from AliExpress, and I did not find them at any reputable distributor. They are very flexible, and it seems internally they contains many small SMD LEDs. They are 30cm long (Chinese guys offers 10cm variant on AliExpress also).

Power

Device is powered by two CR2032 batteries connected in series. The produces about 6.6V when they are new. Because it is too high for MCU and Flash memory, there is 3.3V LDO. The audio amplifier supports power in range 4 to 36V, so it is directly connected to batteries and do not load LDO.

Batteries are connected to the circuit though 200mA PTC resettable fuse (for case when short happens in this hand-soldered flexible circuit), diode (which come to play when circuit is powered by something else, for example when debugging firmware) and mechanical switch.

Firmware Design

In the following sections I will describe interesting parts and stories from firmware development.

Firmware I wrote in C in Atmel Studio. Its main task is to load data from flash and play them.

Audio Playback

Playback is done in interrupt of timer which triggers regularly approx. 44100 times per second (it is influenced mostly by inaccuracy of internal oscillator). Interrupt handler loads sample from buffer, convert it from signed 16-bit number in range -32768 to +32767 to unsigned number in range 0 to 65536 suitable for loading to DAC. For playback data, there is buffer for 512 samples in RAM (whole audio do not fit MCU RAM nor MCU Flash). After playing half of the buffer interrupt handler sets flag which indicates main loop program that it is needed to replace the recently played part of buffer by data for next round. In meantime it plays the other half of the buffer. There are several optimizations in the code for making interrupt handler as fast as possible. It is possible to make it even faster when implemented in assembler, but even at C level there is space for optimization. For example, cast from int16_t to uint16_t is done using XOR 0x8000 instead of some fancy copies, casts and additions (note that compiler can optimize it to single 8-bit operation, but I did not check if avr-gcc did it). It basically converts signed number from range -32768 to 32767 to range 0 to 65536. It requires some thinking about second complement, but I did this analysis, and it works for all positive numbers, negative numbers and zero. Similarly checking for half-buffer processed event is done using ANDing constant and efficiently processing 8-bit value instead of 16-bit (for this reason the buffer size is exactly twice 256).

Flash read

The second most important firmware task is reading flash memory. Read is done in main loop when requested by interrupts. Read must be fast enough for reading 256 samples (512 bytes) before the audio playback interrupt need it. SPI speed must be fast enough and code for copying data must be as fast as possible. I tried to process data in SPI interrupt (note that new generation of AVR can very basically configure interrupt priorities and using SPI interrupt can be done in a way that do not influence audio timer interrupt in any way). But this did not work. It did not work because avr-gcc at interrupt entry and exit generates extensive register backup (and restore) stuff which is too slow. After experiments, I realized that it is more performant to wait for flag using busy waiting approach. When the SPI speed is fast, it take only few busy waiting cycles.

LEDs

Since MCU is utilized by continuous extensive tasks, there were very low performance margin for anything else, but LEDs are also important in this project. I originally planned support 8 LEDs and drive whole port A. Firmware still supports 8 LEDs, but I actually solder only 6 (4 LEDs + 2 strips). All LEDs blink the same pattern but they are phase shifted. Since MCU do not have enough timers for generating 8 PWM channels I originally went by soft way only. I implemented small buffer of precomputed soft-PWM signal GPIO port states. Imagine for example buffer with values 0, 0, 0, 0, 0, 0, 1, 1. If you regularly update GPIO port output value by value from this buffer one by one, it will generate PWM signal with 25% duty cycle. Audio timer interrupt handler does such GPIO output port value update. In main loop after loading new audio data (which happens 44100 / 256 = approx. 172 times per second), the firmware updates brightness of LEDs by adjusting content of the buffer. This approach worked and was performant enough, but there was one big issue. It produced switching noise in audio spectrum which induced on sensitive audio DAC output and was played by the speaker. The PWM frequency is 44100 / gpio state buffer size kHz (my buffer actually has 44 values, not 8 like it was mentioned in example above) which in my case is about 1 and this is in hearable range. So, when loading port with power-hungry LED strips, there were hearable whistling.

So, I needed to rework it. I tried several filters, but proper design will most probably need LC filter separating suplies, and external transistors for driving LEDs which is too many components and more precisely too much work when soldering using such crazy solder technique. Instead, I decided to make software fix for hardware problem. I decide to shift PWM frequency outside of audible range. There will still be noise, but human ear does not sense it. Because I had no computational performance for faster soft-PWM, I needed to give up idea of using just soft PWM for high-current LEDs and use hardware-accelerated high frequency PWM instead. I use Timer Counter D which allows generating advanced PWM waveforms (originally, it was designed for motor control). In other words, it allows me to drive both LED strips using single timer and it also allows me to make ON cycle for both timer a different time for load balancing current demand (time when both strips are ON is minimized). It works. I considered using other timers and PWM for other LEDs but there are not enough timers (especially when one of them is “wasted” for audio playback interrupt) for driving all LEDs using hardware accelerated PWMs. But in case of low-current LEDs the audio noise is not very significant, and you need to place ear very near device to hear it. So, the software controlled PWM for small LEDs remained in the project and in case of LED strips, precomputed values from algorithm are forwarded to the timer which generates high-frequency PWM (approx. 20 kHz) for them. Here you can see generated waveform on oscilloscope for both LED strips and you can sense brightness linearization:

There were one other issue coming from very high PWM frequency. When the PWM frequency is too high the LED do not light on very low duty cycles because “start-up” time of LED is too high. I implemented linearization to all LEDs but using high frequency PWM basically break it. I fixed (or compensated) it by increasing ON time using constant. It basically does not swing between 0 to 100% duty cycles, but rather between 15 to 100% because 15% duty cycle looks like OFF.

Firmware Flash

The last thing to describe is way to flash firmware. I originally planned use onboard debugger on Curiosity Nano but modifying board for allowing flashing and debugging external MCU requires cutting some traces and I did not go this way. Instead, I found way using simple USB-to-UART converter with single resistor added. Debugger connection look as follows. There is no need to connect reset anywhere, but it should not be pulled low. MCU must be of course powered (otherwise you can power it from 3.3V pin of USB-to-UART converter).

image

Commands for installing pymcuprog command and flashing firmware are following (you need to replace COM12 by number of your serial adapter and update path to hex file):

python -m venv .avrpyprog
.\.avrpyprog\Scripts\activate
pip install pymcuprog
pymcuprog.exe -t uart -u COM12 -d avr32dd20 write -f "C:\...\ChristmassPostcardAVR32DD20.hex" --erase –verify

Before flashing you can test connectivity by ping command:

pymcuprog.exe -t uart -u COM12 -d avr32dd20 ping

Similar commands you can use on Linux. You need to change pymcuprog.exe to pymcuprog, instead of COM12 use path to serial adapter in /dev/ and use Linux style path to firmware hex file.

BOM

Here is list of hardware parts that you need when decided to make your own card:

Product Name Manufacturer Quantity Buy Kit
AVR32DD20-I/SO Microcontroller MICROCHIP 1 Buy Now
W25Q64FVSIG SPI Flash Memory or compatible INTEGRATED SILICON SOLUTION (ISSI) 1 Buy Now
TSB582IDT Operational Amplifier STMICROELECTRONICS 1 Buy Now
L78L33ACZ LDO STMICROELECTRONICS 1 Buy Now
Speaker 8ohm 0.5W MULTICOMP PRO 1 Buy Now
CR2032 Battery RENATA 2 Buy Now
Schottky Diode 0.5A MIN TAIWAN SEMICONDUCTOR 1 Buy Now
PTC Fuse 200mA BEL FUSE 1 Buy Now
Switch SPDT MULTICOMP PRO 1 Buy Now
Wire with Silicon Insulation ADAFRUIT 1 Buy Now
R SMD 4R7 0603 YAGEO 2 Buy Now
R THT 220ohm YAGEO 2 Buy Now
R THT 510ohm NEOHM - TE CONNECTIVITY 1 Buy Now
R THT 2k2 YAGEO 3 Buy Now
C SMD 100nF MLCC 0603 KEMET 1 Buy Now
C SMD 1uF MLCC 0603 KEMET 1 Buy Now
C THT 100nF MLCC KEMET 3 Buy Now
C THT 1uF MLCC KEMET 1 Buy Now
C THT 470uF ELLYT KEMET 1 Buy Now
LED STRIP RED 1 Buy Now
LED STRIP YELLOW 1 Buy Now
LED SMD PURPLE/BLUE 0603 MULTICOMP PRO 2 Buy Now
LED SMD RED 0603 MULTICOMP PRO 1 Buy Now
LED SMD GREEN 0603 WURTH ELEKTRONIK 1 Buy Now
USB-to-UART Bridge LC231X 1 Buy Now
 

Additional Parts

Product Name Manufacturer Quantity
Paper A4 n/a 1
Paper 190g/m2 A4 n/a 1
Printer n/a 1
Sticky type n/a 1 (ton)

Closing

This is all. Thank you for attention and reading this article as far. In article I described my project and shown all it’s internal as well as described some interesting parts and stories from firmware development. I hope you enjoy reading it and if you decide to make your own, you can download all resources need for building one in the section below. Finally, I wish everyone Merry Christmas and all the best to next year, 2024.

Resources

Category : Holiday Projects