The personal website of Scott W Harden
⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

Controlling Bus Pirate with Python

After using the AVR-ISP mkII for years (actually the cheap eBay knock-offs) to program ATMEL AVR microcontrollers, today I gave the Bus Pirate a shot. Far more than just a microcontroller programmer, this little board is basically a serial interface to basic microcontroller peripherals. In a nutshell, you plug it in via USB and it looks like a serial port which has a command-line interface that lets you do things like turn pins on and off, perform voltage measurements, and it naively supports bidirectional use of common protocols like I2C, SPI, UART, and even HD44780 series LCDs. Note that although you could directly interface with the Bus Pirate using HyperTerminal, I recommend using TeraTerm. It can supply voltages (3.3V and 5V) to power small circuits, and if current draw is too high (indicating something is hooked-up wrong) it automatically turns the supply off. So clever! At <$30, it's a cool tool to have around. In addition, it's naively supported as an AVR programmer by AVRDUDE. Although I could write assembly to perform tasks, I almost always write in C for the convenience. For my reference (and that of anyone who may want to do something similar), I'm posting the simplest-case method I use to program AVR microcontrollers with the Bus Pirate on Windows (noting that Linux would be nearly identical). I also wrote a Python script to connect with the Bus Pirate and run simple commands (which turns the power supply on and report the voltage of the VCC line immediately after programming completes). Yes, there are fancy packages that allow you to interact with Bus Pirate from Python, but the advantage of my method is that it runs from native Python libraries! To get this all up and running for yourself, just install WinAVR (which supplies AVRDUDE and AVR-GCC) and Python 3. I assume this code will work just as well on Python 2, but haven't tried.

To ensure my Bus Pirate is working properly, I start off by running the Bus Pirate's built-in test routine. For full details read the guide. It just involves connecting two pairs of pins together as shown in the picture here, connecting to the Bus Pirate with the serial terminal, and running the command "~". It will output all sorts of useful information. Once I know my hardware is up and running, I'm good to continue.

Here's the code which runs on the microcontroller to twiddle all the pins (saved as main.c). Note that my MCU is an ATTiny85. I'm using standard clock settings (internal RC clock, 8MHz), but if I wanted to modify fuses to do things like use an external clock source or crystal, I'd calculate them with engbedded's handy dandy fuse calculator (which also shows AVRdude arguments needed to make the change!).

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

int main (void)
{
    DDRB = 255;
    while(1)
    {
        PORTB ^= 255;
        _delay_ms(500);
    }
}

To compile the code and program the MCU with it, I always have a bash script in the same folder that I can double-click on to delete old compiled files (so we don't accidentally re-program our MCU with old code), compile main.c, and load it onto the MCU using the Bus Pirate. You may have to change COM3 to reflect the com port of your Bus Pirate. Note that it is required that you disconnect other terminals from the Bus Pirate before doing this, otherwise you'll get an "access denied" error.

@echo off
del *.elf
del *.hex
avr-gcc -mmcu=attiny85 -Wall -Os -o main.elf main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c buspirate -p attiny85 -P com3 -e -U flash:w:main.hex
python up.py

Although the programmer briefly supplies my MCU with power from the +5V pin, it's cut after programming completes. Rather than manually re-opening my terminal program, re-connecting with the bus pirate, re-setting the mode (command "m") to something random (DIO, command "9"), and re-enableing voltage output (command "W") just to see my LED blink, I want all that to be automated. Thanks python for making this easy. The last line calls "up.py". This fancy script even outputs the voltage of the VCC line after it's turned on!

Python3 Control of Bus Pirate

import serial

BUSPIRATE_PORT = 'com3' #customize this! Find it in device manager.

def send(ser,cmd):
    """send the command and listen to the response."""
    ser.write(str(cmd+'\n').encode('ascii')) # send our command
    for line in ser.readlines(): # while there's a response
        print(line.decode('utf-8').strip()) # show it

ser=serial.Serial(BUSPIRATE_PORT, 115200, timeout=1) # is com free?
assert ser.isOpen() #throw an exception if we aren't connected
send(ser,'#') # reset bus pirate (slow, maybe not needed)
send(ser,'m') # change mode (goal is to get away from HiZ)
send(ser,'9') # mode 9 is DIO
send(ser,'W') # turn power supply to ON. Lowercase w for OFF.
send(ser,'v') # show current voltages
ser.close() # disconnect so we can access it from another app
print("disconnected!") # let the user know we're done.

When "burn.cmd" is run, the code is compiled and loaded, the power supply is turned on (and killed if too much current is drawn!), and the voltage on VCC is reported. The output is:

C:\Users\scott\Documents\important\AVR\2016-07-13 ATTiny85 LEDblink>burn.cmd

Detecting BusPirate...
**
**  Bus Pirate v3a
**  Firmware v5.10 (r559)  Bootloader v4.4
**  DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
**  http://dangerousprototypes.com
**
BusPirate: using BINARY mode
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.12s

avrdude: Device signature = 0x1e930b
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (84 bytes):

Writing | ################################################## | 100% 3.12s

avrdude: 84 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex auto detected as Intel Hex
avrdude: input file main.hex contains 84 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 2.72s

avrdude: verifying ...
avrdude: 84 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.
#
RESET

Bus Pirate v3a
Firmware v5.10 (r559)  Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com

HiZ>
m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)

(1)>
9
Ready
DIO>
W
Power supplies ON
DIO>
v
Pinstates:
1.(BR)  2.(RD)  3.(OR)  4.(YW)  5.(GN)  6.(BL)  7.(PU)  8.(GR)  9.(WT)  0.(Blk)
GND     3.3V    5.0V    ADC     VPU     AUX     CLK     MOSI    CS      MISO
P       P       P       I       I       I       I       I       I       I
GND     3.17V   5.00V   0.00V   0.00V   L       L       L       H       L
DIO>
disconnected!

This is a minimal-case scenario, but can be obviously expanded to perform some complicated tasks! For example, all commands could be run from a single python program. Considering the Bus Pirate's ability to communicate with so many different protocols (I2C, 2-write, etc.), being able to naively control it from Python without having to install special additional libraries will certainly prove to be convenient.

PS: I noted there is a surprising delay when initializing programming the AVR with the bus pirate. The process hangs for about 10 seconds after the bus pirate introduces itself with the welcome message, then seems to resume at full speed writing to the flash of the microchip. After a bit of Googling, I believe the delay is due to the Bus Pirate slowly bit-banging SPI to initialize the programming sequence. The AVR has rich SPI functionality, some of which involves its own programming. Satisfied with this answer for now, I'm not going to try to speed it up. It's a little annoying, but not too bad that I won't use this to program my AVRs.

⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

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"
⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

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))
⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

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:

⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

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.

page 1, page 2, page 3, page 4, page 5, page 6, page 7, page 8, page 9, page 10, page 11, page 12, page 13, page 14, page 15, page 16, page 17, page 18, page 19, page 20, page 21, page 22, page 23, page 24, page 25, page 26, page 27, page 28, page 29, page 30, page 31, page 32, page 33, page 34, page 35, page 36, page 37, page 38, page 39, page 40, page 41, page 42, page 43, page 44, page 45, page 46, page 47, page 48, page 49
All Blog Posts