Microchip RN4020 Bluetooth LE

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
BTW, this is turning into the hardest thing I've ever done. But only because the documentation absolutely sucks. And I have to discover things (things which should be clearly documented) along the way...
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Here's where the code stands now. If you look at state bts2, you'll see that there is actually a "sub" state machine running within the main state machine. I did this because I found that, often, a series of commands must be sent, each requiring an 'AOK' response. So I just gathered them all into one state of the main machine.

Code:
;Bluetooth Flags

#define    fbtinit    btflag0,0,1        ;1=bluetooth initialized
;#define     btflag0,1,1        ;
;#define     btflag0,2,1        ;
;#define     btflag0,3,1        ;
;#define     btflag0,4,1        ;
;#define     btflag0,5,1        ;
;#define     btflag0,6,1        ;
;#define     btflag0,7,1        ;

;standard response flags

#define    FSRAOK    btsresp,0,1        ;"AOK"
#define    FSRERR    btsresp,1,1        ;"ERR"
#define    FSRBOND    btsresp,2,1        ;"BONDED"
#define    FSRSEC    btsresp,3,1        ;"SECURED"
#define    FSRCEND    btsresp,4,1        ;"CONNECTION END"
#define    FSRRBT    btsresp,5,1        ;"REBOOT"
#define FSRZCMD    btsresp,6,1        ;"\0CMD" (reply when reboot complete)
#define    FSRCON    btsresp,7,1        ;"Connected"

#define    FSRNSR    btsresp+1,0,1        ;non-standard response received
;#define     btsresp+1,1,1        ;
;#define     btsresp+1,2,1        ;
;#define     btsresp+1,3,1        ;
;#define     btsresp+1,4,1        ;
;#define     btsresp+1,5,1        ;
;#define     btsresp+1,6,1        ;
;#define     btsresp+1,7,1        ;

;wait for standard response flags

#define    FWRAOK    btswait,0,1        ;"AOK"
#define    FWRERR    btswait,1,1        ;"ERR"
#define    FWRBOND    btswait,2,1        ;"Bonded"
#define    FWRSEC    btswait,3,1        ;"Secured"
#define    FWRCEND    btswait,4,1        ;"Connection End"
#define    FWRRBT    btswait,5,1        ;"Reboot"
#define FWRZCMD    btswait,6,1        ;"\0CMD" (reply when reboot complete)
#define    FWRCON    btswait,7,1        ;"Connected"

#define    FWRNSR    btswait+1,0,1        ;non-standard response received
;#define    btswait+1,1,1        ;
;#define    btswait+1,2,1        ;
;#define    btswait+1,3,1        ;
;#define    btswait+1,4,1        ;
;#define    btswait+1,5,1        ;
;#define    btswait+1,6,1        ;
;#define    btswait+1,7,1        ;
Code:
;*************************************
;** BTININT -- Initialize Bluetooth **
;*************************************

initbt
    BANKSEL    btstate            ;select bluetooth working bank         

    bsf    wakesw            ;wake bluetooth
    bsf    wakehw 

    bra    ibtex            ;exit restoring bank


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

btprcv    retbc    fbtinit            ;don't receive if in initialzation

    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,btcmes        ;tblptr -> possible 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

    xorlw    '\n'            ;new line character?
    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

btcmes    db    "AOK\n","ERR\n","Bonded\n","Secured\n","Connection End\n","Reboot\n","\x00CMD\n","Connected\n",'\r'


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

pollbt
    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

;a response has been received from bluetooth

    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    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    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    ibtex            ;no further processing if timer not expired

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

;otherwise, flag a timeout error

    bra    pbtrto            ;show timeout error

;not a timed function

pbtntim    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    bz    pbstate            ;no, just process timed state after delay

    bra    ibtex            ;or wait until timer expires
 
;Handle improper response

pbtimp    bra    ibtex            ;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    ibtex            ;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    ibtex            ;ignore erroneous responses

;process Bluetooth state machine

pbstate    clrf    btswait,1        ;clear waiting flags for next pass
    clrf    btstime,1        ;and state timer
 
    movfw    btstate
    call    cjump 

    bra    bts0            ;state 0:  await BTAWAKE hardware line 
    bra    bts1            ;state 1:  send feature string
    bra    bts2            ;state 2:  setup and reboot
    bra    bts3            ;state 4:  wait for reboot to complete
    bra    bts4            ;state 5:  start advertising
    bra    bts5            ;state 6:  await connection
    bra    bts6            ;state 7:  await disconnection

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

;-- State 0:  await BTAWAKE hardware line, then wait 100ms

bts0    bbc    btawake,ibtex            ;exit if BTAWAKE not asserted

    movlfb    btstime,clctim(t16ms,115)    ;set up 100 ms timer
    bra    ibtns                ;and get out

;-- State 1: clear receive buffer, then send feature string

bts1    call    rxbyte                ;data in receive buffer?
    bnz    bts1                ;yes, keep reading till clear

    bsf    fbtinit                ;indicate initialized

    txpstr    sbtfs                ;send feature settings string
    bra    ibtexat

;-- State 2: setup prior and reboot

bts2    movfw    btssubst            ;get substate counter
    call    cjump

    bra    bts2a                ;set friendly name
    bra    bts2b                ;set model
    bra    bts2c                ;set manufacturer name
    bra    bts2d                ;clear private services
    bra    bts2e                ;set server services
    bra    bts2f                ;set private service UUID
    bra    bts2g                ;set private service characteristics
    bra    bts2h                ;reboot
 
bts2a    txpstr    sbtfn                ;set friendly name
    bra    ibssx
bts2b    txpstr    sbtmod                ;set model name
    bra    ibssx
bts2c    txpstr    sbtmnm                ;set manufacturer name
    bra    ibssx
bts2d    txpstr    sbtcps                ;clear private services
    bra    ibssx
bts2e    txpstr    sbtstsrv            ;set server services
    bra    ibssx
bts2f    txpstr    sbtpsuid            ;set private service UUID
    bra    ibssx
bts2g    txpstr    sbtpscar            ;set private service characteristic
    bra    ibssx                ;  and index to next state

bts2h    txpstr    sbtres                ;send reset
    bsf    fwrrbt                ;wait for 'Reboot'
    bra    ibtext

;-- State 3: wait up to 3,200 ms for reboot sequence

bts3    bsf    fwrzcmd                ;wait for '\0CMD'
    movlfb    btstime,clctim(t16ms,3212)    ;up to 3212ms allowed for response 
    bra    ibtns

;-- State 4: start advertisement

bts4    movlb    0
    call    bloff                ;turn off for disconnected
    BANKSEL    btstate                ;select bluetooth working bank         

    txpstr    sbtadv                ;send start advertisement
    bra    ibtexae                ;and allow ERR in case already advertising

;-- State 5:  await connection

bts5    bsf    FWRCON                ;await connection
    bra    ibtns

;-- State 6:  await disconnection

bts6    movlb    0
    call    blon                ;turn on backlight to show we are connected
    BANKSEL    btstate                ;select bluetooth working bank         

    bsf    FWRCEND                ;await disconnection
    movlfb    btstate,4            ;restart advertisement when disconnected

    bra    ibtex

;set up next state with AOK/ERR response and 200ms timeout

ibtexae    bsf    FWRERR                ;wait for 'ERR'

;set up next state with AOK response and 200ms timeout

ibtexat    bsf    FWRAOK                ;wait for 'AOK'

;set up next state with 200 ms timeout

ibtext    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response

;set up next state

ibtns    incf    btstate,f,1            ;and skip to next state on completion

;restore BSR and quit till next pass

ibtex    movlb    0
    return

;set up next substate with AOK, and 200ms

ibssx    bsf    FWRAOK                ;wait for 'AOK'
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    incf    btssubst,f,1            ;go to next substate
    movlb    0                ;restore bsr
    return                    ;and get out

;** Strings **

sbtfg    db    "gr\r\0"            ;get features
sbtfs    db    "sr,00060000\r\0"        ;no auto-advertise, no authentication
sbtfn    db    "s-,WECoyote\r\0"        ;set friendly name
sbtmod    db    "sdm,AAC1000\r\0"        ;set model
sbtmnm    db    "sdn,Acme Products\r\0"        ;set manufacturer name
sbtres    db    "r,1\r\0"            ;reset
sbtadv    db    "A\r\0"                ;advertise
sbtunb    db    "U\r\0"                ;unbond

sbtcps    db    "PZ\r\0"                    ;clear private services
sbtstsrv db    "SS,40000001\r\0"                ;allow private services
sbtpsuid db    "PS,1000EEE0000000000000000000000000\r\0"    ;set private service UUID
sbtpscar db    "PC,1000EEE1000000000000000000000000,02,06\r\0"    ;add private service characteristic UUID
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
And here's a ram dump for the above code:

Fileregs.jpg

Edit: 0x600 is the EUSART receive buffer, 0x700 is the transmit buffer. The command is transmitted first, followed by a received response.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
What I want to know is what genius at Microchip thought is was a good idea to 'talk' to a chip in English?

Ok, so it makes it easy to communicate via terminal for development...but, dammit (!) give me a mode where I can switch it to talk binary.
 

GopherT

Joined Nov 23, 2012
8,009
I'm happy to know its here when I need it. I have an HC-06 from eBay that seems to operate with similar commands.

I was going to make a comment about the ASM vs C but I knew that was like trying to talk my kids into eating candy instead of vegetables... Whoa, wait a minute. I think I got that right but.. Never mind.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Ok...I can now parse non-standard responses. Code below. There is only one non-standard response right now -- "ConnParam:'. It's easy to add more and are enumerated according to the list at btnsmes. Once a non-standard response is found, a jump table is then used to determine how many, and what kind, of arguments to capture. In this case, it is 3 <hex16>s, in the format of 0123,4567,89AB.

BTW, some responses include a space between the response and the arguments, other's don't. And some responses are only arguments with no contextual text. This chip is annoying...

I am not sure if anyone noticed, but I use a few macros here and there to make coding easier. I haven't published those. So, if you see a weird mnemonic, it's a macro. Sorry.

Oh, and the private characteristic setup is commented out. I cannot figure out what is wrong with nRF. I've even managed to crash my Android a few times. The issue is definitely with the app, not my code. Really.

Code:
;*************************************
;** BTININT -- Initialize Bluetooth **
;*************************************

initbt
    BANKSEL    btstate            ;select bluetooth working bank       

    bsf    wakesw            ;wake bluetooth
    bsf    wakehw

    bra    ibtex            ;exit restoring bank


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

btprcv    retbc    fbtinit            ;don't receive if in initialzation

    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

;standard response messages -- list is \r terminated (so we can include 0x00 for reboot)

btsmes    db    "AOK\n","ERR\n","Bonded\n","Secured\n","Connection End\n","Reboot\n","\x00CMD\n","Connected\n","NFail\n",'\r'


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

pollbt
    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

;a response has been received from bluetooth

    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    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    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    ibtex            ;no further processing if timer not expired

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

;otherwise, flag a timeout error

    bra    pbtrto            ;show timeout error

;not a timed function

pbtntim    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    ibtex            ;otherwise, just wait until response

;Handle improper response

pbtimp    btfsc    fsrnsr            ;non-standard response?
    rcall    btpnsr            ;go parse it

    bra    ibtex            ;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    ibtex            ;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    ibtex            ;ignore erroneous responses

;process Bluetooth state machine

pbstate    clrf    btswait,1        ;clear waiting flags for next pass
    clrf    btstime,1        ;and state timer

    movfw    btstate
    call    cjump

    bra    bts0            ;state 0:  await BTAWAKE hardware line
    bra    bts1            ;state 1:  send feature string
    bra    bts2            ;state 2:  setup and reboot
    bra    bts3            ;state 4:  wait for reboot to complete
    bra    bts4            ;state 5:  start advertising
    bra    bts5            ;state 6:  await connection
    bra    bts6            ;state 7:  await disconnection

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

;-- State 0:  await BTAWAKE hardware line, then wait 100ms

bts0    bbc    btawake,ibtex            ;exit if BTAWAKE not asserted

    movlfb    btstime,clctim(t16ms,115)    ;set up 100 ms timer
    bra    ibtns                ;and get out

;-- State 1: clear receive buffer, then send feature string

bts1    call    rxbyte                ;data in receive buffer?
    bnz    bts1                ;yes, keep reading till clear

    bsf    fbtinit                ;indicate initialized

    txpstr    sbtfs                ;send feature settings string
    bra    ibtexat

;-- State 2: setup prior and reboot

bts2    movfw    btssubst            ;get substate counter
    call    cjump

    bra    bts2a                ;set friendly name
    bra    bts2b                ;set model
    bra    bts2c                ;set manufacturer name
    bra    bts2d                ;clear private services
;    bra    bts2e                ;set server services
;    bra    bts2f                ;set private service UUID
;    bra    bts2g                ;set private service characteristics
    bra    bts2ga                ;unbond
    bra    bts2h                ;reboot

bts2a    txpstr    sbtfn                ;set friendly name
    bra    ibssx
bts2b    txpstr    sbtmod                ;set model name
    bra    ibssx
bts2c    txpstr    sbtmnm                ;set manufacturer name
    bra    ibssx
bts2d    txpstr    sbtcps                ;clear private services
    bra    ibssx
bts2e    txpstr    sbtstsrv            ;set server services
    bra    ibssx
bts2f    txpstr    sbtpsuid            ;set private service UUID
    bra    ibssx
bts2g    txpstr    sbtpscar            ;set private service characteristic
    bra    ibssx                ;  and index to next state
bts2ga    txpstr    sbtunb                ;unbond
    bsf    FWRERR                ;'ERR' is ok if not bonded
    bra    ibssx                ;  and index to next state

bts2h    txpstr    sbtres                ;send reset
    bsf    fwrrbt                ;wait for 'Reboot'
    bra    ibtext

;-- State 3: wait up to 3,200 ms for reboot sequence

bts3    bsf    fwrzcmd                ;wait for '\0CMD'
    movlfb    btstime,clctim(t16ms,3212)    ;up to 3212ms allowed for response
    bra    ibtns

;-- State 4: start advertisement

bts4    movlb    0
    call    bloff                ;turn off for disconnected
    BANKSEL    btstate                ;select bluetooth working bank       

    txpstr    sbtadv                ;send start advertisement
    bra    ibtexae                ;and allow ERR in case already advertising

;-- State 5:  await connection

bts5    bsf    FWRCON                ;await connection
    bra    ibtns

;-- State 6:  await disconnection

bts6    movlb    0
    call    blon                ;turn on backlight to show we are connected
    BANKSEL    btstate                ;select bluetooth working bank       

    bsf    FWRCEND                ;await disconnection
    movlfb    btstate,4            ;restart advertisement when disconnected

    bra    ibtex

;set up next state with AOK/ERR response and 200ms timeout

ibtexae    bsf    FWRERR                ;wait for 'ERR'

;set up next state with AOK response and 200ms timeout

ibtexat    bsf    FWRAOK                ;wait for 'AOK'

;set up next state with 200 ms timeout

ibtext    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response

;set up next state

ibtns    incf    btstate,f,1            ;and skip to next state on completion

;restore BSR and quit till next pass

ibtex    movlb    0
    return

;set up next substate with AOK, and 200ms

ibssx    bsf    FWRAOK                ;wait for 'AOK'
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    incf    btssubst,f,1            ;go to next substate
    movlb    0                ;restore bsr
    return                    ;and get out

;** Strings **

sbtfg    db    "gr\r\0"            ;get features
sbtfs    db    "sr,00060000\r\0"        ;no auto-advertise, no authentication
sbtfn    db    "s-,WECoyote\r\0"        ;set friendly name
sbtmod    db    "sdm,AAC1000\r\0"        ;set model
sbtmnm    db    "sdn,Acme Products\r\0"        ;set manufacturer name
sbtres    db    "r,1\r\0"            ;reset
sbtadv    db    "A\r\0"                ;advertise
sbtunb    db    "U\r\0"                ;unbond

sbtcps    db    "PZ\r\0"                    ;clear private services
sbtstsrv db    "SS,40000001\r\0"                ;allow private services
sbtpsuid db    "PS,1000EEE0000000000000000000000000\r\0"    ;set private service UUID
sbtpscar db    "PC,1000EEE1000000000000000000000000,02,06\r\0"    ;add private service characteristic UUID


;********************************************
;** 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:'

;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

;non-standard responses -- list is \n separated, \0 terminated

btnsmes    db    "ConnParam:\n",0

;**************************************************************
;** 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 nibble
    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 low nibble

    bra    hex2bin            ;do for all characters
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Oh, and regarding HEX2BIN...if anyone's got a better way to convert a string of an arbitrary number of arbitrary length hex values (in ascii) to binary, I'd love to hear it. Mine's fast, but I don't like the way it looks.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Re private characteristics and nRf:

It seems that the problem is not with nRF. I've found other bluetooth scanner apps, and they all hang when trying to connect when I have the private characteristics defined.

The RN4020 is telling me the private characteristics are setup properly.

So, there is either a problem with my phone, or with the RN4020. Unfortunately, I didn't bring my tablet today to try a different device. Anyone got a phone I can borrow?

Edit: if anyone here has worked with bluetooth LE, and, specifically with private characteristics, can you help?
 

GopherT

Joined Nov 23, 2012
8,009
Re private characteristics and nRf:

It seems that the problem is not with nRF. I've found other bluetooth scanner apps, and they all hang when trying to connect when I have the private characteristics defined.

The RN4020 is telling me the private characteristics are setup properly.

So, there is either a problem with my phone, or with the RN4020. Unfortunately, I didn't bring my tablet today to try a different device. Anyone got a phone I can borrow?

Edit: if anyone here has worked with bluetooth LE, and, specifically with private characteristics, can you help?

Not to shoo you way from here but the Bluetooth sub-forum over at microchip.com seems to be a hotbed of RN4020 questions and a few excellent people with answers.

http://www.microchip.com/forums/f505.aspx
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Fixed! The problem was the UUID for the private service. The RN4020 seems to choke on all the zeros.

Random numbers work much better.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
BTW, this is one of the reasons why I love Microchip. I called my FAE at 11 am this morning, telling him of the problem. Within an hour or so, there was another Microchip Bluetooth engineer sitting with me in my office helping to solve the problem. Thanks, guys.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Yay! Everything is working. I can turn the bluetooth module on and off, check the existing settings (including private characteristics and capturing their 'handle' from the LS command) are reload the settings if necessary, connect and disconnect, and detect and act on, and parse, non-standard responses.

All that is remaining is to handle notifications, indications, and actually writing data to my own private characteristics. That is all pretty easy as the foundation already supports it.

I've removed the requirement for initialization (BTINIT), so the routine is gone. The state machine is smart enough to initialize all by itself during the standard polling process in the main loop of the application code.

I spent much time on the 'LS' command. It produces a table of server services, including primary services and my own private services. The hard part was to parse the table with the minimum code possible while not requiring to duplicate string constants in program memory. It's all good now. I do not think I will require parsing of the 'LC' command -- if so, I can likely reuse the routines that process 'LS'.

This code is very nearly production ready. And the best part is, I wrote it as a module that I can include in any existing 18F project -- another notch in my library belt. Bluetooth in minutes!

Oh! Last thing: as far as I know, the text parsing is pretty complete and robust. This means I can use the responses to control the state machine without even looking at the hardware signals. I can free up two port pins on my CPU, so I only require 4 connections to the RN4020 instead of six. This is pretty cool, IMHO.

Here are the definitions for the module:

Code:
;important Bluetooth states

cbtsid    equ    0        ;idle until bluetooth on and awake
cbtssu    equ    1        ;begin setup -- get feature string
cbtsls    equ    6        ;send LS command
cbtsrbt    equ    10        ;reboot
cbtsaps    equ    11        ;add private services
cbtsadv    equ    14        ;start advertising

;Bluetooh constants

cnumcar    equ    5        ;number of private characteristics

;Bluetooth Flags

#define    fbtrbt    btflag0,0,1        ;1=bluetooth requires reboot
#define fbtcaph    btflag0,1,1        ;1=capture characteristic handle during parsing of UUIDs in LS
;#define     btflag0,2,1        ;
;#define     btflag0,3,1        ;
;#define     btflag0,4,1        ;
;#define     btflag0,5,1        ;
;#define     btflag0,6,1        ;
;#define    btflag0,7,1        ;

;standard response flags

#define    FSRAOK    btsresp,0,1        ;"AOK"
#define    FSRERR    btsresp,1,1        ;"ERR"
#define    FSRBOND    btsresp,2,1        ;"BONDED"
#define    FSRSEC    btsresp,3,1        ;"SECURED"
#define    FSRCEND    btsresp,4,1        ;"CONNECTION END"
#define    FSRRBT    btsresp,5,1        ;"REBOOT"
#define FSRZCMD    btsresp,6,1        ;"\0CMD" (reply when reboot complete)
#define    FSRCON    btsresp,7,1        ;"Connected"

#define FSRNFL    btsresp+1,0,1        ;"NFail"
#define FSRCMD    btsresp+1,1,1        ;"CMD"
#define FSREND    btsresp+1,2,1        ;"END"
#define    FSRNSR    btsresp+1,3,1        ;non-standard response received
;#define     btsresp+1,4,1        ;
;#define     btsresp+1,5,1        ;
;#define     btsresp+1,6,1        ;
;#define     btsresp+1,7,1        ;

;wait for standard response flags

#define    FWRAOK    btswait,0,1        ;"AOK"
#define    FWRERR    btswait,1,1        ;"ERR"
#define    FWRBOND    btswait,2,1        ;"Bonded"
#define    FWRSEC    btswait,3,1        ;"Secured"
#define    FWRCEND    btswait,4,1        ;"Connection End"
#define    FWRRBT    btswait,5,1        ;"Reboot"
#define FWRZCMD    btswait,6,1        ;"\0CMD" (reply when reboot complete)
#define    FWRCON    btswait,7,1        ;"Connected"

#define    FWRNFL    btswait+1,0,1        ;"NFail"
#define    FWRCMD    btswait+1,1,1        ;"CMD"
#define FWREND    btswait+1,2,1        ;"END"
#define    FWRNSR    btswait+1,3,1        ;non-standard response received
;#define    btswait+1,4,1        ;
;#define    btswait+1,5,1        ;
;#define    btswait+1,6,1        ;
#define    FWRNSRX btswait+1,7,1        ;non-standard response not processed

;macros

txpstr    macro    paddr            ;send string from program memory

    movlf    tblptrl,low paddr
    movlf    tblptrh,high paddr

    call    _txpstr

    endm

rxpcstr    macro    paddr            ;compare received string with paddr

    movlf    tblptrl,low paddr
    movlf    tblptrh,high paddr

    call    _rxpcstr

    endm
The strings used by the module:

Code:
;** Strings **

sbtgfs    db    "GR\r\0"            ;get features
sbtsfs    db    "SR,00060000\r\0"        ;no auto-advertise, no authentication

sbtgfn    db    "GN\r\0"            ;get friendly name
sbtsfn    db    "S-,WECoyote2\r\0"        ;set friendly name

sbtgmod    db    "GDM\r\0"
sbtsmod    db    "SDM,AAC1000\r\0"        ;set model

sbtgmn    db    "GDN\r\0"
sbtsmn    db    "SDN,Acme Products\r\0"        ;set manufacturer name

sbtgbms    db    "GS\r\0"
sbtsbms db    "SS,40000001\r\0"        ;allow private services

sbtls    db    "LS\r\0"            ;list services

sbtsps    db    "PS,4FB69667EB9C473992E26908642B1CD0\r\0"    ;set private service UUID

sbtsc1    db    "PC,4CD584F575A14A36833C83D0E75BA483,02,14\r\0"    ;add private service characteristic UUID
sbtsc2  db    "PC,4B90C8B122644767AC0FF10630185C19,02,14\r\0"
sbtsc3  db    "PC,2402A03123EE4E2B919BCF128281B6D5,02,14\r\0"
sbtsc4  db    "PC,4202E4BECC194B2F895234F4AF83609A,02,14\r\0"
sbtsc5  db    "PC,7828D3A795EC48BB961C87B0214CA594,02,14\r\0"

sbtcps    db    "PZ\r\0"                    ;clear private services

sbtres    db    "R,1\r\0"                    ;reset
sbtadv    db    "A\r\0"                        ;advertise
sbtkill    db    "K\r\0"                        ;kill current connection
sbtdorm    db    "O\r\0"                        ;dormant

sbtunb    db    "U\r\0"                        ;unbond

;standard response messages -- list is \r terminated (so we can include 0x00 for reboot)

btsmes    db    "AOK\nERR\nBonded\nSecured\nConnection End\nReboot\n\x00CMD\nConnected\nNFail\nCMD\nEND\n\r"

;non-standard responses -- list is \n separated, \0 terminated

btnsmes    db    "ConnParam:\nNotify,\nIndicate,\nWV,\nWC,\nRV,\n",0
And the actual code:

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

;a response has been received from bluetooth

    movf    btswait,w,1        ;are we waiting for a particular response?
    iorwf    btswait+1,w,1
    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    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    ibtex            ;no further processing if timer not expired

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

;otherwise, flag a timeout error

    bra    pbtrto            ;show timeout error

;not a timed function

pbtntim    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    ibtex            ;otherwise, just wait until response

;Handle improper response

pbtimp    btfsc    fsrnsr            ;non-standard response?
    rcall    btpnsr            ;go parse it

    bra    ibtex            ;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    ibtex            ;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    ibtex            ;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:  idle awaiting BTAWAKE hardware line if bluetooth on

;setup blue tooth

    bra    btssu0            ;state 1:  begin setup, get 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

;idle with bluetooth on

    bra    btsadv            ;state 14:  start advertising
    bra    btsid1            ;state 15:  idle for connects/disconnects and monitor monitor for shutdown

;shudown bluetooth

    bra    btssd0            ;state 16:  put bluetooth in idle mode
    bra    btssd1            ;state 17:  clear wakesw
    bra    btssd2            ;state 18:  clear wakehw

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

;-- State 0:  wake BT, await BTAWAKE hardware line, then wait for CMD message

btsid0    bbc    fbton,ibtex            ;do nothing if bluetooth not activated

    bsf    wakesw                ;ensure bluetooth is awake
    bsf    wakehw

    bcf    fbtrbt                ;clear reboot flag for setup

    bsf    fwrcmd                ;wait for "CMD"
    bra    ibtext                ;with 200ms timeout

;-- 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
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bsf    fwrnsr                ;  wait for non-standard response
    bra    ibtex                ;  wait 200ms and don't advance

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

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsfs                ;send feature settings string
    bra    ibtexat                ;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
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bsf    fwrnsr                ;  wait for non-standard response
    bra    ibtex                ;  wait 200ms and don't advance

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

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

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

    txpstr    sbtgmod                ;get friendly name string
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bsf    fwrnsr                ;  wait for non-standard response
    bra    ibtex                ;  wait 200ms and don't advance

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

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmod                ;send new friendly name
    bra    ibtexat                ;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
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bsf    fwrnsr                ;  wait for non-standard response
    bra    ibtex                ;  wait 200ms and don't advance

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

    bsf    fbtrbt                ;indicate a reboot is going to be required
    txpstr    sbtsmn                ;send new manufacturer's name
    bra    ibtexat                ;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
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bsf    fwrnsr                ;  wait for non-standard response
    bra    ibtex                ;  wait 200ms and don't advance

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

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

btssu5    clrf    bthctr,1            ;clear handle counter for later

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

;-- 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

    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    ibtex                ;await response and return here

btssu60    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    bra    ibtext                ;and await response

;-- State 8: iterate to check UUID for each private characteristic -- capture handle if exists

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

    movfwb    bthctr                ;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 sbtsc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtsc1

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

    movfw    prodl                ;set tblptr->correct characteristic string
    addwf    tblptrl,f
    clrw
    addwfc    tblptrh,f

    bsf    fbtcaph                ;indicate we want private characteristic handle captured
    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

    bsf    fwrnsr                ;wait for non-standard response
    bsf    fwrend                ;allow an END if nothing in the list
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bra    ibtex                ;come back to this state with response

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

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

    movlfb    btstate,cbtsrbt            ;all good, but reboot if necessary
    bra    ibtex

;  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    ibtext                ;and await response

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

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

    bsf    fwrnsr                ;otherwise, wait for non-standard response
    bsf    fwrend                ;wait for END if nothing in the list
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bra    ibtex                ;come back to this state with response

;-- State 10: start reset/reboot process if necessary -- clear all private services    

btssu9    bbs    fbtrbt,btssu90            ;reboot necessary?

    movlfb    btstate,cbtsadv            ;no, start advertising
    bra    ibtex

btrbt    movlfb    btstate,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    ibtexat                ;and advance

;-- State 11: add private services

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

    txpstr    sbtsps                ;add private servcie
    bra    ibtexat                ;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 sbtsc1        ;tblptr->first characteristic setup string
    movlf    tblptrh,high sbtsc1

    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
    clrw
    addwfc    tblptrh,f

    incf    bthctr,f,1            ;index to next characteristic

    call    _txpstr                ;transmit string
    bsf    FWRAOK                ;wait for 'AOK'
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bra    ibtex                ;and return to this state

;all characteristics set.  Reboot

btssuB0    txpstr    sbtres                ;send reset
    bsf    fwrrbt                ;wait for 'Reboot'
    bra    ibtext

;-- 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'
    movlfb    btstate,cbtsls            ;set up LS as next state
    movlfb    btstime,clctim(t16ms,3212)    ;up to 3212ms allowed for response
    bra    ibtex

;-- State 14: start advertisement

btsadv    txpstr    sbtadv                ;send start advertisement
    bra    ibtexae                ;and allow ERR in case already advertising

;-- State 15: idle as connected/disconnect -- monitor fbton in case bluetooth is to shut down

btsid1    bbc    fbton,btsid11            ;bluetooth needs to turn off?
    bbs    FSRCEND,btsid10            ;disconnect received?  


    bsf    FWRCON                ;allow connection response
    bsf    FWRCEND                ;allow connection end response
    bsf    FWRNSR                ;allow non-standard for notifications

    bra    ibtex                ;and come right back next pass

;disconnect received -- start advertising again for seamless reconnect

btsid10    txpstr    sbtadv                ;send start advertisement
    bsf    FWRAOK                ;allow ok
    bsf    FWRERR                ;or error
    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response
    bra    ibtex                ;and return to this state

;shut down gracefully -- kill any existing connections

btsid11    txpstr    sbtkill                ;send kill command
    bra    ibtexae                ;allow ERR if not connected

;-- State 16:  put bluetooth into dormant mode

btssd0    txpstr    sbtdorm                ;send dormant command
    bra    ibtexat                ;wait for aok

;-- State 17: shutdown bluetooth

btssd1    bcf    wakesw                ;turn off bluetooth uart transmitter

    bsf    FWREND                ;end message will be response
    bra    ibtext                ;

;-- State 18: complete shutdown -- pull wakehw low

btssd2    bcf    wakehw                ;pull line low

    movlfb    btstate,cbtsid            ;set up idle0 as next state
    bra    ibtex                ;no messages expected

;****** END OF STATE MACHINE STATES ******

;set up next state with AOK/ERR response and 200ms timeout

ibtexae    bsf    FWRERR                ;wait for 'ERR'

;set up next state with AOK response and 200ms timeout

ibtexat    bsf    FWRAOK                ;wait for 'AOK'

;set up next state with 200 ms timeout

ibtext    movlfb    btstime,clctim(t16ms,213)    ;up to 213ms allowed for response

;set up next state

ibtns    incf    btstate,f,1            ;and skip to next state on completion

;restore BSR and quit till next pass

ibtex    movlb    0
    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    bsf    fwrnsrx            ;indicate non-standard response not processed

    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
    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 fbtcaph is set, handle for char #(bthctr) is captured **
;****************************************************************

_rxpcstr

    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
    retz                ;return with z to indicate match

    bbc    fbtcaph,rxpcb0        ;capture handle flag set?
    movlw    ','            ;handle starts at first comma
    xorwf    indf0,w            ;compare
    skpnz
    rcall    rxpch            ;go capture handle

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

    return                ;return with 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

    include    "btstrings.asm"        ;include command/response strings
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Oh! And here are the register definition specific to the Bluetooth code:

Code:
#ifdef BLUETOOTH
 
;Bank 4 RAM (0x400-0x4ff 256 available) ;Bluetooth configuration only

    cblock    0x400

;bluetooth processing registers

    btflag0                ;bluetooth flags

    btsresp:2            ;standard response flags
    btswait:2            ;wait for a particular response

                    ;bit 0 -- AOK
                    ;bit 1 -- ERR
                    ;bit 2 -- BONDED
                    ;bit 3 -- SECURED
                    ;bit 4 -- CONNECTION END
                    ;bit 5 -- REBOOT
                    ;bit 6 -- \0CMD
                    ;bit 7 -- CONNECTED
                    ;bit 8 -- NFail
                    ;bit 9 -- END
                    ;bit 10-- non-standard response
                    ;bit 11--
                    ;bit 12--
                    ;bit 13--
                    ;bit 14--
                    ;bit 15--

    btstime                ;bluetooth state machine timer
    btstate                ;bluetooth state machine counter             
 
    btrhead                ;bluetooth receiver head pointer

;non-standard response received values

; -- from ConnParam:
    supto:2                ;supervision timeout
    slatency:2            ;slave latency
    conint:2            ;connection interval

; -- private characteristic handles from LS command

    bthctr                ;handle counter for parsing


    hpchar1:4
    hpchar2:4
    hpchar3:4
    hpchar4:4
    hpchar5:4

    endc

;Bank 5 RAM (0x500-0x5ff 256 available) ;Bluetooth configuration only

    cblock    0x500

    txbuff:256                ;EUSART transmit buffer (256 bytes)

    endc

;Bank 6 RAM (0x600-0x6ff 256 available) ;Bluetooth configuration only

    cblock    0x600

    rxbuff:256                ;EUSART receive buffer (256 bytes)

    endc

;Bank 7 RAM (0x700-0x7ff 256 available) ;Bluetooth configuration only

    cblock    0x700

    btrcvbuff:256                ;BT receiver buffer

    endc

#endif
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
The very last thing I'll probably do, after *all* the code is finished, is to add a supervisor that performs a hard hardware reset in the case of timeout errors. I haven't had any, but if I did, the current code would try to limp on, probably unsuccessfully.
 
Top