Microchip RN4020 Bluetooth LE

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
The code continues to grow. Since last update, I've added the ability for the main app to request data transfers through the bluetooth connection. In addition, I spent about 6 hours today adding code to support characteristics with the notification attribute set. This is so Android can be notified when I change local characteristic values.

For those interested, all the heavy lifting, once the bluetooth is set up, occurs in state 15. This state handles pretty much everything for most of the working life of the bluetooth.

BTWPROP is also a really cool routine, if one wants to try to make sense of it.

I still need to add data written to my device from the Android central. That's tomorrow.

Code:
;**************************************
;** BLTOG -- Toggle Bluetooth on/off **
;**************************************

bltog    bbs    fbton,bltoff        ;turn bluetooth off?

;Turn bluetooth off

blton    bsf    fbton

    return

;Turn bluetooth on

bltoff    bcf    fbton

    return
 

;***************************************************************
;** POLLBT -- Bluetooth polling -- called each main loop pass **
;***************************************************************

pollbt    retbc    run            ;don't process bluetooth until system initialized and running

    BANKSEL    btstate            ;select bluetooth working bank         

    clrf    btsresp,1        ;clear standard response flags
    clrf    btsresp+1,1

    rcall    btprcv            ;check to see if a new bluetooth response has been received.
    bz    pbtnrsp            ;no response

;if a non-standard response is received, and allowed, parse it

    bbc    fwrnsr,pbtnnwr        ;nsr allowed?
    btfsc    fsrnsr            ;nsr received?
    rcall    btpnsr            ;go parse non-standard response

;a response has been received from bluetooth

pbtnnwr    movlw    b'01111111'        ;ignore NWR flag
    andwf    btswait+1,w,1
    iorwf    btswait,w,1        ;are we waiting for a particular response?
    bz    pbstate            ;no, just process machine

;decide whether or not to throw an error

    bbs    fwrerr,pbteok        ;is ERR an acceptable response?
    bbs    fsrerr,pbterr        ;No, flag ERR as error.

pbteok    bbs    FWRNWR,pbstate        ;improper responses don't throw errors if NWR bit set

    movf    btswait,w,1        ;are we waiting for a response?
    andwf    btsresp,w,1        ;has a proper response been received?
    bnz    pbstate            ;yes, process state machine
 
    movf    btswait+1,w,1        ;again, next byte
    andwf    btsresp+1,w,1
    bnz    pbstate

    bra    pbtimp            ;flag an improper response

;no response received from bluetooth this pass -- process timed functions

pbtnrsp    movf    btstime,f,1        ;is timer running?
    bz    pbtntim            ;no, not a timed function

;timed function -- either response time-out or timed state

    btfsc    tc16ms            ;process 16ms timer
    decfsz    btstime,f,1        ;decrement state timer
    bra    btx____            ;no further processing if timer not expired

    movlw    b'01111111'        ;ignore NWR flag
    andwf    btswait+1,w,1
    iorwf    btswait,w,1        ;are we waiting for a particular response?
    bz    pbstate            ;no, just process timed state after delay

;otherwise, flag a timeout error

    bra    pbtrto            ;show timeout error

;not a timed function

pbtntim    bbs    fwrnwr,pbstate        ;don't wait for response if bit is set

    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    bz    pbstate            ;no, just process the untimed state

    bra    btx____            ;otherwise, just wait until response
 
;Handle improper response

pbtimp    bra    btx____            ;ignore improper responses (for now)

;Handle ERR response

pbterr    movlb    0
    movlw    bterr            ;get ERR error code
    call    seterr            ;and show error
    BANKSEL    btstate            ;select bluetooth working bank         

    bra    btx____            ;ignore erroneous responses

;Handle timeout during reception of response

pbtrto    movlb    0
    movlw    btrto            ;get time out error code
    call    seterr            ;and set error limp along
    BANKSEL    btstate            ;select bluetooth working bank         

    bra    btx____            ;ignore erroneous responses

;process Bluetooth state machine

pbstate    clrf    btswait,1        ;clear waiting flags for next pass
    clrf    btswait+1,1

    clrf    btstime,1        ;and state timer
 
    movfw    btstate
    call    cjump 

;idle with bluetooth off

    bra    btsid0            ;state 0:  if fbton, wake BT and wait for CMD message 

;setup blue tooth

    bra    btssu0            ;state 1:  get/set feature string
    bra    btssu1            ;state 2:  get/set friendly name
    bra    btssu2            ;state 3:  get/set model name
    bra    btssu3            ;state 4:  get/set manufacturers name
    bra    btssu4            ;state 5:  get/set bitmap services
    bra    btssu5            ;state 6:  send LS to confirm private services
    bra    btssu6            ;state 7:  confirm private service UUID is defined
    bra    btssu7            ;state 8:  confirm all private characteristic UUIDs
    bra    btssu8            ;state 9:  scan for end of LS list
    bra    btssu9            ;state 10: start reset/reboot process -- clear private services
    bra    btssuA            ;state 11: add private services
    bra    btssuB            ;state 12: iteratively add private characteristics then reboot
    bra    btssuC            ;state 13: wait for reboot to complete and redo LS to load handles
    bra    btsadv            ;state 14:  start advertising

;active with bluetooth on

    bra    btsac0            ;state 15:  idle for connects/disconnects and monitor monitor for shutdown

;shudown bluetooth

    bra    btssd0            ;state 16:  switch to dormant mode
    bra    btssd1            ;state 17:  clear wakesw and shutdown EUSART

;***********************
;** Bluetooth states  **
;***********************

;-- State 0:  if fbton, wake BT and wait for CMD message

btsid0    bbc    fbton,btx____            ;do nothing if bluetooth not activated
    bbs    shtdwn,btx____            ;and don't start up if in system shutdown

    call    uarton                ;turn eusart on

    bsf    wakesw                ;wake bluetooth
    bsf    wakehw 

    bcf    fbtrbt                ;clear reboot flag for setup

    bsf    fwrcmd                ;wait for "CMD"
    bsf    fwrzcmd                ; or \0CMD if restart
    bra    btx__LN                ;long timeout in case of restart

;-- State 1:  begin setup, get, compare, and set feature string if necessary

btssu0    bbs    fsrnsr,btssu01            ;gf data received (as non-standard response?)

btssu00    txpstr    sbtgfs                ;get feature settings string
    bsf    fwrnsr                ;  wait for non-standard response
    bra    btx__T_                ;  wait 200ms and don't advance

btssu01    rxpcstr    sbtsfs                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfs                ;send feature settings string
    bra    btx_ATN                ;and advance
     
;-- State 2:  get, compare, and set friendly name if necessary

btssu1    bbs    fsrnsr,btssu10            ;friendly name received (as non-standard response?)

    txpstr    sbtgfn                ;get friendly name string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu10    rxpcstr    sbtsfn                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfn                ;send new friendly name
    bra    btx_ATN                ;and advance
     
;-- State 3:  get, compare, and set model if necessary

btssu2    bbs    fsrnsr,btssu20            ;model received (as non-standard response?)

    txpstr    sbtgmod                ;get model string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu20    rxpcstr    sbtsmod                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmod                ;send new model
    bra    btx_ATN                ;and advance
     
;-- State 4:  get, compare, and set msnufacturer's name if necessary

btssu3    bbs    fsrnsr,btssu30            ;manufacturer's name received (as non-standard response?)

    txpstr    sbtgmn                ;get manufacturer's name string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu30    rxpcstr    sbtsmn                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmn                ;send new manufacturer's name
    bra    btx_ATN                ;and advance
     
;-- State 5:  get, compare, and set bitmap services if necessary

btssu4    bbs    fsrnsr,btssu40            ;bitmap services received (as non-standard response?)

    txpstr    sbtgbms                ;get bitmap services string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu40    rxpcstr    sbtsbms                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsbms                ;send new bitmap services
    bra    btx_ATN                ;and advance
     
;-- State 6:  send LS command to confirm private characteristics set up properly

btssu5    clrf    btsptr,1            ;preclear string pointer
    clrf    btsptr+1,1
    clrf    bthctr,1            ;clear handle counter for later
    clrf    btcctr,1            ;and characteristic counter

    bcf    fbtcapp                ;preclear pcp capture for scan

    txpstr    sbtls                ;send ls
    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TN                ;and advance

;-- State 7:  check if the private service is defined

btssu6    bbs    fsrend,btrbt            ;if END received, list is empty, reset and reboot

    rxpcstr    sbtsps                ;expected value received?
    bz    btssu60                ;yes, set up for private characteristics

    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TR                ;await response for END/NSR and return here

btssu60    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TN                ;and advance
 
;-- State 8: iterate to check UUID for each private characteristic -- capture handle if exists
;            and process notification characteristics

btssu7    bbs    fsrend,btssu70            ;if END received, ensure we got all characteristics

    movfwb    btcctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bz    btssu71                ;yes. Should have been END. Scan to END, reset and reboot.

    movlf    tblptrl,low sbtspc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtspc1 

    movfwb    btsptr                ;add in string pointer to point to proper string
    addwf    tblptrl,f
    movfwb    btsptr+1
    addwfc    tblptrh,f

    bsf    fbtrpc                ;indicate that we are scanning a private characteristic
    call    _rxpcstr            ;compare string & capture handle
    bnz    btssu71                ;no, string not matched.  Scan to end, reboot and reset

    incf    bthctr,f,1            ;bump counter for next private characteristic handle

    bbs    fbtcapp,btssu72            ;need to reload string for notification?

    movlw    44                ;no, update string pointer
    addwf    btsptr,f,1
    clrw
    addwfc    btsptr+1,f,1

    incf    btcctr,f,1            ;and increment characteristic pointer

btssu72    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TR                ;come back to this state with NSR/END response

;  END received -- ensure we captured [cnumcar] characteristics 

btssu70    movfwb    btcctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bnz    btrbt                ;no. Should have been END.  Reset and reboot.

    nextst    cbtsrbt                ;all good, but reboot if necessary
    bra    btx____

;  No match.  Scan to END, reboot and reset

btssu71    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;wait for END if nothing in the list
    bra    btx__TN                ;and advance

;-- State 9: scan for END, reboot and reset

btssu8    bbs    fsrend,btrbt            ;reset and reboot if END received

    bsf    fwrend                ;wait for END if nothing in the list
    bra    btx__TR                ;come back to this state with NSR/END response
 
;-- State 10: start reset/reboot process if necessary -- clear all private services     

btssu9    bbs    fbtrbt,btssu90            ;reboot necessary?

    nextst    cbtsadv            ;no, start advertising
    bra    btx____

btrbt    nextst    cbtsrbt            ;jump in here. Set to this state.

btssu90    bcf    fbtrbt                ;clear reboot flag for next pass
    txpstr    sbtcps                ;clear any existing private services/characteristics
    bra    btx_ATN                ;and advance

;-- State 11: add private services

btssuA    clrf    bthctr,1            ;preclear handle counter to index private characteristics

    txpstr    sbtsps                ;add private servcie
    bra    btx_ATN                ;and advance

;-- State 12: iteratively add private characteristics, then, reboot

btssuB    movfwb    bthctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bz    btssuB0                ;do actual reboot

    movlf    tblptrl,low sbtspc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtspc1 

    movlw    44                ;43 bytes per private service string
                        ;including stupid assembler padding 0
    mulwf    bthctr,1            ;prod = index into list of strings

    movfw    prodl                ;set tblptr->correct characteristic string
    addwf    tblptrl,f
    movfw    prodh
    addwfc    tblptrh,f
 
    incf    bthctr,f,1            ;index to next characteristic

    call    _txpstr                ;transmit string
    bra    btx_AT_                ;AOK and return here

;all characteristics set.  Reboot
 
btssuB0    txpstr    sbtres                ;send reset
    bsf    fwrrbt                ;wait for 'Reboot'
    bra    btx__TN

;-- State 13: wait up to 3,200 ms for reboot sequence -- then go do LS all over again to load handles

btssuC    bsf    fwrzcmd                ;wait for '\0CMD'
    nextst    cbtsls                ;set up LS as next state
    bra    btx__L_                ;long timeout for reset

;-- State 14: start advertisement

btsadv    bsf    fbtwcp                ;ensure we wait for connection parameters
    txpstr    sbtadv                ;send start advertisement
    bra    btxEATN                ;  AOK Expected (Error if already advertising)

;-- State 15: active as connected/disconnecedt -- monitor fbton in case bluetooth is to shut down
;             and process all characteristic writes,reads,and requests from central device

btsac0    bbs    FSRCON,btsac00            ;connect received?
    bbs    FSRCEND,btsac02            ;disconnect received? 
    bbs    FSRAOK,btsac05         

;don't process hardware signals or data writes until expected AOK or CONNP received

    bbs    fbtwcp,btsac01            ;expecting connection parameters?
    bbs    fbtww,btsac01            ;expecting AOK from value write?

;process shutdown signals if necessary

    bbc    fbton,btsac03            ;bluetooth needs to turn off?
    bbs    shtdwn,btsac03            ;system shutting down? Then turn off bluetooth.

;is data ready to write?
 
    rcall    btwprop                ;write properties if necessary

;no other action, try again next main loop

    bra    btsac01                ;quit to next loop

;AOK received -- clear wait for AOK data write flag

btsac05    bcf    fbtww                ;clear flag
    bra    btsac01                ;and quit to next loop

;connect received

btsac00    bsf    fbtconn                ;indicate connection
    rcall    btcbonconn            ;tell app that connection made

;quit and come back

btsac01    bsf    FWRAOK                ;allow AOK for properties written
    bsf    FWRCON                ;allow connection response
    bsf    FWRCEND                ;allow connection end response
    bsf    FWRNSR                ;allow non-standard for notifications
    bsf    FWRNWR                ;don't wait for a response

    bra    btx____                ;and come right back next pass

;disconnect received -- start advertising again for seamless reconnect

btsac02    bcf    fbtconn                ;indicate connection lost
    rcall    btcboffconn            ;tell app that connection is broken

    bsf    fbtwcp                ;wait for connection parameters
    txpstr    sbtadv                ;send start advertisement
    bra    btx_AT_                ;wait for AOK

;shut down gracefully -- stop advertising or kill a connection

btsac03    bbs    fbtconn,btsac04            ;connected?

;stop advertising

    txpstr    sbtkadv                ;no, send stop advertise command
    bra    btxEATN                ;and advance (allow error, but shouldn't happen)

btsac04    bcf    fbtconn                ;killing connection, so clear it

    txpstr    sbtkill                ;send kill command
    bsf    FWRCEND                ;wait for Connection End
    bra    btxE_TN                ;wait for connection end, but allow for error JIC

;-- State 16:  put bluetooth into dormant mode

btssd0    bcf    wakehw                ;pull line low
    txpstr    sbtdorm                ;send dormant command
    bra    btx__TN                ;result will be 0x00, which we cannot parse, so just time it

;-- State 17: shutdown bluetooth and EUSART

btssd1    call    uartoff                ;turn eusart off
    bcf    wakesw                ;turn off bluetooth uart transmitter
    nextst    cbtsid                ;set up idle0 as next state
    bra    btx____                ;and immediate idle

;***** END OF STATES *********

;**** Various Exits of SM to set up next state, timeouts, and responses ****
;     E=ERR A=AOK T=short timeout L=long timeout N=advance to next state R=Allow non-standard response

btxE_TN    movlfb    btstime,cbtsto            ;STO/ERR/NEXT
btxE__N    bsf    FWRERR                ;ERR/NEXT
    bra    btx___N

btxEATN    bsf    FWRERR                ;ERR/AOK/STO/NEXT
btx_ATN    bsf    FWRAOK                ;AOK/STO/NEXT
btx__TN    movlfb    btstime,cbtsto            ;STO/NEXT
btx___N    incf    btstate,f,1            ;NEXT
    bra    btx____

btx__TR    bsf    FWRNSR                ;STO/NSR
    bra    btx__T_

btxEAT_ bsf    FWRERR                ;ERR/AOK/STO
btx_AT_ bsf    FWRAOK                ;AOK/STO
btx__T_    movlfb    btstime,cbtsto
    bra    btx____

btx__LN    incf    btstate,f,1            ;LTO/NEXT     
btx__L_    movlfb    btstime,cbtslo            ;LTO
btx____    movlb    0                ;nothing
    return

;**** END OF SM EXITS ****


;*****************************************
;** BTPRCV -- Process Bluetooth receiver**
;**   return z if no response           **
;*****************************************

btprcv    movlf    fsr1h,high btrcvbuff    ;point to receiver buffer
    movff    btrhead,fsr1l        ;  and index to next character

btplp    rcall    rxbyte            ;receiver byte available?
    retz                ; z if none

    movwf    indf1            ;save character at current position
    movlw    '\r'            ;ignore return characters
    cpfseq    indf1
    movf    postinc1,w        ;get back in w, and update pointer
    movff    fsr1l,btrhead        ;save new head position

    xorlw    '\n'            ;new character a line feed?
    bnz    btplp            ;no, get some more characters, if available 

    movlfb    btrhead,low btrcvbuff    ;reset btrhead for next message

;parse for common messages
;if common response not matched, indicate (likely) parameters returned from RN4020

    bsf    btsresp,0,1        ;set 1st bit in standard response flags

    movdf    tblptr,btsmes        ;tblptr -> possible standard responses (\n separated, \r terminated)

btplp0    lfsr    0,btrcvbuff        ;fsr0 -> point to head of response buffer
 
btplp1    tblrd    *+            ;get next character from list of responses
 
    movlw    '\r'            ;\r is response list terminating character
    xorwf    tablat,w        ;  end of response list?
    bz    btpnmf            ;yes, no response matched

    movf    tablat,w        ;get character

    cpfseq    postinc0        ;test against charcter in response buffer
    bra    btpnom            ;!= means no match, skip to next response

    movlw    '\n'            ;is it a new line character?
    xorwf    tablat,w     
    bz    btpmat            ;yes, we have found a match

    bra    btplp1            ;keep testing characters till match

;no match of current response, skip ahead till /n or /r

btpnom    clrc                ;roll to next response
    rlcf    btsresp,f,1
    rlcf    btsresp+1,f,1

btplp2    tblrd    *+            ;get next character from list of responses

    movlw    '\r'            ;get terminating character
    xorwf    tablat,w        ; end of response list?
    bz    btpnmf            ;yes, no response matched

    movlw    '\n'            ;is it a new line character?
    xorwf    tablat,w     
    bnz    btplp2            ;loop till \0 or \n

    bra    btplp0            ;and test against next response

;match found, response bit set

btpmat

;match not found, non-standard response bit set

btpnmf    clrz                ;nz indicates a new reponse has been received
    return

;********************************************
;** BTPNSR -- Parse Non-Standard Responses **
;********************************************

btpnsr    clrf    bitcnt            ;use bitcnt as index to received response

    movdf    tblptr,btnsmes        ;tblptr -> possible non-standard responses (\n separated, \0 terminated)

btpnlp0    lfsr    0,btrcvbuff        ;fsr0 -> point to head of response buffer
 
btpnlp1    tblrd    *+            ;get next character from list of responses
 
    movf    tablat,w        ;get character from list
    bz    btpnnf            ;z=end of list, not found

    movlw    '\n'            ;== \n means we have a match!
    xorwf    tablat,w
    bz    btpnm            ;go process for arguments

    movf    tablat,w        ;get character from list
    cpfseq    postinc0        ;compare with received
    bra    btpnnm            ;no match, skip to next response

    bra    btpnlp1            ;continue till match/no match 

;no match of current response, skip ahead till /0 or /n

btpnnm    incf    bitcnt,f        ;update response counter

btpnlp2    tblrd    *+            ;get next character from list of responses

    movf    tablat,w        ;get character
    bz    btpnnf            ;z=end of list -- no match
 
    xorlw    '\n'            ;is it a new line character?
    bnz    btpnlp2            ;loop till \0 or \n

    bra    btpnlp0            ;and test against next non-standard response

;no match found - what do we do?

btpnnf    return                ;for now, just ignore the response

;match found -- bit count has response number

btpnm    movfw    bitcnt            ;get response number
    call    cjump            ;and process it

    bra    btnscp            ;'ConnParam:'
    bra    btnsnfy            ;'Notify,'
    bra    btnsind            ;'Indicate,'
    bra    btnswv            ;'WV,'
    bra    btnswc            ;'WC,'
    bra    btnsrv            ;'RV,'

;ConnParam <hex16>,<hex16>,<hex16> (connection interval, slave latency, supervison timeout)

btnscp    lfsr    1,conint+1        ;point to holders
    rcall    hex2bin            ;capture interval
    rcall    hex2bin            ;capture slave latency
    rcall    hex2bin            ;capture supervision timeout
    bcf    fbtwcp            ;indicate connection parameters have been received
    return

;other non-standard responses -- for later -- just indicate unhandled for now

;notification received
btnsnfy                    ;'Notify,'

;indication received
btnsind                    ;'Indicate,'

;write value to local server characteristic
btnswv                    ;'WV,'

;write configuration to local server characteristic
btnswc                    ;'WC,'

;real time read (bit must be set in supported features (SR)
btnsrv                    ;'RV,'

    bra    btpnnf            ;indicate unhandled non-standard response

;**************************************************************
;** HEX2BIN -- Convert arbitrary length hex to binary        **
;**   store result at fsr1 -> RAM (high byte)                **
;**   ignore spaces, comma or \n terminated                  **
;**   target RAM must be large enough to hold expected value **
;**************************************************************
 
hex2bin

;process high byte first

    movf    postinc0,w        ;get next character
    sublw    '\n'            ;is it a newline?
    retz                ;yes, we are done
    addlw    22            ;is it a space?
    bz    hex2bin            ;yes, ignore it
    addlw    12            ;is it a comma?
    retz                ;yes, we are done
    sublw    -14            ;'A'-'F' = 7-12, '0'-'9' = -10 - -1
    skpc                ;correct for 0-9
    addlw    7            ;'0'-'9' = -3 - 6
    addlw    3            ;final result
    swapf    wreg,f            ;swap for high byte
    movwf    indf1            ;and save

;then low byte (no need to check for terminators on odd characters)

h2b1    movlw    58
    subwf    postinc0,w        ;'A'-'F' = 7 - 12, '0'-'9' = -10 - -1
    skpc                ;correct for 0-9
    addlw    7            ;'0'-'9' = -3 - 6
    addlw    3            ;final result
    iorwf    postdec1,f        ;merge in
    bra    hex2bin            ;do for all characters

;********************************************************************
;** RXPCSTR -- Compare received string with program memory         **
;**   program memory string terminated with \r                     **
;**   tblptr -> string                                             **
;**   ignore leading characters in PM until first comma            **
;**   ignore leading spaces in RAM                                 **
;**   z if match, nz if no match                                   **
;**   if fbtrpc=1, pc handle is captured and pc properties checked **
;**   if fbtcaph is set, handle for char #(bthctr) is captured     **
;**   on exit, if fbtcapp=1, line must be rescanned for property   **
;********************************************************************

_rxpcstr

    btfsc    fbtrpc            ;are we scanning a private characteristic?
    bsf    fbtcaph            ;yes, we must capture the handle

    lfsr    0,btrcvbuff        ;point to bluetooth receive buffer

    movlw    ','            ;skip to character after first comma
rxpclp0    tblrd    *+
    cpfseq    tablat
    bra    rxpclp0
 
rxpclp1    movlw    ' '            ;ignore leading spaces in RAM
    xorwf    postinc0,w
    bz    rxpclp1

    movf    postdec0,w        ;move pointer back to first non-space character

rxpclp2    tblrd    *+            ;get character from program memory
    movlw    '\r'            ;terminates with /r
    xorwf    tablat,w        ;compare
    bz    rxpcbx            ;return with z to indicate match

    bbc    fbtrpc,rxpcb0        ;scanning a private characteristic?

    bbc    fbtcaph,rxpcb0        ;capture handle flag set?
    movlw    ','            ;handle starts at first comma
    xorwf    indf0,w            ;compare
    bnz    rxpcb0            ;scan till first comma

;first comma found, parse handle for this characteristic

    rcall    rxpch            ;go capture handle

;adjust for notifiers - match comma

    movf    indf0,w            ;current character should be comma
    movfw    tablat            ;get pm character
    xorwf    postinc0,w        ;compare with received character
    bnz    rxpcbx            ;no match, reload

;check for match of high nibble of characteristic properties

    tblrd    *+            ;indext to next program character
    movfw    tablat            ;get pm character
    xorwf    indf0,w            ;compare with current ram character
    bz    rxpcb1            ;match.  check second character

;if current RAM byte is non-zero, we are scanning properties

    movlw    '0'
    xorwf    indf0,w            ;compare z=value, nz=char
    skpnz                ;if we are scanning characteristic property, proceed as normal
    bsf    fbtcapp            ;otherwise, we must recheck line for property

;check for match of low nibble of characteristic properties

rxpcb1    movf    postinc0,w        ;skip to next character

    tblrd    *+            ;get next program character
    movfw    tablat            ;get rom character
    xorwf    indf0,w            ;compare with current ram character
    bz    rxpcb2            ;continue normally if match (first line, fbtcapp is set)

;second line of notified characteristic -- ensure ftbcapp and char '0'

    bbc    fbtcapp,rxpcbx        ;fbtcapp should be set here, if not, we've got a mismatch

    bcf    fbtcapp            ;clear fbtcapp for next characteristic

    movlw    '0'            ;ram byte should be zero
    xorwf    indf0,w            ;compare z=value, nz=char
    bra    rxpcbx            ;z=string matched (ignore last parameter), nz=mismatched.

;first line of characteristic -- scan normally
 
rxpcb2    movf    postinc0,w        ;to next character
    bra    rxpclp2            ;continue scanning normally

;just normal scan here

rxpcb0    movf    indf0,w
    movfw    tablat            ;get pm character
    xorwf    postinc0,w        ;compare with received character
    bz    rxpclp2            ;match, keep going

rxpcbx    bcf    fbtrpc            ;clear private characteristic indication

    return                ;return with no match/no match

;capture handle at current ram position +1

rxpch    bcf    fbtcaph            ;clear capture flag for any remaining commas
 
    lfsr    1,hpchar1        ;fsr1->start of characteristic handle table
    rlncf    bthctr,w,1        ; *4 (size of handle)
    rlncf    wreg,w

    addwf    fsr1l,f            ;fsr1->proper handle

    movff    preinc0,postinc1    ;copy 4 bytes
    movff    preinc0,postinc1
    movff    preinc0,postinc1
    movff    preinc0,postinc1
     
    movf    postinc0,f        ;index to tailing comma

    return                ;and go finish scan

;******************************************************
;** BTWPROP -- Write property value if indicated     **
;**     btwrite has one or more properties           **
;**        bit 0 for battery service                 **
;**        bit 1 through cnumcar for private service **
;**     format out: "SHW,hhhh,v..."                  **
;**     callback to get value                        **
;******************************************************

btwprop    retbc    fbtconn                ;no writes if not connected

    movfwb    btwrite                ;properties to write?
    iorwf    btwrite+1,w,1            ; two sets
    retz                    ;no, get out

;yes, find next data item to send

    movlf    temp1,1                ;start at bit0 
    clrf    temp2

    clrf    bthctr,1            ;will hold property number

;find the next property who's value requires writing

btwplp0    movfwb    btwrite                ;test a bit
    andwf    temp1,w                ;got a hit?
    bnz    btwp0                ;yes, go send it

    movfwb    btwrite+1            ;test a bit
    andwf    temp2,w                ;got a hit?
    bnz    btwp0                ;yes, go send it

    clrc                    ;shift bit mask
    rlcf    temp1,f
    rlcf    temp2,f

    incf    bthctr,f,1            ;increment property number
    bra    btwplp0                ;and check next

;bthctr has property number to write

btwp0    comf    temp1,w                ;invertmask
    andwf    btwrite,f,1            ;and clear bit for next pass

    comf    temp2,w                ;invertmask
    andwf    btwrite+1,f,1            ;and clear bit for next pass

    bsf    fbtww                ;ensure AOK returned after following command

    decf    bthctr,f,1            ;get bthctr-1
    bc    btwpps                ; c=private service

;bthctr was 0 -- send battery service

    txpstr    sbtbsh                ;send battery service handle
    call    btcbwbl                ;go write actual value in callback
    bra    ptwpx                ;exit adding trailing return

;enumerated private services (up to 10)
 
btwpps    txpstr    sbtshw                ;send "SHW," to begin property write

    lfsr    1,hpchar1            ;point first characteristic handle

    rlncf    bthctr,w            ;double count
    rlncf    wreg,w                ;  4 bytes per entry

    addwf    fsr1l,f                ; and index

    movfw    postinc1            ;send handle for indicated service
    call    txbyte
    movfw    postinc1            ;send handle for indicated service
    call    txbyte
    movfw    postinc1            ;send handle for indicated service
    call    txbyte
    movfw    postinc1            ;send handle for indicated service
    call    txbyte

    movlw    ','
    call    txbyte                ;send a comma to separate from value

    switch    bthctr,1,ptwpx            ;make proper callback and exit with trailing \r

    goto    btcbps1                ;each must send a value to TX
    goto    btcbps2
    goto    btcbps3
    goto    btcbps4
    goto    btcbps5
    goto    btcbps6
    goto    btcbps7
    goto    btcbps8
    goto    btcbps9
    goto    btcbpsA 
             
;add trailing return character to complete command

ptwpx    movlw    '\r'
    goto    txbyte            ;send and return

;************************************************
;** BTSI8 -- send INT8 in btsdata to bluetooth **
;************************************************

btsi8    lfsr 1,btsdata            ;point to btsdata

;********************************************
;** BTSPI8 -- send fsr1->INT8 to bluetooth **
;********************************************
 
btspi8    movlw    1            ;one byte
    bra    btpssd            ;write the data

;**************************************************
;** BTSI16 -- send INT16 in btsdata to bluetooth **
;**************************************************

btsi16    lfsr    1,btsdata        ;point to btsdata

;**********************************************
;** BTSPI16 -- send fsr1->INT16 to bluetooth **
;**********************************************
 
btspi16    movlw    2            ;two bytes to send
    bra    btpssd            ;and write data

;************************************************
;** BTSFLS -- send float on stack to bluetooth **
;************************************************

btsfls    popfl    btsdata            ;get float into btsdata
 
;*******************************************************
;** BTS32 -- send 4 bytes of binary data to bluetooth **
;**   btsdata has data to send                        **
;*******************************************************

bts32    movlw    4            ;four bytes

;***********************************************************
;** BTSSD -- send wreg bytes of binary data to bluetooth  **
;**   btsdata has data to send                            **
;***********************************************************

btssd    lfsr    1,btsdata        ;point to btsdata

;***********************************************************
;** BTPSSD -- send wreg bytes of binary data to bluetooth **
;**   fsr1->data                                          **
;***********************************************************

btpssd    movwf    bitcnt            ;save number of bytes
    addwf    fsr1l,f            ;and point to high byte +1
    movf    postdec1,f        ;point to high byte

btssdl1    swapf    indf1,w            ;get high byte
    rcall    tbc2hex            ;transmit ASCII
    movf    postdec1,w        ;get low byet
    rcall    tbc2hex            ;transmit ASCII
 
    djnz    bitcnt,btssdl1        ;do for all bytes

    return                ;and return
 
;********************************************************
;** BTTHEX -- Convert low nibble of WREG to ASCII HEX  **
;**           and transmit to bluetooth
;********************************************************

tbc2hex    andlw    b'1111'            ;mask lower nibble
    addlw    -10            ;0-9=-10 to (-1) (nc), A-F=0 to 5 (c)
    skpnc                ;A-F?
    addlw    7            ;A-F = 7 to 12
    addlw    58            ;0-9=48-57,A-F=65-70 ('0'-'9','A'-'F')
    goto    txbyte            ;and send it

;custom app files

    include "btcb.asm"        ;include custom callbacks to main program
    include    "btstrings.asm"        ;include command/response strings
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
RN4020 just got really hard. Looks like I've got a few impossible things to do this weekend. More to follow...
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
While I am making progress, I am really pissed off at this chip right now. What the **** is a 0x2E coming in tacked at the end of a response for? Yet another undocumented "feature"...
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
If anyone wants to see what kind of crap I am dealing with, download the "User's Guide" and look at page 78, Table 2-19.

I am currently working on parsing the WC,<hex16>,<hex16> response. As you can see, there is no period '.' at the end of the response.

But look down to RV,<hex16>. There is a period there! So, I must now assume they must have neglected to add the '.' at the end of WC in the document. But why does it even have a '.'? There is not a single other command or response that I have encountered that has had a '.' at the end. It serves no purpose whatsoever! I have spent hours running this down.

Edit: This is, figuratively and literally, a Chinese puzzle. No offense to my Chinese friends.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
Ok. All fixed and working.

Damn, this was a tough slog. I don't have time for a lot of details now, but suffice to say that a majority of the initial problem was due to my (mis)understanding of the data rate capabilities of BLE, in addition to nRF's inability to keep up with BLE's maximum data rate.

In any case, I required a way to govern the rate at which data was sent to the RN4020. I spent a great deal of time adding notification capability to the code, only to realize that this did not produce any feedback. It just told Android that data was available, but Android doesn't respond that it got it.

So, I had to add more code to handle indications, which are notifications with an acknowledgement.

This worked, but I discovered that, while I can send 6 packets of data per one bluetooth interval without notifications or indications, I can only send 3 packets with notifications, and only 1 packet for every 2 intervals with indication. So, the indications, while solving my governing problem, slowed my max data rate by 12! times. By the way, the time between intervals is decided by Android (or IOS), and it can vary, and I have absolutely no control over it.

So, I had to reconfigure my data structure so that I could send all I needed to within the time allotted.

I did about 10 impossible things in 2 days. I need an award.

So much fun. I need a drink.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
Here's the code as it stands now. Still some minor stuff to add tomorrow, like capturing data sent to me from Android, but, compared to everything else, that's easy. I think...

Code:
;**************************************
;** BTTOG -- Toggle Bluetooth on/off **
;**************************************

bttog    btg    fbton            ;toggle on/off
    return
   
;*************************************************************
;** BTSEND -- send characteristic #wreg value via Bluetooth **
;**           actual value is captured via callback         **
;**           WREG=0 for Battery Service                    **
;**           WREG=1..n for Private Characteristics         **
;*************************************************************

btsend    clrf    temp0            ;set for char #0
    clrf    temp1

    setc                ;roll first 1 in
btselp0    rlcf    temp0,f
    rlcf    temp1,f

    movf    wreg,w            ;wreg=0
    bz    btse0            ;yes, bit in proper position

    decf    wreg,w
    clrc                ;clear carry for next shift
    bra    btselp0

;temp1:0 has bit position of characteristic to send

btse0    movff    btwbuff,wreg        ;merge into write buffer (don't know current working bank)
    iorwf    temp0,w
    movff    wreg,btwbuff

    movff    btwbuff+1,wreg        ;merge into write buffer (don't know current working bank)
    iorwf    temp1,w
    movff    wreg,btwbuff+1

    return

;***************************************************************
;** POLLBT -- Bluetooth polling -- called each main loop pass **
;***************************************************************

pollbt    retbc    run            ;don't process bluetooth until system initialized and running

    BANKSEL    btstate            ;select bluetooth working bank           

    clrf    btsresp,1        ;clear standard response flags
    clrf    btsresp+1,1

    rcall    btprcv            ;check to see if a new bluetooth response has been received.
    bz    pbtnrsp            ;no response

;if a non-standard response is received, and allowed, parse it

    bbc    fwrnsr,pbtnnwr        ;nsr allowed?
    btfsc    fsrnsr            ;nsr received?
    rcall    btpnsr            ;go parse non-standard response

;a response has been received from bluetooth

pbtnnwr    movlw    b'01111111'        ;ignore NWR flag
    andwf    btswait+1,w,1
    iorwf    btswait,w,1        ;are we waiting for a particular response?
    bz    pbstate            ;no, just process machine

;decide whether or not to throw an error

    bbs    fwrerr,pbteok        ;is ERR an acceptable response?
    bbs    fsrerr,pbterr        ;No, flag ERR as error.

pbteok    bbs    FWRNWR,pbstate        ;improper responses don't throw errors if NWR bit set

    movf    btswait,w,1        ;are we waiting for a response?
    andwf    btsresp,w,1        ;has a proper response been received?
    bnz    pbstate            ;yes, process state machine
   
    movf    btswait+1,w,1        ;again, next byte
    andwf    btsresp+1,w,1
    bnz    pbstate

    bra    pbtimp            ;flag an improper response

;no response received from bluetooth this pass -- process timed functions

pbtnrsp    movf    btstime,f,1        ;is timer running?
    bz    pbtntim            ;no, not a timed function

;timed function -- either response time-out or timed state

    btfsc    tc16ms            ;process 16ms timer
    decfsz    btstime,f,1        ;decrement state timer
    bra    btx____            ;no further processing if timer not expired

    movlw    b'01111111'        ;ignore NWR flag
    andwf    btswait+1,w,1
    iorwf    btswait,w,1        ;are we waiting for a particular response?
    bz    pbstate            ;no, just process timed state after delay

;otherwise, flag a timeout error

    bra    pbtrto            ;show timeout error

;not a timed function

pbtntim    bbs    fwrnwr,pbstate        ;don't wait for response if bit is set

    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    bz    pbstate            ;no, just process the untimed state

    bra    btx____            ;otherwise, just wait until response
   
;Handle improper response

pbtimp    bra    btx____            ;ignore improper responses (for now)

;Handle ERR response

pbterr    movlb    0
    movlw    bterr            ;get ERR error code
    call    seterr            ;and show error
    BANKSEL    btstate            ;select bluetooth working bank           

    bra    btx____            ;ignore erroneous responses

;Handle timeout during reception of response

pbtrto    movlb    0
    movlw    btrto            ;get time out error code
    call    seterr            ;and set error limp along
    BANKSEL    btstate            ;select bluetooth working bank           

    bra    btx____            ;ignore erroneous responses

;process Bluetooth state machine

pbstate    clrf    btswait,1        ;clear waiting flags for next pass
    clrf    btswait+1,1

    clrf    btstime,1        ;and state timer
   
    movfw    btstate
    call    cjump   

;idle with bluetooth off

    bra    btsid0            ;state 0:  if fbton, wake BT and wait for CMD message   

;setup blue tooth

    bra    btssu0            ;state 1:  get/set feature string
    bra    btssu1            ;state 2:  get/set friendly name
    bra    btssu2            ;state 3:  get/set model name
    bra    btssu3            ;state 4:  get/set manufacturers name
    bra    btssu35            ;state 5:  get/set connection parameters
    bra    btssu4            ;state 6:  get/set bitmap services
    bra    btssu5            ;state 7:  send LS to confirm private services
    bra    btssu6            ;state 8:  confirm private service UUID is defined
    bra    btssu7            ;state 9:  confirm all private characteristic UUIDs
    bra    btssu8            ;state 10:  scan for end of LS list
    bra    btssu9            ;state 11: start reset/reboot process -- clear private services
    bra    btssuA            ;state 12: add private services
    bra    btssuB            ;state 13: iteratively add private characteristics then reboot
    bra    btssuC            ;state 14: wait for reboot to complete and redo LS to load handles

;preconnection loop

    bra    btsadv0            ;state 15: start advertising
    bra    btsadv1            ;state 16: wait for connected
    bra    btsadv2            ;state 17: wait for connection parameters
       
;active with bluetooth on

    bra    btsac0            ;state 18:  idle while connected and monitor monitor for shutdown

;shudown bluetooth

    bra    btssd0            ;state 19:  stop advertising or disconnect existing connection
    bra    btssd1            ;state 20:  switch to dormant mode
    bra    btssd2            ;state 21:  clear wakesw and shutdown EUSART

;***********************
;** Bluetooth states  **
;***********************

;-- State 0:  if fbton, wake BT and wait for CMD message

btsid0    bbc    fbton,btx____            ;do nothing if bluetooth not activated
    bbs    shtdwn,btx____            ;and don't start up if in system shutdown

    call    uarton                ;turn eusart on

    bsf    wakesw                ;wake bluetooth
    bsf    wakehw   

    bcf    fbtrbt                ;clear reboot flag for setup

    bsf    fwrcmd                ;wait for "CMD"
    bsf    fwrzcmd                ; or \0CMD if restart
    bra    btx__LN                ;long timeout in case of restart

;-- State 1:  begin setup, get, compare, and set feature string if necessary

btssu0    bbs    fsrnsr,btssu01            ;gf data received (as non-standard response?)

btssu00    txpstr    sbtgfs                ;get feature settings string
    bsf    fwrnsr                ;  wait for non-standard response
    bra    btx__T_                ;  wait 200ms and don't advance

btssu01    rxpcstr    sbtsfs                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfs                ;send feature settings string
    bra    btx_ATN                ;and advance
       
;-- State 2:  get, compare, and set friendly name if necessary

btssu1    bbs    fsrnsr,btssu10            ;friendly name received (as non-standard response?)

    txpstr    sbtgfn                ;get friendly name string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu10    rxpcstr    sbtsfn                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfn                ;send new friendly name
    bra    btx_ATN                ;and advance
       
;-- State 3:  get, compare, and set model if necessary

btssu2    bbs    fsrnsr,btssu20            ;model received (as non-standard response?)

    txpstr    sbtgmod                ;get model string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu20    rxpcstr    sbtsmod                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmod                ;send new model
    bra    btx_ATN                ;and advance
       
;-- State 4:  get, compare, and set msnufacturer's name if necessary

btssu3    bbs    fsrnsr,btssu30            ;manufacturer's name received (as non-standard response?)

    txpstr    sbtgmn                ;get manufacturer's name string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu30    rxpcstr    sbtsmn                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmn                ;send new manufacturer's name
    bra    btx_ATN                ;and advance
       
;-- State 5:  get, compare, and set connection parameters

btssu35    bbs    fsrnsr,btssu36            ;connection parameters received (as non-standard response?)

    txpstr    sbtgcp                ;get connection parameters string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu36    rxpcstr    sbtscp                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtscp                ;send new manufacturer's name
    bra    btx_ATN                ;and advance
       

;-- State 6:  get, compare, and set bitmap services if necessary

btssu4    nop

    bbs    fsrnsr,btssu40            ;bitmap services received (as non-standard response?)

    txpstr    sbtgbms                ;get bitmap services string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu40    rxpcstr    sbtsbms                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsbms                ;send new bitmap services
    bra    btx_ATN                ;and advance
       
;-- State 7:  send LS command to confirm private characteristics set up properly

btssu5    clrf    btsptr,1            ;preclear string pointer
    clrf    btsptr+1,1
    clrf    bthctr,1            ;clear handle counter for later
    clrf    btcctr,1            ;and characteristic counter

    bcf    fbtcapp                ;preclear pcp capture for scan

    txpstr    sbtls                ;send ls
    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TN                ;and advance

;-- State 8:  check if the private service is defined

btssu6    bbs    fsrend,btrbt            ;if END received, list is empty, reset and reboot

    rxpcstr    sbtsps                ;expected value received?
    bz    btssu60                ;yes, set up for private characteristics

    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TR                ;await response for END/NSR and return here

btssu60    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TN                ;and advance
   
;-- State 9: iterate to check UUID for each private characteristic -- capture handle if exists
;            and process notification characteristics

btssu7    bbs    fsrend,btssu70            ;if END received, ensure we got all characteristics

    movfwb    btcctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bz    btssu71                ;yes. Should have been END. Scan to END, reset and reboot.

    movlf    tblptrl,low sbtspc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtspc1   

    movfwb    btsptr                ;add in string pointer to point to proper string
    addwf    tblptrl,f
    movfwb    btsptr+1
    addwfc    tblptrh,f

    bsf    fbtrpc                ;indicate that we are scanning a private characteristic
    call    _rxpcstr            ;compare string & capture handle
    bnz    btssu71                ;no, string not matched.  Scan to end, reboot and reset

    incf    bthctr,f,1            ;bump counter for next private characteristic handle

    bbs    fbtcapp,btssu72            ;need to reload string for notification?

    movlw    44                ;no, update string pointer
    addwf    btsptr,f,1
    clrw
    addwfc    btsptr+1,f,1

    incf    btcctr,f,1            ;and increment characteristic pointer

btssu72    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TR                ;come back to this state with NSR/END response

;  END received -- ensure we captured [cnumcar] characteristics   

btssu70    movfwb    btcctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bnz    btrbt                ;no. Should have been END.  Reset and reboot.

    nextst    cbtsrbt                ;all good, but reboot if necessary
    bra    btx____

;  No match.  Scan to END, reboot and reset  

btssu71    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;wait for END if nothing in the list
    bra    btx__TN                ;and advance

;-- State 10: scan for END, reboot and reset

btssu8    bbs    fsrend,btrbt            ;reset and reboot if END received

    bsf    fwrend                ;wait for END if nothing in the list
    bra    btx__TR                ;come back to this state with NSR/END response
   
;-- State 11: start reset/reboot process if necessary -- clear all private services       

btssu9    bbs    fbtrbt,btssu90            ;reboot necessary?

    nextst    cbtsadv            ;no, start advertising
    bra    btx____

btrbt    nextst    cbtsrbt            ;jump in here. Set to this state.

btssu90    bcf    fbtrbt                ;clear reboot flag for next pass
    txpstr    sbtcps                ;clear any existing private services/characteristics
    bra    btx_ATN                ;and advance

;-- State 12: add private services

btssuA    clrf    bthctr,1            ;preclear handle counter to index private characteristics

    txpstr    sbtsps                ;add private servcie
    bra    btx_ATN                ;and advance

;-- State 13: iteratively add private characteristics, then, reboot

btssuB    movfwb    bthctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bz    btssuB0                ;do actual reboot

    movlf    tblptrl,low sbtspc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtspc1   

    movlw    44                ;43 bytes per private service string
                        ;including stupid assembler padding 0
    mulwf    bthctr,1            ;prod = index into list of strings

    movfw    prodl                ;set tblptr->correct characteristic string
    addwf    tblptrl,f
    movfw    prodh
    addwfc    tblptrh,f
   
    incf    bthctr,f,1            ;index to next characteristic

    call    _txpstr                ;transmit string
    bra    btx_AT_                ;AOK and return here

;all characteristics set.  Reboot
   
btssuB0    txpstr    sbtres                ;send reset
    bsf    fwrrbt                ;wait for 'Reboot'
    bra    btx__TN

;-- State 14: wait up to 3,200 ms for reboot sequence -- then go do LS all over again to load handles

btssuC    bsf    fwrzcmd                ;wait for '\0CMD'
    nextst    cbtsls                ;set up LS as next state
    bra    btx__L_                ;long timeout for reset

;-- State 15: start advertisement

btsadv0    clrf    btwmask,1            ;clear mask for notification service
    clrf    btwmask+1,1
    clrf    btwrite,1            ;and any pending writes
    clrf    btwrite+1,1   
    clrf    btwbuff,1
    clrf    btwbuff+1,1

    txpstr    sbtadv                ;send start advertisement
    bra    btxEATN                ;  AOK Expected (Error if already advertising)

;-- State 16: wait forever until "Connected"
;         monitor fbton in case bluetooth is to shut down
;             monitor shutdown in case product is shutting down

btsadv1    bbs    FSRCON,btadv10            ;connect received?

;check for change in hardware status

    bbc    fbton,btadv11            ;bluetooth needs to turn off?
    bbs    shtdwn,btadv11            ;system shutting down? Then turn off bluetooth.

;quit and come back

    bsf    FWRCON                ;allow connection response
    bsf    FWRNWR                ;don't wait for a response (need to check hardware)
    bra    btx____                ;and come right back next pass
   
;connect received

btadv10    bsf    fbtconn                ;indicate connection
    rcall    btcbonconn            ;tell app that connection made
    bra    btx___N                ;and go immediately to wait for ConnParm:

;powering off -- start shutdown process

btadv11    nextst    cbtpdwn                ;start powerdown
    bra    btx____                ;immediately

;-- state 17: connection established. Wait for ConnParm: non-standard response

btsadv2    bra    btx_LRN                ;wait for "ConnParm:"

;-- State 18: active as connected -- monitor fbton in case bluetooth is to shut down
;             and process all characteristic writes,reads,and requests from central device

btsac0    bbs    FSRCEND,btsac02            ;disconnect received?   
    bbs    FSRAOK,btsac05            ;AOK from notification?
    bbs    FSRNFL,btsac06            ;NFAIL from notification       

;don't process hardware signals or data writes until expected AOK(s) or CONNP received

    call    aokck                ;awaiting 1 or more AOKs from write?
    bnz    btsac01                ;yes, wait till all received

;process shutdown signals if necessary

    bbc    fbton,btsac03            ;bluetooth needs to turn off?
    bbs    shtdwn,btsac03            ;system shutting down? Then turn off bluetooth.

;is data ready to write?
   
    rcall    btwprop                ;write properties if necessary

;no other action, try again next main loop

    bra    btsac01                ;quit to next loop

;AOK received -- clear wait for AOK data write flag

btsac05    movf    aokcnt,f,1            ;awaiting 1 or more AOKs from write?
    skpz                    ;
    decf    aokcnt,f,1            ;yes decrement it

    bra    btsac01                ;and quit to next loop

;NFail received -- clear AOK wait flags

btsac06    movlw    1                ;increment nfail counter (TEST CODE)
    addwf    nfailctr,f,1
    clrw
    addwfc    nfailctr+1,f,1
    addwfc    nfailctr+2,f,1
    addwfc    nfailctr+3,f,1
       
    bsf    fnfail                ;indicate nfail received
    movlfb    aoktim,cbtslo            ;reset timer for aoks   

    bra    btsac01                ;quit to next loop

;quit and come back

btsac01    bsf    FWRAOK                ;allow AOK for properties written
    bsf    FWRNFL                ;allow NFail for properties written
    bsf    FWRCEND                ;allow connection end response
    bsf    FWRNSR                ;allow non-standard for notifications/data requests
    bsf    FWRNWR                ;don't wait for a response

    bra    btx____                ;and come right back next pass

;disconnect received -- start advertising again for seamless reconnect

btsac02    bcf    fbtconn                ;indicate connection lost
    rcall    btcboffconn            ;tell app that connection is broken
    nextst    cbtsadv                ;start advertising
    bra    btx____                ;  immediately

;module requires powerdown -- kill existing connection

btsac03    bra    btx___N                ;go immediately to next state to begin power off

;-- State 19:  start power-off sequence -- kill existing connection or stop advertising

btssd0    bbs    fbtconn,btssd01            ;connected?

;stop advertising

    txpstr    sbtkadv                ;send stop advertise command
    bra    btxEATN                ;and advance (allow error, but shouldn't happen, possible NSR if incoming data)

btssd01    bcf    fbtconn                ;killing connection, so clear it
    rcall    btcboffconn            ;tell app that connection is broken

    txpstr    sbtkill                ;send kill command
    bsf    FWRCEND                ;wait for Connection End
    bra    btxE_TN                ;wait for connection end, but allow for error JIC

;-- State 20:  put bluetooth into dormant mode

btssd1    bcf    wakehw                ;pull line low
    txpstr    sbtdorm                ;send dormant command
    bra    btx__TN                ;result will be 0x00, which we cannot parse, so just time it

;-- State 21: shutdown bluetooth and EUSART

btssd2    call    uartoff                ;turn eusart off
    bcf    wakesw                ;turn off bluetooth uart transmitter
    nextst    cbtsid                ;set up idle0 as next state
    bra    btx____                ;and immediate idle

;***** END OF STATES *********

;**** Various Exits of SM to set up next state, timeouts, and responses ****
;     E=ERR A=AOK T=short timeout L=long timeout N=advance to next state R=Allow non-standard response

btxE_TN    movlfb    btstime,cbtsto            ;STO/ERR/NEXT
btxE__N    bsf    FWRERR                ;ERR/NEXT
    bra    btx___N

btx_TRN bsf    FWRNSR                ;NSR/NEXT/STO
    bra    btx__TN

btxEATN    bsf    FWRERR                ;ERR/AOK/STO/NEXT
btx_ATN    bsf    FWRAOK                ;AOK/STO/NEXT
btx__TN    movlfb    btstime,cbtsto            ;STO/NEXT
btx___N    incf    btstate,f,1            ;NEXT
    bra    btx____

btx__TR    bsf    FWRNSR                ;STO/NSR
    bra    btx__T_

btxEAT_ bsf    FWRERR                ;ERR/AOK/STO
btx_AT_ bsf    FWRAOK                ;AOK/STO
btx__T_    movlfb    btstime,cbtsto
    bra    btx____

btx_LRN bsf    FWRNSR                ;NSR/NEXT/LTO
btx__LN    incf    btstate,f,1            ;LTO/NEXT       
btx__L_    movlfb    btstime,cbtslo            ;LTO
btx____    movlb    0                ;nothing
    return


;**** END OF SM EXITS ****

;******************************************************************
;** AOKADD -- Add wreg counts to AOK counter and reset AOK timer **
;******************************************************************

aokadd    addwf    aokcnt,f,1            ;add to aok count timer
    movlfb    aoktim,cbtsto            ;allow 200ms for count to get back to zero   
    return

;************************************************
;** AOKCK -- Check AOK counter                 **
;**       return Z if aokcnt=0                 **
;**       decrement timer every 16ms           **
;**       if expire, set AOK to 0 and return z **
;**         otherwise, return nz               **
;************************************************

aokck    movf    aokcnt,f,1            ;is aok zero?
    skpnz
    return                    ;yes, indicate it

    retbc    tc16ms                ;return nz if 16 ms expire
       
    decfsz    aoktim,f,1            ;decrement timer
    return                    ;return nz if not expired

    movlw    1                ;increment timeout counter (TEST CODE)
    addwf    wptoctr,f,1
    clrw
    addwfc    wptoctr+1,f,1
    addwfc    wptoctr+2,f,1
    addwfc    wptoctr+3,f,1
       
    clrf    aokcnt,1            ;clear aok count and set z
    return                   


;*****************************************
;** BTPRCV -- Process Bluetooth receiver**
;**   return z if no response           **
;*****************************************

btprcv    movlf    fsr1h,high btrcvbuff    ;point to receiver buffer
    movff    btrhead,fsr1l        ;  and index to next character

btplp    rcall    rxbyte            ;receiver byte available?
    retz                ; z if none

    movwf    indf1            ;save character at current position
    movlw    '\r'            ;ignore return characters
    cpfseq    indf1
    movf    postinc1,w        ;get back in w, and update pointer

    movff    fsr1l,btrhead        ;save new head position

    xorlw    '\n'            ;new character a line feed?
    bnz    btplp            ;no, get some more characters, if available   

    movlfb    btrhead,low btrcvbuff    ;reset btrhead for next message

;parse for common messages
;if common response not matched, indicate (likely) parameters returned from RN4020

    bsf    btsresp,0,1        ;set 1st bit in standard response flags

    movdf    tblptr,btsmes        ;tblptr -> possible standard responses (\n separated, \r terminated)

btplp0    lfsr    0,btrcvbuff        ;fsr0 -> point to head of response buffer
   
btplp1    tblrd    *+            ;get next character from list of responses
   
    movlw    '\r'            ;\r is response list terminating character
    xorwf    tablat,w        ;  end of response list?
    bz    btpnmf            ;yes, no response matched

    movf    tablat,w        ;get character

    cpfseq    postinc0        ;test against charcter in response buffer
    bra    btpnom            ;!= means no match, skip to next response

    movlw    '\n'            ;is it a new line character?
    xorwf    tablat,w       
    bz    btpmat            ;yes, we have found a match

    bra    btplp1            ;keep testing characters till match

;no match of current response, skip ahead till /n or /r

btpnom    clrc                ;roll to next response
    rlcf    btsresp,f,1
    rlcf    btsresp+1,f,1

btplp2    tblrd    *+            ;get next character from list of responses

    movlw    '\r'            ;get terminating character
    xorwf    tablat,w        ; end of response list?
    bz    btpnmf            ;yes, no response matched

    movlw    '\n'            ;is it a new line character?
    xorwf    tablat,w       
    bnz    btplp2            ;loop till \0 or \n

    bra    btplp0            ;and test against next response

;match found, response bit set

btpmat

;match not found, non-standard response bit set

btpnmf    clrz                ;nz indicates a new reponse has been received
    return

;********************************************
;** BTPNSR -- Parse Non-Standard Responses **
;********************************************

btpnsr    clrf    bitcnt            ;use bitcnt as index to received response

    movdf    tblptr,btnsmes        ;tblptr -> possible non-standard responses (\n separated, \0 terminated)

btpnlp0    lfsr    0,btrcvbuff        ;fsr0 -> point to head of response buffer
   
btpnlp1    tblrd    *+            ;get next character from list of responses
   
    movf    tablat,w        ;get character from list
    bz    btpnnf            ;z=end of list, not found

    movlw    '\n'            ;== \n means we have a match!
    xorwf    tablat,w
    bz    btpnm            ;go process for arguments

    movf    tablat,w        ;get character from list
    cpfseq    postinc0        ;compare with received
    bra    btpnnm            ;no match, skip to next response

    bra    btpnlp1            ;continue till match/no match   

;no match of current response, skip ahead till /0 or /n

btpnnm    incf    bitcnt,f        ;update response counter

btpnlp2    tblrd    *+            ;get next character from list of responses

    movf    tablat,w        ;get character
    bz    btpnnf            ;z=end of list -- no match
   
    xorlw    '\n'            ;is it a new line character?
    bnz    btpnlp2            ;loop till \0 or \n

    bra    btpnlp0            ;and test against next non-standard response

;no match found - what do we do?

btpnnf    return                ;for now, just ignore the response

;match found -- bit count has response number

btpnm    movfw    bitcnt            ;get response number
    call    cjump            ;and process it

    bra    btnscp            ;'ConnParam:'
    bra    btnswc            ;'WC,'
    bra    btnsnfy            ;'Notify,'
    bra    btnsind            ;'Indicate,'
    bra    btnswv            ;'WV,'
    bra    btnsrv            ;'RV,'

;ConnParam <hex16>,<hex16>,<hex16> (connection interval, slave latency, supervison timeout)

btnscp    lfsr    1,conint+1        ;point to holders
    rcall    hex2bin            ;capture interval
    rcall    hex2bin            ;capture slave latency
    rcall    hex2bin            ;capture supervision timeout
    return

;WC,<hex16>,<hex16> (write configuration to local server characteristic)

btnswc;    incf    aokcnt,f,1        ;AOK will be sent by successful write to c prop -- UNDOCUMENTED!

    movlw    1            ;AOK will be sent by successful write to c prop -- UNDOCUMENTED!
    rcall    aokck

    rcall    rxhandle        ;capture first argument as handle
    rcall    btsprop            ;btcharn = matching property number or zero for battery service
    movf    postinc0,f        ;skip trailing comma
    lfsr    1,btint16+1        ;store in int16 holder
    rcall    hex2bin            ;capture new configuration: 0x0000 stop notification, 0x0100 start notification

    movf    btcharn,f,1        ;battery service?
    skpz                ;  private characteristic
    decf    btcharn,f,1        ;  get handle # for PC value

    clrf    temp0            ;clear bit locater
    clrf    temp1

    setc                ;bit to roll in

btnsw0    rlcf    temp0,f            ;roll to next characterisitic
    rlcf    temp1,f

    movf    btcharn,f        ;found bit?
    bz    btnsw1            ; no
   
    decf    btcharn,f        ;try next
    clrc
    bra    btnsw0

;temp1:0 has bit for this characteristic

btnsw1    movf    btint16+1,f        ;stopping notifications?
    bz    btnsw2            ;  yes

    movfw    temp0            ;starting: merge bit into mask
    iorwf    btwmask,f,1
    movfw    temp1
    iorwf    btwmask+1,f,1
    return                ;done!

;stopping notification -- clear bit

btnsw2    comf    temp0,w
    andwf    btwmask,f,1
    comf    temp1,w
    andwf    btwmask+1,f,1           
    return

;other non-standard responses -- for later -- just indicate unhandled for now

;notification received
btnsnfy                    ;'Notify,'

;indication received
btnsind                    ;'Indicate,'

;write value to local server characteristic
btnswv                    ;'WV,'

;real time read (bit must be set in supported features (SR)
btnsrv                    ;'RV,'

    bra    btpnnf            ;indicate unhandled non-standard response

;**************************************************************
;** HEX2BIN -- Convert arbitrary length hex to binary        **
;**   store result at fsr1 -> RAM (high byte)                **
;**   ignore spaces, period, comma or \n terminated          **
;**   target RAM must be large enough to hold expected value **
;**************************************************************
   
hex2bin

;process high byte first

    movf    postinc0,w        ;get next character
    addlw    -10            ;is it a new line?
    retz                ;yes, we are done
    addlw    -22            ;is it a space?
    bz    hex2bin            ;yes, ignore it
    addlw    -12            ;is it a comma?
    retz                ;yes, we are done
    addlw    -2            ;is it a '.' (damn documentation!)
    retz                ;yes, it is a terminator (he'll be back!)
                    ;'0'-'9' = 2 - 11, 'A'-'F' = 19 - 24
    addlw    -12            ;'0'-'9' = -10 - (-1) (nc), 'A'-'F' = 7-12 (c)
    skpnc                ;adjust for 'A'-'F'
    addlw    -7            ;'A'-'F'=0-5
    addlw    10            ;this is my final answer
    swapf    wreg,f            ;swap for high byte
    movwf    indf1            ;and save

;then low byte (no need to check for terminators on odd characters)

h2b1    movlw    58
    subwf    postinc0,w        ;'A'-'F' = 7 - 12, '0'-'9' = -10 - -1
    skpc                ;correct for 0-9
    addlw    7            ;'0'-'9' = -3 - 6
    addlw    3            ;final result
    iorwf    postdec1,f        ;merge in
    bra    hex2bin            ;do for all characters

;********************************************************************
;** RXPCSTR -- Compare received string with program memory         **
;**   program memory string terminated with \r                     **
;**   tblptr -> string                                             **
;**   ignore leading characters in PM until first comma            **
;**   ignore leading spaces in RAM                                 **
;**   z if match, nz if no match                                   **
;**   if fbtrpc=1, pc handle is captured and pc properties checked **
;**   if fbtcaph is set, handle for char #(bthctr) is captured     **
;**   on exit, if fbtcapp=1, line must be rescanned for property   **
;********************************************************************

_rxpcstr

    btfsc    fbtrpc            ;are we scanning a private characteristic?
    bsf    fbtcaph            ;yes, we must capture the handle

    lfsr    0,btrcvbuff        ;point to bluetooth receive buffer

    movlw    ','            ;skip to character after first comma
rxpclp0    tblrd    *+
    cpfseq    tablat
    bra    rxpclp0
   
rxpclp1    movlw    ' '            ;ignore leading spaces in RAM
    xorwf    postinc0,w
    bz    rxpclp1

    movf    postdec0,w        ;move pointer back to first non-space character

rxpclp2    tblrd    *+            ;get character from program memory
    movlw    '\r'            ;terminates with /r
    xorwf    tablat,w        ;compare
    bz    rxpcbx            ;return with z to indicate match

    bbc    fbtrpc,rxpcb0        ;scanning a private characteristic?

    bbc    fbtcaph,rxpcb0        ;capture handle flag set?
    movlw    ','            ;handle starts at first comma
    xorwf    indf0,w            ;compare
    bnz    rxpcb0            ;scan till first comma

;first comma found, parse handle for this characteristic

    rcall    rxpch            ;go capture handle

;adjust for notifiers - match comma

    movf    indf0,w            ;current character should be comma
    movfw    tablat            ;get pm character
    xorwf    postinc0,w        ;compare with received character
    bnz    rxpcbx            ;no match, reload

;check for match of high nibble of characteristic properties

    tblrd    *+            ;indext to next program character
    movfw    tablat            ;get pm character
    xorwf    indf0,w            ;compare with current ram character
    bz    rxpcb1            ;match.  check second character

;if current RAM byte is non-zero, we are scanning properties

    movlw    '0'
    xorwf    indf0,w            ;compare z=value, nz=char
    skpnz                ;if we are scanning characteristic property, proceed as normal
    bsf    fbtcapp            ;otherwise, we must recheck line for property

;check for match of low nibble of characteristic properties

rxpcb1    movf    postinc0,w        ;skip to next character

;******** TO DO! compare notify/indicate bit to reload if they change

    tblrd    *+            ;get next program character
    movfw    tablat            ;get rom character
    xorwf    indf0,w            ;compare with current ram character
    bz    rxpcb2            ;continue normally if match (first line, fbtcapp is set)

;second line of notified characteristic -- ensure ftbcapp and char '0'

    bbc    fbtcapp,rxpcbx        ;fbtcapp should be set here, if not, we've got a mismatch

    bcf    fbtcapp            ;clear fbtcapp for next characteristic

    movlw    '0'            ;ram byte should be zero
    xorwf    indf0,w            ;compare z=value, nz=char
    bra    rxpcbx            ;z=string matched (ignore last parameter), nz=mismatched.

;first line of characteristic -- scan normally
   
rxpcb2    movf    postinc0,w        ;to next character
    bra    rxpclp2            ;continue scanning normally

;just normal scan here

rxpcb0    movf    indf0,w
    movfw    tablat            ;get pm character
    xorwf    postinc0,w        ;compare with received character
    bz    rxpclp2            ;match, keep going

rxpcbx    bcf    fbtrpc            ;clear private characteristic indication

    return                ;return with no match/no match

;capture handle at current ram position +1

rxpch    bcf    fbtcaph            ;clear capture flag for any remaining commas
   
    lfsr    1,hpchar1        ;fsr1->start of characteristic handle table

    rlncf    bthctr,w,1        ; *4 (size of handle)
    rlncf    wreg,w

    addwf    fsr1l,f            ;fsr1->proper handle

    movff    preinc0,postinc1    ;copy 4 bytes
    movff    preinc0,postinc1
    movff    preinc0,postinc1
    movff    preinc0,postinc1
       
    movf    postinc0,f        ;index to tailing comma

    return                ;and go finish scan

;******************************************************
;** BTWPROP -- Write property value if indicated     ** 
;**     btwrite has one or more properties           **
;**        bit 0 for battery service                 **
;**        bit 1 through cnumcar for private service **
;**     format out: "SHW,hhhh,v..."                  **
;**     callback to get value                        **
;******************************************************

btwprop    retbc    fbtconn                ;no writes if not connected

    movfwb    btwrite                ;properties to write?
    iorwf    btwrite+1,w,1            ; two sets
    bnz    btwp1                ;no, get out

    movfwb    btwbuff                ;get pending writes
    andwf    btwmask,w,1            ;mask with notify activated
    movwf    btwrite,1            ;save as new set to send
    comf    wreg,f
    andwf    btwbuff,f,1            ;and indicate buffer clear for those properties

    movfwb    btwbuff+1                ;get pending writes
    andwf    btwmask+1,w,1            ;mask with notify activated
    movwf    btwrite+1,1            ;save as new set to send
    comf    wreg,f
    andwf    btwbuff+1,f,1            ;and indicate buffer clear for those properties

    movfwb    btwrite                ;now properties ready to write?
    iorwf    btwrite+1,w,1            ; two sets
    retz                    ;no, get out

;find next data item to send

btwp1    movlw    1
    addwf    wpropctr,f,1
    clrw
    addwfc    wpropctr+1,f,1
    addwfc    wpropctr+2,f,1
    addwfc    wpropctr+3,f,1

    movlf    temp1,1                ;start at bit0   
    clrf    temp2

    clrf    bthctr,1            ;will hold property number

;find the next property who's value requires writing

btwplp0    movfwb    btwrite                ;test a bit
    andwf    temp1,w                ;got a hit?
    bnz    btwp0                ;yes, go send it

    movfwb    btwrite+1            ;test a bit
    andwf    temp2,w                ;got a hit?
    bnz    btwp0                ;yes, go send it

    clrc                    ;shift bit mask
    rlcf    temp1,f
    rlcf    temp2,f

    incf    bthctr,f,1            ;increment property number
    bra    btwplp0                ;and check next

;bthctr has property number to write

btwp0    comf    temp1,w                ;invertmask
    andwf    btwrite,f,1            ;and clear bit for next pass

    comf    temp2,w                ;invertmask
    andwf    btwrite+1,f,1            ;and clear bit for next pass

    movlw    2                ;need 2 AOKs for notified write
    rcall    aokadd

    decf    bthctr,f,1            ;get bthctr-1
    bc    btwpps                ; c=private service

;bthctr was 0 -- send battery service

    txpstr    sbtbsh                ;send battery service handle
    call    btcbwbl                ;go write actual value in callback
    bra    ptwpx                ;exit adding trailing return

;enumerated private services (up to 10)
   
btwpps    txpstr    sbtshw                ;send "SHW," to begin property write

    lfsr    1,hpchar1            ;point first characteristic handle

    rlncf    bthctr,w            ;double count
    rlncf    wreg,w                ;  4 bytes per entry

    addwf    fsr1l,f                ; and index

    movfw    postinc1            ;send handle for indicated service
    call    txbyte
    movfw    postinc1            ;send handle for indicated service
    call    txbyte
    movfw    postinc1            ;send handle for indicated service
    call    txbyte
    movfw    postinc1            ;send handle for indicated service
    call    txbyte

    movlw    ','
    call    txbyte                ;send a comma to separate from value

    switch    bthctr,1,ptwpx            ;make proper callback and exit with trailing \r

    goto    btcbps1                ;each must send a value to TX
    goto    btcbps2
    goto    btcbps3
    goto    btcbps4
    goto    btcbps5
    goto    btcbps6
    goto    btcbps7
    goto    btcbps8
    goto    btcbps9
    goto    btcbpsA   
               
;add trailing return character to complete command

ptwpx    movlw    '\r'
    goto    txbyte            ;send and return

;************************************************
;** BTSI8 -- send INT8 in btsdata to bluetooth **
;************************************************

btsi8    lfsr 1,btsdata            ;point to btsdata

;********************************************
;** BTSPI8 -- send fsr1->INT8 to bluetooth ** 
;********************************************
   
btspi8    movlw    1            ;one byte
    bra    btpssd            ;write the data

;**************************************************
;** BTSI16 -- send INT16 in btsdata to bluetooth **
;**************************************************

btsi16    lfsr    1,btsdata        ;point to btsdata

;**********************************************
;** BTSPI16 -- send fsr1->INT16 to bluetooth ** 
;**********************************************
   
btspi16    movlw    2            ;two bytes to send
    bra    btpssd            ;and write data

;************************************************
;** BTSFLS -- send float on stack to bluetooth **
;************************************************

btsfls    popfl    btsdata            ;get float into btsdata
   
;*******************************************************
;** BTS32 -- send 4 bytes of binary data to bluetooth **
;**   btsdata has data to send                        **
;*******************************************************

bts32    movlw    4            ;four bytes

;***********************************************************
;** BTSSD -- send wreg bytes of binary data to bluetooth  **
;**   btsdata has data to send                            **
;***********************************************************

btssd    lfsr    1,btsdata        ;point to btsdata

;***********************************************************
;** BTPSSD -- send wreg bytes of binary data to bluetooth **
;**   fsr1->data                                          **
;***********************************************************

btpssd    movwf    bitcnt            ;save number of bytes
    addwf    fsr1l,f            ;and point to high byte +1
    movf    postdec1,f        ;point to high byte

btssdl1    swapf    indf1,w            ;get high byte
    rcall    tbc2hex            ;transmit ASCII
    movf    postdec1,w        ;get low byet
    rcall    tbc2hex            ;transmit ASCII
   
    djnz    bitcnt,btssdl1        ;do for all bytes

    return                ;and return
   
;********************************************************
;** BTTHEX -- Convert low nibble of WREG to ASCII HEX  **
;**           and transmit to bluetooth
;********************************************************

tbc2hex    andlw    b'1111'            ;mask lower nibble
    addlw    -10            ;0-9=-10 to (-1) (nc), A-F=0 to 5 (c)
    skpnc                ;A-F?
    addlw    7            ;A-F = 7 to 12
    addlw    58            ;0-9=48-57,A-F=65-70 ('0'-'9','A'-'F')
    goto    txbyte            ;and send it

;custom app files

    include "btcb.asm"        ;include custom callbacks to main program
    include    "btstrings.asm"        ;include command/response strings

;********************************************************
;** RXHANDLE -- Copy 4 byte handle from fsr0 to bthndl **
;********************************************************

rxhandle

    movff    postinc0,bthndl        ;copy 4 bytes
    movff    postinc0,bthndl+1
    movff    postinc0,bthndl+2
    movff    postinc0,bthndl+3

    return

;**************************************************************
;** BTSPROP -- search for property number with handle bthndl **
;**    not found, return 0 (battery property)                **
;**    otherwise, return 1 indexed handle number             **
;**************************************************************

btsprop    movlfb    bthctr,10        ;10 possible entries

btsplp0    lfsr    1,hpchar1        ;point to first entry

    movfwb    bthctr            ;get current count
    sublw    10            ;0, 1, ... 9

    rlncf    wreg,w            ;double it
    rlncf    wreg,w            ;again
    addwf    fsr1l,f            ;fsr1->next entry

    movfw    postinc1        ;get 1st character
    xorwf    bthndl,w,1        ;test
    bnz    btspne            ;not equal, try next   

    movfw    postinc1        ;get 1st character
    xorwf    bthndl+1,w,1        ;test
    bnz    btspne            ;not equal, try next   

    movfw    postinc1        ;get 1st character
    xorwf    bthndl+2,w,1        ;test
    bnz    btspne            ;not equal, try next   

    movfw    postinc1        ;get 1st character
    xorwf    bthndl+3,w,1        ;test
    bz    btspeq            ;equal, found a match   

btspne    djnz    bthctr,btsplp0        ;try for all 10

    clrf    btcharn,1        ;return with 0 for battery service
    return

btspeq    movfwb    bthctr            ;10=1, 9=2, ..., 1=10
    sublw    11            ;wreg = 11 - bthctr
    movwf    btcharn,1        ;save it
    return
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
I am writing the following for those not too versed in BLE, and also for me to read over and over again until it becomes muscle memory (the operative muscle being my brain, of course).
  1. If a value is written to a characteristic, and neither the notify or indicate property is set or enabled, the value is only written locally, and no transmission occurs to central (i.e. central must manually request the value, if it wants it).
  2. If a value is written to a characteristic, and the notify or indicate bit is set but not enabled, same as #1.
  3. If a value is written to a characteristic, and the notify or indicate bit is both set and enabled, a transmission of the value is sent to central and a notification is also sent to indicate to central that a new value has been received.
  4. If a value is written to a characteristic, and the indicate bit is set and enabled, #3 occurs, followed by a return acknowledgement sent from central.
Specific to the RN4020, for each case:
  1. An AOK is returned to indicate the value has been written locally.
  2. Same as #1.
  3. An AOK is returned to indicate the value has been transmitted followed by another AOK to indicate the notification/indication has been sent.
  4. Same as #3, but the second AOK is not returned until the acknowledgement has been received.
This is very badly, and insufficiently, documented in the RN4020 guide.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
I am not as dumb as I thought.

Back in post #106, I detailed the problem I was seemingly having with bandwidth. I assumed that I was trying to send data faster than was possible, so I added the indications to slow things down. But, in the back of my mind, I knew I was not sending so much data as to swamp the connection. Adding indications did ensure that data was not transmitted faster than the radio connection could handle, and eliminated the errors I was getting, so that had to be the cause of the problem, right?

Wrong. And, no, there was not a bug in my code.

The RN4020 has a problem. And it has to do with the BLE standard battery service. It seems that if the first private characteristic I add has the notify/indicate bit set, any write to that characteristic causes 1) data corruption of the value of the battery service and 2) a stream of battery service notifications to central, thus swamping the connection.

This problem is eliminated if the first private characteristic does not have the notify/indicate bit set.

As a second clue there is something wrong with the RN4020 battery service, I should be able eliminate the service entirely through the SS command. When I do this, the battery service still shows up as available on the Android. This should not be the case.

I cannot believe how many hours I wasted because of this bug.

FYI: The RN4020 I have is firmware version 1.10. The latest is 1.23 (which I'll be using for production), but I don't have one. This may have been fixed, but I've seen no errata on 1.10 that mentions this as a problem.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
This is starting to get old.

New twist: UUIDs and handles are sent/received big endian. Values are sent/received little endian.

One little, two little, three little endians...

Shoulda gone to the dentist today.
 

NorthGuy

Joined Jun 28, 2014
611
New twist: UUIDs and handles are sent/received big endian. Values are sent/received little endian.
That's a text. It cannot be big endian or little endian. Numbers (including hexadecimal) always start from most significant digits, e.g. twelve is 12 not 21. Text (or binary blobs) is always left to right (in English anyway), e.g. Bluetooth, not htooteulB (and there's no most significant or least significant digits in there). I think they teach this in the first grade.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
That's a text. It cannot be big endian or little endian. Numbers (including hexadecimal) always start from most significant digits, e.g. twelve is 12 not 21. Text (or binary blobs) is always left to right (in English anyway), e.g. Bluetooth, not htooteulB (and there's no most significant or least significant digits in there). I think they teach this in the first grade.
I attended first grade! Handles are <hex16> (using the document's nomenclature). If they meant a text or a blob, it should say <text16> or <blob16>.

I.e: sending the value of 0x123456 to handle 0x000F uses the following syntax:

SHW,000F,563412

not

SHW,0f00,563412

or

SHW,000f,123456

as would be consistent.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
ConnParam:<hex16>,<hex16>,<hex16>
WC,<hex16>,<hex16>

These are binary numbers that received from the RN4020 in big endian.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
Done. 100% tested and working. Beer time.

Broken into two posts because I exceeded the 50,000 character limit...

Code:
;**************************************
;** BTTOG -- Toggle Bluetooth on/off **
;**************************************

bttog    btg    fbton            ;toggle on/off
    return
  
;*************************************************************
;** BTSEND -- send characteristic #wreg value via Bluetooth **
;**           actual value is captured via callback         **
;**           WREG=0 for Battery Service                    **
;**           WREG=1..n for Private Characteristics         **
;*************************************************************

btsend    clrf    temp0            ;set for char #0
    clrf    temp1

    setc                ;roll first 1 in
btselp0    rlcf    temp0,f
    rlcf    temp1,f

    movf    wreg,w            ;wreg=0
    bz    btse0            ;yes, bit in proper position

    decf    wreg,w
    clrc                ;clear carry for next shift
    bra    btselp0

;temp1:0 has bit position of characteristic to send

btse0    movff    btwbuff,wreg        ;merge into write buffer (don't know current working bank)
    iorwf    temp0,w
    movff    wreg,btwbuff

    movff    btwbuff+1,wreg        ;merge into write buffer (don't know current working bank)
    iorwf    temp1,w
    movff    wreg,btwbuff+1

    return

;***************************************************************
;** POLLBT -- Bluetooth polling -- called each main loop pass **
;***************************************************************

pollbt    retbc    run            ;don't process bluetooth until system initialized and running

    BANKSEL    btstate            ;select bluetooth working bank          

    clrf    btsresp,1        ;clear standard response flags
    clrf    btsresp+1,1

    rcall    btprcv            ;check to see if a new bluetooth response has been received.
    bz    pbtnrsp            ;no response

;if a non-standard response is received, and allowed, parse it

    bbc    fwrnsr,pbtnnwr        ;nsr allowed?
    btfsc    fsrnsr            ;nsr received?
    rcall    btpnsr            ;go parse non-standard response

;a response has been received from bluetooth

pbtnnwr    movlw    b'01111111'        ;ignore NWR flag
    andwf    btswait+1,w,1
    iorwf    btswait,w,1        ;are we waiting for a particular response?
    bz    pbstate            ;no, just process machine

;decide whether or not to throw an error

    bbs    fwrerr,pbteok        ;is ERR an acceptable response?
    bbs    fsrerr,pbterr        ;No, flag ERR as error.

pbteok    bbs    FWRNWR,pbstate        ;improper responses don't throw errors if NWR bit set

    movf    btswait,w,1        ;are we waiting for a response?
    andwf    btsresp,w,1        ;has a proper response been received?
    bnz    pbstate            ;yes, process state machine
  
    movf    btswait+1,w,1        ;again, next byte
    andwf    btsresp+1,w,1
    bnz    pbstate

    bra    pbtimp            ;flag an improper response

;no response received from bluetooth this pass -- process timed functions

pbtnrsp    movf    btstime,f,1        ;is timer running?
    bz    pbtntim            ;no, not a timed function

;timed function -- either response time-out or timed state

    btfsc    tc16ms            ;process 16ms timer
    decfsz    btstime,f,1        ;decrement state timer
    bra    btx____            ;no further processing if timer not expired

    movlw    b'01111111'        ;ignore NWR flag
    andwf    btswait+1,w,1
    iorwf    btswait,w,1        ;are we waiting for a particular response?
    bz    pbstate            ;no, just process timed state after delay

;otherwise, flag a timeout error

    bra    pbtrto            ;show timeout error

;not a timed function

pbtntim    bbs    fwrnwr,pbstate        ;don't wait for response if bit is set

    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    bz    pbstate            ;no, just process the untimed state

    bra    btx____            ;otherwise, just wait until response
  
;Handle improper response

pbtimp    bra    btx____            ;ignore improper responses (for now)

;Handle ERR response

pbterr    movlb    0
    movlw    bterr            ;get ERR error code
    call    seterr            ;and show error
    BANKSEL    btstate            ;select bluetooth working bank          

    bra    btx____            ;ignore erroneous responses

;Handle timeout during reception of response

pbtrto    movlb    0
    movlw    btrto            ;get time out error code
    call    seterr            ;and set error limp along
    BANKSEL    btstate            ;select bluetooth working bank          

    bra    btx____            ;ignore erroneous responses

;process Bluetooth state machine

pbstate    clrf    btswait,1        ;clear waiting flags for next pass
    clrf    btswait+1,1

    clrf    btstime,1        ;and state timer
  
    movfw    btstate
    call    cjump  

;idle with bluetooth off

    bra    btsid0            ;state 0:  if fbton, wake BT and wait for CMD message  

;setup blue tooth

    bra    btssu0            ;state 1:  get/set feature string
    bra    btssu1            ;state 2:  get/set friendly name
    bra    btssu2            ;state 3:  get/set model name
    bra    btssu3            ;state 4:  get/set manufacturers name
    bra    btssu35            ;state 5:  get/set connection parameters
    bra    btssu4            ;state 6:  get/set bitmap services
    bra    btssu5            ;state 7:  send LS to confirm private services
    bra    btssu6            ;state 8:  confirm private service UUID is defined
    bra    btssu7            ;state 9:  confirm all private characteristic UUIDs
    bra    btssu8            ;state 10:  scan for end of LS list
    bra    btssu9            ;state 11: start reset/reboot process -- clear private services
    bra    btssuA            ;state 12: add private services
    bra    btssuB            ;state 13: iteratively add private characteristics then reboot
    bra    btssuC            ;state 14: wait for reboot to complete and redo LS to load handles

;preconnection loop

    bra    btsadv0            ;state 15: start advertising
    bra    btsadv1            ;state 16: wait for connected
    bra    btsadv2            ;state 17: wait for connection parameters
      
;active with bluetooth on

    bra    btsac0            ;state 18:  idle while connected and monitor monitor for shutdown

;shudown bluetooth

    bra    btssd0            ;state 19:  stop advertising or disconnect existing connection
    bra    btssd1            ;state 20:  switch to dormant mode
    bra    btssd2            ;state 21:  clear wakesw and shutdown EUSART

;***********************
;** Bluetooth states  **
;***********************

;-- State 0:  if fbton, wake BT and wait for CMD message

btsid0    bbc    fbton,btx____            ;do nothing if bluetooth not activated
    bbs    shtdwn,btx____            ;and don't start up if in system shutdown

    call    uarton                ;turn eusart on

    bsf    wakesw                ;wake bluetooth
    bsf    wakehw  

    bcf    fbtrbt                ;clear reboot flag for setup

    bsf    fwrcmd                ;wait for "CMD"
    bsf    fwrzcmd                ; or \0CMD if restart
    bra    btx__LN                ;long timeout in case of restart

;-- State 1:  begin setup, get, compare, and set feature string if necessary

btssu0    bbs    fsrnsr,btssu01            ;gf data received (as non-standard response?)

btssu00    txpstr    sbtgfs                ;get feature settings string
    bsf    fwrnsr                ;  wait for non-standard response
    bra    btx__T_                ;  wait 200ms and don't advance

btssu01    rxpcstr    sbtsfs                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfs                ;send feature settings string
    bra    btx_ATN                ;and advance
      
;-- State 2:  get, compare, and set friendly name if necessary

btssu1    bbs    fsrnsr,btssu10            ;friendly name received (as non-standard response?)

    txpstr    sbtgfn                ;get friendly name string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu10    rxpcstr    sbtsfn                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfn                ;send new friendly name
    bra    btx_ATN                ;and advance
      
;-- State 3:  get, compare, and set model if necessary

btssu2    bbs    fsrnsr,btssu20            ;model received (as non-standard response?)

    txpstr    sbtgmod                ;get model string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu20    rxpcstr    sbtsmod                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmod                ;send new model
    bra    btx_ATN                ;and advance
      
;-- State 4:  get, compare, and set msnufacturer's name if necessary

btssu3    bbs    fsrnsr,btssu30            ;manufacturer's name received (as non-standard response?)

    txpstr    sbtgmn                ;get manufacturer's name string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu30    rxpcstr    sbtsmn                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmn                ;send new manufacturer's name
    bra    btx_ATN                ;and advance
      
;-- State 5:  get, compare, and set connection parameters

btssu35    bbs    fsrnsr,btssu36            ;connection parameters received (as non-standard response?)

    txpstr    sbtgcp                ;get connection parameters string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu36    rxpcstr    sbtscp                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtscp                ;send new manufacturer's name
    bra    btx_ATN                ;and advance
      

;-- State 6:  get, compare, and set bitmap services if necessary

btssu4    nop

    bbs    fsrnsr,btssu40            ;bitmap services received (as non-standard response?)

    txpstr    sbtgbms                ;get bitmap services string
    bra    btx__TR                ;  wait 200ms for NSR and don't advance

btssu40    rxpcstr    sbtsbms                ;expected value received?
    skpnz
    bra    btx___N                ;yes, advance to next state

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsbms                ;send new bitmap services
    bra    btx_ATN                ;and advance
      
;-- State 7:  send LS command to confirm private characteristics set up properly

btssu5    clrf    btsptr,1            ;preclear string pointer
    clrf    btsptr+1,1
    clrf    bthctr,1            ;clear handle counter for later
    clrf    btcctr,1            ;and characteristic counter

    bcf    fbtcapp                ;preclear pcp capture for scan

    txpstr    sbtls                ;send ls
    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TN                ;and advance

;-- State 8:  check if the private service is defined

btssu6    bbs    fsrend,btrbt            ;if END received, list is empty, reset and reboot

    rxpcstr    sbtsps                ;expected value received?
    bz    btssu60                ;yes, set up for private characteristics

    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TR                ;await response for END/NSR and return here

btssu60    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TN                ;and advance
  
;-- State 9: iterate to check UUID for each private characteristic -- capture handle if exists
;            and process notification characteristics

btssu7    bbs    fsrend,btssu70            ;if END received, ensure we got all characteristics

    movfwb    btcctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bz    btssu71                ;yes. Should have been END. Scan to END, reset and reboot.

    movlf    tblptrl,low sbtspc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtspc1  

    movfwb    btsptr                ;add in string pointer to point to proper string
    addwf    tblptrl,f
    movfwb    btsptr+1
    addwfc    tblptrh,f

    bsf    fbtrpc                ;indicate that we are scanning a private characteristic
    call    _rxpcstr            ;compare string & capture handle
    bnz    btssu71                ;no, string not matched.  Scan to end, reboot and reset

    incf    bthctr,f,1            ;bump counter for next private characteristic handle

    bbs    fbtcapp,btssu72            ;need to reload string for notification?

    movlw    44                ;no, update string pointer
    addwf    btsptr,f,1
    clrw
    addwfc    btsptr+1,f,1

    incf    btcctr,f,1            ;and increment characteristic pointer

btssu72    bsf    fwrend                ;allow an END if nothing in the list
    bra    btx__TR                ;come back to this state with NSR/END response

;  END received -- ensure we captured [cnumcar] characteristics  

btssu70    movfwb    btcctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bnz    btrbt                ;no. Should have been END.  Reset and reboot.

    nextst    cbtsrbt                ;all good, but reboot if necessary
    bra    btx____

;  No match.  Scan to END, reboot and reset 

btssu71    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;wait for END if nothing in the list
    bra    btx__TN                ;and advance

;-- State 10: scan for END, reboot and reset

btssu8    bbs    fsrend,btrbt            ;reset and reboot if END received

    bsf    fwrend                ;wait for END if nothing in the list
    bra    btx__TR                ;come back to this state with NSR/END response
  
;-- State 11: start reset/reboot process if necessary -- clear all private services      

btssu9    bbs    fbtrbt,btssu90            ;reboot necessary?

    nextst    cbtsadv            ;no, start advertising
    bra    btx____

btrbt    nextst    cbtsrbt            ;jump in here. Set to this state.

btssu90    bcf    fbtrbt                ;clear reboot flag for next pass
    txpstr    sbtcps                ;clear any existing private services/characteristics
    bra    btx_ATN                ;and advance

;-- State 12: add private services

btssuA    clrf    bthctr,1            ;preclear handle counter to index private characteristics

    txpstr    sbtsps                ;add private servcie
    bra    btx_ATN                ;and advance

;-- State 13: iteratively add private characteristics, then, reboot

btssuB    movfwb    bthctr                ;have we exhausted the number of characteristics?
    xorlw    cnumcar                ;compare
    bz    btssuB0                ;do actual reboot

    movlf    tblptrl,low sbtspc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtspc1  

    movlw    44                ;43 bytes per private service string
                        ;including stupid assembler padding 0
    mulwf    bthctr,1            ;prod = index into list of strings

    movfw    prodl                ;set tblptr->correct characteristic string
    addwf    tblptrl,f
    movfw    prodh
    addwfc    tblptrh,f
  
    incf    bthctr,f,1            ;index to next characteristic

    call    _txpstr                ;transmit string
    bra    btx_AT_                ;AOK and return here

;all characteristics set.  Reboot
  
btssuB0    txpstr    sbtres                ;send reset
    bsf    fwrrbt                ;wait for 'Reboot'
    bra    btx__TN

;-- State 14: wait up to 3,200 ms for reboot sequence -- then go do LS all over again to load handles

btssuC    bsf    fwrzcmd                ;wait for '\0CMD'
    nextst    cbtsls                ;set up LS as next state
    bra    btx__L_                ;long timeout for reset

;-- State 15: start advertisement

btsadv0    clrf    btwmask,1            ;clear mask for notification service
    clrf    btwmask+1,1
    clrf    btwrite,1            ;and any pending writes
    clrf    btwrite+1,1  
    clrf    btwbuff,1
    clrf    btwbuff+1,1

    txpstr    sbtadv                ;send start advertisement
    bra    btxEATN                ;  AOK Expected (Error if already advertising)

;-- State 16: wait forever until "Connected"
;         monitor fbton in case bluetooth is to shut down
;             monitor shutdown in case product is shutting down

btsadv1    bbs    FSRCON,btadv10            ;connect received?

;check for change in hardware status

    bbc    fbton,btadv11            ;bluetooth needs to turn off?
    bbs    shtdwn,btadv11            ;system shutting down? Then turn off bluetooth.

;quit and come back

    bsf    FWRCON                ;allow connection response
    bsf    FWRNWR                ;don't wait for a response (need to check hardware)
    bra    btx____                ;and come right back next pass
  
;connect received

btadv10    bsf    fbtconn                ;indicate connection
    rcall    btcbonconn            ;tell app that connection made
    bra    btx___N                ;and go immediately to wait for ConnParm:

;powering off -- start shutdown process

btadv11    nextst    cbtpdwn                ;start powerdown
    bra    btx____                ;immediately

;-- state 17: connection established. Wait for ConnParm: non-standard response

btsadv2    bra    btx_LRN                ;wait for "ConnParm:"

;-- State 18: active as connected -- monitor fbton in case bluetooth is to shut down
;             and process all characteristic writes,reads,and requests from central device

btsac0    bbs    FSRCEND,btsac02            ;disconnect received?  
    bbs    FSRAOK,btsac05            ;AOK from notification?
    bbs    FSRNFL,btsac06            ;NFAIL from notification      

;don't process hardware signals or data writes until expected AOK(s) or CONNP received

    call    aokck                ;awaiting 1 or more AOKs from write?
    bnz    btsac01                ;yes, wait till all received

;process shutdown signals if necessary

    bbc    fbton,btsac03            ;bluetooth needs to turn off?
    bbs    shtdwn,btsac03            ;system shutting down? Then turn off bluetooth.

;is data ready to write?
  
    rcall    btwprop                ;write properties if necessary

;no other action, try again next main loop

    bra    btsac01                ;quit to next loop

;AOK received -- clear wait for AOK data write flag

btsac05    movf    aokcnt,f,1            ;awaiting 1 or more AOKs from write?
    skpz                    ;
    decf    aokcnt,f,1            ;yes decrement it

    bra    btsac01                ;and quit to next loop

;NFail received -- clear AOK wait flags

btsac06    movlw    1                ;increment nfail counter (TEST CODE)
    addwf    nfailctr,f,1
    clrw
    addwfc    nfailctr+1,f,1
    addwfc    nfailctr+2,f,1
    addwfc    nfailctr+3,f,1
      
    bsf    fnfail                ;indicate nfail received
    movlfb    aoktim,cbtslo            ;reset timer for aoks  

    bra    btsac01                ;quit to next loop

;quit and come back

btsac01    bsf    FWRAOK                ;allow AOK for properties written
    bsf    FWRNFL                ;allow NFail for properties written
    bsf    FWRCEND                ;allow connection end response
    bsf    FWRNSR                ;allow non-standard for notifications/data requests
    bsf    FWRNWR                ;don't wait for a response

    bra    btx____                ;and come right back next pass

;disconnect received -- start advertising again for seamless reconnect

btsac02    bcf    fbtconn                ;indicate connection lost
    rcall    btcboffconn            ;tell app that connection is broken
    nextst    cbtsadv                ;start advertising
    bra    btx____                ;  immediately

;module requires powerdown -- kill existing connection

btsac03    bra    btx___N                ;go immediately to next state to begin power off

;-- State 19:  start power-off sequence -- kill existing connection or stop advertising

btssd0    bbs    fbtconn,btssd01            ;connected?

;stop advertising

    txpstr    sbtkadv                ;send stop advertise command
    bra    btxEATN                ;and advance (allow error, but shouldn't happen, possible NSR if incoming data)

btssd01    bcf    fbtconn                ;killing connection, so clear it
    rcall    btcboffconn            ;tell app that connection is broken

    txpstr    sbtkill                ;send kill command
    bsf    FWRCEND                ;wait for Connection End
    bra    btxE_TN                ;wait for connection end, but allow for error JIC

;-- State 20:  put bluetooth into dormant mode

btssd1    bcf    wakehw                ;pull line low
    txpstr    sbtdorm                ;send dormant command
    bra    btx__TN                ;result will be 0x00, which we cannot parse, so just time it

;-- State 21: shutdown bluetooth and EUSART

btssd2    call    uartoff                ;turn eusart off
    bcf    wakesw                ;turn off bluetooth uart transmitter
    nextst    cbtsid                ;set up idle0 as next state
    bra    btx____                ;and immediate idle

;***** END OF STATES *********

;**** Various Exits of SM to set up next state, timeouts, and responses ****
;     E=ERR A=AOK T=short timeout L=long timeout N=advance to next state R=Allow non-standard response

btxE_TN    movlfb    btstime,cbtsto            ;STO/ERR/NEXT
btxE__N    bsf    FWRERR                ;ERR/NEXT
    bra    btx___N

btx_TRN bsf    FWRNSR                ;NSR/NEXT/STO
    bra    btx__TN

btxEATN    bsf    FWRERR                ;ERR/AOK/STO/NEXT
btx_ATN    bsf    FWRAOK                ;AOK/STO/NEXT
btx__TN    movlfb    btstime,cbtsto            ;STO/NEXT
btx___N    incf    btstate,f,1            ;NEXT
    bra    btx____

btx__TR    bsf    FWRNSR                ;STO/NSR
    bra    btx__T_

btxEAT_ bsf    FWRERR                ;ERR/AOK/STO
btx_AT_ bsf    FWRAOK                ;AOK/STO
btx__T_    movlfb    btstime,cbtsto
    bra    btx____

btx_LRN bsf    FWRNSR                ;NSR/NEXT/LTO
btx__LN    incf    btstate,f,1            ;LTO/NEXT      
btx__L_    movlfb    btstime,cbtslo            ;LTO
btx____    movlb    0                ;nothing
    return


;**** END OF SM EXITS ****

;******************************************************************
;** AOKADD -- Add wreg counts to AOK counter and reset AOK timer **
;******************************************************************

aokadd    addwf    aokcnt,f,1            ;add to aok count timer
    movlfb    aoktim,cbtsto            ;allow 200ms for count to get back to zero  
    return

;************************************************
;** AOKCK -- Check AOK counter                 **
;**       return Z if aokcnt=0                 **
;**       decrement timer every 16ms           **
;**       if expire, set AOK to 0 and return z **
;**         otherwise, return nz               **
;************************************************

aokck    movf    aokcnt,f,1            ;is aok zero?
    skpnz
    return                    ;yes, indicate it

    retbc    tc16ms                ;return nz if 16 ms expire
      
    decfsz    aoktim,f,1            ;decrement timer
    return                    ;return nz if not expired

    movlw    1                ;increment timeout counter (TEST CODE)
    addwf    wptoctr,f,1
    clrw
    addwfc    wptoctr+1,f,1
    addwfc    wptoctr+2,f,1
    addwfc    wptoctr+3,f,1
      
    clrf    aokcnt,1            ;clear aok count and set z
    return                  


;*****************************************
;** BTPRCV -- Process Bluetooth receiver**
;**   return z if no response           **
;*****************************************

btprcv    movlf    fsr1h,high btrcvbuff    ;point to receiver buffer
    movff    btrhead,fsr1l        ;  and index to next character

btplp    rcall    rxbyte            ;receiver byte available?
    retz                ; z if none

    movwf    indf1            ;save character at current position
    movlw    '\r'            ;ignore return characters
    cpfseq    indf1
    movf    postinc1,w        ;get back in w, and update pointer

    movff    fsr1l,btrhead        ;save new head position

    xorlw    '\n'            ;new character a line feed?
    bnz    btplp            ;no, get some more characters, if available  

    movlfb    btrhead,low btrcvbuff    ;reset btrhead for next message

;parse for common messages
;if common response not matched, indicate (likely) parameters returned from RN4020

    bsf    btsresp,0,1        ;set 1st bit in standard response flags

    movdf    tblptr,btsmes        ;tblptr -> possible standard responses (\n separated, \r terminated)

btplp0    lfsr    0,btrcvbuff        ;fsr0 -> point to head of response buffer
  
btplp1    tblrd    *+            ;get next character from list of responses
  
    movlw    '\r'            ;\r is response list terminating character
    xorwf    tablat,w        ;  end of response list?
    bz    btpnmf            ;yes, no response matched

    movf    tablat,w        ;get character

    cpfseq    postinc0        ;test against charcter in response buffer
    bra    btpnom            ;!= means no match, skip to next response

    movlw    '\n'            ;is it a new line character?
    xorwf    tablat,w      
    bz    btpmat            ;yes, we have found a match

    bra    btplp1            ;keep testing characters till match

;no match of current response, skip ahead till /n or /r

btpnom    clrc                ;roll to next response
    rlcf    btsresp,f,1
    rlcf    btsresp+1,f,1

btplp2    tblrd    *+            ;get next character from list of responses

    movlw    '\r'            ;get terminating character
    xorwf    tablat,w        ; end of response list?
    bz    btpnmf            ;yes, no response matched

    movlw    '\n'            ;is it a new line character?
    xorwf    tablat,w      
    bnz    btplp2            ;loop till \0 or \n

    bra    btplp0            ;and test against next response

;match found, response bit set

btpmat

;match not found, non-standard response bit set

btpnmf    clrz                ;nz indicates a new reponse has been received
    return

;********************************************
;** BTPNSR -- Parse Non-Standard Responses **
;********************************************

btpnsr    clrf    bitcnt            ;use bitcnt as index to received response

    movdf    tblptr,btnsmes        ;tblptr -> possible non-standard responses (\n separated, \0 terminated)

btpnlp0    lfsr    0,btrcvbuff        ;fsr0 -> point to head of response buffer
  
btpnlp1    tblrd    *+            ;get next character from list of responses
  
    movf    tablat,w        ;get character from list
    bz    btpnnf            ;z=end of list, not found

    movlw    '\n'            ;== \n means we have a match!
    xorwf    tablat,w
    bz    btpnm            ;go process for arguments

    movf    tablat,w        ;get character from list
    cpfseq    postinc0        ;compare with received
    bra    btpnnm            ;no match, skip to next response

    bra    btpnlp1            ;continue till match/no match  

;no match of current response, skip ahead till /0 or /n

btpnnm    incf    bitcnt,f        ;update response counter

btpnlp2    tblrd    *+            ;get next character from list of responses

    movf    tablat,w        ;get character
    bz    btpnnf            ;z=end of list -- no match
  
    xorlw    '\n'            ;is it a new line character?
    bnz    btpnlp2            ;loop till \0 or \n

    bra    btpnlp0            ;and test against next non-standard response

;no match found - what do we do?

btpnnf    return                ;for now, just ignore the response

;match found -- bit count has response number

btpnm    movfw    bitcnt            ;get response number
    call    cjump            ;and process it

    bra    btnscp            ;'ConnParam:'
    bra    btnswc            ;'WC,'
    bra    btnsnfy            ;'Notify,'
    bra    btnsind            ;'Indicate,'
    bra    btnswv            ;'WV,'
    bra    btnsrv            ;'RV,'

;ConnParam <hex16>,<hex16>,<hex16> (connection interval, slave latency, supervison timeout)

btnscp    lfsr    1,conint+1        ;point to holders
    rcall    hex2binb        ;capture interval
    rcall    hex2binb        ;capture slave latency
    rcall    hex2binb        ;capture supervision timeout
    return

;WC,<hex16>,<hex16> (write configuration to local server characteristic)

btnswc    movlw    1            ;AOK will be sent by successful write to c prop -- UNDOCUMENTED!
    rcall    aokck

    rcall    rxhandle        ;capture first argument as handle
    rcall    btsprop            ;btcharn = matching property number or zero for battery service
    movf    postinc0,f        ;skip trailing comma
    lfsr    1,btint16+1        ;store in int16 holder
    rcall    hex2binb        ;capture new configuration: 0x0000 stop notification, 0x0100 start notification

    movf    btcharn,f,1        ;battery service?
    skpz                ;  private characteristic
    decf    btcharn,f,1        ;  get handle # for PC value

    clrf    temp0            ;clear bit locater
    clrf    temp1

    setc                ;bit to roll in

btnsw0    rlcf    temp0,f            ;roll to next characterisitic
    rlcf    temp1,f

    movf    btcharn,f        ;found bit?
    bz    btnsw1            ; no
  
    decf    btcharn,f        ;try next
    clrc
    bra    btnsw0

;temp1:0 has bit for this characteristic

btnsw1    movf    btint16+1,f        ;stopping notifications?
    bz    btnsw2            ;  yes

    movfw    temp0            ;starting: merge bit into mask
    iorwf    btwmask,f,1
    movfw    temp1
    iorwf    btwmask+1,f,1
    return                ;done!

;stopping notification -- clear bit

btnsw2    comf    temp0,w
    andwf    btwmask,f,1
    comf    temp1,w
    andwf    btwmask+1,f,1          
    return

;WV,<hex16>,<variable> (write value to local server characteristic)

btnswv    rcall    rxhandle        ;capture first argument as handle
    rcall    btsprop            ;btcharn = matching property number or zero for battery service
    movf    postinc0,f        ;skip trailing comma

    movf    btcharn,f,1        ;handle should not be battery property
    retz                ;just get out
    movlw    11            ;10 characteristics, max
    cpfslt    btcharn,1  
    return                ;do nothing if not a sane value

    decf    btcharn,w        ;0 index to private services
    call    cjump            ;and callback to process received value

    bra    btcbpr1  
    bra    btcbpr2
    bra    btcbpr3
    bra    btcbpr4
    bra    btcbpr5
    bra    btcbpr6
    bra    btcbpr7
    bra    btcbpr8
    bra    btcbpr9
    bra    btcbprA  

;other non-standard responses -- for later -- just indicate unhandled for now

;notification received
btnsnfy                    ;'Notify,'

;indication received
btnsind                    ;'Indicate,'

;real time read (bit must be set in supported features (SR)
btnsrv                    ;'RV,'

    bra    btpnnf            ;indicate unhandled non-standard response

;************************************************
;** BTGI16 -- Get 2 bytes of data into BTSDATA **
;************************************************

btgi16    lfsr    1,btsdata

;*************************************************
;** BTGPI16 -- Get 2 bytes of data into fsr->RAM **
;*************************************************

btgpi16    movlw    2
    bra    hex2binl

;************************************************
;** BTGI32 -- Get 4 bytes of data into BTSDATA **
;************************************************

btgi32    lfsr    1,btsdata

;*************************************************
;** BTGPI32 -- Get 2 bytes of data into fsr->RAM **
;*************************************************

btgpi32    movlw    4

;***************************************************************
;** HEX2BINL -- Convert wreg bytes of hex to binary           **
;**             String in Little Endian Format                **
;**   store result at fsr1 -> RAM (low byte)                  **
;**   '.' or \n terminated                                    **
;**   target RAM must be large enough to hold expected value  **
;***************************************************************
  
hex2binl

    movwf    bitcnt            ;save number of bytes

;process high byte first

h2bllp    movf    postinc0,w        ;get high character
    addlw    -10            ;is it a new line?
    retz                ;yes, we are done
    addlw    -36            ;is it a '.'?
    retz                ;yes, we are done
                    ;'0'-'9' = 2 - 11, 'A'-'F' = 19 - 24
    addlw    -12            ;'0'-'9' = -10 - (-1) (nc), 'A'-'F' = 7-12 (c)
    skpnc                ;adjust for 'A'-'F'
    addlw    -7            ;'A'-'F'=0-5
    addlw    10            ;this is my final answer
    swapf    wreg,f            ;swap for high byte
    movwf    indf1            ;and save

;then low byte (no need to check for terminators on odd characters)

    movf    postinc0,w        ;get low character
    addlw    -58            ;'0'-'9' = -10 - (-1) (nc), 'A'-'F' = 7-12 (c)
    skpnc                ;adjust for 'A'-'F'
    addlw    -7            ;'A'-'F'=0-5
    addlw    10            ;this is my final answer
    iorwf    postinc1,f        ;merge in

    djnz    bitcnt,h2bllp

    return        ;do for all characters

;***************************************************************
;** HEX2BINB -- Convert arbitrary length hex to binary        **
;**             String in Big Endian Format                   **
;**   store result at fsr1 -> RAM (high byte)                 **
;**   ignore spaces, period, comma or \n terminated           **
;**   target RAM must be large enough to hold expected value  **
;***************************************************************
  
hex2binb

;process high byte first

    movf    postinc0,w        ;get next character
    addlw    -10            ;is it a new line?
    retz                ;yes, we are done
    addlw    -22            ;is it a space?
    bz    hex2binb        ;yes, ignore it
    addlw    -12            ;is it a comma?
    retz                ;yes, we are done
    addlw    -2            ;is it a '.' (damn documentation!)
    retz                ;yes, it is a terminator (he'll be back!)
                    ;'0'-'9' = 2 - 11, 'A'-'F' = 19 - 24
    addlw    -12            ;'0'-'9' = -10 - (-1) (nc), 'A'-'F' = 7-12 (c)
    skpnc                ;adjust for 'A'-'F'
    addlw    -7            ;'A'-'F'=0-5
    addlw    10            ;this is my final answer
    swapf    wreg,f            ;swap for high byte
    movwf    indf1            ;and save

;then low byte (no need to check for terminators on odd characters)

    movlw    58
    subwf    postinc0,w        ;'A'-'F' = 7 - 12, '0'-'9' = -10 - -1
    skpc                ;correct for 0-9
    addlw    7            ;'0'-'9' = -3 - 6
    addlw    3            ;final result
    iorwf    postdec1,f        ;merge in
    bra    hex2binb        ;do for all characters
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
cont'd:

Code:
;********************************************************************
;** RXPCSTR -- Compare received string with program memory         **
;**   program memory string terminated with \r                     **
;**   tblptr -> string                                             **
;**   ignore leading characters in PM until first comma            **
;**   ignore leading spaces in RAM                                 **
;**   z if match, nz if no match                                   **
;**   if fbtrpc=1, pc handle is captured and pc properties checked **
;**   if fbtcaph is set, handle for char #(bthctr) is captured     **
;**   on exit, if fbtcapp=1, line must be rescanned for property   **
;********************************************************************

_rxpcstr

    btfsc    fbtrpc            ;are we scanning a private characteristic?
    bsf    fbtcaph            ;yes, we must capture the handle

    lfsr    0,btrcvbuff        ;point to bluetooth receive buffer

    movlw    ','            ;skip to character after first comma
rxpclp0    tblrd    *+
    cpfseq    tablat
    bra    rxpclp0
   
rxpclp1    movlw    ' '            ;ignore leading spaces in RAM
    xorwf    postinc0,w
    bz    rxpclp1

    movf    postdec0,w        ;move pointer back to first non-space character

rxpclp2    tblrd    *+            ;get character from program memory
    movlw    '\r'            ;terminates with /r
    xorwf    tablat,w        ;compare
    bz    rxpcbx            ;return with z to indicate match

    bbc    fbtrpc,rxpcb0        ;scanning a private characteristic?

    bbc    fbtcaph,rxpcb0        ;capture handle flag set?
    movlw    ','            ;handle starts at first comma
    xorwf    indf0,w            ;compare
    bnz    rxpcb0            ;scan till first comma

;first comma found, parse handle for this characteristic

    rcall    rxpch            ;go capture handle

;adjust for notifiers - match comma

    movf    indf0,w            ;current character should be comma
    movfw    tablat            ;get pm character
    xorwf    postinc0,w        ;compare with received character
    bnz    rxpcbx            ;no match, reload

;check for match of high nibble of characteristic properties

    tblrd    *+            ;indext to next program character
    movfw    tablat            ;get pm character
    xorwf    indf0,w            ;compare with current ram character
    bz    rxpcb1            ;match.  check second character

;if current RAM byte is non-zero, we are scanning properties

    movlw    '0'
    xorwf    indf0,w            ;compare z=value, nz=char
    skpnz                ;if we are scanning characteristic property, proceed as normal
    bsf    fbtcapp            ;otherwise, we must recheck line for property

;check for match of low nibble of characteristic properties

rxpcb1    movf    postinc0,w        ;skip to next character

;******** TO DO! compare notify/indicate bit to reload if they change

    tblrd    *+            ;get next program character
    movfw    tablat            ;get rom character
    xorwf    indf0,w            ;compare with current ram character
    bz    rxpcb2            ;continue normally if match (first line, fbtcapp is set)

;second line of notified characteristic -- ensure ftbcapp and char '0'

    bbc    fbtcapp,rxpcbx        ;fbtcapp should be set here, if not, we've got a mismatch

    bcf    fbtcapp            ;clear fbtcapp for next characteristic

    movlw    '0'            ;ram byte should be zero
    xorwf    indf0,w            ;compare z=value, nz=char
    bra    rxpcbx            ;z=string matched (ignore last parameter), nz=mismatched.

;first line of characteristic -- scan normally
   
rxpcb2    movf    postinc0,w        ;to next character
    bra    rxpclp2            ;continue scanning normally

;just normal scan here

rxpcb0    movf    indf0,w
    movfw    tablat            ;get pm character
    xorwf    postinc0,w        ;compare with received character
    bz    rxpclp2            ;match, keep going

rxpcbx    bcf    fbtrpc            ;clear private characteristic indication

    return                ;return with no match/no match

;capture handle at current ram position +1

rxpch    bcf    fbtcaph            ;clear capture flag for any remaining commas
   
    lfsr    1,hpchar1        ;fsr1->start of characteristic handle table

    rlncf    bthctr,w,1        ; *4 (size of handle)
    rlncf    wreg,w

    addwf    fsr1l,f            ;fsr1->proper handle

    movff    preinc0,postinc1    ;copy 4 bytes
    movff    preinc0,postinc1
    movff    preinc0,postinc1
    movff    preinc0,postinc1
       
    movf    postinc0,f        ;index to tailing comma

    return                ;and go finish scan

;******************************************************
;** BTWPROP -- Write property value if indicated     ** 
;**     btwrite has one or more properties           **
;**        bit 0 for battery service                 **
;**        bit 1 through cnumcar for private service **
;**     format out: "SHW,hhhh,v..."                  **
;**     callback to get value                        **
;******************************************************

btwprop    retbc    fbtconn                ;no writes if not connected

    movfwb    btwrite                ;properties to write?

    andlw    b'11111110'            ;RN4020 bug fix, ignore battery service

    iorwf    btwrite+1,w,1            ; two sets
    bnz    btwp1                ;no, get out

    bbs    btwrite,0,1,btwp1        ;RN4020 bug fix, process battery service last

    movfwb    btwbuff                ;data in waiting?
    iorwf    btwbuff+1,w,1
    retz                    ;nope, nothing to do

    movff    btwbuff,btwrite            ;set new data to write
    movff    btwbuff+1,btwrite+1

    clrf    btwbuff,1
    clrf    btwbuff+1,1

    return                    ;RN4020 bug fix, process next loop

;find next data item to send

btwp1    movlw    1
    addwf    wpropctr,f,1
    clrw
    addwfc    wpropctr+1,f,1
    addwfc    wpropctr+2,f,1
    addwfc    wpropctr+3,f,1

    movlf    temp1,1                ;start at bit0   
    clrf    temp2

    clrf    bthctr,1            ;will hold property number

;find the next property who's value requires writing

btwplp0    movfwb    btwrite                ;test a bit
    andwf    temp1,w                ;got a hit?
    bnz    btwp0                ;yes, go send it

    movfwb    btwrite+1            ;test a bit
    andwf    temp2,w                ;got a hit?
    bnz    btwp0                ;yes, go send it

    clrc                    ;shift bit mask
    rlcf    temp1,f
    rlcf    temp2,f

    incf    bthctr,f,1            ;increment property number
    bra    btwplp0                ;and check next

;bthctr has property number to write
;temp2:1 had bitmask

;test if non or notified write

btwp0    movfw    temp1
    andwf    btwmask,w,1            ;nz if notified
    bnz    btwp2

    movfw    temp2
    andwf    btwmask+1,w,1
    skpnz

btwp2    movlw    1                ;no notification = 1 AOK
    skpz
    movlw    2                ;notification = 2 AOKs
    rcall    aokadd   

    comf    temp1,w                ;invertmask
    andwf    btwrite,f,1            ;and clear bit for next pass

    comf    temp2,w                ;invertmask
    andwf    btwrite+1,f,1            ;and clear bit for next pass

    decf    bthctr,f,1            ;get bthctr-1
    bc    btwpps                ; c=private service

;bthctr was 0 -- send battery service

    nop

    txpstr    sbtbsh                ;send battery service handle
    call    btcbwbl                ;go write actual value in callback
    bra    ptwpx                ;exit adding trailing return

;enumerated private services (up to 10)
   
btwpps    txpstr    sbtshw                ;send "SHW," to begin property write

    lfsr    1,hpchar1            ;point first characteristic handle

    rlncf    bthctr,w            ;double count
    rlncf    wreg,w                ;  4 bytes per entry

    addwf    fsr1l,f                ; and index

    call    txpbyte                ;send 4 characters of handle
    call    txpbyte
    call    txpbyte
    call    txpbyte

    movlw    ','
    call    txbyte                ;send a comma to separate from value

    switch    bthctr,1,ptwpx            ;make proper callback and exit with trailing \r

    goto    btcbps1                ;each must send a value to TX
    goto    btcbps2
    goto    btcbps3
    goto    btcbps4
    goto    btcbps5
    goto    btcbps6
    goto    btcbps7
    goto    btcbps8
    goto    btcbps9
    goto    btcbpsA   
               
;add trailing return character to complete command

ptwpx    movlw    '\r'
    goto    txbyte            ;send and return

;************************************************
;** BTSI8 -- send INT8 in btsdata to bluetooth **
;************************************************

btsi8    lfsr 1,btsdata            ;point to btsdata

;********************************************
;** BTSPI8 -- send fsr1->INT8 to bluetooth ** 
;********************************************
   
btspi8    movlw    1            ;one byte
    bra    btpssd            ;write the data

;**************************************************
;** BTSI16 -- send INT16 in btsdata to bluetooth **
;**************************************************

btsi16    lfsr    1,btsdata        ;point to btsdata

;**********************************************
;** BTSPI16 -- send fsr1->INT16 to bluetooth ** 
;**********************************************
   
btspi16    movlw    2            ;two bytes to send
    bra    btpssd            ;and write data

;************************************************
;** BTSFLS -- send float on stack to bluetooth **
;************************************************

btsfls    popfl    btsdata            ;get float into btsdata
   
;*******************************************************
;** BTS32 -- send 4 bytes of binary data to bluetooth **
;**   btsdata has data to send                        **
;*******************************************************

bts32    movlw    4            ;four bytes

;***********************************************************
;** BTSSD -- send wreg bytes of binary data to bluetooth  **
;**   btsdata has data to send                            **
;***********************************************************

btssd    lfsr    1,btsdata        ;point to btsdata

;***********************************************************
;** BTPSSD -- send wreg bytes of binary data to bluetooth **
;**           data sent **little endian**                 **
;**   fsr1->data                                          **
;***********************************************************

btpssd    movwf    bitcnt            ;save number of bytes

btssdl1    swapf    indf1,w            ;high nibble first
    rcall    tbc2hex            ;transmit ASCII
    movf    postinc1,w        ;then low nibble
    rcall    tbc2hex            ;transmit ASCII
   
    djnz    bitcnt,btssdl1        ;do for all bytes

    return                ;and return
   
;********************************************************
;** BTTHEX -- Convert low nibble of WREG to ASCII HEX  **
;**           and transmit to bluetooth
;********************************************************

tbc2hex    andlw    b'1111'            ;mask lower nibble
    addlw    -10            ;0-9=-10 to (-1) (nc), A-F=0 to 5 (c)
    skpnc                ;A-F?
    addlw    7            ;A-F = 7 to 12
    addlw    58            ;0-9=48-57,A-F=65-70 ('0'-'9','A'-'F')
    goto    txbyte            ;and send it

;custom app files

    include "btcb.asm"        ;include custom callbacks to main program
    include    "btstrings.asm"        ;include command/response strings

;********************************************************
;** RXHANDLE -- Copy 4 byte handle from fsr0 to bthndl **
;********************************************************

rxhandle

    movff    postinc0,bthndl        ;copy 4 bytes
    movff    postinc0,bthndl+1
    movff    postinc0,bthndl+2
    movff    postinc0,bthndl+3

    return

;**************************************************************
;** BTSPROP -- search for property number with handle bthndl **
;**    not found, return 0 (battery property)                **
;**    otherwise, return 1 indexed handle number             **
;**************************************************************

btsprop    movlfb    bthctr,10        ;10 possible entries

btsplp0    lfsr    1,hpchar1        ;point to first entry

    movfwb    bthctr            ;get current count
    sublw    10            ;0, 1, ... 9

    rlncf    wreg,w            ;double it
    rlncf    wreg,w            ;again
    addwf    fsr1l,f            ;fsr1->next entry

    movfw    postinc1        ;get 1st character
    xorwf    bthndl,w,1        ;test
    bnz    btspne            ;not equal, try next   

    movfw    postinc1        ;get 1st character
    xorwf    bthndl+1,w,1        ;test
    bnz    btspne            ;not equal, try next   

    movfw    postinc1        ;get 1st character
    xorwf    bthndl+2,w,1        ;test
    bnz    btspne            ;not equal, try next   

    movfw    postinc1        ;get 1st character
    xorwf    bthndl+3,w,1        ;test
    bz    btspeq            ;equal, found a match   

btspne    djnz    bthctr,btsplp0        ;try for all 10

    clrf    btcharn,1        ;return with 0 for battery service
    return

btspeq    movfwb    bthctr            ;10=1, 9=2, ..., 1=10
    sublw    11            ;wreg = 11 - bthctr
    movwf    btcharn,1        ;save it
    return
 

NorthGuy

Joined Jun 28, 2014
611
I'll try that again.

When you write a number, it's always what you call a "big endian", format. If I write "25", everyone will understand it as twenty five, and no one will interpret it is fifty two. Hexadecimal numbers (in C or any other uses) follow this rule. Would you expect this to be different?

Handles are examples of numbers.

Entities which are not numbers, are always interpreted left-to-right. E.g. word "hello" is 68656C6C6F00. Would you expect this to be different?

Characteristics are examples of non-numbers. They're arbitrary pieces of information, which can hold anything depending of what their creator wants - numbers, strings, flags, records etc. Interpreting them as numbers could be counterproductive.

If you're still in doubts, look at the HEX files - you should be familiar with the format as it is used for programming PICs (and not only PICs). Look how addresses (numbers) are encoded and how data (non-numbers) are encoded. They are combined within the same line!!! If you start to apply the "big endian" or "little endian" notions here, it will look very inconsistent. However, these notions do not apply here - this is just a historically natural and widely accepted way of writing text. Although it is possible to do it differently, it would be confusing.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
I'll try that again.

When you write a number, it's always what you call a "big endian", format. If I write "25", everyone will understand it as twenty five, and no one will interpret it is fifty two. Hexadecimal numbers (in C or any other uses) follow this rule. Would you expect this to be different?

Handles are examples of numbers.

Entities which are not numbers, are always interpreted left-to-right. E.g. word "hello" is 68656C6C6F00. Would you expect this to be different?

Characteristics are examples of non-numbers. They're arbitrary pieces of information, which can hold anything depending of what their creator wants - numbers, strings, flags, records etc. Interpreting them as numbers could be counterproductive.

If you're still in doubts, look at the HEX files - you should be familiar with the format as it is used for programming PICs (and not only PICs). Look how addresses (numbers) are encoded and how data (non-numbers) are encoded. They are combined within the same line!!! If you start to apply the "big endian" or "little endian" notions here, it will look very inconsistent. However, these notions do not apply here - this is just a historically natural and widely accepted way of writing text. Although it is possible to do it differently, it would be confusing.
I know exactly what you are saying (and I know you are right). My annoyance is the UUIDs, for example, are actually 128 bit numbers, but they are sent high byte first in a string to the RN4020. Handles are just 32 bit short names for the UUIDs. For consistency, they should be sent low byte first, just like if you were sending values. This way, you don't need two different hex2bin and bin2hex routines depending on whether you are dealing with the RN4020 or central. My 2 cents.
 

NorthGuy

Joined Jun 28, 2014
611
For consistency, they should be sent low byte first, just like if you were sending values. This way, you don't need two different hex2bin and bin2hex routines depending on whether you are dealing with the RN4020 or central. My 2 cents.
This is because you're on a little-endian CPU (although you can chose your own endianness or a mix of them). The guy on a big-endian CPU you would need only one hex2bin and bin2hex routines. If the order of digits in handles was reversed, you would need one set of hex2bin/bin2hex routines, but the guy on a big-endian CPU would need two sets.

<edit>Although the big-endian guy would also need two sets if the values of characteristics were numbers coming from a little-endian host.</edit>

I didn't look through all of your code, but if the only operation with handles is a comparison to determine if they're equal or non-equal, you can store the handles in big-endian format. This way you can get away with a single set of bin2hex/hex2bin routines which will work both for handles and values.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
Let's be clear, there are other data that need to be communicated between the RN4020 and the MCU, and they are all high-byte first (big E). Early on, I decided to code for speed of development vs. RAM/ROM space, so I stored all UUIDs and handles as strings. So this wasn't a problem in that respect. If I had chosen to store UUIDs and handles as binary, I would have quickly recognized this issue.

Here is the bottom line: any communication between the MCU and RN4020 is done Big E. Anything between the MCU and the Android/app must be done in a form recognizable to the app.

I am not writing the app. I assume the Android class library expects Little E. Therefore, I need Big E routines for communicating with the RN4020, and Big E or Little E routines for dealing with the app (Little E is general convention).

I know why the developers of the RN4020 did this. It is so you can attach the 4020 to a dumb terminal and test your commands without having to mentally process the byte reversals. But I was annoyed by the long form to begin with. Who'd want to talk to a chip in this way?

With all that said, I was looking for just one more thing to bitch about before putting this project to bed. It's done, and so am I.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,283
Understand my frustration, @NorthGuy. After 16 straight solid 12 to 15 hour days of interpreting God-awful documentation, a buggy chip, coding, and testing, it is easy to forget what the chip sees, and what the world sees. Of course I understand the world expects little E, but I only started communicating with it in a real way about 12 hours ago after ~200 hours of talking big E. And don't check my math. I didn't.
 
Top