<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/50193/queued-write-without-response-questions</link><description>Hello Nordic team, 
 I&amp;#39;m using nrf52840 DK with SDK 15.3.0. 
 So I had an example sending data similarely to the ble_app_uart_c example that worked OK. 
 Basically I have a TX_FIFO with about 1KB data in it and I&amp;#39;m processing the data 20 bytes a time</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Thu, 25 Jul 2019 12:15:35 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/50193/queued-write-without-response-questions" /><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200515?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 12:15:35 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:fbb523e5-be24-4457-b90e-3f2b22a59e37</guid><dc:creator>Vidar Berg</dc:creator><description>&lt;p&gt;All packets are acked at the link layer, but write with response will also need to be acknowledged by the application layer running on top.&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200510?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 12:08:27 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:8118f321-da02-4f9a-aefa-4cbdd34a31af</guid><dc:creator>AKYR</dc:creator><description>&lt;p&gt;I can&amp;#39;t have access to the receiving device implementation so only supositions here :) But it is using an Uart protocol tu proceed Write without Response datas.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;But ins&amp;#39;t acking the transmition of data the point of Write with Response ? Since I&amp;#39;m using Write without Response I thought the packets weren&amp;#39;t &lt;span&gt;acknowledged ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200505?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 11:52:07 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:12cd19ae-f60f-46f1-a712-20ef2ade10b0</guid><dc:creator>Vidar Berg</dc:creator><description>&lt;p&gt;Glad to hear it was of some help:)&lt;/p&gt;
[quote userid="80049" url="~/f/nordic-q-a/50193/queued-write-without-response-questions/200465"]In fact the problem wasn&amp;#39;t in&amp;nbsp; an overlap on the process FIFO function but that it was &lt;strong&gt;too fast&lt;/strong&gt;&amp;nbsp;causing the other side device to lose packets ...[/quote]
&lt;p&gt;I&amp;#39;m not that familiar with your implementation, but just want to point out that packets that are successfully added to the output buffer (hvx function returns NRF_SUCCESS), will be re-transmitted on-air until they&amp;#39;re acked by the peer device. And the link will be considered lost if a packet is not acked withing the supervision timeout.&amp;nbsp;&lt;span style="font-size:1.125rem;letter-spacing:0px;"&gt;My point is that you may want to find what causes the packet loss on the receiving device.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
[quote userid="80049" url="~/f/nordic-q-a/50193/queued-write-without-response-questions/200465"]The clean implementation is to call the send function when a&amp;nbsp;BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE arrive to tell that a buffer is available. What I&amp;#39;m afraid of is that two events like this may overlap.&amp;nbsp;[/quote]
&lt;p&gt;They should not overlap as long as you are not using a preemptive scheduler for SD event processing. The ble callback is called in an interrupt context in&amp;nbsp;most of our examples.&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200465?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 10:16:31 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:607f98e5-94d7-4153-866e-f511b8455099</guid><dc:creator>AKYR</dc:creator><description>&lt;p&gt;Thanks Vidar your remarks made me realise something :)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In fact the problem wasn&amp;#39;t in&amp;nbsp; an overlap on the process FIFO function but that it was &lt;strong&gt;too fast&lt;/strong&gt;&amp;nbsp;causing the other side device to lose packets ...&lt;/p&gt;
&lt;p&gt;I&amp;#39;m indeed using another BLE device as peer.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I tried sending&amp;nbsp;&lt;span&gt;continuously send from your main loop and ignore the return value. This way it took me&amp;nbsp;&lt;strong&gt;43 msec&lt;/strong&gt; to send 1KB of data.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;When I wait for at least 4 buffers to be released, it takes &lt;strong&gt;86 msecs&lt;/strong&gt; to send 1KB of data.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The clean implementation is to call the send function when a&amp;nbsp;BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE arrive to tell that a buffer is available. What I&amp;#39;m afraid of is that two events like this may overlap.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Are successive&amp;nbsp;BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE and other BLE events stacking in some sort of FIFO to be dispatch&amp;nbsp;&lt;span&gt;one after the other (Which means i can synchronously execute my function to avoid overlaps). Or does the SoftDevice implementation could send an event while the execution of another one isn&amp;#39;t terminated yet ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;What I mean is, is this case possible ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src="https://devzone.nordicsemi.com/resized-image/__size/320x240/__key/communityserver-discussions-components-files/4/Fct_5F00_overlap.png" alt=" " /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200436?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 08:45:07 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:4f0bedfa-7cd7-4bc7-8131-addcd79973e3</guid><dc:creator>Vidar Berg</dc:creator><description>&lt;p&gt;There should be plenty of time to prepare the buffers for the next connection event when you get the tx complete signal. So I don&amp;#39;t think you will be able to further improve the throughput by changing the buffer handling. An easy way to confirm&amp;nbsp;this would be to continuously call the send from your main loop and ignore the return value to see how much data you can push through.&lt;/p&gt;
&lt;p&gt;Are you testing with Nordic nRF devices on both sides? The connection event length can have a big impact on how many packets you can send per event.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Below is a sniffer trace from a test I did with coded PHY. Notice the number of packets between the connection intervals:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://devzone.nordicsemi.com/resized-image/__size/320x240/__key/communityserver-discussions-components-files/4/pastedimage1564044300257v1.png" alt=" " /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200433?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 08:36:42 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:f564bef8-f3fc-44ed-a80b-8556d4005100</guid><dc:creator>AKYR</dc:creator><description>&lt;p&gt;Yes this was my first implementation that worked.&lt;/p&gt;
&lt;p&gt;But I need it to go faster to match the speed of a previous device.&lt;/p&gt;
&lt;p&gt;Working on the 4 buffers already upgraded the speed by ~250%.&lt;/p&gt;
&lt;p&gt;But waiting on the 4 buffers to be released to write again 4 times can be optimize and it is more or less what I&amp;#39;m lacking to match the speed I need :)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;When you write do the 4 consecutive writes at the beggining you have a buffer release each 2-3 seconds whereas when working on one buffer only there is a release every 6-7 seconds.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200427?ContentTypeID=1</link><pubDate>Thu, 25 Jul 2019 08:23:21 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:59466617-d0d5-4a96-8ca6-ff72e06c716e</guid><dc:creator>Vidar Berg</dc:creator><description>&lt;p&gt;Hello&amp;nbsp;&lt;span&gt;Aloïs,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Did you consider to just process the buffer&amp;nbsp;once when you insert a new data, and then&amp;nbsp;each&amp;nbsp;time&amp;nbsp;you get the&amp;nbsp;BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE&amp;nbsp;event? This should ensure sufficient utilization of the Softdevice&amp;#39;s transmit buffer. &lt;/span&gt;&lt;/p&gt;
[quote user="AKYR"]I would like to optimize this by doing a&amp;nbsp;sd_ble_gattc_write&amp;nbsp;whenever a buffer is available instead of waiting for the pool to be available.&amp;nbsp;[/quote]
&lt;p&gt;&amp;nbsp;&lt;span&gt;BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE would signalize that you have buffer space available.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Best regards,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Vidar&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200318?ContentTypeID=1</link><pubDate>Wed, 24 Jul 2019 14:48:01 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:8e53cbb4-d4c8-41f8-8d0d-ddfd332b3d62</guid><dc:creator>AKYR</dc:creator><description>&lt;p&gt;Hello Vidar and thanks for your answer,&lt;/p&gt;
&lt;p&gt;I have made this work by waiting for the&amp;nbsp;write_cmd_tx_queue_size number of buffers to be released before looping on&amp;nbsp;&lt;span&gt;sd_ble_gattc_write&amp;nbsp;again.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I would like to optimize this by doing a&amp;nbsp;sd_ble_gattc_write&amp;nbsp;whenever a buffer is available instead of waiting for the pool to be available.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;To do so I need to protect the calls to the function processing the buffers. I tried using&amp;nbsp;&lt;strong&gt;sd_mutex&lt;/strong&gt; functions but i got undefined behavior. Looks like the thread managing the BLE event is forever locked...&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;If you have some suggestion, here is the sample of the code that works:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;#define MINIMUM_AVAILABLE_WRITE_CMD_TX_BUFFER 4
uint8_t m_available_write_cmd_tx_buffer;

static void on_write_cmd_tx_rsp(const ble_gattc_evt_t *const p_ble_gattc_evt)
{
  ret_code_t err_code;
  const ble_gattc_evt_write_cmd_tx_complete_t *p_write_cmd_tx_rsp = &amp;amp;(p_ble_gattc_evt-&amp;gt;params.write_cmd_tx_complete);
  m_available_write_cmd_tx_buffer += p_write_cmd_tx_rsp-&amp;gt;count;
  if (m_available_write_cmd_tx_buffer &amp;gt;= MINIMUM_AVAILABLE_WRITE_CMD_TX_BUFFER)
  {
    err_code = process_tx_fifo_send_bytes();
    APP_ERROR_CHECK(err_code);
  }
}

static uint32_t process_tx_fifo_send_bytes()
{
  uint32_t nbReadableBytes = 0;
  uint32_t length = 0;
  static uint8_t data_array[BLE_MLDP_MAX_DATA_LEN];
  ret_code_t err_code;
  bool exitMainLoop = false;

  // Get nbReadableBytes availables in the FIFO
  err_code = app_fifo_read(&amp;amp;m_mldp_tx_fifo, NULL, &amp;amp;nbReadableBytes);
  if ((err_code != NRF_ERROR_NOT_FOUND) &amp;amp;&amp;amp; (err_code != NRF_SUCCESS))
  {
    APP_ERROR_CHECK(err_code);
  }

  if ((nbReadableBytes == 0) || err_code == NRF_ERROR_NOT_FOUND)
  {
    // No data to send in TX FIFO
    return NRF_SUCCESS;
  }

  while (m_available_write_cmd_tx_buffer &amp;amp;&amp;amp; nbReadableBytes)
  {
    length = fmin(nbReadableBytes, BLE_MLDP_MAX_DATA_LEN);

    err_code = app_fifo_read(&amp;amp;m_mldp_tx_fifo, data_array, &amp;amp;length);
    if ((err_code != NRF_ERROR_NOT_FOUND) &amp;amp;&amp;amp; (err_code != NRF_SUCCESS))
    {
      APP_ERROR_CHECK(err_code);
    }

    do
    {
      err_code = ble_mldps_c_string_send(&amp;amp;m_ble_mldps_c, data_array, length);
      if ((err_code != NRF_SUCCESS) &amp;amp;&amp;amp; (err_code != NRF_ERROR_BUSY))
      {
        NRF_LOG_ERROR(&amp;quot;Failed sending MLDP message. Error 0x%x. &amp;quot;, err_code);
        APP_ERROR_CHECK(err_code);
      }
    } while (err_code == NRF_ERROR_BUSY);

    if (err_code == NRF_SUCCESS)
    {
      m_available_write_cmd_tx_buffer--;
    }
    else if (err_code == NRF_ERROR_RESOURCES)
    {
      // Should reeval m_available_write_cmd_tx_buffer probably but will see this later
      break;
    }
    else
    {
      APP_ERROR_CHECK(err_code);
    }
    nbReadableBytes -= length;
  }
  return NRF_SUCCESS;
}

static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context)
{
  ret_code_t err_code;
  ble_gap_evt_t const *p_gap_evt = &amp;amp;p_ble_evt-&amp;gt;evt.gap_evt;

  pm_handler_secure_on_connection(p_ble_evt);

  switch (p_ble_evt-&amp;gt;header.evt_id)
  {
  case BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE:
  {
    on_write_cmd_tx_rsp(&amp;amp;(p_ble_evt-&amp;gt;evt.gattc_evt));
    APP_ERROR_CHECK(err_code);
  }
  break;

  default:
    break;
  }
}

int main(void)
{
  //Inits the FIFO with 1K bytes data
  init_fifo(&amp;amp;m_mldp_tx_fifo);
  //Inits write_cmd_tx_queue_size with the value MINIMUM_AVAILABLE_WRITE_CMD_TX_BUFFER
  ble_stack_init();
  //First call, the next will be from callback from BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event
  process_tx_fifo_send_bytes();
}&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And here is what I modified to try to make&amp;nbsp;work:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static void on_write_cmd_tx_rsp(const ble_gattc_evt_t *const p_ble_gattc_evt)
{
    ret_code_t err_code;
    const ble_gattc_evt_write_cmd_tx_complete_t *p_write_cmd_tx_rsp = &amp;amp;(p_ble_gattc_evt-&amp;gt;params.write_cmd_tx_complete);
    // mutex inited in main function
    while (sd_mutex_acquire(&amp;amp;m_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN)
    {
        nrf_delay_us(1000);
    }
    m_available_write_cmd_tx_buffer += p_write_cmd_tx_rsp-&amp;gt;count;
    /*if (m_available_write_cmd_tx_buffer &amp;gt;= MINIMUM_AVAILABLE_WRITE_CMD_TX_BUFFER)
    {*/
        err_code = process_tx_fifo_send_bytes();
        APP_ERROR_CHECK(err_code);
        sd_mutex_release(&amp;amp;m_mutex);
    //}
}

&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;My guess is there is something wrong with the way I&amp;#39;m waiting ...&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Do you have an advice on how to do this correctly ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Thanks in advance,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Alo&amp;iuml;s KYROU&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Queued Write without Response questions</title><link>https://devzone.nordicsemi.com/thread/200272?ContentTypeID=1</link><pubDate>Wed, 24 Jul 2019 11:56:30 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d5d20b49-738d-4cc5-8aa9-6f5cc84ad272</guid><dc:creator>Vidar Berg</dc:creator><description>&lt;p&gt;Hi&amp;nbsp;&lt;span&gt;Aloïs,&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;The &lt;a href="https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s140.api.v6.1.1/structble__gattc__conn__cfg__t.html#a5c4f01dc7a8236a3dcb31959a18e0e62"&gt;ble_gattc_conn_cfg_t::write_cmd_tx_queue_size&lt;/a&gt; value is &amp;quot;The guaranteed &lt;strong&gt;minimum&lt;/strong&gt; number of Write without Response &lt;br /&gt;that can be queued for transmission&amp;quot;. To increase the default size you can try to add the following code to ble_stack_init:&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &amp;amp;ram_start);
    APP_ERROR_CHECK(err_code);


    ble_cfg_t ble_cfg;

    memset(&amp;amp;ble_cfg, 0, sizeof(ble_cfg));
    ble_cfg.conn_cfg.conn_cfg_tag = APP_BLE_CONN_CFG_TAG;
    ble_cfg.conn_cfg.params.gattc_conn_cfg.write_cmd_tx_queue_size = 7;

    err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTC, &amp;amp;ble_cfg, ram_start);
    APP_ERROR_CHECK(err_code);
    
    ...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note: you may need to increase the app RAM base to leave more RAM to the Softdevice - See debug log for details. &lt;br /&gt;&lt;br /&gt;Best regards,&lt;br /&gt;Vidar&lt;/pre&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>