Tuesday, October 20, 2009

Project Recap: Binary Clock


Binary Clock Layout
Originally uploaded by hithisishal
This was one of the first microcontroller projects that I completed. I was working on a nixie clock at the time, and while writing the binary to decimal converter, I decided to pull over and make a pure binary clock.

The first version was drawn with a sharpie onto copper by hand. It is ugly (and in my photostream if you want to see). A few months later, I was assigned a project in school that involved getting a board fabbed. I had plenty of room left before reaching the minimum size, so I made a few of these binary clocks. This was my second time doing a layout in Eagle (the first being the school project), and I didn't really know what I was doing. I made a layout, without making a schematic first. Luckily, the layout worked, but I still don't have a schematic drawn up for this project - I will if there is enough demand. There are 2 things about the layout that I would change if i made more - the switch holes are a bit off, and I would add an ICSP header. I finished up 4 of these clocks, and gave two as gifts. The others are currently at my parents' house, though. I will be home and have some pictures in a few months.

Anyway, I think the value of this project is in the code. I learned PIC assembly from John Morton's PIC: Your Personal Introductory Course. I have since moved on to C, but I sometimes miss coding in assembly. The code that follows is very likely not efficient and may have remnants of past versions, but it should be easy to follow, and got the job done.

;********************************************************
;* Binary Clock *
;* Written By: Hal Emmer *
;* Date: July, 2006 *
;* version: 2.0 modified to use interrupts *
;* for PIc: 16f628a *
;* clock: internal oscillator *
;**************************************************************


LIST P=16F628a
ERRORLEVEL -302 ; suppress bank selection messages
__CONFIG 3F18H ; int oscillator


PCL EQU 2 ; bits and registers are defined here to
STATUS EQU 3 ; make this program self contained.
PORTA EQU 5
PORTB EQU 6
INTCON EQU 0BH
TRISA EQU 85H
TRISB EQU 86H
OPTREG EQU 81H
CMCON EQU 1FH
TMR0 EQU 01H

W EQU 0
F EQU 1
C EQU 0 ; bits in STATUS
Z EQU 2
RP0 EQU 5

T0IF EQU 2 ; bit in INTCON



CBLOCK 20H ; define variables required

TICKS
SEGS ; one bit per segment: "-gfedcba"
SEC
MIN
HOUR
FRAME ; used to decide when to display time
HHMM ; one bit per digit displayed
COUNT ; scratch register
DIGIT ; last digit displayed
DELVAR ; delay counter
SPEED
SPEED2 ; used to set time
LIGHT ; hardware compensation
COUNT1
W_SAVE
STATUS_SAVE
ENDC


;*********************************;
; Initialization ;
;*********************************;

ORG 0
goto INIT
ORG 4
goto ISR

INIT CLRF SEC
CLRF MIN
CLRF HOUR
CLRF FRAME
CLRF PORTA
CLRF PORTB
CLRF CMCON
MOVLW 07H
MOVWF CMCON
movlw h'C4'
movwf SPEED
movlw d'59'
movwf SPEED2
BSF STATUS,RP0 ; select register bank 1
CLRF TRISB
BSF TRISB,6 ; RB6 is an input min button
BSF TRISB,7 ; RB7 is an input hr button
CLRF TRISA
bsf TRISA,4 ; RA4 is clockin
MOVLW b'00101000' ; t0cs is RA4/T0CKI
MOVWF OPTREG ; prescalar assigned to WDT (no prescaler)
BCF STATUS,RP0 ; reselect bank 0
BSF INTCON, 5 ; t0 overflow interrupt enable
BSF INTCON, 7 ; global interrupt enable

;*********************************;
; Main Program ;
;*********************************;

MAIN
CALL DISPLAY ; continue to display
GOTO MAIN ; unless interrupted

;*********************************;
; Real-time clock algorithm ;
;*********************************;

CLOCK
INCF SEC,F ; second for next second
MOVF SEC,W
SUBWF SPEED2,W
BTFSC STATUS,C
RETURN

CLRF SEC
INCF MIN,F
MOVF MIN,W
SUBLW d'59'
BTFSC STATUS,C
RETURN

CLRF MIN
INCF HOUR,F
MOVF HOUR,W
SUBLW d'23'
BTFSS STATUS,C
CLRF HOUR
RETURN

;*********************************;
; Displays digit in W ;
;*********************************;

DISPLAY clrf LIGHT
btfsc SEC,0 ; these lines are used to translate
bsf LIGHT,3 ; the stored values to the physical pins
btfsc SEC,1 ; that the LEdS are connected to. Ideally,
bsf LIGHT,2 ; they would not be required, but I wired some
btfsc SEC,2 ; stuff backwards, so I had to fix it in
bsf LIGHT,1 ; firmware - no biggie.
btfsc SEC,3
bsf LIGHT,0
btfsc SEC,4
bsf LIGHT,4
btfsc SEC,5
bsf LIGHT,5
movfw LIGHT
MOVWF PORTB ; and display
BSF PORTA,1
CALL DELAY2 ; short delay
BCF PORTA,1

clrf LIGHT
btfsc MIN,0
bsf LIGHT,3
btfsc MIN,1
bsf LIGHT,2
btfsc MIN,2
bsf LIGHT,1
btfsc MIN,3
bsf LIGHT,0
btfsc MIN,4
bsf LIGHT,4
btfsc MIN,5
bsf LIGHT,5
movfw LIGHT
MOVWF PORTB ; and display
BSF PORTA,0
CALL DELAY2 ; short delay
BCF PORTA,0

clrf LIGHT
btfsc HOUR,0
bsf LIGHT,3
btfsc HOUR,1
bsf LIGHT,2
btfsc HOUR,2
bsf LIGHT,1
btfsc HOUR,3
bsf LIGHT,0
btfsc HOUR,4
bsf LIGHT,4
btfsc HOUR,5
bsf LIGHT,5
movfw LIGHT
MOVWF PORTB ; and display
BSF PORTA,2
CALL DELAY2 ; short delay
BCF PORTA,2
RETURN


;*********************************;
; Checks for a switch press and ;
; updates digit if displayed ;
;*********************************;
CHKSW BTFSC PORTB,6 ; switch closed?
goto CLRCNT1 ; no - return 0
movlw h'FF'
movwf SPEED
movlw d'19'
movwf SPEED2
incf COUNT1,f
movfw COUNT1
SUBLW 5
BTFSC STATUS,C
RETURN
movlw h'FF'
movwf SPEED
RETURN

CHKSW2 BTFSC PORTB,7 ; switch closed?
RETURN ; no
INCF HOUR,F
MOVF HOUR,W
SUBLW 23
BTFSS STATUS,C
CLRF HOUR
RETURN

;*********************************;
; Delay used by switch routine ;
;*********************************;

DELAY MOVLW D'12' ; roughly 100ms delay
MOVWF TICKS
DEL1 CALL DELAY2
DECFSZ TICKS,F
GOTO DEL1
RETURN

DELAY2 MOVLW d'255' ; 255 cycles or so...will test timing.
MOVWF DELVAR
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ DELVAR,F
GOTO DELAY2+2
RETURN

;*********************************;
; Reset speed timers ;
;*********************************;

CLRCNT1 movlw h'C4'
movwf SPEED
movlw d'59'
movwf SPEED2
clrf COUNT1
return

ISR MOVWF W_SAVE ; save W
SWAPF STATUS, W ; save STATUS
MOVWF STATUS_SAVE
BCF STATUS, RP0 ; be sure we are in bank 0
BCF INTCON,2
movfw SPEED
movwf TMR0
movfw SPEED
movwf TMR0
CALL CLOCK ; increment the timer
CALL CHKSW ; and check the buttons
CALL CHKSW2
SWAPF STATUS_SAVE, W
MOVWF STATUS ; restore W and STATUS
SWAPF W_SAVE, F
SWAPF W_SAVE, W
RETFIE ; return from isr
END




Also, a big thanks to Greg Houston for http://formatmysourcecode.blogspot.com/.

Wednesday, June 3, 2009

Really good iced tea

I surprised myself with this one. It was really easy, and actually came out really good! Without further blabbing,

Ingredients
4 bags Wissotzky Raspberry Black Tea.
10 leaves fresh mint (I might use more next time, but I didn't want to cut too much)
1 lemon
1/2 cup sugar
around 3/4 of a pitcher full of water

In a pot, heat the water. Throw in the tea bags (without paper) when the water looks pretty hot. Let it boil, then turn off the heat. When it cools a bit, add the mint. Juice half the lemon, and cut the other half into quartered slices, and place inside. Add sugar. Let it cool some more. Pour into pitcher, and fill the rest of the way with water, and let cool in fridge. Drink and enjoy.

Tuesday, May 26, 2009

A better 'Arduino' Stamp (very beta)


avr stamp board layout
Originally uploaded by hithisishal


Edit (7/10/09): I finally got the boards back. I am working out some small problems, and will update soon.

I'm not exactly sure if this is ready to publish, but I don't think I'm going to get any farther until I get a prototype put together, which may be a while down the road. Anyway, my school has a lot of Boe-Bots. The Boe-bot is a really great beginner robotics/microcontroller platform, but that is exactly what it is. They are used for high school students and freshmen in an "Intro to all types of engineering" class. It would be nice to get some more use out of them in higher level classes, and the ability to program them in C would do just that. So I was given the task of designing a board that I could swap with the BASIC Stamp to allow this. The Arduino Mini is almost that, but not quite. There are a number of problems that make it unsuitable to simply stick in a Board of Education (BoE). I aimed to fix all of these:

1. The BS2 has inverters on TX/RX on the board. This way, it can talk RS-232 without a MAX232. The BoE has an FTDI USB to Serial converter, and inverters built in on the board, so it can talk to the BS2 (it ends up inverting the signal twice). The only way to make a board that is hot swappable would be to put inverters on the AVR Stamp as well (or to use a different USB/serial device, but I chose the first option).
2. The BoE has a silk layer saying the pinouts - and these are broken from the socket to headers around a small breadboard. They were shifted by 2 on the Arduino mini. They're still wrong, but closer. Now it's right from 0-8.
3. The BS2 lacks hardware PWM, so pins 12, 13, 14, and 15 are arbitrarily broken out to headers that can connect to the motors. I needed to connect at least 2 of these to Arduino PWM pins, so I chose Arduino pins 9 and 10 (OC1A and OC1B).
4. The Arduino Mini didn't fit in the socket well. I hope I can find headers/pins that will fit better.
5. The Arduino Mini had to be reset before programming. The Mini Pro (and my AVR Stamp) has a switch to make this easier. I hope the DTR will work now, though. I'll see, and perhaps be able to use this board space for an ISP header or breakout for more analog pins in a later revision.

This board layout is totally unconfirmed. Don't make it if you want it to work. Don't complain to me if you make it and it doesn't work.

Eagle files follow. The library contains 2 parts, and the board also uses the Sparkfun library.

Bill of Materials with Digi-Key part numbers
Eagle Board File
Eagle Schematic File
Eagle Library with the two transistors.

Note: I know that I used the words Arduino and AVR interchangeably here. But that's because they mostly are. This project is not official Arduino hardware, and should not be called an Arduino. However, it is very closely based on the Arduino Mini Pro, which basically makes it an Arduino?

Monday, May 11, 2009

Classes are done!

Summer time = hacking time, which should lead to posting time? And it will. But for now I just bought a Game Boy Advance and Flash Cart. I have this GPS data viewer wired up with my Trimble Ace II. I'm reading through TONC, an awesome tutorial. I want to do tons of stuff with it, starting with rewriting that GPS program without the HAM and HEL libraries (mostly as an exercise), and add a waypoint finder so that it could be used for Geocaching.

It's so weird having a 32 bit native datatype.

Saturday, February 28, 2009

Single cell white LED flashlight


flashlight_schem
Originally uploaded by hithisishal
I have been quite busy with classes and trying to figure out what to do next year, so I still haven't had a chance to finish up the weather station. Anyway, Here's a little project that I am working on (well, waiting for switches to arrive from DX) for a class under Professor Mark Yim . As I have mentioned before, I am very inexperienced (and frankly, just bad) at mechanical design, but for this project we used a 3D Printer to make the case. The assignment was to make an LED flashlight, just as an intro to the course - expect cooler stuff in the future. I chose to make a small light that will run for a long time (~15 hours off AAA, 35 with a AA).

The circuit is basically straight out of an app note, but I don't think the IC is well embraced by the hobbyist community - I didn't see any designs out there at all, so I figured I would share. Anyway, it's very small, and I was able to keep the entire circuit small with some careful soldering. I also had tons of fun using the 3D printer. The knurling and threads came out so much better than I expected! Although i barely have time to hack, it's really nice to be at a university with all of these resources.

Click on the image to see the rest of the pictures from this project on flickr.

Saturday, January 24, 2009

Weather Station Update


Weather Projector Wheel
Originally uploaded by hithisishal
Sorry it's been so long since I last updated. I finished the software, and the mechanics are (as always) holding me up. This time, with the help of Cameron, I laser cut a projection wheel for the project. Light will shine through this wheel, projecting the shape onto a screen, with the color dictated by the forecast temperature, as described in the last post. The shape projected corresponds to the first icon in the forecast section, from Weather Underground, retrieved through the API. The question mark is used for any "chance of" forecast. More on this, and code, soon.

We also took the opportunity to cut 120 snowflakes out of the other side of the box. It's awesome to have all the resources of a university to abuse.

Thursday, December 25, 2008

Standalone Weather Station, Part 1


Nearly The Same
Originally uploaded by hithisishal
Click image for more photos of this project
First, I have to put out a big thank you and acknowledgment to Jeff from mightyOhm. I saw a link to his wireless radio project posted somewhere, and thought it was interesting. I had not even heard of OpenWrt before reading the project on his blog. I began my work by reproducing his, then moving on. Similarly, if you are interested in building this, I recommend you begin by reading his tutorial, at least up to part 5.

Aside from radio functionality, one of the things that I would like to be able to do without a computer is check the weather in the morning. I use Weather Underground to check my weather - a neat, community based weather website with many features (including an API - more on that later). One of my favorite parts of this site is the simple statement under the 5-day forecast, along the lines of "Tomorrow is forecast to be Much Warmer than today." It would be useful to be able to view this statement without turning on my computer. Since it is such a small amount of information (only 5 different possibilities), it would be silly to dedicate complicated hardware to this. Instead, I will be displaying the result on an RGB LED (for now 2 LEDs) that fades between blue and red, based on the forecasted temperature relative to the past day.

I am developing this system using an ATMEGA168P, programmed in C. Excuse the appearance of my development hardware - it is not nearly as complicated as it looks. I'm simply using what I had available, which was originally constructed for a different purpose.

Software on the Router


First, I wrote a script for the router to check the forecast, and output the result as a number from 1 to 5. This would then be easy for the AVR to interpret. Since the router is considerably more powerful, it is logical to do as much processing on the router as possible. All of these scripts were written using vi, with a telnet connection to the router. To use this script, put this file in a location that the $PATH variable points to, such as /usr/bin/, and use chmod 577 to make it executable. Unfortunately, the statement that I want is not contained in the API output, so the script uses wget to extract the contents of the Weather Underground search result website. The rest of the script is beyond the scope of this post, but there is a lot of information about shell scripting available on the internet.


#! /bin/ash

#reltemp.sh

FORECAST=`wget -q -O - "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=$ZIPCODE" | grep 'is forecast to' | sed 's/<[^>]*>//g;s/^ [ ]*//g' `

#echo $FORECAST

case $FORECAST in
"Tomorrow is forecast to be Much Warmer than today."|"Today is forecast to be Much Warmer than yesterday.")
echo 5
;;
"Tomorrow is forecast to be Warmer than today."|"Today is forecast to be Warmer than yesterday.")
echo 4
;;
"Tomorrow is forecast to be nearly the same temperature as today."|"Today is forecast to be nearly the same temperature as yesterday.")
echo 3
;;
"Tomorrow is forecast to be Cooler than today."|"Today is forecast to be Cooler than yesterday.")
echo 2
;;
"Tomorrow is forecast to be Much Cooler than today."|"Today is forecast to be Much Cooler than yesterday.")
echo 1
;;
*)
echo 0
;;
esac


Now, we have to set up our location in the environment variable ZIPCODE. Simply add the following line to the /etc/profile file, obviously with your own zip code.

export ZIPCODE=19104


With this complete, simply type reltemp.sh, and in a couple of seconds, a number between 1 and 5 should appear on the terminal, indicating the forecasted temperature of tomorrow relative to today, or today relative to yesterday, based on the time of day.

External Interface

Now, the AVR must talk to the router in order to display the information. Although not pictured, the hardware is relatively simple. The AVR must be connected to power (provided by the router), a 20 MHz crystal oscillator, and the serial Rx and Tx on the router. Additionally, connect the LEDs as shown in the schematic below. This is connected to the PWM output of the AVR, so that a high signal will turn the red LED on, and a low signal will turn the blue LED on. Therefore, changing the duty cycle of a wave applied between the LEDs allows them to fade between blue and red. Change resistor values as needed to balance the shades, based on the brightness of the LEDs.


LEDs
Originally uploaded by hithisishal


Please note, although we developed similar solutions in parallel, what I did is slightly different from what Jeff does in his parts 6 and 7. I used the default router baud rate of 115.2k, but communication was only stable if I ran the AVR with a baud rate of 110k. You might have to tweak this value for your router, or clock it down as shown at mightyOhm.


/*

Copyright: Hal Emmer 2008
License: Creative Commons Attribution-Noncommercial-Share Alike 3.0

For atmega168, at 20 MHz.

*/

#include <stdio.h>
#include <avr/io.h>
#include <avr/eeprom.h>
#include <ctype.h>
#include <avr/interrupt.h>

#define FOSC 20000000
#define BAUD 110000 //router is nominally 115.2k bps, communication is most stable at 110k bps.
#define MYUBRR FOSC/8/BAUD-1 //double speed
//#define MYUBRR

#define sbi(var, mask) ((var) |= (uint8_t)(1 << mask))
#define cbi(var, mask) ((var) &= (uint8_t)~(1 << mask))

#define STATUS_LED 0

#define BUFFERSIZE 80

volatile unsigned int global_time1;

//======================
//Define functions
//======================

static int uart_putchar(char c2, FILE *stream);
uint8_t uart_getchar(void);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
void init(void);
void get_output(char *str);
void get_rel_temp(void);
//======================




int main (void)
{

init(); //Setup IO pins and defaults
printf("\nreltemp.sh\n");
while(1){

if (global_time1 >= 4578){ //4578 = 1 minute
global_time1 = 0;
get_rel_temp();

}
}
}

void init (void)
{
//1 = output, 0 = input
DDRB = 0b11101111; //PB4 = MISO
DDRC = 0b11111110; //
DDRD = 0b11111110; //PORTD (RX on PD0)

//USART Baud rate: 115200
UBRR0H = ((MYUBRR >> 8));
UBRR0L = MYUBRR;
UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
UCSR0A |= (1<<U2X0); //double rate

stdout = &mystdout; //Required for printf init


/* Set up PWM (for fading LED)*/
DDRB |= (1 << 3); // OC1A (PB3),outputs
ICR1 = 20000; // TOP, set for 125Hz
OCR1A = 10000; // neutral/purple
TCCR1A = (1<<COM1A1)|(1<<WGM11); // Clear on compare, set at TOP
TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11); // Timer 1 fast PWM mode 14 , /8 prescaler


/* Set up TOI on timer2 for task timing */
TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20); //Start Timer2 with 1/1024 prescaler.
TIMSK2 |= (1<<TOIE2); //enable timer2 overflow interrupt
TIFR2 |= (1<<TOV2); //clear the mask
sei();
}


static int uart_putchar(char c2, FILE *stream)
{
//if (c2 == '\n') uart_putchar('\r', stream);

loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c2;

return c2;
}


uint8_t uart_getchar(void)
{
while( !(UCSR0A & (1<<RXC0)) );
return(UDR0);
}



/* not used right now */
void get_output(char *str) {
unsigned char count;
for (count = 0; count < BUFFERSIZE; count++) {
str[count] = uart_getchar();
if (str[count] == '@' && str[count - 1] == 't')
break;
}
str[count - 5] = '\0';
}


void get_rel_temp(void){
char c = 0;

printf("\nreltemp.sh\n"); //as the router for the relative temperature
while(!isdigit(c)) //get a character until you get a digit
c = uart_getchar();
switch(c){
case '0':
OCR1A = 10000; // error / purple
break;
case '1':
OCR1A = 0; // much cooler / blue
break;
case '2':
OCR1A = 2500; // cooler / blue-purple
break;
case '3':
OCR1A = 10000; // same temp as / purple
break;
case '4':
OCR1A = 17500; // warmer / red-purple
break;
case '5':
OCR1A = 20000; // much warmer / red
break;
default:
OCR1A = 10000; // error / purple
break;
}
}

ISR(TIMER2_OVF_vect)
{
global_time1++;
//uart_putchar('0'+global_time1, stdout);
TIFR2 |= (1<<TOV2);

}


That should do it. Next time I will explore how to display the next important piece of information - if it will be sunny, cloudy, rainy, etc. After that I will wrap up the project in a more presentable package.