; Open GPS Tracker firmware ; Version 0.16 (release candidate 2) 2008/04/21 ; Mike Ingle (email: mike at opengpstracker dot org) ; http://www.opengpstracker.org ; This is open source software. ; ; Build 0.14 had over 160 downloads at avrfreaks.net and zero forum posts. ; If you like this code, please go and post. I cannot improve the code ; without your comments. http://www.opengpstracker.org/wordpress ; ; If you are upgrading from build 0.14, you must change the phone configuration: ; Message/Options/Memory Meter/Select SMS Memory/Phone First ; ; For ATTINY84 AVR, Tyco A1035 GPS, and Motorola C168i ; Set MCU to run at 1 MHz (CKDIV8 fuse) ; Commands: ; ; SETSPEED ; speed limit or 0 for none - units ; minimum speed to accept as moving - tenths of a unit ; minimum speed to accept as moving on first try - tenths of a unit ; SETTRACK ; stopped fix interval - seconds ; stopped notify delay - intervals ; blocked fix interval - seconds ; blocked notify delay - intervals ; moving fix interval - seconds ; moving notify frequency - intervals ; minimum displacement for MOVED alert ; SETPOWER ; powersave phone off interval - seconds ; powersave phone on interval - seconds ; four-sat fix wait time (before accepting three-sat fix) - seconds ; fix wait time (before giving up and reporting no fix) - seconds ; blocked fix wait time (shorter fix wait when no signal available) - seconds ; SETNAME tracker-name ; SETPASSWORD newpassword newpassword ; SETADDRESS phone-number optional-email-address ; SALOCATE phone-number optional-email-address ; LOCATE ; STATUS ; TRACKON ; TRACKOFF ; POWERSAVE ; POWERON ; REBOOT ; REINIT ; ; Blink codes: ; 11 = phone polled ; 12 = send message failed ; 13 = phone poll failed ; 14 = no phone number defined ; 21 = invalid password ; 31 = power on or reset ; 32 = watchdog reset ; 33 = EEPROM initialized from defaults ; 34 = remove jumper to reinitialize EEPROM .include "tn84def.inc" ; Must define ONE and ONLY ONE of SPEED_IN_KPH, SPEED_IN_MPH, or SPEED_IN_KNOTS ;#define SPEED_IN_KPH = 1 #define SPEED_IN_MPH = 1 ;#define SPEED_IN_KNOTS = 1 ; Must define ONE and ONLY ONE of ALT_IN_FEET or ALT_IN_METERS #define ALT_IN_FEET = 1 ;#define ALT_IN_METERS = 1 ; Serial debug will copy phone interaction to TX_OUT_TO_DEBUG ;#define SERIAL_DEBUG 1 ; Debug message count will keep track of messages sent and output in status page ;#define DEBUG_MESSAGE_COUNT_ENABLE 1 ; Keeps invalid incoming messages in the phone. This disables reading of REC READ ; folder. It is only for debugging. I put it here to track down a message bounce problem. ;#define DEBUG_KEEP_INVALID_MESSAGES 1 ; Add error codes to WDR_COUNT for send message failures. ;#define DEBUG_SEND_MESSAGES 1 ; Reset phone after every PHONE_RESET_INTERVAL interactions and for power on ; Required for C168i and GoPhone service #define RESET_PHONE_PERIODICALLY 1 .equ BAUD_RATE_COUNTER = 208 ; 4800 baud .equ INIT_RECEIVE_TIMEOUT = 8 ; short timeout for ATE0 (2 seconds) .equ DEFAULT_RECEIVE_TIMEOUT = 20 ; timeout for most commands (5 seconds) .equ SMS_SEND_RECEIVE_TIMEOUT = 120 ; timeout for SMS SEND (24 seconds) .equ DEFAULT_PHONE_POLL_INTERVAL = 900 ; default phone poll interval (not tracking) in seconds (15 minutes) .equ DEFAULT_FOUR_SAT_WAIT_TIME = 18 ; 72 seconds (x4) .equ DEFAULT_GPS_FIX_WAIT_TIME = 34 ; 136 seconds (x4) .equ DEFAULT_BLOCKED_GPS_FIX_WAIT_TIME = 24 ; 96 seconds .equ DEFAULT_SPEED_LIMIT = 0 ; disabled .equ DEFAULT_TRACK_STOPPED_FIX_INTERVAL = 120 ; two minutes .equ DEFAULT_TRACK_STOPPED_NOTIFY_DELAY = 2 ; 2 poll intervals .equ DEFAULT_TRACK_BLOCKED_FIX_INTERVAL = 600 ; ten minutes .equ DEFAULT_TRACK_BLOCKED_NOTIFY_DELAY = 2 ; 2 poll intervals .equ DEFAULT_TRACK_MOVING_FIX_INTERVAL = 120 ; two minutes .equ DEFAULT_TRACK_MOVING_NOTIFY_FREQ = 5 ; 5 poll intervals .equ DEFAULT_POWERSAVE_PHONE_OFF_INTERVAL = 3600 ; one hour .equ DEFAULT_POWERSAVE_PHONE_ON_INTERVAL = 600 ; 10 minutes .equ DEFAULT_TRACK_MIN_DISPLACEMENT = 200 ; 0.2 minute of lat/lon .equ DEFAULT_GPS_MOVING_THRESHOLD = 20 ; 2 or more units reported = moving .equ DEFAULT_GPS_MOVING_RETRY_THRESHOLD = 100 ; if under 10 units, double check speed .equ GPS_DATASTREAM_WAIT_TIME = 60 ; 15 seconds .equ GPS_LINES_AFTER_FIX = 10 ; how many more lines to wait for after fix valid .equ PHONE_RESET_INTERVAL = 100 ; reset phone every N polls, after message received .equ PHONE_RESET_WAKEUP_DELAY = 45 ; time in seconds to wait for the phone to power on .equ PHONE_MAX_CHECKSUM_LENGTH = 160 ; largest message phone will store ; Units conversion factors (from HP50G calculator) .equ CF_KNOT_MPH = 37709 ; KNOT * 2 before conversion .equ CF_KNOT_KPH = 60686 ; KNOT * 2 before conversion .equ CF_M_FT = 53753 ; M * 4 before conversion .equ TX_OUT_TO_PHONE = PORTA1 .equ RX_IN_FROM_PHONE = PORTA0 .equ RX_IN_FROM_GPS = PORTA2 .equ TX_OUT_TO_DEBUG = PORTA7 .equ LED_OUT_RED = PORTB0 .equ LED_OUT_GREEN = PORTB1 .equ GPS_POWER_OUT = PORTB2 .equ REINIT_CHECK_IN = PORTA4 .equ REINIT_CHECK_OUT = PORTA6 .equ MAX_PHONE_ERRORS = 5 ; AT command retries .def FLAG_REGISTER = r16 .equ FLAG_SERIAL_SEND = 0 ; set during serial send .equ FLAG_SERIAL_RECEIVE = 1 ; set during serial receive; clear to detect unsolicited message .equ FLAG_PARSE_NMEA = 2 ; set to put PARSE_LINE in NMEA mode .equ FLAG_SKIP_REPEATED_DELIMITER = 3 ; flag used in PARSE_LINE .equ FLAG_GOT_RECEIVED_MESSAGE_NUMBER = 4 ; indicates +CMGL captured .equ FLAG_NEED_TO_POLL_MESSAGES = 5 ; got CMTI, interrupt, etc. .equ FLAG_PHONE_FULL = 6 ; need to clear space in phone .equ FLAG_MESSAGE_SENT = 7 ; message sent .def REG_ZERO = r0 ; register always zero; saves a lot of clr instructions .def SERIAL_PORT = r1 ; Set bit corresponding to I/O line you want to use. ; Note: if more than one bit is set, in output mode, you can send to multiple output ; lines at once. This is used for the debug output. .def SERIAL_DATA = r2 ; Shift register for data byte. .def SERIAL_COUNT = r17 ; Counts down bits sent/received. .def SERIAL_POINTER = r3 ; Pointer into 256-byte serial receive buffer. .def SERIAL_SCRATCH = r4 ; Scratch register used by serial I/O .def SERIAL_INTERRUPT_SREG = r5 ; Storage for Status flags during serial interrupt .def TIMER_INTERRUPT_SREG = r6 ; Storage for Status flags during timer interrupt .def DELAY = r23 ; this register is counted down by an interrupt 4x/sec, stopping at zero .def RETRY_COUNT = r9 ; error count for serial operations ; Parser register assignments ; Y has pointer into buffer ; T flag is error .def CHAR = r18 ; current character .def PAT = r7 ; current pattern character .def BASE = r8 ; base of string being matched .def CHECKSUM = r10 .def DELIM = r11 ; 26-27, 28-29, 30-31 are pointers .def SCRATCH0 = r19 .def SCRATCH1 = r20 .def SCRATCH2 = r21 .def SCRATCH3 = r22 .def READ_FIELD = r19 .def PARSE_FIELD = r20 .def FIELD_LENGTH = r21 .def VERIFY_CHECKSUM = r12 #ifdef SERIAL_DEBUG .def DEBUG_POINTER = r13 #endif ; Status codes returned by GPS_LOCATE .equ GPS_STATUS_DATASTREAM_TIMEOUT = 0 .equ GPS_STATUS_FIX_TIMEOUT = 1 .equ GPS_STATUS_FIX_STOPPED = 2 .equ GPS_STATUS_FIX_MOVING = 3 ; Tracker state values .equ TRACK_STATE_OFF = 0 .equ TRACK_STATE_BLOCKED = 1 .equ TRACK_STATE_STOPPED = 2 .equ TRACK_STATE_MOVING = 3 .equ TRACK_STATE_INITIAL = 4 .equ TRACK_STATE_POWERSAVE = 5 ; lowest powersave value .equ TRACK_STATE_POWERSAVE_PHONE_OFF = 5 .equ TRACK_STATE_POWERSAVE_PHONE_ON = 6 ; Decode modes .equ DECODE_SETTING_TWO_BYTE = 7 .equ DECODE_SETTING_ONE_BYTE = 6 .equ DECODE_SETTING_ONE_BYTE_X4 = 5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EEPROM addresses ; All the short one and two byte settings are first, so we can use single-byte addressing ; and save space in the program. Assumptions all through the program that settings ; are in the first 256 bytes of EEPROM. Long fields at the end may extend into higher ; pages of EEPROM. EEPROM is never written except for first-time initialization and ; in response to user commands. .equ EEADR_GPS_FOUR_SAT_WAIT_TIME = 0 .equ EELEN_GPS_FOUR_SAT_WAIT_TIME = 1 ; four seconds per count .equ EEADR_GPS_FIX_WAIT_TIME = EEADR_GPS_FOUR_SAT_WAIT_TIME + EELEN_GPS_FOUR_SAT_WAIT_TIME .equ EELEN_GPS_FIX_WAIT_TIME = 1 ; four seconds per count .equ EEADR_BLOCKED_GPS_FIX_WAIT_TIME = EEADR_GPS_FIX_WAIT_TIME + EELEN_GPS_FIX_WAIT_TIME .equ EELEN_BLOCKED_GPS_FIX_WAIT_TIME = 1 ; four seconds per count .equ EEADR_SPEED_LIMIT = EEADR_BLOCKED_GPS_FIX_WAIT_TIME + \ EELEN_BLOCKED_GPS_FIX_WAIT_TIME .equ EELEN_SPEED_LIMIT = 1 ; in selected units .equ EEADR_GPS_MOVING_THRESHOLD = EEADR_SPEED_LIMIT + \ EELEN_SPEED_LIMIT .equ EELEN_GPS_MOVING_THRESHOLD = 1 ; in selected units * 10 .equ EEADR_GPS_MOVING_RETRY_THRESHOLD = EEADR_GPS_MOVING_THRESHOLD + \ EELEN_GPS_MOVING_THRESHOLD .equ EELEN_GPS_MOVING_RETRY_THRESHOLD = 1 ; in selected units * 10 .equ EEADR_TRACK_STOPPED_FIX_INTERVAL = EEADR_GPS_MOVING_RETRY_THRESHOLD + \ EELEN_GPS_MOVING_RETRY_THRESHOLD .equ EELEN_TRACK_STOPPED_FIX_INTERVAL = 2 ; how often to get GPS fix when stopped .equ EEADR_TRACK_STOPPED_NOTIFY_DELAY = EEADR_TRACK_STOPPED_FIX_INTERVAL + \ EELEN_TRACK_STOPPED_FIX_INTERVAL .equ EELEN_TRACK_STOPPED_NOTIFY_DELAY = 1 ; how many intervals after stopped to notify and use ; stopped-mode fix interval .equ EEADR_TRACK_BLOCKED_FIX_INTERVAL = EEADR_TRACK_STOPPED_NOTIFY_DELAY + \ EELEN_TRACK_STOPPED_NOTIFY_DELAY .equ EELEN_TRACK_BLOCKED_FIX_INTERVAL = 2 ; how often to attempt GPS fix when blocked .equ EEADR_TRACK_BLOCKED_NOTIFY_DELAY = EEADR_TRACK_BLOCKED_FIX_INTERVAL + \ EELEN_TRACK_BLOCKED_FIX_INTERVAL .equ EELEN_TRACK_BLOCKED_NOTIFY_DELAY = 1 ; how many intervals after blocked to notify and use ; blocked-mode fix interval .equ EEADR_TRACK_MOVING_FIX_INTERVAL = EEADR_TRACK_BLOCKED_NOTIFY_DELAY + \ EELEN_TRACK_BLOCKED_NOTIFY_DELAY .equ EELEN_TRACK_MOVING_FIX_INTERVAL = 2 ; how often to get GPS fix when moving .equ EEADR_TRACK_MOVING_NOTIFY_FREQ = EEADR_TRACK_MOVING_FIX_INTERVAL + \ EELEN_TRACK_MOVING_FIX_INTERVAL .equ EELEN_TRACK_MOVING_NOTIFY_FREQ = 1 ; notify every N intervals while moving .equ EEADR_POWERSAVE_PHONE_OFF_INTERVAL = EEADR_TRACK_MOVING_NOTIFY_FREQ + \ EELEN_TRACK_MOVING_NOTIFY_FREQ .equ EELEN_POWERSAVE_PHONE_OFF_INTERVAL = 2 ; time in seconds to leave phone off in powerdown .equ EEADR_POWERSAVE_PHONE_ON_INTERVAL = EEADR_POWERSAVE_PHONE_OFF_INTERVAL + \ EELEN_POWERSAVE_PHONE_OFF_INTERVAL .equ EELEN_POWERSAVE_PHONE_ON_INTERVAL = 2 ; time in seconds to leave phone on in powerdown .equ EEADR_TRACK_MIN_DISPLACEMENT = EEADR_POWERSAVE_PHONE_ON_INTERVAL + \ EELEN_POWERSAVE_PHONE_ON_INTERVAL .equ EELEN_TRACK_MIN_DISPLACEMENT = 2 ; displacement to consider MOVED, in minutes*100 of lat/lon .equ EEADR_PHONE_NUMBER = EEADR_TRACK_MIN_DISPLACEMENT + \ EELEN_TRACK_MIN_DISPLACEMENT .equ EELEN_PHONE_NUMBER = 32 .equ EEADR_EMAIL_ADDR = EEADR_PHONE_NUMBER + EELEN_PHONE_NUMBER .equ EELEN_EMAIL_ADDR = 64 .equ EEADR_PASSWORD = EEADR_EMAIL_ADDR + EELEN_EMAIL_ADDR .equ EELEN_PASSWORD = 16 .equ EEADR_DEVICENAME = EEADR_PASSWORD + EELEN_PASSWORD .equ EELEN_DEVICENAME = 16 .equ EEADR_CHECKCODE = 510 .equ EELEN_CHECKCODE = 2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SRAM addresses .dseg CMGL_SAFETY_COUNT: .byte 1 ; at beginning of buffer so wild pointer does not step on it WDR_COUNT: .byte 1 ; count of watchdog resets SEND_CHECKSUM: .byte 1 ; checksum value for sending SEND_CHECKSUM_COUNTER: .byte 1 ; count of characters checksummed SEND_CHECKSUM_BYTES: .byte 1 ; captured count at end of line READ_CHECKSUM_BYTES: .byte 1 ; received message length for verification #ifdef DEBUG_MESSAGE_COUNT_ENABLE DEBUG_MESSAGE_COUNT: .byte 2 ; count of messages sent #endif #ifdef RESET_PHONE_PERIODICALLY PHONE_RESET_COUNT: .byte 1 #endif #ifdef DEBUG_KEEP_INVALID_MESSAGES DEBUG_INVALID_MESSAGE_FLAG: .byte 1 #endif SERIAL_SEND_DELAY: .byte 1 ; number of bit times per byte for serial, min is 11 RECEIVE_TIMEOUT: .byte 1 ; delay for receiving data PHONE_STRING: .byte 2 SEND_STRING_OFFSET_0: .byte 1 SEND_STRING_OFFSET_1: .byte 1 CURRENT_SMS: .byte 4 COMMAND_PENDING: .byte 2 GPS_TIMER: .byte 1 ; counts up if reset to zero, one count per 4 seconds, stops at 0xff GPS_STATUS: .byte 1 SUBSECONDS_COUNT: .byte 1 BLINK_CODES: .byte 8 BLINK_CODES_INDEX: .byte 1 ; High-order halfbyte is INSERT pointer. Low-order is SHOW pointer. TRACK_STATE: .byte 1 TRACK_COUNT: .byte 1 ; counts up when GPS_STATUS != TRACK_STATE until state change CHECKSUM_RETRY_COUNT: .byte 1 MOVING_LOCATE_COUNT: .byte 1 ; counts up to notify while moving POLL_INTERVAL: .byte 2 ; delay between phone/GPS polls in seconds FASTEST_SPEED: .byte 2 ; fastest speed above speed limit during this tracking session .equ LAST_GOOD_FIX_LEN = 27 ; FLDLEN_GPRMC_TIME + FLDLEN_GPRMC_LATITUDE + \ ; FLDLEN_GPRMC_LATDIR + FLDLEN_GPRMC_LONGITUDE + \ ; FLDLEN_GPRMC_LONGDIR LAST_GOOD_FIX: .byte LAST_GOOD_FIX_LEN ; Parse fields are used for parsing strings, and are reused for commands, ; settings, and NMEA strings .equ PARSE_FIELDS_LENGTH = 64 PARSE_FIELDS: .byte PARSE_FIELDS_LENGTH ;DEBUG_LAST_BYTE: .byte 1 ; Receive buffer is 256 bytes at 0x100 ; Receive pointer is one byte and is allowed to roll over to create circular buffer .org 0x0100 RECEIVE_BUFFER: .byte 256 ; Offsets into LAST_GOOD_FIX buffer .equ LAST_GOOD_FIX_TIME = LAST_GOOD_FIX .equ LAST_GOOD_FIX_LATITUDE = LAST_GOOD_FIX + 6 .equ LAST_GOOD_FIX_LATDIR = LAST_GOOD_FIX_LATITUDE + 9 .equ LAST_GOOD_FIX_LONGITUDE = LAST_GOOD_FIX_LATDIR + 1 .equ LAST_GOOD_FIX_LONGDIR = LAST_GOOD_FIX_LONGITUDE + 10 ; Field for parsing DEVICENAME .equ FLDADR_DEVICENAME = PARSE_FIELDS + 0 .equ FLDLEN_DEVICENAME = 16 ; Field for parsing CMGL .equ FLDADR_CMGL_UNSENT = PARSE_FIELDS + 0 .equ FLDADR_CMGL_SENT = PARSE_FIELDS + 32 .equ FLDADR_CMGL_STOP = PARSE_FIELDS + 56 .equ FLDLEN_CMGL_MAXMSGS = 8 ; actually limits it to 7 of each ; Fields for parsing SETPASSWORD .equ FLDADR_PASSWORD1 = PARSE_FIELDS + 0 .equ FLDLEN_PASSWORD1 = 16 .equ FLDADR_PASSWORD2 = PARSE_FIELDS + 16 .equ FLDLEN_PASSWORD2 = 16 .equ MIN_PASSWORD_LENGTH = 3 ; Fields for parsing GPS output .equ FLDADR_GPRMC_FIXVALID = PARSE_FIELDS + 0 .equ FLDLEN_GPRMC_FIXVALID = 1 .equ FLDADR_GPRMC_TIME = FLDADR_GPRMC_FIXVALID + FLDLEN_GPRMC_FIXVALID .equ FLDLEN_GPRMC_TIME = 6 .equ FLDADR_GPRMC_LATITUDE = FLDADR_GPRMC_TIME + FLDLEN_GPRMC_TIME .equ FLDLEN_GPRMC_LATITUDE = 9 .equ FLDADR_GPRMC_LATDIR = FLDADR_GPRMC_LATITUDE + FLDLEN_GPRMC_LATITUDE .equ FLDLEN_GPRMC_LATDIR = 1 .equ FLDADR_GPRMC_LONGITUDE = FLDADR_GPRMC_LATDIR + FLDLEN_GPRMC_LATDIR .equ FLDLEN_GPRMC_LONGITUDE = 10 .equ FLDADR_GPRMC_LONGDIR = FLDADR_GPRMC_LONGITUDE + FLDLEN_GPRMC_LONGITUDE .equ FLDLEN_GPRMC_LONGDIR = 1 .equ FLDADR_GPRMC_SPEED = FLDADR_GPRMC_LONGDIR + FLDLEN_GPRMC_LONGDIR .equ FLDLEN_GPRMC_SPEED = 6 .equ FLDADR_GPRMC_COURSE = FLDADR_GPRMC_SPEED + FLDLEN_GPRMC_SPEED .equ FLDLEN_GPRMC_COURSE = 6 .equ FLDADR_GPRMC_DATE = FLDADR_GPRMC_COURSE + FLDLEN_GPRMC_COURSE .equ FLDLEN_GPRMC_DATE = 6 .equ FLDADR_GPGGA_FIXVALID = FLDADR_GPRMC_DATE + FLDLEN_GPRMC_DATE .equ FLDLEN_GPGGA_FIXVALID = 1 .equ FLDADR_GPGGA_SATS = FLDADR_GPGGA_FIXVALID + FLDLEN_GPGGA_FIXVALID .equ FLDLEN_GPGGA_SATS = 2 .equ FLDADR_GPGGA_ALT = FLDADR_GPGGA_SATS + FLDLEN_GPGGA_SATS .equ FLDLEN_GPGGA_ALT = 5 ; Too long? .equ FLDADR_GPRMC_SPEED_NUMERIC = FLDADR_GPGGA_ALT + FLDLEN_GPGGA_ALT .equ FLDLEN_GPRMC_SPEED_NUMERIC = 2 .equ FLDADR_GPGGA_ALT_NUMERIC = FLDADR_GPRMC_SPEED_NUMERIC + FLDLEN_GPRMC_SPEED_NUMERIC .equ FLDLEN_GPGGA_ALT_NUMERIC = 2 .equ FLDADR_GPS_LINE_COUNT_FLAGS = FLDADR_GPGGA_ALT_NUMERIC + \ FLDLEN_GPGGA_ALT_NUMERIC .equ FLDLEN_GPS_LINE_COUNT_FLAGS = 1 .equ GPS_FLAG_LOW_SPEED_RETRY = 7 .equ GPS_FLAG_DISPLACED = 6 ; fields for status message .equ STATUSMSG_SPEED_LIMIT = PARSE_FIELDS + 0 .equ STATUSMSG_GPS_MOVING_THRESHOLD = PARSE_FIELDS + 2 .equ STATUSMSG_GPS_MOVING_RETRY_THRESHOLD = PARSE_FIELDS + 4 .equ STATUSMSG_TRACK_STOPPED_FIX_INTERVAL = PARSE_FIELDS + 6 .equ STATUSMSG_TRACK_STOPPED_NOTIFY_DELAY = PARSE_FIELDS + 8 .equ STATUSMSG_TRACK_BLOCKED_FIX_INTERVAL = PARSE_FIELDS + 10 .equ STATUSMSG_TRACK_BLOCKED_NOTIFY_DELAY = PARSE_FIELDS + 12 .equ STATUSMSG_TRACK_MOVING_FIX_INTERVAL = PARSE_FIELDS + 14 .equ STATUSMSG_TRACK_MOVING_NOTIFY_FREQ = PARSE_FIELDS + 16 .equ STATUSMSG_MIN_DISPLACEMENT = PARSE_FIELDS + 18 .equ STATUSMSG_POWERSAVE_PHONE_OFF_INTERVAL = PARSE_FIELDS + 20 .equ STATUSMSG_POWERSAVE_PHONE_ON_INTERVAL = PARSE_FIELDS + 22 .equ STATUSMSG_GPS_FOUR_SAT_WAIT_TIME = PARSE_FIELDS + 24 .equ STATUSMSG_GPS_FIX_WAIT_TIME = PARSE_FIELDS + 26 .equ STATUSMSG_BLOCKED_GPS_FIX_WAIT_TIME = PARSE_FIELDS + 28 .equ STATUSMSG_WDR_COUNT = PARSE_FIELDS + 30 .equ STATUSMSG_BATTERY_CHARGE = PARSE_FIELDS + 32 .equ STATUSMSG_SIGNAL_STRENGTH = PARSE_FIELDS + 34 ; Fields for CPMS .equ FLDADR_CPMS_USED = PARSE_FIELDS + 8 .equ FLDADR_CPMS_TOTAL = PARSE_FIELDS + 16 .equ FLDADR_CPMS_USED_NUMERIC = PARSE_FIELDS + 24 .equ FLDADR_CPMS_TOTAL_NUMERIC = PARSE_FIELDS + 32 .cseg ; Interrupt vectors rjmp V_RESET rjmp V_INT0 rjmp V_PCINT0 rjmp V_PCINT1 rjmp V_WDT rjmp V_TIM1_CAPT rjmp V_TIM1_COMPA rjmp V_TIM1_COMPB rjmp V_TIM1_OVF rjmp V_TIM0_COMPA rjmp V_TIM0_COMPB rjmp V_TIM0_OVF ; Watchdog reset on stray interrupts V_INT0: V_PCINT1: V_TIM1_CAPT: V_TIM1_COMPB: V_TIM1_OVF: V_TIM0_COMPA: V_TIM0_COMPB: cli rjmp V_INT0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Main program entry point V_RESET: ; Stack pointer ldi SCRATCH0,high(RAMEND) ldi SCRATCH1,low(RAMEND) out SPH,SCRATCH0 out SPL,SCRATCH1 ; Init registers clr REG_ZERO ; will stay zero clr FLAG_REGISTER out EECR,REG_ZERO ; EEPM1 and EEPM0 should be clear for atomic write sts TRACK_STATE,REG_ZERO sts COMMAND_PENDING,REG_ZERO sts COMMAND_PENDING+1,REG_ZERO sts BLINK_CODES_INDEX,REG_ZERO ; Setup for serial I/O ldi SCRATCH0,0xff-((1< ; Field number starts with 0 meaning first field. Put FF after last field. ; NMEA Checksum goes from right after $ to before * ; Non-NMEA checksum is whole line excluding newline ; NMEA delimiter is a comma and end of line is * then two checksum digits ; Command line delimiter is normally a space and end of line is a CR ; Set FLAG_PARSE_NMEA for NMEA mode, clear it for command line mode ; Timeout sets T flag and aborts. PARSE_NMEA_OR_COMMAND_LINE: clr READ_FIELD ; current field clr PAT ; use as a zero for writing into memory clr CHECKSUM ; calculated in both modes, VERIFY_CHECKSUM only in NMEA mode cbr FLAG_REGISTER,1<10, do nothing. =10, send start bit. <10, send data. breq SERIAL_SEND_START_BIT brcc SERIAL_DEC_AND_RET SERIAL_SEND_DATA_BIT: ror SERIAL_DATA ; drop low bit into carry flag, 1 from carry into high bit SERIAL_SEND_START_BIT: in SERIAL_SCRATCH,PORTA ; IN and OR do not change carry flag or SERIAL_SCRATCH,SERIAL_PORT ; sets to 1 the bit for the selected port brcs SERIAL_SEND_DATA_ONE sub SERIAL_SCRATCH,SERIAL_PORT ; If it's a 0, set bit to zero in output SERIAL_SEND_DATA_ONE: out PORTA,SERIAL_SCRATCH SERIAL_DEC_AND_RET: dec SERIAL_COUNT brne SERIAL_SEND_NOT_DONE ; serial send done out TCCR1B,REG_ZERO ; stop the serial clock SERIAL_SEND_NOT_DONE: out SREG,SERIAL_INTERRUPT_SREG reti ; ; Interrupt for the first bit (long delay after start bit) SERIAL_RECEIVE_INTERRUPT: in SERIAL_SCRATCH,PINA ; get the register and SERIAL_SCRATCH,SERIAL_PORT ; isolate active input pin neg SERIAL_SCRATCH ; this sets carry flag unless register is zero ror SERIAL_DATA ; roll the bit from carry flag into shift register dec SERIAL_COUNT brne SERIAL_RECEIVE_RETI ; last bit? ; last bit out TCCR1B,REG_ZERO ; stop the serial clock push ZH ; borrow Z pointer push ZL ldi ZH,1 ; buffer is 0x100-0x1ff mov ZL,SERIAL_POINTER st Z,SERIAL_DATA inc SERIAL_POINTER ; bump pointer after saving; pointer indicates next byte pop ZL pop ZH out PCMSK0,SERIAL_PORT ; enable Pin Change Interrupt for next character SERIAL_RECEIVE_RETI: out SREG,SERIAL_INTERRUPT_SREG reti ; Pin Change Interrupt 0 used to detect start bit on receive V_PCINT0: in SERIAL_INTERRUPT_SREG,SREG in SERIAL_SCRATCH,PINA ; get the register and SERIAL_SCRATCH,SERIAL_PORT ; isolate active input pin brne PCINT0_IGNORE ; bit should be zero if falling edge, otherwise ignore sbrs FLAG_REGISTER,FLAG_SERIAL_RECEIVE ; receive mode enabled? rjmp PCINT0_UNSOLICITED_MESSAGE ; no ; When we receive the pin change interrupt, we are at the falling edge of the start bit ; We want to sample data bits in the middle of the bit period. Therefore we need to ; set the clock for the first cycle to 1.5 * the bit period, which is the remainder ; of the start bit plus half the data bit period. This code presets the timer to ; a high value equal to 20 - ( 1/2 the baud rate count ) ; The 20 is there to compensate for the interval between receiving the pin change ; interrupt and starting the serial clock. The 16-bit subtract will underflow to a ; high value. When we start the counter, it will roll over through zero to its normal ; value, producing a longer delay. ; 20 - ( 208 / 2 ) = 65452 in 16-bit arithmetic ; Pin change interrupt is in the middle of this, only because I had a zero available. in SERIAL_DATA,OCR1AH ; high byte of baud rate in SERIAL_SCRATCH,OCR1AL ; low byte of baud rate lsr SERIAL_DATA ; divide high byte by two ror SERIAL_SCRATCH ; complete division by two ldi SERIAL_COUNT,20 ; number of cycles to compensate sub SERIAL_COUNT,SERIAL_SCRATCH ; compute num cycles - ( baud / 2 ) (low byte) clr SERIAL_SCRATCH ; does not touch carry flag out PCMSK0,SERIAL_SCRATCH ; shut down Pin Change Interrupt while receiving sbc SERIAL_SCRATCH,SERIAL_DATA ; complete subtraction for high byte out TCNT1H,SERIAL_SCRATCH ; high byte first out TCNT1L,SERIAL_COUNT ; now the low byte ldi SERIAL_COUNT,(1< 45123) ; or 0xff if you want to terminate on decimal point ; If decimal . found and DELIM set to '.' parse one fractional digit, so "45.67" becomes 456 ; SCRATCH0123 and CHAR used PARSE_DECIMAL: clt ; T flag marks last digit after '.' clr SCRATCH0 clr SCRATCH1 PSD_LOOP: ; Get digit ld CHAR,X+ tst DELIM brne PSD_NOT_SKIP_DECIMAL cpi CHAR,'.' ; skip decimal mode breq PSD_NEXTCHAR PSD_NOT_SKIP_DECIMAL: cp CHAR,DELIM brne PSD_NOT_DECIMAL set rjmp PSD_NEXTCHAR PSD_NOT_DECIMAL: subi CHAR,'0' brlo PSD_DONE cpi CHAR,10 brsh PSD_DONE ; multiply by 10 rcall MULX10_SCRATCH10 ; add digit clr SCRATCH2 add SCRATCH0,CHAR adc SCRATCH1,SCRATCH2 ; brts PSD_DONE ; out if decimal and one more digit PSD_NEXTCHAR: dec PAT brne PSD_LOOP PSD_DONE: ret ; multiply by 10 ( X*10 = X*2 + X*8 ) MULX10_SCRATCH10: lsl SCRATCH0 rol SCRATCH1 mov SCRATCH3,SCRATCH0 mov SCRATCH2,SCRATCH1 lsl SCRATCH3 rol SCRATCH2 lsl SCRATCH3 rol SCRATCH2 add SCRATCH0,SCRATCH3 adc SCRATCH1,SCRATCH2 ret ; Called to parse and execute SETNAME command ; Expected: one parameter ; Saves device name to EEPROM ; Called while serial comm is running CMD_SETNAME: rcall CLEAR_PARSE_FIELDS ldi ZH,high(SETNAME_PARSE_STRING<<1) ldi ZL,low(SETNAME_PARSE_STRING<<1) cbr FLAG_REGISTER,1<= 30000, assume it is a rollover and take 60,000 - result ldi SCRATCH1,high(30000) ldi SCRATCH0,low(30000) ; no cpci cp SCRATCH2,SCRATCH0 cpc SCRATCH3,SCRATCH1 brlo CPD_NOT_ROLLOVER mov SCRATCH0,SCRATCH2 mov SCRATCH1,SCRATCH3 ldi SCRATCH3,high(60000) ldi SCRATCH2,low(60000) sub SCRATCH2,SCRATCH0 sbc SCRATCH3,SCRATCH1 CPD_NOT_ROLLOVER: ; Get minimum displacement in SCRATCH1:SCRATCH0 ldi XH,high(EEADR_TRACK_MIN_DISPLACEMENT) ldi XL,low(EEADR_TRACK_MIN_DISPLACEMENT) rcall EEPROM_READWORD_X_TO_SCRATCH10 ; Compare with calculated displacement cp SCRATCH2,SCRATCH0 cpc SCRATCH3,SCRATCH1 ; Here carry is SET if we have NOT moved ret #ifdef ALT_IN_FEET #define NEED_UNITS_CONVERT 1 #endif #ifdef SPEED_IN_MPH #define NEED_UNITS_CONVERT 1 #endif #ifdef SPEED_IN_KPH #define NEED_UNITS_CONVERT 1 #endif #ifdef NEED_UNITS_CONVERT ; Units conversion ; Call with number in SCRATCH1:SCRATCH0 (from PARSE_DECIMAL) ; Conversion factor in XH:XL ; Result returned in ZH:ZL ; Does a 16x16 bit multiply, retaining only the high order word of the product ; This took quite a while to figure out! The trick is to move carry into MSB. UNITS_CONVERT_X4: lsl SCRATCH0 rol SCRATCH1 UNITS_CONVERT_X2: lsl SCRATCH0 rol SCRATCH1 UNITS_CONVERT: ldi SCRATCH2,16 clr ZH clr ZL UC_LOOP: lsr XH ror XL brcc UC_ZERO add ZL,SCRATCH0 adc ZH,SCRATCH1 UC_ZERO: ror ZH ; carry from previous addition becomes MSB ror ZL dec SCRATCH2 brne UC_LOOP ret #endif ; Locate and send, called from CMD_LOCATE via scheduled command ; TRACK_STATE tests are to prevent a double locate when requested in tracking mode GPS_LOCATE_AND_SEND: lds SCRATCH0,TRACK_STATE cpi SCRATCH0,TRACK_STATE_POWERSAVE brsh GLS_LOCATE_REQUIRED cpi SCRATCH0,TRACK_STATE_OFF breq GLS_LOCATE_REQUIRED rjmp GLS_ALREADY_LOCATED GLS_LOCATE_REQUIRED: rcall GPS_LOCATE GLS_ALREADY_LOCATED: ldi SCRATCH0,FIX_TYPE_LOCATE - FIX_TYPE_BASE sts SEND_STRING_OFFSET_1,SCRATCH0 ; GPS_SEND_LOCATION called by tracking function, assumes we have a locate in memory GPS_SEND_LOCATION: lds SCRATCH1,GPS_STATUS ldi SCRATCH0,PHONE_STRING_GPS_DATASTREAM_TIMEOUT - PHONE_STRING_BASE cpi SCRATCH1,GPS_STATUS_DATASTREAM_TIMEOUT breq MULTI_SEND_DO ldi SCRATCH0,PHONE_STRING_GPS_FIX_TIMEOUT - PHONE_STRING_BASE cpi SCRATCH1,GPS_STATUS_FIX_TIMEOUT breq MULTI_SEND_DO ldi SCRATCH0,PHONE_STRING_GPS_LOCATION - PHONE_STRING_BASE rjmp MULTI_SEND_DO ; Send a variety of messages depending on entry point ; Called from COMMAND_PENDING MULTI_SEND_COMMAND_EXECUTED: ldi SCRATCH0,PHONE_STRING_COMMAND_EXECUTED - PHONE_STRING_BASE rjmp MULTI_SEND_DO MULTI_SEND_PASSWORD_CHANGED: ldi SCRATCH0,PHONE_STRING_PASSWORD_CHANGED - PHONE_STRING_BASE rjmp MULTI_SEND_DO MULTI_SEND_PASSWORD_CHANGE_FAILED: ldi SCRATCH0,PHONE_STRING_PASSWORD_CHANGE_FAILED - PHONE_STRING_BASE rjmp MULTI_SEND_DO MULTI_SEND_UNKNOWN_COMMAND: ldi SCRATCH0,PHONE_STRING_UNKNOWN_COMMAND - PHONE_STRING_BASE rjmp MULTI_SEND_DO MULTI_SEND_STATUS: rcall CLEAR_PARSE_FIELDS ldi YH,high(PARSE_FIELDS) ldi YL,low(PARSE_FIELDS) ldi ZH,high(SETSPEED_DECODE_STRING<<1) ldi ZL,low(SETSPEED_DECODE_STRING<<1) clr CHAR ; to determine which string we are on SSM_COPY_LOOP: lpm SCRATCH2,Z+ tst SCRATCH2 ; done with string? breq SSM_DONE_WITH_STRING mov XH,SCRATCH2 andi XH,15 lpm XL,Z+ ; now have address of value in X rcall EEPROM_READBYTE_X_TO_SCRATCH0 ; X incremented here ; clr SCRATCH1 sbrs SCRATCH2,DECODE_SETTING_ONE_BYTE_X4 rjmp SSM_NOT_X4 lsl SCRATCH0 rol SCRATCH1 lsl SCRATCH0 rol SCRATCH1 ; X4 SSM_NOT_X4: sbrs SCRATCH2,DECODE_SETTING_TWO_BYTE st Y+,SCRATCH1 ; write high byte if one byte value st Y+,SCRATCH0 ; sbrs SCRATCH2,DECODE_SETTING_TWO_BYTE rjmp SSM_COPY_LOOP rcall EEPROM_READBYTE_X_TO_SCRATCH0 ; X incremented here st Y+,SCRATCH0 ; write second byte of two-byte value rjmp SSM_COPY_LOOP SSM_DONE_WITH_STRING: inc CHAR cpi CHAR,2 ; which string are we on? breq SSM_NOW_DO_POWER brsh SSM_DONE_WITH_ALL_STRINGS ; now do track ldi ZH,high(SETTRACK_DECODE_STRING<<1) ldi ZL,low(SETTRACK_DECODE_STRING<<1) rjmp SSM_COPY_LOOP SSM_NOW_DO_POWER: ldi ZH,high(SETPOWER_DECODE_STRING<<1) ldi ZL,low(SETPOWER_DECODE_STRING<<1) rjmp SSM_COPY_LOOP SSM_DONE_WITH_ALL_STRINGS: st Y+,REG_ZERO ; high byte of WDR count lds SCRATCH0,WDR_COUNT st Y+,SCRATCH0 ; low byte of WDR count ; ldi ZH,high(PHONE_GET_BATTERY_AND_SIGNAL_STRING<<1) ldi ZL,low(PHONE_GET_BATTERY_AND_SIGNAL_STRING<<1) rcall DO_PHONE_OPERATION ldi SCRATCH0,PHONE_STRING_STATUS_MESSAGE - PHONE_STRING_BASE ; rjmp MULTI_SEND_DO MULTI_SEND_DO: #ifdef DEBUG_MESSAGE_COUNT_ENABLE lds SCRATCH1,DEBUG_MESSAGE_COUNT+1 inc SCRATCH1 sts DEBUG_MESSAGE_COUNT+1,SCRATCH1 brne MSD_NO_INCHIGH lds SCRATCH1,DEBUG_MESSAGE_COUNT inc SCRATCH1 sts DEBUG_MESSAGE_COUNT,SCRATCH1 MSD_NO_INCHIGH: #endif ; sts SEND_STRING_OFFSET_0,SCRATCH0 ; ldi XH,high(EEADR_PHONE_NUMBER) ldi XL,low(EEADR_PHONE_NUMBER) rcall EEPROM_READBYTE_X_TO_SCRATCH0 tst SCRATCH0 brne MSD_PHONENUMBER ; no phone number defined ldi SCRATCH0,0x14 ; BLINK CODE no phone number defined rcall SEND_BLINK_CODE rjmp MSD_END MSD_PHONENUMBER: ; store ldi SCRATCH0,3 ; how many times to retry MSD_STORE_RETRY: sts CHECKSUM_RETRY_COUNT,SCRATCH0 ldi ZH,high(PHONE_SEND_STORE_MESSAGE_STRING<<1) ldi ZL,low(PHONE_SEND_STORE_MESSAGE_STRING<<1) rcall DO_PHONE_OPERATION ; send checksum is in VERIFY_CHECKSUM brts MSD_FAILED_128 ; verify checksum and length ldi ZH,high(PHONE_READ_MESSAGE_STRING<<1) ldi ZL,low(PHONE_READ_MESSAGE_STRING<<1) ; send checksum moved to CHECKSUM rcall DO_PHONE_OPERATION ; read checksum in VERIFY_CHECKSUM brts MSD_FAILED_64 ; cp CHECKSUM,VERIFY_CHECKSUM brne MSD_STORE_CHECKSUM_BAD lds SCRATCH0,SEND_CHECKSUM_BYTES lds SCRATCH1,READ_CHECKSUM_BYTES cp SCRATCH0,SCRATCH1 breq MSD_STORE_DONE ; MSD_STORE_CHECKSUM_BAD: ldi ZH,high(PHONE_DELETE_MESSAGE_STRING<<1) ldi ZL,low(PHONE_DELETE_MESSAGE_STRING<<1) rcall DO_PHONE_OPERATION ; delete corrupt message ; lds SCRATCH0,CHECKSUM_RETRY_COUNT dec SCRATCH0 breq MSD_FAILED_32 ; checksum bad too may times rjmp MSD_STORE_RETRY MSD_STORE_DONE: ; send cbr FLAG_REGISTER,1< ",0, \ low(PHONE_GOT_BATTERY_CHARGE),high(PHONE_GOT_BATTERY_CHARGE),"+CBC: 0,",0, \ low(PHONE_GOT_BATTERY_CHARGE),high(PHONE_GOT_BATTERY_CHARGE),"+CBC: 1,",0, \ low(PHONE_GOT_SIGNAL_STRENGTH),high(PHONE_GOT_SIGNAL_STRENGTH),"+CSQ: ",0, \ low(PHONE_INIT_ERROR),high(PHONE_INIT_ERROR),0 ; These functions should not mess up the Y pointer. COMMAND_MATCH_PATTERN: .db low(CMD_SETADDRESS),high(CMD_SETADDRESS),"SETADDRESS",0, \ low(CMD_SETNAME),high(CMD_SETNAME),"SETNAME",0, \ low(CMD_SETPASSWORD),high(CMD_SETPASSWORD),"SETPASSWORD",0, \ low(CMD_SETTRACK),high(CMD_SETTRACK),"SETTRACK",0, \ low(CMD_SETPOWER),high(CMD_SETPOWER),"SETPOWER",0, \ low(CMD_SETSPEED),high(CMD_SETSPEED),"SETSPEED",0, \ low(CMD_STATUS),high(CMD_STATUS),"STATUS",0, \ low(CMD_LOCATE),high(CMD_LOCATE),"LOCATE",0, \ low(CMD_SALOCATE),high(CMD_SALOCATE),"SALOCATE",0, \ low(CMD_TRACKOFF),high(CMD_TRACKOFF),"TRACKOFF",0, \ low(CMD_TRACKON),high(CMD_TRACKON),"TRACKON",0, \ low(CMD_POWERSAVE),high(CMD_POWERSAVE),"POWERSAVE",0, \ low(CMD_POWERON),high(CMD_POWERON),"POWERON",0, \ low(CMD_REBOOT),high(CMD_REBOOT),"REBOOT",0, \ low(CMD_REINIT),high(CMD_REINIT),"REINIT",0, \ low(CMD_CAUSEWDR),high(CMD_CAUSEWDR),"CAUSEWDR",0, \ low(CMD_UNRECOGNIZED),high(CMD_UNRECOGNIZED),0 CMGL_MATCH_PATTERN: .db low(PPMN_CSMS_MATCH),high(PPMN_CSMS_MATCH),",",0, \ low(PPMN_CSMS_MATCH),high(PPMN_CSMS_MATCH),34,0, \ low(PPMN_CSMS_SENT),high(PPMN_CSMS_SENT),"STO SENT",0, \ low(PPMN_CSMS_UNSENT),high(PPMN_CSMS_UNSENT),"STO UNSENT",0, \ low(PPMN_CSMS_RECEIVED),high(PPMN_CSMS_RECEIVED),"REC ",0, \ low(PPMN_OUT),high(PPMN_OUT),0 ; SETNAME_PARSE_STRING: .db 1,(FLDLEN_DEVICENAME<<2)+high(FLDADR_DEVICENAME),low(FLDADR_DEVICENAME), \ 0xff SETPASSWORD_PARSE_STRING: .db 1,(FLDLEN_PASSWORD1<<2)+high(FLDADR_PASSWORD1),low(FLDADR_PASSWORD1), \ 2,(FLDLEN_PASSWORD2<<2)+high(FLDADR_PASSWORD2),low(FLDADR_PASSWORD2), \ 0xff #ifdef DEBUG_MESSAGE_COUNT_ENABLE PHONE_SEND_STORE_MESSAGE_STRING: .db 0xf0,INIT_RECEIVE_TIMEOUT,"ATE0",13,0, \ 0xf0,DEFAULT_RECEIVE_TIMEOUT, \ "AT+CMGW=",34,0xfe, \ high(EEADR_PHONE_NUMBER)+(EELEN_PHONE_NUMBER<<2), \ low(EEADR_PHONE_NUMBER),34,13,0, \ 0xf7,0xff,high(EEADR_EMAIL_ADDR)+(EELEN_EMAIL_ADDR<<2), \ low(EEADR_EMAIL_ADDR), \ 0xff,high(EEADR_DEVICENAME)+(EELEN_DEVICENAME<<2), \ low(EEADR_DEVICENAME), \ "MC=",0xf8,high(DEBUG_MESSAGE_COUNT),low(DEBUG_MESSAGE_COUNT)," ", \ 0xfa,high(PHONE_STRING_BASE<<1),low(PHONE_STRING_BASE<<1),0xf7,26,13,0,0 #else PHONE_SEND_STORE_MESSAGE_STRING: .db 0xf0,INIT_RECEIVE_TIMEOUT,"ATE0",13,0, \ 0xf0,DEFAULT_RECEIVE_TIMEOUT, \ "AT+CMGW=",34,0xfe, \ high(EEADR_PHONE_NUMBER)+(EELEN_PHONE_NUMBER<<2), \ low(EEADR_PHONE_NUMBER),34,13,0, \ 0xf7,0xff,high(EEADR_EMAIL_ADDR)+(EELEN_EMAIL_ADDR<<2), \ low(EEADR_EMAIL_ADDR), \ 0xff,high(EEADR_DEVICENAME)+(EELEN_DEVICENAME<<2), \ low(EEADR_DEVICENAME), \ 0xfa,high(PHONE_STRING_BASE<<1),low(PHONE_STRING_BASE<<1),0xf7,26,13,0,0 #endif PHONE_SEND_TRANSMIT_MESSAGE_STRING: .db 0xf0,INIT_RECEIVE_TIMEOUT,"ATE0",13,0,0xf0,SMS_SEND_RECEIVE_TIMEOUT, \ "AT+CMSS=",0xfd,(4<<2)+high(CURRENT_SMS),low(CURRENT_SMS),13,0,0 ; Phone string offsets from one base can be up to 512 bytes due to word addressing PHONE_STRING_BASE: PHONE_STRING_COMMAND_EXECUTED: .db "COMMAND EXECUTED",0 PHONE_STRING_PASSWORD_CHANGED: .db "PASSWORD CHANGED",0 PHONE_STRING_PASSWORD_CHANGE_FAILED: .db "PASSWORD CHANGE FAILED",0 PHONE_STRING_UNKNOWN_COMMAND: .db "UNKNOWN COMMAND",0 PHONE_STRING_GPS_DATASTREAM_TIMEOUT: .db "NO DATASTREAM FROM GPS DEVICE", \ 0xfc,high(LAST_GOOD_FIX_STRING<<1),low(LAST_GOOD_FIX_STRING<<1),0 PHONE_STRING_GPS_FIX_TIMEOUT: .db "GPS TIMED OUT WAITING FOR FIX", \ 0xfc,high(LAST_GOOD_FIX_STRING<<1),low(LAST_GOOD_FIX_STRING<<1),0 ; There are three different speed units, and two different altitude units options. ; The assembler will add a null byte after a string if there is an odd number of bytes. ; As far as I know there is no way to incorporare an externally defined string into ; a .db structure. That means I have to define all six cases here, instead of having ; ifdefs inside the string. :-( #ifdef SPEED_IN_MPH #ifdef ALT_IN_FEET PHONE_STRING_GPS_LOCATION: .db 0xfb,high(FIX_TYPE_BASE<<1),low(FIX_TYPE_BASE<<1), \ " POS ",0xfd,(2<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(FLDADR_GPRMC_LATITUDE+2),low(FLDADR_GPRMC_LATITUDE+2), \ " ",0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ " ",0xfd,(3<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ " ",0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(FLDADR_GPRMC_LONGITUDE+3),low(FLDADR_GPRMC_LONGITUDE+3), \ " ",0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ " ALT ",0xf8,high(FLDADR_GPGGA_ALT_NUMERIC),low(FLDADR_GPGGA_ALT_NUMERIC)," FT", \ " SPEED ",0xf9,high(FLDADR_GPRMC_SPEED_NUMERIC),low(FLDADR_GPRMC_SPEED_NUMERIC)," MPH", \ " COURSE ",0xfd,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ " AT ",0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+4),low(FLDADR_GPRMC_DATE+4),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+2),low(FLDADR_GPRMC_DATE+2),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE)," ", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+2),low(FLDADR_GPRMC_TIME+2),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+4),low(FLDADR_GPRMC_TIME+4)," UTC", \ " SATS ",0xfd,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS),0 #endif #endif #ifdef SPEED_IN_KPH #ifdef ALT_IN_FEET PHONE_STRING_GPS_LOCATION: .db 0xfb,high(FIX_TYPE_BASE<<1),low(FIX_TYPE_BASE<<1), \ " POS ",0xfd,(2<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(FLDADR_GPRMC_LATITUDE+2),low(FLDADR_GPRMC_LATITUDE+2), \ " ",0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ " ",0xfd,(3<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ " ",0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(FLDADR_GPRMC_LONGITUDE+3),low(FLDADR_GPRMC_LONGITUDE+3), \ " ",0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ " ALT ",0xf8,high(FLDADR_GPGGA_ALT_NUMERIC),low(FLDADR_GPGGA_ALT_NUMERIC)," FT", \ " SPEED ",0xf9,high(FLDADR_GPRMC_SPEED_NUMERIC),low(FLDADR_GPRMC_SPEED_NUMERIC)," KPH", \ " COURSE ",0xfd,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ " AT ",0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+4),low(FLDADR_GPRMC_DATE+4),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+2),low(FLDADR_GPRMC_DATE+2),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE)," ", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+2),low(FLDADR_GPRMC_TIME+2),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+4),low(FLDADR_GPRMC_TIME+4)," UTC", \ " SATS ",0xfd,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS),0 #endif #endif #ifdef SPEED_IN_KNOTS #ifdef ALT_IN_FEET PHONE_STRING_GPS_LOCATION: .db 0xfb,high(FIX_TYPE_BASE<<1),low(FIX_TYPE_BASE<<1), \ " POS ",0xfd,(2<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(FLDADR_GPRMC_LATITUDE+2),low(FLDADR_GPRMC_LATITUDE+2), \ " ",0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ " ",0xfd,(3<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ " ",0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(FLDADR_GPRMC_LONGITUDE+3),low(FLDADR_GPRMC_LONGITUDE+3), \ " ",0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ " ALT ",0xf8,high(FLDADR_GPGGA_ALT_NUMERIC),low(FLDADR_GPGGA_ALT_NUMERIC)," FT", \ " SPEED ",0xf9,high(FLDADR_GPRMC_SPEED_NUMERIC),low(FLDADR_GPRMC_SPEED_NUMERIC)," KNOTS", \ " COURSE ",0xfd,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ " AT ",0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+4),low(FLDADR_GPRMC_DATE+4),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+2),low(FLDADR_GPRMC_DATE+2),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE)," ", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+2),low(FLDADR_GPRMC_TIME+2),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+4),low(FLDADR_GPRMC_TIME+4)," UTC", \ " SATS ",0xfd,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS),0 #endif #endif #ifdef SPEED_IN_MPH #ifdef ALT_IN_METERS PHONE_STRING_GPS_LOCATION: .db 0xfb,high(FIX_TYPE_BASE<<1),low(FIX_TYPE_BASE<<1), \ " POS ",0xfd,(2<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(FLDADR_GPRMC_LATITUDE+2),low(FLDADR_GPRMC_LATITUDE+2), \ " ",0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ " ",0xfd,(3<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ " ",0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(FLDADR_GPRMC_LONGITUDE+3),low(FLDADR_GPRMC_LONGITUDE+3), \ " ",0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ " ALT ",0xf8,high(FLDADR_GPGGA_ALT_NUMERIC),low(FLDADR_GPGGA_ALT_NUMERIC)," M", \ " SPEED ",0xf9,high(FLDADR_GPRMC_SPEED_NUMERIC),low(FLDADR_GPRMC_SPEED_NUMERIC)," MPH", \ " COURSE ",0xfd,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ " AT ",0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+4),low(FLDADR_GPRMC_DATE+4),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+2),low(FLDADR_GPRMC_DATE+2),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE)," ", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+2),low(FLDADR_GPRMC_TIME+2),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+4),low(FLDADR_GPRMC_TIME+4)," UTC", \ " SATS ",0xfd,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS),0 #endif #endif #ifdef SPEED_IN_KPH #ifdef ALT_IN_METERS PHONE_STRING_GPS_LOCATION: .db 0xfb,high(FIX_TYPE_BASE<<1),low(FIX_TYPE_BASE<<1), \ " POS ",0xfd,(2<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(FLDADR_GPRMC_LATITUDE+2),low(FLDADR_GPRMC_LATITUDE+2), \ " ",0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ " ",0xfd,(3<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ " ",0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(FLDADR_GPRMC_LONGITUDE+3),low(FLDADR_GPRMC_LONGITUDE+3), \ " ",0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ " ALT ",0xf8,high(FLDADR_GPGGA_ALT_NUMERIC),low(FLDADR_GPGGA_ALT_NUMERIC)," M", \ " SPEED ",0xf9,high(FLDADR_GPRMC_SPEED_NUMERIC),low(FLDADR_GPRMC_SPEED_NUMERIC)," KPH", \ " COURSE ",0xfd,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ " AT ",0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+4),low(FLDADR_GPRMC_DATE+4),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+2),low(FLDADR_GPRMC_DATE+2),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE)," ", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+2),low(FLDADR_GPRMC_TIME+2),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+4),low(FLDADR_GPRMC_TIME+4)," UTC", \ " SATS ",0xfd,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS),0 #endif #endif #ifdef SPEED_IN_KNOTS #ifdef ALT_IN_METERS PHONE_STRING_GPS_LOCATION: .db 0xfb,high(FIX_TYPE_BASE<<1),low(FIX_TYPE_BASE<<1), \ " POS ",0xfd,(2<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(FLDADR_GPRMC_LATITUDE+2),low(FLDADR_GPRMC_LATITUDE+2), \ " ",0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ " ",0xfd,(3<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ " ",0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(FLDADR_GPRMC_LONGITUDE+3),low(FLDADR_GPRMC_LONGITUDE+3), \ " ",0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ " ALT ",0xf8,high(FLDADR_GPGGA_ALT_NUMERIC),low(FLDADR_GPGGA_ALT_NUMERIC)," M", \ " SPEED ",0xf9,high(FLDADR_GPRMC_SPEED_NUMERIC),low(FLDADR_GPRMC_SPEED_NUMERIC)," KNOTS", \ " COURSE ",0xfd,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ " AT ",0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+4),low(FLDADR_GPRMC_DATE+4),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE+2),low(FLDADR_GPRMC_DATE+2),"/", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE)," ", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+2),low(FLDADR_GPRMC_TIME+2),":", \ 0xfd,(2<<2)+high(FLDADR_GPRMC_TIME+4),low(FLDADR_GPRMC_TIME+4)," UTC", \ " SATS ",0xfd,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS),0 #endif #endif PHONE_STRING_STATUS_MESSAGE: .db "SPDLMT=", \ 0xf8,high(STATUSMSG_SPEED_LIMIT), \ low(STATUSMSG_SPEED_LIMIT), " STOPD=", \ 0xf8,high(STATUSMSG_TRACK_STOPPED_FIX_INTERVAL), \ low(STATUSMSG_TRACK_STOPPED_FIX_INTERVAL),"/", \ 0xf8,high(STATUSMSG_TRACK_STOPPED_NOTIFY_DELAY), \ low(STATUSMSG_TRACK_STOPPED_NOTIFY_DELAY)," BLKD=", \ 0xf8,high(STATUSMSG_TRACK_BLOCKED_FIX_INTERVAL), \ low(STATUSMSG_TRACK_BLOCKED_FIX_INTERVAL),"/", \ 0xf8,high(STATUSMSG_TRACK_BLOCKED_NOTIFY_DELAY), \ low(STATUSMSG_TRACK_BLOCKED_NOTIFY_DELAY)," MOVNG=", \ 0xf8,high(STATUSMSG_TRACK_MOVING_FIX_INTERVAL), \ low(STATUSMSG_TRACK_MOVING_FIX_INTERVAL),"/", \ 0xf8,high(STATUSMSG_TRACK_MOVING_NOTIFY_FREQ), \ low(STATUSMSG_TRACK_MOVING_NOTIFY_FREQ)," PSV=", \ 0xf8,high(STATUSMSG_POWERSAVE_PHONE_OFF_INTERVAL), \ low(STATUSMSG_POWERSAVE_PHONE_OFF_INTERVAL),"/", \ 0xf8,high(STATUSMSG_POWERSAVE_PHONE_ON_INTERVAL), \ low(STATUSMSG_POWERSAVE_PHONE_ON_INTERVAL)," 3D/FIX/BLKD=", \ 0xf8,high(STATUSMSG_GPS_FOUR_SAT_WAIT_TIME), \ low(STATUSMSG_GPS_FOUR_SAT_WAIT_TIME),"/", \ 0xf8,high(STATUSMSG_GPS_FIX_WAIT_TIME), \ low(STATUSMSG_GPS_FIX_WAIT_TIME),"/", \ 0xf8,high(STATUSMSG_BLOCKED_GPS_FIX_WAIT_TIME), \ low(STATUSMSG_BLOCKED_GPS_FIX_WAIT_TIME)," MD/MS=", \ 0xf8,high(STATUSMSG_MIN_DISPLACEMENT), \ low(STATUSMSG_MIN_DISPLACEMENT),"/", \ 0xf8,high(STATUSMSG_GPS_MOVING_THRESHOLD), \ low(STATUSMSG_GPS_MOVING_THRESHOLD),"/", \ 0xf8,high(STATUSMSG_GPS_MOVING_RETRY_THRESHOLD), \ low(STATUSMSG_GPS_MOVING_RETRY_THRESHOLD)," BAT=", \ 0xf8,high(STATUSMSG_BATTERY_CHARGE), \ low(STATUSMSG_BATTERY_CHARGE)," SIG=", \ 0xf8,high(STATUSMSG_SIGNAL_STRENGTH), \ low(STATUSMSG_SIGNAL_STRENGTH)," WDR=", \ 0xf8,high(STATUSMSG_WDR_COUNT), \ low(STATUSMSG_WDR_COUNT)," V=0.16 Open GPS Tracker",0 ;PHONE_STRING_WATCHDOG_RESET: ; .db "WATCHDOG RESET",0 FIX_TYPE_BASE: FIX_TYPE_STOPPED: .db "STOPPED",0 FIX_TYPE_STARTED: .db "STARTED",0 FIX_TYPE_MOVING: .db "MOVING",0 FIX_TYPE_MOVED: .db "MOVED",0 FIX_TYPE_SPEEDING: .db "SPEEDING",0 FIX_TYPE_LOCATE: .db "LOCATE",0 LAST_GOOD_FIX_STRING: .db ", LAST GOOD FIX ", \ 0xfd,(2<<2)+high(LAST_GOOD_FIX_LATITUDE),low(LAST_GOOD_FIX_LATITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LATITUDE-2)<<2)+high(LAST_GOOD_FIX_LATITUDE+2),low(LAST_GOOD_FIX_LATITUDE+2)," ", \ 0xfd,(FLDLEN_GPRMC_LATDIR<<2)+high(LAST_GOOD_FIX_LATDIR),low(LAST_GOOD_FIX_LATDIR)," ", \ 0xfd,(3<<2)+high(LAST_GOOD_FIX_LONGITUDE),low(LAST_GOOD_FIX_LONGITUDE)," ", \ 0xfd,((FLDLEN_GPRMC_LONGITUDE-3)<<2)+high(LAST_GOOD_FIX_LONGITUDE+3),low(LAST_GOOD_FIX_LONGITUDE+3)," ", \ 0xfd,(FLDLEN_GPRMC_LONGDIR<<2)+high(LAST_GOOD_FIX_LONGDIR),low(LAST_GOOD_FIX_LONGDIR)," AT ", \ 0xfd,(2<<2)+high(LAST_GOOD_FIX_TIME),low(LAST_GOOD_FIX_TIME),":", \ 0xfd,(2<<2)+high(LAST_GOOD_FIX_TIME+2),low(LAST_GOOD_FIX_TIME+2),":", \ 0xfd,(2<<2)+high(LAST_GOOD_FIX_TIME+4),low(LAST_GOOD_FIX_TIME+4)," UTC",0 GPS_MATCH_PATTERN: .db low(GPS_PARSE_GPRMC),high(GPS_PARSE_GPRMC),"$GPRMC",0, \ low(GPS_PARSE_GPGGA),high(GPS_PARSE_GPGGA),"$GPGGA",0, \ low(GPS_PARSE_OTHER),high(GPS_PARSE_OTHER),0 GPRMC_PARSE_STRING: .db 1,(FLDLEN_GPRMC_TIME<<2)+high(FLDADR_GPRMC_TIME),low(FLDADR_GPRMC_TIME), \ 2,(FLDLEN_GPRMC_FIXVALID<<2)+high(FLDADR_GPRMC_FIXVALID),low(FLDADR_GPRMC_FIXVALID), \ 3,(FLDLEN_GPRMC_LATITUDE<<2)+high(FLDADR_GPRMC_LATITUDE),low(FLDADR_GPRMC_LATITUDE), \ 4,(FLDLEN_GPRMC_LATDIR<<2)+high(FLDADR_GPRMC_LATDIR),low(FLDADR_GPRMC_LATDIR), \ 5,(FLDLEN_GPRMC_LONGITUDE<<2)+high(FLDADR_GPRMC_LONGITUDE),low(FLDADR_GPRMC_LONGITUDE), \ 6,(FLDLEN_GPRMC_LONGDIR<<2)+high(FLDADR_GPRMC_LONGDIR),low(FLDADR_GPRMC_LONGDIR), \ 7,(FLDLEN_GPRMC_SPEED<<2)+high(FLDADR_GPRMC_SPEED),low(FLDADR_GPRMC_SPEED), \ 8,(FLDLEN_GPRMC_COURSE<<2)+high(FLDADR_GPRMC_COURSE),low(FLDADR_GPRMC_COURSE), \ 9,(FLDLEN_GPRMC_DATE<<2)+high(FLDADR_GPRMC_DATE),low(FLDADR_GPRMC_DATE), \ 0xff GPGGA_PARSE_STRING: .db 6,(FLDLEN_GPGGA_FIXVALID<<2)+high(FLDADR_GPGGA_FIXVALID),low(FLDADR_GPGGA_FIXVALID), \ 7,(FLDLEN_GPGGA_SATS<<2)+high(FLDADR_GPGGA_SATS),low(FLDADR_GPGGA_SATS), \ 9,(FLDLEN_GPGGA_ALT<<2)+high(FLDADR_GPGGA_ALT),low(FLDADR_GPGGA_ALT), \ 0xff ; Used for SETSPEED, SETTRACK, and SETPOWER SETTINGS_PARSE_STRING: .db 1,(8<<2)+high(PARSE_FIELDS),low(PARSE_FIELDS), \ 2,(8<<2)+high(PARSE_FIELDS+8),low(PARSE_FIELDS+8), \ 3,(8<<2)+high(PARSE_FIELDS+16),low(PARSE_FIELDS+16), \ 4,(8<<2)+high(PARSE_FIELDS+24),low(PARSE_FIELDS+24), \ 5,(8<<2)+high(PARSE_FIELDS+32),low(PARSE_FIELDS+32), \ 6,(8<<2)+high(PARSE_FIELDS+40),low(PARSE_FIELDS+40), \ 7,(8<<2)+high(PARSE_FIELDS+48),low(PARSE_FIELDS+48), \ 8,(8<<2)+high(PARSE_FIELDS+56),low(PARSE_FIELDS+56), \ 0xff SETSPEED_DECODE_STRING: .db high(EEADR_SPEED_LIMIT) + (1<