Silicon Labs Silicon Labs Community
silabs.com
Language
  • 简体中文
  • 日本語
  • MCUs
    • 8-bit MCU
    • 32-bit MCU
  • Wireless
    • Bluetooth
    • Proprietary
    • Wi-Fi
    • Zigbee & Thread
    • Z-Wave
  • More Products
    • Interface
    • Isolation
    • Power
    • Sensors
    • Timing
  • Development Tools
    • Simplicity Studio
    • Third Party Tools
    • Thunderboard
  • Expert's Corner
    • Announcements
    • Blog
    • General Interest
    • Projects
  1. Community - Silicon Labs
  2. Blog

Official Blog of Silicon Labs

  • Show More
    Publish
    • Immediately
    • Draft
    • At scheduled date and time
     
      • How to use Bluetooth Low-Energy for Wi-Fi commissioning

        Juan Benavides | 06/179/2019 | 03:10 PM

        Introduction

        If your Wi-Fi connected product is headless, then technically speaking, the obvious solution for exchanging the Wi-Fi credentials required to commission the product onto the customer’s Wi-Fi network is SoftAP.

        SoftAP stands for Software Enabled Access Point. Some product manufacturers use SoftAP to create a temporary access point for the unique purpose of getting the customer’s Wi-Fi network name (SSID), security mode and password as illustrated in the following diagram:

        Figure 1 Wi-Fi commissioning using SoftAP

        The customer connects his smartphone to the connected product’s SoftAP, and then uses either a mobile app or web page that displays the list of Access Points available to select and enter the password.

        The connected product, once it has the customer’s Wi-Fi network information (SSID, Password and Security Mode) it connects to the Access Point.

        The Access Point lets the connected product join the network and gain access to the Internet.

         

        SoftAP used to be the Wi-Fi commissioning solution of choice for early IoT devices, but the two fundamental problems listed below have made it an unreliable solution:

        • Once the customer’s smartphone connects to a SoftAP, it will lose Internet connection.
        • If the customer’s smartphone loses Internet connection, then the phone’s logic may switch to a different Access Point and thus disconnect from the product.

         

        In order to improve the out-of-box experience, product manufacturers have turned to Bluetooth as a solution for commissioning. For the Wi-Fi connected products that already support Bluetooth for a different purpose (e.g. streaming audio or video) then Bluetooth is the go-to mechanism for Wi-Fi commissioning as illustrated in the following diagram:

        Figure 2 Wi-Fi commissioning using Bluetooth

        The customer installs the product manufacturer’s BLE Mobile Application and pairs with the connected product.

        The mobile application displays a list of Access Points, the customer selects one of them and enters its password.

        The connected product, once it has the customer’s Wi-Fi network information (SSID, Password and Security Mode) it connects to the Access Point.

        The Access Point lets the connected product join the network and gain access to the Internet.

         

        Because of how important the out-of-box experience is to a product’s success, many product manufacturers should consider going to the extremes of adding a Bluetooth chip exclusively to support the Wi-Fi commissioning of the product. Silicon Labs has BLE chips such as the EFR32MG12 that not only supports BLE but also other wireless protocols in the same chip.

        Whatever your case may be, this blog shows you one simple way to use Bluetooth for Wi-Fi commissioning by covering the following topics:

        • BLE GATT Server Design
        • BLE Mobile Application
        • Theory of Operation

         

        Prerequisites

        It is assumed that you are familiar with Web Technologies such as HTML and JavaScript, and more important, that you are familiar with your own Wi-Fi and Bluetooth stacks regarding the following topics:

        BLE Stack

        • Tool to create a BLE Generic Attribute Profile (GATT) database.
        • Callbacks to handle the BLE Characteristics Read and Write requests.
        • API to send Notifications to BLE clients.

        Wi-Fi Stack

        • API to start a Wi-Fi scan
        • Callback to handle the Wi-Fi scan results
        • API to join an Access Point
        • API to store the Access Point’s credentials in non-volatile memory

         

        Hardware Requirements

        • Any Wi-Fi chip/module (e.g. Silicon Labs WF200 module)
        • Any BLE chip/module (e.g. Silicon Labs EFR32MG12 chip)
        • Web Server to host a static web page (e.g. AWS account)

         

        Software Requirements

        • BLE GATT Configurator
        • Google Chrome Web Browser

         

        BLE GATT Server Design

        Using your GATT Configuration tool, you need to create two BLE GATT Services:

        Wi-Fi Scanner Service

        This service allows a Bluetooth Client to initiate the scanning of visible Wi-Fi networks and its corresponding information such as SSID, Security Mode and Signal Strength.

        Wi-Fi Configurator Service

        Allows a Bluetooth Client to configure the connected product with the information necessary to connect to a Wi-FI Access Point (i.e. SSID, Security Mode and Password).

         

        The table below shows an example of such GATT database. Notice that the UUIDs have been truncated for sake of simplicity. In reality, they should be 128-bit and it’s important you keep them documented in a table similar to this.

         

        BLE GATT Configuration

        Service Characteristic
        Name UUID Name BLE Variable UUID Value Settings Properties
        Type Length Read Write Indicate
        Wi-Fi Configurator 2b42…5ab3 State gattdb_wifi_ cfg_state c519…07da user 1 Yes Yes Yes
        SSID gattdb_wifi_ cfg_ssid a4a3…5da7 user 32 No Yes No
        Password gattdb_wifi_ cfg_password 1d4b…c315 user 16 No Yes No
        Security gattdb_wifi_ cfg_security 0420…278c user 1 No Yes No
        Wi-Fi Scanner 9b51…af7d State gattdb_wifi_ scan_state cc89…81c8 user 1 Yes Yes Yes
        AP List Part 1 gattdb_wifi_ scan_ap_list_1 d07c…d141 user 255 Yes No No
        AP List Part 2 gattdb_wifi_ scan_ap_list_2 7e44…99d6 user 255 Yes No No
        AP List Part 3 gattdb_wifi_ scan_ap_list_3 d3c1…b004 user 255 Yes No No
        AP List Part 4 gattdb_wifi_ scan_ap_list_4 a2a0…7ee0 user 255 Yes No No
        AP List Part 5 gattdb_wifi_ scan_ap_list_5 8907…2823 user 255 Yes No No

        Table 1 BLE GATT Configuration

         

        Characteristics

        Both services have a characteristic called State that is used to communicate the state of the corresponding operation. Therefore, both characteristics support Read, Write and Notification.

        Here are the possible states:

        // Wi-Fi Scanner State Machine States
        const WIFI_SCANNER_STATE_IDLE     = 0;
        const WIFI_SCANNER_STATE_SCAN     = 1;
        const WIFI_SCANNER_STATE_SCANNING = 2;
        const WIFI_SCANNER_STATE_SCANNED  = 3;
        const WIFI_SCANNER_STATE_ERROR    = 4;
        
        // Wi-Fi Config State Machine States
        const WIFI_CONFIG_STATE_IDLE      = 0;
        const WIFI_CONFIG_STATE_SAVE      = 1;
        const WIFI_CONFIG_STATE_SAVING    = 2;
        const WIFI_CONFIG_STATE_SAVED     = 3;
        const WIFI_CONFIG_STATE_JOIN      = 4;
        const WIFI_CONFIG_STATE_JOINING   = 5;
        const WIFI_CONFIG_STATE_JOINED    = 6;
        const WIFI_CONFIG_STATE_ERROR     = 7;

        Code Listing 1 Wi-Fi Scanner and Wi-Fi Configurator Service States

         

        The rest of characteristics are meant to exchange the actual data. Notice that the results from the Wi-Fi Scanner need to be communicated in multiple characteristics because of limitations on the maximum length of a BLE Characteristic’s value (i.e. 255 bytes).

        Here is an example of the value exchanged by one of such characteristics. It is in JSON format and it is sorted by signal strength such that the closest access point is displayed on top:

        [
         {
           "ssid": "WHIFERAPPS",
           "rcpi": "156",
           "sec": "2"
         },
         {
           "ssid": "SiliconLabsMobile",
           "rcpi": "129",
           "sec": "2"
         },
         {
           "ssid": "SiliconLabsGuest",
           "rcpi": "128",
           "sec": "2"
         },
         {
           "ssid": "SiliconLabs",
           "rcpi": "118",
           "sec": "2"
         },
         {
           "ssid": "DIRECT-bc-HP M203 LaserJet",
           "rcpi": "108",
           "sec": "2"
         },
         {
           "ssid": "GLC-WESTON",
           "rcpi": "86",
           "sec": "2"
         },
         {
           "ssid": "FCAAC",
           "rcpi": "81",
           "sec": "2"
         },
         {
           "ssid": "KENGOLENDESIGNS",
           "rcpi": "67",
           "sec": "2"
         },
         {
           "ssid": "DreamPlanTrack",
           "rcpi": "60",
           "sec": "2"
         },
         {
           "ssid": "Ameriprise Guest",
           "rcpi": "60",
           "sec": "2"
         },
         {
           "ssid": "Ketamine1290",
           "rcpi": "55",
           "sec": "2"
         }
        ]

        Code Listing 2 List of Access Points in JSON format

         

         

        BLE Mobile Application

        For the BLE mobile application you have the choice of creating a native BLE application to support iOS and Android as a minimum or use Web Bluetooth.

        Web Bluetooth allows a web browser (e.g. Google Chrome) to see and interact directly with Bluetooth devices as illustrated in the following image:

        Figure 3 Web Bluetooth

        The BLE mobile application is actually a web page (i.e. HTML and JavaScript) and it's hosted in the product manufacturer's web server. 

        The customer opens a web browser (e.g. Google Chrome) and goes to the web server’s URL to download the web page.

        The web page displays a list of nearby BLE devices and pairs with the BLE device you want to connect.

         

        The advantages of Web Bluetooth over traditional native BLE mobile applications are:

        • One single web page hosted in a web server allows much easier updates.
        • One single web page can be used to support all mobile devices
        • One single web page can also be used not only to connect via Web Bluetooth but also to host in the connected device in SoftAP mode for a line of products that does not support BLE.
        • Developers with HTML and JavaScript skills are a lot easier to find than those with BLE Mobile App development skills for iOS and Android.

         

        If Web Bluetooth is used instead of a traditional native BLE mobile app, then the revised block diagram for the Wi-Fi commissioning system using Web BLE would look like the following:

        Figure 4 Wi-Fi commissioning using Web BLE

        The BLE mobile application is actually a web page (i.e. HTML and JavaScript) and it's hosted in the product manufacturer's web server. The web page allows the user to select from a list of Wi-Fi Access Points and enter the Access Point’s password.

        The customer opens a web browser (i.e. Google Chrome) and goes to the web server’s URL to download the web page. The web page displays a list of nearby BLE Devices and pairs with the device you want to connect.

        The web page sends a scan request to the BLE device which in turn calls the Wi-Fi stack APIs to scan and get the results. Finally, the web page displays a list of Access Points, the customer selects one of them and enters its password.

        The connected product, once it has the customer’s Wi-Fi network information (SSID, Password and Security Mode) it connects to the Access Point.

        The Access Point lets the connected product join the network and gain access to the Internet.

         

        Theory of Operation

        The diagram below summarizes the way the entire system works:

        Connected Product

        (BLE GATT Server)

         

        Smartphone

        (BLE Client)

         

         

        To initiate a Wi-Fi Scan procedure, the BLE Client sends a request to write:

        BLE GATT characteristic: gattdb_wifi_scan_state 
        Value: WIFI_SCANNER_STATE_SCAN

        The BLE GATT Server receives the request to write:

        BLE GATT characteristic: gattdb_wifi_scan_state
        Value: WIFI_SCANNER_STATE_SCAN

         

         
        If the Wi-Fi Scanner State Machine's state is WIFI_SCANNER_STATE_IDLE then it will call the Wi-Fi API to start a scan

         

         
        The Wi-Fi stack returns the scan results by calling a callback function. There, the list of access points is stored in a data structure with a global scope for general purposes

         

         
        The Wi-Fi stack signals the completion of the scan procedure by calling another callback function

         
        The embedded application prepares a multi-part JSON payload to be stored in a data structure with a global scope for BLE purposes

         

         
        The embedded application sets the characteristic gattdb_wifi_scan_state to WIFI_SCANNER_STATE_SCANNED and notifies BLE clients that this characteristic has changed

         

         
         

         

        The BLE client receives the notification and sends requests to read the following BLE GATT characteristics: 
        gattdb_wifi_scan_ap_list_1
        gattdb_wifi_scan_ap_list_2
        gattdb_wifi_scan_ap_list_3
        gattdb_wifi_scan_ap_list_4
        gattdb_wifi_scan_ap_list_5
        The BLE GATT server receives the requests to read the following characteristics and responds with the values:
        gattdb_wifi_scan_ap_list_1
        gattdb_wifi_scan_ap_list_2
        gattdb_wifi_scan_ap_list_3
        gattdb_wifi_scan_ap_list_4
        gattdb_wifi_scan_ap_list_5

         

         
         

         

        The BLE client receives indication that the values of the characteristics have changed and calls the corresponding event handlers
         

         

        The handler for the event characteristic value changed parses the JSON payload into a JavaScript object, merges each part into a single array of access points, sorts the array by signal strength and populates the drop-down box
         

         

        The customer selects the Wi-Fi Access Point he wants to join from the web page's drop-down box and enters the password in an input box
         

         

        The BLE client sends a request to write the following BLE characteristics:
        gattdb_wifi_cfg_ssid
        gattdb_wifi_cfg_password
        gattdb_wifi_cfg_security
        The BLE GATT server receives the requests to write to the following characteristics:
        gattdb_wifi_cfg_ssid
        gattdb_wifi_cfg_password
        gattdb_wifi_cfg_security

         

         
        The BLE GATT server updates the variables only if there is not any Wi-Fi configuration in progress

         

         
        The BLE GATT server changes the state of the Wi-Fi Configuration state machine to WIFI_CFG_STATE_ONGOING and sends notification to the BLE client that this state has changed

         

         
         

         

        The BLE client receives the notification and to save the Access Point credentials in non-volatile memory, it sends a request to write: 
        BLE GATT characteristic: gattdb_wifi_cfg_state
        Value: WIFI_CFG_STATE_SAVE
        The BLE GATT server receives the request to write:
        BLE GATT characteristic: gattdb_wifi_cfg_state
        Value: WIFI_CFG_STATE_SAVE and if not saving process in progress, it calls the API to store variables in non-volatile memory
         
        The BLE GATT server changes the state of the Wi-Fi Configuration state machine to WIFI_CFG_STATE_SAVED and sends notification to the BLE client that this state has changed

         

         
         

         

        The BLE client receives the notification and to restart the Wi-Fi interface and connect to the new Access Point, it sends a request to write: 
        BLE GATT characteristic: gattdb_wifi_cfg_state
        Value: WIFI_CFG_STATE_JOIN
        The BLE GATT server receives the request to write:
        BLE GATT Characteristic: gattdb_wifi_cfg_state
        Value: WIFI_CFG_STATE_JOIN and calls the Wi-Fi API to join a network
           

        Figure 5 Flow Diagram

         

        Source Code

        To keep the blog relevant to any Wi-FI and BLE stack, the source-code-level details on how to perform the different functions outlined in the previous flow diagram have been omitted. 

        The following Wi-Fi and BLE APIs are better described in their corresponding documentation and are beyond the scope of this blog:

        • Callbacks to handle the BLE Characteristics Read and Write requests.
        • API to send Notifications to BLE clients.
        • API to start a Wi-Fi scan
        • Callback to handle the Wi-Fi scan results
        • API to join an Access Point
        • API to store the Access Point’s credentials in non-volatile memory

        The web page that makes the actual BLE mobile application however, is provided as an attachment (web_ble.zip) and if you like this approach, feel free to modify it and use it under the terms of the Apache License.

        Figure 6 Web Bluetooth Mobile Application

         

        Security Considerations

        Because of the sensitive nature of the information exchanged, you should consider using the security features of your Bluetooth stack:

        • Pairing
        • Bonding
        • Device Authentication
        • Encryption
        • Data Signing

        Check your Bluetooth stack documentation for more information on  these security features.

         

         

        Further Reading

        • Silicon Labs GATT Configurator User's Guide: https://www.silabs.com/documents/login/user-guides/ug365-GATT-configurator-users-guide.pdf
        • Silicon Labs BLE API Reference: https://www.silabs.com/documents/login/reference-manuals/bluetooth-api-reference.pdf
        • Silicon Labs Non-Volatile Data Storage Fundamentals: https://www.silabs.com/documents/public/user-guides/ug103-07-non-volatile-data-storage-fundamentals.pdf
        • Silicon Labs Wi-Fi Solutions: https://www.silabs.com/products/wireless/wi-fi
        • Web Bluetooth Samples: https://googlechrome.github.io/samples/web-bluetooth/
        • Interacting with Bluetooth devices on the Web: https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web

        web_ble_v2.zip

      • Timing 201 #1: The Case of the Phase Noise That Wasn't - Part 1

        kgsmith | 06/178/2019 | 04:50 PM

        Introduction

        Hello and welcome to the inaugural article for a new blog series, Timing 201. My previous blog series, Timing 101, ran for 12 articles over a period of roughly a year and a half.

        As the name suggests, I intend to discuss timing topics with a little bit more breadth and depth than might be considered “Timing 101” material. However, that does not mean I won’t cover introductory material, if and when it’s called for, especially if readers ask for it.

        When More is Less

        You may recall that clock buffers are typically specified in terms of additive jitter. That is because they do not have an intrinsic phase noise source such as an XO (crystal oscillator) or a PLL’s VCXO. They consist of only amplifiers and perhaps dividers. So we would generally expect an XO followed by a clock buffer to have at least somewhat increased phase noise or jitter compared to the XO alone.

        All things being equal, this will be the case, except when the amplifier has sufficiently high gain to act as a limiting amplifier or LA. In these situations, the apparent measured source + LA phase noise may actually decrease. I ran in to this phenomena years ago evaluating various clock + buffer combinations. The results weren’t always making sense and it turned out at the time that some of the noise I was measuring was in fact, not really phase noise, hence the title of this case.

        To see how this might happen, let’s touch on what is meant here by apparent measured phase noise.

        Phasors ON

        You may recall I discussed modulation spurs previously in Timing 101 #7: The Case of the Spurious Phase Noise Part II. In that article, I considered carriers with relatively small amounts of AM (Amplitude Modulation) and narrowband FM (Frequency Modulation) or equivalent PM (Phase Modulation). The general ideas comparing AM and FM/PM spurs also apply to AM and FM/PM noise.

        One topic I did not touch on at that time was the phasor or phase vector representations of AM and NB FM as shown below. The carrier vector is shown as a thick red arrow and the modulation LSB (lower sideband) and USB (upper sideband) vector components are shown as thinner blue arrows. The vector sum or resultant of the modulation is the thick blue arrow. The modulating frequency is f<subscript>M and the rotating arrows indicate the change in the modulation vectors over time versus the carrier. For the figures below the overall vector sum is the geometric addition of the carrier + modulation resultant.

        What’s useful about the phasor representation is that it indicates that random noise modulating the carrier can be regarded as consisting of both AM and PM components. That is, components of the noise contributing to carrier magnitude changes are the AM components. Likewise, components of the noise contributing to carrier angle changes are FM or equivalent PM components.

        You may see authors emphasizing this distinction by using script L(f) or ℒ(f) to refer to PM noise or “real” phase noise and script M(f) or ℳ(f) to refer to AM noise. The first paper I know of using this notation is:

        Spectral Density Analysis: Frequency Domain Specification and Measurement of Signal Stability, by Donald Halford, John H. Shoaf, and A. S. Risley, National Bureau of Standards, Boulder, CO, published in 27th Annual Symposium on Frequency Control, 12-14 June 1973, https://tf.nist.gov/general/pdf/1558.pdf

        The magnitude of noise that is AM + PM will be the RSS or Root Sum Square of the individual modulation contributions. Instruments that treat these noise components the same will then “see” this RSS noise directly as phase noise. This is what is meant by apparent phase noise and it is a particular issue for Spectrum Analyzers as discussed below.

        The Spectrum Analyzer in Brief

        As I noted in Timing 101 #7, Spectrum Analyzers do not preserve phase information and so a low modulation AM spur appears similar to a narrow band low modulation FM spur. 

        Below is a block diagram for a classic swept spectrum analyzer which suggests why. It is essentially a calibrated frequency selective peak responding voltmeter. The difference in phase between the DUT (Device Under Test) and the LO (Local Oscillator) inputs at the mixer is arbitrary. The Spectrum Analyzer doesn’t “know” anything about their relative phases and AM and PM cannot be distinguished. 

        The Phase Noise Analyzer in Brief

        By contrast, the Phase Noise Analyzer is much less susceptible to AM. The simplified block diagram below gives the basic idea behind the method typically used by Phase Noise Analyzers and Signal Source Analyzers. The mixer is usually a double-balanced mixer to suppress even-order mixing products.

        Note that, unlike the Spectrum Analyzer, there is a PLL (Phase Lock Loop) that enforces a specific phase relationship between the DUT and the Reference. Further, it can be shown that AM and PM can be distinguished as follows.

        • If phase offset = 90˚ then the mixer detects PM and suppresses AM
        • If phase offset = 0˚ then the mixer detects AM and suppresses PM

        As designed, the Phase Noise Analyzer will be superior to the spectrum analyzer for rejecting AM.

        Take It to the Limit

        So how exactly does the limiting amplifier or LA help us here? The clue lies in the behavior of the LA: It removes, or at least minimizes, amplitude variation from the clock signal. Therefore, if a source has both AM and PM noise components, then an ideal limiter will remove the AM component noise leaving only the PM noise (the genuine phase noise) behind.  The exaggerated sketch below gives the basic idea.

        Now getting back to the original work which prompted this post:

        If a clock source had apparent phase noise that included both AM and PM noise contributions, then following it with a high gain clock buffer or LA would strip away the AM resulting in less than expected measured phase noise. Rather than yielding additive jitter the new component apparently yielded “subtractive” jitter. Thus the case of the phase noise that wasn’t.

        When should AM be considered when making phase noise measurements?

        The short answer is that AM is always a potential consideration when one is making careful jitter and phase noise measurements. Having a limiter on one’s lab bench is as important as a balun.

        However, there are specific instances where AM can be more of an issue than others.

        1. Measuring phase noise using a spectrum analyzer or any other instrument that does not sufficiently reject AM.

        A limiter can be valuable even when working with a Phase Noise Analyzer by suppressing AM beyond the rejection capability of the mixer. This may be necessary when measuring very low phase noise sources.

        2. Measuring low frequency low phase noise sources.

        You may recall the 20log(N) rule, i.e. if the carrier frequency of a clock is divided down by a factor of N then we expect the phase noise to decrease by 20log(N); however, this rule only applies to phase noise. If there is significant AM noise also then this component will loom larger, as we decrease the carrier frequency, and potentially impact measurements.

        3. Measuring a source known or suspected to have AM noise.

        Clock sources with high common mode noise fall in to this category. For example, we specifically inject power supply ripple when testing oscillator power supply rejection. This is why you see a limiting amplifier shown in Figure 5. PSRR Setup in AN491: Power Supply Rejection For Low-Jitter Clocks. See the highlighted block in the diagram below which comes from that app note.

         

        4. Troubleshooting

        Finally, the ability to distinguish between phase noise and spurs, and AM noise and spurs, can be very helpful when troubleshooting a system and determining the root cause of performance issues. Additional testing may also be needed to determine how sensitive the ultimate receiver is to clock impairments that include AM noise.

        Conclusion

        I hope you have enjoyed this Timing 201 article. In the next article, I will give some measurement examples and offer a few rules of thumb.

        As always, if you have topic suggestions for this blog or timing-related questions, please send them to kevin.smith@silabs.com with “Timing 201” in the subject line. I will give them consideration and see if I can fit them in. Thanks for reading. Keep calm and clock on.

        Cheers,

        Kevin

         

         

         

         

      • Kernel 201: WebBluetooth App

        Mark Mulrooney | 06/178/2019 | 03:02 PM

        This blog is part of the Kernel 201: Designing a Dynamic Multiprotocol Application with Micrium OS. The table of contents for this blog series can be found here.

        Note: It is recommended to download the Kernel 201 WebBluetooth Application and have the code in front of you as you go through this blog post. The code can be downloaded on the Kernel 201: Project Resources page. 

         

        Introduction

        WebBluetooth API is a Javascript API that provides Bluetooth support for web pages. The API, which was first published in 2015, is still considered Experimental but it has been supported in Google Chrome since version 56 and Opera since version 43 for desktop and Android devices. At the time of writing this, WebBluetooth API is still not supported on Edge, Firefox, or Safari and it does not work on iPhones at all.

        The advantage of using the WebBluetooth API is it allows for a quick way to develop an application that can run on desktop or mobile from the same code. In the past if a developer wished to interact with Bluetooth on both desktop and mobile, typically separate applications were needed. The code provided on the Project Resources page runs on any web server that has HTTPS enabled, and will work on desktop and Android.

        The full WebBluetooth API can be found here.

         

        WebBluetooth App

        The WebBluetooth App that interacts with the Kernel 201 Application is based off of StartBootstrap’s SB Admin 2 template. The template provides the necessary css code to support small screens such as mobile devices and large screens like desktop monitors.

        The code included in the WebBluetooth App has been paired back considerably from the full SB Admin template to only include the files needed. If you wish to view the entire SB Admin template, it can be found here.

        There are only 3 files and one folder in the WebBluetooth App specific to the Kernel 201 Application. The rest of the files are part of Bootstrap, jQuery and SB Admin files that do not need modification.

        • index.html & images directory
        • css/bt.css
        • js/bt.js

         

        index.html & images directory

         

        The entire WebBluetooth App’s HTML code is located in this one file. The app’s display is very simplistic, just display fields and buttons for control. All of the different views for the application are already in the index.html page, the Javascript code controls the showing and hiding of different elements based on the different states of the application.

        The images folder holds the different images shown in the WebBluetooth App. In the current version of the application it is used only to hold the logos as seen above.

         

        bt.css

        The bt.css file is the main css file from the SB Admin template. There have been minor changes such as color scheme, screen size and card behavior made to the file.

         

        bt.js

        This file is what controls the behavior of the WebBluetooth App. It contains the WebBluetooth API code as well as control of what elements are shown on the index.html page.

         

        After clicking the Lab 1 button, a user is brought to the screen above. Clicking connect will trigger the function btConnect() in bt.js. This function begins making WebBluetooth API calls that will start the connection process to the Kernel 201 Application.

        function btConnect()
        {
            navigator.bluetooth.requestDevice({ 
                filters: [{ services: [btServiceUUID] }] 
            })
            .then(device => {
                btDevice = device;
                btDevice.addEventListener('gattserverdisconnected', 
                                          bt_cb_ondisconnected);
        
                return btDevice.gatt.connect()
            })
            .then(server => {
                bt_view_connection(true);
                return server.getPrimaryService(btServiceUUID)
            })
            .then(service => {
                btService = service;
        
                return bt_listen(service);
            })
            .then(_ => {
                return bt_update();
            })
            .catch(error => {
                console.log("Error: " + error);
            });
        }
        

         

        The Javascript Engine runs in a single thread, so when it hits a function that requires a delay or user input, instead of blocking the entire application it puts that function off until the delay or input is received and continues executing the rest of the code. In order to ensure the WebBluetooth API functions are called in order, the connect code relies on Javascript Promises. The Promises allow the connection events to happen sequentially using the then() keyword. In the example above you can see that the connect() will not occur until the requestDevice() function has finished. If the then() keyword was not used, Javascript would attempt to run connect() before the requestDevice() function completed causing an error.

         

        When the requestDevice() function is first executed, a filter is applied. The filter is using the UUID of the Lab Control service. You may recall from the Kernel 201: Bluetooth Task blog that the Lab Control service contains all of the tasks in the lab application. By filtering on the Lab Control service, it allows the Bluetooth Connection Dialog to only show Bluetooth devices running the Kernel 201 Application as shown above.

         

        After the connection is made, the WebBluetooth App connects to the Lab Control service and subscribes to notifications from all of the characteristics specified. The notifications allow for the Kernel 201 Application to send a status update to the WebBlueooth App as soon as it occurs, rather than having the WebBluetooth App constantly poll for changes. After its completed the notification subscriptions, the WebBluetooth App makes read requests from the Kernel 201 Application and displays the device state as shown above.

        Once the display has been changed to the current state of the Kernel 201 Application, the WebBluetooth App maintains a Bluetooth connection but sit idle until one of two actions occur:

        • The user clicks a button
        • A notification is received from the Kernel 201 Application

        When the user clicks an LED button, the LED write function will call the Bluetooth write function to send a write command to the Kernel 201 Application with the LED data. After the command is sent, the WebBluetooth App does not change the user interface to reflect the command just sent. The change in the user interface will come from the Kernel 201 Application sending a notification that a change has been made to the LED. This prevents the user interface from getting out of sync with the Kernel 201 Application.

        When a notification is received from the Kernel 201 Application, the callback bt_cb_onnotify() fires and determines what portion of the UI needs to be updated. It then passes along the received data to the necessary functions to update the UI.

         

        Final Thoughts

        The WebBluetooth API provides a simple, quick way to create apps that run on both mobile and desktop natively. Unfortunately, since it is still experimental, there is a lack of support for it. Hopefully in the future, it will become a widely accepted way of interacting with Bluetooth devices. If you have any questions or thoughts on this feel free to leave them below.

      • Kernel 201: Bluetooth Task

        Mark Mulrooney | 06/177/2019 | 08:42 PM

        This blog is part of the Kernel 201: Designing a Dynamic Multiprotocol Application with Micrium OS. The table of contents for this blog series can be found here.

        Note: It is recommended to download the Kernel 201 application and have the code in front of you as you go through this blog post. The code can be downloaded on the Kernel 201: Project Resources page. 

        Note: This project has a slightly different RTOS interface with the Bluetooth stack than other example projects. The differences will be highlighted in this article.

        Introduction

        The goal of the Bluetooth task in the Kernel 201 Application is to communicate with a web browser running a WebBluetooth App. The WebBluetooth App is highlighted in detail in another post that can be found here.

        The Bluetooth task will perform the following actions:

        • Start Bluetooth Advertising after the stack boots and after a disconnect
        • Handle read and write requests from the WebBluetooth App
        • Send notifications to the WebBluetooth App

        Kernel 201 Bluetooth Initialization

        Before starting the Bluetooth Task, configurations and initializations must be performed for the Bluetooth stack to operate correctly. Part of the configuration for the Bluetooth stack comes from the GATT editor in Simplicity Studio; the other portion of the Bluetooth configuration comes from the gecko_configuration_t structure.

         

        To modify the Bluetooth GATT configuration, there is an application in Simplicity Studio to help configure the GATT database. In the Kernel 201 Application there is a file called kernel201.isc. Opening that file in Simplicity Studio will open the GATT Configurator where you can make changes to the GATT database.

        In the GATT Configurator, a new Bluetooth Service called Lab Control has been added for the Kernel 201 Application. Any task in the application that wishes to have Bluetooth control will have a characteristic under the Lab Control service. For the Kernel 201 Application, each characteristic will support Read, Write and Notify. The characteristics are given unique UUID numbers that are hardcoded into the WebBluetooth App. If someone wishes to add a new task, modifications would also have to be made to the WebBluetooth App to support the new UUID. The code for the WebBluetooth App is available for download under the Kernel 201: Project Resources page.

        By default, the Kernel 201 Application shows up as “Kernel201” for the Bluetooth Device Name. If you wish to change the Device Name to something else, this can be accomplished under the Device Name Characteristic in the GATT Configurator. The value field is where the new Device Name would be specified and the length must be adjusted. Note: For the DMP portion of this application to work correctly the Device Name must be 16 characters or less. This is a Kernel 201 Application requirement, not a Bluetooth requirement. 

        After all changes have been made in the GATT Configurator, the kernel201.isc file needs to be saved and then click Generate to update the GATT information. For further information on the GATT Configurator, refer to UG365: GATT Configurator Users Guide

        static const gecko_configuration_t bluetooth_config =
        {
            .config_flags              = GECKO_CONFIG_FLAG_RTOS,
            .sleep.flags               = 0,
            .bluetooth.max_connections = LAB_BLUETOOTH_MAX_CONNECTIONS,
            .bluetooth.heap            = bluetooth_stack_heap,
            .bluetooth.heap_size       = sizeof(bluetooth_stack_heap),
            .gattdb                    = &bg_gattdb_data,
            .ota.flags                 = 0,
            .ota.device_name_len       = 3,
            .ota.device_name_ptr       = "OTA",
            .scheduler_callback        = Bluetooth_LLCallback,
            .stack_schedule_callback   = Bluetooth_UpdateCallback,
        #if (HAL_PA_ENABLE)
            .pa.config_enable          = 1,
        #if defined(FEATURE_PA_INPUT_FROM_VBAT)
            .pa.input                  = GECKO_RADIO_PA_INPUT_VBAT,
        #else
            .pa.input                  = GECKO_RADIO_PA_INPUT_DCDC,
        #endif
        #endif
            .mbedtls.flags             = GECKO_MBEDTLS_FLAGS_NO_MBEDTLS_DEVICE_INIT,
            .mbedtls.dev_number        = 0,
        };
        

         

        In the lab_bluetooth.c file, there is also the gecko_configuration_t Bluetooth configuration structure that must be passed in during initialization. As shown above the structure sets internal flags, heap location and size (separate heap from Micrium OS), GATT Database, callback functions and other power and security parameters. For more information on the structure, refer to UG136: Silicon Labs Bluetooth C Application Developer’s Guide.

        The Kernel 201 Application also introduces a second configuration structure that is not found in other Dynamic Multiprotocol Applications or officially supported by the Bluetooth stack.

        static BLUETOOTH_RTOS_CFG_s bluetooth_rtos_cfg =  // Bluetooth RTOS config
        {
            .ll_prio      = SLAB_LINKLAYER_PRIO,
            .bt_prio      = SLAB_BLUETOOTH_PRIO,
            .bt_callback  = labBluetoothEvtCallback,      // Callback to signal a Bluetooth 
            .gecko_config = &bluetooth_config             //  event to process.
        };
        

         

        Typically, the function bluetooth_start_task() only takes in the task priorities for the Link Layer and Bluetooth tasks. This structure adds a callback function that will be used when there is a Bluetooth event for the labBluetoothTask to process.

        This change is necessary because the existing implementation of the Bluetooth Stack’s RTOS port relied on the user application to pend on an Event Flag. Since the tasks in the Kernel 201 Application pend on Task Message Queues, it was not possible to use the Bluetooth Event Flags in conjunction with the Task Message Queue. The addition of the callback function adds the flexibility for applications to use any kernel service desired for signaling rather than being forced to use an Event Flag.

        Kernel 201 Bluetooth Task

        The main Bluetooth task loop is structured just like all other tasks in the Kernel 201 Application (except for the watchdog task). When the task is created, the Bluetooth stack is configured and initialized and then begins waiting for messages in the Task Message Queue.

         

        while (DEF_TRUE)
        {
            p_msg = OSTaskQPend( LAB_WDOG_TASK_TIMEOUT_MS, // Wait for lab message
                                 OS_OPT_PEND_BLOCKING,     //  or timeout.
                                &lab_q_id,
                                 0,
                                &err);
        
            do {
                if(err.Code == RTOS_ERR_TIMEOUT) {         // If timeout, feed watchdog
                    break;
                } else if(err.Code != RTOS_ERR_NONE) {     // Assert on other errors
                    APP_RTOS_ASSERT_CRITICAL(err.Code == RTOS_ERR_NONE, ;);
                }
        
                switch(lab_q_id) {
                    case LAB_Q_ID_BLUETOOTH_EVT:
                        labBluetoothHandleEvt();          // Handle Bluetooth Event
                        break;
        
                    case LAB_Q_ID_MSG:                    // Handle Lab Message
                        labBluetoothHandleMsg((LAB_MSG_s*) p_msg);
                        labUtilMsgFree(p_msg);            // Then free Lab Message
                        break;
        
                    default:
                        break;
                }
            } while(0);
        
            labWDOGFeed(LAB_TASK_BLUETOOTH);              // Feed the watchdog
        }

         

        There are two types of messages and one error code that the Bluetooth task handles:

        • RTOS_ERR_TIMEOUT – No events have been received in the timeout period, feed the software watchdog and pend again.
        • LAB_Q_ID_BLUETOOTH_EVT – Called when there is an event from the Bluetooth stack to handle.
        • LAB_Q_ID_MSG – Called when there is a message in the void* argument of the Task Message Queue to process.

         

        RTOS_ERR_TIMEOUT

        As part of the software watchdog, the Bluetooth task must feed the software watchdog at a specified rate. There is a define in lab.h that all tasks use called LAB_WDOG_TASK_TIMEOUT_MS. This define specifies how often every task should check-in with the software watchdog. If the Task Message Queue does not receive a message before the timeout value is hit, the pend call returns with RTOS_ERR_TIMEOUT. Since it’s a timeout the Bluetooth task knows that no message was received, it only feeds the software watchdog and then starts pending on the Task Message Queue again.

         

        LAB_Q_ID_BLUETOOTH_EVT

        When the Bluetooth stack has an event for the Kernel 201 Application to process, it makes a call to the callback function specified in the BLUETOOTH_RTOS_CFG_s structure. That function is defined as follows:

        void  labBluetoothEvtCallback (void)
        {
            RTOS_ERR err;
        
        
            OSTaskQPost(&labBluetoothTaskTCB,
                         0,
                         LAB_Q_ID_BLUETOOTH_EVT,
                         OS_OPT_POST_FIFO,
                         &err);
        }
        

         

        In the callback function, a message is sent to the labBluetoothTask to tell it there is a message waiting to be processed. The callback function must send a message because the callback function is executed from the internal Bluetooth Task. If the callback function was to process the Bluetooth event it would lock up the internal Bluetooth task and potentially cause the Bluetooth Stack to miss other Bluetooth events.

        When the signal to process an event is received by the labBluetoothTask, the function labBluetoothHandleEvt() is called. This function first retrieves the event that must be processed, and then determines how to handle it. The chart below shows the flow of the event handler:

         

        gecko_evt_system_boot_id and gecko_evt_le_connection_closed_id

        These events are called when the Bluetooth stack has been booted, and when a Bluetooth connection has been dropped. In both cases, the Bluetooth stack must be told to start advertising again as we don’t have a connection.

         

        gecko_evt_gatt-server_user_write_request_id

        The write event is called when the WebBluetooth App is wishing to send a command to a task in the system. A common example would be using the WebBluetooth App to change the LEDs.

        The Bluetooth write event that is received from the Bluetooth stack also contains data such as the task the data is to be sent to, as well as the data itself. To send the data to the specified task, a lab message buffer is allocated using the utility functions. Once a buffer has been allocated, it is configured for the task destination and then the data specific to that task is copied into the buffer. The buffer is then sent to the desired task/s.

         

        gecko_evt_gatt-server_user_read_request_id

        The read event is called only when a WebBluetooth App first connects to the Kernel 201 Application. The read is used to determine the state of all of the tasks in the system for the WebBluetooth App. After the WebBluetooth App has connected and received the task status the first time, all subsequent updates are received via a Bluetooth notification.

         

        LAB_Q_ID_MSG

        When another task in the system wishes to send data via Bluetooth, a lab message must be sent to the Bluetooth task via the Task Message Queue. In most cases, this is used when a task has completed a request and is updating the remote application. In the LED example, after the LED has been changed a message is sent to the Bluetooth task so the WebBluetooth App connected, if any, is updated.

         

        Kernel 201 Bluetooth Example

        The following diagram shows the typical flow of data when a user wishes to change the LED:

         

        1. The user makes a request on the WebBluetooth App to change the LED color to red.
        2. The WebBluetooth App sends a Bluetooth write command to the Kernel 201 Application.
        3. The Silicon Labs internal Bluetooth stack receives the message via the radio, assemble the data and executes the labBluetoothEvtCallback() that was passed in during the Bluetooth configuration. The callback puts a LAB_Q_ID_BLUETOOTH_EVT message in the labBluetoothTask’s Task Message Queue.
        4. The labBluetoothTask receives the LAB_Q_ID_BLUETOOTH_EVT in its Task Message Queue and handles it as follows:
          1. Get the Bluetooth Event from the Bluetooth stack.
          2. Determine it is a write event.
          3. Determine it is an LED write event.
          4. Allocate a lab message buffer, configure it for a LED message and send it to the LED task via the LED Task’s Task Message Queue.
        5. The labLEDTask receives the LAB_Q_ID_MSG along with the lab message buffer pointer.
          1. It proceeds to change the LED as requested.
          2. After the change has been made, the LED task sends the same lab message content it received back to the Bluetooth task, but signals that it is an update message, not a command message.
        6. The labBluetoothTask receives a LAB_Q_ID_MSG in its Task Message Queue.
          1. The message is sent to the Silicon Labs internal Bluetooth stack via the Gecko characteristic notification command
        7. The Silicon Labs internal Bluetooth stack processes the characteristic notification command and sends the data to the WebBluetooth App.
        8. The WebBluetooth App receives the Bluetooth characteristic notification update and changes the WebBluetooth App to show the LED color has been changed to red.

         

        Final Thoughts

        The Bluetooth Task is one of two wireless tasks in the system that will send commands to the other tasks in the system and report back the status of the system. The other task is the Proprietary Wireless that is covered in Kernel 201: Proprietary Wireless Task. The Bluetooth Task has the advantage that the developer does not need to make multiple RAIL calls to configure the radio or listen for hardware interrupts because it is abstracted away by the Bluetooth Stack. The Proprietary Wireless Task does not have this luxury and will require some more in-depth knowledge of the RAIL API. If you have any questions or comments on the Bluetooth Task feel free to leave them below.

      • Making Peripheral Drivers Thread-safe

        Janos Magasrevy | 06/177/2019 | 03:27 PM

        If you are running in a multi-threaded environment in which you have more than one task making use of a peripheral driver such as USART, SPI, I2C, etc., you should consider making it thread-safe.

        The Micrium OS kernel offers a variety of services designed to protect shared resources. In our case, let’s make use of the Mutual Exclusion Semaphore also known as mutex. Why a mutex? Because we want our resource (peripheral driver) to be accessed only by one task at a time. Regular semaphores present a vulnerability with priority-inversion. Mutexes, on the other hand, are implemented in a way that priority inversions are prevented by using priority inheritance.

        For this exercise, you will be editing files from the Gecko SDK, which is not recommended therefore do this with caution.

        In the peripheral driver file that you want to protect, include the following file:

        #include  <kernel/include/os.h>

        Now we have to declare a global variable for our mutex, for example, if you want to protect the SPI you could declare it as follows:

        OS_MUTEX  SPI_Mutex;

        With the mutex now declared, you need to invoke the kernel call to create it. My recommendation is to make this in the initialization function of the peripheral driver that you are using. You create the mutex by calling:

        OSMutexCreate(&SPI_Mutex, “SPI Mutex”, &err);

        Notice how the function requires an error argument, just declare RTOS_ERR  err locally and pass it on.

        Always make sure to check the error returned, if it’s not RTOS_ERR_NONE then something went wrong.

        The mutex is now created and registered with the kernel. Now you will need to wrap around the driver calls that your application is using with:

        void foo () {
        
          RTOS_ERR  err;
        
          OSMutexPend(&SPI_Mutex,            /* Pointer to the mutex */
                       0,                    /* No timeout */
                       OS_OPT_PEND_BLOCKING, /* Block if not available */
                       DEF_NULL,             /*Timestamp not used */
                      &err);
        
          if (err.Code != RTOS_ERR_NONE) {
              /* handle error */
          }
        
          /* peripheral driver function code */
          ...
          ...
          ...
        
          OSMutexPost(&SPI_Mutex,
                       OS_OPT_POST_NONE,
                      &err);
        
          if (err.Code != RTOS_ERR_NONE) {
              /* handle error */
          }
        }

        Please make sure to check the returned errors. A common one is RTOS_ERR_NOT_READY, this happens when the pend or post calls are made before the kernel is in its running state (after the call to OSStart()).

        If the driver initialization function can potentially be called multiple times from more than one task, my recommendation is to also protect it.

        With this setup, you can be sure that your peripheral is only being accessed by one task at a time.

        Beware also that some drivers have abort functions, you have to be careful with this and not lock your system on a mutex pend. For more information on how to protect a resource using Micrium OS please visit https://doc.micrium.com/display/OSUM50600/Resource+Management+Using+the+Kernel

      • IoT Hero IOTAS Brings Smart Home Experience to Apartments

        Jackie Padgett | 06/170/2019 | 01:48 PM

        Last month, we spoke with the co-founders of IOTAS, Sce Pike, CEO and Jeremy Steinhauer, VP of Data Services and learned how multi-family residential buildings, otherwise known as apartments, have recently stepped up their smart home game. IOTAS, which stands for IoT as a Service, has been a key player in making this happen after creating a smart home platform for apartment renters several years ago. The platform, built for residents, property owners and installers, provides residents with a seamless IoT solution that monitors apartment unit systems, including thermostats, motion detectors, security and lighting. The solution is so easy to use renters have been asking to take it with them when moving out of their apartment.

        IOTAS has grown rapidly since its inception four years ago and was recently recognized by the Bay Area’s Start-up Grind as the 2019 Start-up of the Year. The company’s install base has grown to 100,000 smart devices in more than 70 communities.

        Read more below about our conversation with IOTAS and how the company tapped into a major new market opportunity for smart home technologies.

         

        How did IOTAS get started?

        Sce: In 2014, a real estate developer in Portland approached me and explained he was looking for technology differentiation for his apartment property. My background is in mobile design development, and I had recently sold my first mobile consumer company, so the idea of focusing on the technology experience in the real estate market was intriguing. I quickly realized the $5 billion real estate market had the potential to scale a new technology extremely fast.

        I went ahead and recruited Jeremy as my co-founder and we immediately built out a technology solution focused exclusively on apartments. We installed 40 smart home devices in a 1,000 square foot apartment unit to create a true smart home experience, not something that was cumbersome to put together. We wanted to create a move-in ready solution that just worked. We then took the idea out on a real estate conference roadshow, and it became obvious fast that we had created the next amenity commercial property owners were seeking. Property owners knew they had to provide technological advancements in their buildings to meet the demands of millennials and GenZ.

        Once we received this feedback, we scaled the solution exponentially over the next few years, with our installs growing by 500 percent last year alone. We see the potential of disruption for all multi-family properties within five years, with all of them having some level of smart technology.

        How did the real estate market know they needed this?

        Sce: The real estate industry is 13 percent of the U.S. GDP and does a great deal of collective research. Property companies understand their own demographic very well, and at the time they were seeing technology become pervasive across their tenants’ lives, but the shift wasn’t reflected in apartment buildings. The industry saw this void as an opportunity to create an edge for their properties. Five years ago, when we first entered the market, the conferences were only starting to talk about smart home technology, but now smart apartments are mainstream, and the vast majority of properties are allocating budgets to incorporate smart home technologies.

        Tell me about your product.

        Jeremy: We’re focused on making the smart home experience extremely easy for residents and property managers. We work with property developers and managers to install smart devices in apartments with a suite of applications, including door locks, thermostats, light switches, power outlets and sensors to monitor leaks, motion, temperature, etc. Residents simply download the app to their phone and get a smart home out of the box. We also created automated defaults, like “out for the day” or “welcome home,” which set up preferred systems for that specific time period.

        The second application is geared towards property managers to improve efficiencies – it’s the tool managers use to link the residential units and create their own automations for vacant units, such as temperature, appliances and lighting. Property managers can use it to immediately set-up for tours of certain units by turning on lights, setting the thermostat to a comfortable temperature, etc. on and off before or after. The application also has alerts for property managers if something is amiss, such as extremely high or low temperatures or humidity levels for vacant units.

        The third application is the installer and/or auto-provisioning application. During our initial foray into smart apartment, we were installing 40 devices and quickly learned that commissioning that many devices to a gateway was a painful process and not scalable. We then spent the next few years perfecting the provisioning process. Now we have several auto-provisioning patents, where we have reduced the time to install devices for an installer from one hour to 15 minutes.

        The final application is our internal application – how we administrate the system and create the data around the buildings and users.

        Have you ever thought about going beyond multi-family homes?

        Sce: From the onset, we have been focused on the multi-family market, for a variety of reasons. The economy of scale is a big reason – we didn’t want to sell one device at a time, we wanted to sell to buildings and make installations quickly. It’s much easier to design a solution for multiple homogenous units versus custom homes.

        Though we have had many residents leave their apartments and ask how they can take IOTAS with them – so much so, that we’re now working on a way for residents to be able to upgrade their systems and go on a subscription basis to take IOTAS into their new home. This new solution is on our roadmap and we’re hoping to offer it by the end of this year.

        How does Silicon Labs fit into your product?

        Right now, we’re using the Z-Wave 500 Series Module solution and we are using the Z-Wave 700 Series for our next generation product to be rolled out later this year. We’re also considering using Zigbee in the future for wireless, so we’re using a Silicon Labs Zigbee module as we figure out a potential software stack for a Zigbee-based application. All of the end nodes are controlled by Z-Wave; we don’t have any internal connections over Wi-Fi. The Wi-Fi is purely used to connect the hub to our cloud.

        Why did you decide to use the Z-Wave hub?

        Jeremy: We started with Zigbee, but the standards were not there yet, as the devices weren’t all interoperable. The decision to use Z-Wave was also driven by the cost of each product. Having the ability to work with something like Z-Wave right out of the box with standardized protocols was a big selling point, as well.

        Where do you IoT going in the next 5-8 years?

        Jeremy: We’re extremely focused on the user experience, and we knew early on that we didn’t just want to be remote control for the home. In that same vein, I think that’s where IoT is headed – the more interconnectivity among devices, the richer the home experience can be. We see this going beyond the home, where people can take the experience with them. The smart home experience isn’t just about building profiles around people within the four walls that they live in, but taking the experience everywhere they go in the living world, such as their car, workplace, hotels, and even vacations.

      • Kernel 201: Implementing a Software Watchdog

        Mark Mulrooney | 06/169/2019 | 09:40 PM

        This blog is part of the Kernel 201: Designing a Dynamic Multiprotocol Application with Micrium OS. The table of contents for this blog series can be found here.

        Note: It is recommended to download the Kernel 201 application and have the code in front of you as you go through this blog post. The code can be downloaded on the Kernel 201: Project Resources page. 

         

        Introduction

        There are different levels of safety to consider when developing a real-time system.

        Safety Critical

        • Systems whose failure may result in death/serious injury, loss or damage to property or environmental harm.
        • Typically, has well defined specifications for product certification and release (e.g. DO-178, IEC 61508, IEC 62304)
        • Usually an expensive and time-consuming process to release a product.

        Non-Safety Critical

        • System failures are not directly related to catastrophic events.
        • Minimal or no certification required for product release.

        Even though a majority of products may fall under the non-safety critical system category, it doesn’t mean a safety critical design pattern shouldn’t be considered when developing a real-time system. Designing an application using safety critical components can make a product more robust and reliable.

        There are two properties of a system that are commonly considered in safety critical designs:

        • Safety – System does not create accidents, injuries, loss of life or destruction of property.
        • Reliability – System runs for long periods of time without error.

        These two properties are not mutually exclusive. A system that is considered safe may fail as long as it is able to recover and operate in a manner that does not cause accidents; it is just considered unreliable. On the other hand, a system that can continue to run without error but operates unsafely is a reliable, but unsafe system.

        There are many software architectural patterns that a developer can use to help improve a system’s safety and reliability. While the main focus of this article is on one specific pattern (Watchdog Pattern), these are a few other patterns you may come across when researching safety critical design:

        • Dual Channel – Multiple channels perform the same operations and compare results
        • Monitor-Actuator – Separate channels for actuators performing actions and monitors keeping track of the actuator
        • Watchdog Pattern – Software or hardware component that requires signals from other parts of the system on a periodic basis.
        • Safety Executive – Centralized coordinator to monitor system safety. Typically incorporates a watchdog, build-in tests, and monitor actuator or dual channel patterns.

        This article will focus on implementing a watchdog pattern using a mix of software and hardware.

         

        Hardware Watchdog

        A standard feature on EFM32 and EFR32 microcontrollers is a hardware watchdog. The hardware watchdog is a timer that when enabled, constantly counts up. The watchdog timer has a user configured timeout value that the application is responsible for “feeding” (clearing) the watchdog counter before the counter reaches that value. If the watchdog reaches the configured time value, a system reset will occur. The assumption is because the application did not feed the watchdog in time, an error has occurred in the system and it must be reset.

         

        The hardware watchdog pattern works well for super-loop applications or in RTOS applications where only one task needs to be monitored and a system-wide reset would not negatively affect other tasks in the system. Complications arise when you wish to ensure multiple tasks are running as expected. One way to solve this problem is to implement a software watchdog that is responsible for handling the hardware watchdog.

        This is a simplified overview of the hardware watchdog timer available on EFM32 and EFR32. For more detailed on the EFM32 & EFR32 watchdog timers, Series 0 can be found here and Series 1 can be found here.

         

        Software Watchdog

        A software watchdog pattern is a safety design pattern that allows for the monitoring of multiple tasks in an RTOS application. It is typically used in conjunction with a hardware watchdog, but in cases where a hardware watchdog is not available, the software watchdog can be responsible for performing the system reset.

        Micrium OS Kernel provides two different kernel services that can be used to implement a software watchdog: Event Flags and Message Queues. For applications that have 32 tasks or less, an Event Flag is an ideal kernel service to use due to the Event Flag being able to monitor 32 unique bits. For applications with more than 32 tasks to be monitored, either multiple flag groups are needed or tasks can send unique values via a Message Queue to periodically check in.

         

        In the image above, each task is represented with a unique bit in the Event Flag Group. The Event Flag is configured to pend on almost all bits of the Event Flag Group and has a timeout configured. If every task posts to the Event Flag before the timeout value is reached, the Event Flag pend returns with RTOS_ERR_NONE, the software watchdog will feed the hardware watchdog and then call pend on the Event Flag again.

        If one or more tasks do not post to the Event Flag before the timeout value is reached, the Event Flag pend will return with RTOS_ERR_TIMEOUT. Also, the return value of the pend call will tell you what tasks have and have not checked in. Then with that information the software watchdog can make the decision on whether or not it should feed the hardware watchdog or let it reset the system.

         

        Implementing a Software Watchdog in the Kernel 201 Application

        The rest of this article will refer to the Kernel 201 Dynamic Multiprotocol Application. It is recommended to download the project to follow along. There are three files related to the software watchdog implementation: lab.h, lab_main.c and lab_wdog.c.

        lab.h

        The lab.h file is the centralized location for the application’s configuration parameters such as task priorities, stack sizes, message structures and enums. More detail on lab.h can be found in the blog Kernel 201: Task Architecture & Communication.

        #define  LAB_TASK_BLUETOOTH 0x01
        #define  LAB_TASK_PROP      0x02
        #define  LAB_TASK_LED       0x04
        #define  LAB_TASK_BTN       0x08
        

         

        The defines above can be found in lab.h and are used as unique identifiers for each task in the system that will be monitored by the software watchdog. Each value will represent a bit in the Event Flag Group bitfield.

         

        lab_main.c

        When Micrium OS Kernel is initialized and started in main(), its common to only create one task before calling OSStart(). This is typically done to allow for the statistics task to get a baseline reading of the system at idle before other tasks are created.

        In many sample applications the first task created is called the startup task because it is responsible for creating the rest of the tasks in the system and “starting up” the application. This is a poor naming choice for a task as after the startup task creates the other tasks in the system, some examples delete the startup task. What many do not realize is calling OSTaskDel() does not free up any resources related to the task. If the application does not know to access the startup task's stack and TCB, that memory sits unused.

        It is not recommended to call OSTaskDel() on startup tasks. A better solution is to use the task for monitoring the system or repurpose the task for another task in the system.

        Instead of calling it a startup task, the Kernel 201 Application calls it a system task. It could also be called the watchdog task since the software watchdog will run out of the system task. After the application has been initialized and all of the other tasks have been created, the last call in the labSystemTask function is:

        labWDOGTask();      // Run the WDOG task. This call does NOT return

         

        Rather than creating a new task for the watchdog, the application uses the system task to run the watchdog.

         

        lab_wdog.c

        The software watchdog file is very straightforward with only three functions: labWDOGInit(), labWDOGTask() and labWDOGFeed().

        labWDOGInit()

        The init function initializes the hardware watchdog (but does not enable it yet) and creates the Event Flag for the software watchdog.

        labWDOGTask()

        The software watchdog task operates as follows:

        while(1) {
            OSFlagPend(&labWDOGFlag,                  // Wait for all tasks to check in
                        flags,
                        LAB_WDOG_SYSTEM_TIMEOUT_MS,
                        OS_OPT_PEND_FLAG_SET_ALL + 
                        OS_OPT_PEND_FLAG_CONSUME,
                        0,
                       &err);
            if(err.Code == RTOS_ERR_NONE) {           // All tasks checked on-time.
                WDOG_Feed();                          // Feed the hardware watchdog.
            } else if(err.Code == RTOS_ERR_TIMEOUT) {
                while(1);                             // Trigger a reset. Logic can be set
            }                                         // here to ignore tasks that did not 
        }                                             // check in on time.
        

         

        The watchdog pends on the specified task flags as seen in lab.h, and the pend includes a timeout. If all tasks call a flag post at least once before the timeout value is reached, the OSFlagPend() call returns with RTOS_ERR_NONE, the hardware watchdog is fed via WDOG_Feed() and it loops around and starts the pend over again.

        If one or more tasks fail to check in, the return value will be RTOS_ERR_TIMEOUT. In this example the code will then sit in a while(1) until the hardware watchdog resets the system. This would be the ideal location to either add logic to see what flags did or did not check in, and also to add a flag to persistent memory so when the system does reset, it can see on boot that it reset due to an error.

        labWDOGFeed()

        void  labWDOGFeed(CPU_INT32U task)
        {
            RTOS_ERR err;
        
        
            OSFlagPost(&labWDOGFlag,          // Set the task's flag in the WDOG flag group
                        task,
                        OS_OPT_POST_FLAG_SET,
                       &err);
            APP_RTOS_ASSERT_CRITICAL((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), ;);
        }
        

         

        The feed function is provided so all tasks do not need to have a reference to the Watchdog Event Flag. When tasks check in they just need to provide their identifier defined in lab.h. Since a flag group bit can only be 0 or 1, it does not matter if a task checks in once or multiple times each pend cycle.

        Since every task is centered around a Message Queue, the timeout parameter on the message queue is set to a value less than the watchdog flag group to ensure that each task feeds the watchdog before the watchdog timeout.

         

        Final Thoughts

        One downside to this pattern is it does not work well with a low-power, battery operated system where you may want to sleep for longer periods of time than the hardware watchdog can handle. However, if power is not a concern the software watchdog pattern provides a simple way to ensure all tasks in an RTOS application are operating as expected. If you have any questions or comments, feel free to leave them below.

      • Kernel 201: Project Resources

        Mark Mulrooney | 06/165/2019 | 09:26 PM

        This blog is part of the Kernel 201: Designing a Dynamic Multiprotocol Application with Micrium OS. The table of contents for this blog series can be found here.

         

        Kernel 201 Application

        Hardware: https://www.silabs.com/products/development-tools/thunderboard/thunderboard-sense-two-kit
        SDK Requirements: Gecko SDK Suite 2.5.5, Bluetooth 2.11.5, Flex 2.5.5, MCU 5.7.3, Micrium OS Kernel 5.6
        SLS Project File: https://www.dropbox.com/s/8cupsgovf0f7odq/kernel201.sls?dl=0

         

        Kernel 201 Instructor Application

        Hardware: https://www.silabs.com/products/development-tools/wireless/bluetooth/blue-gecko-bluetooth-low-energy-soc-starter-kit
        SDK Requirements: Gecko SDK Suite 2.5.5, Flex 2.5.5, MCU 5.7.3, Micrium OS Kernel 5.6
        SLS Project File: https://www.dropbox.com/s/u6a25v38mpdm1fl/kernel201_rail.sls?dl=0

         

        WebBluetooth Application

        Live webpage: https://kernel201.micrium.com
        Zip File: https://www.dropbox.com/s/j4tlgmqvge8p4g3/kernel-201-web.zip?dl=0

         

        Electron Application

        Zip File: https://www.dropbox.com/s/s98rsobqqhfr5wk/kernel-201-instructor-app.zip?dl=0

      • Kernel 201: Task Architecture & Communication

        Mark Mulrooney | 06/165/2019 | 09:11 PM

        This blog is part of the Kernel 201: Designing a Dynamic Multiprotocol Application with Micrium OS. The table of contents for this blog series can be found here.

        Note: It is recommended to download the Kernel 201 application and have the code in front of you as you go through this blog post. The code can be downloaded on the Kernel 201: Project Resources page. 

        Task Architecture

        The first step of designing any real-time system, before writing any code, is to identify all events the system will be responsible for. Listing out these actions will help identify what events may be grouped together in tasks and what actions will require their own task/s. There are a number of resources available that discuss different task modeling strategies. Some common strategies you may come across are:

        • Signal event groups – Every event is put in its own task. Not efficient in large systems.
        • Sequential events – Events that must be processed in order can be grouped together.
        • Timing events – Events that occur on a periodic time base can be grouped together
        • Interface events – All events associated with a particular interface
        • Safety monitoring – Any safety monitoring events should be separated from any actuation tasks

        For this application, these are the following events that must be handled and how they’ll be grouped into tasks:

        • Bluetooth TX – labBluetoothTask
        • Bluetooth RX – labBluetoothTask
        • Proprietary Wireless TX – labPropTask
        • Proprietary Wireless RX – labPropTask
        • GPIO LED Control – labLEDTask
        • RBG LED Control – labLEDTask
        • Button 0 Read – labBtnTask
        • Button 1 Read – labBtnTask
        • Hardware Watchdog – labWDOGTask

        labBluetoothTask
        Silicon Labs’ Bluetooth stack already creates two tasks, Bluetooth and LinkLayer to handle the transmission and reception. The Bluetooth task provides callbacks for the user to implement to know when there is an event to process. The labBluetoothTask will handle those events from the Bluetooth task and pass that data along to the other tasks in the system. The labBluetoothTask will also receive data from other tasks in the system to transmit via Bluetooth.

        labPropTask
        Silicon Labs does not provide internal tasks for Proprietary Wireless (RAIL) as it does for Bluetooth, but it is important to group the RAIL functions together as they are not thread-safe functions. The labPropTask will handle all data received by RAIL and send it to other tasks in the system as needed. It will also receive data from other tasks in the system and send it out via RAIL.

        labLEDTask
        Since only the GPIO or RGB LEDs can be active at one time, it makes sense to group them together.

        labBtnTask
        The button task operates on a low priority polling interval. It could be switched to an interrupt based task but by leaving it as a polling task, it leaves open the option to add other polling events to the task in the future.

        labWDOGTask
        The watchdog task will implement a software based watchdog to control how the system feeds the hardware watchdog. It is important that this task have no other operations than handling the watchdog.

         

        Task Communication

        Now that the events have been divided up into their respective tasks, it is important to implement a consistent method for communication between tasks. There are again many different ways to implement task communication. Micrium OS Kernel provides Semaphores, Task Semaphores, Event Flags, Message Queues and Task Message Queues. While each of these kernel objects have various pros and cons, no one of these services is better than another; it just depends on the application and how it is using these kernel services.

        For this application, each task (with the exception of the watchdog task) will be centered around a Task Message Queue and the entire application will use a sort-of layered messaging scheme, similar to the OSI model but much more simplified. The main loop of each task will pend on a Task Message Queue and wait for a message or timeout to determine its next action.

        The image above shows the basic layout for all tasks in the system. The code snippet below shows some pseudocode for the task

        void* p_msg;
        LAB_Q_ID_e lab_q_id;
        
        while (DEF_TRUE)
        {
            p_msg = OSTaskQPend( LAB_WDOG_TASK_TIMEOUT_MS,     // Block the task until we
                                 OS_OPT_PEND_BLOCKING,         //  receive a message
                                &lab_q_id,
                                 0,
                                &err);
            do {
                if(err.Code == RTOS_ERR_TIMEOUT) {             // If we timeout, just feed
                    break;                                     //  the watchdog task
                } else if(err.Code != RTOS_ERR_NONE) {         // Shouldn't occur, assert
                    APP_RTOS_ASSERT_CRITICAL(err.Code == RTOS_ERR_NONE, ;);
                }
        
                switch(lab_q_id) {
                    case LAB_Q_ID_XXX_EVT:
                        labXXXHandleEvt();                    // Handle a task event
                        break;
        
                    case LAB_Q_ID_MSG:
                        labXXXHandleMsg((LAB_MSG_s*) p_msg);  // Handle a lab message
                        labUtilMsgFree(p_msg);                // Then free the message
                        break;                                //  back to the pool
        
                    default:
                        break;
                }
            } while(0);
        
            labWDOGFeed(LAB_TASK_ID_XXX);                     // Feed the watchdog after 
        }                                                     //  every task action
        

         

        By using the Task Message Queue, it allows a task to wait on three different types of events:

        • Timeouts
        • Events
        • Lab Messages

        Timeouts

        The software watchdog wants every task to check in on a specified rate to show it has not gone out to lunch. By specifying a timeout value in the Task Message Queue pend, the pend call will return with RTOS_ERR_TIMEOUT if a message is not received in the specified time. If a task expects a message in a set time interval, logic can also be added to the error check for RTOS_ERR_TIMEOUT to keep track of how many timeouts we receive in a row.

        Events

        Some tasks may have a hardware interrupt or software timer callback they need to handle in addition to processing messages from other tasks in the system. The msg_size data field in OSTaskQPend() provides a 32-bit value that is used as an identifier field for this task design. When a task receives a message, it will have a switch statement to handle the value of msg_size. In this application, all message identifiers can be found in an enum called LAB_Q_ID_e.

        When an interrupt or software timer callback wants to send a message to the task it is associated with, it will make a call similar to the one below:

        OSTaskQPost(&labXXXTaskTCB,
                     0,
                     LAB_Q_ID_XXX_EVT,
                     OS_OPT_POST_FIFO,
                    &err);
        

         

        It is important to note that the LAB_Q_ID_e value is specified in the msg_size field and not the void* field. All messages sent must have a valid identifier in the msg_size field where the void* argument is optional. In this case, the void* argument is set to 0 as there is no extra data to send.

        Lab Messages

        These messages are how tasks communicate with each other in this application. Rather than having the Bluetooth or Proprietary Wireless tasks spend time configuring PWM timers to drive the RGB LEDs, it makes more sense to send the LED task a LED command to be processed. This also allows us to handle wireless communication at a higher priority than changing the LEDs or polling the button states. The wireless tasks may be affected if responses are not sent out at specific intervals where if the system has to wait an extra 10ms to toggle the LED, the user most likely won’t notice.

        The lab messages follow a layered model where every message has the same header, the header defines what else is to follow in the packet. This application provides utility functions for getting, sending and freeing messages (refer to lab_util.c). There are advantages to these utility functions that will be discussed a little later. When you get/free a message you are actually pulling from a memory pool of fixed block sizes. All messages, regardless of how much of the block they use, are the same size. In the case of this application, all messages are 32 bytes in size. This means that if a task needs to send or receive a message larger than 32 bytes, either the size of every block in the system must be increased, multiple messages must be sent or modifications must be made to the utility functions to allow for allocation and freeing of different size blocks.

        typedef struct {
            LAB_MSG_ID_e id;
            LAB_MSG_TYPE_e type;
            void *p_data;
        } LAB_MSG_s;
        

        The struct above shows the lab message structure that all messages will start with. The ID field identifies what message will follow it and the type corresponds to if it’s a command, response, update or error message. The void* argument is used so you can cast the next layer to the end of the lab message struct.

        LAB_MSG_s *p_msg;
        
        p_msg = (LAB_MSG_s*) labUtilMsgGet();       // Get a data buffer to send a message
        if(p_msg == DEF_NULL) {
            break;
        }
        
        p_msg->id = LAB_MSG_ID_LED;                 // Set the message id
        p_msg->type = LAB_MSG_TYPE_CMD;             // Set that its a command message
        

        When you wish to allocate a message to send, the code snippet above shows how you would use the labUtil function to allocate a message. The image below shows how the lab message fits into the allocated block.

        After you configure the message ID and message type fields, you need to configure the next part of the block for the message ID you specified. For this example, let’s assume we’re changing the LEDs. LEDs are controlled by the LAB_LED_MSG_s struct. To accomplish this, you will cast the LAB_LED_MSG_s to p_data so when you enter information for the LED message, it goes into the correct location in lab message.

        LAB_MSG_s *p_msg;
        LAB_LED_MSG_s *p_led_msg;
        
        p_msg = (LAB_MSG_s*) labUtilMsgGet();       // Get a data buffer to send a message
        if(p_msg == DEF_NULL) {
            break;
        }
        
        p_msg->id = LAB_MSG_ID_LED;                 // Set the message id
        p_msg->type = LAB_MSG_TYPE_CMD;             // Set that its a command message
        
        p_led_msg = (LAB_MSG_LED_s*) &(p_msg->p_data);
        
        p_led_msg->mode  = LAB_LED_MODE_RGB;
        p_led_msg->color = LAB_LED_COLOR_GREEN;
        p_led_msg->state = LAB_LED_STATE_BLINK;
        

        The code snippet above shows how to cast the p_data argument to the LED message structure. Now when you modify the fields of the LED message structure it will be aligned in the message block as shown below.

        Once the full message is assembled, the message needs to be sent to the desired task/s. Some messages may only go to one task where others may need to be sent to multiple tasks. The lab utility function for sending offers flexibility for sending messages. Using the labUtilSend() function as shown below, you pass the pointer to the message block just configured and the task/s the message should be sent to. 

        labUtilMsgSend(p_msg, dest_task);  // Send the message to the specified destination
        

        In the case where one message is being sent to multiple tasks, the send function will keep track of the number of tasks it was sent to. This allows each task to call the free function after its processed the message, but the memory block is not actually freed until all tasks that received the message free it. To free a message all a task needs to do is make the following call:

        labUtilMsgFree(p_msg);   // Free the message back to the pool
        

        It is the labUtilMsgFree function's job to determine when the message is actually returned to the message pool. If one task never calls free on the message it received, that block will never be freed and that memory will be lost so it is imperative that all tasks free all messages they receive, even if they receive the message in error. 

        Final Thoughts

        This wraps up the Task Communication and Architecture section of the Kernel 201 application. Moving forward the Bluetooth, Proprietary Wireless, LED and Button tasks will all be using this communication structure. If you have any questions about this post feel free to leave comments below.

      • Kernel 201: Configuring Micrium OS

        Mark Mulrooney | 06/165/2019 | 08:12 PM

        This blog is part of the Kernel 201: Designing a Dynamic Multiprotocol Application with Micrium OS. The table of contents for this blog series can be found here.

        Note: It is recommended to download the Kernel 201 application and have the code in front of you as you go through this blog post. The code can be downloaded on the Kernel 201: Project Resources page. 

        Introduction

        Micrium OS offers default configurations to allow a user to get started quickly. These configurations are useful for quick starts, test applications and instances where memory is not a constraint. It is important that all developers using Micrium OS do give some attention to the RTOS configuration as the defaults are not ideal for a released application.

        To ensure the dynamic multiprotocol application starts off on a good foot, one of the first steps that will be taken is to configure Micrium OS. Most of the sample applications available in Simplicity Studio provide a good starting point, but this post will look a little more in-depth at the configuration that’s available.

        Micrium OS Configuration Files

        os_cfg.h

        Note: Some example projects have already made modifications to os_cfg.h. The comments here are based on differences between the default os_cfg.h found in the Gecko SDK Micrium OS directory and this project.

        This file provides all compile-time options for Micrium OS Kernel. It provides the ability to turn on and off almost all parts of the kernel. Typically, the default values are fine to start a project out with. In this project there are a few differences from the default config worth highlighting:

        #define  OS_CFG_APP_HOOKS_EN        DEF_ENABLED
        #define  OS_CFG_DYN_TICK_EN         DEF_ENABLED
        #define  OS_CFG_PRIO_MAX            32u
        #define  OS_CFG_TASK_DEL_EN         DEF_DISABLED
        

         

        App Hooks

        App hooks enable will turn on the callback functions (if one is defined) anytime the following events occur:

        • Redzone Hit (if Redzone is enabled) – Occurs when a stack overflow is detected
        • Task Create – Occurs after the task has been created but before OSTaskCreate() returns
        • Task Delete (if task delete is enabled) – Occurs after the TCB is cleared and removed from the kernel but before OSTaskDel() returns.
        • Task Return – Occurs if a task attempts to return
        • Idle Task – Occurs at the end of the idle task while(1) loop
        • Stat Task – Occurs after CPU usage calculation in the stat task
        • Task Switch – Occurs right before a task is switched out
        • Time Tick – Occurs right before a semaphore post to the tick task

        The hook that is set up in most projects is the idle task hook. Since this hook is called from the idle task loop, it is an ideal time to enter a lower power mode. Right now the kernel can run from EM0, EM1 or EM2 (EM2 requires use of the LFRCO).

        Dynamic Tick

        Dynamic tick allows the kernel to adjust the tick rate as needed to minimize the amount of time the kernel has to wake from sleep. The images below show the advantage of using dynamic tick over a periodic tick.

        Periodic Tick

        Dynamic Tick

        Priority Maximum

        The PRIO_MAX setting controls the maximum number of task priorities allowed in the system. Micrium OS supports an unlimited* number of task priorities but there are advantages of having the value set to a smaller number.

        If a system only has 10 tasks, there is an advantage to setting PRIO_MAX to 32 vs 64, 128, 256, 512 or some other very large number. The way the kernel’s scheduler looks for the next available task is it has an array called OSPrioTbl[] which is defined as follows:

        CPU_INT32 OSPrioTbl[(((OS_CFG_PRIO_MAX - 1u) / DEF_INT_CPU_NBR_BITS) + 1u)]

         

        Each bit in the PrioTbl corresponds to a task priority so since each row holds 32 bits, that’s 32 priority levels that can be checked in one go. If PRIO_MAX is set to 512, the kernel will potentially have to go through 16 rows before deciding to drop to the idle task. That’s a lot of extra overhead for task priorities that are not being used.

        *Hardware provides the limitation, there is no limitation in software.

        OSTaskDel

        The OSTaskDel() should be disabled by default in almost all Micrium OS applications. Deleting a task only removes it from the kernel so it is no longer scheduled. It does not take into account any kernel objects associated with the task nor does it free up any memory associated with the task being deleted. There are very few situations where OSTaskDel() is needed.

         

        common_cfg.h

        The common_cfg.h file is used to configure the memory, string and clock modules. The defaults for string and clock are fine for this project, but since the goal of this project is to reduce the kernel’s footprint the memory will be adjusted.

        By default the kernel uses an internal heap to allocate space for internal tasks and message queues. By making the following change to the heap size, it will turn off the internal heap.

        #define  LIB_MEM_CFG_HEAP_SIZE                              0uL

         

        If this was the only change is made to the project, the project would compile but fail immediately during runtime when OSInit() is called due to no memory being available. A change also has to be made in rtos_cfg.h to disable the use of the internal heap for internal tasks and message queues.

         

        rtos_cfg.h

        The rtos_cfg.h file controls three things:

        • Assert configuration

        • Memory usage configuration for internal tasks and message queues

        • Logging

        The assert configuration defaults to locking the system in a while(1) loop for debug and critical asserts. The debug asserts should be modified when going into production but for development it can be useful to leave the while(1) loop to find issues quicker.

        The logging is an optional module that is not used in this project.

        The Default Config configuration is what determines if the heap is used for internal tasks or not. By making the following change, it will require that a number of internal structures are externally configured. This will allow for the kernel to operate with the heap being set to 0.

        #define  RTOS_CFG_EXTERNALIZE_OPTIONAL_CFG_EN               DEF_ENABLED

         

        rtos_description.h

        This file defines what Micrium OS modules are enabled. For this project only two defines are needed:

        #define  RTOS_MODULE_KERNEL_AVAIL
        #define  RTOS_MODULE_COMMON_CLK_AVAIL

         

        A full list of available RTOS_MODULE options can be found in the Gecko SDK version of rtos_description.h.

         

        Main Configuration

        Once the changes have been made to the necessary config files, the desired kernel configuration must be made in the application. In this example, the file lab_main.c has the following kernel configuration.

        Tick Task

        The tick task is used to control the kernel’s system tick which is used for time delay calls and pend timeouts. In this application it is based off of the RTCC interrupt. The following configuration will define a stack for the tick task and set the config only if OS_CFG_TASK_TICK_EN is set in the os_cfg.h file. The stack size, priority and frequency of the tick task are all configured in the lab.h file.

        #if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)               // Tick Task Configuration
        static CPU_STK TickTaskStk[LAB_OS_TICK_TASK_STK_SIZE];
        #define TICK_TASK_CFG  .TickTaskCfg =       \
        {                                           \
            .StkBasePtr = &TickTaskStk[0],          \
            .StkSize    = LAB_OS_TICK_TASK_STK_SIZE,\
            .Prio       = LAB_OS_TICK_TASK_PRIO,    \
            .RateHz     = LAB_OS_TICK_TASK_RATE_HZ  \
        },
        #else
        #define TICK_TASK_CFG
        #endif 
        

         

        Idle Task

        The idle task is the lowest priority task in the system. When the kernel has no other tasks to schedule, it will run the Idle Task. When entering the Idle Task, there is a hook function that can be implemented to allow the user application to enter a lower power mode. The following configuration will define a stack for the idle task and set the config only if OS_CFG_TASK_IDLE_EN is set in the os_cfg.h file. If the idle task is not enabled, when the current task finishes the scheduler sits in a while(1) loop until another action happens in the system rather than jump to a different task. When the idle task is enabled the stack size of the idle task can be found in lab.h. You can not set the priority of the idle task because it must always be the lowest priority task in the system.

        #if (OS_CFG_TASK_IDLE_EN == DEF_ENABLED)               // Idle Task Configuration
        static CPU_STK IdleTaskStk[LAB_OS_IDLE_TASK_STK_SIZE];
        #define IDLE_TASK_CFG  .IdleTask =          \
        {                                           \
            .StkBasePtr = &IdleTaskStk[0],          \
            .StkSize    = LAB_OS_IDLE_TASK_STK_SIZE \
        },
        #else
        #define IDLE_TASK_CFG
        #endif
        

         

        Timer Task

        The timer task is used to control Micrium OS Kernel’s software timers. The reason the timers operate in a different task than the tick task is the timer callback functions are made from the timer task, so it allows the timer callbacks to run at a different priority than the tick task if desired. The following configuration will define a stack and set the config only if OS_CFG_TMR_ EN is enabled in the os_cfg.h file. The stack size, priority and frequency of the timer task are all configured in the lab.h file.

        #if (OS_CFG_TMR_EN == DEF_ENABLED)                     // Timer Task Configuration
        static CPU_STK TimerTaskStk[LAB_OS_TMR_TASK_STK_SIZE];
        #define TIMER_TASK_CFG  .TmrTaskCfg =       \
        {                                           \
            .StkBasePtr = &TimerTaskStk[0],         \
            .StkSize    = LAB_OS_TMR_TASK_STK_SIZE, \
            .Prio       = LAB_OS_TMR_TASK_PRIO,     \
            .RateHz     = LAB_OS_TMR_TASK_RATE_HZ   \
        },
        #else
        #define TIMER_TASK_CFG
        #endif
        

         

        Stat Task

        The stat task is an internal task that provides such run-time statistics as overall CPU utilization (0.00 to 100.00%), per-task CPU utilization (0.00 to 100.00%), and per-task stack usage. The following configuration will define a stack and set the config only if OS_CFG_STAT_TASK_EN is enabled in the os_cfg.h file. The stack size, priority and frequency of the stat task are all configured in the lab.h file.

        #if (OS_CFG_STAT_TASK_EN == DEF_ENABLED)               // Statistics Task Config
        static CPU_STK StatTaskStk[LAB_OS_STAT_TASK_STK_SIZE];
        #define STAT_TASK_CFG  .StatTaskCfg =       \
        {                                           \
            .StkBasePtr = &StatTaskStk[0],          \
            .StkSize    = LAB_OS_STAT_TASK_STK_SIZE,\
            .Prio       = LAB_OS_STAT_TASK_PRIO,    \
            .RateHz     = LAB_OS_STAT_TASK_RATE_HZ  \
        },
        #else
        #define STAT_TASK_CFG
        #endif
        

         

        Interrupt Stack

        The ARM Cortex-M series has a separate stack for interrupts to execute out of. Micrium OS Kernel will configure the stack location during the OSInit() call, but a stack must be provided. The following configuration creates the interrupt stack and sets the necessary configuration. This is not an optional configuration, it is required of all Micrium OS Kernel applications.

        static CPU_STK ISRStk[LAB_OS_ISR_STK_SIZE]; // ISR Configuration
        #define ISR_CFG                  .ISR = \
        {                                       \
            .StkBasePtr = (CPU_STK*)&ISRStk[0], \
            .StkSize    = LAB_OS_ISR_STK_SIZE   \
        },
        

         

        Message Pool

        If message queues will be used in an application (this application makes heavy use of them) then a message pool must be defined. When messages are sent in either a message queue or task message queue, a message pool object is allocated to hold the data being sent (void* argument and message size argument). The message pool should have enough entries for every message queue entry in the application. For example, if you have 3 message queues each with 10 entries in them, you should have a message pool size of 30. The following configuration is used to specify the message pool size in this application.

        #define  MSG_POOL_SIZE  (LAB_TOTAL_MSG_Q * sizeof(OS_MSG))
        #define  MSG_POOL_CFG    .MsgPoolSize = LAB_TOTAL_MSG_Q, \
                                 .MemSeg      = &MsgPoolMemSeg,
        

         

        In addition to specifying the configuration, a memory segment and memory buffer must be allocated as follows:

        static  MEM_SEG    MsgPoolMemSeg;
        static  CPU_INT08U MsgPoolMem[MSG_POOL_SIZE];
        

         

        And finally, in the main() before OSInit() is called, the memory segment must be created so the kernel knows how to access the memory buffer specified above.

        Mem_SegCreate(          "Msg Pool Mem",         // Create the Message Pool segment
                                &MsgPoolMemSeg,
                      (CPU_ADDR)&MsgPoolMem,
                                 MSG_POOL_SIZE,
                                 LIB_MEM_PADDING_ALIGN_NONE,
                                &err);
        APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), 1);
        

         

        Putting it all together

        You may have noticed that all of the configs are just #defines. These #defines are used to create one giant #define as shown below.

        #define  OS_INIT_CFG_APP            {   \
            ISR_CFG                             \
            IDLE_TASK_CFG                       \
            TICK_TASK_CFG                       \
            TIMER_TASK_CFG                      \
            STAT_TASK_CFG                       \
            MSG_POOL_CFG                        \
            .TaskStkLimit    = 0u               \
        }
        

         

        Once the OS_INIT_CFG_APP define is put assembled with all of the task and memory pool configs, the define should be assigned to the global variable as follows:

        const  OS_INIT_CFG           OS_InitCfg          = OS_INIT_CFG_APP;

         

        This variable is externed by the kernel so it must be named OSInitCfg as this is what the kernel uses internally.

        Final Thoughts

        While it may appear a little complicated, the configuration of Micrium OS Kernel is very important to the success of an application and worth the extra effort. A misconfigured kernel can cause issues farther down the road so it is important to configure it correctly to start with. By removing the general purpose heap it allows the developer to have a full understanding of what's going on under the hood so there is no confusion about how the kernel is configured. If you have any questions or comments feel free to leave them below.

      • 1
      • 2
      Next

      Tags

      • Wireless
      • High Performance Jitter Attenuators
      • EFR32MG21 Series 2 SoCs
      • Blue Gecko Series 2
      • Zigbee SDK
      • ZigBee and Thread
      • Internet Infrastructure
      • Sensors
      • Blue Gecko Bluetooth Low Energy SoCs
      • Z-Wave
      • Micrium OS
      • Blog Posts
      • Low Jitter Clock Generators
      • Bluetooth Classic
      • Makers
      • Flex SDK
      • Tips and Tricks
      • Smart Homes
      • IoT Heroes
      • Reviews
      • RAIL
      • Simplicity Studio
      • Mighty Gecko SoCs
      • Timing
      • Blue Gecko Bluetooth Low Energy Modules
      • Clocks
      • Ultra Low Jitter Clock Generators
      • General Purpose Clock Generators
      • Industry 4.0
      • Giant Gecko
      • 32-bit MCUs
      • blue-gecko-xpress-modules
      • Bluetooth Low Energy
      • 32-bit MCU SDK
      • Gecko
      • Microcontrollers
      • News and Events
      • Industrial Automation
      • Wi-Fi
      • Bluetooth SDK
      • Community Spotlight
      • Biometric Sensors
      • General Purpose Jitter Attenuators
      • Giant Gecko S1
      • Flex Gecko
      • Internet of Things
      • 8-bit MCUs
      • Isolation
      • Powered Devices

      Top Authors

      • Avatar image Mark Mulrooney
      • Avatar image Siliconlabs
      • Avatar image Nari Shin
      • Avatar image lynchtron
      • Avatar image deirdrewalsh
      • Avatar image Lance Looper
      • Avatar image lethawicker

      Archives

      • 2014 December
      • 2015 January
      • 2015 February
      • 2015 March
      • 2015 April
      • 2015 May
      • 2015 June
      • 2015 July
      • 2015 August
      • 2015 September
      • 2015 October
      • 2015 November
      • 2015 December
      • 2016 January
      • 2016 February
      • 2016 March
      • 2016 April
      • 2016 May
      • 2016 June
      • 2016 July
      • 2016 August
      • 2016 September
      • 2016 October
      • 2016 November
      • 2016 December
      • 2017 January
      • 2017 February
      • 2017 March
      • 2017 April
      • 2017 May
      • 2017 June
      • 2017 July
      • 2017 August
      • 2017 September
      • 2017 October
      • 2017 November
      • 2017 December
      • 2018 January
      • 2018 February
      • 2018 March
      • 2018 April
      • 2018 May
      • 2018 June
      • 2018 July
      • 2018 August
      • 2018 September
      • 2018 October
      • 2018 November
      • 2018 December
      • 2019 January
      • 2019 February
      • 2019 March
      • 2019 April
      • 2019 May
      • 2019 June
      • 2019 July
      • 2019 August
      • 2019 September
      • 2019 October
      • 2019 November
      Silicon Labs
      • About Us
      • In the News
      • Email Newsletter
      • Cookies
      • Contact Us
      • Community
      • Site Feedback
      • Investor Relations
      • Blog
      • Privacy and Terms
      • Corporate Citizenship
      Copyright © Silicon Laboratories. All rights reserved.
      粤ICP备15107361号-1