The personal website of Scott W Harden
July 9th, 2011

Sound Card Microcontroller/PC Communication

_This page describes a method of sending data from a microchip to a PC using pulses of data. It's an alternative to more traditional serial or USB methods of connectivity. It's not intended as a solution for consumer products, but rather an easy hack for hobbyists to employ if they don't have the equipment for other methods. This method doesn't require any circuitry, just a sound card. The one built in your computer is fine, but I'm using a $1.30 USB sound card for simplicity. It boils down to just a single microcontroller pin connected to a PC sound card microphone jack!

MY PROBLEM: I want to send data from a simple microcontroller to a PC. While USART and a serial port is the common solution like I've done before, it's not convenient because it requires a level converter (like a MAX232, about $4), crystal (specific values based on bit and error rate, if you're lucky you might have a right value in your junk box), and an archaic PC which actually has a serial port. A usb serial port adapter sounds clever, but many aren't supported on Linux, Windows Vista, or Windows 7. Also, many small chips (most of the ATTiny series) don't have built in serial capabilities, so it has to be bit-banged in software! Yuk! The second choice would be USB. This requires a crystal too, zener diodes, and bit-banging the USB protocol with something like V-USB since most of the AVR series don't have built in USB (do they even make breadbordable DIP chips with USB?). Even so, it requires drivers, custom software, cross-platform frustrations, etc. I know PIC has some 18f series chips with USB, but I don't feel like switching architectures just to send a few bytes of data to a PC. FDTI has a FT232R chip which is a USB serial port adapter, but it's expensive (about $5) and doesn't come in dip, so no breadboarding! Sure there are adapter boards, but that just adds the cost. I'm not excited about a $5 solution for a $1 microcontroller. I even did a bit of trolling on AVR Freaks to see if anyone could help me out - just more of the same!

MY SOLUTION: Send data through the sound card! USB sound cards are $1.30 (shipped) on eBay! It couldn't be simpler. Send pulses, measure distance between pulses. Short pulses are a zero, longer ones are a 1, and very long pulses are number separators. A Python solution with PyAudio allows 1 script which will work on Mac, Linux, Windows, etc, and because it calibrates itself, this will work on any chip at any clock rate. Data is initiated with calibration pulses so timing is not critical - the PC figures out how fast the data is coming in. Check it out! (scroll way down for a bidirectional communication solution)

Here is a sound card I used for bidirectional communication:

Output graph (python and excel) of temperature when I put a soldering iron near the sensor:

UNIDIRECTIONAL SOLUTION

The following code is designed to have a chip send data to your PC automatically. This can be run on any micro-controller (PIC or AVR I guess, the concept is the same) at any clock rate. Just make sure the sound card is recording fast enough to differentiate pulses. (keep scrolling down for a bidirectional solution)

A NOTE ABOUT MY CODE: This is just the code I used for my demonstration. It might be more logical for you to write your own since the concept is so simple. I'm a dental student, not a programmer, so I'm sure it's not coded very elegantly. I didn't work hard to make this code easy to read or easy to share. With that being said, help yourself!

/*The following code is written in AVR-GCC for an ATTiny44a.
It reads ADC values on 3 pins and reports it each second along
 with a number which increments each time data is sent.
It's designed as a starting point, allowing anyone to
customize it from here!*/

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

// bytes we want to send to the PC
volatile int data1=0;
volatile int data2=0;
volatile int data3=0;
volatile int data4=0;

void solid(){  // dont touch
    _delay_ms(1);
    pulse(1);pulse(1);pulse(1);pulse(3);pulse(3);
    pulse(3);pulse(5);pulse(5);// CALIBRATION PULSES
}
void pulse(char size){ // dont touch
    PORTA|=_BV(PA3);
    _delay_us(100);
    PORTA&=~_BV(PA3);
    while (size){size--;_delay_us(100);}
}
void sendVal(unsigned long tosend){ // dont touch
    pulse(5); // send a space
    while (tosend){
        if (tosend&1){pulse(3);} // send ONE
        else {pulse(1);} // send ZERO
        tosend=tosend>>1;
    }
}

int readADC(char adcNum){
    _delay_ms(1);
    ADMUX=adcNum; // select which ADC to read, VCC as ref.
    ADCSRA=0b11000111; // enable, start, 128 prescale
    while (ADCSRA&( 1<<ADSC)) {}; // wait for measurement
    return ADC;
}

void takeReadings(){
        data1=readADC(0); // ADC0
        data2=readADC(1); // ADC1
        data3=readADC(2); // ADC2
        data4++; // incriment just because we want to
}

void sendStuff(){ // EDIT to send what you want
    solid(); //required
    sendVal(12345); //required
    sendVal(12345); //required
    sendVal(54321); //required

    sendVal(data1);
    sendVal(data2);
    sendVal(data3);
    sendVal(data4);

    pulse(1); //required
}

int main(){
    DDRA|=_BV(PA2)|_BV(PA3);
    for (;;){
        _delay_ms(1000);
        takeReadings();
        sendStuff();
    }
    return 0;
}
"""
file name: listenOnly.py

This is the PC code to listen to the microphone and display
and log the data. It probably does NOT need adjustment!
 Make sure the correct sound card is selected (in the code)
 and make sure microphone input is turned up in volume control.

This code is what was used on my PC for the demonstration
video. This is the listenOnly.py file which will turn any audio
 detected from a sound card into data, optionally logging it
(if the last few lines are uncommented). This also works to
capture data for the bidirectional communication method,
described below on this website.

If this is running but no data is coming through, make sure the
microphone is selected as a recording device, the correct sound
card is selected, and the microphone volume is turned to high.

REQUIRED: To run this, you need to have the following installed:
-- Python 2.6
-- numpy for python 2.6
-- matplotlib for python 2.6
-- pyaudio for python 2.6
(other versions may work, but this is what I'm using)
"""
import numpy
import pyaudio
import matplotlib.pyplot as plt
import wave
import time

def listCards(dontAsk=True):
    p=pyaudio.PyAudio()
    print "SOUND CARDS:"
    for i in range(p.get_default_host_api_info()["deviceCount"]):
        if p.get_device_info_by_index(i)["maxInputChannels"]>0:
                cardName = p.get_device_info_by_index(i)["name"]
                cardIndex = p.get_device_info_by_index(i)["index"]
                print "[%d] %s"%(cardIndex,cardName)
    if dontAsk: return
    return int(raw_input("CARD NUMBER TO USE:"))

cardID=1
listCards()
print "USING CARD:",cardID

rate=44100.0
sampleSize=1024

def data2vals(data):
    vals=numpy.array([])
    lastPeak=0
    for i in range(1,len(data)):
        if data[i]==True and data[i-1]==False:
            if lastPeak>0: vals=numpy.append(vals,i-lastPeak)
            lastPeak=i
    return vals

def binary2dec(binary):
    binary=binary[:-1]
    dec=0
    s=""
    for i in range(len(binary)):
        dec=dec*2
        dec+=binary[i]
        s="%d"%binary[i]+s
    #print s,"=",dec #11111100101100000 = 3391
    return dec

def readVals(vals):
    if len(vals)<7: return False
    vals2=[]
    aLow = min(vals[0:3])
    aMed = min(vals[3:6])
    aHigh = vals[6]
    thresh1=sum([aLow,aMed])/2+2
    thresh2=sum([aMed,aHigh])/2+2
    #print "tresholds:",thresh1,thresh2
    #print vals
    vals=vals[8:]
    binary=[]
    for i in range(len(vals)):
        if vals[i]>thresh2:
            vals2.append(binary2dec(binary))
            binary=[]
        if vals[i]>thresh1:binary=[1]+binary
        else:binary=[0]+binary
    vals2.append(binary2dec(binary))
    for i in range(len(vals2)):
        if vals2[i]==54321: return vals2[i+1:]
    return False

def playFile():
    chunk = 1024
    wf = wave.open("short_onenum.wav", 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format =
                    p.get_format_from_width(wf.getsampwidth()),
                    channels = wf.getnchannels(),
                    rate = wf.getframerate(),
                    output = True)
    data = wf.readframes(chunk)
    while data != '':
        stream.write(data)
        data = wf.readframes(chunk)
    stream.close()

def captureData():
    pyaud = pyaudio.PyAudio()
    stream = pyaud.open(format=pyaudio.paInt16,channels=1,
        rate = 44100,input_device_index=cardID,input=True,output=True)
    sample=numpy.array([])
    while True:
        sampleNew=numpy.fromstring(stream.read(sampleSize),dtype=numpy.int16)
        sampleNew=(sampleNew<-25000)*1
        if True in sampleNew: sample=numpy.append(sample,sampleNew)
        else:
            if len(sample):
                stream.close()
                return sample
    stream.close()

tone_quiet=0

def buildNumber(num=123):

    if num>255: print "NUMBER TOO HIGH!!!"
    #print num,'=',
    num+=1
    for i in [7,6,5,4,3,2,1,0]:
        if num>2**i:one();num=num-2**i;#print"1",
        else: zero();#print"0",
    #print
    space()

def pulse():
    global data
    data+=[-30000]*10

def space():
    global data
    data+=[tone_quiet]*900
    pulse()

def one():
    global data
    data+=[tone_quiet]*600
    pulse()

def zero():
    global data
    data+=[tone_quiet]*300
    pulse()

def silence(msec=1000):
    global data
    data+=[tone_quiet]*int(41.1*msec)

data=[]
def sendAudio(numbers=[11,66,77]):
    global data
    data=[]
    silence(100)
    buildNumber(250)
    print "SENDING",
    for numba in numbers:
        buildNumber(numba)
        print numba,
    buildNumber(250)
    silence(100)
    data=numpy.array(data)
    data=-data
    data=data.tostring()
    print

    p = pyaudio.PyAudio()
    stream = p.open(rate=44100, channels=1, format=pyaudio.paInt16,
                    input_device_index=cardID, output=True)
    stream.write(data)
    stream.close()
    p.terminate()

i=0
while True:
    i+=1
    val=readVals(data2vals(captureData()))
    if val == False: continue
    line=""
    for item in val: line+=str(item)+","
    print i,line
    #f=open('log.csv','a')
    #f.write("%sn"%line)
    #f.close()

BIDIRECTIONAL SOLUTION

What if we want to send data TO the microcontroller? The solution is a little more complex, but quite doable. Just add an extra wire to the sound card's speaker output and attach it to PCINT0 (the highest level internal interrupt). This is intended for advanced users, and if you're doing this you probably are better off with USB or serial anyway! ... but heck, why not do it as a proof of concept!

Note that the USB sound card speaker output was not powerful enough to trigger the digital input pin of the AVR, so an inverting buffer was made from a single NPN transistor (2n3904). The hardware interrupt was attacked to the collector, and the collector was attached through +5V through a 220 ohm resistor. The emitter was grounded. The base was attached directly to the sound card output. I also tried running the sound card output through a small series capacitor (0.1uF) and biasing the base to ground through a 1Mohm resistor and it worked the same. Hardware, simple. Chip-side software... a little more complex.

"""
This code is what was used on my PC for the
 demonstration video. The listenonly.py file
 (above on site) was also used without modification.
"""
import pyaudio
from struct import pack
from math import sin, pi
import wave
import random
import numpy
import time

RATE=44100
maxVol=2**15-1.0 #maximum amplitude
p = pyaudio.PyAudio()
stream = p.open(rate=44100, channels=1, format=pyaudio.paInt16,
        input_device_index=1, output=True)

def pulseZero():
    global wvData
    wvData+=pack('h', 0)*30
    wvData+=pack('h', maxVol)

def pulseOne():
    global wvData
    wvData+=pack('h', 0)*40
    wvData+=pack('h', maxVol)

def pulseSpace():
    global wvData
    wvData+=pack('h', 0)*50
    wvData+=pack('h', maxVol)

def buildNumber(num=123):
    if num>255: print "NUMBER TOO HIGH!!!"
    num+=1
    for i in [7,6,5,4,3,2,1,0]:
        if num>2**i:
            pulseOne()
            num=num-2**i
        else:
            pulseZero()

wvData=""
wvData+=pack('h', 0)*2000
pulseOne() #required before sending data

buildNumber(55)
buildNumber(66)
buildNumber(77)
buildNumber(123)

wvData+=pack('h', 0)*2000

while True:
    print "SENDING",
    stream.write(wvData)
    raw_input()
/*
This code is what was used on my AVR
microcontroller for the demonstration video
*/
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

volatile long commandIncoming=0;
volatile char command1=0;
volatile char command2=0;
volatile char command3=0;
volatile char command4=0;
volatile char bitsGotten=0;

// timing thresholds are critical! Send pulses to the chip
// and have it report the time between them. Use this to
// determine the best threshold value for your application.
// The ones here must be changed if you run at a speed other
// than 1mhz or if you use different timings in PC software
#define thresh_low 100 // between this and the next
#define thresh_high 130 // is the range for a logical 'one'

// ######## OUTGOING AUDIO DATA #########
void solid(){
    _delay_ms(1); //LONG LOW
    pulse(1);pulse(1);pulse(1);pulse(3);pulse(3);
    pulse(3);pulse(5);pulse(5);// CALIBRATION PULSES
}
void pulse(char size){
    PORTA|=_BV(PA3);
    _delay_us(100);
    PORTA&=~_BV(PA3);
    while (size){size--;_delay_us(100);}
}
void sendVal(unsigned long tosend){
    pulse(5); // send a space
    while (tosend){
        if (tosend&1){pulse(3);} // send ONE
        else {pulse(1);} // send ZERO
        tosend=tosend>>1;
    }
}

// ######## INCOMING AUDIO DATA #########
// NOTE THAT INPUTS ARE NORMALLY *HIGH* AND DROP *LOW* FOR SIGNAL
SIGNAL (PCINT0_vect) { // audio input trigger
    TIMSK0|=(1<<TOIE1); //Overflow Interrupt Enable
    if (TCNT0<10){return;} // seem too fast? ignore it!
    // Enable the following line to test custom timings
    //command1=command2;command2=command3;
    //command3=command4;command4=TCNT0;
    bitsGotten++;
    commandIncoming=commandIncoming*2; // shift left
    if (TCNT0>thresh_low){commandIncoming++;} // make 1
    TCNT0=0;
}

ISR(TIM0_OVF_vect){ // TIMER OVERFLOW
    if (bitsGotten){sendStuff();}
}

void fillCommands(){
    command1=(char*)(commandIncoming>>24);
    command2=(char*)(commandIncoming>>16);
    command3=(char*)(commandIncoming>>8);
    command4=(char*)(commandIncoming);
}

void sendStuff(){
    TIMSK0=0; //Overflow Interrupt
    cli(); // disable interrupts!
    fillCommands();
    solid(); // start data transmissions with this
    sendVal(12345);
    sendVal(12345);
    sendVal(54321);
    sendVal(command1);
    sendVal(command2);
    sendVal(command3);
    sendVal(command4);
    sendVal(1234567890);
    pulse(1);
    bitsGotten=0;
    sei(); // enable interrupts again!
    TIMSK0|=(1<<TOIE1); //Overflow Interrupt
}

// ######## MAIN PROGRAM #########
int main(){

    DDRA|=_BV(PA2)|_BV(PA3);

    // SET UP FOR SOUND CARD INTERRUPT
    MCUCR = 0b00000010; // trigger interrupt on falling edge
    GIMSK = 0b00010000; // pin change interrupt enable 0
    GIFR =  0b00010000; // flag register, same as above
    PCMSK0 = (1<<PCINT0); // Set Pin to use (PCINT0)
    sei(); // enable global interrupts

    // SET UP 8-bit COUNTER
    TCCR0B|=0b00000010;
    //TCCR1B|=(1<<CS12)|(1<<CS10); // prescaler 1024
    TIMSK0|=(1<<TOIE1); //Enable Overflow Interrupt Enable
    TCNT0=0;//Initialize our varriable (set for 1/15th second?)

    // MAIN PROGRAM
    for (;;){}
    return 0;

}

In closing, I'm tickled this works so well. It's funny to me that no one's really done this before in the hobby field. I'm sure I'm not the only one who wished there were an easy way to do this. I'm sure the process could be greatly improved, but this is a fun start. Wow, it's late, I should get to bed. I have to treat patients tomorrow morning!

PS: If you replicate this concept, let me know about it! I'd love to see your project!

UPDATE: This story was featured on this post of HackADay.com!

Markdown source code last modified on January 18th, 2021
---
title: Sound Card Microcontroller/PC Communication
date: 2011-07-09 23:30:44
tags: microcontroller, circuit, old
---

# Sound Card Microcontroller/PC Communication

_This page describes a method of sending data from a microchip to a PC using pulses of data. It's an alternative to more traditional serial or USB methods of connectivity. It's not intended as a solution for consumer products, but rather an easy hack for hobbyists to employ if they don't have the equipment for other methods. This method doesn't require any circuitry, just a sound card. The one built in your computer is fine, but I'm using a $1.30 USB sound card for simplicity. It boils down to just a single microcontroller pin connected to a PC sound card microphone jack!

<div class="text-center img-border">

![](DSCN1532.jpg)

</div>

__MY PROBLEM:__ I want to send data from a simple microcontroller to a PC. While USART and a serial port is the common solution [like I've done before](http://www.swharden.com/blog/2009-05-14-simple-case-avrpc-serial-communication-via-max232/), it's not convenient because it requires a level converter (like a MAX232, about $4), crystal (specific values based on bit and error rate, if you're lucky you might have a right value in your junk box), and an archaic PC which actually has a serial port. A usb serial port adapter sounds clever, but many aren't supported on Linux, Windows Vista, or Windows 7. Also, many small chips (most of the ATTiny series) don't have built in serial capabilities, so it has to be bit-banged in software! Yuk! The second choice would be USB. This requires a crystal too, zener diodes, and bit-banging the USB protocol with something like [V-USB](http://www.obdev.at/products/vusb/index.html) since most of the AVR series don't have built in USB (do they even make breadbordable DIP chips with USB?). Even so, it requires drivers, custom software, cross-platform frustrations, etc. I know PIC has some 18f series chips with USB, but I don't feel like switching architectures just to send a few bytes of data to a PC. FDTI has a [FT232R](http://www.ftdichip.com/Products/ICs/FT232R.htm) chip which is a USB serial port adapter, but it's expensive (about $5) and doesn't come in dip, so no breadboarding! Sure there are adapter boards, but that just adds the cost. I'm not excited about a $5 solution for a $1 microcontroller. I even did [a bit of trolling on AVR Freaks](http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=109298) to see if anyone could help me out - just more of the same!

![](https://www.youtube.com/embed/I0UEooQH2bw)

__MY SOLUTION:__ Send data through the sound card! USB sound cards are $1.30 (shipped) on eBay! It couldn't be simpler. Send pulses, measure distance between pulses. Short pulses are a zero, longer ones are a 1, and very long pulses are number separators. __A Python solution with PyAudio allows 1 script which will work on Mac, Linux, Windows, etc, and because it calibrates itself, this will work on any chip at any clock rate.__ Data is initiated with calibration pulses so timing is not critical - the PC figures out how fast the data is coming in. Check it out! (scroll way down for a bidirectional communication solution)

![](https://www.youtube.com/embed/WKp0P43uhzY)

Here is a sound card I used for bidirectional communication:

<div class="text-center img-border">

![](DSCN1466.jpg)
![](DSCN1470.jpg)

</div>

Output graph (python and excel) of temperature when I put a soldering iron near the sensor: 

<div class="text-center img-border">

[![](python1_thumb.jpg)](python1.png)
[![](excel_thumb.jpg)](excel.jpg)

</div>

## UNIDIRECTIONAL SOLUTION

__The following code is designed to have a chip send data to your PC automatically.__ This can be run on any micro-controller (PIC or AVR I guess, the concept is the same) at any clock rate. Just make sure the sound card is recording fast enough to differentiate pulses. (keep scrolling down for a bidirectional solution)

__A NOTE ABOUT MY CODE:__ This is just the code I used for my demonstration. It might be more logical for you to write your own since the concept is so simple. I'm a dental student, not a programmer, so I'm sure it's not coded very elegantly. I didn't work hard to make this code easy to read or easy to share. With that being said, help yourself!

```c
/*The following code is written in AVR-GCC for an ATTiny44a.
It reads ADC values on 3 pins and reports it each second along
 with a number which increments each time data is sent.
It's designed as a starting point, allowing anyone to
customize it from here!*/

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

// bytes we want to send to the PC
volatile int data1=0;
volatile int data2=0;
volatile int data3=0;
volatile int data4=0;

void solid(){  // dont touch
    _delay_ms(1);
    pulse(1);pulse(1);pulse(1);pulse(3);pulse(3);
    pulse(3);pulse(5);pulse(5);// CALIBRATION PULSES
}
void pulse(char size){ // dont touch
    PORTA|=_BV(PA3);
    _delay_us(100);
    PORTA&=~_BV(PA3);
    while (size){size--;_delay_us(100);}
}
void sendVal(unsigned long tosend){ // dont touch
    pulse(5); // send a space
    while (tosend){
        if (tosend&1){pulse(3);} // send ONE
        else {pulse(1);} // send ZERO
        tosend=tosend>>1;
    }
}

int readADC(char adcNum){
    _delay_ms(1);
    ADMUX=adcNum; // select which ADC to read, VCC as ref.
    ADCSRA=0b11000111; // enable, start, 128 prescale
    while (ADCSRA&( 1<<ADSC)) {}; // wait for measurement
    return ADC;
}

void takeReadings(){
        data1=readADC(0); // ADC0
        data2=readADC(1); // ADC1
        data3=readADC(2); // ADC2
        data4++; // incriment just because we want to
}

void sendStuff(){ // EDIT to send what you want
    solid(); //required
    sendVal(12345); //required
    sendVal(12345); //required
    sendVal(54321); //required

    sendVal(data1);
    sendVal(data2);
    sendVal(data3);
    sendVal(data4);

    pulse(1); //required
}

int main(){
    DDRA|=_BV(PA2)|_BV(PA3);
    for (;;){
        _delay_ms(1000);
        takeReadings();
        sendStuff();
    }
    return 0;
}
```

```python
"""
file name: listenOnly.py

This is the PC code to listen to the microphone and display
and log the data. It probably does NOT need adjustment!
 Make sure the correct sound card is selected (in the code)
 and make sure microphone input is turned up in volume control.

This code is what was used on my PC for the demonstration
video. This is the listenOnly.py file which will turn any audio
 detected from a sound card into data, optionally logging it
(if the last few lines are uncommented). This also works to
capture data for the bidirectional communication method,
described below on this website.

If this is running but no data is coming through, make sure the
microphone is selected as a recording device, the correct sound
card is selected, and the microphone volume is turned to high.

REQUIRED: To run this, you need to have the following installed:
-- Python 2.6
-- numpy for python 2.6
-- matplotlib for python 2.6
-- pyaudio for python 2.6
(other versions may work, but this is what I'm using)
"""
import numpy
import pyaudio
import matplotlib.pyplot as plt
import wave
import time

def listCards(dontAsk=True):
    p=pyaudio.PyAudio()
    print "SOUND CARDS:"
    for i in range(p.get_default_host_api_info()["deviceCount"]):
        if p.get_device_info_by_index(i)["maxInputChannels"]>0:
                cardName = p.get_device_info_by_index(i)["name"]
                cardIndex = p.get_device_info_by_index(i)["index"]
                print "[%d] %s"%(cardIndex,cardName)
    if dontAsk: return
    return int(raw_input("CARD NUMBER TO USE:"))

cardID=1
listCards()
print "USING CARD:",cardID

rate=44100.0
sampleSize=1024

def data2vals(data):
    vals=numpy.array([])
    lastPeak=0
    for i in range(1,len(data)):
        if data[i]==True and data[i-1]==False:
            if lastPeak>0: vals=numpy.append(vals,i-lastPeak)
            lastPeak=i
    return vals

def binary2dec(binary):
    binary=binary[:-1]
    dec=0
    s=""
    for i in range(len(binary)):
        dec=dec*2
        dec+=binary[i]
        s="%d"%binary[i]+s
    #print s,"=",dec #11111100101100000 = 3391
    return dec

def readVals(vals):
    if len(vals)<7: return False
    vals2=[]
    aLow = min(vals[0:3])
    aMed = min(vals[3:6])
    aHigh = vals[6]
    thresh1=sum([aLow,aMed])/2+2
    thresh2=sum([aMed,aHigh])/2+2
    #print "tresholds:",thresh1,thresh2
    #print vals
    vals=vals[8:]
    binary=[]
    for i in range(len(vals)):
        if vals[i]>thresh2:
            vals2.append(binary2dec(binary))
            binary=[]
        if vals[i]>thresh1:binary=[1]+binary
        else:binary=[0]+binary
    vals2.append(binary2dec(binary))
    for i in range(len(vals2)):
        if vals2[i]==54321: return vals2[i+1:]
    return False

def playFile():
    chunk = 1024
    wf = wave.open("short_onenum.wav", 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format =
                    p.get_format_from_width(wf.getsampwidth()),
                    channels = wf.getnchannels(),
                    rate = wf.getframerate(),
                    output = True)
    data = wf.readframes(chunk)
    while data != '':
        stream.write(data)
        data = wf.readframes(chunk)
    stream.close()

def captureData():
    pyaud = pyaudio.PyAudio()
    stream = pyaud.open(format=pyaudio.paInt16,channels=1,
        rate = 44100,input_device_index=cardID,input=True,output=True)
    sample=numpy.array([])
    while True:
        sampleNew=numpy.fromstring(stream.read(sampleSize),dtype=numpy.int16)
        sampleNew=(sampleNew<-25000)*1
        if True in sampleNew: sample=numpy.append(sample,sampleNew)
        else:
            if len(sample):
                stream.close()
                return sample
    stream.close()

tone_quiet=0

def buildNumber(num=123):

    if num>255: print "NUMBER TOO HIGH!!!"
    #print num,'=',
    num+=1
    for i in [7,6,5,4,3,2,1,0]:
        if num>2**i:one();num=num-2**i;#print"1",
        else: zero();#print"0",
    #print
    space()

def pulse():
    global data
    data+=[-30000]*10

def space():
    global data
    data+=[tone_quiet]*900
    pulse()

def one():
    global data
    data+=[tone_quiet]*600
    pulse()

def zero():
    global data
    data+=[tone_quiet]*300
    pulse()

def silence(msec=1000):
    global data
    data+=[tone_quiet]*int(41.1*msec)

data=[]
def sendAudio(numbers=[11,66,77]):
    global data
    data=[]
    silence(100)
    buildNumber(250)
    print "SENDING",
    for numba in numbers:
        buildNumber(numba)
        print numba,
    buildNumber(250)
    silence(100)
    data=numpy.array(data)
    data=-data
    data=data.tostring()
    print

    p = pyaudio.PyAudio()
    stream = p.open(rate=44100, channels=1, format=pyaudio.paInt16,
                    input_device_index=cardID, output=True)
    stream.write(data)
    stream.close()
    p.terminate()

i=0
while True:
    i+=1
    val=readVals(data2vals(captureData()))
    if val == False: continue
    line=""
    for item in val: line+=str(item)+","
    print i,line
    #f=open('log.csv','a')
    #f.write("%sn"%line)
    #f.close()
```

## BIDIRECTIONAL SOLUTION

__What if we want to send data TO the microcontroller?__ The solution is a little more complex, but quite doable. Just add an extra wire to the sound card's speaker output and attach it to PCINT0 (the highest level internal interrupt). This is intended for advanced users, and if you're doing this you probably are better off with USB or serial anyway! ... but heck, why not do it as a proof of concept!

![](https://www.youtube.com/embed/fhsYGRdwIaw)

Note that the USB sound card speaker output was not powerful enough to trigger the digital input pin of the AVR, so an inverting buffer was made from a single NPN transistor (2n3904). The hardware interrupt was attacked to the collector, and the collector was attached through +5V through a 220 ohm resistor. The emitter was grounded. The base was attached directly to the sound card output. I also tried running the sound card output through a small series capacitor (0.1uF) and biasing the base to ground through a 1Mohm resistor and it worked the same. Hardware, simple. Chip-side software... a little more complex.

```python
"""
This code is what was used on my PC for the
 demonstration video. The listenonly.py file
 (above on site) was also used without modification.
"""
import pyaudio
from struct import pack
from math import sin, pi
import wave
import random
import numpy
import time

RATE=44100
maxVol=2**15-1.0 #maximum amplitude
p = pyaudio.PyAudio()
stream = p.open(rate=44100, channels=1, format=pyaudio.paInt16,
        input_device_index=1, output=True)

def pulseZero():
    global wvData
    wvData+=pack('h', 0)*30
    wvData+=pack('h', maxVol)

def pulseOne():
    global wvData
    wvData+=pack('h', 0)*40
    wvData+=pack('h', maxVol)

def pulseSpace():
    global wvData
    wvData+=pack('h', 0)*50
    wvData+=pack('h', maxVol)

def buildNumber(num=123):
    if num>255: print "NUMBER TOO HIGH!!!"
    num+=1
    for i in [7,6,5,4,3,2,1,0]:
        if num>2**i:
            pulseOne()
            num=num-2**i
        else:
            pulseZero()

wvData=""
wvData+=pack('h', 0)*2000
pulseOne() #required before sending data

buildNumber(55)
buildNumber(66)
buildNumber(77)
buildNumber(123)

wvData+=pack('h', 0)*2000

while True:
    print "SENDING",
    stream.write(wvData)
    raw_input()
```

```c
/*
This code is what was used on my AVR
microcontroller for the demonstration video
*/
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

volatile long commandIncoming=0;
volatile char command1=0;
volatile char command2=0;
volatile char command3=0;
volatile char command4=0;
volatile char bitsGotten=0;

// timing thresholds are critical! Send pulses to the chip
// and have it report the time between them. Use this to
// determine the best threshold value for your application.
// The ones here must be changed if you run at a speed other
// than 1mhz or if you use different timings in PC software
#define thresh_low 100 // between this and the next
#define thresh_high 130 // is the range for a logical 'one'

// ######## OUTGOING AUDIO DATA #########
void solid(){
    _delay_ms(1); //LONG LOW
    pulse(1);pulse(1);pulse(1);pulse(3);pulse(3);
    pulse(3);pulse(5);pulse(5);// CALIBRATION PULSES
}
void pulse(char size){
    PORTA|=_BV(PA3);
    _delay_us(100);
    PORTA&=~_BV(PA3);
    while (size){size--;_delay_us(100);}
}
void sendVal(unsigned long tosend){
    pulse(5); // send a space
    while (tosend){
        if (tosend&1){pulse(3);} // send ONE
        else {pulse(1);} // send ZERO
        tosend=tosend>>1;
    }
}

// ######## INCOMING AUDIO DATA #########
// NOTE THAT INPUTS ARE NORMALLY *HIGH* AND DROP *LOW* FOR SIGNAL
SIGNAL (PCINT0_vect) { // audio input trigger
    TIMSK0|=(1<<TOIE1); //Overflow Interrupt Enable
    if (TCNT0<10){return;} // seem too fast? ignore it!
    // Enable the following line to test custom timings
    //command1=command2;command2=command3;
    //command3=command4;command4=TCNT0;
    bitsGotten++;
    commandIncoming=commandIncoming*2; // shift left
    if (TCNT0>thresh_low){commandIncoming++;} // make 1
    TCNT0=0;
}

ISR(TIM0_OVF_vect){ // TIMER OVERFLOW
    if (bitsGotten){sendStuff();}
}

void fillCommands(){
    command1=(char*)(commandIncoming>>24);
    command2=(char*)(commandIncoming>>16);
    command3=(char*)(commandIncoming>>8);
    command4=(char*)(commandIncoming);
}

void sendStuff(){
    TIMSK0=0; //Overflow Interrupt
    cli(); // disable interrupts!
    fillCommands();
    solid(); // start data transmissions with this
    sendVal(12345);
    sendVal(12345);
    sendVal(54321);
    sendVal(command1);
    sendVal(command2);
    sendVal(command3);
    sendVal(command4);
    sendVal(1234567890);
    pulse(1);
    bitsGotten=0;
    sei(); // enable interrupts again!
    TIMSK0|=(1<<TOIE1); //Overflow Interrupt
}

// ######## MAIN PROGRAM #########
int main(){

    DDRA|=_BV(PA2)|_BV(PA3);

    // SET UP FOR SOUND CARD INTERRUPT
    MCUCR = 0b00000010; // trigger interrupt on falling edge
    GIMSK = 0b00010000; // pin change interrupt enable 0
    GIFR =  0b00010000; // flag register, same as above
    PCMSK0 = (1<<PCINT0); // Set Pin to use (PCINT0)
    sei(); // enable global interrupts

    // SET UP 8-bit COUNTER
    TCCR0B|=0b00000010;
    //TCCR1B|=(1<<CS12)|(1<<CS10); // prescaler 1024
    TIMSK0|=(1<<TOIE1); //Enable Overflow Interrupt Enable
    TCNT0=0;//Initialize our varriable (set for 1/15th second?)

    // MAIN PROGRAM
    for (;;){}
    return 0;

}
```

__In closing__, I'm tickled this works so well. It's funny to me that no one's really done this before in the hobby field. I'm sure I'm not the only one who wished there were an easy way to do this. I'm sure the process could be greatly improved, but this is a fun start. Wow, it's late, I should get to bed. I have to treat patients tomorrow morning!

__PS:__ If you replicate this concept, let me know about it! I'd love to see your project!

__UPDATE: This story was featured on [this post of HackADay.com](http://hackaday.com/2011/07/10/sound-card-microcontrollerpc-communication/)!__
July 8th, 2011

Create Mono and Stereo Wave Files with Python

My current project involves needing to create stereo audio in real time with Python. I'm using PyAudio to send the audio data to the sound card, but in this simple example I demonstrate how to create mono and stereo sounds with Python. I'm disappointed there aren't good simple case examples on the internet, so I'm sharing my own. It doesn't get much easier than this!

Python 2

from struct import pack
from math import sin, pi
import wave
import random

RATE=44100

## GENERATE MONO FILE ##
wv = wave.open('test_mono.wav', 'w')
wv.setparams((1, 2, RATE, 0, 'NONE', 'not compressed'))
maxVol=2**15-1.0 #maximum amplitude
wvData=""
for i in range(0, RATE*3):
    wvData+=pack('h', maxVol*sin(i*500.0/RATE)) #500Hz
wv.writeframes(wvData)
wv.close()

## GENERATE STERIO FILE ##
wv = wave.open('test_stereo.wav', 'w')
wv.setparams((2, 2, RATE, 0, 'NONE', 'not compressed'))
maxVol=2**15-1.0 #maximum amplitude
wvData=""
for i in range(0, RATE*3):
    wvData+=pack('h', maxVol*sin(i*500.0/RATE)) #500Hz left
    wvData+=pack('h', maxVol*sin(i*200.0/RATE)) #200Hz right
wv.writeframes(wvData)
wv.close()

The output is two sound files which look like this:

Python 3

from struct import pack
from math import sin, pi
import wave
import random
from os.path import abspath

# create a bytestring containing "short" (2-byte) sine values
SAMPLE_RATE = 44100
waveData = b''
maxVol = 2**15-1.0
frequencyHz = 500.0
fileLengthSeconds = 3
for i in range(0, SAMPLE_RATE * fileLengthSeconds):
    pcmValue = sin(i*frequencyHz/SAMPLE_RATE * pi * 2)
    pcmValue = int(maxVol*pcmValue)
    waveData += pack('h', pcmValue)

# save the bytestring as a wave file
outputFileName = 'output.wav'
wv = wave.open(outputFileName, 'w')
wv.setparams((1, 2, SAMPLE_RATE, 0, 'NONE', 'not compressed'))
wv.writeframes(waveData)
wv.close()
print(f"saved {abspath(outputFileName)}")
Markdown source code last modified on January 18th, 2021
---
title: Create Mono and Stereo Wave Files with Python
date: 2011-07-08 09:22:04
tags: python, old
---

# Create Mono and Stereo Wave Files with Python

__My current project involves needing to create stereo audio in real time__ with Python. I'm using PyAudio to send the audio data to the sound card, but in this simple example I demonstrate how to create mono and stereo sounds with Python. I'm disappointed there aren't good simple case examples on the internet, so I'm sharing my own. It doesn't get much easier than this!

### Python 2

```python
from struct import pack
from math import sin, pi
import wave
import random

RATE=44100

## GENERATE MONO FILE ##
wv = wave.open('test_mono.wav', 'w')
wv.setparams((1, 2, RATE, 0, 'NONE', 'not compressed'))
maxVol=2**15-1.0 #maximum amplitude
wvData=""
for i in range(0, RATE*3):
    wvData+=pack('h', maxVol*sin(i*500.0/RATE)) #500Hz
wv.writeframes(wvData)
wv.close()

## GENERATE STERIO FILE ##
wv = wave.open('test_stereo.wav', 'w')
wv.setparams((2, 2, RATE, 0, 'NONE', 'not compressed'))
maxVol=2**15-1.0 #maximum amplitude
wvData=""
for i in range(0, RATE*3):
    wvData+=pack('h', maxVol*sin(i*500.0/RATE)) #500Hz left
    wvData+=pack('h', maxVol*sin(i*200.0/RATE)) #200Hz right
wv.writeframes(wvData)
wv.close()
```

__The output__ is two sound files which look like this:

<div class="text-center img-border">

[![](mono_thumb.jpg)](mono.png)

[![](stereo_thumb.jpg)](stereo.png)

</div>

### Python 3

```python
from struct import pack
from math import sin, pi
import wave
import random
from os.path import abspath

# create a bytestring containing "short" (2-byte) sine values
SAMPLE_RATE = 44100
waveData = b''
maxVol = 2**15-1.0
frequencyHz = 500.0
fileLengthSeconds = 3
for i in range(0, SAMPLE_RATE * fileLengthSeconds):
    pcmValue = sin(i*frequencyHz/SAMPLE_RATE * pi * 2)
    pcmValue = int(maxVol*pcmValue)
    waveData += pack('h', pcmValue)

# save the bytestring as a wave file
outputFileName = 'output.wav'
wv = wave.open(outputFileName, 'w')
wv.setparams((1, 2, SAMPLE_RATE, 0, 'NONE', 'not compressed'))
wv.writeframes(waveData)
wv.close()
print(f"saved {abspath(outputFileName)}")
```

June 19th, 2011

Using Timers and Counters to Clock Seconds

My current secret project involves cramming a bunch of features into a single microcontroller. The chip I chose to use is an ATMega48. The ATMega 48 is $1.40 each in small quantities and comes crammed packed with features. The chip will be quite busy performing many functions, but its main loop will be executed at least every 50ms (required for USB, did I mention I'm bit-banging USB?!). I desire to have a bit of RTC (real time clock) functionality in that I need to precisely measure seconds, although I don't need to actually know the time or date. I desire to execute a function once per second, consuming a minimum of resources. The solution was quite simple, but I'm choosing to document it because it's somewhat convoluted in its explanation elsewhere on the net.

In summary, the way I accomplished this is using the built-in 16-bit timer (most AVRs have such a timer, including the ATTiny series). If I'm clocking the microcontroller at a known rate (determined by my selection of crystal, 12 MHz in my case), I can set the chip to continuously increment a register (timer1) and execute a function every time it overflows. Timer1 overflows at 2^16 (65,536). I enabled a prescaler value of 256 so that it takes 256 clock pulses to increment the timer. 12MHz/256 = 46,875 Timer1 increments each second. Since Timer1 overflows at 65,536, if I initiate Timer1 at 18,661 (65,536-46,875), it will take 1 second exactly to overflow. Upon overflowing, I do something (maybe flip a LED on or off), and reset the Timer1 back to its starting value 18,661. Done! Without using an external RTC module or even an external crystal or asynchronous timer, we managed to execute a function every second on the second with minimal overhead, allowing the chip to do everything it wants in the rest of the time!

The following example is a little more specific, executing a function exactly 15 times a second, and executing another function (to flash an LED) exactly every 1 second. It should be self explanatory:

// This function is called every second on the second
volatile int count; // this should be global
ISR(TIMER1_OVF_vect){
    TCNT1=62411;//Initialize our varriable (set for 1/15th second)
    count++; //increment 1/15th second counter
    if(count==15){
        statusTOGGLE(); // do your event (flash a LED in my case)
        count=0;//reset global variable
        }
    }
// This is for ATMega48, consult datasheet for variations for different chips
// place this just inside main(), before your primary loop
TCCR1B|=(1<<CS12);// prescaler 256
TIMSK1|=(1<<TOIE1); //Enable Overflow Interrupt Enable
TCNT1=62411;//Initialize our varriable (set for 1/15th second)
count=0; //Initialize a global variable
sei(); // enable interrupts

I'm having a lot of fun spending time going through the datasheet of this chip. It has a lot of features, and some I didn't really dig deeply into. Without giving away too much of my project, I'll show some photos I'm excited to share. My project interfaces the PC through USB directly attached to 2 pins using no intermediate chips (wow!). The photos demonstrate various steps in the temperature measurement and calibration tests...

Markdown source code last modified on January 18th, 2021
---
title: Using Timers and Counters to Clock Seconds
date: 2011-06-19 23:06:41
tags: circuit, microcontroller, old
---

# Using Timers and Counters to Clock Seconds

__My current secret project involves cramming a bunch of features into a single microcontroller.__ The chip I chose to use is an [ATMega48](http://www.swharden.com/blog/images/atmega48pinout.png). The ATMega 48 is $1.40 each in small quantities and comes crammed packed with features. The chip will be quite busy performing many functions, but its main loop will be executed at least every 50ms (required for USB, did I mention I'm bit-banging USB?!).  I desire to have a bit of RTC (real time clock) functionality in that I need to precisely measure seconds, although I don't need to actually know the time or date. I desire to execute a function once per second, consuming a minimum of resources. The solution was quite simple, but I'm choosing to document it because it's somewhat convoluted in its explanation elsewhere on the net.

__In summary, the way I accomplished this__ is using the built-in 16-bit timer (most AVRs have such a timer, including the ATTiny series). If I'm clocking the microcontroller at a known rate (determined by my selection of crystal, 12 MHz in my case), I can set the chip to continuously increment a register (timer1) and execute a function every time it overflows. Timer1 overflows at 2^16 (65,536).  I enabled a prescaler value of 256 so that it takes 256 clock pulses to increment the timer. 12MHz/256 = 46,875 Timer1 increments each second. Since Timer1 overflows at 65,536, if I initiate Timer1 at 18,661 (65,536-46,875), it will take 1 second exactly to overflow. Upon overflowing, I do something (maybe flip a LED on or off), and reset the Timer1 back to its starting value 18,661. Done! Without using an external RTC module or even an external crystal or asynchronous timer, we managed to execute a function every second on the second with minimal overhead, allowing the chip to do everything it wants in the rest of the time!

__The following example__ is a little more specific, executing a function exactly 15 times a second, and executing another function (to flash an LED) exactly every 1 second. It should be self explanatory:

```c
// This function is called every second on the second
volatile int count; // this should be global
ISR(TIMER1_OVF_vect){
    TCNT1=62411;//Initialize our varriable (set for 1/15th second)
    count++; //increment 1/15th second counter
    if(count==15){
        statusTOGGLE(); // do your event (flash a LED in my case)
        count=0;//reset global variable
        }
    }
```

```c
// This is for ATMega48, consult datasheet for variations for different chips
// place this just inside main(), before your primary loop
TCCR1B|=(1<<CS12);// prescaler 256
TIMSK1|=(1<<TOIE1); //Enable Overflow Interrupt Enable
TCNT1=62411;//Initialize our varriable (set for 1/15th second)
count=0; //Initialize a global variable
sei(); // enable interrupts
```

__I'm having a lot of fun__ spending time going through the datasheet of this chip. It has a lot of features, and some I didn't really dig deeply into. Without giving away too much of my project, I'll show some photos I'm excited to share. My project interfaces the PC through USB directly attached to 2 pins using no intermediate chips (wow!). The photos demonstrate various steps in the temperature measurement and calibration tests...

<div class="text-center img-border">

[![](DSCN1367_thumb.jpg)](DSCN1367.jpg)
[![](DSCN1372_thumb.jpg)](DSCN1372.jpg)

</div>
June 5th, 2011

Permeability Tuned Oscillator (PTO) Working Nicely

My last entry described my accidental discovery of the PTO for QRP purposes. I breadboarded it and was amazed at the results! I went ahead and built this carefully in an enclosure and the output is wonderful. It's strong, it's stable, and it tunes effortlessly over the same range it did before (about 1MHz). The video describes details of the action, and demonstrates the stability of the oscillator by letting you hear it audibly on a nearby receiver.

The fundamental concept and hardware is straightforward. Two nuts are soldered into an Altoids tin providing much-needed grounding for the screw (reduces shift when it's touched). Also the wire soldered over the screw is pinched firmly at the base to apply constant pressure to the screw to make it hard to turn and therefore more stable while turning. The inductor is a bunch of turns (no idea how many, about a meter of magnet wire) around a McDonalds straw.

Alltogether it's a simple colpitts oscillator with a MPF102 JFET at its heart, using a 74hc240 CMOS buffer as an amplifier. There's a voltage regulator in there too.

The result? Pretty darn stable (by CW QSO standards). That's without any regard to thermal isolation or temperature compensation. I'm quite pleased! I look forward to MUCH more experimentation now that I'm starting to feel good about designing and building simple, tunable, stable oscillators. It's always hard to nail all 3 in a single device!

Markdown source code last modified on January 18th, 2021
---
title: Permeability Tuned Oscillator (PTO) Working Nicely
date: 2011-06-05 02:21:40
tags: circuit, amateur radio, old
---

# Permeability Tuned Oscillator (PTO) Working Nicely

__My last entry described my accidental discovery of the PTO__ for QRP purposes. I breadboarded it and was amazed at the results! I went ahead and built this carefully in an enclosure and the output is wonderful. It's strong, it's stable, and it tunes effortlessly over the same range it did before (about 1MHz). The video describes details of the action, and demonstrates the stability of the oscillator by letting you hear it audibly on a nearby receiver.

![](https://www.youtube.com/embed/jm3a1OqNFVI)

__The fundamental concept and hardware is straightforward.__ Two nuts are soldered into an Altoids tin providing much-needed grounding for the screw (reduces shift when it's touched). Also the wire soldered over the screw is pinched firmly at the base to apply constant pressure to the screw to make it hard to turn and therefore more stable while turning. The inductor is a bunch of turns (no idea how many, about a meter of magnet wire) around a McDonalds straw.

<div class="text-center img-border">

[![](DSCN1350_thumb.jpg)](DSCN1350.jpg)

</div>

__Alltogether it's a simple colpitts oscillator__ with a MPF102 JFET at its heart, using a 74hc240 CMOS buffer as an amplifier. There's a voltage regulator in there too.

<div class="text-center img-border">

[![](DSCN1356_thumb.jpg)](DSCN1356.jpg)

</div>

__The result?__ Pretty darn stable (by CW QSO standards).  That's without any regard to thermal isolation or temperature compensation. I'm quite pleased!  I look forward to MUCH more experimentation now that I'm starting to feel good about designing and building simple, tunable, stable oscillators. It's always hard to nail all 3 in a single device!

<div class="text-center img-border">

[![](DSCN1357_thumb.jpg)](DSCN1357.jpg)

</div>
June 2nd, 2011

Screwy Oscillator Idea

Can you believe it's been almost 3 months since my last post? A lot's been going on since then, namely the national board dental exam. I'm happy to report I prepared for it and performed above and beyond my expectations on the exam, and I'm quite satisfied. The last few weeks were quite a strain on my life in my aspects, and during that time I realized that I didn't appreciate the little things (such as free time) that I would have loved to experience instead of studying. I guess it's the feeling you have when you're really sick and think to yourself "remember this moment so that when you're well again, you can appreciate feeling well". Now that it's all behind me, what do I do? I sit at my work station, play some light music, grab an adult beverage, turn on the soldering iron, and make something special.

I'm resuming work on my simple transmitter/receiver projects, but I'm working at the heart of the device and experimenting with oscillator designs. I built various Colpitts, Hartley, Clapp, and other oscillator designs, and I think I landed on a design I'm most comfortable with replicating. I'm actually creating a voltage controlled oscillator (VCO or VFO), with a frequency that can be adjusted by rotating a dial or two. It's always a balance between stability and tunability for me. I don't want to use polyvaricon variable capacitors (expensive!), and LED-based varactor diode configurations only give me a swing of about 20pf. What did I come up with?

I had tremendous success using a variable inductor for coarse tuning! The inductor is nothing more than a screw entering and exiting the center of an air core inductor. I can't claim all the credit, because I got the idea from this photo on one of the coolest websites on the planet, Alan Yates' Lab. It looks like Alan got the idea from this page... This is so useful! Is this common HAM knowledge? Why am I, someone who's been into RF circuitry for a couple of years now, JUST learning about this? I'm documenting it because I haven't seen it out there on the web, and I feel it should be represented more! Here's a video of it in action:

This is the circuit I was using:

This is what it looked like before the glue or screw:

Here's the variable inductor enveloped in hot glue before it cooled and turned white:

At the end of the day, it looks nice!

Band changes can be accomplished by swapping the capacitor between the inductor and ground. It couldn't be any easier! I'll see if I can build this in a more compact manner...

UPDATE (2 days later): Apparently this is called a "Permeability Tuned Oscillator", or PTO. It's an early design for radios (earlier than variable capacitors) and I guess therefore not described often on the internet. Knowing it's official title, searching yielded a few pages describing this action: Dave, G7UVW did some analytical measurements using a mercury core!The Tin Ear uses a PTO as its primary tuning method (also McDonalds straw?) This guy made a PTO out of PVC with a nice screw handle! This PTO kit seems to be used in many projects.The Century 21's VFO is a PTO! I love that rig and had no idea it tuned like that... This guy used a PTO in his MMR-40 radio.

Someone on Hackaday recommended This ARRL Challenge winner with an almost identical design as mine!I guess this bright idea was so bright, it was thought of by many people long ago...

Markdown source code last modified on January 18th, 2021
---
title: Screwy Oscillator Idea
date: 2011-06-02 21:18:47
tags: circuit, amateur radio, old
---

# Screwy Oscillator Idea

__Can you believe it's been almost 3 months since my last post?__ A lot's been going on since then, namely the national board dental exam. I'm happy to report I prepared for it and performed above and beyond my expectations on the exam, and I'm quite satisfied.  The last few weeks were quite a strain on my life in my aspects, and during that time I realized that I didn't appreciate the little things (such as free time) that I would have loved to experience instead of studying. I guess it's the feeling you have when you're really sick and think to yourself "remember this moment so that when you're well again, you can appreciate feeling well". Now that it's all behind me, what do I do?  I sit at my work station, play some [light music](http://www.youtube.com/watch?v=7nmTRZLLO2M), grab an adult beverage, turn on the soldering iron, and make something special.

__I'm resuming work on my simple transmitter/receiver projects,__ but I'm working at the heart of the device and experimenting with oscillator designs. I built various [Colpitts](http://en.wikipedia.org/wiki/Colpitts_oscillator), [Hartley](http://en.wikipedia.org/wiki/Hartley_oscillator), [Clapp](http://en.wikipedia.org/wiki/Clapp_oscillator), and other oscillator designs, and I think I landed on a design I'm most comfortable with replicating. I'm actually creating a voltage controlled oscillator (VCO or VFO), with a frequency that can be adjusted by rotating a dial or two. It's always a balance between stability and tunability for me. I don't want to use polyvaricon variable capacitors (expensive!), and LED-based varactor diode configurations only give me a swing of about 20pf. What did I come up with?

<div class="text-center img-border">

[![](DSCN1335_thumb.jpg)](DSCN1335.jpg)

</div>

__I had tremendous success__ using a variable _inductor_ for coarse tuning! The inductor is nothing more than a screw entering and exiting the center of an air core inductor. I can't claim all the credit, because I got the idea from [this photo](http://www.vk2zay.net/article/45) on one of the coolest websites on the planet, [Alan Yates' Lab](http://www.vk2zay.net). It looks like Alan got the idea from [this](http://www.wa6otp.com/pto.htm) page... This is so useful! Is this common HAM knowledge? Why am I, someone who's been into RF circuitry for a couple of years now, JUST learning about this? I'm documenting it because I haven't seen it out there on the web, and I feel it should be represented more! Here's a video of it in action:

![](https://www.youtube.com/embed/5JjF8-hjL9E)

This is the circuit I was using: 

<div class="text-center img-border">

[![](DSCN1334_thumb.jpg)](DSCN1334.jpg)

</div>

This is what it looked like before the glue or screw: 

<div class="text-center img-border">

[![](DSCN1307_thumb.jpg)](DSCN1307.jpg)

</div>

Here's the variable inductor enveloped in hot glue before it cooled and turned white: 

<div class="text-center img-border">

[![](DSCN1316_thumb.jpg)](DSCN1316.jpg)

</div>

At the end of the day, it looks nice! 


<div class="text-center img-border">

[![](DSCN1339_thumb.jpg)](DSCN1339.jpg)

</div>

__Band changes can be accomplished by__ swapping the capacitor between the inductor and ground. It couldn't be any easier! I'll see if I can build this in a more compact manner...

**UPDATE (2 days later):** Apparently this is called a "Permeability Tuned Oscillator", or PTO. It's an early design for radios (earlier than variable capacitors) and I guess therefore not described often on the internet. Knowing it's official title, searching yielded a few pages describing this action: [Dave, G7UVW](http://webshed.org/wiki/Mercury_PTO) did some analytical measurements using a mercury core!The [Tin Ear](http://www.amqrp.org/kits/tin_ear/index.html) uses a PTO as its primary tuning method (also McDonalds straw?) [This guy](http://www.geocities.ws/k7hkl_arv/PTO_Simplified_Mechanicals.html) made a PTO out of PVC with a nice screw handle! [This PTO](http://www.wa6otp.com/pto.htm) kit seems to be used in many projects.The [Century 21's VFO](http://www.io.com/~n5fc/c21_pto.htm) is a PTO! I love that rig and had no idea it tuned like that... [This guy](http://kd1jv.qrpradio.com/ARRLHBC/ARRL_MMR40.html) used a PTO in his MMR-40 radio.

Someone on Hackaday recommended [This ARRL Challenge winner](http://www.arrl.org/files/file/QST/Homebrew%20Challenge/HBC%201%20Winner-KD1JV.pdf) with an almost identical design as mine!I guess this bright idea was so bright, it was thought of by many people long ago...
Pages