The personal website of Scott W Harden
December 23rd, 2015

Festivus Pole Video Game

December 23 is Festivus! To commemorate the occasion, I have built a traditional Festivus pole with a couple added features. To my knowledge, this is the first electronic Festivus pole on the internet.

Festivus is a holiday comically celebrated as an alternative to the pressures of commercialism commonly associated with other winter holidays. Originating from the 1997 Seinfeld episode "The Strike", the traditions of Festivus include demonstrating feats of strength, declaring common occurrences as Festivus miracles, airing of grievances, and of course the fabrication of a Festivus pole.

Over the years various Festivus poles (often made of beer cans) have been erected in government buildings alongside the nativity scene and menorah, including this year in my home state Florida (the video is a good laugh). Here, I show a Festivus pole I made made from individually illuminated diet coke cans which performs as a simple video game, controlled by a single button. The illuminated can scrolls up and down, and the goal is to push the button when the top can is lit. If successful, the speed increases, and the game continues! It's hours of jolly good fun.

After playing at my workbench for a while, I figured out a way I could light-up individual coke cans. I drilled a dozen holes in each can (with a big one in the back), stuck 3 blue LEDs (wired in parallel with a 220-ohm current limiting resistor in series) in the can, and hooked it up to 12V. This was the motivation I needed to continue...

Now for the design. I found a junk box 12V DC wall-wart power supply which I decided to commandeer for this project. Obviously a microcontroller would be the simplest way to implement this "game", and I chose to keep things as minimal as possible. I used a single 8-pin ATMEL ATTiny85 microcontroller ($1.67) which takes input from 1 push-button and sends data through two daisy-chained 74hc595 shift-registers ($0.57) to control base current of 2n3904 transistors ($.019) to illuminate LEDs which I had on hand (ebay, 1000 3mm blue LEDs, $7.50 free shipping). A LM7805 linear voltage regulator ($0.68) was used to bring the 12V to 5V, palatable for the microcontroller. Note that all prices are for individual units, and that I often buy in bulk from cheap (shady) vendors, so actual cost of construction was less.

To build the circuit, I used perf-board and all through-hole components. It's a little messy, but it gets the job done! Admire the creative resistor hops connecting shift registers and microcontroller pins. A purist would shriek at such construction, but I argue its acceptability is demonstrated in its functionality.

The installation had to be classy. To stabilize the fixture, I used epoxy resin to cement a single coke can to an upside-down Pyrex dish (previously used for etching circuit boards in ferric chloride). I then used clear packaging tape to hold each successive illuminated can in place. All wires were kept on the back side of the installment with electrical tape. Once complete, the circuit board was placed beneath the Pyrex container, and the controller (a single button in a plastic enclosure connected with a telephone cord) was placed beside it.

It's ready to play! Sit back, relax, and challenge your friends to see who can be the Festivus pole video game master!

A few notes about the code... The microcontroller ran the following C code (AVR-GCC) and is extremely simple. I manually clocked the shift registers (without using the chip's serial settings) and also manually polled for the button press (didn't even use interrupts). It's about as minimal as it gets! What improvements could be made to this Festivus hacking tradition? We will have to wait and see what the Internet comes up with next year...

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

// PB2 data
// PB1 latch
// PB0 clock
// PB4 LED

// PB3 input button

volatile int speed=400;
volatile char canlit=0;
volatile char levelsWon=0;

char buttonPressed(){
    char state;
    state = (PINB>>PB3)&1;
    if (state==0) {
        PORTB|=(1<<PB4);
        return 1;
    }
    else {
        PORTB&=~(1<<PB4);
        return 0;
    }
}

void shiftBit(char newval){
    // set data value
    if (newval==0){PORTB&=~(1<<PB2);}
    else {PORTB|=(1<<PB2);}
    // flip clock
    PORTB|=(1<<PB0);
    PORTB&=~(1<<PB0);
}

void allOff(){
    char i=0;
    for(i=0;i<16;i++){
        shiftBit(0);
    }
    updateDisplay();
}

void allOn(){
    char i=0;
    for(i=0;i<14;i++){
        shiftBit(1);
    }
    updateDisplay();
}

void onlyOne(char pos){
    if (pos>=8) {pos++;}
    char i;
    allOff();
    shiftBit(1);
    for (i=0;i<pos;i++){shiftBit(0);}
    //if (pos>8) {shiftBit(0);} // because we skip a shift pin
    updateDisplay();
    }

void updateDisplay(){PORTB|=(1<<PB1);PORTB&=~(1<<PB1);}

void ledON(){PORTB|=(1<<PB4);}
void ledOFF(){PORTB&=~(1<<PB4);}

char giveChance(){
    int count=0;
    for(count=0;count<speed;count++){
        _delay_ms(1);
        if (buttonPressed()){return 1;}
    }
    return 0;
}

void strobe(){
    char i;
    for(i=0;i<50;i++){
        allOn();_delay_ms(50);
        allOff();_delay_ms(50);
    }
}

char game(){
    for(;;){
        for(canlit=1;canlit<15;canlit++){
            onlyOne(canlit);
            if (giveChance()) {return;}
        }
        for(canlit=13;canlit>1;canlit--){
            onlyOne(canlit);
            if (giveChance()) {return;}
        }
    }
}

void levelWin(){
    char i;
    for(i=0;i<levelsWon;i++){
        allOn();
        _delay_ms(200);
        allOff();
        _delay_ms(200);
    }
}
void levelLose(){
    char i;
    for(i=0;i<20;i++){
        for(canlit=13;canlit>1;canlit--){
            onlyOne(canlit);
            _delay_ms(10);
        }
    }
}

void showSelected(){
    char i;
    for(i=0;i<20;i++){
        onlyOne(canlit);
        _delay_ms(50);
        allOff();
        _delay_ms(50);
    }
}

void nextLevel(){
    // we just pushed the button.
    showSelected();
    levelsWon++;
    if (canlit==14) {
        levelWin();
        speed-=speed/5;
        }
    else {
        levelLose();
        speed=400;
        levelsWon=0;
    }
}

int main(void){
    DDRB=(1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB4);
    char i;
    for(;;){
        game();
        nextLevel();
    }
}

Programming: note that the code was compiled and programmed onto the AVR from a linux terminal using AvrDude. The shell script I used for that is here:

rm main
rm *.hex
rm *.o
echo "MAKING O"
avr-gcc -w -Os -DF_CPU=1000000UL -mmcu=attiny85 -c -o main.o main.c
echo "MAKING BINARY"
avr-gcc -w -mmcu=attiny85 main.o -o main
echo "COPYING"
avr-objcopy -O ihex -R .eeprom main main.hex
echo "PROGRAMMING"
avrdude -c usbtiny -p t85 -F -U flash:w:"main.hex":a -U lfuse:w:0x62:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
echo "DONE"
Markdown source code last modified on January 18th, 2021
---
title: Festivus Pole Video Game
date: 2015-12-23 18:24:11
tags: old
---

# Festivus Pole Video Game

__December 23 is Festivus! To commemorate the occasion, I have built a traditional Festivus pole with a couple added features.__ To my knowledge, this is the first _electronic_ Festivus pole on the internet. <a href="http://www.SWHarden.com/blog/images/2015/12/full.gif" rel="attachment wp-att-3921">

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

![](full.gif)

</div>

**[Festivus](https://en.wikipedia.org/wiki/Festivus) is a holiday comically celebrated as an alternative to the pressures of commercialism commonly associated with other winter holidays.** Originating from the 1997 Seinfeld episode "The Strike", the traditions of Festivus include demonstrating feats of strength, declaring common occurrences as Festivus miracles, airing of grievances, and of course the fabrication of a _Festivus pole_. 

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

![](strike.jpg)

</div>

**Over the years** various Festivus poles (often made of beer cans) have been erected in government buildings alongside the nativity scene and menorah, [including this year in my home state Florida](http://www.fox35orlando.com/home/60908334-story) (the video is a good laugh). Here, I show a Festivus pole I made made from individually illuminated diet coke cans which performs as a simple video game, controlled by a single button. The illuminated can scrolls up and down, and the goal is to push the button when the top can is lit. If successful, the speed increases, and the game continues! It's _hours_ of jolly good fun.

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

__After playing at my workbench for a while, I figured out a way I could light-up individual coke cans.__ I drilled a dozen holes in each can (with a big one in the back), stuck 3 blue LEDs (wired in parallel with a 220-ohm current limiting resistor in series) in the can, and hooked it up to 12V. This was the motivation I needed to continue...

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

[![](a_thumb.jpg)](a.jpg)
[![](b_thumb.jpg)](b.jpg)
[![](c_thumb.jpg)](c.jpg)
[![](d_thumb.jpg)](d.jpg)
[![](e_thumb.jpg)](e.jpg)
[![](f_thumb.jpg)](f.jpg)

</div>

__Now for the design.__ I found a junk box 12V DC wall-wart power supply which I decided to commandeer for this project. Obviously a microcontroller would be the simplest way to implement this "game", and I chose to keep things as minimal as possible. I used a single 8-pin [ATMEL ATTiny85 microcontroller](http://www.mouser.com/ProductDetail/Atmel/ATtiny85-20PU/?qs=sGAEpiMZZMu9ReDVvI6ax7XpNAHo%252bKm8HQZQZgS5360%3d) ($1.67) which takes input from 1 push-button and sends data through two daisy-chained[ 74hc595 shift-registers](http://www.mouser.com/ProductDetail/Texas-Instruments/SN74HC595N/?qs=sGAEpiMZZMtsbn1GaJysl80qBaGZ8bjzTAKqk4DYcpY%3d) ($0.57) to control base current of [2n3904 transistors](http://www.mouser.com/search/ProductDetail.aspx?R=0virtualkey0virtualkey2N3904BU) ($.019) to illuminate LEDs which I had on hand (ebay, 1000 3mm blue LEDs, $7.50 free shipping). A [LM7805 linear voltage regulator](http://www.mouser.com/ProductDetail/Fairchild-Semiconductor/LM7805CT/?qs=sGAEpiMZZMtUqDgmOWBjgPJMQlYvsHAW%252bBbwCYnQla0%3d) ($0.68) was used to bring the 12V to 5V, palatable for the microcontroller. Note that all prices are for individual units, and that I often buy in bulk from cheap (shady) vendors, so actual cost of construction was less.

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

[![](festivus-pole-video-game-schematic_thumb.jpg)](festivus-pole-video-game-schematic.png)

</div>

__To build the circuit,__ I used perf-board and all through-hole components. It's a little messy, but it gets the job done! Admire the creative resistor hops connecting shift registers and microcontroller pins. A purist would shriek at such construction, but I argue its acceptability is demonstrated in its functionality.

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

[![](IMG_4350_thumb.jpg)](IMG_4350.jpg)
[![](IMG_4352_thumb.jpg)](IMG_4352.jpg)
[![](IMG_4362_thumb.jpg)](IMG_4362.jpg)
[![](IMG_4364_thumb.jpg)](IMG_4364.jpg)
[![](IMG_4366_thumb.jpg)](IMG_4366.jpg)

</div>

__The installation had to be classy.__ To stabilize the fixture, I used epoxy resin to cement a single coke can to an upside-down Pyrex dish (previously used for [etching circuit boards in ferric chloride](http://hackaday.com/2008/07/28/how-to-etch-a-single-sided-pcb/)). I then used clear packaging tape to hold each successive illuminated can in place. All wires were kept on the back side of the installment with electrical tape. Once complete, the circuit board was placed beneath the Pyrex container, and the controller (a single button in a plastic enclosure connected with a telephone cord) was placed beside it.

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

[![](IMG_4402_thumb.jpg)](IMG_4402.jpg)
[![](IMG_4403_thumb.jpg)](IMG_4403.jpg)
[![](IMG_4404_thumb.jpg)](IMG_4404.jpg)
[![](IMG_4406_thumb.jpg)](IMG_4406.jpg)
[![](IMG_4396_thumb.jpg)](IMG_4396.jpg)
[![](IMG_4375_thumb.jpg)](IMG_4375.jpg)

</div>

__It's ready to play!__ Sit back, relax, and challenge your friends to see who can be the Festivus pole video game master!

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

[![](IMG_4401_thumb.jpg)](IMG_4401.jpg)
[![](IMG_4408_thumb.jpg)](IMG_4408.jpg)

</div>

__A few notes about the code...__ The microcontroller ran the following C code ([AVR-GCC](http://www.nongnu.org/avr-libc/)) and is extremely simple. I manually clocked the [shift registers](https://en.wikipedia.org/wiki/Shift_register) (without using the chip's serial settings) and also manually polled for the button press (didn't even use interrupts). It's about as minimal as it gets! What improvements could be made to this Festivus hacking tradition? We will have to wait and see what the Internet comes up with next year...

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

// PB2 data
// PB1 latch
// PB0 clock
// PB4 LED

// PB3 input button

volatile int speed=400;
volatile char canlit=0;
volatile char levelsWon=0;

char buttonPressed(){
    char state;
    state = (PINB>>PB3)&1;
    if (state==0) {
        PORTB|=(1<<PB4);
        return 1;
    }
    else {
        PORTB&=~(1<<PB4);
        return 0;
    }
}

void shiftBit(char newval){
    // set data value
    if (newval==0){PORTB&=~(1<<PB2);}
    else {PORTB|=(1<<PB2);}
    // flip clock
    PORTB|=(1<<PB0);
    PORTB&=~(1<<PB0);
}

void allOff(){
    char i=0;
    for(i=0;i<16;i++){
        shiftBit(0);
    }
    updateDisplay();
}

void allOn(){
    char i=0;
    for(i=0;i<14;i++){
        shiftBit(1);
    }
    updateDisplay();
}

void onlyOne(char pos){
    if (pos>=8) {pos++;}
    char i;
    allOff();
    shiftBit(1);
    for (i=0;i<pos;i++){shiftBit(0);}
    //if (pos>8) {shiftBit(0);} // because we skip a shift pin
    updateDisplay();
    }

void updateDisplay(){PORTB|=(1<<PB1);PORTB&=~(1<<PB1);}

void ledON(){PORTB|=(1<<PB4);}
void ledOFF(){PORTB&=~(1<<PB4);}

char giveChance(){
    int count=0;
    for(count=0;count<speed;count++){
        _delay_ms(1);
        if (buttonPressed()){return 1;}
    }
    return 0;
}

void strobe(){
    char i;
    for(i=0;i<50;i++){
        allOn();_delay_ms(50);
        allOff();_delay_ms(50);
    }
}

char game(){
    for(;;){
        for(canlit=1;canlit<15;canlit++){
            onlyOne(canlit);
            if (giveChance()) {return;}
        }
        for(canlit=13;canlit>1;canlit--){
            onlyOne(canlit);
            if (giveChance()) {return;}
        }
    }
}

void levelWin(){
    char i;
    for(i=0;i<levelsWon;i++){
        allOn();
        _delay_ms(200);
        allOff();
        _delay_ms(200);
    }
}
void levelLose(){
    char i;
    for(i=0;i<20;i++){
        for(canlit=13;canlit>1;canlit--){
            onlyOne(canlit);
            _delay_ms(10);
        }
    }
}

void showSelected(){
    char i;
    for(i=0;i<20;i++){
        onlyOne(canlit);
        _delay_ms(50);
        allOff();
        _delay_ms(50);
    }
}

void nextLevel(){
    // we just pushed the button.
    showSelected();
    levelsWon++;
    if (canlit==14) {
        levelWin();
        speed-=speed/5;
        }
    else {
        levelLose();
        speed=400;
        levelsWon=0;
    }
}

int main(void){
    DDRB=(1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB4);
    char i;
    for(;;){
        game();
        nextLevel();
    }
}
```

__Programming:__ note that the code was compiled and programmed onto the AVR from a linux terminal using [AvrDude](http://www.nongnu.org/avrdude/). The shell script I used for that is here:

```bash
rm main
rm *.hex
rm *.o
echo "MAKING O"
avr-gcc -w -Os -DF_CPU=1000000UL -mmcu=attiny85 -c -o main.o main.c
echo "MAKING BINARY"
avr-gcc -w -mmcu=attiny85 main.o -o main
echo "COPYING"
avr-objcopy -O ihex -R .eeprom main main.hex
echo "PROGRAMMING"
avrdude -c usbtiny -p t85 -F -U flash:w:"main.hex":a -U lfuse:w:0x62:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
echo "DONE"
```

Epoch Timestamp Hashing

I was recently presented with the need to rename a folder of images based on a timestamp. This way, I can keep saving new files in that folder with overlapping filenames (i.e., 01.jpg, 02.jpg, 03.jpg, etc.), and every time I run this script all images are prepended with a timestamp. I still want the files to be sorted alphabetically, which is why an alphabetical timestamp (rather than a random hash) is preferred.

  • At first I considered a long date such as 2014-04-19-01.jpg, but that adds so much text!...also, it doesn't include time of day.
  • If I include time of day, it becomes 2014-04-19-09-16-23-01.jpg
  • If I eliminate dashes to shorten it, it becomes hard to read, but might work 140419091623-01.jpg
  • If I use Unix Epoch time, it becomes 1397912944-01.jpg

The result I came up with uses base conversion and a string table of numbers and letters (in alphabetical order) to create a second-respecting timestamp hash using an arbitrary number of characters. For simplicity, I used 36 characters: 0-9, and a-z. I then wrote two functions to perform arbitrary base conversion, pulling characters from the hash. Although I could have nearly doubled my available characters by including the full ASCII table, respecting capitalization, I decided to keep it simple. The scheme goes like this:

  • Determine the date / time: 19-Apr-2014 13:08:55
  • Create an integer of Unix Epoch time (seconds past Jan 1, 1970): 1397912935
  • Do a base conversion from a character list: n4a4iv
  • My file name now becomes n4a4iv-01.jpg - I can accept this!and when I sort the folder alphabetically, they're in order by the timestamp

I can now represent any modern time, down to the second, with 6 characters. Here's some example output:

19-Apr-2014 13:08:55 <-> 1397912935 <-> n4a4iv
19-Apr-2014 13:08:56 <-> 1397912936 <-> n4a4iw
19-Apr-2014 13:08:57 <-> 1397912937 <-> n4a4ix
19-Apr-2014 13:08:58 <-> 1397912938 <-> n4a4iy
19-Apr-2014 13:08:59 <-> 1397912939 <-> n4a4iz
19-Apr-2014 13:09:00 <-> 1397912940 <-> n4a4j0
19-Apr-2014 13:09:01 <-> 1397912941 <-> n4a4j1
19-Apr-2014 13:09:02 <-> 1397912942 <-> n4a4j2
19-Apr-2014 13:09:03 <-> 1397912943 <-> n4a4j3
19-Apr-2014 13:09:04 <-> 1397912944 <-> n4a4j4

Interestingly, if I change my hash characters away from the list of 36 alphanumerics and replace it with just 0 and 1, I can encode/decode the date in binary:

19-Apr-2014 13:27:28 <-> 1397914048 <-> 1010011010100100111100111000000
19-Apr-2014 13:27:29 <-> 1397914049 <-> 1010011010100100111100111000001
19-Apr-2014 13:27:30 <-> 1397914050 <-> 1010011010100100111100111000010
19-Apr-2014 13:27:31 <-> 1397914051 <-> 1010011010100100111100111000011
19-Apr-2014 13:27:32 <-> 1397914052 <-> 1010011010100100111100111000100
19-Apr-2014 13:27:33 <-> 1397914053 <-> 1010011010100100111100111000101
19-Apr-2014 13:27:34 <-> 1397914054 <-> 1010011010100100111100111000110
19-Apr-2014 13:27:35 <-> 1397914055 <-> 1010011010100100111100111000111
19-Apr-2014 13:27:36 <-> 1397914056 <-> 1010011010100100111100111001000
19-Apr-2014 13:27:37 <-> 1397914057 <-> 1010011010100100111100111001001

Here's the code to generate / decode Unix epoch timestamps in Python:

hashchars='0123456789abcdefghijklmnopqrstuvwxyz'
#hashchars='01' #for binary

def epochToHash(n):
  hash=''
  while n>0:
    hash = hashchars[int(n % len(hashchars))] + hash
    n = int(n / len(hashchars))
  return hash

def epochFromHash(s):
  s=s[::-1]
  epoch=0
  for pos in range(len(s)):
    epoch+=hashchars.find(s[pos])*(len(hashchars)**pos)
  return epoch

import time
t=int(time.time())
for i in range(10):
  t=t+1
  print(time.strftime("%d-%b-%Y %H:%M:%S", time.gmtime(t)),
              "<->", t,"<->",epochToHash(t))
Markdown source code last modified on January 18th, 2021
---
title: Epoch Timestamp Hashing
date: 2014-04-19 08:31:53
tags: python, old
---

# Epoch Timestamp Hashing

__I was recently presented with the need to rename a folder of images based on a timestamp.__ This way, I can keep saving new files in that folder with overlapping filenames (i.e., <span style="color: #339966;">01.jpg</span>, <span style="color: #339966;">02.jpg</span>, <span style="color: #339966;">03.jpg</span>, etc.), and every time I run this script all images are prepended with a timestamp. I still want the files to be sorted alphabetically, which is why an alphabetical timestamp (rather than a random hash) is preferred.

*   At first I considered a long date such as <span style="color: #339966;">2014-04-19-01.jpg</span>, but that adds so much text!...also, it doesn't include time of day.
*   If I include time of day, it becomes <span style="color: #339966;">2014-04-19-09-16-23-01.jpg</span>
*   If I eliminate dashes to shorten it, it becomes hard to read, but might work <span style="color: #339966;">140419091623-01.jpg</span>
*   If I use [Unix Epoch](http://en.wikipedia.org/wiki/Unix_time) time, it becomes <span style="color: #339966;">1397912944-01.jpg</span>

__The result I came up with uses base conversion and a string table of numbers and letters (in alphabetical order) to create a second-respecting timestamp hash using an arbitrary number of characters.__ For simplicity, I used 36 characters: 0-9, and a-z. I then wrote two functions to perform arbitrary base conversion, pulling characters from the hash. Although I could have nearly doubled my available characters by including the full ASCII table, respecting capitalization, I decided to keep it simple. The scheme goes like this:

*   Determine the date / time: <span style="color: #339966;">19-Apr-2014 13:08:55</span>
*   Create an integer of [Unix Epoch](http://en.wikipedia.org/wiki/Unix_time) time (seconds past Jan 1, 1970):  <span style="color: #339966;">1397912935</span>
*   Do a base conversion from a character list: <span style="color: #339966;">n4a4iv</span>
*   My file name now becomes <span style="color: #888888;">n4a4iv-01.jpg</span> - I can accept this!_and when I sort the folder alphabetically, they're in order by the timestamp_

__I can now represent any modern time, down to the second, with 6 characters.__ Here's some example output:

```python
19-Apr-2014 13:08:55 <-> 1397912935 <-> n4a4iv
19-Apr-2014 13:08:56 <-> 1397912936 <-> n4a4iw
19-Apr-2014 13:08:57 <-> 1397912937 <-> n4a4ix
19-Apr-2014 13:08:58 <-> 1397912938 <-> n4a4iy
19-Apr-2014 13:08:59 <-> 1397912939 <-> n4a4iz
19-Apr-2014 13:09:00 <-> 1397912940 <-> n4a4j0
19-Apr-2014 13:09:01 <-> 1397912941 <-> n4a4j1
19-Apr-2014 13:09:02 <-> 1397912942 <-> n4a4j2
19-Apr-2014 13:09:03 <-> 1397912943 <-> n4a4j3
19-Apr-2014 13:09:04 <-> 1397912944 <-> n4a4j4
```

__Interestingly, if I change my hash characters away from the list of 36 alphanumerics and replace it with just 0 and 1, I can encode/decode the date in binary:__

```python
19-Apr-2014 13:27:28 <-> 1397914048 <-> 1010011010100100111100111000000
19-Apr-2014 13:27:29 <-> 1397914049 <-> 1010011010100100111100111000001
19-Apr-2014 13:27:30 <-> 1397914050 <-> 1010011010100100111100111000010
19-Apr-2014 13:27:31 <-> 1397914051 <-> 1010011010100100111100111000011
19-Apr-2014 13:27:32 <-> 1397914052 <-> 1010011010100100111100111000100
19-Apr-2014 13:27:33 <-> 1397914053 <-> 1010011010100100111100111000101
19-Apr-2014 13:27:34 <-> 1397914054 <-> 1010011010100100111100111000110
19-Apr-2014 13:27:35 <-> 1397914055 <-> 1010011010100100111100111000111
19-Apr-2014 13:27:36 <-> 1397914056 <-> 1010011010100100111100111001000
19-Apr-2014 13:27:37 <-> 1397914057 <-> 1010011010100100111100111001001
```

__Here's the code to generate / decode Unix epoch timestamps in Python:__

```python
hashchars='0123456789abcdefghijklmnopqrstuvwxyz'
#hashchars='01' #for binary

def epochToHash(n):
  hash=''
  while n>0:
    hash = hashchars[int(n % len(hashchars))] + hash
    n = int(n / len(hashchars))
  return hash

def epochFromHash(s):
  s=s[::-1]
  epoch=0
  for pos in range(len(s)):
    epoch+=hashchars.find(s[pos])*(len(hashchars)**pos)
  return epoch

import time
t=int(time.time())
for i in range(10):
  t=t+1
  print(time.strftime("%d-%b-%Y %H:%M:%S", time.gmtime(t)),
              "<->", t,"<->",epochToHash(t))
```

Directly Driving 7-Segment Display with AVR IO Pins

I came across the need for a quick and dirty display to show a 4 digit number from a microcontroller. The right way to do this would be to use a microcontroller in combination with a collection of transistors and current limiting resistors, or even a dedicated 7-segment LED driver IC. The wrong way to do this is to wire LEDs directly to microcontroller IO pins to source and sink current way out of spec of the microcontroller... and that's exactly what I did! With no current limiting resistors, the AVR is sourcing and sinking current potentially far out of spec for the chip. But, heck, it works! With 2 components (just a microcontroller and a 4 digit, 7-segment LED display) and a piece of ribbon cable, I made something that used to be a nightmare to construct (check out this post from 3 years ago when I accomplished the same thing with a rats nest of wires - it was so much work that I never built one again!) The hacked-together method I show today might not be up to spec for medical grade equipment, but it sure works for my test rig application, and it's easy and cheap to accomplish... as long as you don't mind breaking some electrical engineering rules. Consider how important it is to know how to hack projects like this together: Although I needed this device, if it were any harder, more expensive, or less convenient to build, I simply wouldn't have built it! Sometimes hacking equipment together the wrong way is worth it.

Segments are both current sourced and sunk directly from AVR IO pins. Digits are multiplexed with 1ms illumination duration. I don't really have a part number for the component because it was a China eBay special. The display was $6.50 for 4 (free shipping). That's ~$1.65 each. The microcontroller is ~$1.[/caption]

SCHEMATIC? If you want it, read this. It's so simple I don't feel like making it. Refer to an ATMega48 pin diagram. The LCD is common anode (not common cathode), and here's the schematic on the right. I got it from eBay (link) for <$2. The connections are as follows:

  • Segments (-) A...H are directly wired to PD0...PD7

  • I call the decimal point (dp) segment "H" - I don't use current limiting resistors. I'm not making a consumer product. It works fine, especially multiplexed. Yeah I could use transistors and CLRs to drive the segments to have them bright and within current specifications, but I'm not building an airplane or designing a pacemaker, I'm making a test device at minimum cost! Direct LED wiring to my microcontroller is fine for my purposes.

  • I am multiplexing the characters of my display. I could have used a driver IC to simplify my code and eliminate the current / wiring issues described above.

  • A MAX7219 or MAX7221 would have been easy choices for this (note common anode vs. common cathode drivers / displays). It adds an extra $5 to my project cost though, so I didn't go with a driver. I drove the segments right out of my IO pins.

  • Characters (+) 1...4 are PC0...PC3

  • Obviously I apply +5V and GND to the appropriate AVR pins

Here it all is together in my microcontroller programming set up. I'll place this device in a little enclosure and an an appropriate BNC connector and either plan on giving it USB power or run it from 3xAA batteries. For now, it works pretty darn well on the breadboard.

Here is my entire programming setup. On the top left is my eBay special USB AVR programmer. On the right is a little adapter board I made to accomodate a 6 pin ISP cable and provide a small breadboard for adding programming jumpers into a bigger breadboard. The breadboard at the bottom houses the microcontroller and the display. No other components! Well, okay, a 0.1uF decoupling capacitor to provide mild debouncing for the TTL input.

Let's talk about the code. Briefly, I use an infinite loop which continuously displays the value of the volatile long integer "numba". In the display function, I set all of my segments to (+) then momentarily provide a current sink (-) on the appropriate digit anode for 1ms. I do this for each of the four characters, then repeat. How is the time (the value of "numba") incremented? Using a hardware timer and its overflow interrupt! It's all in the ATMega48 datasheet, but virtually every microcontroller has some type of timer you can use to an equivalent effect. See my earlier article "Using Timers and Counters to Clock Seconds" for details. I guess that's pretty much it! I document my code well enough below that anyone should be able to figure it out. The microcontroller is an ATMega48 (clocked 8MHz with an internal RC clock, close enough for my purposes).

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

// for simplicity, define pins as segments
#define A (1<<PD0)
#define B (1<<PD1)
#define C (1<<PD2)
#define D (1<<PD3)
#define E (1<<PD4)
#define F (1<<PD5)
#define G (1<<PD6)
#define H (1<<PD7)

void setDigit(char dig){ // set the digit starting at 0
    PORTC=(1<<dig)|(1<<PC4); // always keep the PC4 pin high
}

void setChar(char c){
    // given a number, set the appropraite segments
    switch(c){
        case 0:    DDRD=A|B|C|D|E|F;    break;
        case 1:    DDRD=B|C;            break;
        case 2:    DDRD=A|B|G|E|D;        break;
        case 3: DDRD=A|B|G|C|D;        break;
        case 4: DDRD=F|G|B|C;        break;
        case 5: DDRD=A|F|G|C|D;        break;
        case 6: DDRD=A|F|G|E|C|D;    break;
        case 7: DDRD=A|B|C;            break;
        case 8: DDRD=A|B|C|D|E|F|G;    break;
        case 9: DDRD=A|F|G|B|C;        break;
        case 31: DDRD=H;            break;
        default: DDRD=0;             break;
    }
}

void flashNumber(long num){
    char i;

    for (i=0;i<4;i++){
        setChar(num%10);
        if (i==2){DDRD|=H;} // H is the decimal point
        setDigit(3-i);
        num=num/10;
        _delay_ms(1); // time to leave the letter illuminated
    }
}

volatile long numba = 0;
volatile long addBy = 1;

ISR(PCINT1_vect){ // state change on PC4
    if ((PINC&(1<<PC4))==0) {addBy=0;} // pause
    else {numba=0;addBy=1;} // reset to 0 and resume
}

ISR(TIMER1_OVF_vect){
    TCNT1=65536-1250; // the next overflow in 1/100th of a second
    numba+=addBy;      // add 1 to the secound counter
}

int main(void)
{
    DDRC=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3); // set all characters as outputs
    DDRD=255;PORTD=0;     // set all segments as outputs, but keep them low

    TCCR1B|=(1<<CS11)|(1<<CS10); // prescaler 64
    TIMSK1|=(1<<TOIE1); //Enable Overflow Interrupt Enable
    TCNT1=65536-1250;   // the next overflow in 1/100th of a second

    // note that PC4 (PCINT12) is an input, held high, and interrupts when grounded
    PCICR |= (1<<PCIE1); // enable interrupts on PCING13..8 -> PCI1 vector
    PCMSK1 |= (1<<PCINT12); // enable PCINT12 state change to be an interrupt
    sei(); // enable global interrupts

    for(;;){flashNumber(numba);} // just show the current number repeatedly forever
}

I edit my code in Notepad++ by the way. To program the chip, I use a bash script...

avr-gcc -mmcu=atmega48 -Wall -Os -o main.elf main.c -w
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c usbtiny -p m48 -F -U flash:w:"main.hex":a -U lfuse:w:0xe2:m -U hfuse:w:0xdf:m

Nothing here is groundbreaking. It's simple, and convenient as heck. Hopefully someone will be inspired enough by this write-up that, even if they don't recreate this project, they'll jump at the opportunity to make something quick and dirty in the future. It's another example that goes to show that you don't have to draw schematics, run simulations, do calculations and etch boards to make quick projects. Just hack it together and use it.

Update a two days later... I found a similarly quick and dirty way to package this project in an enclosure. I had on hand some 85x50x21mm project boxes (eBay, 10 for $14.85, free shipping, about $1.50 each) so I used a nibbler to hack out a square to accomodate the display. After a little super glue, ribbon cable, and solder, we're good to go!

Related reading for the technically inclined:

Markdown source code last modified on January 18th, 2021
---
title: Directly Driving 7-Segment Display with AVR IO Pins
date: 2014-02-27 16:19:53
tags: circuit, old
---

# Directly Driving 7-Segment Display with AVR IO Pins

__I came across the need for a quick and dirty display to show a 4 digit number from a microcontroller.__ The _right_ way to do this would be to use a microcontroller in combination with a collection of transistors and current limiting resistors, or even a dedicated 7-segment LED driver IC. The _wrong_ way to do this is to wire LEDs directly to microcontroller IO pins to source and sink current way out of spec of the microcontroller... and that's <span style="text-decoration: underline;">exactly</span> what I did! With no current limiting resistors, the AVR is sourcing and sinking current potentially far out of spec for the chip. But, heck, it works! With 2 components (just a microcontroller and a 4 digit, 7-segment LED display) and a piece of ribbon cable, I made something that used to be a nightmare to construct (check out this post from 3 years ago when I accomplished the same thing [with a rats nest of wires](http://www.swharden.com/blog/2011-03-14-frequency-counter-finished/) - it was so much work that I never built one again!) The hacked-together method I show today might not be up to spec for medical grade equipment, but it sure works for my test rig application, and it's easy and cheap to accomplish... as long as you don't mind breaking some electrical engineering rules. Consider how important it is to know how to hack projects like this together: Although I needed this device, if it were any harder, more expensive, or less convenient to build, I simply wouldn't have built it! Sometimes hacking equipment together the wrong way is worth it.

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

[![](IMG_2316_thumb.jpg)](IMG_2316.jpg)

</div>

Segments are both current sourced and sunk directly from AVR IO pins. Digits are multiplexed with 1ms illumination duration. I don't really have a part number for the component because it was a China eBay special. The display was $6.50 for 4 (free shipping). That's ~$1.65 each. The microcontroller is ~$1.[/caption]

__SCHEMATIC?__ If you want it, read this. It's so simple I don't feel like making it. Refer to an [ATMega48 pin diagram](http://www.swharden.com/blog/images/atmega48pinout.png). The LCD is common anode (not common cathode), and here's the schematic on the right. I got it from eBay ([link](http://www.ebay.com/itm/4Pcs-7seg-4digit-LED-Display-work-with-arm7-MCU-Arduino-/280533977596?ssPageName=ADME:L:OC:US:3160)) for <$2.  The connections are as follows:


<div class="text-center">

[![](common-cathode-7-segment-display-lcd_thumb.jpg)](common-cathode-7-segment-display-lcd.jpg)

</div>

*   Segments (-) A...H are directly wired to PD0...PD7 
  * I call the decimal point (dp) segment "H" - I don't use current limiting resistors. I'm not making a consumer product. It works fine, especially multiplexed. Yeah I could use transistors and CLRs to drive the segments to have them bright and within current specifications, but I'm not building an airplane or designing a pacemaker, I'm making a test device at minimum cost! Direct LED wiring to my microcontroller is fine for my purposes.
  * I am multiplexing the characters of my display. I could have used a driver IC to simplify my code and eliminate the current / wiring issues described above. 
  * A [MAX7219 or MAX7221](http://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) would have been easy choices for this (note common anode vs. common cathode drivers / displays). It adds an extra $5 to my project cost though, so I didn't go with a driver. I drove the segments right out of my IO pins.

*   Characters (+) 1...4 are PC0...PC3

*   Obviously I apply +5V and GND to the appropriate AVR pins

__Here it all is together in my microcontroller programming set up.__ I'll place this device in a little enclosure and an an appropriate BNC connector and either plan on giving it USB power or run it from 3xAA batteries. For now, it works pretty darn well on the breadboard.

<div class="text-center">

[![](IMG_2320_thumb.jpg)](IMG_2320.jpg)

</div>

Here is my entire programming setup. On the top left is my eBay special USB AVR programmer. On the right is a little adapter board I made to accomodate a 6 pin ISP cable and provide a small breadboard for adding programming jumpers into a bigger breadboard. The breadboard at the bottom houses the microcontroller and the display. No other components! Well, okay, a 0.1uF decoupling capacitor to provide mild debouncing for the TTL input.

__Let's talk about the code.__ Briefly, I use an infinite loop which continuously displays the value of the volatile long integer "numba". In the display function, I set all of my segments to (+) then momentarily provide a current sink (-) on the appropriate digit anode for 1ms. I do this for each of the four characters, then repeat. How is the time (the value of "numba") incremented? Using a hardware timer and its overflow interrupt! It's all in the ATMega48 datasheet, but virtually every microcontroller has some type of timer you can use to an equivalent effect. See my earlier article "[_Using Timers and Counters to Clock Seconds_](http://www.swharden.com/blog/2011-06-19-using-timers-and-counters-to-clock-seconds/)" for details. I guess that's pretty much it! I document my code well enough below that anyone should be able to figure it out. The microcontroller is an [ATMega48](http://www.atmel.com/images/doc2545.pdf) (clocked 8MHz with an internal RC clock, close enough for my purposes).

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

// for simplicity, define pins as segments
#define A (1<<PD0)
#define B (1<<PD1)
#define C (1<<PD2)
#define D (1<<PD3)
#define E (1<<PD4)
#define F (1<<PD5)
#define G (1<<PD6)
#define H (1<<PD7)

void setDigit(char dig){ // set the digit starting at 0
    PORTC=(1<<dig)|(1<<PC4); // always keep the PC4 pin high
}

void setChar(char c){
    // given a number, set the appropraite segments
    switch(c){
        case 0:    DDRD=A|B|C|D|E|F;    break;
        case 1:    DDRD=B|C;            break;
        case 2:    DDRD=A|B|G|E|D;        break;
        case 3: DDRD=A|B|G|C|D;        break;
        case 4: DDRD=F|G|B|C;        break;
        case 5: DDRD=A|F|G|C|D;        break;
        case 6: DDRD=A|F|G|E|C|D;    break;
        case 7: DDRD=A|B|C;            break;
        case 8: DDRD=A|B|C|D|E|F|G;    break;
        case 9: DDRD=A|F|G|B|C;        break;
        case 31: DDRD=H;            break;
        default: DDRD=0;             break;
    }
}

void flashNumber(long num){
    char i;

    for (i=0;i<4;i++){
        setChar(num%10);
        if (i==2){DDRD|=H;} // H is the decimal point
        setDigit(3-i);
        num=num/10;
        _delay_ms(1); // time to leave the letter illuminated
    }
}

volatile long numba = 0;
volatile long addBy = 1;

ISR(PCINT1_vect){ // state change on PC4
    if ((PINC&(1<<PC4))==0) {addBy=0;} // pause
    else {numba=0;addBy=1;} // reset to 0 and resume
}

ISR(TIMER1_OVF_vect){
    TCNT1=65536-1250; // the next overflow in 1/100th of a second
    numba+=addBy;      // add 1 to the secound counter
}

int main(void)
{
    DDRC=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3); // set all characters as outputs
    DDRD=255;PORTD=0;     // set all segments as outputs, but keep them low

    TCCR1B|=(1<<CS11)|(1<<CS10); // prescaler 64
    TIMSK1|=(1<<TOIE1); //Enable Overflow Interrupt Enable
    TCNT1=65536-1250;   // the next overflow in 1/100th of a second

    // note that PC4 (PCINT12) is an input, held high, and interrupts when grounded
    PCICR |= (1<<PCIE1); // enable interrupts on PCING13..8 -> PCI1 vector
    PCMSK1 |= (1<<PCINT12); // enable PCINT12 state change to be an interrupt
    sei(); // enable global interrupts

    for(;;){flashNumber(numba);} // just show the current number repeatedly forever
}
```

I edit my code in [Notepad++](http://notepad-plus-plus.org/) by the way. To program the chip, I use a bash script...

```bash
avr-gcc -mmcu=atmega48 -Wall -Os -o main.elf main.c -w
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c usbtiny -p m48 -F -U flash:w:"main.hex":a -U lfuse:w:0xe2:m -U hfuse:w:0xdf:m
```

__Nothing here is groundbreaking.__ It's simple, and convenient as heck. Hopefully someone will be inspired enough by this write-up that, even if they don't recreate _this_ project, they'll jump at the opportunity to make something quick and dirty in the future. It's another example that goes to show that you don't have to draw schematics, run simulations, do calculations and etch boards to make quick projects. Just hack it together and use it.

__Update a two days later... __I found a similarly quick and dirty way to package this project in an enclosure. I had on hand some 85x50x21mm project boxes (eBay, 10 for $14.85, free shipping, about $1.50 each) so I used a [nibbler](http://www.amazon.com/Hand-Sheet-Metal-Nibbler-Cutter/dp/B000T5FV4Q) to hack out a square to accomodate the display. After a little super glue, ribbon cable, and solder, we're good to go!

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

[![](IMG_2336_thumb.jpg)](IMG_2336.jpg)
[![](IMG_2351_thumb.jpg)](IMG_2351.jpg)
[![](IMG_2355_thumb.jpg)](IMG_2355.jpg)
[![](IMG_2356_thumb.jpg)](IMG_2356.jpg)
[![](IMG_2362_thumb.jpg)](IMG_2362.jpg)
[![](IMG_2380_thumb.jpg)](IMG_2380.jpg)

</div>

__Related reading for the technically inclined:__

*   [Interfacing LEDs with the AVR microcontroller](http://www.avr-tutorials.com/interfacing/interfacing-leds-avr-microcontroller) (the right way)
*   [Driving LEDS with vs. without a current limiting resistor](http://tinkerlog.com/2009/04/05/driving-an-led-with-or-without-a-resistor/) (nice write-up!)
*   Official datasheet for [driving LCDs with AVR](http://www.atmel.com/Images/doc2569.pdf) (not LEDs)
*   Official datasheet for [using AVR timers](http://www.atmel.com/Images/doc2505.pdf)

Calculate QRSS Transmission Time with Python

How long does a particular bit of Morse code take to transmit at a certain speed? This is a simple question, but when sitting down trying to design schemes for 10-minute-window QRSS, it doesn't always have a quick and simple answer. Yeah, you could sit down and draw the pattern on paper and add-up the dots and dashes, but why do on paper what you can do in code? The following speaks for itself. I made the top line say my call sign in Morse code (AJ4VD), and the program does the rest. I now see that it takes 570 seconds to transmit AJ4VD at QRSS 10 speed (ten second dots), giving me 30 seconds of free time to kill.

Output of the following script, displaying info about "AJ4VD" (my call sign).

Here's the Python code I whipped-up to generate the results:

xmit=" .- .--- ....- ...- -..  " #callsign
dot,dash,space,seq="_-","_---","_",""
for c in xmit:
    if c==" ": seq+=space
    elif c==".": seq+=dot
    elif c=="-": seq+=dash
print "QRSS sequence:n",seq,"n"
for sec in [1,3,5,10,20,30,60]:
    tot=len(seq)*sec
    print "QRSS %02d: %d sec (%.01f min)"%(sec,tot,tot/60.0)

How ready am I to implement this in the microchip? Pretty darn close. I've got a surprisingly stable software-based time keeping solution running continuously executing a "tick()" function thanks to hardware interrupts. It was made easy thanks to Frank Zhao's AVR Timer Calculator. I could get it more exact by using a /1 prescaler instead of a /64, but this well within the range of acceptability so I'm calling it quits!

Output frequency is 1.0000210 Hz. That'll drift 2.59 sec/day. I'm cool with that.

Markdown source code last modified on January 18th, 2021
---
title: Calculate QRSS Transmission Time with Python
date: 2013-06-23 21:19:59
tags: qrss, python, old
---

# Calculate QRSS Transmission Time with Python

__How long does a particular bit of Morse code take to transmit at a certain speed?__ This is a simple question, but when sitting down trying to design schemes for 10-minute-window QRSS, it doesn't always have a quick and simple answer. Yeah, you could sit down and draw the pattern on paper and add-up the dots and dashes, but why do on paper what you can do in code? The following speaks for itself. I made the top line say my call sign in Morse code (AJ4VD), and the program does the rest. I now see that it takes 570 seconds to transmit AJ4VD at QRSS 10 speed (ten second dots), giving me 30 seconds of free time to kill.


<div class="text-center">

[![](qrss-calclator_thumb.jpg)](qrss-calclator.png)

</div>

Output of the following script, displaying info about "AJ4VD" (my call sign).

__Here's the Python code I whipped-up to generate the results:__

```python
xmit=" .- .--- ....- ...- -..  " #callsign
dot,dash,space,seq="_-","_---","_",""
for c in xmit:
    if c==" ": seq+=space
    elif c==".": seq+=dot
    elif c=="-": seq+=dash
print "QRSS sequence:n",seq,"n"
for sec in [1,3,5,10,20,30,60]:
    tot=len(seq)*sec
    print "QRSS %02d: %d sec (%.01f min)"%(sec,tot,tot/60.0)
```

__How ready am I to implement this in the microchip? __Pretty darn close. I've got a surprisingly stable software-based time keeping solution running continuously executing a "tick()" function thanks to hardware interrupts. It was made easy thanks to [Frank Zhao's AVR Timer Calculator](http://www.frank-zhao.com/cache/avrtimercalc.php). I could get it more exact by using a /1 prescaler instead of a /64, but this well within the range of acceptability so I'm calling it quits!


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

[![](photo-11_thumb.jpg)](photo-11.jpg)

</div>

Output frequency is 1.0000210 Hz. That'll drift 2.59 sec/day. I'm cool with that.

Crystal Oven Testing

To maintain high frequency stability, RF oscillator circuits are sometimes "ovenized" where their temperature is raised slightly above ambient room temperature and held precisely at one temperature. Sometimes just the crystal is heated (with a "crystal oven"), and other times the entire oscillator circuit is heated. The advantage of heating the circuit is that other components (especially metal core instructors) are temperature sensitive. Googling for the phrase "crystal oven", you'll find no shortage of recommended circuits. Although a more complicated PID (proportional-integral-derivative) controller may seem enticing for these situations, the fact that the enclosure is so well insulated and drifts so little over vast periods of time suggests that it might not be the best application of a PID controller. One of my favorite write-ups is from M0AYF's site which describes how to build a crystal oven for QRSS purposes. He demonstrates the MK1 and then the next design the MK2 crystal oven controller.

Briefly, desired temperature is set with a potentiometer. An operational amplifier (op-amp) compares the target temperature with measured temperature (using a thermistor - a resistor which varies resistance by tempearture). If the measured temperature is below the target, the op-amp output goes high, and current flows through heating resistors. There are a few differences between the two circuits, but one of the things that struck me as different was the use of negative feedback with the operational amplifier. This means that rather than being on or off (like the air conditioning in your house), it can be on a little bit. I wondered if this would greatly affect frequency stability. In the original circuit, he mentions

The oven then cycles on and off roughly every thirty or forty seconds and hovers around 40 degrees-C thereafter to within better than one degree-C. I wondered how much this on/off heater cycle affected temperature. Is it negligible, or could it affect frequency of an oscillator circuit? Indeed his application heats an entire enclosure so small variations get averaged-out by the large thermal mass. However in crystal oven designs where only the crystal is heated, such as described by Bill (W4HBK), I'll bet the effect is much greater. Compare the thermal mass of these two concepts.

How does the amount of thermal mass relate to how well it can be controlled? How important is negative feedback for partial-on heater operation? Can simple ON/OFF heater regulation adequately stabalize a crystal or enclosure? I'd like to design my own heater, pulling the best elements from the rest I see on the internet. My goals are:

  1. use inexpensive thermistors instead of linear temperature sensors (like LM335)
  2. use inexpensive quarter-watt resistors as heaters instead of power resistors
  3. be able to set temperature with a knob
  4. be able to monitor temperature of the heater
  5. be able to monitor power delivered to the heater
  6. maximum long-term temperature stability

Right off the bat, I realized that this requires a PC interface. Even if it's not used to adjust temperature (an ultimate goal), it will be used to log temperature and power for analysis. I won't go into the details about how I did it, other than to say that I'm using an ATMEL ATMega8 AVR microcontroller and ten times I second I sample voltage on each of it's six 10-bit ADC pins (PC0-PC5), and send that data to the computer with USART using an eBay special serial/USB adapter based on FTDI. They're <$7 (shipped) and come with the USB cable. Obviously in a consumer application I'd etch boards and use the SMT-only FTDI chips, but for messing around at home I a few a few of these little adapters. They're convenient as heck because I can just add a heater to my prototype boards and it even supplies power and ground. Convenient, right? Power is messier than it could be because it's being supplied by the PC, but for now it gets the job done. On the software side, Python with PySerial listens to the serial port and copies data to a large numpy array, saving it every once and a while. Occasionally a bit is sent wrong and a number is received incorrectly (maybe one an hour), but the error is recognized and eliminated by the checksum (just the sum of all transmitted numbers). Plotting is done with numpy and matpltolib. Code for all of that is at the bottom of this post.

That's the data logger circuit I came up with. Reading six channels ten times a second, it's more than sufficient for voltage measurement. I went ahead and added an op-amp to the board too, since I knew I'd be using one. I dedicated one of the channels to serve as ambient temperature measurement. See the little red thermistor by the blue resistor? I also dedicated another channel to the output of the op-amp. This way I can measure drive to whatever temperature controller circuity I choose to use down the road. For my first test, I'm using a small thermal mass like one would in a crystal oven. Here's how I made that:

I then build the temperature controller part of the circuit. It's pretty similar to that previously published. it uses a thermistor in a voltage divider configuration to sense temperature. It uses a trimmer potentiometer to set temperature. An LED indicator light gives some indication of on/off, but keep in mind that a fraction of a volt will turn the Darlington transistor (TIP122) on slightly although it doesn't reach a level high enough to drive the LED. The amplifier by default is set to high gain (55x), but can be greatly lowered (negative gain actually) with a jumper. This lets me test how important gain is for the circuitry.

When using a crystal oven configuration, I concluded high high gain (cycling the heater on/off) is a BAD idea. While average temperature is held around the same, the crystal oscillates. This is what is occurring above when M0AYF indicates his MK1 heater turns on and off every 40 seconds. While you might be able to get away with it while heating a chassis or something, I think it's easy to see it's not a good option for crystal heaters. Instead, look at the low gain (negative gain) configuration. It reaches temperature surprisingly quickly and locks to it steadily. Excellent.

high gain configuration tends to oscillate every 30 seconds

low gain / negative gain configuration is extremely stable (fairly high temperature)

Here's a similar experiment with a lower target temperature. Noise is due to unregulated USB power supply / voltage reference. Undeniably, this circuit does not oscillate much if any

Clearly low (or negative) gain is best for crystal heaters. What about chassis / enclosure heaters? Let's give that a shot. I made an enclosure heater with the same 2 resistors. Again, I'm staying away from expensive components, and that includes power resistors. I used epoxy (gorilla glue) to cement them to the wall of one side of the enclosure.

I put a "heater sensor" thermistor near the resistors on the case so I could get an idea of the heat of the resistors, and a "case sensor" on the opposite side of the case. This will let me know how long it takes the case to reach temperature, and let me compare differences between using near vs. far sensors (with respect to the heating element) to control temperature. I ran the same experiments and this is what I came up with!

heater temperature (blue) and enclosure temperature (green) with low gain (first 20 minutes), then high gain (after) operation. High gain sensor/feedback loop is sufficient to induce oscillation, even with the large thermal mass of the enclosure

CLOSE SENSOR CONTROL, LOW/HIGH GAIN: TOP: heater temperature (blue) and enclosure temperature (green) with low gain (first 20 minutes), then high gain (after) operation. High gain sensor/feedback loop is sufficient to induce oscillation, even with the large thermal mass of the enclosure. BOTTOM: power to the heater (voltage off the op-amp output going into the base of the Darlington transistor). Although I didn't give the low-gain configuration time to equilibrate, I doubt it would have oscillated on a time scale I am patient enough to see. Future, days-long experimentation will be required to determine if it oscillates significantly.[/caption]

Even with the far sensor (opposite side of the enclosure as the heater) driving the operational amplifier in high gain mode, oscillations occur. Due to the larger thermal mass and increased distance the heat must travel to be sensed they take much longer to occur, leading them to be slower and larger than oscillations seen earlier when the heater was very close to the sensor.

FAR SENSOR CONTROL, HIGH GAIN: Even with the far sensor (opposite side of the enclosure as the heater) driving the operational amplifier in high gain mode, oscillations occur. Blue is the far sensor temperature. Green is the sensor near the heater temperature. Due to the larger thermal mass and increased distance the heat must travel to be sensed they take much longer to occur, leading them to be slower and larger than oscillations seen earlier when the heater was very close to the sensor.

Right off the bat, we observe that even with the increased thermal mass of the entire enclosure (being heated with two dinky 100 ohm 1/4 watt resistors) the system is prone to temperature oscillation if gain is set too high. For me, this is the final nail in the coffin - I will never use a comparator-type high gain sensor/regulation loop to control heater current. With that out, the only thing to compare is which is better: placing the sensor near the heating element, or far from it. In reality, with a well-insulated device like I seem to have, it seems like it doesn't make much of a difference! The idea is that by placing it near the heater, it can stabilize quickly. However, placing it far from the heater will give it maximum sensation of "load" temperature. Anywhere in-between should be fine. As long as it's somewhat thermally coupled to the enclosure, enclosure temperature will pull it slightly away from heater temperature regardless of location. Therefore, I conclude it's not that critical where the sensor is placed, as long as it has good contact with the enclosure. Perhaps with long-term study (on the order of hours to days) slow oscillations may emerge, but I'll have to build it in a more permanent configuration to test it out. Lucky, that's exactly what I plan to do, so check back a few days from now!

Since the data speaks for itself, I'll be concise with my conclusions:

  • two 1/4 watt 100 Ohm resistors in parallel (50 ohms) are suitable to heat an insulated enclosure with 12V
  • two 1/4 watt 100 Ohm resistors in parallel (50 ohms) are suitable to heat a crystal with 5V
  • low gain or negative gain is preferred to prevent oscillating tempeartures
  • Sensor location on an enclosure is not critical as long as it's well-coupled to the enclosure and the entire enclosure is well-insulated.

I feel satisfied with today's work. Next step is to build this device on a larger scale and fix it in a more permanent configuration, then leave it to run for a few weeks and see how it does. On to making the oscillator! If you have any questions or comments, feel free to email me. If you recreate this project, email me! I'd love to hear about it.

Here's the code that went on the ATMega8 AVR (it continuously transmits voltage measurements on 6 channels).

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

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

/*
ISR(ADC_vect)
{
    PORTD^=255;
}
*/

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

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

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

int readADC(char adcn){
    ADMUX = 0b0100000+adcn;
    ADCSRA |= (1<<ADSC); // reset value
    while (ADCSRA & (1<<ADSC)) {}; // wait for measurement
    return ADC>>6;
}

int sendADC(char adcn){
    int val;
    val=readADC(adcn);
    sendNum(val);
    USART_Transmit(',');
    return val;
}

int main(void){
    ADCSRA = (1<<ADEN)  | 0b111;
    DDRB=255;
    USART_Init();
    int checksum;

    for(;;){
        PORTB=255;
        checksum=0;
        checksum+=sendADC(0);
        checksum+=sendADC(1);
        checksum+=sendADC(2);
        checksum+=sendADC(3);
        checksum+=sendADC(4);
        checksum+=sendADC(5);
        sendNum(checksum);
        USART_Transmit('n');
        PORTB=0;
        _delay_ms(200);
    }
}

Here's the command I used to compile the code, set the AVR fuse bits, and load it to the AVR.

del *.elf
del *.hex
avr-gcc -mmcu=atmega8 -Wall -Os -o main.elf main.c -w
pause
cls
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c usbtiny -p m8 -F -U flash:w:"main.hex":a -U lfuse:w:0xe4:m -U hfuse:w:0xd9:m

Here's the code that runs on the PC to listen to the microchip, match the data to the checksum, and log it occasionally.

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

line=ser.readline()[:-1]
t1=time.time()
lines=0

data=[]

def adc2R(adc):
    Vo=adc*5.0/1024.0
    Vi=5.0
    R2=10000.0
    R1=R2*(Vi-Vo)/Vo
    return R1

while True:
    line=ser.readline()[:-1]
    lines+=1
    if "," in line:
        line=line.split(",")
        for i in range(len(line)):
            line[i]=int(line[i][::-1])

    if line[-1]==sum(line[:-1]):
        line=[time.time()]+line[:-1]
        print lines, line
        data.append(line)
    else:
        print  lines, line, "<-- FAIL"

    if lines%50==49:
        numpy.save("data.npy",data)
        print "nSAVINGn%d lines in %.02f sec (%.02f vals/sec)n"%(lines,
            time.time()-t1,lines/(time.time()-t1))

Here's the code that runs on the PC to graph data.

import matplotlib
matplotlib.use('TkAgg') # <-- THIS MAKES IT FAST!
import numpy
import pylab
import datetime
import time

def adc2F(adc):
    Vo=adc*5.0/1024.0
    K=Vo*100
    C=K-273
    F=C*(9.0/5)+32
    return F

def adc2R(adc):
    Vo=adc*5.0/1024.0
    Vi=5.0
    R2=10000.0
    R1=R2*(Vi-Vo)/Vo
    return R1

def adc2V(adc):
    Vo=adc*5.0/1024.0
    return Vo

if True:
    print "LOADING DATA"
    data=numpy.load("data.npy")
    data=data
    print "LOADED"

    fig=pylab.figure()
    xs=data[:,0]
    tempAmbient=data[:,1]
    tempPower=data[:,2]
    tempHeater=data[:,3]
    tempCase=data[:,4]
    dates=(xs-xs[0])/60.0
    #dates=[]
    #for dt in xs: dates.append(datetime.datetime.fromtimestamp(dt))

    ax1=pylab.subplot(211)
    pylab.title("Temperature Controller - Low Gain")
    pylab.ylabel('Heater (ADC)')
    pylab.plot(dates,tempHeater,'b-')
    pylab.plot(dates,tempCase,'g-')
    #pylab.axhline(115.5,color="k",ls=":")

    #ax2=pylab.subplot(312,sharex=ax1)
    #pylab.ylabel('Case (ADC)')
    #pylab.plot(dates,tempCase,'r-')
    #pylab.plot(dates,tempAmbient,'g-')
    #pylab.axhline(0,color="k",ls=":")

    ax2=pylab.subplot(212,sharex=ax1)
    pylab.ylabel('Heater Power')
    pylab.plot(dates,tempPower)

    #fig.autofmt_xdate()
    pylab.xlabel('Elapsed Time (min)')

    pylab.show()

print "DONE"
Markdown source code last modified on January 18th, 2021
---
title: Crystal Oven Testing
date: 2013-06-22 15:14:37
tags: circuit, old
---

# Crystal Oven Testing

__To maintain high frequency stability, RF oscillator circuits are sometimes "ovenized" where their temperature is raised slightly above ambient room temperature and held precisely at one temperature.__ Sometimes just the crystal is heated (with a "crystal oven"), and other times the entire oscillator circuit is heated. The advantage of heating the circuit is that other components (especially metal core instructors) are temperature sensitive. Googling for the phrase "crystal oven", you'll find no shortage of recommended circuits. Although a more complicated[ PID (proportional-integral-derivative) controller](http://en.wikipedia.org/wiki/PID_controller) may seem enticing for these situations, the fact that the enclosure is so well insulated and drifts so little over vast periods of time suggests that it might not be the best application of a PID controller. One of my favorite write-ups is from [M0AYF's site](http://www.qsl.net/m0ayf/Crystal-Ovens.html) which describes how to build a crystal oven for QRSS purposes. He demonstrates the MK1 and then the next design the MK2 crystal oven controller.

__Briefly, desired temperature is set with a potentiometer.__ An operational amplifier (op-amp) compares the target temperature with measured temperature (using a thermistor - a resistor which varies resistance by tempearture). If the measured temperature is below the target, the op-amp output goes high, and current flows through heating resistors. There are a few differences between the two circuits, but one of the things that struck me as different was the use of negative feedback with the operational amplifier. This means that rather than being on or off (like the air conditioning in your house), it can be on a little bit. I wondered if this would greatly affect frequency stability. In the original circuit, he mentions

The oven then cycles on and off roughly every thirty or forty seconds and hovers around 40 degrees-C thereafter to within better than one degree-C.
__I wondered how much this on/off heater cycle affected temperature. Is it negligible, or could it affect frequency of an oscillator circuit?__ Indeed his application heats [an entire enclosure](http://www.qsl.net/m0ayf/Crystal-Ovens/OCXO.jpg) so small variations get averaged-out by the large thermal mass. However in crystal oven designs where only the crystal is heated, such as [described by Bill (W4HBK)](http://pensacolasnapper.blogspot.com/2011_03_01_archive.html), I'll bet the effect is much greater. Compare the thermal mass of these two concepts.

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

[![](m0ayf-enclosure-oven_thumb.jpg)](m0ayf-enclosure-oven.jpg)
[![](w4hbk-crystal-oven_thumb.jpg)](w4hbk-crystal-oven.jpg)

</div>

__How does the amount of thermal mass relate to how well it can be controlled?__ How important is negative feedback for partial-on heater operation? Can simple ON/OFF heater regulation adequately stabalize a crystal or enclosure? I'd like to design my own heater, pulling the best elements from the rest I see on the internet. My goals are:

1.   use inexpensive thermistors instead of linear temperature sensors (like [LM335](http://www.ti.com/lit/ds/symlink/lm335.pdf))
2.   use inexpensive quarter-watt resistors as heaters instead of power resistors
3.   be able to set temperature with a knob
4.   be able to monitor temperature of the heater
5.   be able to monitor power delivered to the heater
6.   maximum long-term temperature stability

__Right off the bat, I realized that this requires a PC interface.__ Even if it's not used to adjust temperature (an ultimate goal), it will be used to log temperature and power for analysis. I won't go into the details about how I did it, other than to say that I'm using an ATMEL ATMega8 AVR microcontroller and ten times I second I sample voltage on each of it's six 10-bit ADC pins (PC0-PC5), and send that data to the computer with USART using an eBay special serial/USB adapter based on FTDI. They're <$7 (shipped) and come with the USB cable. Obviously in a consumer application I'd etch boards and use the SMT-only FTDI chips, but for messing around at home I a few a few of these little adapters. They're convenient as heck because I can just add a heater to my prototype boards and it even supplies power and ground. Convenient, right? Power is messier than it could be because it's being supplied by the PC, but for now it gets the job done. On the software side, Python with PySerial listens to the serial port and copies data to a large numpy array, saving it every once and a while. Occasionally a bit is sent wrong and a number is received incorrectly (maybe one an hour), but the error is recognized and eliminated by the checksum (just the sum of all transmitted numbers). Plotting is done with numpy and matpltolib. Code for all of that is at the bottom of this post.

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

[![](IMG_0278_thumb.jpg)](IMG_0278.jpg)
[![](IMG_0279_thumb.jpg)](IMG_0279.jpg)
[![](IMG_0280_thumb.jpg)](IMG_0280.jpg)
[![](IMG_0282_thumb.jpg)](IMG_0282.jpg)

</div>

__That's the data logger circuit I came up with.__ Reading six channels ten times a second, it's more than sufficient for voltage measurement. I went ahead and added an op-amp to the board too, since I knew I'd be using one. I dedicated one of the channels to serve as ambient temperature measurement. See the little red thermistor by the blue resistor? I also dedicated another channel to the output of the op-amp. This way I can measure drive to whatever temperature controller circuity I choose to use down the road. For my first test, I'm using a small thermal mass like one would in a crystal oven. Here's how I made that:

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

[![](IMG_0265_thumb.jpg)](IMG_0265.jpg)
[![](IMG_0264_thumb.jpg)](IMG_0264.jpg)
[![](IMG_0263_thumb.jpg)](IMG_0263.jpg)
[![](IMG_0262_thumb.jpg)](IMG_0262.jpg)

</div>

__I then build the temperature controller part of the circuit.__ It's pretty similar to that previously published. it uses a thermistor in a voltage divider configuration to sense temperature. It uses a trimmer potentiometer to set temperature. An LED indicator light gives some indication of on/off, but keep in mind that a fraction of a volt will turn the Darlington transistor (TIP122) on slightly although it doesn't reach a level high enough to drive the LED. The amplifier by default is set to high gain (55x), but can be greatly lowered (negative gain actually) with a jumper. This lets me test how important gain is for the circuitry.

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

[![](controller_thumb.jpg)](controller.png)

</div>

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

[![](IMG_0261_thumb.jpg)](IMG_0261.jpg)

</div>

__When using a crystal oven configuration, I concluded high high gain (cycling the heater on/off) is a BAD idea.__ While average temperature is held around the same, the crystal oscillates. This is what is occurring above when M0AYF indicates his MK1 heater turns on and off every 40 seconds. While you might be able to get away with it while heating a chassis or something, I think it's easy to see it's not a good option for crystal heaters. Instead, look at the low gain (negative gain) configuration. It reaches temperature surprisingly quickly and locks to it steadily. Excellent.

<div class="text-center">

[![](high-gain_thumb.jpg)](high-gain.png)

</div>

high gain configuration tends to oscillate every 30 seconds

<div class="text-center">

[![](low-gain_thumb.jpg)](low-gain.png)

</div>

low gain / negative gain configuration is extremely stable (fairly high temperature)

<div class="text-center">

[![](low-gain1_thumb.jpg)](low-gain1.png)

</div>

Here's a similar experiment with a lower target temperature. Noise is due to unregulated USB power supply / voltage reference. Undeniably, this circuit does not oscillate much if any

__Clearly low (or negative) gain is best for crystal heaters.__ What about chassis / enclosure heaters? Let's give that a shot. I made an enclosure heater with the same 2 resistors. Again, I'm staying away from expensive components, and that includes power resistors. I used epoxy (gorilla glue) to cement them to the wall of one side of the enclosure.

__I put a "heater sensor" thermistor near the resistors on the case so I could get an idea of the heat of the resistors, and a "case sensor" on the opposite side of the case.__ This will let me know how long it takes the case to reach temperature, and let me compare differences between using near vs. far sensors (with respect to the heating element) to control temperature. I ran the same experiments and this is what I came up with!

<div class="text-center">

[![](case_thumb.jpg)](case.png)

</div>

heater temperature (blue) and enclosure temperature (green) with low gain (first 20 minutes), then high gain (after) operation. High gain sensor/feedback loop is sufficient to induce oscillation, even with the large thermal mass of the enclosure

__CLOSE SENSOR CONTROL, LOW/HIGH GAIN:__ TOP: heater temperature (blue) and enclosure temperature (green) with low gain (first 20 minutes), then high gain (after) operation. High gain sensor/feedback loop is sufficient to induce oscillation, even with the large thermal mass of the enclosure. BOTTOM: power to the heater (voltage off the op-amp output going into the base of the Darlington transistor). Although I didn't give the low-gain configuration time to equilibrate, I doubt it would have oscillated on a time scale I am patient enough to see. Future, days-long experimentation will be required to determine if it oscillates significantly.[/caption]

<div class="text-center">

[![](case-far-in-control_thumb.jpg)](case-far-in-control.png)

</div>

Even with the far sensor (opposite side of the enclosure as the heater) driving the operational amplifier in high gain mode, oscillations occur. Due to the larger thermal mass and increased distance the heat must travel to be sensed they take much longer to occur, leading them to be slower and larger than oscillations seen earlier when the heater was very close to the sensor.

__FAR SENSOR CONTROL, HIGH GAIN:__ Even with the far sensor (opposite side of the enclosure as the heater) driving the operational amplifier in high gain mode, oscillations occur. Blue is the far sensor temperature. Green is the sensor near the heater temperature. Due to the larger thermal mass and increased distance the heat must travel to be sensed they take much longer to occur, leading them to be slower and larger than oscillations seen earlier when the heater was very close to the sensor.

__Right off the bat, we observe that even with the increased thermal mass of the entire enclosure (being heated with two dinky 100 ohm 1/4 watt resistors) the system is prone to temperature oscillation if gain is set too high.__ For me, this is the final nail in the coffin - I will never use a comparator-type high gain sensor/regulation loop to control heater current. With that out, the only thing to compare is which is better: placing the sensor near the heating element, or far from it. In reality, with a well-insulated device like I seem to have, it seems like it doesn't make much of a difference! The idea is that by placing it near the heater, it can stabilize quickly. However, placing it far from the heater will give it maximum sensation of "load" temperature. Anywhere in-between should be fine. As long as it's somewhat thermally coupled to the enclosure, enclosure temperature will pull it slightly away from heater temperature regardless of location. Therefore, I conclude it's not that critical where the sensor is placed, as long as it has good contact with the enclosure. Perhaps with long-term study (on the order of hours to days) slow oscillations may emerge, but I'll have to build it in a more permanent configuration to test it out. Lucky, that's exactly what I plan to do, so check back a few days from now!

__Since the data speaks for itself, I'll be concise with my conclusions:__

*   two 1/4 watt 100 Ohm resistors in parallel (50 ohms) are suitable to heat an insulated enclosure with 12V
*   two 1/4 watt 100 Ohm resistors in parallel (50 ohms) are suitable to heat a crystal with 5V
*   low gain or negative gain is preferred to prevent oscillating tempeartures
*   Sensor location on an enclosure is not critical as long as it's well-coupled to the enclosure and the entire enclosure is well-insulated.

I feel satisfied with today's work. Next step is to build this device on a larger scale and fix it in a more permanent configuration, then leave it to run for a few weeks and see how it does. On to making the oscillator! If you have any questions or comments, feel free to email me. If you recreate this project, email me! I'd love to hear about it.

__Here's the code that went on the ATMega8 AVR (it continuously transmits voltage measurements on 6 channels).__

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

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

/*
ISR(ADC_vect)
{
    PORTD^=255;
}
*/

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

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

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

int readADC(char adcn){
    ADMUX = 0b0100000+adcn;
    ADCSRA |= (1<<ADSC); // reset value
    while (ADCSRA & (1<<ADSC)) {}; // wait for measurement
    return ADC>>6;
}

int sendADC(char adcn){
    int val;
    val=readADC(adcn);
    sendNum(val);
    USART_Transmit(',');
    return val;
}

int main(void){
    ADCSRA = (1<<ADEN)  | 0b111;
    DDRB=255;
    USART_Init();
    int checksum;

    for(;;){
        PORTB=255;
        checksum=0;
        checksum+=sendADC(0);
        checksum+=sendADC(1);
        checksum+=sendADC(2);
        checksum+=sendADC(3);
        checksum+=sendADC(4);
        checksum+=sendADC(5);
        sendNum(checksum);
        USART_Transmit('n');
        PORTB=0;
        _delay_ms(200);
    }
}
```

__Here's the command I used to compile the code, set the AVR fuse bits, and load it to the AVR.__

```bash
del *.elf
del *.hex
avr-gcc -mmcu=atmega8 -Wall -Os -o main.elf main.c -w
pause
cls
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c usbtiny -p m8 -F -U flash:w:"main.hex":a -U lfuse:w:0xe4:m -U hfuse:w:0xd9:m
```

__Here's the code that runs on the PC to listen to the microchip, match the data to the checksum, and log it occasionally. __

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

line=ser.readline()[:-1]
t1=time.time()
lines=0

data=[]

def adc2R(adc):
    Vo=adc*5.0/1024.0
    Vi=5.0
    R2=10000.0
    R1=R2*(Vi-Vo)/Vo
    return R1

while True:
    line=ser.readline()[:-1]
    lines+=1
    if "," in line:
        line=line.split(",")
        for i in range(len(line)):
            line[i]=int(line[i][::-1])

    if line[-1]==sum(line[:-1]):
        line=[time.time()]+line[:-1]
        print lines, line
        data.append(line)
    else:
        print  lines, line, "<-- FAIL"

    if lines%50==49:
        numpy.save("data.npy",data)
        print "nSAVINGn%d lines in %.02f sec (%.02f vals/sec)n"%(lines,
            time.time()-t1,lines/(time.time()-t1))
```

__Here's the code that runs on the PC to graph data.__

```python
import matplotlib
matplotlib.use('TkAgg') # <-- THIS MAKES IT FAST!
import numpy
import pylab
import datetime
import time

def adc2F(adc):
    Vo=adc*5.0/1024.0
    K=Vo*100
    C=K-273
    F=C*(9.0/5)+32
    return F

def adc2R(adc):
    Vo=adc*5.0/1024.0
    Vi=5.0
    R2=10000.0
    R1=R2*(Vi-Vo)/Vo
    return R1

def adc2V(adc):
    Vo=adc*5.0/1024.0
    return Vo

if True:
    print "LOADING DATA"
    data=numpy.load("data.npy")
    data=data
    print "LOADED"

    fig=pylab.figure()
    xs=data[:,0]
    tempAmbient=data[:,1]
    tempPower=data[:,2]
    tempHeater=data[:,3]
    tempCase=data[:,4]
    dates=(xs-xs[0])/60.0
    #dates=[]
    #for dt in xs: dates.append(datetime.datetime.fromtimestamp(dt))

    ax1=pylab.subplot(211)
    pylab.title("Temperature Controller - Low Gain")
    pylab.ylabel('Heater (ADC)')
    pylab.plot(dates,tempHeater,'b-')
    pylab.plot(dates,tempCase,'g-')
    #pylab.axhline(115.5,color="k",ls=":")

    #ax2=pylab.subplot(312,sharex=ax1)
    #pylab.ylabel('Case (ADC)')
    #pylab.plot(dates,tempCase,'r-')
    #pylab.plot(dates,tempAmbient,'g-')
    #pylab.axhline(0,color="k",ls=":")

    ax2=pylab.subplot(212,sharex=ax1)
    pylab.ylabel('Heater Power')
    pylab.plot(dates,tempPower)

    #fig.autofmt_xdate()
    pylab.xlabel('Elapsed Time (min)')

    pylab.show()

print "DONE"
```
Pages