about Microchip RN4020

nsaspook

Joined Aug 27, 2009
16,321
You need to send null clocks before the start bit. The dummy8 of A_data will be auto aligned/padded by the compiler to the correct storage boundary for the structure so we can use a union to send the correct 3 bytes of 4 and 24 bits to the ADC.
24-bit spi mode 0,0 transmit sequence in the C structure with union overlay to long and 4 byte array.
https://forum.allaboutcircuits.com/threads/pic24-bluetooth-low-energy-dk.128497/#post-1056985
SDO to ADC

SDI from ADC





ADC spi data: top command, bottom response.
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,321
I've modified the original demo code to add a few public services for examples. The ADC value now updates the heart rate and battery level characteristics. The 'downsides' (if you run a earlier version of firmware) of this code addition is that RN4020 firmware 1.33.4 is necessary now for proper operation. A upgrade to 1.33 is highly recommended.

GATT LS command during PIC24 boot of services.


Discovery using nRF Connect Android APP https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en_US

'Unknown Service' is the private service defined in the RN4020 PIC24 software.
C:
////////////////// #define code fragment

//BTLE services
#define PRIVATE_SERVICE            "28238791ec55413086e0002cd96aec9d"
#define PRIVATE_SERVICE_SPI        "8ee15902ee6f49dc9cfb5c4c2eff6057"
#define PRIVATE_CHAR_SWITCHES        "8f7087bdfdf34b87b10fabbf636b1cd5"
#define PRIVATE_CHAR_POTENTIOMETER    "362232e5c5a94af6b30ce208f1a9ae3e"
#define PRIVATE_CHAR_LEDS        "cd8306093afa4a9da58b8224cd2ded70"
#define PRIVATE_CHAR_RELAYS        "cd83060a3afa4a9da58b8224cd2ded70"
#define PRIVATE_CHAR_ADC_CHAN        "cd83060b3afa4a9da58b8224cd2ded70"
#define PRIVATE_CHAR_PIC_SLAVE        "cd83060c3afa4a9da58b8224cd2ded70"

// Battery
#define PUBLIC_BATT_UUID       "180F" // Battery level service
#define PUBLIC_BATT_CHAR_BL    "2A19"

// Heartbeat
#define PUBLIC_HR_UUID         "180D" // Heart Rate service
#define PUBLIC_HR_CHAR_HRM     "2A37" // Heart Rate Measurement
#define PUBLIC_HR_CHAR_BSL     "2A38" // Heart body sensor location
#define PUBLIC_HR_CHAR_RCP     "2A39" // Heart rate control point

// handles that change with added services and characteristics
// manually parse the LS command for UUID handles
#define PUBLIC_BATT_CHAR_H        "0032"
#define PUBLIC_BATT_CHAR_C        "0033"
#define PUBLIC_HR_CHAR_HRM_H        "001B"
#define PUBLIC_HR_CHAR_HRM_C        "001C"
#define PRIVATE_CHAR_LEDS_H        "0029"
#define PRIVATE_CHAR_ADC_CHAN_H        "002D"
#define PRIVATE_CHAR_PIC_SLAVE_H    "002F"

///////////// setup frag

    if (version_code >= 33) { // V1.33.4 public services
        // Public BTLE services and characteristics

        // heart rate service with standard 16-bit UUID
        BT_SendCommand("ps,"PUBLIC_HR_UUID",\r", false);
        if (!BT_CheckResponse(AOK)) {
            return false;
        }

        // heart rate measurement characteristic
        BT_SendCommand("pc,"PUBLIC_HR_CHAR_HRM",22,02\r", false); //Indicate, Read
        if (!BT_CheckResponse(AOK)) {
            return false;
        }

        // heart body sensor location characteristic
        BT_SendCommand("pc,"PUBLIC_HR_CHAR_BSL",0A,0F\r", false); //Write w/ACK, Read
        if (!BT_CheckResponse(AOK)) {
            return false;
        }

        // heart rate control point characteristic
        BT_SendCommand("pc,"PUBLIC_HR_CHAR_RCP",0A,0F\r", false); //Write w/ACK, Read
        if (!BT_CheckResponse(AOK)) {
            return false;
        }
    }

/////////////// update frag
        //Transmit new potentiometer reading?
        if (TimerDone(TMR_POT)) {
            //Send message only if pot value has changed
            if (appData.potValue != appData.potValueLastTX) {
                //Form message
                sprintf(appData.transmit_packet, "suw,"PRIVATE_CHAR_POTENTIOMETER",%04d\r\n", appData.potValue);
                //Try to transmit the message; reset timer if successful
                if (BT_SendCommand(appData.transmit_packet, true)) {
                    appData.potValueLastTX = appData.potValue;
                    StartTimer(TMR_POT, POT_TX_MS);
                }

                //Form message
                sprintf(appData.transmit_packet, "suw,"PUBLIC_BATT_CHAR_BL",%d\r", (appData.potValue >> 6)&0b00111111);
                //Try to transmit the message; reset timer if successful
                BT_SendCommand(appData.transmit_packet, false);
                sprintf(appData.transmit_packet, "suw,"PUBLIC_HR_CHAR_HRM",%02x%02x\r", 0, (appData.potValue >> 4)&0xff); // format mask and ADC data
                //Try to transmit the message; reset timer if successful
                BT_SendCommand(appData.transmit_packet, false);

            } else {
                StartTimer(TMR_POT, POT_TX_MS);
            } //value not changed - skip this transmission
        }
 

joeyd999

Joined Jun 6, 2011
6,279
I've modified the original demo code to add a few public services for examples. The ADC value now updates the heart rate and battery level characteristics. The 'downsides' (if you run a earlier version of firmware) of this code addition is that RN4020 firmware 1.33.4 is necessary now for proper operation. A upgrade to 1.33 is highly recommended.

GATT LS command during PIC24 boot of services.


Discovery using nRF Connect Android APP https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en_US

'Unknown Service' is the private service defined in the RN4020 PIC24 software.
C:
////////////////// #define code fragment

//BTLE services
#define PRIVATE_SERVICE            "28238791ec55413086e0002cd96aec9d"
#define PRIVATE_SERVICE_SPI        "8ee15902ee6f49dc9cfb5c4c2eff6057"
#define PRIVATE_CHAR_SWITCHES        "8f7087bdfdf34b87b10fabbf636b1cd5"
#define PRIVATE_CHAR_POTENTIOMETER    "362232e5c5a94af6b30ce208f1a9ae3e"
#define PRIVATE_CHAR_LEDS        "cd8306093afa4a9da58b8224cd2ded70"
#define PRIVATE_CHAR_RELAYS        "cd83060a3afa4a9da58b8224cd2ded70"
#define PRIVATE_CHAR_ADC_CHAN        "cd83060b3afa4a9da58b8224cd2ded70"
#define PRIVATE_CHAR_PIC_SLAVE        "cd83060c3afa4a9da58b8224cd2ded70"

// Battery
#define PUBLIC_BATT_UUID       "180F" // Battery level service
#define PUBLIC_BATT_CHAR_BL    "2A19"

// Heartbeat
#define PUBLIC_HR_UUID         "180D" // Heart Rate service
#define PUBLIC_HR_CHAR_HRM     "2A37" // Heart Rate Measurement
#define PUBLIC_HR_CHAR_BSL     "2A38" // Heart body sensor location
#define PUBLIC_HR_CHAR_RCP     "2A39" // Heart rate control point

// handles that change with added services and characteristics
// manually parse the LS command for UUID handles
#define PUBLIC_BATT_CHAR_H        "0032"
#define PUBLIC_BATT_CHAR_C        "0033"
#define PUBLIC_HR_CHAR_HRM_H        "001B"
#define PUBLIC_HR_CHAR_HRM_C        "001C"
#define PRIVATE_CHAR_LEDS_H        "0029"
#define PRIVATE_CHAR_ADC_CHAN_H        "002D"
#define PRIVATE_CHAR_PIC_SLAVE_H    "002F"

///////////// setup frag

    if (version_code >= 33) { // V1.33.4 public services
        // Public BTLE services and characteristics

        // heart rate service with standard 16-bit UUID
        BT_SendCommand("ps,"PUBLIC_HR_UUID",\r", false);
        if (!BT_CheckResponse(AOK)) {
            return false;
        }

        // heart rate measurement characteristic
        BT_SendCommand("pc,"PUBLIC_HR_CHAR_HRM",22,02\r", false); //Indicate, Read
        if (!BT_CheckResponse(AOK)) {
            return false;
        }

        // heart body sensor location characteristic
        BT_SendCommand("pc,"PUBLIC_HR_CHAR_BSL",0A,0F\r", false); //Write w/ACK, Read
        if (!BT_CheckResponse(AOK)) {
            return false;
        }

        // heart rate control point characteristic
        BT_SendCommand("pc,"PUBLIC_HR_CHAR_RCP",0A,0F\r", false); //Write w/ACK, Read
        if (!BT_CheckResponse(AOK)) {
            return false;
        }
    }

/////////////// update frag
        //Transmit new potentiometer reading?
        if (TimerDone(TMR_POT)) {
            //Send message only if pot value has changed
            if (appData.potValue != appData.potValueLastTX) {
                //Form message
                sprintf(appData.transmit_packet, "suw,"PRIVATE_CHAR_POTENTIOMETER",%04d\r\n", appData.potValue);
                //Try to transmit the message; reset timer if successful
                if (BT_SendCommand(appData.transmit_packet, true)) {
                    appData.potValueLastTX = appData.potValue;
                    StartTimer(TMR_POT, POT_TX_MS);
                }

                //Form message
                sprintf(appData.transmit_packet, "suw,"PUBLIC_BATT_CHAR_BL",%d\r", (appData.potValue >> 6)&0b00111111);
                //Try to transmit the message; reset timer if successful
                BT_SendCommand(appData.transmit_packet, false);
                sprintf(appData.transmit_packet, "suw,"PUBLIC_HR_CHAR_HRM",%02x%02x\r", 0, (appData.potValue >> 4)&0xff); // format mask and ADC data
                //Try to transmit the message; reset timer if successful
                BT_SendCommand(appData.transmit_packet, false);

            } else {
                StartTimer(TMR_POT, POT_TX_MS);
            } //value not changed - skip this transmission
        }

You wrote blocking code! Heathen!
 

joeyd999

Joined Jun 6, 2011
6,279
Every time you send a command to the 4020, you are waiting for a response: either AOK or ERR -- thus setting the return value for the call TRUE or FALSE. The code stalls until the response is received.

This is worse when you use the Indicate bit: you must wait for two AOKs until (a send acknowledge (from the 4020) + a receive acknowledge (from the host)) the function returns.

This would blow my apps out of the water.

Note: the response time is also indeterminate. Much depends on the integrity of the radio connection -- and other traffic -- and where you are in the "slice" time of the Bluetooth protocol.
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,321
Every time you send a command to the 4020, you are waiting for a response: either AOK or ERR -- thus setting the return value for the call TRUE or FALSE. The code stalls until the response is received.

This is worse when you use the Indicate bit: you must wait for two AOKs until (a send acknowledge (from the 4020) + a receive acknowledge (from the host)) until the function returns.

This would blow my apps out of the water.

Note: the response time is also indeterminate. Much depends on the integrity of the radio connection -- and other traffic -- and where you are in the "slice" time of the Bluetooth protocol.
BT_CheckResponse has a timeout so it will fail with no response but I didn't write that part of the code so I'm not the Heathen this time! I'm planning on a rewire of much of the init and control logic to something that will auto configure services, handles and characteristic as they are added but it's not a top priority.
C:
bool BT_CheckResponse(const char *data)
{
    uint16_t i, ByteCount = 0;
    char NewByte, Buffer[50], *BufPtr;

    StartTimer(TMR_RN_COMMS, 600); //Start 600ms timeout for routine

    BufPtr = Buffer;
    while (ByteCount < 50) //Don't accept more than the buffer size
    {
        if (UART_IsNewRxData()) //Check if new data byte from BT module and return if nothing new
        {
            NewByte = (char) UART_ReadRxBuffer(); //Read the data byte for processing
            *BufPtr++ = NewByte; //Add it to the buffer
            ByteCount++;
            if (NewByte == '\n') //Check if got linefeed
                break; //If linefeed then we have what we want
        }
        if (TimerDone(TMR_RN_COMMS)) //Check if timed out
            return false; //If timed out then return failure
    }

    BufPtr = Buffer;
    for (i = 0; i < ByteCount; i++) //Compare all bytes received
    {
        if (*data == '\0') //See if reached end of string with no bytes different
            return true; //No bytes were different so return success
        else if (*data++ != *BufPtr++) //else see if the bytes are different
            return false; //Bytes differ so return failure
    }
    return true; //All bytes matched so return success
}
[/quote]
 

joeyd999

Joined Jun 6, 2011
6,279
Just so you recognize the impact, let's talk about what happens with your SUW command. I will reference the PIC18, not the PIC24, as I don't use that.

First, my code runs at 8Mhz, so a Tinst of 0.5uS. I'll assume the RN4020 native 118kBaud (84uS/byte or 170 Tinst/byte).

We both have to send the command to the TX buffer -- no issue there except I use SHW (send by 4 octet handle) instead of SUW (send by 32 octet UUID). This saves me some time.

Next, you have to wait for -- i.e., "SUW,362232e5c5a94af6b30ce208f1a9ae3e/r/n" -- 38 character times, or 6,440 Tinst just for the command to clear the buffer.

Then, the 4020 must parse your command, wait for an appropriate time slice, and send it to the host. Tinst is undeterminate.

The 4020 must then formulate a response -- either AOK, ERR, or FAIL, and then send it "AOK/r/n". Another 848 Tinst.

Finally, the PIC must parse the response and compute a TRUE or FALSE and return to your calling routine.

This is such a significant waste of MCU cycles. Like I said, this'd kill me. I usually have far too much going on in the background to let the CPU stall to a halt for >> 10,000 Tinst.

Writing non-blocking code for the RN4020 is hard.
 

nsaspook

Joined Aug 27, 2009
16,321
Just so you recognize the impact, let's talk about what happens with your SUW command. I will reference the PIC18, not the PIC24, as I don't use that.

First, my code runs at 8Mhz, so a Tinst of 0.5uS. I'll assume the RN4020 native 118kBaud (84uS/byte or 170 Tinst/byte).

We both have to send the command to the TX buffer -- no issue there except I use SHW (send by 4 octet handle) instead of SUW (send by 32 octet UUID). This saves me some time.

Next, you have to wait for -- i.e., "SUW,362232e5c5a94af6b30ce208f1a9ae3e/r/n" -- 38 character times, or 6,440 Tinst just for the command to clear the buffer.

Then, the 4020 must parse your command, wait for an appropriate time slice, and send it to the host. Tinst is undeterminate.

The 4020 must then formulate a response -- either AOK, ERR, or FAIL, and then send it "AOK/r/n". Another 848 Tinst.

Finally, the PIC must parse the response and compute a TRUE or FALSE and return to your calling routine.

This is such a significant waste of MCU cycles. Like I said, this'd kill me. I usually have far too much going on in the background to let the CPU stall to a halt for >> 10,000 Tinst.

Writing non-blocking code for the RN4020 is hard.
I agree but I'm not going to completely rewrite most of another persons code that doesn't need the speedup just to make it faster when it's not needed. I've got several other 'real' projects.
 

joeyd999

Joined Jun 6, 2011
6,279
I agree but I'm not going to completely rewrite most of another persons code that doesn't need the speedup just to make it faster when it's not needed. I've got several other 'real' projects.
I always write code assuming it's going to become a "real" project eventually.

And I've never used a third party library for anything (PIC wise). I trust programmers about as far as I can throw them -- whether I know them or not.
 

nsaspook

Joined Aug 27, 2009
16,321
I always write code assuming it's going to become a "real" project eventually.

And I've never used a third party library for anything (PIC wise). I trust programmers about as far as I can throw them -- whether I know them or not.
I finished the original 'real' BLE project (a remote switcher) some time ago, this code is for a plaything eventually.
 

nsaspook

Joined Aug 27, 2009
16,321
BTW, the local Microchip RN4020 App Engineer attempted to write non-blocking code for the 4020. He gave up.
Last thing about that section of code, it's in the boot initialization section so timing is not critical and it will timeout and fail if the RN4020 craps.

The main state machine loop gets/sends RN4020 serial data from the interrupt driven rx/tx buffers so it never blocks.

C:
//**********************************************************************************************************************
// Interrupt routine for UART receive interrupts

void __attribute__((interrupt, no_auto_psv)) _U1RXInterrupt(void)
{
    UART_RX_IF = 0; //Clear UART Receive interrupt flag
    *rxBuf.head++ = UART_RX_BUF; //Put received byte in the buffer
    if (rxBuf.head > &rxBuf.buffer[SIZE_RxBuffer - 1]) { //Check if end of buffer
        rxBuf.head = &rxBuf.buffer[0]; //Wrap pointer to beginning
    }
    if (rxBuf.byteCount < SIZE_RxBuffer) { //Increment byte count
        rxBuf.byteCount++;
    }
}

// Receive a message over the Bluetooth link
bool BT_ReceivePacket(char * Message)
{
    static enum BluetoothDecodeState btDecodeState = WaitForCR; //Static so maintains state on reentry   //Byte read from the UART buffer
    static uint16_t i = 0;

    if (UART_IsNewRxData()) //Check if new data byte from BT module and return if nothing new
    {
        Message[i++] = UART_ReadRxBuffer();
        if (i == BT_RX_PKT_SZ) {
            i = 0;
        }

        switch (btDecodeState) {
        case WaitForCR:
            if (Message[i - 1] == '\r') { //See if this is the CR
                btDecodeState = WaitForLF; //Is CR so wait for LF
            }
            break;

        case WaitForLF:
            btDecodeState = WaitForCR; //Will be looking for a new packet next
            if (Message[i - 1] == '\n') //See if this is the LF
            {
                Message[i] = NULL; //Got a complete message!
                i = 0;
                return true;
            }
            break;

        default: //Invalid state so start looking for a new start of frame
            btDecodeState = WaitForCR;
        }
    }
    return false;
}

/* in the main loop */

//Process any new messages received from RN module
appData.got_packet = BT_ReceivePacket(appData.receive_packet); //Get new message if one has been received from the RN4020
        if (appData.got_packet == true) { //true if new packet received
         // stuff
            if (strstr(appData.receive_packet, "WV,"PRIVATE_CHAR_LEDS_H",")) { //Check for LED update message 1.33
                GetNewLEDs(); //Latch new LED values
                appData.update_packet = true;
            }
            //
            //Other message handling can be added here
            //
            //receive new SPI ADC channel
            if (strstr(appData.receive_packet, "WV,"PRIVATE_CHAR_ADC_CHAN_H",")) {
                GetNewADC_Chan(); // new ADC config data
            }
            //receive new SPI SLAVE request
            if (strstr(appData.receive_packet, "WV,"PRIVATE_CHAR_PIC_SLAVE_H",")) {
// yada yada
          }:
      };
 

joeyd999

Joined Jun 6, 2011
6,279
The main state machine loop gets/sends RN4020 serial data from the interrupt driven rx/tx buffers so it never blocks.
I assumed this was the case, so I intentionally did not include it in the analysis.

Of course one must scan for received packets that are asynchronous with our code. And it must be done non-blocking or nothing else can happen ever.

But that's not what I was talking about!
 

nsaspook

Joined Aug 27, 2009
16,321
I assumed this was the case, so I intentionally did not include it in the analysis.

Of course one must scan for received packets that are asynchronous with our code. And it must be done non-blocking or nothing else can happen ever.

But that's not what I was talking about!
Sure, I took a quick look at the throughput with the MSO. For what I'm doing it's fine but I see room for improvement by using handles instead of long private characteristics.

[0..1] RN4020 rx/tx lines, [4..6] SPI clock/data with [3] 3208 ADC select line.
Normal update timing.

Cranked up the ADC refresh and POT message rates.


 

joeyd999

Joined Jun 6, 2011
6,279
Remember: if you choose to reference private characteristics by handle, you must enumerate them using the LS command during your configuration process. In fact, since I assume the flash in the RN4020 has a finite write life, I use a 3 step process when I configure:

1. Send LS. Read private characteristics (if any) and capture handles and compare with desired configuration
2. If configuration is not correct, clear existing configuration, write new configuration.
3. Send LS. Read private characteristics and capture handles.

Steps 1 and 2 are done in the same routine (processed once for each read/write private characteristic, or twice for each indicate/notify private characteristic (two handles generated)):

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
Finally, LS blows all characteristic data out at once (public and private). If you are not using hardware flow control, make sure your receive buffer is large enough to capture everything expected.

Here is a typical private characteristic string embedded in program memory and indexed by tblptr above. I use the same string for each step so I don't waste program memory on multiple identical UUID strings:

Code:
sbtspc2 db    "PC,4CD584F575A14A36833C83D0E75BA483,12,14\r\0"
 
Last edited:

joeyd999

Joined Jun 6, 2011
6,279
...but currently don't parse the results automatically because this was a static application.
This'll likely be fine as long as you don't change the radio firmware version.

But I still wouldn't bank on it. The RN4020 is a horribly implemented -- and poorly documented -- device. They seem to have a habit of making undocumented changes to the firmware that -- quite often -- break existing code.
 

nsaspook

Joined Aug 27, 2009
16,321
This'll likely be fine as long as you don't change the radio firmware version.

But I still wouldn't bank on it. The RN4020 is a horribly implemented -- and poorly documented -- device. They seem to have a habit of making undocumented changes to the firmware that -- quite often -- break existing code.
I don't think you will see more firmware updates for the RN4020, newer and 'better' devices have been released.
 

nsaspook

Joined Aug 27, 2009
16,321
The public battery, heart rate characteristic with demo data server is working in the pic24 example code.
NEXUS 7 Table
Generic BLE Heart Monitor app

Microchip Data app
 
Top