XIAO BLE Sense battery level and charging status

I just received my XIAO BLE Sense board and was disappointed to see this thread indicating problems reading the battery voltage.

I note that the return value trying analogRead from P0_31 is 0xFFFFFFFF, which is the traditional return value when trying to do an analogRead from a pin not connected to an ADC input.

So, poking around in the pins_arduino.h file in the variants directory for this board, I found the following:

#define PIN_VBAT (32u)

I decided to try PIN_VBAT in my analogRead, and, guess what?
Taa-daa!

Here’s my test sketch:

/*
 * Test to read a LiPo battery connected to VBAT on the
 * underside of XIAO BLE boards.
 * 
 * Note that the ADC input from the VBAT voltage divider is
 * NOT pin P0_31 as shown on the schematic!
 * 
 * March, 2022
 * davekw7x
 */
// For mbed compilers, the following allows us to use the
// standard printf() function.
REDIRECT_STDOUT_TO(Serial)

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  while (!Serial)
    ;
  printf("\nBattery Test compiled by davekw7x on %s at %s\n",   __DATE__, __TIME__);
  printf("Note: PIN_VBAT = %u\n\n", PIN_VBAT);
  
  pinMode(P0_14, OUTPUT);
  digitalWrite(P0_14, LOW);
}  // End of setup()

const double vRef = 3.3; // Assumes 3.3V regulator output is ADC reference voltage
const unsigned int numReadings = 1024; // 10-bit ADC readings 0-1023, so the factor is 1024

void loop()
{
  unsigned int adcCount = analogRead(PIN_VBAT);
  double adcVoltage = (adcCount * vRef) / numReadings;
  double vBat = adcVoltage*1510.0/510.0; // Voltage divider from Vbat to ADC
  
  printf("adcCount = %3u = 0x%03X, adcVoltage = %.3fV, vBat = %.3f\n",
             adcCount, adcCount, adcVoltage, vBat);
             
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  delay(1000);
} // End of loop()

And here’s the output, consistent with my very freshly charged LiPo battery:

BatteryTest

Regards,

Dave

5 Likes

Thanks Dave ! Unfortunately the code is not working for me. I am using BLE and IMU and the moment I am reading from PIN_VBAT, peripheral gets disconnected. Not sure if I am missing any bit.

I posted the exact sketch I used to get the indicated output

Here is the setup information for my Sketch on Windows 10 using Arduino 1.8.16
I tacked small wires (30 gauge stranded) to the BAT and GND pads on the underside of the XIAO BLE Sense board and connected to a battery holder for my LiPo.

Of course I ran the sketch with no battery installed first, and saw “drifting” values for the ADC reading—all of them within the 10-bit range of the ADC. Then I installed the battery and got the measurements I showed.

Regards,

Dave

1 Like

Thanks Dave
I got XIAO BLE.
I’ll try your codes.

Match

Hi Dave! I tested your code and I was able to get the reading. But problem is when using the same code with BLE & IMU together. In my code, I am sending IMU data over bluetooth to a mobile app and reading PIN_VBAT disconnects the device from mobile app. An example code would be like this


/**
This prorgam is written to collect IMU data from XIAO BLE Sense and send to EI Blue mobile app. 
EI Blue mobile app uploads the data to Edge Impulse Studio.
Visit https://wiki.seeedstudio.com/XIAO-BLE-Sense-Bluetooth-Usage/ to setup Arduino IDE 
Get EI Blue mobile app from https://github.com/just4give/ei-blue 
*/
#include <ArduinoBLE.h>
#include <LSM6DS3.h>
#include <Wire.h>

#define BLENAME                       "EIBLUE"
#define SERVICE_UUID                  "4D7D1101-EE27-40B2-836C-17505C1044D7"
#define TX_PRED_CHAR_UUID             "4D7D1106-EE27-40B2-836C-17505C1044D7"
#define TX_BAT_CHAR_UUID              "4D7D1107-EE27-40B2-836C-17505C1044D7"
#define RX_CHAR_UUID                  "4D7D1108-EE27-40B2-836C-17505C1044D7"
#define SAMPLING_RATE                 50   //Hz
#define DURATION                      1 //seconds


BLEService bleService(SERVICE_UUID); // Bluetooth® Low Energy LED Service

// Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEStringCharacteristic rxCharacteristic(RX_CHAR_UUID, BLEWrite, 1024);
BLEStringCharacteristic txPredCharacteristic(TX_PRED_CHAR_UUID, BLERead | BLENotify, 1024);
BLEStringCharacteristic txBatCharacteristic(TX_BAT_CHAR_UUID, BLERead | BLENotify, 1024);

LSM6DS3 myIMU(I2C_MODE, 0x6A);    //I2C device address 0x6A
float aX, aY, aZ, gX, gY, gZ;
const float accelerationThreshold = 2.5; // threshold of significant in G's


const double vRef = 3.3; // Assumes 3.3V regulator output is ADC reference voltage
const unsigned int numReadings = 1024; 

void setup() {
  Serial.begin(115200);
  
  pinMode(P0_14, OUTPUT);
  digitalWrite(P0_14,LOW);
  
  // set LED pin to output mode
  pinMode(LEDB, OUTPUT);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDB, HIGH);
  digitalWrite(LEDG, LOW);

  
   
  // begin initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");

    while (1);
  }

  // set advertised local name and service UUID:
  BLE.setLocalName(BLENAME);
  BLE.setDeviceName(BLENAME);
  BLE.setAdvertisedService(bleService);

  // add the characteristic to the service
  bleService.addCharacteristic(txBatCharacteristic);
  bleService.addCharacteristic(txPredCharacteristic);
  bleService.addCharacteristic(rxCharacteristic);

  // add service
  BLE.addService(bleService);

  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);

  
  // start advertising
  BLE.advertise();

  Serial.println("BLE Peripheral");

  if (myIMU.begin() != 0) {
    Serial.println("Device error");
  } else {
    Serial.println("aX,aY,aZ,gX,gY,gZ");
  }

}

void loop() {
  
  BLEDevice central = BLE.central();

  if (central) {
  Serial.print("Connected to central: ");
  unsigned int adcCount = analogRead(PIN_VBAT);
  double adcVoltage = (adcCount * vRef) / numReadings;
  double vBat = adcVoltage*1510.0/510.0; // Voltage divider from Vbat to ADC
  
  printf("adcCount = %3u = 0x%03X, adcVoltage = %.3fV, vBat = %.3f\n",
             adcCount, adcCount, adcVoltage, vBat);


    String data="";
    data =  String(myIMU.readFloatAccelX(), 3)+","+String(myIMU.readFloatAccelY(), 3)+","+String(myIMU.readFloatAccelZ(), 3);

    
    txBatCharacteristic.writeValue(String(vBat));
    txPredCharacteristic.writeValue(data.c_str());
  
    delay(1000);
  }
}

void blePeripheralConnectHandler(BLEDevice central) {
  // central connected event handler
  Serial.print("Connected event, central: ");
  Serial.println(central.address());
  digitalWrite(LEDB, LOW);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDR, HIGH);

}

void blePeripheralDisconnectHandler(BLEDevice central) {
  // central disconnected event handler
  Serial.print("Disconnected event, central: ");
  Serial.println(central.address());
  digitalWrite(LEDB, HIGH);
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDR, HIGH);
}



I haven’t tried to access the LSM6DS3, but here’s the deal:

There are discrepancies between schematic and software that I haven’t figured out yet.

I’m leaving this project for the time being (got other fish to fry). Maybe, just maybe someone can get help from the folks at Seeed. Like, perhaps a real schematic and software examples that really work.

Here are some issues (Pardon my verbosity, but I just can’t seem to boil it down)

Issue 1:
The schematic shows pin 31 (P0.31) as the input to the ADC to read battery voltage through its voltage divider. We have all discovered that this does not work.

On the other hand, as I discovered, the arduino_pins.h header in the variants directory for this board defined PIN_VBAT to be 32.

That’s P1.0, but the schematic labels P1.0 as P1.00_PDM_CLK. I haven’t tried any PDM functions.

Bottom line: P1.0 works to read the ADC

Score so far: Software wins. (At least as far as reading the Battery Voltage.)

Issue 2:
Everyone on this thread, (including me) has used pin 14 (P0.14) as the active-low signal to ground one end of the voltage divider so that the battery voltage is scaled properly. This is shown on the schematic as P0_14_READ_BAT (with a bar over the ‘READ_BAT’ to indicate active-low).

However, in the arduino_pins.h, there is the following:

#define PIN_LSM6DS3TR_C_POWER (14u)

The Seeed_Arduino_LSM6DS3 library that you are apparently using causes PIN_LSM6DS3TR_C_POWER to go high once (during the begin() method) to access the LSM6DS3. I don’t know if that is the actual connection. In this case, the schematic shows the power pins and Chip Select pin for the LSM6DS3 are connected to a signal named 6D_PWR, which goes to P1.08 on the CPU, which is numerically pin 40.

Bottom line: If you can get the LSM6DS3 to work by making pin 14 High and you can read the battery by making pin 14 go low, then you have to set pin 14 high (every time) after reading the battery before accessing the LSM6D3. This might pinpoint the problem, but doesn’t sound practical in an application.

The problem is, of course, those connections are hidden below the shield, and I’m not going to pry things apart to see what really goes where, and I don’t have time for more software exploration.

Sorry, but I’m done for now—maybe done with Seeed forever. Got other (non-Seeed) things to do that actually pay the bills.

Regards,

Dave

6 Likes

I found a symbol about VBATT in valiant…cpp file.

// VBAT
{ P0_14, NULL, NULL, NULL }, // D31/VBAT_ENABLE
{ P0_31, NULL, NULL, NULL }, // D32/VBAT_READ

So, We can use VBAT_ENABLE with this statement.

#define PIN_VBATENABLE (31u) // this is not defined in pins_arduino.h file.

pinMode(PIN_VBATENABLE, OUTPUT);
digitalWrite(PIN_VBATENABLE, LOW);

I really appreciate your insight on this topic. I have some areas to explore as you indicated. Will post here if I am successful. Thanks again :slight_smile:

Dave, you’re a godsend. Have a great week, good luck with your other stuff.

Well, I’m baaaaaaack

Update to mithundotdas (and anyone else out there who is still interested):

I loaded your test program, and a little experimentation indicated that the problem manifests itself with the analogRead() of the ADC value, not the digitalWrite() of the enable signal.

So…
I wired an external voltage divider that used the XIAO BLE analog pin 3 as the ADC input and digital pin 4 as the enable signal.

Result was: Taa-Daa!

Details:

1.
I connected a 1 Megohm resistor from the + terminal of the battery to Pin A3.

2.
I connected a 560K Ohm from Pin A3 to Pin D4. (The schematic shows 510K here, but my spare parts box didn’t have a 510K, so I used something not too different that I did have.)

3.
I made some definitions in the sketch and changed the formula to take into account the changes.

I started the sketch and tested the reported values on the Serial Monitor and on my phone. (Green LED on the XIAO BLE came on, then turned Blue upon successful connection to Central, ADC voltage values were printed out every second, and the phone app could obtain the battery voltage, as expected.)

I used a generic app, LightBlue, instead of the specific app for this program. It’s somewhat awkward, but for debugging, I don’t have to worry about understanding (and debugging) some specific program other than the one I’m working on with the XIAO BLE.

Bottom line:
The existing board is useless for the application you have in mind without a little (very little, actually) external circuit . (Pardon me if you have heard this before, but don’t those Seeed guys actually test this stuff before releasing hardware and/or software and/or the integration?)

There are other discrepancies on the schematic involving various connections, and it just may happen that my selection of pins for the external voltage divider screw the pooch for other applications, but maybe it gets you past top dead center for further testing.

Final note:
Wiring the new connections on my solderless breadboard was quick and easy, but note that it tends to be pretty noisy unless you dress the leads carefully. Adding a small capacitor from the ADC input pin to ground might help a little. A simple running average in the program will make it settle down a lot. (Or so I claim.)

Here’s my modified sketch:


/**
This prorgam is written to collect IMU data from XIAO BLE Sense and send to EI Blue mobile app. 
EI Blue mobile app uploads the data to Edge Impulse Studio.
Visit https://wiki.seeedstudio.com/XIAO-BLE-Sense-Bluetooth-Usage/ to setup Arduino IDE 
Get EI Blue mobile app from https://github.com/just4give/ei-blue 

Some mods by davekw7x
March, 18, 2022
*/

REDIRECT_STDOUT_TO(Serial);

#include <ArduinoBLE.h>
#include <LSM6DS3.h>
#include <Wire.h>

#define BLENAME                       "EIBLUE"
#define SERVICE_UUID                  "4D7D1101-EE27-40B2-836C-17505C1044D7"
#define TX_PRED_CHAR_UUID             "4D7D1106-EE27-40B2-836C-17505C1044D7"
#define TX_BAT_CHAR_UUID              "4D7D1107-EE27-40B2-836C-17505C1044D7"
#define RX_CHAR_UUID                  "4D7D1108-EE27-40B2-836C-17505C1044D7"
#define SAMPLING_RATE                 50   //Hz
#define DURATION                      1 //seconds


BLEService bleService(SERVICE_UUID); // Bluetooth® Low Energy LED Service

// Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEStringCharacteristic rxCharacteristic(RX_CHAR_UUID, BLEWrite, 1024);
BLEStringCharacteristic txPredCharacteristic(TX_PRED_CHAR_UUID, BLERead | BLENotify, 1024);
BLEStringCharacteristic txBatCharacteristic(TX_BAT_CHAR_UUID, BLERead | BLENotify, 1024);

LSM6DS3 myIMU(I2C_MODE, 0x6A);    //I2C device address 0x6A
float aX, aY, aZ, gX, gY, gZ;
const float accelerationThreshold = 2.5; // threshold of significant in G's


const double vRef = 3.3; // Assumes 3.3V regulator output is ADC reference voltage
const unsigned int numReadings = 1024; 
#define EXTERNAL_VBAT PIN_A3
#define EXTERNAL_VBAT_READ_ENABLE  D4
const double rhik = 1000.0; // 1000K Ohms = 1 Meg to Vref
const double rlok =  560.0;  // 560K Ohms to Gnd
const double dividerFactor = (rhik + rlok)/rlok; // Multiply ADC voltage by this to get Vbat

void setup() {
  Serial.begin(115200);
  while (!Serial);
  
  Serial.println("BLE Test compiled on " __DATE__ " at " __TIME__);
  Serial.println("BLE Test compiled on " __DATE__ " at " __TIME__);
  Serial.print("EXTERNAL_VBAT_READ_ENABLE = ");Serial.println(EXTERNAL_VBAT_READ_ENABLE);
  Serial.print("EXTERNAL_VBAT = "); Serial.println(EXTERNAL_VBAT);
  pinMode(EXTERNAL_VBAT_READ_ENABLE, OUTPUT);
  digitalWrite(EXTERNAL_VBAT_READ_ENABLE,LOW);
  
  // set LED pin to output mode
  pinMode(LEDB, OUTPUT);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDB, HIGH);
  digitalWrite(LEDG, LOW);

  
   
  // begin initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");

    while (1);
  }

  // set advertised local name and service UUID:
  BLE.setLocalName(BLENAME);
  BLE.setDeviceName(BLENAME);
  BLE.setAdvertisedService(bleService);

  // add the characteristic to the service
  bleService.addCharacteristic(txBatCharacteristic);
  bleService.addCharacteristic(txPredCharacteristic);
  bleService.addCharacteristic(rxCharacteristic);

  // add service
  BLE.addService(bleService);

  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);

  
  // start advertising
  BLE.advertise();

  Serial.println("BLE Peripheral");

  if (myIMU.begin() != 0) {
    Serial.println("Device error");
  } else {
    Serial.println("aX,aY,aZ,gX,gY,gZ");
  }

}


void loop() {
  
  BLEDevice central = BLE.central();

  if (central) {
  Serial.print("Connected to central: ");
  //unsigned int adcCount = 400;
  unsigned int adcCount = analogRead(EXTERNAL_VBAT);
  double adcVoltage = (adcCount * vRef) / numReadings;
  double vBat = adcVoltage*dividerFactor; // Voltage divider from Vbat to ADC
  printf("adcCount = %3u = 0x%03X, adcVoltage = %.3fV, vBat = %.3f\n",
             adcCount, adcCount, adcVoltage, vBat);


    String data="";
    data =  String(myIMU.readFloatAccelX(), 3)+","+String(myIMU.readFloatAccelY(), 3)+","+String(myIMU.readFloatAccelZ(), 3);

    
    txBatCharacteristic.writeValue(String(vBat));
    txPredCharacteristic.writeValue(data.c_str());
  
    delay(1000);
  }
}

void blePeripheralConnectHandler(BLEDevice central) {
  // central connected event handler
  Serial.print("Connected event, central: ");
  Serial.println(central.address());
  digitalWrite(LEDB, LOW);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDR, HIGH);

}

void blePeripheralDisconnectHandler(BLEDevice central) {
  // central disconnected event handler
  Serial.print("Disconnected event, central: ");
  Serial.println(central.address());
  digitalWrite(LEDB, HIGH);
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDR, HIGH);
}

Regards,

Dave

7 Likes

Thanks Dave for checking it out. I assumed external circuit with voltage divider would work but did not want to have external circuit due to small form factor of my POC and also as XIAO already has a charging chip in it, this feature should have been out of the box :slight_smile: Hope Seeed will fix this or document it so that someone can read battery without external circuit.

1 Like

Hi mithundotdas,

After some digging, I found a way to read PIN_VBAT value and keep BLE & IMU working at the same time.

Issue:

As noted by Dave, bluetooth drops connection when calling analogRead. It turns out that the current analogRead implementation blocks the thread until reading is completed. Here is the calling stack:

analogRead()
  -> mbed::AnalogIn::read()
    -> mbed::AnalogIn::read_u16()
      -> mbed::analog_read_u16()
        -> nrfx_saadc_sample_convert() // blocking

Fortunately Nordic provides another non-blocking api to convert adc value. Details here SAADC Drivers.

So the solution becomes clear, replace nrfx_saadc_sample_convert() with nrfx_saadc_buffer_convert() in analogRead.

But we can’t just modify the source code, because the implementation of mbed::AnalogIn is in the static library libmbed.a.

So we need to implement a non-blocking version of analogRead.

Quick hacky solution:

#include <nrf52840.h>
#include <nrfx_saadc.h>
#include <AnalogIn.h>
#include <pinDefinitions.h>

class HackAnalogIn: public mbed::AnalogIn {
  using mbed::AnalogIn::AnalogIn;
  public:
    analogin_t getAnalogIn_t();
};

analogin_t HackAnalogIn::getAnalogIn_t() {
  return this->_adc;
}

void startReadingBatteryLevel (nrf_saadc_value_t* buffer) {
  auto pin = PIN_VBAT;
  PinName name = analogPinToPinName(pin);
  if (name == NC) { return; }
  HackAnalogIn* adc = static_cast<HackAnalogIn*>(analogPinToAdcObj(pin));
  if (adc == NULL) {
    adc = new HackAnalogIn(name);
    analogPinToAdcObj(pin) = static_cast<mbed::AnalogIn*>(adc);
#ifdef ANALOG_CONFIG
    if (isAdcConfigChanged) {
      adc->configure(adcCurrentConfig);
    }
#endif
  }

  nrfx_saadc_buffer_convert(buffer, 1);
  nrfx_err_t ret = nrfx_saadc_sample();
  if (ret == NRFX_ERROR_BUSY) {
    // failed to start sampling
    return;
  }
}

void setup() {
  pinMode(P0_14, OUTPUT);
  digitalWrite(P0_14,LOW);
  // init BLE, IMU and other peripherals here
  ...
}

nrf_saadc_value_t BatteryLevel = { 0 };

void loop() {
  // BLE.poll()
  static unsigned long _lastT = 0;
  unsigned long _t = millis();

  if (_t - _lastT > 1000) {
    // read battery level every 1 second
    startReadingBatteryLevel(&BatteryLevel);
    _lastT = _t;
  }
  
  // check if ADC conversion has completed
  if (nrf_saadc_event_check(NRF_SAADC_EVENT_DONE)) {
    // ADC conversion completed. Reading is stored in BatteryLevel
    nrf_saadc_event_clear(NRF_SAADC_EVENT_DONE);
    float vBat = (float)BatteryLevel / 4096 * 3.3 / 510 * (1000 + 510);
    // write value to characteristic or things you want to do
    ...
  }
  ...
}

here are some test result:

vBat          EXTERNAL_VOLTMETER
4.03          4.13
4.01          4.14
3.96          4.06
3.93          4.04

Although the value is not absolute precise, but a strong linear correlation can be seen. I was able to obtain a pretty good reading by adjusting the conversion function to (float)BatteryLevel / 1340 * 3.3.

I’ll post a proper designed battery reading library later.

Hope it helps.

6 Likes

Nice solution POPOBE97!

Everything works fine with your code when I use it without any modifications.

I have one issue though. When I set the pin mode for the RGB leds to OUTPUT then nrf_saadc_event_check(NRF_SAADC_EVENT_DONE) always returns zero/false. I have no idea why this could cause an issue though. Do you have any ideas?

pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);

I will continue to test some more

Edit:

It seems that only LEDG will cause the issue if you set that to pinMode OUTPUT.

Here is the complete sketch/code:

#include <nrf52840.h>
#include <nrfx_saadc.h>
#include <AnalogIn.h>
#include <pinDefinitions.h>

// Reading Battery Level
// https://forum.seeedstudio.com/t/xiao-ble-sense-battery-level-and-charging-status/263248/24

class HackAnalogIn: public mbed::AnalogIn 
{
  using mbed::AnalogIn::AnalogIn;
  public:
    analogin_t getAnalogIn_t();
};

analogin_t HackAnalogIn::getAnalogIn_t() 
{
  return this->_adc;
}

void startReadingBatteryLevel(nrf_saadc_value_t* buffer) 
{
  auto pin = PIN_VBAT;
  PinName name = analogPinToPinName(pin);
  if (name == NC)
  {
    return;
  }
  HackAnalogIn* adc = static_cast<HackAnalogIn*>(analogPinToAdcObj(pin));
  if (adc == NULL)
  {
    adc = new HackAnalogIn(name);
    analogPinToAdcObj(pin) = static_cast<mbed::AnalogIn*>(adc);
#ifdef ANALOG_CONFIG
    if (isAdcConfigChanged)
    {
      adc->configure(adcCurrentConfig);
    }
#endif
  }

  nrfx_saadc_buffer_convert(buffer, 1);
  nrfx_err_t ret = nrfx_saadc_sample();
  if (ret == NRFX_ERROR_BUSY)
  {
    // failed to start sampling
    return;
  }
}

nrf_saadc_value_t BatteryLevel = { 0 };

void setup()
{
  Serial.begin(115200);
  //while (!Serial);

  Serial.println("Battery Level Example!");  

  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT); // Setting LEDG to pinMode OUTPUT casues the monitoring of Battery Level to stop working
  pinMode(LEDB, OUTPUT);
  
  // Battery Level setup
  pinMode(P0_14, OUTPUT);
  digitalWrite(P0_14,LOW);
}

void loop()
{
  // Monitor the Battery Level
  static unsigned long _lastT = 0;
  unsigned long _t = millis();

  if (_t - _lastT > 1000)
  {
    // read battery level every 1 second
    startReadingBatteryLevel(&BatteryLevel);
    _lastT = _t;
    Serial.print("startReadingBatteryLevel at time ");
    Serial.println(_t);
  }

  // check if ADC conversion has completed
  if (nrf_saadc_event_check(NRF_SAADC_EVENT_DONE))
  {
    // ADC conversion completed. Reading is stored in BatteryLevel
    nrf_saadc_event_clear(NRF_SAADC_EVENT_DONE);
    float vBat = (float)BatteryLevel / 4096 * 3.3 / 510 * (1000 + 510);
    // write value to characteristic or things you want to do
    
    Serial.print("BatteryLevel: ");
    Serial.println(vBat);
  }
}
1 Like

Hi MonsterSmurf,

Unfortunately I have no idea why the code is causing this issue. And I’m moving on to the next project so possibly never going to inspect deeper.

I can provide with you some possible guesses for further inspection, although I strongly suggest you not to because the lack of documentation. (TBH by what we have discussed so far, this hardware really is nothing but a black hole)

  1. What I have placed in the code is a hack way of catching ADC event. This means it will effect all ADC related functionalities. Although LED does not depend on ADC, it does require DAC to function correctly. ADC and DAC may share some resources and therefore causes the problem. Possible solution to this is adding lock before you read/write on DAC related registers.
  2. As you described, only LEDG have this issue. Again, according to what we’ve discussed, the circuit diagram they provide is not very credible. So who know where LEDG is attached to. Possible solution to this is use other LEDs. You can combine LEDR with LEDB to generate countless types of color anyways.

Wish you luck with your project

1 Like

Hi, I’m sorry I can’t help you. I’ve been working on a similar project recently and have encountered similar problems. When I find a solution to the problem, I will share it with you.

2 Likes

I am also able to get battery reading without LEDG. So I think @POPOBE97 's code is the solution. I am making that a solution. Thanks a lot @POPOBE97

2 Likes

I do not have the hardware yet, but reading the various fixes. Looks like this solution will only work with the Mbed based 2.6.1 version of the board (ArduinoBLE) not the 1.0 version based on Adafruit’s libraries. What would be a quick fix for the 1.0 version?

@POPOBE97 thanks for sharing your code! Could anyone explain where the various numbers are coming from in float vBat = (float)BatteryLevel / 4096 * 3.3 / 510 * (1000 + 510); - specifically the 4096, 3.3, 510 and 1510.

I’m using a 3.7v 300mAh battery connected to the pads and it seems to be working well and I’m getting readings using the code from @POPOBE97 but I’m looking to convert that into a rough percentage (or even just a high/medium/low)

4096 : this is the range limit of the 12-bit ADC (so 0=0% and 4095=100%) . (Although I think the code should use 4095 not 4096? :thinking: )

3.3 : this is supposed to correspond to the reference voltage being used by the ADC here, i.e. 100% on ADC corresponds to 3.3v on the analog input pin. Although as I understood it the default vref is actually 3.6v . So readings will be wrong if you have the wrong value here.

510 and 1000 correspond (as noted in the comments in the earlier source code) to the voltage divider in the Xiao schematic (built-in), which lies between the battery output, the vbat pin, and ground. According to the schematic, there’s 1 M Ohm (1000 K Ohm) to battery, and 510K Ohm to ground

Hi everyone! I have a question related to the wiring of a LiPo battery to the Xiao BLE. Must the battery be connected to the VIN/GND pads located underneath the Xiao BLE board? Or can it be connected to pin #13 (the so-called “5V” pin) and pin #12 (GND pin)? I am asking because soldering a wire to these flat pads is a little tricky. I was able to do it, but it’s tricky. Whereas soldering a wire to a pin is a lot easier.

I have yet to receive my Xiao BLE, but I experimented a little on a Xiao SAMD21, and it looks like the voltage of the VIN pad and that of the “5V” pin (#13) are identical. I connected my bench DC power supply to the VIN pad and gave it 3.7V or 5.5V, and pin #13 showed the same exact voltage.

Note: As long as I power the board with a voltage > 3.3V, pin #11 is always at 3.3V.

Thanks in advance!