Reference: http://blog.chinaunix.net/uid-28852942-id-5753308.html
Explain 2 points:
1, why the Nordic of the 4.0 protocol stack ble can only send 20 bytes of application load data.
2. How to improve the sending rate when a large amount of data is sent
Analysis of transmission rate of BLE
According to the Bluetooth BLE protocol, the transfer rate of the physical layer physical layer is 1Mbps, equivalent to 125K bytes per second. In fact, it is only the benchmark transfer rate, the Protocol stipulates that BLE can not continuously transmit packets, otherwise it can not be called Low-power Bluetooth. Continuous transmission will naturally bring high power consumption. Therefore, the maximum transmission rate of Bluetooth is not determined by the operating frequency of the physical layer.
In the actual operation process, if the host line continuously send packets, either drop the packet seriously or connect an exception and disconnect.
In BLE, the transmission speed is affected by the connection parameters. The connection parameters are defined as follows:
1) connection interval. Bluetooth baseband is the frequency hopping work, the host and from the opportunity to agree on how long to do the frequency hopping connection, the connection can be data transmission. This connection is not the same as the connection state and the broadcast state. The host's connection when broadcasting from the machine is the active software behavior of the application layer. In the process of frequency hopping, the connection is the rules of Bluetooth baseband protocol, which is completely controlled by hardware and transparent to the application layer. Obviously, if the connection interval is shorter, the speed of transmission is increased. After the connection is uploaded, the Bluetooth baseband is in the dormant state to ensure low power consumption. It is a unit of 1.25 milliseconds.
2) connection delay. It is for low power considerations, allowing the machine in the process of frequency hopping ignoring the host's frequency hopping instructions, continue to sleep for a period of time. The host cannot be considered disconnected because of the machine sleep. It is a unit of 1.25 milliseconds. Obviously, the smaller the value, the higher the transmission speed.
The Bluetooth BLE protocol provides for a minimum connection parameter of 5, or 7.25 milliseconds, while the Android phone sets the minimum connection parameter to 8, or 10 milliseconds. The iOS rule is 16, or 20 milliseconds.
The connection parameters are entirely up to the host, but the request for update parameters from the machine can be accepted or rejected by the host. Android phones are accepted, and iOS is stricter, and the probability of rejection is high. In general, the connection parameter is set to 16, or 20 milliseconds, and the general transmission rate is 50* 20 = 1000 bytes per second. If each connection event transmits more packets, a higher transfer rate can be achieved.
1: Why the upper application load is up to 20 bytes per time
First understand the package format for the 4.0 link layer as follows:
PDU is the protocol data unit, that is, the load data of the link layer. The data sent by the application-tier user is here, but not all user data.
The BLE has a divided broadcast state and a connection state. Therefore, the above link layer frame may be broadcast data and may be the data after the connection. So there are two scenarios, one for the PDU in the broadcast channel and the other for the PDU in the data channel. We are mainly talking about the data frames in the data channel in the connection state, where the broadcast channel is briefly introduced.
In the broadcast state, the PDU in the broadcast frame is shown in the following illustration, contains 2 bytes of head, the subsequent payload is broadcast data, such as the usual set of device name, manufacturer custom data, etc. are in here, broadcast data must contain the address of the device, so the first 6 bytes in payload is the device address
Then look at the next link in the data channel in the link layer frame in the composition of the PDU, and broadcast channel frames similar to the PDU, but also has 2-byte head, followed by payload that is the link layer of real load data.
Mic is 4 bytes, only exists in the case of link encryption, for Message integrity verification, to prevent the message from being tampered with.
PS: Encrypted link in the empty packet does not exist mic
Protocols are layered, ble is the same, then the link layer of the Load data payload is the upper-layer protocol data frame, the link layer of the previous protocol is L2CAP, and l2cap frame format as follows as shown in the first 4 bytes are length and channel value respectively.
PS: If the Llid in the header above is 3, then the load is the link layer Control message instead of the L2CAP layer frame, not described here.
Similarly, the load data of the L2CAP layer information payload as the data frame of the upper protocol, for the transmission of user data, the device uses write data to the machine as the host, and the device as the machine is sending data to the host with notify or indication, At this point the load of the L2CAP layer contains the protocol frames of the upper att.
The discussion here is why the user sent the data is limited to the maximum 20 bytes, so understand the ATT protocol in the write,notify,indication command format is OK.
As shown in the figure above, contains 1 bytes opcode to indicate write,notify,indication. A 2-byte handle is a handle used to identify which attribute value is to be manipulated. After that, the real user sends the data.
So the final limit on how much data can be sent at a time is how much this att_mtu.
The default MTU in the specification is 23 bytes, which can actually be negotiated by command, while the default value of 23 is only supported by default in the Nordic 4.0 protocol stack, which limits the final upper-level data to be sent at 20 bytes.
The latest s132 protocol stack used by nrf52832 has already started to support the MTU negotiation so that more data can be transferred at one time.
In a comprehensive way, the data in the PDU of the link layer is shown in the following figure
PS: Review the first link layer frame structure can be seen in the PDU allows a length of 2-39, that is, at least 2 bytes, the payload data up to 37 bytes.
However, from the ATT protocol to the link layer, ATT up to 20 bytes of user data, plus 3-byte header, plus the L2cap 4-byte header, also 27 bytes, why there is a difference of 10 bytes.
The reason is because the PDU is divided into the broadcast channel of the PDU, and the data channel PDU,PDU in addition to the 2-byte header, the payload is 37 bytes, in the broadcast data PDU needs to include 6-byte broadcast address, other broadcast data is only 31 bytes. However, it is not required in the data channel, but for simplicity it limits the maximum number of 31 bytes of payload data in the data channel. On the other hand, if the link is encrypted, the PDU in the data channel will eventually contain a 4-byte mic, then the encrypted payload data becomes 27 bytes, and for convenience, even the unencrypted link sends the payload data to 27. That is the reason for the difference.
2: Since the data is sent up to 20 bytes at a time, how to increase the sending rate if more data is sent.
Simple handling with no request for send rate:
Some simple applications usually take a long time to send data, the data is not sent to 20 bytes, in this case, call the function directly to send data.
uint32_t ble_nus_string_send (ble_nus_t * p_nus, uint8_t * p_string, uint16_t length);
In another case, more data is sent, but there is no requirement for the rate of delivery. The simplest thing to do is to send it directly in a loop.
While (not finished sending) {
Ble_nus_string_send (data);
Delay_ms (n);
}
The more data that is usually sent, the longer it takes to delay_ms the delay, the more it will be tested on its own. Typically, you can use only a small amount of data, such as one hundred or two hundred bytes.
A more standardized approach should be to use the protocol stack to send the completion of the event Ble_evt_tx_complete, this event is at the bottom of the send data is completed by the protocol stack sent to the application layer. Then you can take advantage of this event, first send 20 bytes, when the bottom send completed after the upper layer received the send completion of the event and then send subsequent data.
Here's a simple implementation:
typedef struct BLK_SEND_MSG_TAG
{
uint32_t start; Start offset for Send
uint32_t Max_len; Total length of data to be sent
uint8_t *pdata;
}blk_send_msg;
Define a global variable
Blk_send_msg g_send_msg;
This function is called when the data is sent, passing in the buff and the length
uint32_t Ble_send_data (uint8_t *pdata, uint32_t len)
{
if (NULL = = Pdata | | Len <= 0) {return nrf_error_invalid_param;}
uint8_t Temp_len;
uint32_t Err_code;
G_send_msg.start = 0;
G_send_msg.max_len = Len;
G_send_msg.pdata = pdata;
Temp_len = len>20? 20:len;
Err_code = Ble_nus_string_send (&m_nus, pdata, Temp_len);
if (nrf_success = = Err_code) {
G_send_msg.start + = Temp_len; Send success to update start offset
}
return err_code;
}
This function completes the sending of subsequent data and places it in the process of receiving BLE_EVT_TX_COMPLETE events
uint32_t Ble_send_more_data ()
{
uint32_t Err_code;
uint32_t Dif_value;
Calculate how much more data has not been sent
Dif_value = G_send_msg.max_len-g_send_msg.start;
if (0 = Dif_value | | NULL = = g_send_msg.pdata) {return nrf_success;} All subsequent data sent out.
uint8_t Temp_len;
Temp_len = dif_value>20? 20:dif_value;
Err_code = ble_nus_string_send (&m_nus,g_send_msg.pdata + G_send_msg.start, Temp_len);
if (nrf_success = = Err_code) {
G_send_msg.start + = Temp_len;
}
return err_code;
}
uint8_t g_data[500];//100-byte data sent to the phone
int main (void)
{
for (uint16_t i = 0; i < i++)
{
if (i>=250)
G_data[i] = i-250;
Else
G_data[i] = i;
}
...
...
for (;;)
{
Power_manage ();
}
}
void User_main_handle (void * p_context)
{
if (send = = 1)
{
send = 0;
Ble_send_data (G_data, 100);
}
}
The first 20 bytes will be sent after the launch, and when the 20 bytes are sent, the Ble_evt_tx_complete event will be received and the sending of the remaining data should be added to the event processing.
Add the handling of this event directly in the On_ble_evt event handler
static void On_ble_evt (ble_evt_t * p_ble_evt)
{
Case Ble_evt_tx_complete:
Ble_send_more_data ();
Break
}
Because each connection event arrives, it is switched to another channel (frequency) for data transfer, and in the duration of the connection event, the interaction is on the same channel. That is, when each connection event arrives, the channel is switched, but the communication inside a connection event is always on that channel, so it can be distinguished by the channel number. This is basically two connection events that send data once, so the efficiency is low because the actual underlying baseband send is fast 1mbit/s, That is, 1us send 1bit. Theoretically simple calculation, here directly to the link layer of the longest package to calculate, 1+4+39+3 also only 47 bytes,
47*8 = 376bit is the actual time to send a packet of less than 1ms, the base with the start of the delivery and protocol stack some of the processing should also be a few MS, then a connection interval in addition to the first few milliseconds to send data, after the connection interval is closed. Wait until the connection interval arrives before continuing to send subsequent data. Then the sending efficiency is very low.
If you increase the number of packets sent in each connection interval, you can increase the send rate.
The previous method is to call every time the function is sent to wait for the completion of the event, in fact, the bottom of the stack should have a own send buff, can store a certain amount of data, we call send data after the protocol stack will put the data into this buff, and eventually send the data in this buff. If you can put as much data into the buff of the stack as possible before the next connection event arrives, he will send more data at the next connection interval.
Improve the delivery rate processing mode:
The SDK actually provides this method, but it's more obscure.
We use the Send function Ble_nus_string_send, which is actually called SD_BLE_GATTS_HVX this protocol stack API function, this function has a return value nrf_error_busy is busy, is processing. This should be the indication that the start is sent.
Then you can call this ble_nus_string_send function directly until it returns a nrf_error_busy error, indicating that it has started sending and cannot process the data you submitted.
In addition, the buff of the protocol stack is certainly limited, if we call this Send function, the next connection event is coming, then buff definitely fill dissatisfaction, the final error is nrf_error_busy, said already started to send, you can not fill out.
But if the call happens to be a long time before the next connection event arrives, then the buff in the stack will be filled up and the ble_error_no_tx_buffers error will occur.
Here just to introduce these two errors, the actual implementation can not need to determine whether these errors, because the delivery is 1.1 points sent by the packet, we can directly determine whether the Ble_nus_string_send function call is not returned nrf_success, if it is only update send offset, and continue to loop call the function to fill out more data to the protocol stack buff, if the return value is incorrect, then jump directly, do not update the send offset can be, and do not use to distinguish between the busy error or no buff error.
Code: The following two functions directly replace the above can be
uint32_t send_data (void)
{
uint8_t temp_len;
uint32_t Dif_value;
uint32_t err_code = nrf_success;
uint8_t *pdata = g_send_msg.pdata;
uint32_t start = G_send_msg.start;
uint32_t Max_len = G_send_msg.max_len;
//Circular forwarding, calling the Send function repeatedly as long as the return value is correct
do{
Dif_value = Max_len-start;
Temp_len = dif_value>20? 20:dif_value;
Err_code = Ble_nus_string_send (&m_nus, pdata + start, temp_len);
if (nrf_success = Err_code)
{
//= Update offset only if the return value is correct
//do not need to consider whether it is a busy error or no buff error
start = Temp_len;
}
//Call function is successful and there is still data that continues to invoke &NBSP
while ((nrf_success = = Err_code) && (Max_len-start) >0);
G_send_msg.start = start;
return err_code;
}
uint32_t Ble_send_data (uint8_t *pdata, uint32_t len)
{
if (NULL = = Pdata | | Len <= 0) {
return nrf_error_invalid_param;
}
uint32_t err_code = nrf_success;
G_send_msg.start = 0;
G_send_msg.max_len = Len;
G_send_msg.pdata = pdata;
Err_code = Send_data ();
The return value should be processed outside, and the return value, if it is success,
or nrf_error_busy or ble_error_no_tx_buffers should think it's right.
Since both of these errors occurred, we did not update the start offset, so later
will still be sent correctly.
Other situations should be handled according to the situation
return err_code;
}
uint32_t Ble_send_more_data ()
{
uint32_t Err_code;
uint32_t Dif_value;
Dif_value = G_send_msg.max_len-g_send_msg.start;
if (0 = Dif_value | | NULL = = G_send_msg.pdata)
{
return nrf_success; All subsequent data sent back directly
}
Err_code = Send_data ();
return err_code;
}
void User_main_handle (void * p_context)
{
if (send = = 1)
{
send = 0;
Ble_send_data (G_data, 500);
}
}
We're going to grab the air bag again. See if multiple packets are sent in each connection interval by the channel number you can see now a connection event sent a number of packets (up to 6), from the serial port through the data can also see a significant increase in speed.