Skip navigation
Home > All Places > WICED Smart Bluetooth > WICED Smart Bluetooth Forums > Blog > Authors JacobT_81

WICED Smart Bluetooth Forums

14 Posts authored by: JacobT_81

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

Formal Tag4 Documentation:

Hardware User Guide (SDK 2.x and TAG4 Board)

WICED Smart Quick Start Guide (SDK 2.x and TAG4 Board)

 

 

 

Tag 4 Quick Hardware Setup:

 

       1. Default switch settings for USB power, Booting SFLASH:

                 A. SW6 – 2,4 ON, rest OFF

                 B. SW9 – all ON

                 C. SW8 – 2, 3, 6, 7, 8 ON, rest OFF

                 D. SW4 - VUSBs

                 E. SW5 – VREG

       2. On it's very first download, the board requires a recovery:

                 A. Press-hold BOOT, press-release RESET, release BOOT

                 B. Edit any make target: replace “download” with “recover UART=COMxx”

                 C. Use make target. Replace ‘xx’ with your COM port (found in Device Manager)

                 D. Double click make target to execute build.

       NOTE: After your initial recovery, regular “download” can be used.

 

Tag 4 Traces:

 

       1. Start traces:

                 A. Download your app

                 B. Switch SW6 1,2,3 OFF, 4 ON

                 C. Enable traces on the SDK (WICED Smart IDE > Trace > Start Debug Traces)

                 D. Press reset button

       2. Stop traces:

                 A. Disable traces on the SDK (WICED Smart IDE > Trace > Stop Debug Traces)

                 B. Switch SW6 1, 3 OFF, 2, 4 ON

                 C. Power cycle the Tag 4 board

       NOTE: if the board is not power cycled after stopping traces it will not download

       NOTE: the SDK traces must be stopped before they will start again.

 

Tag 4 Power Measurements:

 

       1. Basic Power measurement

                 A. Remove R29

                 B. Attach power analyzer to J12

                 C. Run an app, and start analysis

                 NOTE: Leads of J12 must be shorted by power analyzer in order to download app

       2. Power Optimization:

                 A. 20737 Chip Specific Optimization:

                      - Move RD5 and RD6 to A-C position (switches to external DC-DC converter)

                 B. To reduce peak current of RF TX:

                     - Remove FB3, disabling FTDI chip (download your app first)

                 C. To reduce idle current of board:

                      - Remove R39: disables I2C Expander (no sensors will function)

 

Tag 4 Boot Sources:

       1. Boot EEPROM

                  A. SW8 – 1,3,5 ON, rest OFF

                  B. Power cycle

       2. Boot SFLASH

                  A. SW8 – 2,3,6,7,8 ON, rest OFF

                  B. Power cycle

       3. Boot ROM only

                  A. Press and hold the 'ROM' button on the board

                  B. Power cycle

                  C. Release 'ROM' button

NOTE: when switching between boot sources it is often necessary to perform a recovery

 

Tag 4 I2C:

 

       1. Without any additional action, I2C APIs will only interact with EEPROM.

       2. In order to interact with the on-board MEMS sensors and bring out I2C onto J10:

                  A. The on-board I2C expander must be output enabled by calling:

 

 gpio_configurePin((GPIO_PIN_P8) / 16, (GPIO_PIN_P8) % 16, GPIO_OUTPUT_ENABLE, 1);

 

                  B. The I2C voltage rail must be powered by calling:

 

 gpio_configurePin((GPIO_PIN_P2) / 16, (GPIO_PIN_P2) % 16, GPIO_OUTPUT_ENABLE, 0);

 

 

Jacob W Torres

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

The Tag4 platform driver can be downloaded below.

 

Tag 4 Quick Demo Setup:

 

       1. Download driver below

       2. Copy driver into your application folder

       3. In your apps make file add:

                 include $(DIR)/tag_IV_driver/makefile.mk
       4. In your application include:

                 #include './tag_IV_driver/tag_IV_demo.h'

       5. Call tag_IV_demo() in a loop from within you application.

       6. Ensure that SW9 is on in all positions.

       7. Run traces on the app.

 

       Your application folder should look like this:

 

 

Tag 4 Driver Use:

 

       1. In your apps make file add:

                 include $(DIR)/tag_IV_driver/makefile.mk

       2. In your application include:

                 #include './tag_IV_driver/tag_IV_driver.h'

       3. To use sensors, ensure that SW9 is on in all positions.

       4. Call tag_IV_ functions from main application

 

       NOTE: see tag_IV_demo.h for a demonstration of API use.

 

 

Jacob W Torres

 

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

What are connection parameters?

 

There are three parameters that define a connection.

 

Connection Interval: the connection interval is the time between connection events. When in connection a central pings the peripheral as often as is defined by this connection interval. It is the peripheral's duty to respond to this ping in order for the central to consider the connection alive. At these intervals the client can perform it's reads/writes or the peripheral can perform notifications/indications. The interval can be anywhere from 7.5ms to 4s. When the peripheral requests an updated connection interval from the central, the peripheral sends an acceptable range. This increases the likelihood that the allowable window will overlap with what the central is able to provide.

 

Timeout: connection supervision timeout is the maximum time since the most recent received packet that the connection will be terminated by the central. The valid timeout range is 100ms to 3200ms.

 

Latency: slave latency is the number of connection events the peripheral is allowed to miss. If set to zero, the peripheral must respond to every connection event. The slave latency must be less than 500 and in the range from 0 to ( supervision_timeout / interval_max ) - 1 ) so that utilizing latency doesn't violate the timeout and cause a terminated connection. If there is no data to be sent, the peripheral will skip as many connection events as is allowed by the connection latency. Once there is data to be sent, every connection event is utilized to send that data until the tx buffer is clear, at which point the peripheral will resume skipping events. This scheme helps to save battery while maintaining throughput in the generally power-constrained peripheral device.

 

 

 

How do we control connection parameters?

 

Upon initial connection, there is no negotiation of parameters. The peripheral takes whatever the central gives. Upon successful connection, the peripheral may then request different parameters that are more suitable to it's functionality i.e. a higher throughput device will want a lower connection interval, while a power constrained device with low-throughput will want the longest possible connection interval with the most lenient latency.

 

No matter what, the central device always has final say over the connection parameters. It's important to note that the central can do absolutely nothing if it chooses. If the central has no intention of ever updating the connection parameters, it can simply reject any update requests that are sent its way. It is up to the peripheral to kill a connection if a central won't grant the parameters it needs to maintain its functionality and/or battery life.

 

There are two standard methods for the peripheral to request updated parameters:

 

 

Method 1: L2CAP Connection Parameter Update Request:

 

Step 1. The peripheral sends an l2cap packet to the central requesting updated parameters:

 

           void bleprofile_SendConnParamUpdateReq(UINT16 minInterval,

                UINT16 maxInterval, UINT16 slaveLatency, UINT16 timeout);

 

Example use:

 

           bleprofile_SendConnParamUpdateReq(32, 64, 0, 10);

 

These parameters are in special units:

 

     Min Interval = minInterval* 1.25 ms

     Max Interval = maxInterval* 1.25 ms

     Supervision Timeout = timeout * 10ms

 

This means that, as the peripheral, I am requesting a new connection interval between 40 and 80ms, a slave latency of 0, and a supervision timeout of 100ms.


Generally, this function is used in the connection up callback and it is recommended by the spec that it be done after GATT discovery and pairing/encryption.

 

Step 2. The central receives the request at the l2cap level. Different centrals will do different things at this point. By default, our 20736/7 stack will automatically grant whatever the client requested as long as it is feasible for the link layer to carry it out. On the other hand, resource constrained central devices will be far less likely grant the requested parameters. An iPhone, for instance, will not grant any connection interval below 30ms except for special use-cases.

 

Step 3. After the peripheral sends its request, it may want to hear back from the central once the process is complete. Of course, this is an optional step. But if the peripheral chooses, it can register a callback from the stack to be alerted when a connection parameter update is complete. To do so, declare a callback handler function with no parameters, and pass the function as a parameter to bleprofile_regAppEvtHandler_leConnUpdateComplete.

 

               Step A.      void paramUpdateHandler( void )

                                  {

                              ble_trace3("ConnParam in Timeout: %d, %d, %d",

                                    emconninfo_getConnInterval(),

                                    emconninfo_getSlaveLatency(),

                                    emconninfo_getSupervisionTimeout());

                                  }

               Step B. bleprofile_regAppEvtHandler_leConnUpdateComplete((BLEPROFILE_NO_PARAM_CB) paramUpdateHandler);

 

 

 

Method 2: Peripheral Preferred Connection Parameters (PPCP):

 

Step 1. the peripheral, instead of sending an l2cap request, puts the same requested parameters into a GATT characteristic of the mandatory GAP service. This characteristic has a value 8 octets in length. This value is further split into 2 octets per connection parameter. The format can be found below, as implemented in a4wp_power_receiver.c:

 

             // Handle 0x2e: characteristic preferred connection param, handle 0x2f characteristic value.

             // 0x0050 (100ms preferred min connection interval)

             // 0x00A0 (200ms preferred max connection interval)

             // 0x0000 (0 preferred slave latency)

             // 0x03E8 (10,000ms preferred supervision timeout)

             CHARACTERISTIC_UUID16 (0x0001, 0x0002, UUID_CHARACTERISTIC_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS,

                           LEGATTDB_CHAR_PROP_READ, LEGATTDB_PERM_READABLE, 8),

                  0x50, 0x00, 0xA0, 0x00, 0x00, 0x00, 0xE8, 0x03,

 

Step 2. the central will, in this case, need to first find the requested connection parameters. So, it must perform a GATT discovery on the peripheral and identify and have a special case for the existence of a PPCP characteristic. The exception to this is in Attribute Caching. If two devices are already bonded and the central cached the preferred connection parameters, there is no need to perform a GATT discovery. Once the characteristic is discovered by the client, different stacks will do different things. Our 20736/37 stack does not have built in support for this method of updating parameters. It is up to the app designer to discover the peripheral characteristic, read in the requested values, and handle, at the application level, whether or not to grant the requested parameters.

 

If, at the application level, it is decided to grant the requested parameters, or to grant new ones at least closer to the requested, the central device can call the following API:

 

           /**

           * \brief Connection update request

           * \ingroup blecm

           *

           * \details This function issues HCI LE Connection Update Command.  This

           * command may be used in the client application if it needs to update

           * connection parameters when connection is already established and

           * required parameters are different than ones requested by the peripheral. 

           * When peripheral device requests some parameters using L2CAP message, stack

           * automatically replies and sets controller appropriately and there is no

           * need to use this function call.

           *

           * \param connHandle HCI handle of established connection

           * \param connMinInterval Minimum value for the connection event interval

           * \param connMaxInterval Maximum value for the connection event interval

           * \param connLatency Slave latency for the connection in number of connection events

           * \param supervisionTimeout Supervision timeout for the LE Link

           * \param connMinEventLen Minimum length of connection needed for this LE connection

           * \param connMaxEventLen Maximum length of connection needed for this LE connection

           *

           */

           void blecm_ConnectionUpdate(INT32 connHandle, INT32 connMinInterval, INT32 connMaxInterval,

                            INT32 connLatency, INT32 supervisionTimeout, INT32 connMinEventLen,

                            INT32 connMaxEventLen);

 

Step 3. See Method 1 for Step 3 as it will be the same process to register for a parameter update callback to be alerted when the central has updated the parameters.

 

*some BLE centrals are known to not utilize this method. For instance, Apple devices will not look for a PPCP characteristic in their GATT discovery, they will only respond to l2cap requests.

The beginning of every GATT database (GATTDB) within the SDK begins the comment:

 

      // Service change characteristic is optional and is not present

 

What is the service change characteristic? Why would I want it? And how would I implement it?

 

Introduction:

     When a GATT client and server connect for the very first time, the client knows nothing about the server. In order to achieve a useful relationship, the client performs a GATT discovery on the server. There are a few variations in how the client can choose to go about this--search by handle, by type, etc.--we won't delve into the specifics of discovery.

     With low power in mind, these GATT discoveries are costly. Given that the client has the resources, it is far more efficient to jot down the details of the server's GATTDB and store that locally--this is called an Attribute Cache. In order to save power, upon future reconnections, the client will not perform a GATT discovery, but rather reach into its local memory to save power on both the client and server devices.

     However, a problem arises when we change the GATTDB between connections. The client will think the servers GATTDB is of the old form and begin operating on that basis. Now, this will only happen in the field under the premise that your firmware alters the GATTDB between connections.

     How do we fix this? One way is to utilize the Service Changed Characteristic. This characteristic is essentially a way for the server to indicate to the client that a change has been made to its GATTDB. The client should then update its Attribute Cache with the newest values.

 

Implementation:

 

This is how you would implement it in your GATTDB:

 

 

     const UINT8 gatt_database[]=

     {    

          // Handle 0x01: GATT service

          PRIMARY_SERVICE_UUID16 (0x0001, UUID_SERVICE_GATT),

   

          // Indicate that entire GATTDB has been altered

          // (handles: 0x0000 - 0xffff)

          CHARACTERISTIC_UUID16(0x0002, 0x0003,

               UUID_CHARACTERISTIC_SERVICE_CHANGED,

               LEGATTDB_CHAR_PROP_INDICATE,

               LEGATTDB_PERM_NONE, 4),

               0x00, 0x00, 0xff, 0xff,

   

          CHAR_DESCRIPTOR_UUID16_WRITABLE (0x0004,

               UUID_DESCRIPTOR_CLIENT_CHARACTERISTIC_CONFIGURATION,

               LEGATTDB_PERM_READABLE | LEGATTDB_PERM_WRITE_REQ, 2),

               0x00, 0x00,

          ...

     }

 

     This characteristic is implemented under the GATT Service that is mandatory to all GATTDB's. The Service Change Characteristic has a spec defined UUID. It needs only the ability to indicate and should have no required permissions. Lastly, it needs a value length of 4 bytes. These 4 bytes are used to designate the range of handles which have been changed. The first 2 bytes are the start handle, the last 2 bytes are the stop handle. As can be seen in the code above, we have specified this range to be all handles 0x0000 - 0xffff--tlling the client to completely rediscover the GATTDB.

     Since we need to use indications, this characteristic now needs a Client Configuration Descriptor--as all server initiated communications need. This descriptor is, essentially, two client-writable bytes that allow the client to give the server permission to transmit asynchronous communications--one byte is for notifications and the other is for indications. The service changed characteristic requires the use of indications (a notification with an application level ACK).

 

Use:

     The actual use of the Service Changed Characteristic requires the implementation of indications, GATT discovery by the client, and a change in the GATTDB by the server. These will not be covered here, but there exist individual resources for each:

 

     For implementation of indications see:

          Wiced-Smart-SDK/Apps/hello_client

          Wiced-Smart-SDK/Apps/hello_server

     For example firmware of a GATT change see:

          Re: How to change the GATT database after init (bleapp_set_cfg) ?

     For implementation of GATT discovery see:

          Wiced-Smart-SDK/Apps/automation_io_client

          Wiced-Smart-SDK/Apps/http_client

The attached sample app implements the HW timer. Run traces on the app to see the HW timer callback fire once every 100ms--10 times for every SW timeout loop.

 

The necessary components to get the HW timer working are (see attached .c file for full implementation):

 

     -Disable sleep:   

          1.  #include "devicelpm.h"

          2.  devlpm_registerForLowPowerQueries(hello_hw_timer_device_lpm_queriable, 0);

          3.  UINT32 hello_hw_timer_device_lpm_queriable(LowPowerModePollType type, UINT32 context) { return 0; }

 

     -Import patch and declaration:

          1.  #include "hw_timer.h"

          2.  In your make target add:

                    APP_PATCHES_AND_LIBS += hw_timer.a

          3.  declare in your .c :

                    extern void hw_timer_int_handler(void);

 

     -Define callback (must call stop, only call start is you want a loop):

          void hw_timer_int_handler(void)

          {

               hw_timer_stop();

               //do something

               hw_timer_start(10000);

          }

 

     -Initialize:

          1.  hw_timer_register_timer_expired_callback(hw_timer_int_handler);

          2.  hw_timer_start(100000);

 

 

    See Sleep Deep_Sleep Explanation and Techniques for alternate methods of disabling sleep.

 

Jacob

JacobT_81

WICED Smart ADC Example

Posted by JacobT_81 Sep 1, 2015

The attached app shows sample usage of the ADC hardware built into the 36/37/s chips.

 

To use the app, load it onto a Tag board that has a push button on P0. Run traces on the board. You'll initially see ~0V, and when you press the button you'll see the applied voltage on the voltage rail (~3300mV).

 

There are three necessary steps to get the ADC running, as seen in the app:

 

     Import:

     #include "adc.h"

 

     Instantiate in create function:

     adc_config();

 

     And poll the ADC using the function:

          adc_readVoltage(adc_convertGPIOtoADCInput(gpio_number));

 

 

*Calibration occurs within the initialization function. However, it may be useful to your application to calibrate the ADC to an external voltage for added assurance, or to do so periodically throughout the run-time of your application. To do so, call the function below: parameter one is the known voltage of the reference source in mV, and parameter two is the location of the reference voltage. The location can be configured to many different buses and all GPIOs (for external sources). Control click the second parameter within the source code to see a complete list of the locations that can be utilized as a reference voltage.

     adc_adcCalibrate(2000, ADC_INPUT_VDDIO);

 

The output of the polling function is in mV.

 

JacobT_81

 

More info on the ADC:

     BCM20732S ADC Configuration

     BCM20737S - ADC Noise-free resolution

     Questions related to the ADC implementation on the BCM2073XS...

     BCM20737 ADC DMA or Interrupt Service Routine

Hello Everyone,

 

See the attached file for an example of use of the PUART.

 

To use the sample code, load it onto two Tag boards, connect the boards via PUART, rx-to-tx, with a common ground (currently setup for P32, P33). Run HCI traces (default) on one board and click the button (P0) on the other board. An incrementing value, starting at 0, will be displayed on your terminal with every click of the button. This value was sent via PUART from the peripheral board (the value will increment twice on every click because the button registers an interrupt on each rising and falling edge).

 

Jacob

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

                When programming on the WICED Smart platform you are operating on our kernel. The kernel is responsible for the timing and handling of all processes related to the chip. The terms that you hear used commonly in embedded coding—timers, interrupts, BLE events, handlers, callbacks, etc.—are all interactions with the kernel. Not surprisingly, they’re the only way to interact with the kernel—and, some interaction with the kernel must take place in order for any of your code to be executed outside of the create function.

 

                Because you’re operating on a kernel, there is no main function. Instead, there is a “create” function which executes once on every power-up. Also embedded in the kernel is a watchdog timer. If any statement in one of your callbacks (explained below) takes more than 2 seconds to execute, the kernel will reset the chip to terminate the process—your code is sure to fail with an infinite loop in there.

                Before you embark on creating your own applications, it’s important to have a feeling for the layout of a basic application and how to interact with the kernel at a basic level.

 

Registering for callbacks

 

                There are three very important callbacks that will be utilized in almost every single application you create. They are almost always registered in the create function. For all of the following callback functions, you must do three things:

 

  1. Declare the function
  2. Define the function
  3. Register the function as a callback

 

The “connection_up” callback is called every time the device establishes a connection with a master device. Upon connection, one will generally want to stop advertisements. Additionally, you may wish to initialize a sensor or carry out other tasks that are only performed when in connection.

 

//register the callback in create fx

bleprofile_regAppEvtHandler(BLECM_APP_EVT_LINK_UP, called_on_conn_up);

 

The “connection_down” callback is called every time the device disconnects from its master device. Generally, you will restart advertisements in this function. Additionally, you may wish to de-initialize sensors or anything else that may conserve power while you are not in a connection in not in need of.

 

//register the callback in create fx

bleprofile_regAppEvtHandler(BLECM_APP_EVT_LINK_DOWN, called_on_conn_down);

 

The “advertisement_stopped” callback is called when the device has been advertising and has surpassed its set advertisement period without having entered a connection (advertisement period customized below). Without this callback function, the app will go dead once advertisements stop because the app never sets up a process to reinitiate them (there are ways around this: interrupts, timers, etc.). Naturally, we then want to put something in this function which restarts advertisements under some circumstance.

 

//register the callback in create fx

bleprofile_regAppEvtHandler(BLECM_APP_EVT_ADV_TIMEOUT, called_on_adv_stopped);

 

 

Registering Timers

 

                The timers are very important when you are first learning the use the WICED platform. They provide an extremely easy approach to interacting with the kernel without having to deal with interrupts (which will be explained in a later post).

 

The timers, which must be instantiated together, call two respective functions every time the timer completes a cycle. The regular timeout always calls its function once per second. On the other hand, the “fine timeout” calls its function however frequently you wish (within reason). If you set the fine timeout to be called in the range of microseconds (below 5ms) it will significantly inhibit the timing of the kernel and likely disable all BLE transmissions.

 

The timers can be started and stopped at your will using the WICED API.

 

Add to your create function:

 

    bleprofile_regTimerCb(fine_timeout, one_sec_timeout);

 

Define and declare the callback functions:

 

          static void one_sec_timeout( UINT32 count );

          static void fine_timeout( UINT32 finecount );

 

Altering fine timer:

 

          const BLE_PROFILE_CFG hello_sensor_cfg = {

                /*.fine_timer_interval            =*/ 1000, // ms

          }

 

Start/Stop APIs:

 

    bleprofile_StartTimer();

    bleprofile_KillTimer();

 

Customizing Advertising Interval and Timeout

 

                This won’t come in handy until the completion of your first app, when you will be attempting to optimize your power consumption. The WICED platform allows you to customize the duration of “high” advertising (more frequent/powerful transmissions) as well as the subsequent “low” advertisements (less powerful, less frequent transmissions). At the end of both of these durations, if we have registered for an “advertisement_stopped” callback, it will be called.

 

           const BLE_PROFILE_CFG hello_sensor_cfg = {

                /*.fine_timer_interval        =*/ 1000, // ms

                /*.default_adv                =*/ 4,    // HIGH_UNDIR

                /*.button_adv_toggle          =*/ 0,    // pairing button

                /*.high_undirect_adv_interval =*/ 32,   // slots

                /*.low_undirect_adv_interval  =*/ 1024, // slots

                /*.high_undirect_adv_duration =*/ 30,   // seconds

                /*.low_undirect_adv_duration  =*/ 300,  // seconds

           }

 

                For further reading, refer toHow to Write WICED Smart Applications. You will find longer lists of event callbacks that are at your disposal.

 

Jacob W. Torres

 

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

When using a microcontroller with a kernel, interrupts are the most fundamental way to interact with that kernel. In order for the push of a button to turn on an LED we must register that button as an interrupt, and toggle the LED within the interrupt_handler function that you will declare, define, and register as a callback with the kernel.

 

If all you wish to do is push a button that turns on the LED, there is very quick and easy way to register for interrupts on the WICED platform:

 

                Configure the push button in the GPIO configure definition.

 

              const BLE_PROFILE_GPIO_CFG interrupt_example_gpio_cfg = {

                  {GPIO_PIN_WP, GPIO_PIN_BUTTON, 13,

                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},

 

                  {GPIO_SETTINGS_WP, GPIO_SETTINGS_BUTTON, GPIO_SETTINGS_LED,

                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

              };

 

                Define, declare, and register the interrupt handler (handler must have a UINT8 parameter).

 

              bleprofile_regIntCb((BLEPROFILE_SINGLE_PARAM_CB)interrupt_handler);

 

                Toggle the LED within interrupt handler.

 

              void interrupt_handler(UINT8 gpio) {

                  bleprofile_LEDBlink  (50, 50, 3);   

                }

 

Interrupts, on the other hand, that are necessary for other, higher volume, higher speed/frequency applications require a different method of instantiation. There are a few more steps as well as more parameters to take into account. If you require interrupts for anything other than a single push button the following method is necessary to use:

         

  1. Define and declare your interrupt handler function.
  2. Configure interrupt mask.
  3. Register your interrupt handler using the mask.
  4. Configure the pin as an interrupt.

 

The interrupt handler must be defined and declared as a function that takes two parameters, one of which being an 8-bit integer, the other a pointer.

 

                            static void interrupt_handler (void* parameter, UINT8 arg);

 

The interrupt mask is an array of three 16 bit integers. The mask is passed as a parameter while registering for an interrupt in the following line of code. The three integers refer to the three ports of GPIOs. For the purpose of instantiating GPIO 4 as an interrupt, the

following mask would suffice:

 

                            UINT16 masks[3] = {(1 << 4), 0 , 0}; //replace 4 with GPIO number

 

But if you wished to access a GPIO outside of port 0, for instance pin 24, the mask would look like this:

 

              UINT16 masks2[3] = { 0, (1 << 8), 0};

              //port 1 starts at 16, 24 is 16+8, mask is set to 8

 

Below the mask we’ll register the interrupt handler function using the above mask as a parameter in order for the interrupt handler to be called on the appropriate GPIO interrupt.

 

              gpio_registerForInterrupt(masks, interrupt_handler, NULL);

 

In the above registration of the interrupt, when pin 4 receives an interrupt, the interrupt handler will be called and execute whatever code it contains.

 

But pin 4 doesn’t receive interrupts until its configured to. So, the final step is to configure it. The following code is worthy of copying and using for most interrupts. The third parameter can be changed to either rising edge or both edges.

 

              gpio_configurePin(0, 4, GPIO_EN_INT_RISING_EDGE, GPIO_PIN_OUTPUT_LOW);

 

The attached sample code implements the interrupt in the create function where you will see the last three steps executed sequentially.

 

              UINT16 masks[3] = {(1 << 4), 0 , 0};

              gpio_registerForInterrupt(masks, interrupt_handler, 0);

              gpio_configurePin(0, 4, GPIO_EN_INT_RISING_EDGE, GPIO_PIN_OUTPUT_LOW);

 

Now to complete our final project, all we must do is toggle the GPIO within the interrupt handler. The easiest way to do this is to call the following function (see API directory for detailed parameters):

 

              bleprofile_LEDBlink(50, 50, 3);

 

If using this API be sure that you properly configure your GPIO configuration array (as seen in prior posting of this project).

 

The attached sample firmware shows all these aspects put together, so, when loaded the LED flashes on every push of the button (sample firmware uses GPIO 0 not 4).

 

The second method is well suited for receiving interrupts from sensors or anything else attached to one of your serial busses. The interrupt pin from the sensor should be hardwired directly to the GPIO you wish to register as an interrupt (if the interrupt on the sensor is open-drain, pull it up with a 10K).

 

Registering for interrupts from sensors is another topic in itself which we will cover in a future blog posting. In summary, interrupts are a fundamental way of interacting with the kernel via push button and sensor interrupts.

 

Jacob W Torres

 

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

JacobT_81

How to Toggle a GPIO

Posted by JacobT_81 Jul 21, 2015

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

    To toggle a GPIO, paste the following code into your program:

 

          #define GPIO_NUM      8

          #define PIN           GPIO_NUM%16

          #define PORT          GPIO_NUM/16

          gpio_configurePin(PORT, PIN, GPIO_OUTPUT_ENABLE | GPIO_INPUT_DISABLE, GPIO_OUTPUT_HIGH);

          gpio_setPinOutput(PORT, PIN, GPIO_OUTPUT_HIGH);

 

    It really is that simple, despite some confusion on the topic. All you have to do it change the pin number, and toggle the parameter high and low. There are, in fact, some special cases to take into account, but for the most part these lines of code are all you need to toggle a GPIO. gpio_configurePin must be called only once, gpio_setPinOutput is the only one that must be called thereafter.

 

    Description of parameters:

                    gpio_configurePin(PORT, PIN, PARAMETER, HIGH/LOW)

        gpio_setPinOutput(PORT, PIN, HIGH/LOW);

 

    When referring to a pin, however, we use two separate numbers. First we use the port number, next the pin number. There’s nothing special about these numbers, they just correspond the description below:

 

                    GPIO 00-15            PORT 0

                    GPIO 16-31            PORT 1

                    GPIO 32-39            PORT 2 

 

    For the HIGH/LOW parameter, always put a zero, one, or a corresponding reference.

 

    The PARAMETER is what defines the behavior of the GPIO. Paste the provided code into any app function, and control-click one of the parameters that’s in place. You’ll see a list of every possible parameter to put in here. This is where confusion stems from, because the same API is being used to instantiate inputs, outputs, interrupts, etc. For the purposes of toggling a GPIO as an output, leave the code above as I’ve written it.

 

    The second API call is only used after initial configuration of an output GPIO. The parameters are easy to work with and identical to parameters 1, 2, and 4 of the configure API.

 

    There’s one additional thing to take into consideration. There is a GPIO configuration array at the top of every sample app. This is only to be used if you wish to utilize the APIs that they register pins for. For example, in order to blink an LED using the API below, you must configure pin 13 (LED on Tag 4) within that array.

 

bleprofile_LEDBlink(10, 10, 3);

 

    But utilizing these APIs can also create problems. By instantiating pin the 13 in the GPIO configuration array you relinquish control of the GPIO to the kernel. Meaning, if you attempt to use both LED APIs and manually toggle the LED GPIO in the same app, you may get unexpected behavior. Choose one or the other to be safe.

 

Jacob W Torres

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

JacobT_81

Project 1 Wrap-Up

Posted by JacobT_81 Jul 21, 2015

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

          In order to transmit sensor data via BLE notification, all we’re going to do is combine the preceding two sample applications and alter a few details. We’re going to slightly alter our GATT database to accommodate for the volume of data we’ll be sending. Additionally, our transmission process will be altered to accommodate for data larger than one byte.

 

          For the sake of accuracy, when we send data from the HTS221, we multiply the decimal values by 10 (our platform doesn’t support decimal, float values). These scaled-up numbers will often exceed one byte, but we must send those values one byte at a time.

 

          As a result, in the sample firmware, the length of the GATT characteristic has been extended from 1 byte to 5: 2 bytes for humidity, 2 for temperature, and one as a gap between the two values (for readability).

 

          CHARACTERISTIC_UUID128 (0x0029, HANDLE_HTS221_VALUE_NOTIFY,

                                  UUID_HTS221_CHARACTERISTIC_NOTIFY,

                                  LEGATTDB_CHAR_PROP_NOTIFY,

                                  LEGATTDB_PERM_NONE, 5),

               'D','a','t','a',' ','T','x',

 

          When working with this many values, it’s easier to weed out any discrepancies by zeroing all values out in between your read and write. For that reason, you’ll see a block of code within the transmission function doing just that.

 

  //clear out the previous values
  db_pdu.pdu[0x00] = 0;
  db_pdu.pdu[0x01] = 0;
  db_pdu.pdu[0x02] = 0;
  db_pdu.pdu[0x03] = 0;
  db_pdu.pdu[0x04] = 0;

 

          Additionally, we need to deal with the issue of sensor data exceeding one byte. If you try to put more than one byte of data into a one byte slot, you’ll be transmitting only the value that exceeds the one bye. For instance, one byte is equal to 256. If we try to fill it with the value 257, we’ll end up transmitting the value 1.

 

          With that said, you’ll find in the sample app a logic block within the transmit function that allows the data to carry over into the next byte if it exceeds one byte. This is, again, for the sake of readability and it isn’t necessary to dissect the logic. In your own apps, you can adapt the data transmission any way you’d like. And it's likely a better choice to just send changes in the data, which will likely never exceed a single byte.

 

          if (humidity < 255) {

            db_pdu.pdu[0x04] = humidity;

          }else if(humidity < 65535){

            db_pdu.pdu[0x03] = (humidity / 255);

            db_pdu.pdu[0x04] = humidity;

          }

 

---------------------------------------------------------SAMPLE FIRMWARE STEP-BY-STEP----------------------------------------------------

 

The following list is a combination of the preceding 2 posts and it will allow us to successfully transmit data to our LightBlue app:

 

    1. Power up the sensor
    2. Initialize the sensor
    3. On a loop:
      1. Start measurements of sensor
      2. Read data from sensor to a buffer
      3. Read the GATT characteristic
      4. Write the sensor buffer to the GATT characteristic
      5. Send the notification

 

Run the app on your WICED device and find it on your LightBlue app. Upon connection, you’ll receive a stream of notifications of the current temperature and humidity in hex format separated by a zeroed one byte margin for readability. Again, breathe on your board and watch the values sharply increase.

 

Being able to stream values to your LightBlue app is more than just the end goal. Rather, the ability to stream these values is an incredibly powerful tool in the design process. Consider the case that you’ve just designed your own board using a WICED BLE chip, but you don’t have the ability to run traces on the board. You now have the power to receive data from your chip and figure out where your code is failing.

 

Jacob W. Torres

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

           Transmitting BLE notifications is a straightforward process using the WICED APIs. The APIs are much more user friendly, however, with a conceptual understanding of the process. Before proceeding you should be comfortable with common terms in BLE as well as the very basics of the protocol. How do devices find one another? Which is the master and which is the slave? And which one advertises?

 

The question we’ll delve into today is: how do BLE devices exchange data? There is one key concept that must be understood to be able to instantiate BLE communication in your code: the GATT Database is the medium for tranceiving all data.

 

                In BLE, both the master (likely your phone) and the slave (likely your WICED device) are allowed to read or write to/from your GATT database. These reads and writes can follow any logical sequence of events:  the client can read data which is being continuously written by the slave, the client can write to the slave in order to tell the slave to write data to the GATT (the client will then perform a read), etc. These sequences are not fixed, and can be instantiated in any logical manner.

 

Additionally, the slave can send unrequested, unacknowledged “notifications” to the master, the master has the power to either ignore or listen to these notifications. This cuts out the complexities of reads and writes. The majority of BLE transmissions carried out are notifications. They are easy to use and very practical. For that reason, this post will demonstrate the execution of a BLE notification.

 

To send a notification, you must do two things: load the data into your GATT database and call the “notify” API to carry out the transmission at a hardware level.

 

The GATT database is not conceptual as it may have sounded up to this point—it’s to be clearly defined in every BLE app you ever create. The GATT database a hierarchy of services, characteristics, and descriptors. Some of these are mandatory—they will be identical in every app. The unique services and characteristics that you’ll be adding to your future apps are “vendor-specific.”

 

Within the GATT database there are services. Within services there are characteristics. Within characteristics there are descriptors. That’s all you need to know about the hierarchy within the GATT database. Characteristics are where the data is held, and where you’ll be writing in order to send data over BLE.

 

A service may have one or more characteristics. But a characteristic cannot exist outside of a service. For instance, although measuring someone’s heart rate only has one possible characteristic it requires a Heart Rate Service, and underneath it a Heart Rate Characteristic (the actual pulse rate). On the other hand, other services will require many characteristics which can all hold their own values. For example, a Vital Signs Service would have individual characteristics for pulse rate, blood pressure, respiratory rate, etc. For now, we will not discuss descriptors. They are only utilized for reads and writes to the GATT database (not notifications).

 

 

--------------------------------------------------------STEP-BY-STEP GUIDE TO FIRMWARE------------------------------------------------------

 

 

There are 5 distinct steps required to instantiate BLE notifications using the WICED API:

 

  1. Setup your GATT database
  2. Read the current value of your vendor-specific characteristic.
  3. Change that value.
  4. Write it back to your vendor-specific characteristic.
  5. Send the notification.

 

1.  Setting up the GATT database is the most difficult part of the process. The complexity of setting up your GATT database is dependent upon the complexity of your app. Contained in the sample firmware is likely the most simplified GATT database you will come across—aside from the mandatory inclusions, there is one service and one characteristic.

 

Each service must have a handle and a UUID. Each characteristic must have a handle and a handle value. These are unique values that must be defined by you. UUIDs can be generated randomly using an online generator.

 

In addition to these identifiers, each GATT definition must have defined permissions, property definitions, and lengths (number of bytes). The parameters used in the sample code are all you will need in order to send notifications. But there is a long list of these parameters available in How to Write WICED Smart Applications that guide you through the process of customizing your GATT database form the ground up. For further reading, I highly recommend reading about the GATT database parameters that are at your disposal in this document. 

 

                      const UINT8 ble_tx_gatt_database[]={

              /////////////////////mandatory for all apps//////////////////////////

              PRIMARY_SERVICE_UUID16 (0x0001, UUID_SERVICE_GATT),

 

 

              PRIMARY_SERVICE_UUID16 (0x0014, UUID_SERVICE_GAP),

 

 

              CHARACTERISTIC_UUID16 (0x0015, 0x0016, UUID_CHARACTERISTIC_DEVICE_NAME,

                                    LEGATTDB_CHAR_PROP_READ,

                                    LEGATTDB_PERM_READABLE, 16),

                'B','L','E',' ','D','a','t','a',' ','T','x',0x00,0x00,0x00,0x00,0x00,

 

 

              CHARACTERISTIC_UUID16 (0x0017, 0x0018, UUID_CHARACTERISTIC_APPEARANCE,

                                    LEGATTDB_CHAR_PROP_READ,

                                    LEGATTDB_PERM_READABLE, 2),

                BIT16_TO_8(APPEARANCE_GENERIC_TAG),

              /////////////////////////////////////////////////////////////////////

 

              ////////////////vendor specific, optional, customize///////////////

              PRIMARY_SERVICE_UUID128 (HANDLE_BLE_TX_SERVICE_UUID,

                                      UUID_BLE_TX_SERVICE),

 

              CHARACTERISTIC_UUID128 (0x0029, HANDLE_BLE_TX_VALUE_NOTIFY,

                                      UUID_BLE_TX_CHARACTERISTIC_NOTIFY,

                                      LEGATTDB_CHAR_PROP_NOTIFY,

                                      LEGATTDB_PERM_NONE, 1),

                '1','2','3','4','5','6','7',

              ////////////////////////////////////////////////////////////////////

          };

 

2.  Reading the current value of the characteristic you wish to write to is as simple as calling an API. First, we must instantiate a variable of type BLEPROFILE_DB_PDU, then call on the appropriate API to read. This API requires two parameters: the handle of the value you wish to read, and a pointer to the newly created variable.

 

                        BLEPROFILE_DB_PDU db_pdu;

            bleprofile_ReadHandle(HANDLE_BLE_TX_VALUE_NOTIFY, &db_pdu);

 

3.  In order to change the transmitted value, we change the "pdu" component of the db_pdu struct (.pdu[]) equal to the data we wish to send. In this case a constant:

 

            db_pdu.pdu[0] = my_data;

 

4.  Again, all we must do is call an API to write the changed value back to the characteristic, using the same parameters as the read:

 

            bleprofile_WriteHandle(HANDLE_BLE_TX_VALUE_NOTIFY, &db_pdu);

 

5. Lastly, we call the notify API to carry out the transmission at the hardware level. Note the parameters and emulate in your own code.

 

            bleprofile_sendNotification(HANDLE_BLE_TX_VALUE_NOTIFY,

                                      (UINT8 *)db_pdu.pdu, db_pdu.len);

 

                For the most basic BLE notification, there are no further steps that you need to take. In the sample app, we load the notification with an arbitrary constant and send it to out. But this very byte can be loaded with any data of our choosing: sensor data, status, timing, etc. Run the test app along with your LightBlue App and watch the constant value be received every 500ms.

 

Jacob W. Torres

 

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

 

 

    We won’t get into the fine details of the I2C protocol. Here, we will show how to execute I2C reads and writes using the WICED Smart platform and API. Keep in mind that what’s contained in this blog only applies to other chips at an abstract level. The specific API and drivers we have are for WICED Devices.

 

    What is a driver? Without an understanding of I2C you may look at an I2C driver in wonder. The driver is a set of functions that calls on the I2C API (the API is what is actually executing the I2C protocol at a hardware level) in a device-specific manner to make the sensor serve its purpose. Every sensor has its own driver and therefore requires that a different sequence/different register addresses/different I2C addresses be used. When creating your own apps you will never interact with the I2C API, but rather the driver for the specific sensor with which you wish to interact.

 

    The drivers necessary for the Tag4 are attached to this post. But let’s consider the other case: you get your hands on an I2C sensor that isn’t attached to your Tag4 device and you want to attach it. That’s for a separate, future project entirely, but we will, indeed, enter the realm of writing our own drivers for I2C devices.

 

    There are basic principles to understand before attempting to use an I2C device. It’s highly advised that you gain a general understanding of the protocol before proceeding (understand the use of addresses versus hardware chip select lines like on SPI, a different serial protocol).

 

    In all the research you’ve done at this point, you likely understand what a read and write sequence looks like. Conceptually, however, how do we interact with a sensor?

 

    • Writing to an I2C Register
      • In the sensor’s data sheet you will find a series of command registers. These command registers are to be written to in order to change the characteristics of the sensor. This is what your initialization will consist of (see below).

 

    • Reading Data from an I2C Device
      • In order to gather data from an I2C sensor, there are generally multiple steps taken. In order to read sensor data, the device must be written to, told to fill its data register with data, then that register must be read from. Sometimes, a third step is necessary: you may need to check the status of the data prior to reading (by reading a status register) and not perform the reading until the data is available. This is sometimes necessary because prematurely reading data will destroy it.

 

Fortunately, prewritten drivers do most of the tough work for you. Nonetheless, it’s necessary to understand for future I2C projects and debugging.

 

 

-------------------------------------------------STEP-BY-STEP GUIDE TO SAMPLE FIRMWARE-----------------------------------------------

 

 

With all that said, there are four steps that you will see in the attached test code that are necessary to accomplish the task at hand:

      1. We must apply power to the sensor.
      2. Then, we must initialize the sensor.
      3. Third, we tell the sensor to fill its data registers with the information we need.
      4. Lastly, we read the data register.

 

1.  On the Tag4 board the sensors are powered by a BJT that that is controlled by GPIO 2. In order to apply power to the sensors, in our create function we put GPIO 2 low (PNP BJT) in order to apply power to the sensors. Additionally, the sensor switch block must be physically switched on (SW 9).

 

gpio_configurePin(0, 2, GPIO_OUTPUT_ENABLE | GPIO_INPUT_DISABLE, 0);
gpio_setPinOutput(0, 2, 0);

 

2.  To initialize the sensor, we have a prewritten function in the test code that initializes the sensors that will suit most of your needs. In this function we activate the sensor, set the ODR rate (sample rate, linearly related to power consumption), set average values, and finally, we enable the BDU feature. Without ever looking into the driver, however, we can alter these characteristics in order to fit app-specific needs.

 

   

                        void init_hts221(void){

                if(HTS221_Activate() == HTS221_OK)

                    ble_trace0("HTS221_Activate Successful");

                else

                    ble_trace0("HTS221_Activate Failed.");

 

                if(HTS221_Set_Odr(HTS221_ODR_7HZ) == HTS221_OK)

                    ble_trace0("HTS221_Set_Odr Successful");

                else

                    ble_trace0("HTS221_Set_Odr Failed.");

 

                if(HTS221_Set_AvgHT(HTS221_AVGH_8, HTS221_AVGT_4) == HTS221_OK)

                    ble_trace0("HTS221_Set_AvgHT Successful");

                else

                    ble_trace0("HTS221_Set_AvgHT failed");

 

                if(HTS221_Set_BduMode(HTS221_ENABLE) == HTS221_OK)

                    ble_trace0("HTS221_Set_BduMode Successful");

                else

                    ble_trace0("HTS221_Set_BduMode failed");

            }

 

 

3.  Given that we’re working with one stop measurements it is necessary to tell the sensor to start its one stop measurement. This is compared to instantiating a FIFO which will constantly stream data. The code for starting the measurement is paired with that of step 4..

 

4.  Lastly we read the data register. This is a call to a function in the driver, into which we pass two pointers for the data to be inserted. We'll then use this variable as the buffer of our data to accomplish anything we want: printing their value, transmitting it, etc.

 

                          if(HTS221_StartOneShotMeasurement() == HTS221_OK){

 

                  if(HTS221_Get_Measurement(&humidity, &temperature) == HTS221_OK)

                      ble_trace2("Humi*10=%6d, Temp*10=%6d", humidity, temperature);

 

              }

 

 

Steps 1 and 2 are to be executed once in your create function. Steps 3 and 4 will be done continuously, in a loop (in our test app). When we work with interrupts we will be able to trigger a read on an interrupt. Alternatively, we may consider the case of power saving. In the test app, we start the sensor and never stop it. But if every micro-amp counts for your application, you may de-initialize the sensor and put P2 high, then reinitialize when you need the sensor again.

 

              void hts221_deinitialize ( void ) {

 

            if(HTS221_DeActivate() == HTS221_OK)

                ble_trace0("HTS221_DeActivate Successful");

            else

                ble_trace0("HTS221_DeActivate Failed.");

 

        }

 

Run traces on the attached test code. The values will stream across your terminal. Breathe on your board and watch the values for humidity and temperature rise.

 

Jacob W Torres

 

 

Back to main page: Leveraging TAG4 to better understand the WICED Smart Programming Environment

******************************************************************************************************

Tag 4 Specific Information

******************************************************************************************************

 

 

 

******************************************************************************************************

Simple Projects for All WICED Smart Platforms

******************************************************************************************************

 

 

 

******************************************************************************************************

Setting Up the WICED Smart Platform

******************************************************************************************************

Learn to Navigate the Forum

  • Learn to find your way around the WICED Sense forum.
  • When using the search bar, be sure to use "click here to only show results for Bluetooth Forums" when it appears

 

Download the SDK

  • Download the most recent release for your operating system here: WICED Smart Docs & Downloads
  • You may run into problems with Java, see here: Java Issues....
  • You may need to update your USB drivers as well: Virtual COM Port Drivers
  • The device driver may not have downloaded when you plugged in your WICED Device (pops up in bottom right corner if failed). If this is the case, go to Device Manager>Other Devices. Find BOTH devices associated with your board, double click one, click "Look for Drivers," click "Next" and the download should complete shortly thereafter. Then do this for the second device.

 

Download LightBlue

  • Allows your phone to be used as a test device for your BLE apps
  • Download in your mobile device’s app store

 

Learn to Use the SDK

  • Refer to the WICED Smart Quick Start Guide (SDK 2.x and TAG4 Board)
    • Download your first test App. "Heart Rate Monitor" feeds fake data notifications.
    • Connect to the device on LightBlue, listen to its notifications
  • Explore the SDK
  • Explore the API library
    • This is a highly useful tool to have in your arsenal.
    • Normally found in the SDK (WICED-Smart-SDK>Doc>API)
    • But, you may have trouble accessing it that way.
      • Alternate: right-click the link>"Open With">"Web Browser"
    • Get to know the APIs at your disposal through this library
    • When you begin writing your own apps, you will want to heavily utilize this resource.

 

 

Jacob W. Torres

JacobT_81

Filter Blog

By date:
By tag: