UPDATE: An improved ECG design was posted in August, 2016.
Check out: https://www.swharden.com/wp/2016-08-08-diy-ecg-with-1-op-amp/

Of the hundreds of projects I’ve shared over the years, none has attracted more attention than my DIY ECG machine on the cheap posted almost 4 years ago. This weekend I re-visited the project and made something I’m excited to share!  The original project was immensely popular, my first featured article on Hack-A-Day, and today “ECG” still represents the second most searched term by people who land on my site. My gmail account also has had 194 incoming emails from people asking details about the project. A lot of it was by frustrated students trying to recreate the project running into trouble because it was somewhat poorly documented. Clearly, it’s a project that a wide range of people are interested in, and I’m happy to revisit it bringing new knowledge and insight to the project. I will do my best to document it thoroughly so anyone can recreate it!

The goal of this project is to collect heartbeat information on a computer with minimal cost and minimal complexity.  I accomplished this with fewer than a dozen components (all of which can be purchased at RadioShack). It serves both as a light-based heartbeat monitor (similar to a pulse oximeter, though it’s not designed to quantitatively measure blood oxygen saturation), and an electrocardiogram (ECG) to visualize electrical activity generated by heart while it contracts. Let’s jump right to the good part – this is what comes out of the machine:

That’s my actual heartbeat. Cool, right? Before I go into how the circuit works, let’s touch on how we measure heartbeat with ECG vs. light (like a pulse oximeter).  To form a heartbeat, the pacemaker region of the heart (called the SA node, which is near the upper right of the heart) begins to fire and the atria (the two top chambers of the heart) contract. The SA node generates a little electrical shock which stimulated a synchronized contraction. This is exactly what defibrillators do when a heart has stopped beating. When a heart attack is occurring and a patient is undergoing ventricular fibrillation, it means that heart muscle cells are contracting randomly and not in unison, so the heart quivers instead of pumping as an organ. Defibrillators synchronize the heart beat with a sudden rush of current over the heart to reset all of the cells to begin firing at the same time (thanks Ron for requesting a more technical description).  If a current is run over the muscle, the cells (cardiomyocytes) all contract at the same time, and blood moves. The AV node (closer to the center of the heart) in combination with a slow conducting pathway (called the bundle of His) control contraction of the ventricles (the really large chambers at the bottom of the heart), which produce the really large spikes we see on an ECG.  To measure ECG, optimally we’d place electrodes on the surface of the heart. Since that would be painful, we do the best we can by measuring voltage changes (often in the mV range) on the surface of the skin. If we amplify it enough, we can visualize it. Depending on where the pads are placed, we can see different regions of the heart contract by their unique electrophysiological signature. ECG requires sticky pads on your chest and is extremely sensitive to small fluctuations in voltage. Alternatively, a pulse oximeter measures blood oxygenation and can monitor heartbeat by clipping onto a finger tip. It does this by shining light through your finger and measuring how much light is absorbed. This goes up and down as blood is pumped through your finger. If you look at the relationship between absorbency in the red vs. infrared wavelengths, you can infer the oxygenation state of the blood. I’m not doing that today because I’m mostly interested in detecting heart beats.

For operation as a pulse oximeter-type optical heartbeat detector (a photoplethysmograph which produces a photoplethysmogram), I use a bright red LED to shine light through my finger and be detected by a phototransistor (bottom left of the diagram). I talk about how this works in more detail in a previous post. Basically the phototransistor acts like a variable resistor which conducts different amounts of current depending on how much light it sees. This changes the voltage above it in a way that changes with heartbeats. If this small signal is used as the input, this device acts like a pulse oximeter.

For operation as an electrocardiograph (ECG), I attach the (in) directly to a lead on my chest. One of them is grounded (it doesn’t matter which for this circuit – if they’re switched the ECG just looks upside down), and the other is recording. In my original article, I used pennies with wires soldered to them taped to my chest as leads. Today, I’m using fancier sticky pads which are a little more conductive. In either case, one lead goes in the center of your chest, and the other goes to your left side under your arm pit. I like these sticky pads because they stick to my skin better than pennies taped on with electrical tape. I got 100 Nikomed Nikotabs EKG Electrodes 0315 on eBay for $5.51 with free shipping (score!). Just gator clip to them and you’re good to go!

In both cases, I need to build a device to amplify small signals. This is accomplished with the following circuit. The core of the circuit is an LM324 quad operational amplifier.  These chips are everywhere, and extremely cheap. It looks like Thai Shine sells 10 for $2.86 (with free shipping). That’s about a quarter each. Nice!  A lot of ECG projects use instrumentation amplifiers like the AD620 (which I have used with fantastic results), but these are expensive (about $5.00 each). The main difference is that instrumentation amplifiers amplify the difference between two points (which reduces noise and probably makes for a better ECG machine), but for today an operational amplifier will do a good enough job amplifying a small signal with respect to ground. I get around the noise issue by some simple filtering techniques. Let’s take a look at the circuit.

This project utilizes one of the op-amps as a virtual ground. One complaint of using op-amps in simple projects is that they often need + and – voltages. Yeah, this could be done with two 9V batteries to generate +9V and -9V, but I think it’s easier to use a single power source (+ and GND). A way to get around that is to use one of the op-amps as a current source and feed it half of the power supply voltage (VCC), and use the output as a virtual ground (allowing VCC to be your + and 0V GND to be your -). For a good description of how to do this intelligently, read the single supply op amps web page. The caveat is that your signals should remain around VCC/2, which can be done if it is decoupled by feeding it through a series capacitor. The project works at 12V or 5V, but was designed for (and has much better output) at 12V. The remaining 3 op-amps of the LM324 serve three unique functions:

STAGE 1: High gain amplifier. The input signals from either the ECG or pulse oximeter are fed into a chain of 3 opamp stages. The first is a preamplifier. The output is decoupled through a series capacitor to place it near VCC/2, and amplified greatly thanks to the 1.8Mohm negative feedback resistor. Changing this value changes initial gain.

STAGE 2: active low-pass filter. The 10kOhm variable resistor lets you adjust the frequency cutoff. The opamp serves as a unity gain current source / voltage follower that has high input impedance when measuring the output f the low-pass filter and reproduces its voltage with a low impedance output. There’s some more information about active filtering on this page. It’s best to look at the output of this stage and adjust the potentiometer until the 60Hz noise (caused by the AC wiring in the walls) is most reduced while the lower-frequency component of your heartbeat is retained. With the oximeter, virtually no noise gets through. Because the ECG signal is much smaller, this filter has to be less aggressive, and this noise is filtered-out by software (more on this later).

STAGE 3: final amplifier with low-pass filter. It has a gain of ~20 (determined by the ratio of the 1.8kOhm to 100Ohm resistors) and lowpass filtering components are provided by the 22uF capacitor across the negative feedback resistor. If you try to run this circuit at 5V and want more gain (more voltage swing), consider increasing the value of the 1.8kOhm resistor (wit the capacitor removed). Once you have a good gain, add different capacitor values until your signal is left but the noise reduced. For 12V, these values work fine. Let’s see it in action!

Now for the second half – getting it into the computer. The cheapest and easiest way to do this is to simply feed the output into a sound card! A sound card is an analog-to-digital converter (ADC) that everybody has and can sample up to 48 thousand samples a second! (overkill for this application) The first thing you should do is add an output potentiometer to allow you to drop the voltage down if it’s too big for the sound card (in the case of the oximeter) but but also allow full-volume in the case of sensitive measurements (like ECG). Then open-up sound editing software (I like GoldWave for Windows or Audacity for Linux, both of which are free) and record the input. You can do filtering (low-pass filter at 40Hz with a sharp cutoff) to further eliminate any noise that may have sneaked through. Re-sample at 1,000 Hz (1kHz) and save the output as a text file and you’re ready to graph it! Check it out.

Here are the results of some actual data recorded and processed with the method shown in the video. let’s look at the pulse oximeter first.

That looks pretty good, certainly enough for heartbeat detection. There’s obvious room for improvement, but as a proof of concept it’s clearly working. Let’s switch gears and look at the ECG. It’s much more challenging because it’s signal is a couple orders of magnitude smaller than the pulse oximeter, so a lot more noise gets through. Filtering it out offers dramatic improvements!

Here’s the code I used to generate the graphs from the text files that GoldWave saves. It requires Python, Matplotlib (pylab), and Numpy. In my case, I’m using 32-bit 2.6 versions of everything.

# DIY Sound Card ECG/Pulse Oximeter
# by Scott Harden (2013) http://www.SWHarden.com

import pylab
import numpy

f=open("light.txt")
raw=f.readlines()[1:]
f.close()

data = numpy.array(raw,dtype=float)
data = data-min(data) #make all points positive
data = data/max(data)*100.0 #normalize
times = numpy.array(range(len(data)))/1000.0
pylab.figure(figsize=(15,5))
pylab.plot(times,data)
pylab.xlabel("Time Elapsed (seconds)")
pylab.ylabel("Amplitude (% max)")
pylab.title("Pulse Oximeter - filtered")
pylab.subplots_adjust(left=.05,right=.98)
pylab.show()

Future directions involve several projects I hope to work on soon. First, it would be cool to miniaturize everything with surface mount technology (SMT) to bring these things down to the size of a postage stamp. Second, improved finger, toe, or ear clips (or even taped-on sensors) over long duration would provide a pretty interesting way to analyze heart rate variability or modulation in response to stress, sleep apnea, etc. Instead of feeding the signal into a computer, one could send it to a micro-controller for processing. I’ve made some darn-good progress making multi-channel cross-platform USB option for getting physiology data into a computer, but have some work still to do. Alternatively, this data could be graphed on a graphical LCD for an all-in-one little device that doesn’t require a computer. Yep, lots of possible projects can use this as a starting point.

Notes about safety: If you’re worried about electrical shock, or unsure of your ability to make a safe device, don’t attempt to build an ECG machine. For an ECG to work, you have to make good electrical contact with your skin near your heart, and some people feel this is potentially dangerous. Actually, some people like to argue about how dangerous it actually is, as seen on Hack-A-Day comments and my previous post comments. Some people have suggested the danger is negligible and pointed-out that it’s similar to inserting ear-bud headphones into your ears. Others have suggested that it’s dangerous and pointed-out that milliamps can kill a person. Others contest that pulses of current are far more dangerous than a continuous applied current. Realists speculate that virtually no current would be delivered by this circuit if it is wired properly. Rational, cautionary people worried about it reduce risk of accidental current by applying bidirectional diodes at the level of the chest leads, which short any current (above 0.7V) similar to that shown here. Electrically-savvy folks would design an optically decoupled solution. Intelligent folks who abstain from arguing on the internet would probably consult the datasheets regarding ECG input protection. In all cases, don’t attach electrical devices to your body unless you are confident in their safety. As a catch-all, I present the ECG circuit for educational purposes only, and state that it may not be safe and should not be replicated  There, will that cover me in court in case someone tapes wires to their chest and plugs them in the wall socket?

LET ME KNOW WHAT YOU THINK! If you make this, I’m especially interested to see how it came out. Take pictures of your projects and send them my way! If you make improvements, or take this project further, I’d be happy to link to it on this page. I hope this page describes the project well enough that anyone can recreate it, regardless of electronics experience. Finally, I hope that people are inspired by the cool things that can be done with surprisingly simple electronics. Get out there, be creative, and go build something cool!





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

Install necessary software

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

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

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

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

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

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

 return 0;
}

Compile the code (generate a HEX file)

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

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

Program the HEX firmware onto the AVR

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

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

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


avrdude: AVR device initialized and ready to accept instructions

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

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

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

avrdude: 94 bytes of flash written

Your Program should now be loaded! Sit back and watch your LED blink.

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

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

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





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

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


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

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

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

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

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

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

For good measure, here’s the original song:

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

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

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

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

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

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

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

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

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

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

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

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

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

void head_go(char times){
	// rocks the head back and forth once
	for (char i=0;i<(times-1);i++){
		head_left();
		_delay_ms(400);
		PORTA^=(1<<PA2);
		PORTA^=(1<<PA3);
		head_right();
		_delay_ms(400);
		PORTA^=(1<<PA2);
		PORTA^=(1<<PA3);
	}
	head_center(); // returns head to center when done
	_delay_ms(400);
	PORTA^=(1<<PA2);
	PORTA^=(1<<PA3);
}

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

		go_lowest();head_center();// set starting positions

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

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

		while ((PINB & _BV(PB0))){} // wait for beep que
		_delay_ms(200);
		head_go(16); // rock head 16 times
		go_lowest(); // reset position
		PORTA=0;
	}
  return 0;
}

Finally, I’d like to take a moment to indicate one of the reasons this project is special to me. My wife, Angelina Harden, died one year ago today. This project was the last one she worked on with me. She died a few days after the video was taken, and in the process of moving out of our apartment I threw away almost everything (including this project). Although I never finished it, I remember working on it with Angelina – we went to wal-mart together to buy the foam board I used to make it, and she told me that I should make her head rock back and forth rather than just move her arm. I remember that, once it was all done, I let her sit in the chair in front of it and played it through, and she laughed nearly the whole time 🙂 I’ll always miss her.





I’m not ashamed to say it: I’m a bit of an ATMEL guy. AVR microcontrollers are virtually exclusively what I utilize when creating hobby-level projects. Wile I’d like to claim to be an expert in the field since I live and breathe ATMEL datasheets and have used many intricate features of these microchips, the reality is that I have little experience with other platforms, and have likely been leaning on AVR out of habit and personal convention rather than a tangible reason. Although I was initially drawn to the AVR line of microcontrollers because of its open-source nature (The primary compiler is the free AVR-GCC) and longstanding ability to be programmed from non-Windows operating systems (like Linux), Microchip’s PIC has caught my eye over the years because it’s often a few cents cheaper, has considerably large professional documentation, and offers advanced integrated peripherals (such as native USB functionality in a DIP package) more so than the current line of ATTiny and ATMega microcontrollers. From a hobby standpoint, I know that ATMEL is popular (think Arduino), but from a professional standpoint I usually hear about commercial products utilizing PIC microcontrollers. One potential drawback to PIC (and the primary reason I stayed away from it) is that full-featured C compilers are often not free, and as a student in the medical field learning electrical engineering as a hobby, I’m simply not willing to pay for software at this stage in my life.

I decided to take the plunge and start gaining some experience with the PIC platform. I ordered some PIC chips (a couple bucks a piece), a PIC programmer (a Chinese knock-off clone of the Pic Kit 2 which is <$20 shipped on eBay), and shelved it for over a year before I got around to figuring it out today. My ultimate goal is to utilize its native USB functionality (something at ATMEL doesn’t currently offer in DIP packages). I’ve previously used bit-banging libraries like V-USB to hack together a USB interface on AVR microcontrollers, but it felt unnecessarily complex. PIC is commonly used and a bit of an industry standard, so I’m doing myself a disservice by not exploring it. My goal is USB functionality, but I have to start somewhere: blinking a LED.

Here’s my blinking LED. It’s a bit anticlimactic, but it represents a successful program design from circuit to writing the code to programming the microchip.

Based on my limited experience, it seems you need 4 things to program a PIC microcontroller with C:

The first thing I did was familiarize myself with the pin diagram of my PIC from its datasheet. I’m playing with an 18F2450 and the datasheet is quite complete. If you look at the pin diagram, you can find pins labeled MCLR (reset), VDD (+5V), VSS (GND), PGC (clock), and PGD (data). These pins should be connected to their respective counterparts on the programmer. To test connectivity, install and run the PICkit2 installer software and it will let you read/verify the firmware on the chip, letting you know connectivity is solid. Once you’re there, you’re ready to start coding!

I wish I were friends with someone who programmed PIC, such that in 5 minutes I could be shown what took a couple hours to figure out. There are quite a few tutorials out there – borderline too many, and they all seem to be a bit different. To quickly get acquainted with the PIC programming environment, I followed the “Hello World” Program in C tutorial on PIC18F.com. Unfortunately, it didn’t work as posted, likely because their example code was based on a PIC 18F4550 and mine is an 18F2450, but I still don’t understand why such a small difference caused such a big problem. The problem was in their use of LATDbits and TRISDbits (which I tried to replace with LATBbits and TRISBbits). I got around it by manually addressing TRISB and LATB. Anyway, this is what I came up with:

#include <p18f2450.h> // load pin names
#include <delays.h>   // load delay library

#pragma config WDT = OFF // disable watchdog timer
#pragma config FOSC = INTOSCIO_EC // use internal clock

void main() // this is the main program
{
	TRISB=0B00000000; // set all pins on port B as output
	while(1) // execute the following code block forever
	{
		LATB = 0b11111111; // turn all port B pins ON
		Delay10KTCYx(1);   // pause 1 second
		LATB = 0b00000000; // turn all port B pins OFF
		Delay10KTCYx(1);   // pause 1 second
	}
}


A couple notes about the code: the WDT=OFF disables the watchdog timer, which if left unchecked would continuously reboot the microcontroller. The FOSC=INTOSCIO_EC section tells the microcontroller to use its internal oscillator, allowing it to execute code without necessitating an external crystal or other clock source. As to what TRIS and LAT do, I’ll refer you to basic I/O operations with PIC.

Here is what the MPLAB IDE looked like after I successfully loaded the code onto the microcontroller. At this time, the LED began blinking about once per second. I guess that about wraps it up! This afternoon I pulled a PIC out of my junk box and, having never programmed a PIC before, successfully loaded the software, got my programmer up and running, and have a little functioning circuit. I know it isn’t that big of a deal, but it’s a step in the right direction, and I’m glad I’ve taken it.





Sometimes it’s tempting to re-invent the wheel to make a device function exactly the way you want. I am re-visiting the field of homemade electrophysiology equipment, and although I’ve already published a home made electocardiograph (ECG), I wish to revisit that project and make it much more elegant, while also planning for a pulse oximeter, an electroencephalograph (EEG), and an electrogastrogram (EGG). This project is divided into 3 major components: the low-noise microvoltage amplifier, a digital analog to digital converter with PC connectivity, and software to display and analyze the traces. My first challenge is to create that middle step, a device to read voltage (from 0-5V) and send this data to a computer.

This project demonstrates a simple solution for the frustrating problem of sending data from a microcontroller to a PC with a USB connection. My solution utilizes a USB FTDI serial-to-usb cable, allowing me to simply put header pins on my device which I can plug into providing the microcontroller-computer link. This avoids the need for soldering surface-mount FTDI chips (which gets expensive if you put one in every project). FTDI cables are inexpensive (about $11 shipped on eBay) and I’ve gotten a lot of mileage out of mine and know I will continue to use it for future projects. If you are interested in MCU/PC communication, consider one of these cables as a rapid development prototyping tool. I’m certainly enjoying mine!

It is important to me that my design is minimalistic, inexpensive, and functions natively on Linux and Windows without installing special driver-related software, and can be visualized in real-time using native Python libraries, such that the same code can be executed identically on all operating systems with minimal computer-side configuration. I’d say I succeeded in this effort, and while the project could use some small touches to polish it up, it’s already solid and proven in its usefulness and functionality.

This is my final device. It’s reading voltage on a single pin, sending this data to a computer through a USB connection, and custom software (written entirely in Python, designed to be a cross-platform solution) displays the signal in real time. Although it’s capable of recording and displaying 5 channels at the same time, it’s demonstrated displaying only one. Let’s check-out a video of it in action:

This 5-channel realtime USB analog sensor, coupled with custom cross-platform open-source software, will serve as the foundation for a slew of electrophysiological experiments, but can also be easily expanded to serve as an inexpensive multichannel digital oscilloscope. While more advanced solutions exist, this has the advantage of being minimally complex (consisting of a single microchip), inexpensive, and easy to build.

 To the right is my working environment during the development of this project. You can see electronics, my computer, microchips, and coffee, but an intriguingly odd array of immunological posters in the background. I spent a couple weeks camping-out in a molecular biology laboratory here at UF and got a lot of work done, part of which involved diving into electronics again. At the time this photo was taken, I hadn’t worked much at my home workstation. It’s a cool picture, so I’m holding onto it.

Below is a simplified description of the circuit schematic that is employed in this project. Note that there are 6 ADC (analog to digital converter) inputs on the ATMega48 IC, but for whatever reason I ended-up only hard-coding 5 into the software. Eventually I’ll go back and re-declare this project a 6-channel sensor, but since I don’t have six things to measure at the moment I’m fine keeping it the way it is. RST, SCK, MISO, and MOSI are used to program the microcontroller and do not need to be connected to anything for operation. The max232 was initially used as a level converter to allow the micro-controller to communicate with a PC via the serial port. However, shortly after this project was devised an upgrade was used to allow it to connect via USB. Continue reading for details…

Below you can see the circuit breadboarded. The potentiometer (small blue box) simulated an analog input signal.

The lower board is my AVR programmer, and is connected to RST, SCK, MISO, MOSI, and GND to allow me to write code on my laptop and program the board. It’s a Fun4DIY.com AVR programmer which can be yours for $11 shipped! I’m not affiliated with their company, but I love that little board. It’s a clone of the AVR ISP MK-II.

As you can see, the USB AVR programmer I’m using is supported in Linux. I did all of my development in Ubuntu Linux, writing AVR-GCC (C) code in my favorite Linux code editor Geany, then loaded the code onto the chip with AVRDude.

I found a simple way to add USB functionality in a standard, reproducible way that works without requiring the soldering of a SMT FTDI chip, and avoids custom libraries like V-USB which don’t easily have drivers that are supported by major operating systems (Windows) without special software. I understand that the simplest long-term and commercially-logical solution would be to use that SMT chip, but I didn’t feel like dealing with it. Instead, I added header pins which allow me to snap-on a pre-made FTDI USB cable. They’re a bit expensive ($12 on ebay) but all I need is 1 and I can use it in all my projects since it’s a sinch to connect and disconnect. Beside, it supplies power to the target board! It’s supported in Linux and in Windows with established drivers that are shipped with the operating system. It’s a bit of a shortcut, but I like this solution. It also eliminates the need for the max232 chip, since it can sense the voltages outputted by the microcontroller directly.

The system works by individually reading the 10-bit ADC pins on the microcontroller (providing values from 0-1024 to represent voltage from 0-5V or 0-1.1V depending on how the code is written), converting these values to text, and sending them as a string via the serial protocol. The FTDI cable reads these values and transmits them to the PC through a USB connection, which looks like “COM5” on my Windows computer. Values can be seen in any serial terminal program (i.e., hyperterminal), or accessed through Python with the PySerial module.

As you can see, I’m getting quite good at home-brewn PCBs. While it would be fantastic to design a board and have it made professionally, this is expensive and takes some time. In my case, I only have a few hours here or there to work on projects. If I have time to design a board, I want it made immediately! I can make this start to finish in about an hour. I use a classic toner transfer method with ferric chloride, and a dremel drill press to create the holes. I haven’t attacked single-layer SMT designs yet, but I can see its convenience, and look forward to giving it a shot before too long.

Here’s the final board ready for digitally reporting analog voltages. You can see 3 small headers on the far left and 2 at the top of the chip. These are for RST, SCK, MISO, MOSI, and GND for programming the chip. Once it’s programmed, it doesn’t need to be programmed again. Although I wrote the code for an ATMega48, it works fine on a pin-compatible ATMega8 which is pictured here. The connector at the top is that FTDI USB cable, and it supplies power and USB serial connectivity to the board.

If you look closely, you can see that modified code has been loaded on this board with a Linux laptop. This thing is an exciting little board, because it has so many possibilities. It could read voltages of a single channel in extremely high speed and send that data continuously, or it could read from many channels and send it at any rate, or even cooler would be to add some bidirectional serial communication capabilities to allow the computer to tell the microcontroller which channels to read and how often to report the values back. There is a lot of potential for this little design, and I’m glad I have it working.

Unfortunately I lost the schematics to this device because I formatted the computer that had the Eagle files on it. It should be simple and intuitive enough to be able to design again. The code for the microcontroller and code for the real-time visualization software will be posted below shortly. Below are some videos of this board in use in one form or another:

Here is the code that is loaded onto the microcontroller:

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

void readADC(char adcn){
		//ADMUX = 0b0100000+adcn; // AVCC ref on ADCn
		ADMUX = 0b1100000+adcn; // AVCC ref on ADCn
		ADCSRA |= (1<<ADSC); // reset value
        while (ADCSRA & (1<<ADSC)) {}; // wait for measurement
}

int main (void){
    DDRD=255;
	init_usart();
    ADCSRA = 0b10000111; //ADC Enable, Manual Trigger, Prescaler
    ADCSRB = 0;

    int adcs[8]={0,0,0,0,0,0,0,0};

    char i=0;
	for(;;){
		for (i=0;i<8;i++){readADC(i);adcs[i]=ADC>>6;}
		for (i=0;i<5;i++){sendNum(adcs[i]);send(44);}
		readADC(0);
		send(10);// LINE BREAK
		send(13); //return
		_delay_ms(3);_delay_ms(5);
	}
}

void sendNum(unsigned int num){
	char theIntAsString[7];
	int i;
	sprintf(theIntAsString, "%u", num);
	for (i=0; i < strlen(theIntAsString); i++){
		send(theIntAsString[i]);
	}
}


void send (unsigned char c){
	while((UCSR0A & (1<<UDRE0)) == 0) {}
	UDR0 = c;
}

void init_usart () {
	// ATMEGA48 SETTINGS
	int BAUD_PRESCALE = 12;
	UBRR0L = BAUD_PRESCALE; // Load lower 8-bits
	UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits
	UCSR0A = 0;
	UCSR0B = (1<<RXEN0)|(1<<TXEN0); //rx and tx
	UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); //We want 8 data bits
}

Here is the code that runs on the computer, allowing reading and real-time graphing of the serial data. It’s written in Python and has been tested in both Linux and Windows. It requires *NO* non-standard python libraries, making it very easy to distribute. Graphs are drawn (somewhat inefficiently) using lines in TK. Subsequent development went into improving the visualization, and drastic improvements have been made since this code was written, and updated code will be shared shortly. This is functional, so it’s worth sharing.

import Tkinter, random, time
import socket, sys, serial

class App:

	def white(self):
		self.lines=[]
		self.lastpos=0

		self.c.create_rectangle(0, 0, 800, 512, fill="black")
		for y in range(0,512,50):
			self.c.create_line(0, y, 800, y, fill="#333333",dash=(4, 4))
			self.c.create_text(5, y-10, fill="#999999", text=str(y*2), anchor="w")
		for x in range(100,800,100):
			self.c.create_line(x, 0, x, 512, fill="#333333",dash=(4, 4))
			self.c.create_text(x+3, 500-10, fill="#999999", text=str(x/100)+"s", anchor="w")

		self.lineRedraw=self.c.create_line(0, 800, 0, 0, fill="red")

		self.lines1text=self.c.create_text(800-3, 10, fill="#00FF00", text=str("TEST"), anchor="e")
		for x in range(800):
			self.lines.append(self.c.create_line(x, 0, x, 0, fill="#00FF00"))

	def addPoint(self,val):
		self.data[self.xpos]=val
		self.line1avg+=val
		if self.xpos%10==0:
			self.c.itemconfig(self.lines1text,text=str(self.line1avg/10.0))
			self.line1avg=0
		if self.xpos>0:self.c.coords(self.lines[self.xpos],(self.xpos-1,self.lastpos,self.xpos,val))
		if self.xpos<800:self.c.coords(self.lineRedraw,(self.xpos+1,0,self.xpos+1,800))
		self.lastpos=val
		self.xpos+=1
		if self.xpos==800:
			self.xpos=0
			self.totalPoints+=800
			print "FPS:",self.totalPoints/(time.time()-self.timeStart)
		t.update()

	def __init__(self, t):
		self.xpos=0
		self.line1avg=0
		self.data=[0]*800
		self.c = Tkinter.Canvas(t, width=800, height=512)
		self.c.pack()
		self.totalPoints=0
		self.white()
		self.timeStart=time.time()

t = Tkinter.Tk()
a = App(t)

#ser = serial.Serial('COM1', 19200, timeout=1)
ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while True:
	while True: #try to get a reading
		#print "LISTENING"
		raw=str(ser.readline())
		#print raw
		raw=raw.replace("n","").replace("r","")
		raw=raw.split(",")
		#print raw
		try:
			point=(int(raw[0])-200)*2
			break
		except:
			print "FAIL"
			pass
	point=point/2
	a.addPoint(point)

If you re-create this device of a portion of it, let me know! I’d love to share it on my website. Good luck!





It’s been my goal for quite some time to design a simple, easy-to-replicate transmitter for high altitude balloon telemetry transmission. I’m quite satisfied by what I came up with because it’s very simple, cheap, easy to code for, and easy to change frequency. I’d say the most common alternative is a handheld amateur radio transmitter which starts around $60, requires an amateur radio license, and typically output 5W of FM on 144MHz (2m) or 440MHz (70cm). Fancier handheld radios are capable of transmitting APRS packets, and use established base station repeaters to listen to these frequencies, decode the packets, and update an internet database about current location information. Although it’s quite fancy, elegant, and technical (and expensive), I desire a much simpler, cheaper, disposable option! If my balloon lands in the Atlantic ocean, I don’t want to be out $100+ of radio equipment! This alternative is about $7.
DSCN1718

Here’s my solution. I don’t normally build things on perf-board (I prefer sloppy Manhattan construction), but since this might go near the edge of space and be jerked around in turbulent winds, I figured it would be a nice and strong way to assemble it. Anyhow, it uses a can crystal oscillator as the frequency source. These things are pretty cool, because they’re very frequency stable, even with changing temperatures.
DSCN1701

The can oscillator (28.704MHz, selected to be in a rarely-used region of the 10m amatuer radio allocation which I’m licensed to use, call sign AJ4VD) outputs 5V square waves which I use to drive two successive class C amplifiers. The signal can be shunted to ground between the two stages by a third “control” transistor, which allows micro-controller control over the final amplifier. Although it may have seemed logical to simply supply/cut power from the oscillator to key the transmitter, I decided against it because that can oscillator takes 20ms to stabilize, and I didn’t think that was fast enough for some encoding methods I wish to employ!
DSCN1717

Although during my tests I power the device from my bench-top power supply (just a few LM3805 and LM3812 regulators in a fancy case), it’s designed to be run off 3xAAA batteries (for logic) and a 9V battery (for the transmitter). I could have probably used a regulator to drop the 9V to 5V for the MCU and eliminated some extra weight, but I wonder how low the 9V will dip when I draw a heavy RF load? The 3xAAAs seemed like a sure bet, but quite at the expense of weight. I should consider the regulator option further… [ponders]

There’s the device in action while it was in a breadboard. I’ve since wired it up in a perf board (pictured) and left it to transmit into a small string of wire inside my apartment as an antenna as I went to the UF Gator Amateur Radio Club (a few miles away) and tried to tune into it. It produced a stunningly beautiful signal! I can’t wait for its first test on a high altitude balloon! Here it’s transmitting CW Morse code the words “scott rocks”, separated by appropriate call sign identification every 10 minutes, AJ4VD, my amateur radio license… of course!

DOWNLOAD: cw.mp3
DOWNLOAD: usb.mp3

Above is what the audio sounded like with a narrow CW filter (awesome, right?), and a 3KHz wide USB configuration. I think this should be more than enough to carry us through a mission, and aid in direction finding of a landed payload!

Notes about filtering: The output of this transmitter is quite harmonic-rich. The oscillator produces square waves for goodness’ sake! The class C amplifier smooths a bit of that out, but you still need some low-pass filtering, not shown on the schematic. I think for my purposes a 3-pole Chebyshev filter will suffice, but just keep this in mind in case you replicate my design. You certainly don’t want to be transmitting out of band! Below is the output of the transmitter viewed on my scope. It’s suspiciously smooth, which leads me to wonder about the accuracy of my scope! I really should get a spectrum analyzer.
DSCN1707





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!

Untitled from DJ Meyers on Vimeo.

group

Below is a panoramic photo right before the launch – see our balloon on the right? So cool!
pan

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

DSC_6013

The device consisted of 4 main components: IMG_2095a 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.
DSC_6009

Kunal demonstrates the effectiveness of our parachute with a scientific “run test”!
DSC_6015

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.

DSC_6016 DSC_6042 DSC_6049 DSC_6071

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.
DSC_6079

The balloon is ready to be inflated!
DSC_6119

The balloon is about 75% inflated at this point.
DSC_6132

Richard looking all serious as he finishes inflating the balloon.

pan

…panoramic above, zoomed below…

panZoomed

What a cool photo! It needs no words to describe!
DSC_6145

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!
DSC_6151

Here we are in the station… let’s get to work!


.
IMG_2118

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! anaglyph-hell-GL-11The 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!
DSCN1663

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!
DSCN1667

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:
hell

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;
}




[warning]This is one part of a multi-post project

view PART 1 for circuity and theory[/warning]

Last week I had the crazy idea of sending data from a PC to a microchip through the monitor, using javascript and a web interface as a ridiculously simple data transfer platform that would work on virtually any computer! While I quickly hacked together the hardware, I struggled with the web interface (I’m a little slow with javascript) and I got a lot of help from people around the internet, especially after my project (and need for assistance) was mentioned on Hack-A-Day!
DSCN1657

This is part two of a multi-page project. To fully understand what I’m trying to accomplish and why I want to accomplish it, read the first part of the project.

Finally, I have a working javascript! I’d like to thank Tom, Riskable, Ben, and Mike for their input on this script. We got it to a point where we think it’s friendly to the majority of browsers and platforms. The idea is simple – enter two bytes to send the chip, it generates it’s own checksum (an XOR of the two bytes), and it flashes it out. Here’s a photo of the interface, click it for a live demo:

flasher_interfaceHere’s the code that goes on the microchip:

#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define F_CPU 12000000UL
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"

volatile int times=1000;

char readADC(char pin){
  ADMUX = 0b1100000+pin; // AVCC ref on ADC5
  ADCSRA = 0b10000111; //ADC Enable, Manual Trigger, Prescaler 128
  ADCSRA |= (1<<ADSC); // reset value
  while (ADCSRA & ( 1<<ADSC)) {}; // wait for measurement
  return ADCH;
}

int main(void)
{
  lcd_init(LCD_DISP_ON);
  char lastClock=0;
  char thisClock=0;
  char thisClock2=0;
  char thisData=0;
  char buffer[8];

  char lastNum=0;
  char bitsGotten=0;

  int msInactive=0;

  /*for(;;){
    itoa(readADC(5), buffer, 10);
    lcd_gotoxy(0,15);
    lcd_puts(buffer);

    itoa(readADC(4), buffer, 10);
    lcd_gotoxy(8,0);
    lcd_puts(buffer);
  }*/

  for(;;){
    thisClock = readADC(5);
    if (thisClock<250){
      _delay_ms(1);
      if (readADC(5)>250) {break;}

      _delay_ms(1);
      if (readADC(4)<250) {thisData=1;}
      else {thisData=0;}
      lastNum=lastNum*2+thisData; // left shift, add data
      itoa(thisData, buffer, 10);
      lcd_puts(buffer);
      msInactive=0;

      bitsGotten++;
      if (bitsGotten==8){
        lcd_gotoxy(1,1);
        lcd_puts("=   ");
        lcd_gotoxy(2,1);
        itoa(lastNum, buffer, 10);
        lcd_puts(buffer);
        bitsGotten=0;
        lastNum=0;
        lcd_gotoxy(0,0);
      }

      while (1) {
        if (readADC(5)>250){
          _delay_ms(10);
          if (readADC(5)>250){break;}
        }
      }
    }
    else{
      msInactive++;
      if (msInactive==400){
        bitsGotten=0;
        lastNum=0;
        lcd_clrscr();
        lcd_puts(" TIMEOUT");
        _delay_ms(1000);
        lcd_clrscr();
        lcd_gotoxy(0,0);
        lcd_puts("________ =");
        lcd_gotoxy(0,0);
      }
    }
    _delay_ms(1);
  }
}

Here’s the javascript in a web page:

<html>
<head>
<style>
.flasher {
  font-weight: bold;
  text-align: center;
  color: #888888;
  width: 200px;
  height: 200px;
  background-color: black;
  float: left;
  -webkit-transform: translateZ(0);
  border-right-style:dotted;
  border-color:#888888;
  border-width:1px;
}
</style>
<script type="text/javascript">

/* Copyright 2011, Tom Hayward <tom@tomh.us>, MIT License */

var ms = 50,
  bytes = 0,
  leftblock = null,
  rightblock = null,
  statustext = null;

function sendBit(bit) {
  if (bit) {rightblock.style.backgroundColor = 'white';}
  else {rightblock.style.backgroundColor = 'black';}
  leftblock.style.backgroundColor = 'white';
  setTimeout(function() {
  leftblock.style.backgroundColor = 'black';
  rightblock.style.backgroundColor = 'black';
  }, ms);
}

function sendByte(byte) {
  var bits = 8;
  setTimeout(function() {
  var timer = setInterval(function() {
    bits--;
    sendBit(byte >> bits & 1);
    if (bits == 0) {clearInterval(timer);return;}
  }, ms * 2);
  }, ms * 2 * bits * bytes++);
}

function Pause() {
timer = setTimeout("endpause()",5000); // 3 secs
return false;
}

function endpause() {
sendData();
return false;
}

function sendData() {

  var button = document.getElementById('sendnow'),
    byte1 = parseInt(document.getElementById('b1').value),
    byte2 = parseInt(document.getElementById('b2').value),
    checksum = byte1 ^ byte2;
  leftblock = document.getElementById('leftblock');
  rightblock = document.getElementById('rightblock');
  statustext = document.getElementById('status');
  bytes = 0; // reset byte counter

  document.getElementById('b3').value = checksum;
  button.disabled = true;
  statustext.innerHTML = "Writing data...";

  sendByte(byte1);
  sendByte(byte2);
  sendByte(checksum);

  setTimeout(function() {
  statustext.innerHTML = "done";
  button.disabled = false;
  }, ms * 2 * 8 * bytes);

}

</script>
</head>
<body bgcolor="#666">

<h1>PC/MCU Flasher Interface</h1>
<code>
Byte 1: <input id="b1" type="text" name="b1" size="3" value="255" /> <br>
Byte 2: <input id="b2" type="text" name="b2" size="3" value="0" />  <br>
CHKsum: <input id="b3" type="text" name="b3" size="3" value="" disabled="disabled" />  <br>
<br>
<input id="sendnow" type="button" value="SEND NOW" onClick="javascript:Pause();" />
<br><br><br>
<p>Status: <span id="status"></span></p>
</code>
<div id="leftblock" class="flasher"> CLOCK</div>
<div id="rightblock" class="flasher"> DATA</div>

</body>
</html>




UPDATE! >>> [FINISHED PROJECT HERE]

Several days ago I had a crazy idea. I was driving to Orlando to pick my wife up from the airport and it was dark and stormy on the highway and I was thinking about the backlash I got from my Sound Card Microcontroller/PC Communication project, where I used an embarrassingly simple hardware to accomplish the simple task of exchanging a few bytes of data between a PC and microcontroller (in the face of many people who adamantly prefer more complicated “traditional standard” methods). The car in front of me drove with his emergency flashers on, and at times all I could see were his lights. At that moment the crazy idea popped in my head – I wonder if I could use a PC monitor and phototransistors to send data to a microchip? I can’t think of any immediate uses for this capability, but perhaps if I make a working prototype I’ll stumble upon some. Either way, it sounds like a fun project!
DSCN1652

The circuit is as simple as it gets. PHOTOTRANSISTOR MICROCONTROLLER CIRCUITA phototransistor is exactly what it says, a photo (light-triggered) transistor (uses small current to trigger a large current). It’s a photodiode with a small transistor circuit built in. Make sure you give it right polarity when you plug it in! For some reason (likely known to electrical engineers, not dental students) the larger metal piece in the plastic part, which I normally associate as negative for LEDs, should be plugged in the +5V for my photodiode. Again, make sure you hook yours up right. I purchased mine from eBay quite cheaply, but I’ll bet you can find some in RadioShack. Note that the value of the 22k resistor is important, and that your needed value may differ from mine. The resistor relates to sensitivity, the larger the value the more sensitive the device is to light. If it’s too sensitive, it will sense light even when aimed at a black portion of the screen.
hardcode

Initial tests were done using the pins as digital inputs. This was difficult to achieve because, even as transistorized photo-diodes, it took a large difference in light to go from 5V to 0V (even past the 2.5V threshold). After a few minutes of frustration, I decided to use ADC to measure the light intensity. I use only the most significant 8 bits (ADCH). I found that in ambient light the readings are 255, and that white monitor light is around 200. Therefore my threshold is 250 (4.88V?) and I use this for logic decisions. Here’s my setup showing the ADC value of each phototransistor translated into a 1 and 0 for clock (C) and data (D). Both are aimed toward the lamp, so both show a logical 1:
DSCN1651

My first test involved reading the data from the image above. The clock is on the bottom line, data is on the top. Every time the clock transitions from black to white, the value of the data at that point is read (white=1, black=0) and the number is placed on a screen. Here’s what it looks like in action:

Hopefully soon we can get a JavaScript interface going! Rather than swiping I’d like to just point this at the screen and let JS flash some squares for my device to read. This will allow virtually unlimited amounts of data to be transferred, albeit slowly, to the micro-controller. Here’s a preliminary sketch of how to send strings.
string

Remember now we’re using a time domain, not a 2d barcode. I really stink at writing JavaScript, I’m going to have to pull in some help on this one!