Tim Allen

Software Reverse Engineer

Beacons using a PIC10F200 August 14, 2015

Before there were standards like iBeacon and Eddystone we had to “roll our own.” This is a primitive beacon using a PIC10F200 (8pin DIP) along with a simple 315MHz OOK transmitter. Later versions, not shown here, use SDR.

beacon.asm

This is written in PIC assembly. It simply transmits my FCC Call sign with a number tacked on the end. Production versions would substitute a UUID and checksum in place of my Call sign. The signal is intentionally weak, we only want to know if the watch (wearable device) is within a few meters. Battery life is important too, this thing should be good for about 57 years on a couple AAA batteries.

The gist of what this code does is to set a timer and go to sleep. When the timer expires the chip wakes up and increments some counter (the timer durations are rather short) and we need some randomization to minimize the possibility of interference betwixt proximal beacons1. If it’s time to send a burst, my Call sign, then it does so, resets the counter and timer and goes back to sleep. The idea is to spend as much time asleep as possible to conserve batteries.

beacon.asm

; FILE: beacon.asm
; AUTH: Tim Allen
; DATE: 1.0 - 18 April `11
; DESC: Controls a 315MHz radio, sends a 
;       serial number periodically (~30s)
; NOTE: Tested on PIC10F200

       include <p10f200.inc>
       list       p=10F200
       radix       hex
       __config _WDT_ON

;----------------------------------------------------------------------
;       macro(s)

xmit   macro   r0
       call    txstart
       movlw   r0
       call    txhex
       call    txstop
       endm
;----------------------------------------------------------------------
; ram "variables"
cblock 0x10
 warmcnt       ; counter for radio warm-up
 count0        ; counter for main prog
 txcount       ; counter for tx0 and tx1
 hex           ; scratch for txhex
 xmitcnt       ; bit counter for txhex
 lfsr          ; pseudo-random number
endc
;----------------------------------------------------------------------

       org     0x000
       goto    main

main                                   ;code start       
       movlw   b'11001111'             ; set watchdog post-scaler
       option
       movlw   b'11111000'             ; 0,1,& 2 output
       tris    GPIO                    ; set GPIO I/O 
       btfss   STATUS,NOT_PD
       goto    run

;----------------------------------------------------------------------
powerup                                ; initialize things
       clrwdt                          ; clear watchdog timer

       movlw   b'00000000'
       movwf   GPIO                    ; turn GPIOs off

       movlw   0x05
       movwf   count0                  ; initialize the counter
       goto    burst                   ; send an initial burst

;----------------------------------------------------------------------
run    ; is it time for a burst?
       decfsz  count0,F
       sleep                           ; no go back to sleep

       ; generate a pseudo-random number
       bcf     STATUS,C
       rrf     lfsr,W
       btfsc   STATUS,C
       xorlw   0xb4
       movwf   lfsr
       andlw   0x03                    ; limit the random to 0-3
       movwf   count0                  ; set the counter
       incf    count0,F                ; just make sure it's not 0

burst  ; send a beacon string
       bsf     GPIO,2                  ; power-up the radio
       movlw   0xff
       movwf   warmcnt
warmlp decfsz  warmcnt,F               ; give it a sec
       goto    warmlp

       xmit    'K'
       xmit    'G'
       xmit    '4'
       xmit    'E'
       xmit    'U'
       xmit    'K'
       xmit    '0'
       xmit    '0'
       xmit    0xf5

       bcf     GPIO,2                  ; power-down the radio
       sleep                           ; go back to sleep

txstart; transmit a start
       bsf     GPIO,0                  ; trigger the scope
       call    tx0
       retlw   0x00

txstop ; transmit a stop
       call    tx1
       bcf     GPIO,0                  ; un-trigger the scope
       retlw   0x00

tx1    ; transmit a 1 bit subroutine
       bcf     GPIO,1
       movlw   d'185'
       movwf   txcount
tx1lp  nop
       decfsz  txcount,F
       goto    tx1lp
       retlw   0x00

tx0    ; transmit a 0 bit subroutine
       bsf     GPIO,1
       movlw   d'180'
       movwf   txcount
tx0lp  nop
       decfsz  txcount,F
       goto    tx0lp
       retlw   0x00

txhex  ; transmit a hex
       movwf   hex
       movlw   0x08                    ; set the counter
       movwf   xmitcnt
nxtop  rrf     hex,F
       btfsc   STATUS,C
       goto    x1
       call    tx0                     ; zero
       goto    nxbtm
x1     call    tx1                     ; one
nxbtm  decfsz  xmitcnt,F
       goto       nxtop
       retlw       0x00

       end

makefile

beacon.hex: beacon.asm
       gpasm beacon.asm

burn:
       pk2cmd -PPIC10F200 -J -R -F beacon.hex -M

clean:
       rm *.hex *.lst *.cod

  1. This is a bit tough to explain so bare with me. The oscillators on these things are not precise. So imagine we were to send a burst every 30 wake-ups (e.g. no randomization). Okay so lets say you have two of these close together and we put batteries in them at the same time. Now every 30 wake-ups they both wake up and clobber each other (the wearable can’t hear either because they “talk” over each other). Now eventually, because the oscillators are imprecise, they drift apart but that may take hours; therefore, we introduce some randomization to keep them asynchronous. I’m skipping some nuance here but I hope you get the idea.