Closed probonopd closed 6 years ago
Maybe @d00616 could help, given your apparent knowledge about sleep modes on the nRF5x?
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.
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
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.
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.
I also see the functions
We should make an example sketch using them for nRF5.
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
}
}
}
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.
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.
How to add BLE batteryService to Beacon code
// 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); }
@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?
can i get the code for nrf52832 Development kit
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
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.