Thursday 7 December 2017

CSR1010 Bluetooth Low Energy (BLE) Demo With Source

There are many Bluetooth Low Energy chips on the market, by a chance I got a CSR development system and did some experiments with it.

CSR1010 is a BLE SoC and the development system is based on C. You don't need to deal with the low level Bluetooth protocols, all you need to do is creating your own service and using the APIs.

To be honest, the CSR development system has only very limited debugging abilities, you can somewhat debug your code doing such as 'stepping' and set break point, but there's no way you can see the values of your variables, so it makes little sense to do the debugging.

Fortunately, my code runs and it seems OK.

Let's describe the functionalities of this simple demo first, besides the basic mandatory BLE services, there is only one user service created, it has 2 characteristics, one is feeding a random data of 1 byte to the client, when the notification is turned on, the Bluetooth board will push the data to the mobile phone every 2 seconds. And the another one is 24-bit and has both read and write properties, this data is used to control the tri-color LED on board. The MSB byte is for the red LED, the LSB is used to control the blue. The number controls the duty cycle of the PWM, 0 is OFF, and set to 0xff you get 100% duty cycle the LED will be in full brightness.

The user service is created like this:

#ifndef JJ_TEST_SERVICE_H
#define JJ_TEST_SERVICE_H

primary_service
{
    uuid : 333f5834-123e-4f2b-9da8-8e863c322e5f,
    name : "JJ_test_service",
    
    characteristic
    {
        uuid : e8069f93-c150-48ef-a02d-589b652e9538,
        name : "RANDOM_NUMBER",
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        properties : [read, notify],
        value : 0x00,
        client_config
        {
            flags : [FLAG_IRQ],
            name : "RANDOM_NUMBER_C_CFG"
        }
    },
    
    characteristic
    {
        uuid : d1dea016-a7fb-4dfa-84c5-b24158669e20,
        name : "LIGHT_CTL",
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        properties : [read, write],
        size_value : 3
    }
}


#endif

This demo is based on the CSR demo application "gatt_server", as the whole source code is too much, I only include the user created part here.

JJ_test_service.c:

/*============================================================================*
 *  SDK Header Files
 *===========================================================================*/

#include <gatt.h>           /* GATT application interface */
#include <buf_utils.h>      /* Buffer functions */
#include <random.h>
#include <pio.h>


/*============================================================================*
 *  Local Header Files
 *===========================================================================*/

#include "gatt_server.h"    /* Definitions used throughout the GATT server */
#include "app_gatt_db.h"    /* GATT database definitions */
#include "JJ_test_service.h"

#define PIO_BUTTON      1          /* PIO connected to the button on CSR10xx */
#define PIO_LED_RED     9          /* PIO connected to the RED on CSR10xx */
#define PIO_LED_GREEN   10           /* PIO connected to the GREEN on CSR10xx */
#define PIO_LED_BLUE    11          /* PIO connected to the BLUE on CSR10xx */

#define PIO_DIR_OUTPUT  TRUE        /* PIO direction configured as output */
#define PIO_DIR_INPUT   FALSE       /* PIO direction configured as input */

gatt_client_config jj_client_cfg;
typedef struct _LIGHT_CTL
{
    uint8   red;
    uint8   green;
    uint8   blue;
} LIGHT_CTL;

LIGHT_CTL jj_light_ctrl;

extern void JJ_Test_InitChipReset(void)
{
    PioSetMode(PIO_LED_RED, pio_mode_pwm3);
    PioSetMode(PIO_LED_GREEN, pio_mode_pwm1);
    PioSetMode(PIO_LED_BLUE, pio_mode_pwm2);
    PioSetDir(PIO_LED_RED, PIO_DIR_OUTPUT);
    PioSetDir(PIO_LED_GREEN, PIO_DIR_OUTPUT);
    PioSetDir(PIO_LED_BLUE, PIO_DIR_OUTPUT);
    PioEnablePWM(1, TRUE);
    PioEnablePWM(2, TRUE);
    PioEnablePWM(3, TRUE);
    PioConfigPWM(3, pio_pwm_mode_inverted_push_pull,
                 0, 255, 0,
                 0, 255, 255, 0);
    PioConfigPWM(1, pio_pwm_mode_inverted_push_pull,
                 0, 255, 0,
                 0, 255, 255, 0);
    PioConfigPWM(2, pio_pwm_mode_inverted_push_pull,
                 0, 255, 255,
                 0, 255, 255, 0);
}

extern void JJ_Test_DataInit(void)
{
    jj_client_cfg = gatt_client_config_none;
}

extern void JJ_Test_ReadDataFromNVM(uint16 *nvm_offset)
{
}

extern bool JJ_Test_CheckHandleRange(uint16 handle)
{
    return ((handle >= HANDLE_JJ_test_service) &&
            (handle <= HANDLE_JJ_test_service_END))
            ? TRUE : FALSE;
}

extern void JJ_Test_HandleAccessRead(GATT_ACCESS_IND_T *p_ind)
{
    sys_status rc = sys_status_success; /* Function status */
    uint16 result;
    switch (p_ind->handle)
    {
        case    HANDLE_RANDOM_NUMBER:
            result = Random16();
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 1, (uint8*)&result);
            break;
        case    HANDLE_RANDOM_NUMBER_C_CFG:
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 2, (uint8*)&jj_client_cfg);
            break;
        case    HANDLE_LIGHT_CTL:
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 3, (uint8*)&jj_light_ctrl);
            break;
        default:
            rc = gatt_status_read_not_permitted;
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 0, NULL);
    }
}

extern void JJ_Test_HandleAccessWrite(GATT_ACCESS_IND_T *p_ind)
{
    sys_status rc = sys_status_success; /* Function status */
    switch (p_ind->handle)
    {
        case    HANDLE_RANDOM_NUMBER_C_CFG:
            jj_client_cfg = BufReadUint16(&p_ind->value);
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 0, NULL);
            break;
        case    HANDLE_LIGHT_CTL:
            jj_light_ctrl = *((LIGHT_CTL*)p_ind->value);
            PioConfigPWM(3, pio_pwm_mode_inverted_push_pull,
                         jj_light_ctrl.red, 255-jj_light_ctrl.red, 0,
                         jj_light_ctrl.red, 255-jj_light_ctrl.red, 255, 0);
            PioConfigPWM(1, pio_pwm_mode_inverted_push_pull,
                         jj_light_ctrl.green, 255-jj_light_ctrl.green, 0,
                         jj_light_ctrl.green, 255-jj_light_ctrl.green, 255, 0);
            PioConfigPWM(2, pio_pwm_mode_inverted_push_pull,
                         jj_light_ctrl.blue, 255-jj_light_ctrl.blue, 0,
                         jj_light_ctrl.blue, 255-jj_light_ctrl.blue, 255, 0);
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 0, NULL);
        default:
            rc = gatt_status_write_not_permitted;
            GattAccessRsp(p_ind->cid, p_ind->handle, rc, 0, NULL);
    }
}

void notificationTimerHandler(timer_id tid)
{
    uint16   temp;
    temp = Random16();
    if (jj_client_cfg == gatt_client_config_notification)
    {
        GattCharValueNotification (GetConnectionID(), HANDLE_RANDOM_NUMBER, 1, (uint8*)&temp);
    }
    TimerCreate(2*SECOND, TRUE, notificationTimerHandler);
}

As CSR is not providing a mobile phone app to be used, I'll use a generic BLE app from Nordic, another Bluetooth chip manufacture. That seems a bit weird but it works.

A video:

2 comments:

  1. Hello JJ. Could you send me this project? I'm begineer using CSR1010 and that'll support me. Thanks and best regards.

    ReplyDelete