The personal website of Scott W Harden
August 18th, 2012

Geek Spin - ATTiny44 Project Prototype

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

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

(look how close I came to replicating the original:

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

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

LISTEN to the 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!)

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.

Markdown source code last modified on January 18th, 2021
---
title: Geek Spin - ATTiny44 Project Prototype
date: 2012-08-18 15:28:38
tags: microcontroller, old
---

# Geek Spin - ATTiny44 Project Prototype

__Some days you feel like working on projects to benefit humanity. The day I made this clearly wasn't one of those days.__ A little over a year ago, I got into a troll war with my friend [Mike Seese](http://www.mikeseese.com/). The joke, similar to that of [rick rolling](http://www.youtube.com/watch?v=oHg5SJYRHA0), was to get each other to unexpectedly click a link to the [Hatsune Miku version of the leekspin song](http://www.youtube.com/watch?v=kbbA9BhCTko&feature=player_embedded). 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](http://www.youtube.com/watch?v=1ygdAiDxKfI) for his birthday.

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

![](leek-spin.gif)

</div>

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

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

(look how close I came to replicating the original:

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

__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](http://www.servocity.com/html/how_do_servos_work_.html). Perhaps check out [how to control servos with AVR microcontrollers](http://www.engineersgarage.com/embedded/avr-microcontroller-projects/atmega16-servo-motor-circuit). 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.

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

![](2012-08-18-15.21.34-525x289.jpg)

</div>

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


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

![](geek_spin.gif)

</div>

__LISTEN__ to the [music with control beeps](http://www.SWHarden.com/blog/images/2012/08/GOLEEKGO.mp3) (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.

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

[![](01_findpic_thumb.jpg)](01_findpic.png)
[![](02_dropbg_thumb.jpg)](02_dropbg.jpg)
[![](08_hairtop_thumb.jpg)](08_hairtop.jpg)
[![](06_blockskirt_thumb.jpg)](06_blockskirt.jpg)
[![](body_thumb.jpg)](body.png)
[![](hairtop_thumb.jpg)](hairtop.png)

</div>

[This is the video](https://www.youtube.com/embed/kbbA9BhCTko) 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!)

__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 :)

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

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

```c

// 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.
June 24th, 2012

Introduction to PIC Programming for AVR users

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.

Markdown source code last modified on January 18th, 2021
---
title: Introduction to PIC Programming for AVR users
date: 2012-06-24 20:29:03
tags: microcontroller, old
---

# Introduction to PIC Programming for AVR users

__I'm not ashamed to say it: I'm a bit of an ATMEL guy.__ [AVR microcontrollers](http://en.wikipedia.org/wiki/Atmel_AVR) 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.

<div class="text-center">

![](150-28-DIP1-200x128.jpg)

</div>

**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](http://en.wikipedia.org/wiki/PIC_microcontroller) 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](http://en.wikipedia.org/wiki/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](http://en.wikipedia.org/wiki/PICKit) 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](http://www.obdev.at/products/vusb/index.html) 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.

<div class="text-center">

![](2012-06-24-15.57.56-525x393.jpg)

</div>

__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:_
>
> *   __[PIC microcontroller](http://en.wikipedia.org/wiki/PIC_microcontroller)__ compatible with your programmer and your software (I'm using [18F2450](http://ww1.microchip.com/downloads/en/DeviceDoc/39760d.pdf))
> *   __PIC programmer__ (I'm using a clone [PicKit 2](http://en.wikipedia.org/wiki/PICKit), [$19.99 shipped on eBay](http://www.ebay.com/sch/i.html?_trksid=p5197.m570.l1313&_nkw=pic+kit+2&_sacat=0)) - get the PICkit2 installer [here](http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en023805)
> *   Install __[MPLAB IDE](http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469&part=SW007002)__ (programming environment for PIC) - has a free version
> *   Install a __C compiler__: I'm using [PIC18 C Compiler for MPLAB](http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en010014) Lite - has a free version
>
>
__The first thing I did was familiarize myself with the pin diagram of my PIC from its datasheet__. I'm playing with an [18F2450](http://ww1.microchip.com/downloads/en/DeviceDoc/39760d.pdf) and the [datasheet is quite complete](http://ww1.microchip.com/downloads/en/DeviceDoc/39760d.pdf). 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](http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en023805) 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](http://www.pic18f.com/18f4550-c-tutorial/2009/11/16/tutorial-4-hello-world-program-in-c/) on [PIC18F.com](http://www.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:

```c
#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](http://en.wikipedia.org/wiki/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](http://www.mikroe.com/eng/chapters/view/4/chapter-3-i-o-ports/).

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

[![](pic-ledblink_thumb.jpg)](pic-ledblink.png)

</div>

__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.
June 14th, 2012

Multichannel USB Analog Sensor with ATMega48

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.

Below is a simplified description of the circuit 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.

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!

Markdown source code last modified on January 18th, 2021
---
title: Multichannel USB Analog Sensor with ATMega48
date: 2012-06-14 10:42:00
tags: microcontroller, circuit, old
---

# Multichannel USB Analog Sensor with ATMega48

__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](http://www.swharden.com/blog/2009-08-14-diy-ecg-machine-on-the-cheap/) a home made [electocardiograph](http://en.wikipedia.org/wiki/Electrocardiography) (ECG), I wish to revisit that project and make it much more elegant, while also planning for a [pulse oximeter](http://en.wikipedia.org/wiki/Pulse_oximeter), an [electroencephalograph](http://en.wikipedia.org/wiki/Electroencephalography) (EEG), and an [electrogastrogram](http://en.wikipedia.org/wiki/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](http://www.ftdichip.com/Products/Cables/USBTTLSerial.htm), 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](http://en.wikipedia.org/wiki/File:Arduino_ftdi_chip-1.jpg) (which gets expensive if you put one in every project). [FTDI cables are inexpensive](http://www.ebay.com/sch/i.html?_trksid=p5197.m570.l1313&_nkw=ftdi+cable&_sacat=0) (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.

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

[![](011_thumb.jpg)](011.jpg)

</div>

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

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

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


__Below is a simplified description of the circuit__ that is employed in this project. Note that there are 6 ADC (analog to digital converter) inputs on the [ATMega48](http://www.atmel.com/devices/atmega48.aspx) 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](http://en.wikipedia.org/wiki/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. 


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

[![](031_thumb.jpg)](031.jpg)

</div>

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

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

[![](041_thumb.jpg)](041.jpg)

</div>

**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 <a href="http://fun4diy.com/AVRISP_mkII.htm">Fun4DIY.com AVR programmer</a> 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.

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

[![](051_thumb.jpg)](051.jpg)

</div>

<p style="text-align: left;"><strong>As you can see, the USB AVR programmer I'm using is supported in Linux.</strong> I did all of my development in Ubuntu Linux, writing AVR-GCC (C) code in my favorite Linux code editor <a href="http://www.geany.org/">Geany</a>, then loaded the code onto the chip with <a href="http://www.nongnu.org/avrdude/">AVRDude</a>.</p>

<p style="text-align: left;"><strong>I found a simple way to add USB functionality in a standard, reproducible way</strong> that works without requiring the soldering of a <a href="http://en.wikipedia.org/wiki/File:Arduino_ftdi_chip-1.jpg">SMT FTDI chip</a>, and avoids custom libraries like <a href="http://www.obdev.at/products/vusb/index.html">V-USB</a> 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 href="http://www.ftdichip.com/Products/Cables/USBTTLSerial.htm">a pre-made FTDI USB cable</a>. <em><strong>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.</strong></em> 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.</p>

<blockquote><p style="text-align: left;">The system works by individually reading the 10-bit <a href="http://en.wikipedia.org/wiki/Analog-to-digital_converter">ADC</a> 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 <a href="http://www.ftdichip.com/Products/Cables/USBTTLSerial.htm">FTDI cable</a> 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 <a href="http://pyserial.sourceforge.net/">PySerial</a> module.</p></blockquote>

**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.</p>

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

[![](091_thumb.jpg)](091.jpg)

</div>

**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 <a href="http://www.atmel.com/devices/atmega48.aspx">ATMega48</a>, it works fine on a pin-compatible <a href="http://www.atmel.com/devices/atmega8.aspx">ATMega8</a> 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.</p>

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

[![](101_thumb.jpg)](101.jpg)

</div>

<p style="text-align: left;"><strong>If you look closely, you can see that modified code has been loaded</strong> 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,<strong><em> 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</em></strong>. There is a lot of potential for this little design, and I'm glad I have it working.</p>

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

[![](111_thumb.jpg)](111.jpg)

</div>

<p style="text-align: left;"><strong>Unfortunately I lost the schematics</strong> 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:</p>

![](https://www.youtube.com/embed/GJcrXoIC7Q8)
![](https://www.youtube.com/embed/HsV-LK3KO1U)
![](https://www.youtube.com/embed/tdf0wzS-H-8)
![](https://www.youtube.com/embed/VZkWUR-gAZk)

__Here is the code that is loaded onto the microcontroller:__

```c

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

```python
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!
August 5th, 2011

I before E except after Hellschreiber

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 3x5 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;
}
Markdown source code last modified on January 18th, 2021
---
title: I before E except after Hellschreiber
date: 2011-08-05 18:52:40
tags: microcontroller, circuit, old, amateur radio
---

# I before E except after Hellschreiber

**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](http://en.wikipedia.org/wiki/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?](http://www.amazon.com/Midland-GXT760VP4-36-Mile-42-Channel-Two-Way/dp/B0039YON6Q/ref=sr_1_1?ie=UTF8&qid=1312588647&sr=8-1)

___SPECIAL THANKS:__ I'd like to think [Mike Seese](http://www.mikeseese.com) for his brainstorming help in making this project a reality. [Mike](http://www.mikeseese.com) and I are working on a high altitude balloon project together, and a creative inexpensive radio link is one of our goals. Thanks [Mike](http://www.mikeseese.com)!_

__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](http://www.swharden.com/blog/2011-07-24-frequency-counter-gen2/comment-page-1/#comment-16485) 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)](http://en.wikipedia.org/wiki/Radioteletype) 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](http://en.wikipedia.org/wiki/Enigma_machine) 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](http://www.youtube.com/watch?v=mN8pkJoDDfI) and [video 2](http://www.youtube.com/watch?v=Ml00ngVwrcU)]

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

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

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

![](anaglyph-hell-GL-11.jpg)

</div>

The [designers of these things were extraordinarily creative](http://www.nonstopsystems.com/radio/hellschreiber-fonts.htm)! 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](http://en.wikipedia.org/wiki/Amateur_radio) and an amateur radio transceiver you can tune around special [Hellschreiber calling frequencies](http://www.nonstopsystems.com/radio/frank-radio-dig-mode-freqs.htm) 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](http://www.ham-radio-deluxe.com/Downloads.aspx).

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

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

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

[![](DSCN1663_thumb.jpg)](DSCN1663.jpg)

</div>

__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](http://www.taydaelectronics.com/servlet/the-709/OSC-dsh-40M-dsh-MEC-dsh-LF-CRYSTAL-OSCILLATOR-40.00/Detail), 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!

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

[![](DSCN1667_thumb.jpg)](DSCN1667.jpg)

</div>

__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](http://www.eham.net/reviews/detail/3046) and I'm completely in love with it. It's quite pricey though! You can find wide coverage receive-only radios called [radio scanners](http://en.wikipedia.org/wiki/Scanner_(radio)) (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:

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

[![](hell_thumb.jpg)](hell.png)

</div>

__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 3x5 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.

```c
// 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;
}

```
July 31st, 2011

PC/microcontroller “wireless” data transfer (part 2)

This is one part of a multi-post project

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!

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:

Here'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>
Markdown source code last modified on January 18th, 2021
---
title: PC/microcontroller “wireless” data transfer (part 2)
date: 2011-07-31 22:35:12
tags: circuit, microcontroller, old
---

# PC/microcontroller “wireless” data transfer (part 2)

This is one part of a multi-post project

__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](http://hackaday.com/2011/07/28/microcontroller-communications-using-flashing-lights/)!


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

[![](DSCN1657_thumb.jpg)](DSCN1657.jpg)

</div>

__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](http://www.swharden.com/blog/2011-07-26-pcmicrocontroller-wireless-data-transfer/).

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

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


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

![](flasher_interface.jpg)

</div>

<strong>Here's the code that goes on the microchip:</strong>

```c

#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);
  }
}
```

<strong>Here's the javascript in a web page:</strong>

```html
<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>
```
Pages