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 beacons*. 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
; 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
1 2 3 4 5 6 7 8 |
beacon.hex: beacon.asm gpasm beacon.asm burn: pk2cmd -PPIC10F200 -J -R -F beacon.hex -M clean: rm *.hex *.lst *.cod |
* 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.