This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF51 BLE mbed ADC

Hi,

I am trying to make a program for the NRF51 development kit on mbed. I have been able to take ADC samples at 1000Hz which is what I want, but I can't seem to print the actual voltage over BLE UART on my phone. I am new to this type of thing altogether and don't know how to really print/write anything over BLE. The code below prints out a string every time ADC is called, but I don't really understand how to print out the actual ADC value over BLE which is a float.

Thanks

#include "mbed.h" 
#include "BLE.h"
#include "UARTService.h"

#define NEED_CONSOLE_OUTPUT 1 /* Set this if you need debug messages on the console;
                               * it will have an impact on code-size and power consumption. */

#if NEED_CONSOLE_OUTPUT
#define DEBUG(STR) { if (uart) uart->write(STR, strlen(STR)); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

BLE ble;

Ticker t;
static float value; // stored voltage reading from ADC

Ticker ledticker;  //ticker to turn on and off LED to ensure system is working properly

float Vref = 1.2; //float for reference voltage

UARTService *uart;

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    DEBUG("Disconnected!\n\r");
    DEBUG("Restarting the advertising process\n\r");
    ble.startAdvertising();
}

/* Functio for initalizing ADC Configuration */
void my_analogin_init(void)
{
    /* Function for Enabling ADC Initialization to 10bits, 1.2V Internal reference, OneThird Prescaling, at analog pin1
    *   with no external reference voltage */
    
    NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
    NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |                                //Configure Resolution to 10 bits
                      (ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |  //Configure ADC input selection and prescalar settings
                                                                                                    //Internal reference voltage of 1.2V. Onethird prescaling -> 3.6V 
                      (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |                            //ADC reference selection to internal 1.2V VBG 
                      (ADC_CONFIG_PSEL_AnalogInput1 << ADC_CONFIG_PSEL_Pos) |                       //Select Pin to be used as ADC input pin -> Disable analog pins
                      (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);                      //Select external reference pin -> No external reference (we use internal reference of 1.2V)
}
/* Callback function for taking ADC Sample */
void my_analogin_read_u16(void)
{ 
                      
      NRF_ADC->CONFIG   &= ~ADC_CONFIG_PSEL_Msk;                                                    // Check if Analog input pin has been selected
      NRF_ADC->CONFIG   |= ADC_CONFIG_PSEL_AnalogInput1 << ADC_CONFIG_PSEL_Pos;                      //Check if Analog Input pin has been selected
      NRF_ADC->TASKS_START = 1;
      while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {}   //Produce ADC value only when ADC is not busy
      value = (uint16_t) NRF_ADC -> RESULT;
      value = (value * Vref ) /1024; //returns voltage from range of (0 to 3.6V)
      DEBUG("Value = \n\r");
}

int main(void)
{
    ble.init();
    ble.onDisconnection(disconnectionCallback);
    uart = new UARTService(ble);
    /* setup advertising */
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
                                     (const uint8_t *)"BLE UART", sizeof("BLE UART") - 1);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
                                     (const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed));
    ble.setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
    ble.startAdvertising();

    my_analogin_init(); //Initialize ADC settings
    
    t.attach_us(&my_analogin_read_u16,1000); //repeatedly read voltage every 1000us or every millisecond.
    /* Infinite Loop*/
    while(true)
    {
        ble.waitForEvent();       
    }
}
  • Hi

    You cant transmit float over BLE, only bytes are accepted, i.e. uint8_t. So if you have a float, you need to code that into a byte and then decode that on the BLE receiving side. You can see in the ble_nus.c file, ble_nus_string_send function how data is sent over BLE with calling sd_ble_gatts_hvx function. I.e. this ADC example uses the NUS (Nordic UART Service) service.

    There is also possibility to calculate battery remaining in procentage, before sending it over BLE, like done here

  • I suggest you try the new mBed Simple BLE library: developer.mbed.org/.../

    Unfortunately BLE doesn't yet expose a TCP-like connection where you can just send data (it's coming though). You have to use GATT which imposes a structure on your communications. Data is stored in GATT Characteristics, and you 'send' it by having the client (phone app) enable notifications on your characteristic, and then writing to the characteristic on the device.

    Characteristics are grouped into Services. The easiest way to get a feel for this is to download the Master Control Panel app, connect to some BLE device and play around.

    You can send floats perfectly fine. Although you may as well send as uint16 since that's what is actually read, and it is shorted. Something like this:

    #include "mbed.h"
    #include "SimpleBLE.h"
     
    AnalogIn voltage(A0);
     
    SimpleBLE ble("VoltageSensor");
     
    // Create a new characteristic under service 0x8000, char 0x8001
    // This characteristic will support READ and NOTIFY.
    SimpleChar<float> voltageValue = ble.readOnly_float(0x8000, 0x8001);
     
    void read() {
        voltageValue = voltage.read();
    }
     
    int main(int, char**) {
        Ticker t;
        t.attach(&read, 1.0f); // read new value every second
     
        ble.start();
        while (1) { ble.waitForEvent(); }
    }
    

    By the way you will get no-where near 1000 Hz unless you pack multiple reads into each notification. BLE data rates are typically around 1 kB/s, up to a theoretical max of around 6 kB/s (I forget the exact number). It depends on the phone, and how you set the connection parameters.

  • Ok, this code is really helpful. When you say BLE does not support TCP-like connection. Does that apply to all bluetooth modules, or just mbed? Thanks.

  • All bluetooth modules. There is a TCP-like layer call L2CAP but you can never get access to it. You have to use GATT which runs on top of L2CAP. BLE 4.2 did introduce "Connection Oriented Channels" which give you access to L2CAP (or something like that), but it's not available in iOS or Android. Give it 5 years or so, maybe. See www.nordicsemi.com/.../Bluetooth-technology-opens-up-to-IP-traffic

Related