Application Programming
You can generate random HTMaA projects by picking an input device and an output device and thinking about what it could mean to wire them together. This week, I picked an accelerometer and a speaker and made an instrument.
I originally thought of making a sort of theremin, but dead reckoning absolute position with an accelerometer turns out to be almost impossible. I can't reliably integrate over noisy data, so I have to be happy with second derivatives. This means this instrument is more like a conductor's baton, where the information is captured in relative motion, not actual position.
The board I made was a minimal merge of the hello.ADXL343 and the hello.speaker.45. I used the ADXL343 Eagle library from SparkFun and planned for the 0.25 watt, 8-Ohm speakers that I found in the architecture shop. I did the math and with a 3.3v regulator, I'd need an additional ~30 Ohms of resistance to hit 0.25 watts for the speaker, so I laid out three parallel 100-ohm resistors on the other side of the MOSFET.
I missed the reflow soldering recitation, but the accelerometer didn't seem that small, so I soldered it by hand. It took two tries to get it working, but I think the first one didn't work because the soldering iron was too hot, not (only) because I missed connections.
The second board I made worked perfectly with both accelerometer input and speaker output.
For speaker output I used the ATtiny's Timer0 with a prescale of 1024.
// Timers cli(); TCCR0B |= ((1 << CS02) | (1 << CS00)); // Timer 0 prescaling - divides by 1024 */ TCCR0A |= (1 << WGM01); OCR0A = 50; TIMSK |= (1 << OCIE0A); DDRB |= (1 << PB1); PORTB |= (1 << PB1); sei();
... And on every cycle toggled the speaker (this means it's making a square wave, which is not ideal, but it was easy so I'll take it).
ISR(TIMER0_COMPA_vect) { PORTB ^= (1 << PB1); }
I copied the I2C code from the accelerometer example but stripped away the serial communication part.
I didn't have time to come up with something clever to do with the actual sensor values, so I just decided to use the norm of the magnitudes on all axes v = x^2 + y^2 + z^2
and scale v
down to a human-frequency-range value.
After a bit of fiddling (but not bit-fiddling, mind you) I settled on this arbitrary code:
value = ((int) data[0] * (int) data[0]) + ((int) data[2] * (int) data[2]) + ((int) data[4] * (int) data[4]); filter = (1 - EPS) * filter + (EPS * value); if (++counter > 100) { counter = 0; OCR0A = (unsigned char) round(filter / 100); }
Hooray!