Last year a group of high school students, in collaboration with a seminar course on Space Systems sponsored by the University of Florida’s Student Science Training Program (SSTP), gained some real-world experience planning, building, and launching a research payload to the edge of space – all in a couple weeks! Last year’s high altitude balloon launch was covered on my website, and the radio transmitter I built for it was featured on this Hack-A-Day post. Unlike last year’s payload, whose only homebrew device was the radio transmitter, this year’s payload had equipment we assembled ourselves, and instead of launching from NASA we launched from the UF football stadium! There were a couple problems along the way, and the payload hasn’t been recovered (yet), but it was a fun project and we all learned a lot along the way!
Below is a panoramic photo right before the launch – see our balloon on the right? So cool!
Our goal was to take photos from the edge of space, and log temperature, pressure, humidity, and GPS coordinates along the way. On-board were a radio transmitter, an Arduino with a GPS shield, and an Android phone to take pictures every few seconds.
Android details: Most of the Android development was handled by UF student Richard along with high school students Benji, Tyler, Michael, and Kevin. Their GitHub project is here:https://github.com/rich90usa/AndroidSensorLogger. Also note that the automatic photo capture utilized Photo Log Lite. We also used GPSLogger to handle logging GPS to SD. “Both of these programs were chosen for their ability to run in the background – and do so reliably by using the ‘correct’ Android supported methods of doing so.” — Richard
Our code used the phone’s text-to-speech engine to speak out an encoded version of every 90th new GPS coordinate. The data was encoded by connecting every number (0-9) to a word the NATO phonetic alphabet. The code also used text-to-speech to have the phone speak out the phone’s altitude data.
–Benji
The device consisted of 4 main components:a payload (the styrofoam box in which all of the electrical equipment was housed), a radar reflector (hanging off the bottom of the payload, to help make this object visible to aircraft), a parachute (at the top, made of bio-degradable plastic), and the balloon itself which measured about 6 feet wide when inflated at ground level (supposedly it reaches approximately 30 feet wide at high altitudes before it bursts). Once the balloon bursts, the parachute fills with air and the device floats back to earth.
Kunal demonstrates the effectiveness of our parachute with a scientific “run test”!
The radio communication system we used this year were a little more commercial than last year. Due to my limited time availability (I had an oral surgery rotation all week the week before launch), I chose to get something pre-packaged. My intent was to use FRS (those little 500mW family radio service radios) to send GPS data back to earth, but I later (after launch) did a little more research and realized that it probably wasn’t the most legal way to do it. However, it was extremely cost effective (amateur radio transmitters and RF transmitter modules are quite pricey). For about the cost of a pizza, we were able to interface a FRS radio to the android phone, and the phone ran a program which polled its GPS, turned coordinates into NATO letter abbreviations, and spoke them through the speaker line. The FRS radio with VOX (voice operated transmit) sensed audio and transmitted accordingly. Although it worked very well, I later learned that this may not have been legal in the US because, although FRS doesn’t require a user license and is legal to use anywhere as long as you use its stock antenna, I violated the rule that it cannot be operated above a certain height (20m I think?). Note that this should not be replicated, and probably shouldn’t have been done in the first place. I know I’ll take a lot of heat over this, but it’s in the past now and will be done differently in the future.
Here are some photos right before the launch. It was a sunny day at the UF football stadium! The Android phone is taped the the outside of the box and takes pictures every few seconds, storing them on a micro SD card. Inside the box is an Arduino with GPS shield, and the FRS radio transmitter.
The balloon is ready to be inflated!
The balloon is about 75% inflated at this point.
Richard looking all serious as he finishes inflating the balloon.
…panoramic above, zoomed below…
What a cool photo! It needs no words to describe!
The balloon is way up there!
After launch the balloon ascended at a rate of about 500ft/min. It spat out GPS data often, and altitude (not encoded with NATO abbreviations) was the easiest to hear as I walked from the UF football stadium to the UF Gator Amateur Radio Club to use their equipment (namely an AZEL-rotor-controlled 70cm yagi antenna attached to an I-Com 706) to listen in as the balloon ascended… but not before a group photo!
Here we are in the station… let’s get to work!
.
The results were a bit disappointing, as we believe the Android phone froze/crashed about 10,000 feet in the air! Since that was the device which generated the audio fed into the transmitter, when that phone died, the transmitter stopped transmitting, and we didn’t hear anything else from the transmitter ever again! We included contact information in the payload and it’s possible it will be found one day and we will be contacted about it. If this is the case, we’ll view the SD cards and see the full GPS log and pictures from the edge of space! Until then, we can only cross our fingers and hope for the best. Either way we had a blast, and learned a lot along the way. Next time we can be better prepared for a solid recovery!
Here’s audio of the device’s last words when it was about 10,000 feet in the air:
DOWNLOAD lastwords.mp3
Overall we had an awesome time! I’d like to thank everyone who helped with this project, especially UF students Richard, Kunal, Dante, and all of the SSTP high school students!
This post describes a project I designed which transmits strings of data from a microcontroller to a PC’s screen using audio beeping in a special mode called Hellschreiber. Although these days it’s almost exclusively used by amateur radio operators, I thought it would make a cool microcontroller project! The result can be accomplished with a microcontroller and a speaker as a transmitter and a PC with a microphone as a receiver and decoder, or with actual radio equipment (even toy walkie talkies) by transmitting the tones over modulated radio frequencies for long distance communication! Ideas anyone?
SPECIAL THANKS: I’d like to think Mike Seese for his brainstorming help in making this project a reality. Mike and I are working on a high altitude balloon project together, and a creative inexpensive radio link is one of our goals. Thanks Mike!
As a professional dental student by day and amateur electrical/RF engineer by night, I’m having a very strange summer. I’m developing rapidly in my experience and skills in both arenas. I finally feel like I have a working knowledge of most fundamental electrical and radio frequency concepts, and I’m starting to see patients and do procedures on humans (no more mannequins) in the student dental clinic. For legal and ethical reasons I do not write specifics about what I do with my patients, but I certainly make up for it by documenting the electronic projects I work on! My goals of doing this are to (a) inspire potential electronics tinkerers to come up with new ideas and attack new projects, and (b) receive feedback and insight from those more experienced than me to help me grow in my knowledge. My eye caught a comment a few posts ago that made me smile: You have been blessed with talent and the drive to attempt things not been tried before, keep it up, great job. –David S While I can’t claim that everything I do is truly novel or never tried before, I appreciate the encouraging words. Thank you David S!
Today’s project is a fun one involving vintage wartime radio equipment, amateur radio computer software, and a healthy dose of microcontrollers! My goal is to design a single chip Hellschreiber (technically Feldhellschreiber) transmitter. “Hellschreiber” translates into English as “Light Writer” and is a pun on the name of its inventor, Rudolf Hell, who built the first device in 1920. It was intended to allow messages to be transferred over poor radio links too noisy for intelligible voice or radioteletype (RTTY) communication. Its cool factor is upped by the fact that it was sometimes used by the German military in conjunction with the Enigma encryption system during World War 2! [As an aside, RTTY is still pretty sweet and dates back to the mid 1800s! Check out hardware receivers in video 1 and video 2]
Seeing a battlefield-ready Hellschreiber receiver gives you a good idea of how it works. (The video isn’t mine, I found it on youtube.) The concept is relatively simple (shown above), and the receiver has only 2 moving parts. A spinning corkscrew presses a ticker tape into ink when it receives a radio signal. As the radio signal beeps on and off, the corkscrew contacts at different positions at different times, and letters are written on the ticker tape! The designers of these things were extraordinarily creative! The picture on the right shows a Hellschreiber transmitter – basically a typewriter with mechanical wizardry that turns key presses into a series of radio tones corresponding to the pixelated shape of a character.
Almost a century later, people are still sending messages around the world using Hellschreiber! With an amateur radio license and an amateur radio transceiver you can tune around special Hellschreiber calling frequencies and engage in conversations with other people who enjoy using this unique mode. Computers have modernized the process, allowing you to send Hellschreiber text by typing on your keyboard and receive it by just looking at your screen. My favorite program (free) to do this is Digital Master 780, part of Ham Radio Deluxe.
This is the project I just completed. It takes strings of text stored (or dynamically generated) in an array on a microcontroller (I’m using an ATMega48, but the code is almost identical for any ATMEL AVR microcontroller, and easy adapted for other architectures) and turns it into an audio tone using PWM. This audio tone could be fed into a speaker and a microphone across the room could receive it and use the software to show the received data, or the audio could be fed into a radio transmitter and a PC hooked to the receiver could decode the audio. Either way, the text in the microcontroller is converted to Hellschreiber audio tones ready to be used however you see fit! Although I designed it as a resilient way to transmit GPS/altitude data from a high altitude balloon using a small, cheap, low-power radio transmitter, this project is just the foundation of a plethora of potential projects!
Here’s the circuit I’m using. It’s actually less complicated than shown – all those yellow wires are going to my AVR programmer! The chip just receives +5V and GND, and the audio is generated automatically and output on the OC0A pin, which happens to be pin 12 on my ATMega48. The output (audio level square waves) is fed to a crystal oscillator like this one, which generates square waves with an amplitude equal that to the input. Thus, by audio-frequency AC from the microchip, decoupled through a series capacitor, added to the power supply of the oscillator (provided by the 5V rail through a 1.8k resistor), we effectively produce an amplitude modulated (AM) radio signal!
This is the receiver I’m using. I’m lucky enough to have an all-mode, general-coverage, 100W amateur radio transceiver! It’s a Yaesu 857-D and I’m completely in love with it. It’s quite pricey though! You can find wide coverage receive-only radios called radio scanners (or police scanners), often for $20 or so on eBay which would do just as good a job of receiving all sorts of radio signals! Whatever you use, after tuning into the audio with the ham radio delux software, you’ll be able to decode Hellschreiber like this:
A few notes about the code: Each letter is sent twice vertically and I don’t think I should have done that. It’s easy enough to correct by eliminating the second FOR loop in the sendChar() function, and doubling the height of the pixels transmitted by changing on(1) and off(1) to on(2) and off(2). Then again, I could be mistaken – I don’t use this mode much. Also, horizontal width of characters (increase this and horizontally compress the received image to reduce the effects of noise) is controlled by a single variable, dynamically adjustable in software. Characters are created from a 3×5 grid (15 bits) and stored as an integer (16 bits, 2 bytes in AVR-GCC). Custom characters are certainly possible! This program takes 16.1% of program space (658 bytes) and 25.4% of data space (130 bytes) and certainly leaves room for optimization.
// designed for and tested with ATMega48
#include <avr/io.h>
#define F_CPU 8000000UL
#include <avr/delay.h>
#include <avr/interrupt.h>
/*
character format (3x5):
KFA
LGB
MHC
NID
OJE
variable format:
2-byte, 16-bit int 0b0ABCDEFGHIJKLMNO
(note that the most significant bit is not used)
*/
#define A 0b0111111010011111
#define B 0b0010101010111111
#define C 0b0100011000101110
#define D 0b0011101000111111
#define E 0b0100011010111111
#define F 0b0100001010011111
#define G 0b0100111000101110
#define H 0b0111110010011111
#define I 0b0100011111110001
#define J 0b0111110000100011
#define K 0b0110110010011111
#define L 0b0000010000111111
#define M 0b0111110110011111
#define N 0b0011111000001111
#define O 0b0011101000101110
#define P 0b0010001010011111
#define Q 0b0111011001011110
#define R 0b0010111010011111
#define S 0b0100101010101001
#define T 0b0100001111110000
#define U 0b0111110000111111
#define V 0b0111100000111110
#define W 0b0111110001111111
#define X 0b0110110010011011
#define Y 0b0110000011111000
#define Z 0b0110011010110011
#define n0 0b0111111000111111
#define n1 0b0000011111101001
#define n2 0b0111011010110111
#define n3 0b0111111010110001
#define n4 0b0111110010011100
#define n5 0b0101111010111101
#define n6 0b0101111010111111
#define n7 0b0110001011110000
#define n8 0b0111111010111111
#define n9 0b0111111010111101
#define SP 0b0000000000000000
#define BK 0b0111111111111111
#define SQ 0b0001000111000100
#define PR 0b0000110001100011
#define AR 0b0001000111011111
volatile char width=1; // width of characters, widen to slow speed
#define spd 8300 // synchronization, incr to make it slant upward
void rest(char times){while (times){times--;_delay_us(spd);}}
void on(char restfor){OCR0A=110;rest(restfor);}
void off(char restfor){OCR0A=0;rest(restfor);}
void sendChar(int tosend){
char w;
char bit;
for(w=0;w<width*2;w++){ // left column
off(1);
for (bit=0;bit<5;bit++){
if ((tosend>>bit)&1) {on(1);}
else {off(1);}
}
off(1);
}
for(w=0;w<width*2;w++){ // middle column
off(1);
for (bit=5;bit<10;bit++){
if ((tosend>>bit)&1) {on(1);}
else {off(1);}
}
off(1);
}
for(w=0;w<width*2;w++){ // right column
off(1);
for (bit=10;bit<15;bit++){
if ((tosend>>bit)&1) {on(1);}
else {off(1);}
}
off(1);
}
off(14); // letter space (1 column)
}
// CUSTOMIZE THE MESSAGE, OR GENERATE IT DYNAMICALLY!
int message[]={AR,AR,AR,S,W,H,A,R,D,E,N,PR,C,O,M,SP,R,O,C,K,S,
SP,AR,AR,AR,SP,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,
V,W,X,Y,Z,n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,BK,SP};
void sendMessage(){
char i;
for(i=0;i<sizeof(message)/2;i++){
sendChar(message[i]);
}
}
int main(){ // ### PROGRAM STARTS HERE ###
// this sets up CPWM in CTC mode,
// it may be slightly different for other chips
DDRD|=255; // OC0A is now an output
TCCR0A=0b01000010; // toggle on match, CTC mode
TCCR0B=0B00000011; // set prescalar
for(;;){
width=1; // fast mode
sendMessage();
width=3; // slow mode
sendMessage();
}
return 0;
}
I’m working to further simplify my frequency counter design. This one is simpler than my previous design both in hardware and software! Here’s a video to demonstrate the device in its current state:
I utilize the ATMega48’s hardware counter which is synchronous with the system clock, so it can only measure frequency less than half of its clock speed. I solve this issue by dividing the input frequency by 8 and clocking the chip at 12mhz. This allows me to measure frequencies up to about 48MHz, but can be easily adapted to measure over 700MHz (really?) by dividing the input by 128. Division occurs by a 74HC590 8-bit counter (not a 74HC595 as I accidentally said in the video, which is actually a common shift register), allowing easy selection of input divided by 1, 2, 4, 8, 16, 32, 64, or 128. The following image shows the o-scope showing the original signal (bottom) and the divided-by-8 result (top)
The device outputs graphically to a LCD simply enough. That LCD is from eBay and is only $3.88 shipped! I’m considering buying a big box of them and implementing them in many more of my projects. They’re convenient and sure do look nice!
The signal I test with comes from an oscillator I built several months ago. It’s actually a SA612 style receiver whose oscillator is tapped, amplified, and output through a wire. It’s tunable over almost all of 40m with a varactor diode configuration. It was the start of a transceiver, but I got so much good use out of it as a function generator that I decided to leave it like it is!
THIS IS HOW THE PROGRAM WORKS: I don’t supply a schematic because it’s simple as could be. Divide the input frequency to something relatively slow, <1MHz at least. Configure the 16-bit counter to accept an external pin as the counter source (not a prescaled clock, as I often use in other applications). Then set the timer value to 0, _delay_ms() a certainly amount of time (1/10th second), and read the counter value. Multiply it by 10 to account for the 1/10th second, then multiply it by 8 to account for the divider, and it’s done! It will update 10 times a second, with a resolution down to 10*8 = 80 Hz. It’s well within the range of amateur radio uses! If you’re considering replicating this, read up on how to use hardware counters with ATMEL AVR microcontrollers. That should be enough to get you started! Here’s the code I used…
This is a multi-part blog entry added over 2 days of time documenting the progress of the addition of USB to a simple frequency counter. The final result lets me graph frequency over time on the computer, automatically, over days of time if desired. I’m quite pleased at the result, especially considering so little circuitry was required! Although this page documents all the way from conception to completion, if you wish you can jump straight to the final product.
It looks like this will be a multi-part blog entry. I’m in the process of figuring out how to add USB functionality to this simple device, which will be a fun way for me to express my creativity and think a bit outside the box while gaining some simple electrical engineering experience! Here’s the jist of what I’m planning…
After a brief trip to Orlando to visit family, I decided to stop by the house of one of my neighbors who worked at the same small engineering company I did when I was first starting college (about the time I decided to peruse biology rather than engineering). I hadn’t seen him in a while and we talked about various electronics things (he’s working on an impressive project currently), and before I left he offered me a brown box. “Do you have any use for a function generator?” I got excited and said “sure!” On closer inspection, it was actually a frequency counter, and he said “oh well I don’t need one of those anyway” and gave it to me. I was ecstatic! Between this post, this post, this post, this post, and this final project post you can tell that building a frequency counter was really important to me, and that I was never truly satisfied with the result – it wasn’t stable to the Hz! I’m excited to finally have a real counter at my workstation. (It’s an instek GFC-8010H, 1-120 MHz range.) Now onto figuring out how to build a spectrum analyzer… X_x
Update (2 days later)
I never can leave things alone can I? While basking in happiness over this new acquisition I pondered how easy it would be to interface this to a PC. I would like to graph frequency drift over time directly (not using a radio receiver outputting audio which I graph, since the radio is sensitive to drift). Plus this counter allows sample sizes of 10 seconds! That’s some serious resolution (compared to what I’m used to at least). First step to PC interfacing is to see what I’ve got to work with. I unscrewed the box and snapped some photos of the surprisingly simple device… I can’t believe this costs over $175 (as listed on Amazon.com) – it’s so simple!
I guess it all makes sense. AC transformer and rectifier diodes with a smoothing capacitor on the bottom left, fed into a 7805 linear voltage regulator, no doubt powering the micro-controller (big IC), logic buffer (small IC), and whatever analog circuitry is under the panel.
I’m not going to lift the panel because it’s obvious what’s under there. Likely some high gain, high distortion amplifier stages to provide a lot of buffering, eventually the input shape is fed to the chip for counting.
After posting and thinking about it, the curiosity got to me! I lifted the panel and this is what I found… There’s our buffer and wave shaper! The full datasheet shows it’s a (triple) line driver.
Come to think of it, I’m not entirely sure about that smaller IC. It’s a 74HC00N, quad NAND gate. Knee-jerk was to say it was used for dividing-down the frequency, but that’s silly since it takes 2 NAND gates to make a flip flop, and that chip would be only 2 flip flops (/4), and there are flip flip chips for all that. Perhaps this has something to do with the buttons on the front panel? Perhaps something to do with square-shaping the oscillator with the 10mhz crystal? The big GFC 9701-1 IC seems to be a custom counter chip used in many Instek products. Here’s a blurb from a page of their manual for a function generator they make:
The most important function of the internal counter is to show the main frequency on the display. So we take a square signal from the square shaper and change the level to TTL compatible level with a TTL shaper block (is this the role of that NAND gate?) then the signal will connect with the counter GFC-9701. Because the counter directly connects with the MPU system, the MPU can get correct frequency and show it on the display.
So, it seems that chip is already outputting data ready to go into a CPU. I wonder if it’s outputting some type of data on an unused pin, ripe for the picking? I can’t find more ICs in this device, so it’s possible no other MCU is driving the display, and this counter IC is doing that all on its own. Bah, again curiosity is getting the best of me… [unscrews front panel] More ICs! I couldn’t see them well before so I’m glad I opened up the display. The ULN2003A is a 7 channel darlington array, x2 = 14 darlingtons. The CD4511 is a common 7-segment driver – BINGO! If I’m going to interface this device by intercepting the display, this is the way to do it! The darlingtons tell me which character is selected, and the input of this chip tells me the value to be displayed. Pow!
Let’s take a closer look at that main chip again… X-RAY VISION TIME! I used Image-J to extract the red channel of the image and increased contrast, inverted, then used a 10 pixel wide unsharp mask with 0.8 weight to bring-out the leads. I guess I could have just unscrewed it and looked at the bottom, but where’s the fun in that? I imagine the top left pin is input of frequency. The bottom left pins go to buttons on the front, so they’re for front panel input. The headers on the right go to the front panel display. The pin going to the smaller IC must be the clock input, so that NAND gate DOES do something with shaping the input clock signal. On the top fight of the image you can see the crystal connecting to that gate. The trace going in the center of the chip on top is probably +5V supply for the chip. I’m not sure about much else, and I don’t feel like poking around with a continuity meter right now. UPDATE – I tested each pin with an analog o-scope. I found that pin 6 (unconnected) outputs a burst of data every time the display updates – this is my data line! If I had a logic analyzer I’d know how to read it… BAH! What’s this?! The voltage regulator with its hefty heat sink (which obviously gets quite warm) is attached to a 10.000 MHz crystal! Is this the time base crystal? Doesn’t accuracy depend on thermostability of this crystal? It’s not just near it – it’s physically connected with it through metal! Does this imply that a loaded 7805 voltage regulator produces heat more steadily, and with a final temperature more stable than room air in a plastic enclosure??
update: The following was emailed to me in response to this puzzling issue. It’s from my good friend Bill!
It may be an SC-cut crystal which is the best type for precision oscillators because the turn around inflection occurs at a much broader temperature range than the regular AT-cut, el cheapo types we often use. SC types, if carefully selected, can remain within a fraction of a ppm over a temperature range to 10 to 20 C. The turn around point temperature is pretty high, about 90 C, compared to around 25C for the at-cut. So, my guess is that the 7805 provides this really high temperature to the xtal and can be trusted to not vary by more than a few degrees, particularly in a laboratory environment. –Bill (W4HBK)
Afterthought: This would make one hell of a huff-and-puff oscillator!
PROJECT COMPLETED!
I’m quite excited, the end product works wonderfully! It looks pretty spiffy too!
Here’s some video showing the device at work!
Of course Python and MatPlotLib can graph it:
… but so can Excel!
UPDATE Oops, I forgot to remove the trailing zero. That’s 9.9 MHz, not 99 MHz. That’s easy enough to do later, so I’m not going to fix it and re-post updated images. Don’t email me about it, I know ^_^
UPDATE 2 Here’s some useful data! I hooked up a canned oscillator at 3.57 something MHz (very stable) and watched it as my frequency counter warmed up. The result showed that the counter takes about 2 hours to warm up!!! he shift is only about 15 Hz over 2 hours, but still it’s good to know.
Once it’s warm, it’s stable!
Schematic
This device is very simple and specialized for my use and I have not designed a custom schematic. USB functionality is as recommended by V-USB, similar to:
Microcontroller code – Although it’s hard for me, I really don’t think I can release this right now. I’m working on an idiot’s guide to USB connectivity with ATMEL microcontrollers, and it would cause quite a stir to post that code too early. It’ll be shared soon! Here are the python scripts for the logging and for the graphing:
#This code polls the USB device and displays/logs frequency
from optparse import OptionParser
import time
import usb.core
import usb.util
import os
while True:
print "\nTrying to communicate with the Gator Keyer ...",
dev = usb.core.find(find_all=True, idVendor=0x16c0, idProduct=0x5dc)
if len(dev)==0: print "FAIL"
dev=dev[0]
dev.set_configuration()
print "there it is!"
break
def readVals(c):
x=dev.ctrl_transfer(0xC0, 3, c,4,4).tolist()
val=x[0]
if val>9: val=0
return val
def readDisp():
c=[]
for i in range(1,9):
val=readVals(i)
c.append(val)
#print "char",i,"=",val
disp="%d%d%d%d%d%d%d%d"%(c[0],c[1],c[2],c[5],c[6],c[3],c[4],c[7])
return disp
def readFreq():
i=0
first=readDisp()
while True:
if first==readDisp():
i+=1
if i==5: break #we're good!
else: #FAIL! start over...
i=0
first=readDisp()
return first
### PROGRAM START ##################
start=time.time()
while True:
line="%.02f,%s"%(time.time()-start,readFreq())
print line
f=open("freq.csv",'a')
f.write(line+"\n")
f.close()
time.sleep(1)
#This code reads the log file and graphs it with matplotlib
import matplotlib.pyplot as plt
import numpy
print "loading"
f=open("freq.csv")
raw=f.readlines()
f.close()
print raw
print "crunching"
times=numpy.array([])
data=numpy.array([])
for line in raw:
if len(line)<10: continue
line=line.replace("\n",'').split(',')
times=numpy.append(times,float(line[0]))
data=numpy.append(data,float(line[1]))
#data=data/1000000.0
print times, data
print "DONE processing",len(data),"lines\n\n"
print "plotting..."
plt.figure()
plt.grid()
plt.plot(times,data,'-')
plt.plot(times,data,'.')
plt.show()
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!
This is the finished product ready to send data to a PC:
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("%s\n"%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.
### VIDEO ###
"""
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!
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!
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.
Update: read the bottom of the post for reflections about the concept discussed below...
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. This guy uses a straw too!
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…
I’m ecstatic! Finally I built something that worked the first time. Well… on the 3rd attempt! The goal was to develop a minimal-cost, minimal complexity frequency counter suitable for amateur radio. Although I think I can still cut cost by eliminating components and downgrading the microcontroller, I’m happy with my first working prototype.
I haven’t tested it rigorously with anything other than square waves, but I imagine that anything over 1PPV is sufficient (the input is through a bypass capacitor, internally biased right at the trigger threshold). Counting is accomplished by a 74LV8154N (dual 16-bit counter configured as 32-bit) which displays the count as four selectable bytes presented on 8 parallel pins. The heart of the device is an ATMega16 which handles multiplexing of the display and has a continuously-running 16-bit timer which, upon overflowing, triggers a reset of the counter and measurement of the output. Software isn’t perfect (you can see the timing isn’t accurate) but I imagine its inaccuracy can be measured and is a function of frequency such that it can be corrected via software. Here are some photos…
A PCB is DESPERATELY needed. I’ll probably make one soon. Once it’s a PCB, the components are pretty much drop-in and go! No wires! It’ll be a breeze to assemble in 5 minutes. I wonder if it would make a fun kit? It would run on a 9V battery of course, but a calculator-like LCD (rather than LED) display would be ultra-low-current and might make a good counter for field operation (3xAAA batteries would last for months!)
UPDATE: I found out that the ATMega16 donation was from my friend Obulpathi, a fellow Gator Amateur Radio Club member! He also gave me a pair of ATMega32 chips. Thanks Obul!
This is the current state of my receiver. Unlike earlier designs this one uses NO VARIABLE CAPACITORS! This helps because (a) it reduces cost, (b) makes it easier to build for anyone (it’s hard to hunt down identical variable capacitors), and (c) allows it to be totally voltage controlled so microchip or PC control of frequency becomes trivial. Tuning over the entire 40m band is achieved with 3 LEDs reverse biased acting as varactors (wow!). The knobs are potentiometers. The whole circuit runs on 5v.
In the spirit of furthering my knowledge of AC circuity, I’m trying to build a 100% homebrew transceiver. Yeah, QRSS and ultra-weak signal, ultra-narrowband communications is still fun, but it’s not the same thrill as actually engaging in real time communication with somebody! My goal is a transmitter / receiver in a box. The basic features I desire are (1) multiple bands (at least 40m, 30m, 20m), (2) FULL-band coverage, (3) direct conversion receiver, (4) 10W transmitter, (5) digital frequency display, (6) common standard components (nothing mechanical, no air variable capacitors, everything must be easily obtainable on sites like Mouser and DigiKey), (7) SMT capability, (8) inexpensive ($20 is my goal, but that’s a tough goal!). My designs are changing daily, so I’m not going to waste time posting schematics every time I write on this blog, but here are some photos and videos of the product in its current state.
(I just found that last video – it was one of my favorite songs as a teenager, performed live!)
UPDATE: I got a cool dual 16-bit counter IC made by TI, a SN74LV8154N – very cheap, and can be configured as a 32-bit counter. It seemed like a better option than multiple 8-bit counters, and this chip is about $0.60 so if I can make it work I’ll be happy! I breadboarded it up (see circuit diagram) and it seemed to work. I started wiring it on the perf board, but haven’t written software for it yet…
UPDATE – I just found this video on youtube I never posted on my blog, so this seems like an appropriate location for it: