sandeepmistry / arduino-BLEPeripheral

An Arduino library for creating custom BLE peripherals with Nordic Semiconductor's nRF8001 or nR51822.
MIT License
462 stars 179 forks source link

Example to send requestTemperature() and requestBatteryLevel() every x seconds from nRF5x #215

Closed probonopd closed 6 years ago

probonopd commented 6 years ago

Please add an example sketch to send battery voltage every x seconds with nRF5x. Possibly a circuit like shown here is needed, although I am not sure since https://github.com/sandeepmistry/arduino-BLEPeripheral/issues/203 talks about an "inbuilt function to measure the battery voltage using the VDD given to the chip".

The sketch should be optimized for low power consumption to run off a coin cell for a long time.

I think this kind of example will be very useful for anyone who wants to make a sensor that sends data in intervals from battery-operated sensor devices.

probonopd commented 6 years ago

Maybe @d00616 could help, given your apparent knowledge about sleep modes on the nRF5x?

probonopd commented 6 years ago

Also relevant: https://github.com/sandeepmistry/arduino-BLEPeripheral/issues/203. Unfortunately this was closed because it would need "changes to library".

So this ticket is here to request those changes if needed.

mristau commented 6 years ago

i found this sketch linked somewhere in the issues here https://github.com/kriswiner/nRF52832DevBoard/blob/master/BMP280_nRF52.ino there is a BLE BatteryService implemented and it will update the battery reading in loop.

He also has a description of the voltage measurement at the comments on top

probonopd commented 6 years ago

Thanks @mristau for linking the example sketch of @kriswiner. He writes

A resistor divider needs to be added to any analog pin so the voltage can be sampled

  • between 0 and the 3V3 of the board (27K/100K will do) such that the ADC will read 0 at 0 V and 4095 at
  • 4.2 V of LiPo battery voltage.

Probably one needs different values for a coin cell.

Since I assume this is of interest to many users, I'd suggest to include a stripped-down version of his sketch (only the battery part) as one of the official examples.

probonopd commented 6 years ago

So what I am essentially looking for is an Arduino version of https://github.com/NordicPlayground/nrf51-ble-app-temp

This project takes a temperature measurement using the internal temperature sensor of the nRF51822, does an ADC reading to get the battery voltage, packs it up as service data in an advertisement packet and sends it, along with an ID string based on the Bluetooth address of the chip. This is the format expected by the nRF Temp apps for iOS and Android, so these will be capable of picking this up and showing the measurement.

probonopd commented 6 years ago

I also see the functions

https://github.com/sandeepmistry/arduino-BLEPeripheral/blob/f591da1a841bf6b0a5c1eac4d1255ba6e71154e8/src/BLEDevice.h#L92-L93

We should make an example sketch using them for nRF5.

probonopd commented 6 years ago

This is what I have at the moment - questions inline:

/* Partly from https://github.com/zebular13/BLE_Thermometer/blob/master/BLE_Thermometer_final/BLE_Thermometer_final.ino
   and from http://mochikun.hatenablog.com/entry/2016/03/28/genuino-101-dht22-%25e3%2581%25a7%25e6%25b8%25a9%25e5%25ba%25a6%25e6%2583%2585%25e5%25a0%25b1%25e3%2582%2592ble%25e3%2581%25a7%25e9%2580%2581%25e3%2582%258b%25ef%25bc%2588%25e3%2581%25aa%25e3%2582%2593
   and from https://github.com/sandeepmistry/arduino-BLEPeripheral/pull/115
   and from https://github.com/kriswiner/nRF52832DevBoard/blob/master/BMP280_nRF52.ino
   Shows 32 degrees for me in the nRF Connect app, and nRF Toolbox app HTM does connect to it but not show the temperature - why?
*/

#include <BLEPeripheral.h>

// Health Thermometer Service
BLEPeripheral blePeripheral;
BLEService tempService("1809");
BLECharacteristic tempChar("2A1C", BLERead | BLENotify, 5); // Temperature Measurement

// Environmental Sensing Service
BLEService env_sensingService = BLEService("181A");
BLEShortCharacteristic tempCharacteristic = BLEShortCharacteristic("2A6E", BLERead | BLENotify); // temperature is int16_t

// Battery Service
BLEService batteryService = BLEService("180F");
BLEUnsignedCharCharacteristic battlevelCharacteristic = BLEUnsignedCharCharacteristic("2A19", BLERead | BLENotify); // battery level is uint8_t

long previousMillis = 0;  // last time the temperature was checked, in ms

void setup() {

  blePeripheral.setLocalName("TempSketch");

  // Health Thermometer Service
  blePeripheral.setAdvertisedServiceUuid(tempService.uuid()); // Note: must only be used once, for the "main" service
  blePeripheral.addAttribute(tempService);
  blePeripheral.addAttribute(tempChar);

  // Battery service
  blePeripheral.addAttribute(batteryService);
  blePeripheral.addAttribute(battlevelCharacteristic);

  // Environmental Sensor service
  blePeripheral.addAttribute(env_sensingService);
  blePeripheral.addAttribute(tempCharacteristic);

  blePeripheral.begin();
}

void loop() {
  BLECentral central = blePeripheral.central();
  if (central.connected()) {
    // If 
    long currentMillis = millis();
    // If a central is connected to this peripheral and some time has passed, 
    // measure and update the temperature. 
    // Does it make a difference if we do this only if there is a significant change?
    if (currentMillis - previousMillis >= 5000) {
      previousMillis = currentMillis;
      int32_t rawTemperature = 0;
      sd_temp_get(&rawTemperature); // Does this work? I get 32 degrees
      float t = rawTemperature / 4.0;
      int it = (int)t;
      // How can we determine the following line from the BLE spec?
      const unsigned char vals[5] = {0, (char)t, 0, 0, 0}; // Incorrect, we are losing the decimals here?
      tempChar.setValue(vals, 5);
      uint8_t battlevel = 50; // Battery level from 0 to 100 - how do I get the real value?
      battlevelCharacteristic.setValue((uint8_t)battlevel); // Set battery level value
      int16_t reading = it * 100;
      tempCharacteristic.setValue((int16_t)(reading)); // Needs temperature as degrees celsius x 100
    }
  }
}
mristau commented 6 years ago

I didn't work with that temperature sensor, we have one embedded into our accelerometer.

There has to be some connection from battery to a nRF pin, at our board it's to A2, then analogRead(PIN) will get a value between 0 and 1023, or 4095 if using 12bit 0 is 0V or LOW, 4095 is 3.3V or HIGH, with your resistor divider you can calculate the voltage of the battery itself like @kriswiner did it

The voltage divider should be chosen by the Voltage of the battery, at LiPo 3.7-4.2V are max values. Those should be reduced to below 3.3V Our board designer chose a 47K / 120K divider for this.

probonopd commented 6 years ago

While this thread is very useful (thanks @mristau) I still would like to see an example sketch to be added so that one can get this kind of thing right from the examples rather than having to search for it on GitHub. Hence I would appreciate if you could give it consideration @sandeepmistry. I think making low power devices that can also send their own battery level and temperature is one of the killer applications for this project.

Autobot86 commented 5 years ago

How to add BLE batteryService to Beacon code

include

include

include

if !defined(NRF51) && !defined(NRF52) && !defined(RFduino)

error "This example only works with nRF51 boards"

endif

// int GI_Bat_Pin = A4; // Board pin p0.30 float temperature, pressure, altitude, VBAT;

BLEPeripheral blePeripheral ; iBeacon beacon; BLECentral central = blePeripheral.central(); uint16_t level = 0; uint8_t lastBattLevelReading = 0;

char* Data = "a196c876-de8c-4c47-1234-d7afd5ae7127"; unsigned short major = 10035; unsigned short minor = 47647; unsigned short measuredPower = -55;

BLEService batteryService = BLEService("180F"); BLEUnsignedCharCharacteristic battlevelCharacteristic = BLEUnsignedCharCharacteristic("2A19", BLERead | BLENotify); // battery level is uint8_t BLEDescriptor battlevelDescriptor = BLEDescriptor("2901", "Battery Level 0 - 100"); void setup() { Serial.begin(115200); pinMode(GI_Bat_Pin, INPUT); analogReadResolution(12); BLE_channel_selection(); beacon.setAdvertisingInterval(2000); beacon.begin(Data, major, minor, level);

if (blePeripheral.setTxPower(4)) Serial.println("Set TX power"); blePeripheral.setAdvertisedServiceUuid(batteryService.uuid()); blePeripheral.addAttribute(batteryService);

}

void loop() { beacon.loop(); level = analogRead(GI_Bat_Pin); // sample ADC to get battery voltage level VBAT = (127.0f / 100.0f) 3.30f ((float)level) / 4095.0f; // convert to the LiPo battery voltage Serial.print("VBAT = "); Serial.println(VBAT, 2); uint8_t battlevel = map(level, 0, 4095, 0, 100); // map battery level from 0 - 100 % battlevelCharacteristic.setValue((uint8_t)battlevel); // set the battery level characteristic value

delay(1000); }

TamojitSaha commented 5 years ago

@probonopd

This is what I have at the moment - questions inline:

/* Partly from https://github.com/zebular13/BLE_Thermometer/blob/master/BLE_Thermometer_final/BLE_Thermometer_final.ino
   and from http://mochikun.hatenablog.com/entry/2016/03/28/genuino-101-dht22-%25e3%2581%25a7%25e6%25b8%25a9%25e5%25ba%25a6%25e6%2583%2585%25e5%25a0%25b1%25e3%2582%2592ble%25e3%2581%25a7%25e9%2580%2581%25e3%2582%258b%25ef%25bc%2588%25e3%2581%25aa%25e3%2582%2593
   and from https://github.com/sandeepmistry/arduino-BLEPeripheral/pull/115
   and from https://github.com/kriswiner/nRF52832DevBoard/blob/master/BMP280_nRF52.ino
   Shows 32 degrees for me in the nRF Connect app, and nRF Toolbox app HTM does connect to it but not show the temperature - why?
*/

#include <BLEPeripheral.h>

// Health Thermometer Service
BLEPeripheral blePeripheral;
BLEService tempService("1809");
BLECharacteristic tempChar("2A1C", BLERead | BLENotify, 5); // Temperature Measurement

// Environmental Sensing Service
BLEService env_sensingService = BLEService("181A");
BLEShortCharacteristic tempCharacteristic = BLEShortCharacteristic("2A6E", BLERead | BLENotify); // temperature is int16_t

// Battery Service
BLEService batteryService = BLEService("180F");
BLEUnsignedCharCharacteristic battlevelCharacteristic = BLEUnsignedCharCharacteristic("2A19", BLERead | BLENotify); // battery level is uint8_t

long previousMillis = 0;  // last time the temperature was checked, in ms

void setup() {

  blePeripheral.setLocalName("TempSketch");

  // Health Thermometer Service
  blePeripheral.setAdvertisedServiceUuid(tempService.uuid()); // Note: must only be used once, for the "main" service
  blePeripheral.addAttribute(tempService);
  blePeripheral.addAttribute(tempChar);

  // Battery service
  blePeripheral.addAttribute(batteryService);
  blePeripheral.addAttribute(battlevelCharacteristic);

  // Environmental Sensor service
  blePeripheral.addAttribute(env_sensingService);
  blePeripheral.addAttribute(tempCharacteristic);

  blePeripheral.begin();
}

void loop() {
  BLECentral central = blePeripheral.central();
  if (central.connected()) {
    // If 
    long currentMillis = millis();
    // If a central is connected to this peripheral and some time has passed, 
    // measure and update the temperature. 
    // Does it make a difference if we do this only if there is a significant change?
    if (currentMillis - previousMillis >= 5000) {
      previousMillis = currentMillis;
      int32_t rawTemperature = 0;
      sd_temp_get(&rawTemperature); // Does this work? I get 32 degrees
      float t = rawTemperature / 4.0;
      int it = (int)t;
      // How can we determine the following line from the BLE spec?
      const unsigned char vals[5] = {0, (char)t, 0, 0, 0}; // Incorrect, we are losing the decimals here?
      tempChar.setValue(vals, 5);
      uint8_t battlevel = 50; // Battery level from 0 to 100 - how do I get the real value?
      battlevelCharacteristic.setValue((uint8_t)battlevel); // Set battery level value
      int16_t reading = it * 100;
      tempCharacteristic.setValue((int16_t)(reading)); // Needs temperature as degrees celsius x 100
    }
  }
}

Any idea about how to send string data from app(nrfToolBox) to ble module(nrf51822) and vice-versa?

Praneeth2498 commented 5 years ago

can i get the code for nrf52832 Development kit

nobodyguy commented 3 years ago

Code for reading cr2032 battery level (via VDD pin):

// https://github.com/andenore/NordicSnippets/blob/master/examples/saadc/main.c
float getBatteryVoltage()
{
  volatile uint16_t result = 9999; 
  volatile float voltage = 0;

  NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_14bit << SAADC_RESOLUTION_VAL_Pos;

  for (int i = 0; i < 8; i++) {
    NRF_SAADC->CH[i].PSELN = SAADC_CH_PSELP_PSELP_NC;
    NRF_SAADC->CH[i].PSELP = SAADC_CH_PSELP_PSELP_NC;
  }
  // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain.
  NRF_SAADC->CH[0].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos) |
                            (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos) |
                            (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                            (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos) |
                            (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                            (SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos);
  NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_VDD;
  NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELP_PSELP_NC;

  NRF_SAADC->RESULT.PTR = (uint32_t)&result;
  NRF_SAADC->RESULT.MAXCNT = 1; // One sample

  // No automatic sampling, will trigger with TASKS_SAMPLE.
  NRF_SAADC->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task << SAADC_SAMPLERATE_MODE_Pos;

  // Enable SAADC (would capture analog pins if they were used in CH[0].PSELP)
  NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;

  // Calibrate the SAADC (only needs to be done once in a while)
  NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
  while (NRF_SAADC->EVENTS_CALIBRATEDONE == 0);
  NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
  while (NRF_SAADC->STATUS == (SAADC_STATUS_STATUS_Busy <<SAADC_STATUS_STATUS_Pos));

  NRF_SAADC->TASKS_START = 1;

  while (!NRF_SAADC->EVENTS_STARTED);
  NRF_SAADC->EVENTS_STARTED = 0;

  NRF_SAADC->TASKS_SAMPLE = 1;

  while (!NRF_SAADC->EVENTS_END);
  NRF_SAADC->EVENTS_END = 0;

  // Stop the SAADC, since it's not used anymore.
  NRF_SAADC->TASKS_STOP = 1;

  while (NRF_SAADC->EVENTS_STOPPED == 0);
  NRF_SAADC->EVENTS_STOPPED = 0;

  NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);

  // Convert the result to voltage
  // Result = [V(p) - V(n)] * GAIN/REFERENCE * 2^(RESOLUTION)
  // Result = (VDD - 0) * ((1/6) / 0.6) * 2^14
  // VDD = Result / 4551.1
  voltage = (float)result / 4551.1f;
  return voltage;
}

You can use it like this for your battery characteristic:

float battVoltage = getBatteryVoltage();
uint8_t battLevel = map(battVoltage, 0, 3.0, 0, 100); // map battery level from 0-3V to 0-100%
battlevelCharacteristic.setValue(battLevel); // set the battery level characteristic value