Now that you understand how ISRs and the different types of threads act as the logical building blocks for an nRF Connect SDK application, it’s time to learn how you can safely exchange data between these blocks. There are several data passing mechanics, each with their own use cases. In this topic, we will cover message queues and FIFOs, which are commonly used and cover a variety of use cases.
Message queue
A message queue is a thread-safe data container that holds a fixed amount of data (messages), which can be accessed safely by multiple threads at the same time. The queue can store different types of data, such as variables, structs, pointers, or any other type of your preference. The maximum number of objects the queue can hold is limited only by the amount of available RAM in your system.
The kernel takes care of all the necessary operations and safeguards for adding new data to the queue and removing data from it. In other words, the message queue is a kernel object. This means you don’t have to worry about manually managing the queue’s internal mechanisms; the kernel manages it.
In addition, the queue also supports timeouts. You can use a timeout to:
Make a thread that puts data to the queue go to sleep if the queue is full.
Make a thread that gets data from the queue go to sleep if the queue is empty.
If the message queue is empty, any number of receiving threads may wait simultaneously, and when a data item becomes available, it is given to the highest-priority receiving thread. The same concept applies to sending threads when the message queue is full.
The message queue size (N in the above figure) is implemented internally by the kernel as a ring buffer and is statically defined. The data size (message size) must be a multiple of the data alignment. If you have an odd data size, you can either pad the data or use a compiler attribute (such as __aligned(4)) to specify minimum alignment.
While you can use a message queue with interrupts, be careful not to trigger a long or blocking operation, and never use the timeout options (for example, K_FOREVER) with interrupts.
How to use:
1. Decide on what data you want to put in a message.
This depends on the use-case for the application. Be mindful that the data type of a message is set statically, so you can’t change it dynamically later. You can define the message as a simple integer, a string, a sturct, or a union within a struct. By using a union within a struct, you can save memory compared to allocating separate memory space for each data type, especially when those data types are not required simultaneously. Here’s an example to illustrate the usage of a union nested within a struct:
Copy
struct MyStruct {int dataType; // Indicates the active data typeunion {int intValue;float floatValue;charstringValue[24]; } data;};
C
In the above example, the struct MyStruct contains a member dataType to indicate the active data type within the union data. Depending on the value of dataType, you can access the appropriate data member (intValue, floatValue, or stringValue) to retrieve or modify the corresponding value.
Another example that uses a message queue is the Bluetooth: Central and Peripheral HRS sample, where you can see that the messages are defined as struct bt_hrs_client_measurement structs.
2. Define the message queue and initialize it.
You can do both simultaneously with K_MSGQ_DEFINE(), which takes four parameters as shown below:
The below simple snippet shows how to define and initialize a message queue (device_message_queue) with 16 messages. Each element is 4 bytes in size (uint32_t). For the alignment, the snippet specifies sizeof(uint32_t), but you could simply pass 4.
Write the message using the k_msgq_put() function, which expects three parameters: the message queue defined in step 2, a pointer to a message of the type defined in step 1, and a timeout option. With the timeout option, you can decide what happens if the message queue is full:
K_FOREVER – The thread putting the data waits indefinitely until there is space in the message queue, meaning a receiving thread has consumed a message.
K_MSEC() – The thread putting the data waits for the specified time when using this option.
K_NO_WAIT – The thread putting the data does nothing. This means that the new data will not be added, and is lost. Alternatively, you can use this option to discard all messages in the queue and insert the new data. This is done by also checking the return value of the function, and manually calling k_msgq_purge().
4. Read a message from the message queue.
To read a message, use the k_msgq_get() function. Note that calling this function will remove the message from the message queue (known as popping the message). This is the recommended way as you don’t want your message queue to fill up. The alternative is use the k_msgq_peek() function, which reads a message without removing it from the message queue.
In both cases, messages are read in a First in, First out fashion. In other words, the first message pushed by a sender thread will be read first. The k_msgq_get() function takes three parameters: the message queue defined in step 2, a pointer to a local variable to hold incoming messages (of the type defined in step 2), and a timeout option. The timeout option allows you to decide what happens if the message queue is empty.
Suggested use: Use a message queue to transfer data items of known size and number between threads in an asynchronous manner. You can also use message queues with interrupts, but do so with care as discussed before.
FIFO
A FIFO is a kernel object that facilitates a traditional first-in, first-out (FIFO) queue structure. It enables threads and Interrupts Service Routines (ISRs) to add or remove any number of data items of varying sizes from the queue structure.
Unlike a message queue, you don’t need to specify the number of items in a FIFO or their size statically. Instead, you typically need to use the heap memory (k_malloc() and k_free()) to allocate space for the items on the fly. The FIFO will only hold the addresses of the data items, so the number of items in a FIFO can dynamically change and is only limited by memory, more precisely, the area allocated to the heap.
How to use
1. Specify the size of the heap memory pool to store the items.
The value should be set based on your application requirements. By default, CONFIG_HEAP_MEM_POOL_SIZE is set to zero. Therefore you need to set it to a value that represents the maximum number of elements you can have in the FIFO at a given time.
Copy
CONFIG_HEAP_MEM_POOL_SIZE=4096
Kconfig
Note that using heap and dynamic memory allocation in embedded systems firmware must be handled with extra care. It’s the responsibility of the firmware developer to make sure to free the popped items from the heap memory safely.
2. Define the FIFO.
You can use the K_FIFO_DEFINE() macro to statically define a FIFO.
Copy
K_FIFO_DEFINE(my_fifo);
C
Note that, unlike the message queue, you don’t need to specify the data type and size of the FIFO in the definition.
3. Specify the data type of the items.
The important point here is to define the data item as a struct where the first member is always a reserved void pointer. This is needed because the FIFO is implemented internally by the kernel as a simple linked list and the first word of an item should be reserved for use as a pointer to the next data item in the FIFO. The first example below shows a data item struct where the data has a defined size.
Add the item with the k_fifo_put() function. The API is shown below:
Copy
/* create data item to send */structdata_item_t *buf = k_malloc(sizeof(structdata_item_t));if (buf == NULL){/* Unable to locate memory from the heap */return ;}/* Populate the data item. This is usually done using memcpy() *//* send data to consumers */k_fifo_put(&my_fifo,buf);
C
A data item may be added to a FIFO by a thread or an ISR. The item is given directly to a waiting thread if one exists, or added to the FIFO’s queue if one does not exist. The only limit to the number of items that may be queued is the heap size memory allocated. This also means there is no timeout option available for the k_fifo_put() call, and the size of the heap must be set carefully to accommodate the use case.
5. Read a data item from a FIFO.
Read the item with the k_fifo_get() function. The API is shown below:
Calling k_fifo_get() removes the data from the FIFO. However, the developer must handle removing allocated memory from the heap memory by calling k_free() afterwards. Failing to properly free the memory allocated to consumed data items will result in a run-time error (heap overflow).
Using a timeout parameter allows a thread to wait for a data item to be given in case the FIFO’s queue is empty. Any number of threads may wait on an empty FIFO simultaneously. When a data item is added, it is given to the highest priority thread that has waited the longest.
The complete list of APIs for FIFO is available here.
Copy
structdata_item_t *rec_item = k_fifo_get(&my_fifo, K_FOREVER);/* process FIFO data item *//*Free */k_free(rec_item);
C
Suggested Use: Use a FIFO to asynchronously transfer data items of arbitrary number and size between threads in a First in, First out manner. You can also use FIFO with interrupts, but do so with care as discussed before.
Important
You don’t have to use the heap with FIFO if you have reservations about using dynamic memory allocation in your code. You can choose to allocate the memory needed for the items statically. Also, it’s important to remember that you can NOT add the same data item twice in a FIFO. This is likely to break the linked list used by the FIFO internally, and results in undefined behavior.
Nordic Developer Academy Privacy Policy
1. Introduction
In this Privacy Policy you will find information on Nordic Semiconductor ASA (“Nordic Semiconductor”) processes your personal data when you use the Nordic Developer Academy.
References to “we” and “us” in this document refers to Nordic Semiconductor.
2. Our processing of personal data when you use the Nordic Developer Academy
2.1 Nordic Developer Academy
Nordic Semiconductor processes personal data in order to provide you with the features and functionality of the Nordic Developer Academy. Creating a user account is optional, but required if you want to track you progress and view your completed courses and obtained certificates. If you choose to create a user account, we will process the following categories of personal data:
Email
Name
Password (encrypted)
Course progression (e.g. which course you have completely or partly completed)
Certificate information, which consists of name of completed course and the validity of the certificate
Course results
During your use of the Nordic Developer Academy, you may also be asked if you want to provide feedback. If you choose to respond to any such surveys, we will also process the personal data in your responses in that survey.
The legal basis for this processing is GDPR article 6 (1) b. The processing is necessary for Nordic Semiconductor to provide the Nordic Developer Academy under the Terms of Service.
2.2 Analytics
If you consent to analytics, Nordic Semiconductor will use Google Analytics to obtain statistics about how the Nordic Developer Academy is used. This includes collecting information on for example what pages are viewed, the duration of the visit, the way in which the pages are maneuvered, what links are clicked, technical information about your equipment. The information is used to learn how Nordic Developer Academy is used and how the user experience can be further developed.
2.2 Newsletter
You can consent to receive newsletters from Nordic from within the Nordic Developer Academy. How your personal data is processed when you sign up for our newsletters is described in the Nordic Semiconductor Privacy Policy.
3. Retention period
We will store your personal data for as long you use the Nordic Developer Academy. If our systems register that you have not used your account for 36 months, your account will be deleted.
4. Additional information
Additional information on how we process personal data can be found in the Nordic Semiconductor Privacy Policy and Cookie Policy.
Nordic Developer Academy Terms of Service
1. Introduction
These terms and conditions (“Terms of Use”) apply to the use of the Nordic Developer Academy, provided by Nordic Semiconductor ASA, org. nr. 966 011 726, a public limited liability company registered in Norway (“Nordic Semiconductor”).
Nordic Developer Academy allows the user to take technical courses related to Nordic Semiconductor products, software and services, and obtain a certificate certifying completion of these courses. By completing the registration process for the Nordic Developer Academy, you are agreeing to be bound by these Terms of Use.
These Terms of Use are applicable as long as you have a user account giving you access to Nordic Developer Academy.
2. Access to and use of Nordic Developer Academy
Upon acceptance of these Terms of Use you are granted a non-exclusive right of access to, and use of Nordic Developer Academy, as it is provided to you at any time. Nordic Semiconductor provides Nordic Developer Academy to you free of charge, subject to the provisions of these Terms of Use and the Nordic Developer Academy Privacy Policy.
To access select features of Nordic Developer Academy, you need to create a user account. You are solely responsible for the security associated with your user account, including always keeping your login details safe.
You will able to receive an electronic certificate from Nordic Developer Academy upon completion of courses. By issuing you such a certificate, Nordic Semiconductor certifies that you have completed the applicable course, but does not provide any further warrants or endorsements for any particular skills or professional qualifications.
Nordic Semiconductor will continuously develop Nordic Developer Academy with new features and functionality, but reserves the right to remove or alter any existing functions without notice.
3. Acceptable use
You undertake that you will use Nordic Developer Academy in accordance with applicable law and regulations, and in accordance with these Terms of Use. You must not modify, adapt, or hack Nordic Developer Academy or modify another website so as to falsely imply that it is associated with Nordic Developer Academy, Nordic Semiconductor, or any other Nordic Semiconductor product, software or service.
You agree not to reproduce, duplicate, copy, sell, resell or in any other way exploit any portion of Nordic Developer Academy, use of Nordic Developer Academy, or access to Nordic Developer Academy without the express written permission by Nordic Semiconductor. You must not upload, post, host, or transmit unsolicited email, SMS, or \”spam\” messages.
You are responsible for ensuring that the information you post and the content you share does not;
contain false, misleading or otherwise erroneous information
infringe someone else’s copyrights or other intellectual property rights
contain sensitive personal data or
contain information that might be received as offensive or insulting.
Such information may be removed without prior notice.
Nordic Semiconductor reserves the right to at any time determine whether a use of Nordic Developer Academy is in violation of its requirements for acceptable use.
Violation of the at any time applicable requirements for acceptable use may result in termination of your account. We will take reasonable steps to notify you and state the reason for termination in such cases.
4. Routines for planned maintenance
Certain types of maintenance may imply a stop or reduction in availability of Nordic Developer Academy. Nordic Semiconductor does not warrant any level of service availability but will provide its best effort to limit the impact of any planned maintenance on the availability of Nordic Developer Academy.
5. Intellectual property rights
Nordic Semiconductor retains all rights to all elements of Nordic Developer Academy. This includes, but is not limited to, the concept, design, trademarks, know-how, trade secrets, copyrights and all other intellectual property rights.
Nordic Semiconductor receives all rights to all content uploaded or created in Nordic Developer Academy. You do not receive any license or usage rights to Nordic Developer Academy beyond what is explicitly stated in this Agreement.
6. Liability and damages
Nothing within these Terms of Use is intended to limit your statutory data privacy rights as a data subject, as described in the Nordic Developer Academy Privacy Policy. You acknowledge that errors might occur from time to time and waive any right to claim for compensation as a result of errors in Nordic Developer Academy. When an error occurs, you shall notify Nordic Semiconductor of the error and provide a description of the error situation.
You agree to indemnify Nordic Semiconductor for any loss, including indirect loss, arising out of or in connection with your use of Nordic Developer Academy or violations of these Terms of Use. Nordic Semiconductor shall not be held liable for, and does not warrant that (i) Nordic Developer Academy will meet your specific requirements, (ii) Nordic Developer Academy will be uninterrupted, timely, secure, or error-free, (iii) the results that may be obtained from the use of Nordic Developer Academy will be accurate or reliable, (iv) the quality of any products, services, information, or other material purchased or obtained by you through Nordic Developer Academy will meet your expectations, or that (v) any errors in Nordic Developer Academy will be corrected.
You accept that this is a service provided to you without any payment and hence you accept that Nordic Semiconductor will not be held responsible, or liable, for any breaches of these Terms of Use or any loss connected to your use of Nordic Developer Academy. Unless otherwise follows from mandatory law, Nordic Semiconductor will not accept any such responsibility or liability.
7. Change of terms
Nordic Semiconductor may update and change the Terms of Use from time to time. Nordic Semiconductor will seek to notify you about significant changes before such changes come into force and give you a possibility to evaluate the effects of proposed changes. Continued use of Nordic Developer Academy after any such changes shall constitute your acceptance of such changes. You can review the current version of the Terms of Use at any time at https://academy.nordicsemi.com/terms-of-service/
8. Transfer of rights
Nordic Semiconductor is entitled to transfer its rights and obligation pursuant to these Terms of Use to a third party as part of a merger or acquisition process, or as a result of other organizational changes.
9. Third Party Services
To the extent Nordic Developer Academy facilitates access to services provided by a third party, you agree to comply with the terms governing such third party services. Nordic Semiconductor shall not be held liable for any errors, omissions, inaccuracies, etc. related to such third party services.
10. Dispute resolution
The Terms of Use and any other legally binding agreement between yourself and Nordic Semiconductor shall be subject to Norwegian law and Norwegian courts’ exclusive jurisdiction.