TITLE: Egg locator source code
AUTHOR: Chuck McManis
LAST UPDATE: 12-Nov-2004

Description

This simple program was designed to turn an inexpensive 8 pin microcontroller into a self-contained low power transmitter. The goals of that were described in the EELB article. Transmitting is accomblished by enhancing signal leakage into the AM band, its hardly a threat to your neighbor but it’s a great way to demonstrate how AM radios work in a fun way.

The Source Code

;
; Emergency Egg Locator Beacon
; Written 02-Nov-2004
; Copyright (C) 2004, Charles McManis, All Rights Reserved
; 
; A non-exclusive, non-transferrable right to use, modify, or 
; distribute is granted to non-commercial purposes.
;
; This is a fairly simple hack that is fun and educational. 
; All the way back to the 1950's scientists have known that
; putting an AM radio near the computer allowed you to "pick up" 
; interference related to what was going on inside. Some creative
; individuals figured out they could make timing loops that actually
; generated tones such that music could be played.
;
; This program brings that hack into the 21st century (or at least
; late 20th century) using a PIC12F6xx chip. The interesting thing
; about this chip is that it has an internal 4Mhz oscillator, that
; you can route as a divide/4 value (1Mhz) to one of its output pins.
; 1Mhz happens to be smack dab in the middle of the AM radio band.
; The other feature of PIC chips that comes into play is that they 
; can drive 25mA or sink 25mA with their I/O pins. 
;
; So take the 1Mhz output, modulate it with another pin, and voila'
; instant CW AM transmitter. Given an inexpensive AM radio the signal
; generated can be picked up for 30 - 50' away. Probably more if you
; had a directional antenna on your reciever. 
;
; CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
; SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
; BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS
; SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
; OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
;
;
    TITLE "Emergency Egg Locator Beacon v1.0"
    LIST P=PIC12F629, C=120, N=50, R=HEX
    include "P12F629.inc"
    __FUSES _CP_OFF&_WDT_ON&_MCLRE_OFF&_INTRC_OSC_CLKOUT&_BODEN_ON&_PWRTE_ON
    ERRORLEVEL 2

    CBLOCK H'20'
        BIT_COUNT:1     ; One byte of BIT Count
        BYTE_COUNT:1    ; Counter for bytes
        BIT_DELAY:1     ; Delay for one bit.
        BOOLS:1         ; Our flags register
        ISR_W           ; Storage for W
        ISR_FSR         ; Copy of the FSR
        ISR_STATUS      ; Copy of the Status register
        EE_SRC          ; EEPROM Source Pointer
        EE_DST          ; EEPROM Destination Pointer
        EE_LEN          ; EEPROM Byte count
        MSG_BUF         ; Buffer byte for message
        MSG:16          ; 16 Bytes (128 bits) for our message
    ENDC


BIT_WIDTH   EQU D'100'  ; 100 mS or .1 seconds
BIT_FLIP    EQU 0       ; Bit 0 of our bools is 'time to flip'

T0_CONST_4MHZ   EQU     D'133'  ; Constant for 1mS ticks at 4Mhz

    ORG H'2100'
;
; 16 Bytes of Message 
; in this case EELB ALPHA ONE
; 
;
EGG_MSG:
;       B'01234567'
;         E   E       LLLLLLLLLLLLL   BBBBBBBBBBBBB
    DE  B'10001000',B'10111010',B'10001110',B'10101000'
;            AAAAA       LLLLLLLLLLLLL   PPPPPPPPPPPPP
    DE  B'00010111',B'00010111',B'01010001',B'01110111'
;         PP   HHHHHHHHHHH   AAAAAAAAA           OOOOOO      NNNNNNNNN   E
    DE  B'01000101',B'01010001',B'0111000', B'00010101',B'00011101',B'00010000'
    DE  B'00000000'     ;
EGG_MSG_SIZE    EQU $-EGG_MSG

;
; EEREAD macro
; Written 12-Apr-2002, Chuck McManis
;
; Uses the temporary variable EE_LEN, EE_SRC, and EE_DST
; During read, Source is EEPROM and Destination is RAM
;
; Read a block of EEPROM and store it in RAM
; Assumes RAM block is in BANK0.
;                                  
EEREAD  MACRO   SRC_ADDR, DST_ADDR, LEN
        LOCAL   RLOOP
        MOVLW   LEN             ; Number of bytes to copy
        MOVWF   EE_LEN          ; Store the byte count.
        MOVLW   SRC_ADDR        ; Get the source address in EEPROM
        MOVWF   EE_SRC          ; Store it here.
        MOVLW   DST_ADDR        ; Get the destination address
        MOVWF   EE_DST          ; Store it here.
RLOOP:
        MOVF    EE_DST,W        ; Get byte destination
        MOVWF   FSR             ; Store it in the FSR
        BSF     STATUS, RP0     ; Switch to bank 1 
        MOVF    EE_SRC,W        ; Address to read
        MOVWF   EEADR           ; Start address
        BSF     EECON1, RD      ; Read the byte
        MOVF    EEDATA, W       ; Assume a single cycle read?
        BCF     STATUS, RP0     ; Back to bank 0
        MOVWF   INDF            ; Store in Destination
        INCF    EE_SRC,F        ; Next source address
        INCF    EE_DST,F        ; Next destination address
        DECFSZ  EE_LEN,F        ; Decrement the count
        GOTO    RLOOP           ; Loop if not done.
        ENDM


    ORG H'0000'
INIT:

    GOTO    MAIN

;
; The only interrupt source in this program is timer0. I've empirically
; determined a 'reload' value such that the interrupts occur at approximately
; 1mS intervals. If we toggle a pin, the pin generates a squarewave with a
; frequency of approximately 500Hz (somewhere above middle A 440 in the scale)
; This pin is tied externally to a voltage divider to modulate the CLKOUT
; signal.
;
; Additionally there is a counter for determining one "bit" width. Each bit 
; is held for approximately .1s so three "one" bits in a row generates a
; .3s tone and a single one bit generates a .1s tone.
;
    ORG     H'0004'
ISR_ENTER:
    MOVWF   ISR_W           ; Preserve W
    SWAPF   STATUS,W        ; And Status register
    CLRF    STATUS          ; Force Page 0
    MOVF    FSR,W           ; Get the FSR
    MOVWF   ISR_FSR

    ; Check to see if it is Timer 0 interrupting us (it should be!)
    BTFSS   INTCON, T0IF
    GOTO    ISR_EXIT        ; Else exit
    BCF     INTCON, T0IF
    MOVLW   T0_CONST_4MHZ   ; 1Khz interrupt 500hz waveform
    MOVWF   TMR0


    BTFSC   BOOLS, BIT_FLIP ; If this is true we're waiting for next bit
    GOTO    ISR_EXIT        ; next ...

    CLRWDT
    BTFSS   MSG_BUF,7       ; Check high order bit of MSG buffer
    GOTO    NO_TONE         ; If zero don't generate a tone


    ; 
    ; Generates a tone
    ; Should generate 500 hz on GPIO.5 (0x20 is 00100000b)
    ;
TONE:
    MOVF    GPIO,W          ; Read the GPIO Register
    XORLW   H'20'           ; Toggle GPIO,0
    MOVWF   GPIO            ; Write it out 
    BSF     GPIO,GPIO2      ; Turn on GPIO2
    GOTO    BIT_TIMER

NO_TONE:
    MOVF    GPIO,W          ; Get GPIO pins
    IORLW   H'20'           ; Set output high
    MOVWF   GPIO            ; Write it out
    BCF     GPIO,GPIO2      ; Turn off GPIO2

;
; run down the clock on the bit timer.
;
BIT_TIMER:
    DECFSZ  BIT_DELAY       ; This is the beep timer
    GOTO    ISR_EXIT        ; Not timed out.
    MOVLW   BIT_WIDTH
    MOVWF   BIT_DELAY
    BSF     BOOLS, BIT_FLIP ; Next bit please

;
; Worst case the ISR takes about 26 instructions or 26 uS out of 1mS
;
ISR_EXIT:
    MOVF    ISR_FSR,W       ; Get FSR register
    MOVWF   FSR             ; put it back into FSR
    SWAPF   ISR_STATUS,W    ; Pull Status back into W
    MOVWF   STATUS          ; Store it in status 
    SWAPF   ISR_W,F         ; Prepare W to be restored
    SWAPF   ISR_W,W         ; Return it, preserving Z bit in STATUS
    RETFIE

;
; Initialize Timer 0 to drive everything
;

MAIN:
    MOVLW   BIT_WIDTH       ; Basic bit width
    MOVWF   BIT_DELAY       ; Store it
    BSF     BOOLS, BIT_FLIP ; 
    CLRF    GPIO            ; Clear GPIO Bits
    BSF     GPIO, GPIO5
    CLRF    MSG_BUF         ; Make sure MSG byte 0 is clear
    MOVLW   07h             ; Make GPIO bits digital
    MOVWF   CMCON           ; Like so.
    BSF     STATUS, RP0     ; Set Bank 1
    CALL    H'03FF'         ; Get calibration constant
    MOVWF   OSCCAL          ; Store it
    MOVLW   B'0000010'      ; Set TMR0 prescaler to 8
    MOVWF   OPTION_REG      ; Store it in the OPTION register
    BSF     INTCON, T0IE    ; Enable Timer 0 to interrupt
    BCF     INTCON, T0IF    ; Reset flag that indicates interrupt
    MOVLW   0               ; Make GPIO all outputs
    MOVWF   GPIO            ; Using the TRISO register in bank 1
    BCF     STATUS, RP0     ; Now in bank 0.
    BSF     INTCON, GIE     ; Enable interrupts
;
; The purpose of this code is to read out the data bits in
; EEPROM and then one by one put them out on the LED pin
; (GPIO5 in this case). To send morse, the convention of three
; one bits is a dash, one one bit is a dit, and three zero bits
; are a space. 
; 128 bits lets you send about 10 characters in morse.
;
; In C code, this is what is going on:
; 
; memcpy(eeprom, msg, 16); /* get message from EEPROM */
; for (i = 0; i < 16; i++) {
;   msg = data[i];
;   flip = 0; /* reset bit timer */
;   for (j = 0; j < 8; j++) {
;       while (!flip); /* wait time allotment */
;       msg = msg >> 1; /* shift out a bit */
;   }
; }
; msg = 0;
; halt(); 
;
OUTER:
;
; Copy message from EEPROM to the MSG memory
;
    EEREAD  EGG_MSG, MSG, EGG_MSG_SIZE  ; Read msg bytes out of the EEPROM
    MOVLW   EGG_MSG_SIZE
    MOVWF   BYTE_COUNT
    MOVLW   MSG
    MOVWF   FSR
MSG_LOOP:
    MOVF    INDF,W          ; Read message byte
    MOVWF   MSG_BUF         ; Store it in the message buffer byte
    MOVLW   D'8'            ; Send 8 bits
    MOVWF   BIT_COUNT       ; out.
BIT_LOOP:
    BCF     BOOLS, BIT_FLIP ; Send next bit out
    BTFSS   BOOLS, BIT_FLIP ; Wait for it to flip
    GOTO    $-1
    RLF     MSG_BUF,F       ; Rotate next bit out
    DECFSZ  BIT_COUNT,F
    GOTO    BIT_LOOP        ; 
    INCF    FSR,F           ; Point to next byte of message
    DECFSZ  BYTE_COUNT,F    ; Until all sent
    GOTO    MSG_LOOP
;
; Now ideally the chip should go to sleep here, and wake up again in 10
; seconds or so and re-transmit. But its hard to get a fix on it that way
; so for now we just loop back to the beginning and do it all again.
;
    GOTO OUTER

    END

License

Creative Commons License

This work is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License. You are free to play around with it and modify it but you are not licensed to use it for commercial purposes. Click the link for more details.