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!

Parents
  • 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,

    If you do as suggests, you will see that 8 is NRF_ERROR_INVALID_STATE. And that will be returned from nrf_drv_twi_init() if it is already initialized. Looking at your code that will be the case, since you initialize it every time heart_rate_meas_timeout_handler() is run, without uninitializing it in between. This is not legal. Either just initialize it once, or uninitialized it once you are done, and initialize it again before the next time. Also, you do not actually do a transaction in this code snippet, (only initialize the driver), so you will not get any sensor data with this code.

    This is a good example of why this kind of debugging is very useful. By looking at the error code and API documentation (sometimes together with a small peak into the function returned the error) you can most of the time pinpoint the problem or at least narrow it down significantly.

  • Hi, Sorry for the delay. To clear things up. I decided to restart the project again and now I am back here at this position. 

    • I configured the TWI driver to be non-blocking and use DMA - passed an event handler when initializing the driver.

    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)
                {
                    
                    // call ble_hrs_heart_rate_measurement_send in twi_handler
                    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
        };
        
        // Non-blocking, passing twi_handler when initializing driver
        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);
    }

    • Initiate the TWI transaction in heart_rate_meas_timeout_handler()
    • Call ble_hrs_heart_rate_measurement_send() from the TWI event handler to send the new heard rate measurement data. (shown above)

    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);
    
        // Initiate TWI transaction here
        heart_rate = readHRS();
        
        cnt++;
    
    }
    
    
    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);
    
    }

    The BLE can be connected, but automatically disconnects after one second. 

    I get these error codes when I run debug.

    Please advise on what I have done wrong here.

    Thank you.

  • Hi,

    Looking at the call stack, you can see that it is in an error handler. So the first thing you should do is build with DEBUG enabled, and then either just enable logging or inspect the info struct in the error handler. This will tell you in which file and at which line you get which error code. As mentioned in my previous post, this is the by far best starting point when you see errors like this.

  • Does that mean to click on the “build and debug”? Also, Is there a link to the list of error codes? I don’t seem to be able to find it. Thanks.

  • Hi,

    If you are using Segger Embedded Studio, you simply select the debug build configuration (usually by a drop-down in the upper left corner of the screen). If you are using another toolchain, just make sure you have a global define "DEBUG" and build without optimization.

    Then enable debug logging in sdk_config.h if it is not already enabled (UART logging is enabled by default in most examples). If you are using an example without logging, you could just use the debugger to inspect the info struct in the error handler to see the same information, but it is a bit more hassle.

    Unfortunately, there is no unified list of error codes in the SDK, but there is a hierarchy with error bases etc. If you use logging the error code will most of the time be spelled out with the name of the define, so you won't have to piece it together.

Reply
  • Hi,

    If you are using Segger Embedded Studio, you simply select the debug build configuration (usually by a drop-down in the upper left corner of the screen). If you are using another toolchain, just make sure you have a global define "DEBUG" and build without optimization.

    Then enable debug logging in sdk_config.h if it is not already enabled (UART logging is enabled by default in most examples). If you are using an example without logging, you could just use the debugger to inspect the info struct in the error handler to see the same information, but it is a bit more hassle.

    Unfortunately, there is no unified list of error codes in the SDK, but there is a hierarchy with error bases etc. If you use logging the error code will most of the time be spelled out with the name of the define, so you won't have to piece it together.

Children
Related