SUMMARY: A small group of high school students taking an AP class for college credit launched a high-altitude weather balloon with a small payload. In addition to a video transmitter and GPS transmitter, they decided to include a simple transmitter built from scratch. This is the story of the project, with emphasis on the simple transmitter’s design, construction, implementation, and reception (which surprised me, being detected ~200 miles away and lasting the entire duration of the flight!) [sample.ogg]
6/16/2010 – TRACKING
I’m completely amazed at how well the transmitter/receiver worked! For only a few milliwatts, I was able to track that thing all the way from takeoff to landing in Gainesville, FL a few hundred miles away. Here is the data assembled in a special, annotated way!
ANALYSIS: the text on the image describes most if it, but one of the most interesting features is the “multipathing” during the final moments of the descent, where the single carrier signal splits into two. I believe this is due to two Doppler shifts: (1) as the distance between the falling transmitter and the receiver is decreasing, producing a slight in increase in frequency, and (2) a signal reflected off of a layer of the atmosphere above the craft (the ionosphere?) before it gets to the receiver, the distance of which is increasing as the craft falls, producing a decrease in frequency. I’ll bet I can mathematically work backwards and determine how high the craft was, how fast it was falling, and/or how high the layer of the reflecting material is – but that’s more work than this dental student is prepared to do before his morning coffee!
HERE IS SOME AUDIO of some of the strongest signals I received. Pretty good for a few milliwatts a hundred miles away! [beeps.ogg]
6/16/2010 – THE FLIGHT
The launch:
This is the design team:
Walking the balloon to its launch destination at NASA with an awesome rocket (Saturn 1B – identified by Lee, KU4OS) in the background.
The team again, getting ready for launch. I’ve been informed that the reason their hands are up is to prevent the balloon from tilting over too much. I’d imagine that a brush with a grass blade could be bad news for the project!
Last minute checks – you can see the transmitter and battery holders for it taped to the Styrofoam.
The transmitter in its final position. Note the coil of yellow wire. That serves as a rudimentary “ground” for the antenna’s signal to push off of. I wasn’t very clear on my instructions on how to make it. I meant that it should be a huge coil wrapped around the entire payload (as large as it can be), which would have probably produced a better signal, but since I was able to capture the signal during the whole flight it turned out to be a non-issue.
The antenna can be seen dropping down as a yellow wire beneath the payload. (arrow)
Awesome photo.
Launch! Look how fast that balloon is rising!
It’s out of our hands now. When I got the text message that it launched, I held my breath. I was skeptical that the transmitter would even work!
One of the students listening to my transmitter with QRSS VD software (score!)
Video capture from an on-board camera was also attempted (900MHz), but from what I hear it didn’t function well for very long.
6/15/2010 – IMPROVED BUILD
Here you can see me (center arrow) showing the students how to receive the Morse code signal sent from the small transmitter (left arrow) using a laptop running QRSS VD (my software) analyzing audio from and an Icom706 mkII radio receiver attached to a dipole (right arrow).
I amped-up the output of the oscillator using an octal buffer chip (74HC240) with some decent results. I’m pleased! It’s not perfect (it’s noisy as heck) but it should be functional for a 2 hour flight.
Closeup of the transmitter showing the oscillator at 29.4912 MHz, the Atmel ATTiny44a AVR microcontroller (left chip), octal buffer 74HC240 (right chip), and some status lights which blink as the code is executed.
This is my desk where I work from home. Note the styrofoam box in the background – that’s where my low-power transmitter lives (the one that’s spotted around the world). All I needed to build this device was a soldering iron.
Although I had a radio, it is not capable of receiving 29MHz so I was unable to test the transmitter from home. I had to take it to the university to assess its transmitting capabilities.
At UF I used an oscilloscope to measure the waveform of the transmitter.
I connected the leads to the output of the transmitter, shorted by a 39ohm resistor. By measuring the peak-to-peak voltage of the signal going into a resistor, we can measure its power.
Here’s the test setup. The transmitter is on the blue pad on the right, and the waveform can be seen on the oscilloscope on the upper left.
Here’s a closer view.
With the amplifier off, the output power is just that of the oscillator. Although the wave should look like a sine wave, it’s noisy, and simply does not. While this is unacceptable if our goal is a clean radio signal with maximum efficiency, this is good enough to be heard at our target frequency. The PPV (peak-to-peak voltage) as seen on the screen is about 100mV. Since I’m using a x10 probe, this value should be multiplied by 10 = 1V. 1V PPV into 39 ohms is about 3 milliwatts! ((1/(2*2^.5))^2/39*1000=3.2). For the math, see this post
With the amplifier, the output is much more powerful. At 600mV peak-to-peak with a 10x probe (actually 6V peak-to-peak, expected because that’s the voltage of the 4xAAA battery supply we’re using) into 39 ohms we get 115 millivolts! (6/(2*2^.5))^2/39*1000=115.38.
Notes about power: First of all, the actual power output isn’t 115mW. The reason is that the math equations I used work only for pure sine waves. Since our transmitter has multiple waves in it, less than that power is going to produce our primary signal. It’s possible that only 50mW are going to our 29MHz signal, so the power output assessment is somewhat qualitative. Something significant however is the difference between the measured power with and without the amplifier. The 6x increase in peak-to-peak voltage results in a 36x (6^2) increase in power, which is very beneficial. I’m glad I added this amplifier! A 36 times increase in power will certainly help.
The final schematic is here:
6/14/2010 – THE BUILD
Last week I spoke with a student in the UF aerospace engineering department who told me he was working with a group of high school students to add a payload to a high-altitude balloon being launched at (and tracked by) NASA. We tossed around a few ideas about what to put on it, and we decided it was worth a try to add a transmitter. I’ll slowly add to this post as the project unfolds, but with only 2 days to prepare (wow!) I picked a simplistic design which should be extremely easy to understand by everyone. Here’s the schematic:
The code is as simple as it gets. It sends some Morse code (”go gators”), then a long tone (about 15 seconds) which I hope can be measured QRSS style. I commented virtually every line so it should be easy to understand how the program works.
#include <avr /io.h>
#include <util /delay.h>
char call[]={2,2,1,0,2,2,2,0,0,2,2,1,0,1,2,0,2,0,2,2,2,0,1,2,1,0,1,1,1,0,0};
// 0 for space, 1 for dit, 2 for dah
void sleep(){
_delay_ms(100); // sleep for a while
PORTA^=(1<<PA1); // "flip" the state of the TICK light
}
void ON(){
PORTB=255; // turn on transmitter
PORTA|=(1<<PA3); // turn on the ON light
PORTA&=~(1<<PA2); // turn off the ON light
}
void OFF(){
PORTB=0; // turn off transmitter
PORTA|=(1<<PA2); // turn on the OFF light
PORTA&=~(1<<PA3); // turn off the OFF light
}
void ID(){
for (char i=0;i<sizeof(call);i++){
if (call[i]==0){OFF();} // space
if (call[i]==1){ON();} // dot
if (call[i]==2){ON();sleep();sleep();} // dash
sleep();OFF();sleep();sleep(); // between letters
}
}
void tone(){
ON(); // turn on the transmitter
for (char i=0;i<200;i++){ // do this a lot of times
sleep();
}
OFF();sleep();sleep();sleep(); // a little pause
}
int main(void) // PROGRAM STARTS HERE
{
DDRB = 255; // set all of port B to output
DDRA = 255; // set all of port A to output
PORTA = 1; // turn on POWER light
while (1){ // loop forever
ID(); // send morse code ID
tone(); // send a long beep
}
}
I’m now wondering if I should further amplify this signal’s output power. Perhaps a 74HC240 can handle 9V? … or maybe it would be better to use 4 AAA batteries in series to give me about 6V. [ponders] this is the schematic I’m thinking of building.
UPDATE
This story was featured on Hack-A-Day! Way to go everyone!
I found a way to quadruple the output power of my QRSS transmitter without changing its input parameters. Thanks to a bunch of people (most of whom are on the Knights QRSS mailing list) I decided to go with a push-pull configuration using 2 pairs of 4 gates (8 total) of a 74HC240. I’ll post circuit diagrams when I perfect it, but for now check out these waveforms!
First of all, this is the waveform before and after amplification with the 74HC240. I artificially weakened the input signal (top) with a resistor and fed it to the 74HC240. For the rest of the images, the input is 5v p-p and the output is similar, so amplification won’t be observed. The wave I’m starting with is the output of a microcontroller which is non-sinusoidal, but this can be fixed later with lowpass filtering.
Here you can see the test circuit I’m using. It should be self-explanatory.
Here’s the output of the microcontroller compared to the in-phase output of the 74HC240
Here are the two outputs of the 74HC240. 4 of the gates are used to create output in-phase with the input, and the other four are used to create out-of-phase wave. Here are the two side by side. The top is 0 to 5v, the bottom is 0 to -5v, so we have a push-pull thing going on… woo hoo!
The waves, when overlapped, look similar (which I guess is a good thing) with a slight (and I mean VERY slight) offset of the out-of-phase signal. I wonder if this is caused by the delay in the time it takes to trigger the 74HC240 to make the out-of-phase signal? The signal I’m working with is 1MHz.
Okay, that’s it for now. I’m just documenting my progress. 73
I re-wrote the code from the previous entry to do several things. Once of which was to make a gator rather than a fish. It’s more appropriate since I’m planning on housing the transmitter at the University of Florida. To do it, I drew a gator in paint and wrote a python script to convert the image into a series of points. I’ll post it later. One thing to note was that size was a SERIOUS issue. I only have two thousand bytes of code, and every point of that gator was a byte, so it was a memory hog. I helped it dramatically by using repeating segments wherever possible, and some creative math to help out the best I could (i.e., the spines on the back) Here’s what it looks like, and the code below it…
#include <avr/io.h>
#include <util/delay.h>
// front top LED - PA0
// inside top LED - PA1
// inside bot LED - PA2
// front bot LED - PA3
unsigned long int t_unit; // units of time
const int tDit = 100; //units for a dit
const int tDah = 255; //units for a dah
char fsk; // degree of frequency shift to use for CW
char fsk2; // degree of frequency shift to use for HELL
char light = 0; // which lights are on/off
void delay(){
_delay_loop_2(t_unit);
}
void blink(){
return;
if (light==0){
PORTA|=(1<<PA0); //on
PORTA|=(1<<PA1); //on
PORTA&=~(1<<PA2); //off
PORTA&=~(1<<PA3); //off
light=1;
} else {
PORTA|=(1<<PA2); //on
PORTA|=(1<<PA3); //on
PORTA&=~(1<<PA0); //off
PORTA&=~(1<<PA1); //off
light=0;
}
}
void tick(unsigned long ticks){
while (ticks>0){
delay();
delay();
ticks--;
}
}
void pwm_init() {
//Output on PA6, OC1A pin (ATTiny44a)
OCR1A = 0x00; //enter the pulse width. We will use 0x00 for now, which is 0 power.
TCCR1A = 0x81; //8-bit, non inverted PWM
TCCR1B = 1; //start PWM
}
void set(int freq, int dly){
OCR1A = freq;
tick(dly);
}
void fish(){
char mult = 3;
char f2[]={2, 3, 4, 5, 6, 7, 4, 3, 7, 4, 7, 7, 6, 5, 4, 3, 2, 2, 2, 3, 3, 3, 2, 2, 2, 3, 3, 3, 2, 2, 2, 3, 4, 5, 6, 7, 8, 4, 9, 5, 9, 6, 9, 6, 9, 6, 9, 8, 8, 7, 7, 6, 5, 4, 3, 3, 3, 4, 5, 5};
for (int i=0;i<sizeof(f2);i++) {
OCR1A = f2[i]*mult;
blink();
tick(20);
OCR1A = 1*mult;
blink();
tick(20);
}
char f3[]={1,2,3,4,3,2};
char offset=0;
while (offset<9){
for (char j=0;j<3;j++){
for (char i=0;i<sizeof(f3);i++){
char val = (f3[i]+5-offset)*mult;
if (val<mult || val > 10*mult){val=mult;}
OCR1A = val;
blink();
tick(20);
OCR1A = 1*mult;
blink();
tick(20);
}
}
offset++;
}
}
void id(){
char f[]={0,0,1,2,0,1,2,2,2,0,1,1,1,1,2,0,1,1,1,2,0,2,1,1,0,0};
char i=0;
while (i<sizeof(f)) {
blink();
if (f[i]==0){OCR1A = 0;tick(tDah);}
if (f[i]==1){OCR1A = fsk;tick(tDit);}
if (f[i]==2){OCR1A = fsk;tick(tDah);}
blink();
OCR1A=0;
tick(tDit);
i++;
}
}
void slope(){
char i=0;
while (i<25){
OCR1A = 255-i;
i++;
}
while (i>0){
i--;
OCR1A = 255-i;
}
}
int main(void)
{
DDRA = 255;
blink();
pwm_init();
t_unit=1000;fsk=10;id(); // set to fast and ID once
//fsk=50;//t_unit = 65536; // set to slow for QRSS
t_unit=60000;
while(1){;
fish();
id();
}
return 1;
}
Finally! After a few years tumbling around in my head, a few months of reading-up on the subject, a few weeks of coding, a few days of bread-boarding, a few hours of building, a few minutes of soldering, and a few seconds of testing I’ve finally done it – I’ve created my first QRSS transmitter! I’ll describe it in more detail once I finalize the design, but for now an awesome working model. It’s ~100% digital, consisting of 2 ICs (an ATTiny44a for the PWM-controlled frequency modulation, and an octal buffer for the preamplifier) followed by a simple pi low-pass filter. I don’t want to waste time typing – let’s show some pics!
My desk is a little messy. I’m hard at work! Actually, I’m thinking of building another desk. I love the glass because I don’t have to worry (as much) about fires. Scary, I know…
This is the transmitter. The box is mostly empty space, but it consists of the circuit, an antenna connection, a variable capacitor for center frequency tuning, and a potentiometer for setting the degree of frequency shift modulation.
AMAZING! Yeah, that’s a fishy. Specifically a goldfish (the cracker). It’s made with a single tone, shifting rapidly (0.5 sec) between tones. So cool. Anyway, I’m outta here for now – getting back to the code! I think I’ll try to make a gator…
As it is, here’s the code. It sends my ID quickly, some fish, then my ID in QRSS speed using PWM. You can figure out the pinout… I’ll document it with circuit diagrams soon!
#include <avr/io.h>
#include <util/delay.h>
const int tDit = 270/3;
const int tDah = 270;
char fsk;
unsigned long int t_unit;
void delay(){
_delay_loop_2(t_unit);
}
void blink(){
PORTA^=(1<<0);
PORTA^=(1<<1);
PORTA^=(1<<2);
PORTA^=(1<<3);
}
void tick(unsigned long ticks){
while (ticks>0){
delay();
delay();
ticks--;
}
}
void pwm_init() {
//Output on PA6, OC1A pin (ATTiny44a)
OCR1A = 0x00; //enter the pulse width. We will use 0x00 for now, which is 0 power.
TCCR1A = 0x81; //8-bit, non inverted PWM
TCCR1B = 1; //start PWM
}
void set(int freq, int dly){
OCR1A = freq;
tick(dly);
}
void fish(){
char f[]={0,0,0,4,5,3,6,2,7,1,5,6,8,1,8,1,8,1,8,1,8,2,7,3,6,2,7,1,8,1,8,4,5,2,3,6,7,0,0,0};
char i=0;
while (i<sizeof(f)) {
i++;
OCR1A = 255-f[i]*15;
blink();
tick(20);
}
}
void id(){
char f[]={0,0,1,2,0,1,2,2,2,0,1,1,1,1,2,0,1,1,1,2,0,2,1,1,0,0};
char i=0;
while (i<sizeof(f)) {
blink();
if (f[i]==0){OCR1A = 255;tick(tDah);}
if (f[i]==1){OCR1A = 255-fsk;tick(tDit);}
if (f[i]==2){OCR1A = 255-fsk;tick(tDah);}
blink();
OCR1A = 255;tick(tDit);
i++;
}
}
void slope(){
char i=0;
while (i<25){
OCR1A = 255-i;
i++;
}
while (i>0){
i--;
OCR1A = 255-i;
}
}
int main(void)
{
DDRA = 255;
PORTA^=(1<<0);
PORTA^=(1<<1);
pwm_init();
t_unit=2300;fsk=50;id(); // set to fast and ID once
fsk=50;t_unit = 65536; // set to slow for QRSS
while(1){
id();
for (char i=0;i<3;i++){
fish();
}
}
return 1;
}
My microcontroller-powered prime number generator/calculator is virtually complete! Although I’m planning on improving the software (better menus, the addition of sound, and implementation of a more efficient algorithm) and hardware (a better enclosure would be nice, battery/DC wall power, and a few LEDs on the bottom row are incorrectly wired), this device is currently functional therefore theoretically complete (I met my goal). This entry will serve as the primary reference page for the project, so I will provide a brief description of what it is and what it does. First, here’s a picture of the device in its current state (click to enlarge):
BRIEF DESCRIPTION: This device generates large prime numbers (v) while keeping track of how many prime numbers have been identified (N). The 5′th prime number is 11. Therefore, at one time this device displayed N=5 and V=11. N/V values are displayed on the 20×2 LCD. In the photo, the 16,521,486th prime is 305,257,039 (see for yourself!). The LCD had some history. In December, 2003 (6 years ago) I worked with this SAME display, and I even located the blog entry on November 25′th, 2003 where I mentioned I was thinking of buying the LCD (it was $19 at the time). Funny stuff. Okay, fast forward to today. Primes (Ns and Vs) are displayed on the LCD, but what’s with all those other LED lights? I’ll tell you:
In short, each row of LEDs displays a number. Each row of 30 LEDs allows me to represent numbers up to 2^31-1 (2,147,483,647, about 2.15 billion) in the binary numeral system. Since there’s no known algorithm to generate prime numbers (especially the Nth prime), the only way to generate large Nth primes is to start small (2) and work up (to 2 billion) testing every number along the way for primeness. The number being tested is displayed on the middle row (Ntest). The last two digits of Ntest are shown on the top left. To test a number (Ntest) for primeness, it is divided by every number from 2 to the square root of Ntest. If any divisor divides evenly (with a remainder of zero) it’s assumed not to be prime, and Ntest is incremented. If it can’t be evenly divided by any number, it’s assumed to be prime and loaded into the top row. In the photo (with the last prime found over 305 million) the device is generating new primes every ~10 seconds. Not bad! Let’s discuss technical details.
I’d like to emphasize that this device is not so much technologically innovative as it is creative. I made it because no one’s ever made one. It’s not realistic, practical, or particularly useful. It’s just unique. The brain behind it is an ATMEL ATMega8 AVR microcontroller (What is a microcontroller?), the big 28-pin microchip near the center of the board. (Note: I usually work with ATTiny2313 chips, but for this project I went with the ATMega8 in case I wanted to do analog-to-digital conversions. The fact that the ATMega8 is the heart of the Arduino is coincidental, as I’m not a fan of Arduino for purposes I won’t go into here).
I’d like to thank my grandmother’s brother and his wife (my great uncle and aunt I guess) for getting me interested in microcontrollers almost 10 years ago when they gave me BASIC Stamp kit (similar to this one) for Christmas. I didn’t fully understand it or grasp its significance at the time, but every few years I broke it out and started working with it, until a few months ago when my working knowledge of circuitry let me plunge way into it. I quickly outgrew it and ventured into directly programming cheaper microcontrollers which were nearly disposable (at $2 a pop, compared to $70 for a BASIC stamp), but that stamp kit was instrumental in my transition from computer programming to microchip programming.
The microcontroller is currently running at 1 MHz, but can be clocked to run faster. The PC I’m writing this entry on is about 2,100 MHz (2.1 GHz) to put it in perspective. This microchip is on par with computers of the 70s that filled up entire rooms. I program it with the C language (a language designed in the 70s with those room-sized computers in mind, perfectly suited for these microchips) and load software onto it through the labeled wires two pictures up. The microcontroller uses my software to bit-bang data through a slew of daisy-chained shift registers (74hc595s, most of the 16-pin microchips), allowing me to control over 100 pin states (on/off) using only 3 pins of the microcontroller. There are also 2 4511-type CMOS chips which convert data from 4 pins (a binary number) into the appropriate signals to illuminate a 7-segment display. Add in a couple switches, buttons, and a speaker, and you’re ready to go!
I’ll post more pictures, videos, and the code behind this device when it’s a little more polished. For now it’s technically complete and functional, and I’m very pleased. I worked on it a little bit every day after work. From its conception on May 27th to completion July 5th (under a month and a half) I learned a heck of a lot, challenged my fine motor skills to complete an impressive and confusing soldering job, and had a lot of fun in the process.
I’ll update my progress on this project as I go. I added a lot more light bars to the shift registers on my prime number generator project. I’m up to 5 daisy-chained shift registers completed (powering 40 LEDs) with 7 more to go! I’m using 22 gauge solid-core (fancy and expensive, from digikey, 100′ 14$!) wire for the back of this project. Being that I plan to keep it for many years, I want it to look crazy awesome. Remember, I’m only about 1/3 done so far…
I powered the device up and it produced proper output. Yay! I was so discouraged yesterday when I wired-up an entire row (the top one), powered it on, and 1/2 the LEDs didn’t work. At first I thought it was software, but then I realized that I burned the LEDs out in the soldering process by getting them too hot. I had to de-solder EVERYTHING, rip out the destroyed LED bars, and start over. I’ll have to pick up some more light bars at Skycraft soon. This is what it looks like currently:
I’m making this project a priority because I only have a few weeks before I move to Gainesville, FL for dental school (the cutoff date for all electronics/radio/programming projects). I’ll be busy the next few days with other obligations (work, apartment hunting, field day, etc.) but I hope to resume this project soon.
UPDATE (June 26, 2009 @ 7:30pm): I finished wiring all the light bars I have. I need to purchase 3 more 10-led bars at Skycraft to replace the ones I melted with my soldering iron. D’oh! Anyway, here’s the beaut:
Bitwise programming techniques (manipulating binary numbers) is simple in theory, but it’s often hard to remember how to do specific tasks if you don’t do them often. Recently in my microcontroller programming endeavors (where you’re pressed to conserve every bit of memory) I’ve needed to perform a lot of bitwise operations. If I’m storing true/false (1-bit) information in variables, it’s a waste to assign a whole variable to the task (even a char, the smallest variable in C is a waste because it uses 8 bits of memory!). When cramming multiple values into individual variables, it’s nice to know how to manipulate each bit of a variable.
Questions like “how do I retrieve the value of a certain bit in a variable”, “how do I set the value of a certain bit in a variable”, and “how do I flip a certain bit in a variable” can eventually be answered by twiddling around with bitwise operators in C, but often the solutions you randomly discover this way are not elegant or efficient. This afternoon I ran across the following chart on an Arduino help site and although I’m not a fan of Arduino, I can certainly appreciate the chart. I hope you find it as useful as I did.
y = (x>>n)&1; // stores nth bit of x in y. y becomes 0 or 1.
x&=~(1<<n); // forces nth bit of x to be 0. all other bits left alone.
x&=(1<<(n+1))-1; // leaves lowest n bits of x; all higher bits set to 0.
x|=(1<<n); // forces nth bit of x to be 1. all other bits left alone.
x^=(1<<n); // toggles nth bit of x. all other bits left alone.
x=~x; // toggles ALL the bits in x.
For the last several weeks I’ve been hacking together a small prototype of a microcontroller (ATTiny2313)-powered prime number generator. I can finally say that it (the prototype) is complete, functional, and elegant [get the song I'm referencing while it's up!]. The name says what it does, so I won’t waste time describing it. If you’re interested in how it does what it does, check out the other posts with the same category tag for a full (and I mean full as in “more than you ever wanted to know”) description. A schematic is soon to come. Code is below. For now, here’s a video of the completed project:
And the source code I used. I chose the ATTiny2313 because it was cheap (~$2) and has 18 IO pins to work with. I had to fight memory usage every step of the way. I’m limited to 2kb, and this program compiles within 1% of that! For future projects (more LEDs, more menus) I’ll use the 20-pin and/or 40-pin ATMega series. I’m thinking about having one be in charge of the display (multiplexing) leaving the other to think about generating prime numbers.
#define F_CPU 10240000
#include <avr/interrupt.h>
#include <util/delay.h>
#include <math.h>
// ~~~ PORT D ~~~
#define r1 0b00000100
#define r2 0b00000010
#define r3 0b00001000
#define r4 0b00100000
#define r5 0b00010000
#define f1 0b01000000
#define id 0b00000001
#define f0 0b10111111
// ~~~ PORT B ~~~
#define c1 0b00010000
#define c2 0b00000001
#define c3 0b00000010
#define c4 0b00000100
#define c5 0b00001000
char delay=1;
int redo=0;
char rows[] = {r1,r2,r3,r4,r5};
char cols[] = {c1,c2,c3,c4,c5};
char vals[] = {0,0,0,0,0};
char funcs=f1+id;
unsigned long showNum=5;//33554431;
void displayNum();
void incrimentNum();
void menu_pause();
void menuCheck();
char button();
char isPrime(unsigned long);
unsigned int twoTo(char);
int main(void) {
DDRD = r1+r2+r3+r4+r5+f1+id;
DDRB = c1+c2+c3+c4+c5;
DDRB &= ~_BV(DDB7);
PORTB = 0;
PORTD = 0;
unsigned int i=0;
int j;
//convertNum();
//showNum=5;
while (1) {
showNum+=1;
if (isPrime(showNum)){
convertNum();
}
for (j=0;j<redo;j++) {
displayNum();
menu();
}
if (i%10==0){funcs^=r1;
funcs^=id;
if (i%100==0){funcs^=r2;
if (i%1000==0){funcs^=r3;i=0;
}
}
}
i++;
}
return 0;
}
char isPrime(unsigned long test){
if (test%2==0) return 0;
unsigned long div = 3;
while(div*div<test+1){
if (test%div==0) return 0;
div+=2;
displayNum();
}
return 1;
}
void menu(){
//return;
char j,but;
but = button();
if (but==0) return;
else if (but==1){
while (1) {
PORTD=id;
if (PINB & _BV(PB7)) {
funcs=f1;
for (j=0;j<200;j++){
if (j%25==0) funcs^=r1;
displayNum();
}
}
else {
if (redo==0) redo=200;
else redo=0;
_delay_ms(300);
return;
}
}
}
return;
}
char button(){
PORTD=id;
if (PINB & _BV(PB7)) return 0; // not pressed
_delay_ms(1000);
if (PINB & _BV(PB7)) return 1; // pressed
return 2; // held down
}
void convertNum(){
char col,row,rowcode;
unsigned long msk=1;
for (col=0;col<5;col++){
rowcode=0;
for (row=0;row<5;row++){
if (showNum&msk) rowcode+=rows[row];
msk = msk < < 1;
}
vals[col]=rowcode;
}
return;
}
void displayNum(){
char i,j;
PORTB = 0b0;
PORTD = funcs;
_delay_ms(delay);
for (i=0;i<5;i++){
PORTD = vals[i];
PORTB = cols[i];
_delay_ms(delay);
}
return;
}
I decided to crank up the power on my prime number identifier and attempt to document its efficiency in both C (GCC) and Python. I used the [very inefficient] code from the previous entry, modified it to test every integer up to 1×10^8, and let them both run on my laboratory computer overnight (an AMD Athlon 64-bit X2 Dual Core 2 GHz PC with 2GB of ram). The Python program took 3.24 processor hours, and the C program took 1.85 processor hours to complete. Each step along the way each program recorded how long it took to test the last 10,000 integers, creating a logfile with 1,000 data points, which can be easily graphed (see previous entry). On this nice, fast computer the data look very clean and curve-like. I curve-fitted the data using my new favorite website, ZunZun.com. Here’s what I came up with…
Coeffecients:
### C (GCC) ###
a = 1.4193535434065586E-03
b = -1.2708466972699874E-07
c = -4.9489218065978358E-16
d = 1.3032518272036044E-11
e = 1.3231610935638881E-06
### PYTHON ###
a = 2.6579973323520396E-03
b = -2.8299592901357803E-07
c = -1.2080374779761445E-15
d = 3.0281624700595501E-11
e = 2.4778595094623538E-06
### Generating Data Points: ###
Ys=[]
for x in range(len(values)):
Ys.append(a*(x**.5)+b*(x)+c*(x**2)+d*(x**1.5)+e)
Note that this fitted curve is the representation of how long (in seconds, vertical axis) it takes to systematically test 10,000 integers for primeness (starting at an integer on the horizontal axis), This does NOT represent how long it takes to reach a certain X. To properly calculate this, one would need to integrate the equation. This would allow for the easy prediction of how long it would take to reach a certain integer (the solution would be the integral of the provided equation from 0 to the integer). I’ll let someone else attack that. It’s time for me to get back to work!
While working on my current microcontroller project I’ve become a little obsessed with the prospect of rapidly generating lists of prime numbers. I’ve been testing various strategies for speeding up the process using logic modifications to a base concept. I’ve been doing my conceptual work in Python because it’s so fast and easy to rapidly develop applications with. Now that I have a good understanding of my strategy (code scheme) of choice, I’m starting to wonder how much better the same code would run in C. It’s no secret that Python’s strength is its versatility and ease of development of scritps, not its speed. C on the other hand can be very cumbersome and awkward to develop with, but its compiled code is very efficient so it’s better suited for mathematical applications which require billions of repetitive tasks.
To visualize the speed difference between C and Python, I wrote the same prime-number-testing code in both languages and timed their output. Basically I wrote identical code in each language (variable names and functions are the same, almost a line-by-line translation) and timed how long it took to find all prime numbers up to 10,000,000. (It reported the length of runtime at every 100,000 integers, totalling 100 time points).
The results:
The black line is code written in C and the blue line is Python. According to this output, Pythin is about 4 times slower than C at identifying primes utilizing the identical method. I’m not a coding expert, and there may be better ways to code/compile things in each language, so I’ll asy this is an indication of speed rather than a detailed, scientific study comparing the two languages. For fairness, I’ll provide my code.
Python code to generate prime numbers up to 10^7:
import time
def isPrime(testPrime):
for div in xrange(2,int(testPrime**.5)+1):
if testPrime%div==0: return False
return True
def testSpeed(startAt, stopAt):
primesFound=0
startTime = time.clock()
for testPrime in xrange(startAt,stopAt):
if isPrime(testPrime):
primesFound+=1
s="%d,%d,%d,%fn"%(primesFound,startAt,stopAt,time.clock()-startTime)
print s
f=open("log_py.txt",'a')
f.write(s)
f.close()
return
for i in range(100):
testSpeed(100000*i,100000*(i+1))
raw_input()
C code to generate prime numbers up to 10^7:
#include <stdio.h>
#include <math.h>
#include <time.h>
char isPrime(int testPrime){
int div;
for (div=2;div<(int)sqrt(testPrime)+1;div++){
if (testPrime%div==0) return 0;
}
return 1;
}
void testSpeed(unsigned int startAt, unsigned int stopAt){
int primesFound=0;
char buf[256],len;
unsigned int testPrime;
clock_t startTime = clock();
for(testPrime=startAt;testPrime<stopAt;testPrime++){
if (isPrime(testPrime)) primesFound++;
}
len=sprintf(buf,"n%i,%u,%u,%f",primesFound,startAt,stopAt,
(float)(clock()-startTime)/CLOCKS_PER_SEC);
printf("%s",buf);
FILE *fptr = fopen("log_c.txt","a");
fputs(buf,fptr);
fclose(fptr);
return;
}
int main(){
char i;
for (i=0;i<100;i++){
testSpeed(100000*i,100000*(i+1));
}
return 0;
}
Python code to graph the difference utilizing MatPlotLib:
import pylab
def graphIt(fname,col):
f=open(fname,'r')
data=f.readlines()
f.close()
val,time=[],[]
for line in data:
if line.count(',')<3: continue
line=line.strip('n').split(',')
val.append(int(line[1]))
time.append(float(line[3]))
pylab.plot(val,time,color=col)
return [val,time]
pylab.figure()
pylab.subplot(311)
v,tc=graphIt('log_c.txt','k');
v,tp=graphIt('log_py.txt','b');
pylab.title("C vs Python: test 10,000 numbers for primeness")
pylab.ylabel("time (seconds)")
pylab.xlabel("starting value")
pylab.grid(alpha=.3)
pylab.subplot(312)
pylab.grid(alpha=.3)
graphIt('log_c.txt','k');
graphIt('log_py.txt','b');
pylab.ylabel("time (seconds)")
pylab.xlabel("starting value")
pylab.semilogy()
pylab.subplot(313)
ratios=[]
for x in range(len(v)):
ratios.append(float(tp[x])/tc[x])
pylab.plot(v,ratios,'k')
pylab.ylabel("speed ratio")
pylab.xlabel("starting value")
pylab.axis([None,None,None,5])
pylab.grid(alpha=.3)
pylab.show()
In my quest to build a hardware-based binary prime number generator I decided to build a demonstration model / rapid prototype to prove to myself (and the world) that I can reliably (and quickly) generate prime numbers. This code needs a lot of work, but it’s functional. Instead of messing with tons of little LEDs, it just dumps the output to a LCD. Of note, the library to run the LCD takes up about 90% of the memory of the chip leaving only a handful of free bytes to write the actual code in!
Keep in mind this is a PROTOTYPE and that the full version will have over 80 LEDs to represent every number in binary form (up to 2^25, 33.5 million, with 25 LEDs/number). For now, this version (which dumps data to a HD44780 LCD screen) serves as a proof of concept. Powered by an ATTiny2313.
Here’s some video:
Here’s the code responsible:
#define F_CPU 1E6
#include <stdlib.h>
#include <avr/io.h>
#include <math.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"
const unsigned long int primeMax=pow(2,25);
unsigned long int primeLast=2;
unsigned long int primeTest=0;
unsigned int primeDivy=0;
void wait(void);
void init(void);
void updateDisplay(void);
char *toString(unsigned long int);
int main(void){
init();
short maybePrime;
unsigned int i;
//for(primeTest=3;primeTest<sqrt(primeMax);primeTest=primeTest+2){
for(primeTest=2;primeTest<sqrt(primeMax);primeTest++){
maybePrime=1;
//for (i=3;i<=(sqrt(primeTest)+1);i=i+2){
for (i=2;i<=(sqrt(primeTest)+1);i++){
primeDivy=i;
updateDisplay();
if (primeTest%primeDivy==0){maybePrime=0;break;}
}
if (maybePrime==1){primeLast=primeTest;updateDisplay();}
}
return 0;
}
void updateDisplay(void){
lcd_gotoxy(12,0);lcd_puts(toString(primeLast));
lcd_gotoxy(5,1);lcd_puts(toString(primeTest));
lcd_gotoxy(16,1);lcd_puts(toString(primeDivy));
//_delay_ms(1000);
return;
}
void init(void){
lcd_init(LCD_DISP_ON);
//lcd_clrscr();
lcd_puts("PRIME IDENTIFICATIONn");
//lcd_puts("MAX PRIME: ");
//lcd_puts(toString(primeMax));
//lcd_puts("nsquare root: ");
//lcd_puts(toString(sqrt(primeMax)+1));
_delay_ms(2000);
lcd_clrscr();
lcd_puts("LAST PRIME:n");
lcd_puts("TRY:");
lcd_gotoxy(14,1);lcd_puts("/");
return;
}
char *toString(unsigned long int x){
char s1[8];
ltoa(x,s1,10);
return s1;
}
In other news, I’ve seen the new Star Trek movie and found it enjoyable. My wife liked it too (perhaps more than I did). Here’s a relevant news story I found informative:
After a day of tinkering I finally figured out how to control a HD44780-style LCD display from an ATTiny2313 class ATMEL AVR microcontroller. There are a lot of websites out there claiming to show you how to do this on similar AVRs. I tried about 10 of them and, intriguingly, only one of them worked! I don’t know if it’s user error, or an incompatibility of the ATTiny2313 running code written for an ATMega8, but since it took me so long to get this right I decided to share it on the internet for anyone else having a similar struggle. First, the results: You might recognize this LCD panel from some PC parallel port / LCD interface projects I worked on about 5 years ago. It’s a 20-column, 2-row, 8-bit parallel character LCD. This means that ranter than telling each little square to light up to form individual letters, you can just output text to the microcontroller embedded in the display and it can draw the letters, move the cursor, or clear the screen. As you can see this thing is pretty easy to wire-up to the ATTiny2313. These are the connections I made:
LCD1 -> GND
LCD2 -> +5V
LCD3 (contrast) -> GND
LCD4 (RS) -> AVR D0 (pin2)
LCD5 (R/W) -> AVR D1 (pin3)
LCD6 (ES) -> AVR D2 (pin6)
LCD 11-14 (data) -> AVR B0-B3 (pins 12-15)
The code I used to FINALLY allow me to control this LCD from the ATTiny2313 was found on Martin Thomas’ page. [HIS CODE] I included the .h and .c files and successfully ran the following program (with great results) on my AVR. The internal RC clock works, and supposedly any external clock (<8MHz) should work too.
// THIS THE TEST PROGRAM "main.c"
// ATTiny2313 / HD44780 LCD INTERFACE
#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"
int main(void)
{
int i=0;
lcd_init(LCD_DISP_ON);
lcd_clrscr();
lcd_puts("ATTiny 2313 LCD Demon");
lcd_puts(" www.SWHarden.com n");
_delay_ms(1000);
lcd_clrscr();
for (;;) {
lcd_putc(i);
i++;
_delay_ms(50);
}
}
// THIS IS PART OF MY INCLUDED "lcd.h"
// THE WIRING CHART DESCRIBED IN THIS BLOG ENTRY
// IS MATCHED TO THE VALUES DISPLAYED BELOW
#define LCD_PORT PORTB /**< port for the LCD lines */
#define LCD_DATA0_PORT LCD_PORT /**< port for 4bit data bit 0 */
#define LCD_DATA1_PORT LCD_PORT /**< port for 4bit data bit 1 */
#define LCD_DATA2_PORT LCD_PORT /**< port for 4bit data bit 2 */
#define LCD_DATA3_PORT LCD_PORT /**< port for 4bit data bit 3 */
#define LCD_DATA0_PIN 0 /**< pin for 4bit data bit 0 */
#define LCD_DATA1_PIN 1 /**< pin for 4bit data bit 1 */
#define LCD_DATA2_PIN 2 /**< pin for 4bit data bit 2 */
#define LCD_DATA3_PIN 3 /**< pin for 4bit data bit 3 */
#define LCD_RS_PORT PORTD /**< port for RS line */
#define LCD_RS_PIN 0 /**< pin for RS line */
#define LCD_RW_PORT PORTD /**< port for RW line */
#define LCD_RW_PIN 1 /**< pin for RW line */
#define LCD_E_PORT PORTD /**< port for Enable line */
#define LCD_E_PIN 2 /**< pin for Enable line */
// AND A LITTLE LOWER, I CHANGED THIS LINE TO 4-BIT MODE
#define LCD_FUNCTION_8BIT 0 /* DB4: set 8BIT mode (0->4BIT mode) */
And some video of the output… Notice how this display can show English (lowercase/uppercase/numbers) as well as the Japanese character set! Sweet!
I recently had the desire to be able to see data from an ATMEL AVR microcontroller (the ATTiny2313) for development and debugging purposes. I wanted an easy way to have my microcontroller talk to my PC (and vise versa) with a minimum number of parts. The easiest way to do this was to utilize the UART capabilities of the ATTiny2313 to talk to my PC through the serial port. One problem is that the ATTiny2313(as with most microcontrollers) puts out 5V for “high” (on) and 0V for “low” (off). The RS-232 standard (which PC serial ports use) required -15V for high and +15v for low! Obviously the microcontroller needs some help to achieve this. The easiest way was to use the MAX232 serial level converter which costs about 3 bucks at DigiKey. Note that it requires a few 10uF capacitors to function properly.
Here’s a more general schematic:
I connected my ATTiny2313 to the MAX232 in a very standard way. (photo) MAX232 pins 13 and 14 go to the serial port, and the ATTiny2313 pins 2 and 3 go to the MAX232 pins 12 and 11 respectively. I will note that they used a oscillator value (3.6864MHz) different than mine (9.216MHz).
Determining the speed of serial communication is important. This is dependent on your oscillator frequency! I said I used a 9.216Mhz oscillator. First, a crystal or ceramic oscillator is required over the internal RC oscillator because the internal RC oscillator is not accurate enough for serial communication. The oscillator you select should be a perfect multiple of 1.8432MHz. Mine is 5x this value. Many people use 2x this value (3.6864Mhz) and that’s okay! You just have to make sure your microchip knows (1) to use the external oscillator (google around for how to burn the fuses on your chip to do this) and (2) what the frequency of your oscillator is and how fast it should be sending data. This is done by setting the UBRRL value. The formula to do this is here:
The datasheet of your microcontroller may list a lot of common crystal frequencies, bandwidths, and their appropriate UBRR values. However my datasheet lacked an entry for a 9.216MHz crystal, so I had to do the math myself. I Googled around and no “table” is available! Why not make one? (picture, below). Anyway, for my case I determined that if I set the UBRR value to 239, I could transmit data at 2800 baud (bits/second). This is slow enough to ensure accuracy, but fast enough to quickly dump a large amount of text to a PC terminal.
This is the bare-minimum code to test out my setup. Just load the code (written in C, compiled with avr-gcc) onto your chip and it’s ready to go. Be sure you set your fuses to use an external oscillator and that you set your UBRRL value correctly.
Once you load it, it’s ready to roll! It continuously dumps letters to the serial port. To receive them, open HyperTerminal (on windows, under accessories) or minicom (on Linux, look it up!). Set your baud rate to 2800 (or whatever you selected) and you’re in business. This (picture below) is the output of the microcontroller to HyperTerminal on my PC. Forgive the image quality, I photographed the LCD screen instead of taking a screenshot.
This is the circuit which generates the output of the previous image. I have a few extra components. I have an LED which I used for debugging purposes, and also a switch (labeled “R”). The switch (when pressed) grounds pin 1 of the ATTiny2313 which resets it. If I want to program the chip, I hold “R” down and the PC can program it with the inline programmer “parallel port, straight-through, DAPA style. One cable going into the circuit is for the parallel port programmer, one cable is for the serial port (data transfer), and one is for power (5v which I stole from a USB port).
I hope you found this information useful. Feel free to contact me with any questions you may have, but realize that I’m no expert, and I’m merely documenting my successes chronologically on this website.