Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
probonopd opened this issue Feb 17, 2018 · 13 comments

Comments

@probonopd
Copy link

probonopd commented Feb 17, 2018

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 #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
Copy link
Author

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

@probonopd
Copy link
Author

probonopd commented Feb 17, 2018

Also relevant: #203. Unfortunately this was closed because it would need "changes to library".

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

@mristau
Copy link

mristau commented Feb 18, 2018

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
Copy link
Author

probonopd commented Feb 18, 2018

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
Copy link
Author

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
Copy link
Author

probonopd commented Feb 18, 2018

I also see the functions

virtual void requestTemperature() { }
virtual void requestBatteryLevel() { }

We should make an example sketch using them for nRF5.

@probonopd probonopd changed the title Example sketch to send battery voltage every x seconds with nRF5x Example to send requestTemperature() and requestBatteryLevel() every x seconds from nRF5x Feb 18, 2018
@probonopd
Copy link
Author

probonopd commented Feb 18, 2018

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
Copy link

mristau commented Feb 19, 2018

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
Copy link
Author

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
Copy link

Autobot86 commented Aug 29, 2018

How to add BLE batteryService to Beacon code

#include <BLEPeripheral.h>
#include <iBeacon.h>
#include <ble_gap.h>
#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
Copy link

TamojitSaha commented Sep 24, 2018

@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
Copy link

can i get the code for nrf52832 Development kit

@nobodyguy
Copy link

nobodyguy commented Aug 23, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants