This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Sending actual TWI sensor values through BLE

Hello! I am trying to modify the ble_app_hrs_freertos example to send actual heart rate sensor value to the nRF Toolbox app over BLE. I am able to read values from my sensor through TWI, and I am also able to run the ble_app_hrs_freertos example and send the simulated data through BLE. Now, I would like to combine the two, and send the actual reading from the sensor to the app through BLE.

I tried modifying the heart_rate_meas_timeout_handler function of the code to take value from the sensor instead of sensorsim_measure(). However when I do this, I am not able to the device anymore. After trying different things, I realized that the nrf_drv_twi_tx() and nrf_drv_twi_rx() function is the issue. My I wrong to call nrf_drv_twi_tx() and nrf_drv_twi_rx() in the handler? How then, can I get the sensor reading through TWI?

static void heart_rate_meas_timeout_handler(TimerHandle_t xTimer)
{
    static uint32_t cnt = 0;
    ret_code_t      err_code;
    uint8_t        heart_rate;

    UNUSED_PARAMETER(xTimer);

    //heart_rate = (uint16_t)sensorsim_measure(&m_heart_rate_sim_state, &m_heart_rate_sim_cfg);
    heart_rate = readHRS();
    
    cnt++;
    err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heart_rate);
    if ((err_code != NRF_SUCCESS) &&
        (err_code != NRF_ERROR_INVALID_STATE) &&
        (err_code != NRF_ERROR_RESOURCES) &&
        (err_code != NRF_ERROR_BUSY) &&
        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
       )
    {
        APP_ERROR_HANDLER(err_code);
    }

    // Disable RR Interval recording every third heart rate measurement.
    // NOTE: An application will normally not do this. It is done here just for testing generation
    // of messages without RR Interval measurements.
    m_rr_interval_enabled = ((cnt % 3) != 0);
}


void readHRS(uint8_t * read_long_data){
    ret_code_t err_code;
    
    err_code = nrf_drv_twi_tx(&m_twi_nrf52, HRS_ADDR, (uint8_t *) HRS_FIFO_DATA, sizeof(HRS_FIFO_DATA), true);

    if (NRF_SUCCESS == err_code){
	    err_code = nrf_drv_twi_rx(&m_twi_nrf52, HRS_ADDR, (uint8_t *) read_long_data, 6);
    }
    APP_ERROR_CHECK(err_code);
    nrf_delay_ms(10);
}

THANK YOU!

  • Hi,

    The heart_rate_meas_timeout_handler() is just an app_timer timeout handler function, so it should not be any problems starting a TWI transaction here.

    What do you mean by "when I do this, I am not able to the device anymore."? Have you done any debugging to find out what actually happens?

    Without knowing more, the one thing that stands out is your call to nrf_delay_ms(). Note that this is in an RTC interrupt routine (app_timer), so any same or lower interrupt priorities will be blocked. You should not use nrf_delay_ms or other forms of bussy waiting. Instead, it would make sense to start the TWI transfer and then wait for an event when the TWI transfer has completed. Then you can call ble_hrs_heart_rate_measurement_send() from the TWI event handler.

  • What do you mean by "when I do this, I am not able to the device anymore."? 

    Sorry, I meant that I wasn't able to connect to the device anymore when tried to modify the heart_rate_meas_timeout_handler function of the code to take value from the sensor instead of sensorsim_measure(). 

    Have you done any debugging to find out what actually happens?

    Yes, so I tried commenting out lines of code that are used in the readHRS() function and I was only able to connect to the device through BLE when I commented out nrf_drv_twi_tx() and nrf_drv_twi_rx().So I thought that was the issue.

    Then you can call ble_hrs_heart_rate_measurement_send() from the TWI event handler.

    Can you elaborate more on how I can do this? Right now I am calling the ble_hrs_heart_rate_measurement_send() in the heart_rate_meas_timeout_handler() and using the heart_rate_meas_timeout_handler() in timer_init()

    static void timers_init(void)
    {
        // Initialize timer module.
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
        // Create timers.
        m_battery_timer = xTimerCreate("BATT",
                                       BATTERY_LEVEL_MEAS_INTERVAL,
                                       pdTRUE,
                                       NULL,
                                       battery_level_meas_timeout_handler);
        m_heart_rate_timer = xTimerCreate("HRT",
                                          HEART_RATE_MEAS_INTERVAL,
                                          pdTRUE,
                                          NULL,
                                          heart_rate_meas_timeout_handler);
        m_rr_interval_timer = xTimerCreate("RRT",
                                           RR_INTERVAL_INTERVAL,
                                           pdTRUE,
                                           NULL,
                                           rr_interval_timeout_handler);
        m_sensor_contact_timer = xTimerCreate("SCT",
                                              SENSOR_CONTACT_DETECTED_INTERVAL,
                                              pdTRUE,
                                              NULL,
                                              sensor_contact_detected_timeout_handler);
    
        /* Error checking */
        if ( (NULL == m_battery_timer)
             || (NULL == m_heart_rate_timer)
             || (NULL == m_rr_interval_timer)
             || (NULL == m_sensor_contact_timer) )
        {
            APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
        }
    }
    
    
    static void heart_rate_meas_timeout_handler(TimerHandle_t xTimer)
    {
        static uint32_t cnt = 0;
        ret_code_t      err_code;
        uint8_t        heart_rate;
    
        UNUSED_PARAMETER(xTimer);
    
        //heart_rate = (uint16_t)sensorsim_measure(&m_heart_rate_sim_state, &m_heart_rate_sim_cfg);
        heart_rate = readHRS();
        
        cnt++;
        err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heart_rate);
        if ((err_code != NRF_SUCCESS) &&
            (err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != NRF_ERROR_BUSY) &&
            (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
           )
        {
            APP_ERROR_HANDLER(err_code);
        }
    
        // Disable RR Interval recording every third heart rate measurement.
        // NOTE: An application will normally not do this. It is done here just for testing generation
        // of messages without RR Interval measurements.
        //m_rr_interval_enabled = ((cnt % 3) != 0);
    }
    
    
    void readHRS(uint8_t * read_long_data){
        ret_code_t err_code;
        
        err_code = nrf_drv_twi_tx(&m_twi_nrf52, HRS_ADDR, (uint8_t *) HRS_FIFO_DATA, sizeof(HRS_FIFO_DATA), true);
    
        if (NRF_SUCCESS == err_code){
    	    err_code = nrf_drv_twi_rx(&m_twi_nrf52, HRS_ADDR, (uint8_t *) read_long_data, 6);
        }
        APP_ERROR_CHECK(err_code);
    
    }

     

  • Hi,

    KCY said:
    Yes, so I tried commenting out lines of code that are used in the readHRS() function and I was only able to connect to the device through BLE when I commented out nrf_drv_twi_tx() and nrf_drv_twi_rx().So I thought that was the issue.

    I see. What I meant by "debugging" in this context was more like using a debugging tool to see what is actually going on. What prevents BLE from working when you use TWI to communicate with the sensor? Is an error returned somewhere? Something along these lines. That said, the combination of your code and that TWI transactions seem to cause the problem gives us a good clue, as described in the previous post.

    KCY said:
    Can you elaborate more on how I can do this? Right now I am calling the ble_hrs_heart_rate_measurement_send() in the heart_rate_meas_timeout_handler() and using the heart_rate_meas_timeout_handler() in timer_init()

    Yes. I will repeat the suggestion in my previous post in more detail:

    • The idea is that you should use asynchronous (non-blocking) TWI transactions. To do so:
    • Make sure you configure the TWI driver to be non-blocking and use DMA. That means that you have to pass an event handler when you initialize the driver.
    • Initiate the TWI transaction in heart_rate_meas_timeout_handler(). Since this is now a non-blocking call, it will not cause problems for BLE or anything else.
    • The TWI transaction is now handled by HW, so the CPU can do whatever it needs, including BLE stuff.
    • When the TWI transaction is finished, your TWI event handler will be called.
    • Call ble_hrs_heart_rate_measurement_send() from the TWI event handler to send the new heard rate measurement data.

    The important difference here compared to your implementation is that instead of spending a lot of time just busy waiting in heart_rate_meas_timeout_handler(), the CPU will be idle, available for handling other events. I do not know if this is a solution since you have not tracked down why your calls to nrf_drv_twi_tx() and nrf_drv_twi_rx(), but it seems very likely.

  • Hi Einar,

    Thank you for your response. I called the twi_init() function in heart_rate_meas_timeout_handler(). And then in twi_init(), the TWI event handler is used to ensure non-blocking mode. Then the readHRS() and ble_hrs_heart_rate_measurement_send() functions are called from the TWI event handler as shown in the code below. With these fixes, I am able to connect the device to the app through BLE, but it disconnects  automatically after a second. 

    Did I misunderstand anything from your suggestion?

    void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
    {
        ret_code_t err_code;
        uint16_t heart_rate;
        static sample_t m_sample;
        switch (p_event->type)
        {
            case NRF_DRV_TWI_EVT_DONE:
                if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
                {
                    heart_rate = readHRS();
                    
                    err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heart_rate);
                    if ((err_code != NRF_SUCCESS) &&
                        (err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_BUSY) &&
                        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                       )
                    {
                        APP_ERROR_HANDLER(err_code);
                    }
                }
                m_xfer_done = true;
                break;
            default:
                break;
        }
    }
    
    
    void twi_init (void)
    {
        ret_code_t err_code;
        
        const nrf_drv_twi_config_t twi_nrf52_config = {
           .scl                = ARDUINO_SCL_PIN,
           .sda                = ARDUINO_SDA_PIN,
           .frequency          = NRF_DRV_TWI_FREQ_100K,
           .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
           .clear_bus_init     = false
        };
        
        err_code = nrf_drv_twi_init(&m_twi_nrf52, &twi_nrf52_config, twi_handler, NULL);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_twi_enable(&m_twi_nrf52);
    }
    
    
    void heart_rate_meas_timeout_handler(TimerHandle_t xTimer)
    {
        static uint32_t cnt = 0;
        ret_code_t      err_code;
        uint16_t        heart_rate;
    
        UNUSED_PARAMETER(xTimer);
        
        twi_init();
          
        cnt++;
    }

    With the debugging tool, I received these errors. 

    It seems like it is an error code 8 in this line in the twi_init() function.

    err_code = nrf_drv_twi_init(&m_twi_nrf52, &twi_nrf52_config, twi_handler, NULL);

    What might be causing this issue?

    Thank you!

  • It seems like it is an error code 8 in this line

    Indeed.

    So look up what error code 8 actually means.

    The documentation for nrf_drv_twi_init will list the possible error  codes it can return ...

Related