### Precision Temperature Measurement

In an effort to resume previous work [A, B, C, D] on developing a crystal oven for radio frequency transmitter / receiver stabilization purposes, the first step for me was to create a device to accurately measure and log temperature. I did this with common, cheap components, and the output is saved to the computer (over 1,000 readings a second). Briefly, I use a LM335 precision temperature sensor (\$0.70 on mouser) which outputs voltage with respect to temperature. It acts like a Zener diode where the breakdown voltage relates to temperature. 2.95V is 295K (Kelvin), which is 22ºC / 71ºF. Note that Kelvin is just ºC + 273.15 (the difference between freezing and absolute zero). My goal was to use the ADC of a microcontroller to measure the output. The problem is that my ADC (one of 6 built into the ATMEL ATMega8 microcontroller) has 10-bit resolution, reporting steps from 0-5V as values from 0-1024. Thus, each step represents 0.0049V (0.49ºC / 0.882ºF). While ~1ºF resolution might be acceptable for some temperature measurement or control applications, I want to see fractions of a degree because radio frequency crystal temperature stabilization is critical. Here’s a video overview.

This is the circuit came up with. My goal was to make it cheaply and what I had on hand. It could certainly be better (more stable, more precise, etc.) but this seems to be working nicely. The idea is that you set the gain (the ratio of R2/R1) to increase your desired resolution (so your 5V of ADC recording spans over just several ºF you’re interested in), then set your “base offset” temperature that will produce 0V. In my design, I adjusted so 0V was room temperature, and 5V (maximum) was body temperature. This way when I touched the sensor, I’d watch temperature rise and fall when I let go.  Component values are very non-critical. LM324 is powered 0V GND and +5V Vcc. I chose to keep things simple and use a single rail power supply. It is worth noting that I ended-up using a 3.5V Zener diode for the positive end of the potentiometer rather than 5V.  If your power supply is well regulated 5V will be no problem, but as I was powering this with USB I decided to go for some extra stability by using a Zener reference.

On the microcontroller side, analog-to-digital measurement is summed-up pretty well in the datasheet. There is a lot of good documentation on the internet about how to get reliable, stable measurements. Decoupling capacitors, reference voltages, etc etc. That’s outside the scope of today’s topic. In my case, the output of the ADC went into the ATMega8 ADC5 (PC5, pin 28). Decoupling capacitors were placed at ARef and AVcc, according to the datasheet. Microcontroller code is at the bottom of this post.

To get the values to the computer, I used the USART capability of my microcontroller and sent ADC readings (at a rate over 1,000 a second) over a USB adapter based on an FTDI FT232 chip. I got e-bay knock-off FTDI evaluation boards which come with a USB cable too (they’re about \$6, free shipping). Yeah, I could have done it cheaper, but this works effortlessly. I don’t use a crystal. I set fuse settings so the MCU runs at 8MHz, and thanks to the nifty online baud rate calculator determined I can use a variety of transfer speeds (up to 38400). At 1MHz (if DIV8 fuse bit is enabled) I’m limited to 4800 baud. Here’s the result, it’s me touching the sensor with my finger (heating it), then letting go.

I spent a while considering fancy ways to send the data (checksums, frame headers, error correction, etc.) but ended-up just sending it old fashioned ASCII characters. I used to care more about speed, but even sending ASCII it can send over a thousand ADC readings a second, which is plenty for me. I ended-up throttling down the output to 10/second because it was just too much to log comfortable for long recordings (like 24 hours). In retrospect, it would have made sense to catch all those numbers and do averaging on the on the PC side.

On the receive side, I have nifty Python with PySerial ready to catch data coming from the microcontroller. It’s decoded, turned to values, and every 1000 receives saves a numpy array as a NPY binary file. I run the project out of my google drive folder, so while I’m at work I can run the plotting program and it loads the NPY file and shows it – today it allowed me to realize that my roomate turned off the air conditioning after I left, because I saw the temperature rising mid-day. The above graph is temperature in my house for the last ~24 hours. That’s about it! Here’s some of the technical stuff.

AVR ATMega8 microcontroller code:

```#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

/*
8MHZ: 300,600,1200,2400,4800,9600,14400,19200,38400
1MHZ: 300,600,1200,2400,4800
*/
#define USART_BAUDRATE 38400
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

/*
{
PORTD^=255;
}
*/

void USART_Init(void){
UBRRL = BAUD_PRESCALE;
UBRRH = (BAUD_PRESCALE >> 8);
UCSRB = (1<<TXEN);
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // 9N1
}

void USART_Transmit( unsigned char data ){
while ( !( UCSRA & (1<<UDRE)) );
UDR = data;
}

void sendNum(long unsigned int byte){
if (byte==0){
USART_Transmit(48);
}
while (byte){
USART_Transmit(byte%10+48);
byte-=byte%10;
byte/=10;
}

}

}

}

int main(void){
//DDRD=255;
USART_Init();
for(;;){
USART_Transmit('n');
_delay_ms(100);
}
}```

Here is the Python code to receive the data and log it to disk:

```import serial, time
import numpy
ser = serial.Serial("COM15", 38400, timeout=100)

t1=time.time()
lines=0

data=[]

while True:

if "," in line:
line=line.split(",")
for i in range(len(line)):
line[i]=line[i][::-1]
else:
line=[line[::-1]]
temp=int(line[0])
lines+=1
data.append(temp)
print "#",
if lines%1000==999:
numpy.save("DATA.npy",data)
print
print line
print "%d lines in %.02f sec (%.02f vals/sec)"%(lines,
time.time()-t1,lines/(time.time()-t1))```

Here is the Python code to plot the data that has been saved:

```import numpy
import pylab

print data
data=data*.008 #convert to F
xs=numpy.arange(len(data))/9.95  #vals/sec
xs=xs/60.0# minutes
xs=xs/60.0# hours

pylab.plot(xs,data)
pylab.grid(alpha=.5)
pylab.axis([None,None,0*.008,1024*.008])
pylab.ylabel(r'\$Delta\$ Fahrenheit')
pylab.xlabel("hours")
pylab.show()```

If you recreate this project, or have any questions, feel free to email me!

### Wireless Microcontroller / PC Interface for \$3.21

Here I demonstrate a dirt-cheap method of transmitting data from any microchip to any PC using \$3.21 in parts.  I’ve had this idea for a while, but finally got it working tonight. On the transmit side, I’m having a an ATMEL AVR microcontroller (ATMega48) transmit data (every number from 0 to 200 over and over) wirelessly using 433mhz wireless modules. The PC receives the data through the microphone port of a sound card, and a cross-platform Python script I wrote decodes the data from the audio and graphs it on the screen. I did something similar back in 2011, but it wasn’t wireless, and the software wasn’t nearly as robust as it is now.

This is a proof-of-concept demonstration, and part of a larger project. I think there’s a need for this type of thing though! It’s unnecessarily hard to transfer data from a MCU to a PC as it is. There’s USB (For AVR V-USB is a nightmare and requires a precise, specific clock speed, DIP chips don’t have native USB, and some PIC DIP chips do but then you have to go through driver hell), USART RS-232 over serial port works (but who has serial ports these days?), or USART over USB RS-232 interface chips (like FTDI FT-232, but surface mount only), but both also require precise, specific clock speeds. Pretend I want to just measure temperature once a minute. Do I really want to etch circuit boards and solder SMT components? Well, kinda, but I don’t like feeling forced to. Some times you just want a no-nonsense way to get some numbers from your microchip to your computer. This project is a funky out-of-the-box alternative to traditional methods, and one that I hope will raise a few eyebrows.

Ultimately, I designed this project to eventually allow multiple “bursting” data transmitters to transmit on the same frequency routinely, thanks to syncing and forced-sync-loss (read on). It’s part of what I’m tongue-in-cheek calling the Scott Harden RF Protocol (SH-RFP). In my goal application, I wish to have about 5 wireless temperature sensors all transmitting data to my PC.  The receive side has some error checking in that it makes sure pulse sizes are intelligent and symmetrical (unlike random noise), and since each number is sent twice (with the second time being in reverse), there’s another layer of error-detection.  This is *NOT* a robust and accurate method to send critical data. It’s a cheap way to send data. It is very range limited, and only is intended to work over a distance of ten or twenty feet. First, let’s see it in action!

The RF modules are pretty simple. At 1.56 on ebay (with free shipping), they’re cheap too! I won’t go into detail documenting the ins and out of these things (that’s done well elsewhere). Briefly, you give them +5V (VCC), 0V (GND), and flip their data pin (ATAD) on and off on the transmitter module, and the receiver module’s DATA pin reflects the same state. The receiver uses a gain circuit which continuously increases gain until signal is detected, so if you’re not transmitting it WILL decode noise and start flipping its output pin. Note that persistent high or low states are prone to noise too, so any protocol you use these things for should have rapid state transitions. It’s also suggested that you maintain an average 50% duty cycle. These modules utilize amplitude shift keying (ASK) to transmit data wirelessly. The graphic below shows what that looks like at the RF level. Transmit and receive is improved by adding a quarter-wavelength vertical antenna to the “ANT” solder pad. At 433MHz, that is about 17cm, so I’m using a 17cm copper wire as an antenna.

Transmitting from the microcontroller is easy as pie! It’s just a matter of copying-in a few lines of C.  It doesn’t rely on USART, SPI, I2C, or any other protocol. Part of why I developed this method is because I often use ATTiny44A which doesn’t have USART for serial interfacing. The “SH-RFP” is easy to implement just by adding a few lines of code. I can handle that.  How does it work? I can define it simply by a few rules:

SHRFP (Scott Harden RF Protocol)

Pulses can be one of 3 lengths: A (0), B (1), or C (break).

Each pulse represents high, then low of that length.

Step 1: prime synchronization by sending ten ABCs

Step 2: indicate we’re starting data by sending C.

Step 3: for each number you want to send:

A: send your number bit by bit (A=0, B=1)

B: send your number bit by bit (A=1, B=0)

C: indicate number end by sending C.

Step 4: tell PC to release the signal by sending ten Cs.

Decoding is the same thing in reverse. I use an eBay sound card at \$1.29 (with free shipping) to get the signal into the PC. Syncronization is required to allow the PC to know that real data (not noise) is starting. Sending the same number twice (once with reversed bit polarity) is a proofchecking mechanisms that lets us throw-out data that isn’t accurate.

From a software side, I’m using PyAudio to collect data from the sound card, and the PythonXY distribution to handle analysis with numpy, scipy, and plotting with QwtPlot, and general GUI functionality with PyQt. I think that’s about everything.

The demonstration interface is pretty self-explanatory. The top-right shows a sample piece of data. The top left is a histogram of the number of samples of each pulse width. A clean signal should have 3 pulses (A=0, B=1, C=break). Note that you’re supposed to look at the peaks to determine the best lengths to tell the software to use to distinguish A, B, and C. This was intentionally not hard-coded because I want to rapidly switch from one microcontroller platform to another which may be operating at a different clock speed, and if all the sudden it’s running 3 times slower it will be no problem to decide on the PC side. Slick, huh? The bottom-left shows data values coming in. The bottom-right graphs those values. Rate reporting lets us know that I’m receiving over 700 good data points a second. That’s pretty cool, especially considering I’m recording at 44,100 Hz.

Here’s the MCU code I used. It’s an ATMega48 ATMEL AVR microcontroller. Easy code!

```#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>

void tick(char ticks){
while (ticks>0){
_delay_us(100);
ticks--;
}
}

void pulse(char ticks){
PORTB=255;
tick(ticks);
PORTB=0;
tick(ticks);
}

void send_sync(){
char i;
for (i=0;i<10;i++){
pulse(1);
pulse(2);
pulse(3);
}
pulse(3);
}

void send_lose(){
char i;
for (i=0;i<5;i++){
pulse(3);
}
}

void sendByte(int val){
// TODO - make faster by only sending needed bytes
char i;
for (i=0;i<8;i++){
if ((val>>i)&1){pulse(2);}
else{pulse(1);}
}
}

void send(int val){
sendByte(val);  // regular
sendByte(~val); // inverted
pulse(3);
}

int main (void)
{
DDRB = 255;
int i;

while(1) {
send_sync();
for (i=0;i<200;i++){
send(i);
}
send_lose();
}
}```

Here’re some relevant snippits of the PC code. Download the full project below if you’re interested.

```import matplotlib
matplotlib.use('TkAgg') # -- THIS MAKES IT FAST!
import numpy
import pyaudio
import pylab
import scipy
import time
import sys

class SwhRecorder:
"""Simple, cross-platform class to record from the microphone.
This version is optimized for SH-RFP (Scott Harden RF Protocol)
Pulse data extraction. It's dirty, but it's easy and it works.

BIG PICTURE:
continuously record sound in buffers.
if buffer is detected:

### POPULATE DELAYS[] ###
downsample data
find Is where data>0
use ediff1d to get differences between Is
append >1 values to delays[]
--if the old audio[] ended high, figure out how many
--on next run, add that number to the first I

### PLUCK DELAYS, POPULATE VALUES ###
only analyze delays through the last 'break'
values[] is populated with decoded delays.

."""

def __init__(self):
"""minimal garb is executed when class is loaded."""
self.RATE=44100
self.BUFFERSIZE=2**10
print "BUFFER:",self.BUFFERSIZE
self.newAudio=[]
self.lastAudio=[]
self.SHRFP=True
self.dataString=""
self.LEFTOVER=[]

self.pulses=[]
self.pulsesToKeep=1000

self.data=[]
self.dataToKeep=1000

self.SIZE0=5
self.SIZE1=10
self.SIZE2=15
self.SIZEF=3

self.totalBits=0
self.totalNumbers=0
self.totalSamples=0
self.totalTime=0

self.nothingNewToShow=True

def setup(self):
"""initialize sound card."""
#TODO - windows detection vs. alsa or something for linux
#TODO - try/except for sound card selection/initiation
self.p = pyaudio.PyAudio()
self.inStream = self.p.open(input_device_index=None,
format=pyaudio.paInt16,channels=1,
rate=self.RATE,input=True,
frames_per_buffer=self.BUFFERSIZE)

def close(self):
"""cleanly back out and release sound card."""
self.p.close(self.inStream)

def decodeBit(self,s):
"given a good string 1001101001 etc, return number or None"
if len(s)<2:return -2
s=s[::-1]
A=s[:len(s)/2] #INVERTED
A=A.replace("0","z").replace("1","0").replace("z","1")
B=s[len(s)/2:] #NORMAL

if A<>B:
return -1
else:
return int(A,2)

def analyzeDataString(self):
i=0
bit=""
lastB=0
while i<len(self.dataString):
if self.dataString[i]=="B":
self.data.append(self.decodeBit(bit))
self.totalNumbers+=1
lastB=i
if self.dataString[i] in ['B','?']:
bit=""
else:
bit+=self.dataString[i]
i+=1
self.dataString=self.dataString[lastB+1:]
if len(self.data)>self.dataToKeep:
self.data=self.data[-self.dataToKeep:]

def continuousAnalysis(self):
"""keep watching newAudio, and process it."""
while True:
while len(self.newAudio)< self.BUFFERSIZE:
time.sleep(.1)

analysisStart=time.time()

audio=self.newAudio

# TODO - insert previous audio sequence here

# GET Is where data is positive
Ipositive=numpy.nonzero(audio>0)[0]
diffs=numpy.ediff1d(Ipositive)
Idiffs=numpy.where(diffs>1)[0]
Icross=Ipositive[Idiffs]
pulses=diffs[Idiffs]

# remove some of the audio buffer, leaving the overhang

if len(Icross)>0:
processedThrough=Icross[-1]+diffs[Idiffs[-1]]
else:
processedThrough=len(audio)

self.lastAudio=self.newAudio[:processedThrough]
self.newAudio=self.newAudio[processedThrough:]

if False:
# chart audio data (use it to check algorythm)
pylab.plot(audio,'b')
pylab.axhline(0,color='k',ls=':')

for i in range(len(Icross)):
# plot each below-zero pulse whose length is measured
pylab.axvspan(Icross[i],Icross[i]+diffs[Idiffs[i]],
color='b',alpha=.2,lw=0)

# plot the hangover that will be carried to next chunk
pylab.axvspan(Icross[i]+diffs[Idiffs[i]],len(audio),
color='r',alpha=.2)
pylab.show()
return

# TODO - histogram of this point to assess quality
s=''
for pulse in pulses:
if (self.SIZE0-self.SIZEF)<pulse<(self.SIZE0+self.SIZEF):
s+="0"
elif (self.SIZE1-self.SIZEF)<pulse<(self.SIZE1+self.SIZEF):
s+="1"
elif (self.SIZE2-self.SIZEF)<pulse<(self.SIZE2+self.SIZEF):
s+="B"
else:
s+="?"

self.pulses=pulses
self.totalBits+=len(pulses)

print "[%.02f ms took %.02f ms] T: 0=%d 1=%d B=%d ?=%d"%(
len(audio)*1000.0/self.RATE,
time.time()-analysisStart,
s.count('0'),s.count('1'),s.count('B'),s.count('?'))

self.dataString+=s
self.analyzeDataString()

self.totalSamples+=self.BUFFERSIZE
self.totalTime=self.totalSamples/float(self.RATE)
self.totalBitRate=self.totalBits/self.totalTime
self.totalDataRate=self.totalNumbers/self.totalTime

self.nothingNewToShow=False

def continuousRecord(self):
maxSecBack=5
while len(self.newAudio)>(maxSecBack*self.RATE):
print "DELETING NEW AUDIO!"
self.newAudio=self.newAudio[self.BUFFERSIZE:]
audio=numpy.fromstring(audioString,dtype=numpy.int16)
self.newAudio=numpy.hstack((self.newAudio,audio))

def continuousDataGo(self):
self.t.start()
self.t2.start()

def continuousEnd(self):
"""shut down continuous recording."""

if __name__ == "__main__":
SHR=SwhRecorder()
SHR.SHRFP_decode=True
SHR.setup()
SHR.continuousDataGo()

#SHR.DataStart()

print "---DONE---"```

Finally, if you’re interested, here’s the full code (and demo audio WAV files):

If you use these concepts, hardware, or ideas in your project, let me know about it! Send me an email showing me your project – I’d love to see it. Good luck!

## I got everything running on Windows 7 x64 with the following:

Here’s the “hello world” of microchip programs (it simply blinks an LED). I’ll assume the audience of this page knows the basics of microcontroller programming, so I won’t go into the details. Just note that I’m using an ATMega48 and the LED is on pin 9 (PB6). This file is named “blink.c”.

```#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
DDRB = 255;
while(1)
{
PORTB ^= 255;
_delay_ms(500);
}
}```

Here’s how I compiled the code:

```avr-gcc -mmcu=atmega48 -Wall -Os -o blink.elf blink.c

In reality, it is useful to put these commands in a text file and call them “compile.bat”

Here’s how I program the AVR. I used AVRDudess! I’ve been using raw AVRDude for years. It’s a little rough around the edges, but this GUI interface is pretty convenient. I don’t even feel the need to include the command to program it from the command line! If I encourage nothing else by this post, I encourage (a) people to use and support AVRDudess, and (b) AVRDudess to continue developing itself as a product nearly all hobby AVR programmers will use. Thank you 21-year-old Zak Kemble.

And finally, the result. A blinking LED. Up and running programming AVR microcontrollers in 64-bit Windows 7 with an unofficial programmer, and never needing to install bloated AVR Studio software.

### AVR Programming in Linux

It is not difficult to program ATMEL AVR microcontrollers with linux, and I almost exclusively do this because various unofficial (inexpensive) USB AVR programmers are incompatible with modern versions of windows (namely Windows Vista and Windows 7). I am just now setting-up a new computer station in my electronics room (running Ubuntu Linux 12.04), and to make it easy for myself in the future I will document everything I do when I set-up a Linux computer to program microcontrollers.

Install necessary software

`sudo apt-get install gcc-avr avr-libc uisp avrdude`

Connect the AVR programmer
This should be intuitive for anyone who has programmed AVRs before. Visit the datasheet of your MCU, identify pins for VCC (+), GND (-), MOSI, MISO, SCK, and RESET, then connect them to the appropriate pins of your programmer.

Write a simple program in C
I made a file “main.c” and put the following inside. It’s the simplest-case code necessary to make every pin on PORTD (PD0, PD1, …, PD7) turn on and off repeatedly, sufficient to blink an LED.

```#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
DDRD = 255; // MAKE ALL PORT D PINS OUTPUTS

while(1) {
PORTD = 255;_delay_ms(100); // LED ON
PORTD = 0;  _delay_ms(100); // LED OFF
}

return 0;
}
```

Compile the code (generate a HEX file)

```avr-gcc -w -Os -DF_CPU=2000000UL -mmcu=atmega8 -c -o main.o main.c
avr-gcc -w -mmcu=atmega8 main.o -o main
avr-objcopy -O ihex -R .eeprom main main.hex
```

note that the arguments define CPU speed and chip model – this will need to be customized for your application

Program the HEX firmware onto the AVR

```sudo avrdude -F -V -c avrispmkII -p ATmega8 -P usb -U flash:w:main.hex
```

note that this line us customized based on my connection (-P flag, USB in my case) and programmer type (-c flag, AVR ISP mkII in my case)

When this is run, you will see something like this:

```
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9307
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (94 bytes):

Writing | ################################################## | 100% 0.04s

avrdude: 94 bytes of flash written
```

TIP 1: I’m using a clone AVRISP mkII from Fun4DIY.com which is only \$11 (shipped!). I glued it to a perf-board with a socket so I can jumper its control pins to any DIP AVR (80 pins or less). I also glued a breadboard for my convenience, and added LEDs (with ground on one of their pins) for easy jumpering to test programs. You can also build your own USB AVR ISP (free schematics and source code) from the USBtinyISP project website.

TIP 2: Make a shell script to run your compiling / flashing commands with a single command. Name them according to architecture. i.e., “build-m8” or “build-t2313”. Make the first line delete all .hex files in the directory, so it stalls before it loads old .hex files if the compile is unsuccessful. Make similar shell scripts to program fuses. i.e., “fuse-m8-IntClock-8mhz”, “fuse-m8-IntClock-1mhz”, “fuse-m8-ExtCrystal”.

TIP 3: Use a nice text editor well suited for programming. I love Geany.

### Geek Spin – ATTiny44 Project Prototype

Some days you feel like working on projects to benefit humanity. The day I made this clearly wasn’t one of those days. A little over a year ago, I got into a troll war with my friend Mike Seese. The joke, similar to that of rick rolling, was to get each other to unexpectedly click a link to the Hatsune Miku version of the leekspin song. After several weeks of becoming beyond annoying, I decided to make an actual Hatsune Miku which would spin her leek and bobble her head to the techno version of the Levan Polka for his birthday.

The goal was to create a minature Miku which would perform perfectly in sync with audio coming from a portable music player (iPod or something) and NOT require a computer connection. I accomplished it by sending some creative control beeps out of the left channel of the stereo signal. Although I didn’t finish the project, I got pretty far with the prototype, so I decided to dig it out of the archives and share it with the world because it’s pretty entertaining!

(look how close I came to replicating the original!)

How did I do it? First off, I used servos. If you’re not familiar with them, I suggest you look up how servos work. Perhaps check out how to control servos with AVR microcontrollers. Basically, their position along a rotational axis is determined by the width of a pulse on a 20ms time window. Anyhow, if I only had 1 servo to control (i.e., leek only), I’d have controlled the servo directly with PWM signals in the left channel – no microcontroller needed, easy as pie, problem solved. However, since I needed to control two servos, I had to come up with something a bit more creative. Although I could have probably done this ten different ways, the way I chose to do it was using a series of pre-encoded leek spin and head bobble motions, triggered by control beeps in the left channel of the audio cable. (The right channel was patched through to the speakers.) Below is a diagram of what I believe I did, although I didn’t thoroughly document it at the time, so you might have to use your imagination if you decide to re-create this project.

The idea is that by sending bursts of sine waves, the circuit can rectify them and smooth them out to have them look to a microcontroller like a brief “high” signal. Each signal would tell the microcontroller to proceed to the next pre-programmed (and carefully timed) routine. With enough practice listening, watching, and tweaking the code, I was able to make a final version which worked pretty darn well!

LISTEN TO:MUSIC WITH CONTROL BEEPS (it’s a surprisingly fun listen)

A few technical details are that I used an ATTiny44a microcontroller (it may have been an ATTiny2313, I can’t remember for sure, but they’re so similar it’s virtually negligable). The servos I used were cheap (maybe \$4?) from eBay. They looked like the one pictured below. The servo position was controlled by PWM, but I manually sent the pulses and didn’t actually use the integrated PWM in the microcontroller. I can’t remember why I did it this way – perhaps because it was so simple to use the _delay_us() and _delay_ms() functions? I also used an operational amplifier (if I remember, it was a LM741) to boost the left channel control signals rather than rectifying/assessing the left channel directly.

This is the video which I mimiced to create my prototype (note how the leek in her arm and her head move exactly the same as the prototype I made – score!)

For good measure, here’s the original song:

And how did I find out about this song? I actually saw it on the video below which was hosted on wimp.com. I thought the song was catchy, looked it up, and the rest was history. It’s worth noting that (perhaps to avoid copyright issues?) the key was shifted two half-steps up. I get a kick out of the way the girl waves her arm in the beginning, mimicking the leek 🙂

Here are some of the images I made which I printed, glued to foam board, and cut out with a razor blade. I’m not sure how useful they are, but they’re provided just in case.

… but sometimes Japan takes it a bit too far and things get awkward …

Below is the code I used. Note that PWM that controls the servos isn’t the integrated PWM, but rather a couple pins I manually pulse on and off to control the arm and head positions. Also notice how, in the main routine, I wait for the control beeps before continuing the next sequences.

```// leek spin code - designed for ATTiny
// by Scott Harden, www.SWHarden.com

#include <avr/io.h>
#include <avr/delay.h>

void go_high(){
// sets the arm to the highest position
for (char i=0;i<5;i++){
PORTA|=(1<<PA0);
_delay_us(1400);
PORTA&=~(1<<PA0);
_delay_us(20000-1200);
}
}

void go_low(){
// sets the leek to the middle position
for (char i=0;i<5;i++){
PORTA|=(1<<PA0);
_delay_us(1900);
PORTA&=~(1<<PA0);
_delay_us(20000-1900);
}
}

void go_lowest(){
// sets the leek to the lowest position
for (char i=0;i<5;i++){ // takes 100ms total
PORTA|=(1<<PA0);
_delay_us(2300);
PORTA&=~(1<<PA0);
_delay_us(20000-2500);
}
}

void go_slow(char times){
// does one slow leek down/up
// beat is 500ms
for (char i=0;i<times;i++){
go_low();
_delay_ms(10);
go_high();
_delay_ms(290);
PORTA^=(1<<PA2);
PORTA^=(1<<PA3);
}
}

void go_fast(char times){
// does one fast leek down/up
// beat is 250ms
for (char i=0;i<times;i++){
go_low();
_delay_ms(10);
go_high();
_delay_ms(15);
PORTA^=(1<<PA2);
PORTA^=(1<<PA3);
}
}
// tilts the head to the left
for (char i=0;i<5;i++){
PORTA|=(1<<PA1);
_delay_us(1330);
PORTA&=~(1<<PA1);
_delay_us(20000-1200);
}
}

// tilts the head to the right
for (char i=0;i<5;i++){
PORTA|=(1<<PA1);
_delay_us(1500);
PORTA&=~(1<<PA1);
_delay_us(20000-1200);
}
}

for (char i=0;i<5;i++){
PORTA|=(1<<PA1);
_delay_us(1400);
PORTA&=~(1<<PA1);
_delay_us(20000-1200);
}
}

// rocks the head back and forth once
for (char i=0;i<(times-1);i++){
_delay_ms(400);
PORTA^=(1<<PA2);
PORTA^=(1<<PA3);
_delay_ms(400);
PORTA^=(1<<PA2);
PORTA^=(1<<PA3);
}
_delay_ms(400);
PORTA^=(1<<PA2);
PORTA^=(1<<PA3);
}

int main(void) {
while (1){
DDRA=255; // set port A (servos) as outputs
DDRB=0; // set port B (listening pins) as inputs

while ((PINB & _BV(PB0))){} // wait for beep que
PORTA=(1<<PA3);
go_high();_delay_ms(1000);
while ((PINB & _BV(PB0))){} // wait for beep que
go_slow(31); // tilt leek slowly 31 times
while ((PINB & _BV(PB0))){} // wait for beep que
go_slow(31); // tilt leek slowly 31 times

while ((PINB & _BV(PB0))){} // wait for beep que
_delay_ms(200);
while ((PINB & _BV(PB0))){} // wait for beep que
go_fast(68); // tilt leek rapidly 68 times
while ((PINB & _BV(PB0))){} // wait for beep que
go_slow(24); // tilt leek slowly 24 times
while ((PINB & _BV(PB0))){} // wait for beep que
go_fast(17); // tilt leek rapidly 17 times
while ((PINB & _BV(PB0))){} // wait for beep que
go_slow(31); // tilt leek slowly 31 times
while ((PINB & _BV(PB0))){} // wait for beep que
go_slow(31); // tilt leek slowly 31 times

while ((PINB & _BV(PB0))){} // wait for beep que
_delay_ms(200);