XIAO_BLE_Sense(mbed 2.7.2) battery charge and voltage monitor, analogRead(P0_31) does not work

Seeed has a new commit after v2.9.1 which solved battery reading but still got other issues.

The best way is to apply msfujino’s patch after upgrading to v2.9.1.

By the way I encountered a new problem today. The OLED of the expansion board which originally could display up to 8 rows of characters, failed to display the first 2 rows. It would only display on the 3rd row even though cursor was set to (0,0). This is very weird as the OLED had been working for a week. It started to behave like above after a power cycling. I noticed the OLED is a SSD1315 instead of SSD1306 after dismantling it off the board. While many users have no problems using 1306 library for 1315, I noticed a few of them got unexpected issues. I guess I am just another rare victim, sigh …

Yes, This works however as You can see it’s "horse-shoe’s and Hand-Grenades’ accurate(close-enough) but I hope they come with something that doesn’t require this low level maintenance B.S. And BTW “adafruit” product software data and doc’s eat their lunch compared to Seeed engineering(should rename it to licensing dept.) but I digress,

I do this on any systems I’m building with this ISSUE; It’s a “WORK-AROUND”
I can test that it tracks some what with the PPK2 as the source 2.2v to 3.85 range tests always off by about .6 ish.
I ,

  1. Delete “pin_arduino.h” and “variant.cpp” in directry “\2.9.1\variants\SEEED_XIAO_NRF5284” and “\2.9.1\variants\SEEED_XIAO_NRF5284_SENSE”,
    then copy “pin_arduino.h” and “variant.cpp” from the unzip folder.
  2. RESTART Arduino IDE
This Breaks the Sleep interrupts, (i.e the code bellow sleeps properly and wakes properly without the variants fix above) after the patch , NONE of the sleep sketches work ??? I don't know why.

Choose board, compile and test it.
I have two expansion units both work as advertised, albeit the battery can’t be read in that? LOL
Fresh Battery…OK

High reading a TAD

maybe I expect too much? or I’m doing it wrong :grinning:

Both work currently and I do think there’s room for improvement, It’s expansion/development pick a lane for goodness sake also.

Can’t beat the Footprint though… A+

#include "LSM6DS3.h"
#include "Wire.h"
LSM6DS3 myIMU(I2C_MODE, 0x6A); // IMU
#define int1Pin PIN_LSM6DS3TR_C_INT1

const int ledPin = LED_BUILTIN; // set ledPin to on-board LED

uint8_t interruptCount = 0; // Amount of received interrupts
uint8_t prevInterruptCount = 0; // Interrupt Counter from last loop

void setup() {
  while ( !Serial ) delay(10);   // for nrf52840 with native usb
  pinMode(ledPin, OUTPUT); // use the LED as an output
  Serial.println("Hello, I am awake!");
  myIMU.settings.gyroEnabled = 0; // Gyro currently not used, disabled to save power
  if (myIMU.begin() != 0) {
    Serial.println("IMU error");
  } else {
    Serial.println("IMU OK!");


  pinMode(int1Pin, INPUT);
  attachInterrupt(digitalPinToInterrupt(int1Pin), int1ISR, RISING);

void loop() {
  Serial.print("Interrupt Counter: ");

  if (interruptCount > prevInterruptCount) {
    Serial.println("Interrupt received!");
  prevInterruptCount = interruptCount;

  if (interruptCount >= 3) {
    // Trigger System OFF after 5 interrupts


void goToPowerOff() {
  Serial.println("Going to System OFF");
  delay(1000); // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int1Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  delay(2000);// Trigger System OFF

void setupDoubleTapInterrupt() {
  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  // Double Tap Config
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer
  // ODR_XL = 416 Hz, FS_XL = 2g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);// INTERRUPTS_ENABLE, SLOPE_FDS// Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x85);// Set tap threshold 8C
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);// Set Duration, Quiet and Shock time windows 7F
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80);// Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);// Double-tap interrupt driven to INT1 pin

void int1ISR() {

  void setLED(bool on)
  // data = 1 -> LED = On
  // data = 0 -> LED = Off
  digitalWrite(LED_BUILTIN, on ? HIGH : LOW);

The Hint is the charge LED… It’s illuminated when sleeping(with no battery connected), USB only. as before the variants patch no battery voltage is readable and so also After taping to wake up and fully on, No battery voltage is readable? Useless . Terrible for a battery powered chip.

I’m disappointed.

1 Like


Turns an LED on for one second, then off for one second, repeatedly.

Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
the correct LED pin independent of which board is used.
If you want to know what pin the on-board LED is connected to on your Arduino
model, check the Technical Specs of your board at:

modified 8 May 2014
by Scott Fitzgerald
modified 2 Sep 2016
by Arturo Guadalupi
modified 8 Sep 2016
by Colby Newman

This example code is in the public domain.

#include <bluefruit.h>

const char TestString[12] = “Test String”;

// XIAO VBAT Measurement
// Set the GPIO PIN and ADC configuration for VBat measurements
void SetVBatMeasurement() {
pinMode(PIN_VBAT, INPUT); //Battery Voltage monitoring pin
pinMode(VBAT_ENABLE, OUTPUT); //Enable Battery Voltage monitoring pin
digitalWrite(VBAT_ENABLE, LOW); //Enable
analogReference(AR_INTERNAL_2_4); //Vref=2.4V
analogReadResolution(12); //12bits
// Perform a VBat voltage Read
#define VBatAdj (3.970 / 3.892) // 2% Uncertainty: in specifications
float ReadVbat() {
// Read VBAT voltage
pinMode(VBAT_ENABLE, OUTPUT); //Enable Battery Voltage monitoring pin
digitalWrite(VBAT_ENABLE, LOW); //Enable
digitalWrite(LED_RED, LOW); // turn the LED on (HIGH is the voltage level)
delay(200); // To be sure the pin voltage is stable
int VBatMin = 4096;
int VBatMax = 0;
int VBat = 0;
for (int i = 0; i < 10; i++) {
int v = analogRead(PIN_VBAT);
VBatMin = min(VBatMin, v);
VBatMax = max(VBatMax, v);
VBat += v;
VBat -= VBatMin; // Remove min ADC value
VBat -= VBatMax; // Remove max ADC value
VBat /= 8;
pinMode(VBAT_ENABLE, INPUT); //Disable Battery Voltage monitoring pin
digitalWrite(LED_RED, HIGH); // turn the LED off by making the voltage LOW
float Vbattery = ((510e3 + 1000e3) / 510e3) * 2.4 * VBat / 4096;
return (Vbattery * VBatAdj);
// Convert VBat in %
int ConvertVBatInPercent(float v) {
long int iv = (long int)(v * 1000.0);
long int val = map(iv, 3100, 4100, 0, 100);
return ((int)val);

// the setup function runs once when you press reset or power the board
void setup() {

// initialize digital pin LED_BUILTIN as an output.
Serial.println("---- TEST XIAO");
// TestString = “TEST String”;
#define HICHG (13)
// pinMode(HICHG, OUTPUT); //Charge Current setting pin
// digitalWrite(HICHG, HIGH); //Charge Current 0:100mA, 1:50mA

Serial.println("- RUN LED -");
digitalWrite(LED_BLUE, LOW); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BLUE, HIGH); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
digitalWrite(LED_GREEN, LOW); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_GREEN, HIGH); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
digitalWrite(LED_RED, LOW); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_RED, HIGH); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second

// the loop function runs over and over again forever
void loop() {
float vb = ReadVbat();
Serial.print(vb * 100);
Serial.print(F(", "));

Is this thing ON? Why is it marked SOLVED? WTH? :grimacing:
I wonder if we will ever be able to read the battery voltage on the Xiao nRf52840 BLE Sense micro?. Why is this a problem? I’m using

Using library ArduinoBLE at version 1.3.4 in folder: D:\Arduino_projects\libraries\ArduinoBLE 
Using library Seeed Arduino LSM6DS3 at version 2.0.3 in folder: D:\Arduino_projects\libraries\Seeed_Arduino_LSM6DS3 
Using library Wire in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\Seeeduino\hardware\mbed\2.9.1\libraries\Wire (legacy)
Using library SPI in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\Seeeduino\hardware\mbed\2.9.1\libraries\SPI (legacy)
--Removed for clarity--
Sketch uses 342496 bytes (42%) of program storage space. Maximum is 811008 bytes.
Global variables use 72160 bytes (30%) of dynamic memory, leaving 165408 bytes for local variables. Maximum is 237568 bytes.

Everything else works, C’mon Seeed what’s up with this stuff?

Hi Folks,

I was looking for a way to read the voltage of the battery on the xiao ble sense nrf52840 (that is actually connected to the Expansion board) but I ran into this post.

So if I understand correctly there was the issue that
analogRead(PIN_VBAT) hangs the IDE ----> GREEN LED ON/OFF hangs the IDE

It has been fixed with a patch but now I read in that post
that the reading is not consistent and also
and when you put the device in deep sleep and wake it up, you get no reading at all?

Could somebody give an update about what works and what doesn’t ?

Also does this issue happen only when you read VBAT pin from the xiao ble sense nrf52840
and the battery’s wires are soldered to Bat+ and Bat- pads ?

Or could the issue happen too if the battery is plugged to the JST connector on the Expansion board and you read the battery pin ? Which I don’t even know if it’s possible on the Expansion board because I don’t know if the BATT pin can be read by the MCU.

That’s the Expansion board schematic:
Expansion board schematic

Would appreciate any insightful input on this.

Cheers !

Hi there and welcome to the SHOW… :grinning:

Ah’s the SEEED Studio’s
The code name “XIAO” here represents its half feature “Tiny”, and the other half will be “Puissant”. :grinning:

"Elaborate Power Design: Provide ultra-low power consumption as 5 μA in deep sleep mode while supporting lithium battery charge management"

"Seeed Studio XIAO nRF52840 Sense is carrying Bluetooth 5.0 wireless capability and is able to operate with low power consumption."

"strong software compatibilities, which supports Arduino, Micropython, and CircuitPython. Except for the software"


IMHO, They need a DISCLAIMER though! (none of the battery crap works out of the box)
I suggest you look at this post HERE, Msfujino He has the battery reading on the Expansion board mod on Lock. :wink: :+1:

SEEED engineering a.k.a. (marketing) on the other hand DOES NOT!!! :v:

GL :-p

1 Like

Hi lguapo,

The battery connected to the JST connector on the XIAO_Expansion_Board is not connected to the XIAO port, so the voltage cannot be read. The modification in the link below #1 allows you to read the battery voltage with analonRead(A0).
XIAO nRF52840(mbed) 1-Central 2-Peripherals Project "Sleeping Posture, Heart rate, Pulse oximetry Monitor"

The battery voltage connected to the pad on the back side of XIAO can be read with analogRead(P0_31) or analogRead(PIN_VBAT). However, when using Board Service Package:“Seeed nRF52 mbed-enable Borads 2.9.1”, the patch in this post #31 is required; when using “Seeed nRF52 Boards 1.1.1”, no patch is required.

1 Like

Yes, XIAO for tiny but can be a big pain in the a** too sometimes.
I wish their doc and wiki was a bit better.
As you mentioned before, Adafruit’s doc is walk in the park on a sunny Sunday with an ice-cream in your hand compared to Seeed.

Maybe it’ll get better over time.

1 Like

Thanks for the information !
I see. You created a voltage divider and you read the value with the A0 pin.


I’m not planning to use the mbed OS, so I should be fine.
If I want to plug the battery through the pads and read the battery voltage from PIN_VBAT,
would that give me an accurate reading ?

From what I read in that post, I was under the impression there was an issue with inconsistent readings.

I there a difference with reading the battery voltage from VBAT and from P0.31?

I see there is a risk to burn out the P0.31 pin if the ADC function of Pin P0.14 is off (which is done with setting the Pin P0.14 high).

You cannot apply VBAT directly to any port of nRF52840. According to datasheet the absolute maximum input voltage is (VDD + 0.3V) or 3.6V. A charged battery can rise up to 4.2V and so you must always use a potential divider to step down the voltage.

You can also easily calculate the voltage at P0.31 to be around 3.6V when the battery reaches 4.2V and P0.14 is off. This is the risk mentioned by Seeed.

As long as you use correct ADC resolution and potential divider values, the voltage measurement will be fine.

1 Like

Thanks for the clarification !

So Still
Looking for an UPDATE from SEEED on this XIAO_BLE_SENSE Board file situ, 2.9.1 and reading the battery voltage along with IMU and BLE operation. concurrently :slight_smile:
NO tricks , No hacks, Patched or Not ? What’s the deal on this battery powered device?
GL :slight_smile:

Seeed nRF52 mbed-enabled Boards v2.9.2 was released on June 6.
Bugs related to LEDs and battery voltage reading were fixed.

nRF52_XIAO_mbed_VBAT_READ_TEST.zip (911 Bytes)

Hi there,
I see, but it broke the interrupts from IMU demo. works with 2.9.1 but not 2.9.2
I’ll retest and let you know.
GL :slight_smile: PJ

Hi there,
Yes it’s confirmed that the Sleep does NOT work after updating to 2.9.2. the Battery does read and track well, the

notice the 0 in the lower left corner of display shows usb cable PLUGGED in charging "0"

Unplugged changes to “1” NOT charging…

which is great, but not helpful if the SLeep function that worked in all previous BSP’s
does NOT work anymore.
Try any of the TAP to wake demo’s on here.
the MCU won’t stay asleep until tap’d.
So for me BSP 2.9.2 is Broken

I combined the test and the Battery read . One should load with 2.9.1 BSP , Sleep works
(3button presses)
load BSP 2.9.2 battery read works , but 3 button presses (User Button) Xiao won’t stay sleeping. Until double tap “wakeUp”
sketch_sep5a_PARK_IMU_copy_Posted.zip (4.6 KB)
GL :slight_smile: PJ

Confirmed that Sleep mode cannot be maintained; BSP 2.9.2 is not yet available.
It is difficult to properly communicate this problem to the Seeed developers.
I think the problem is that there is no menu to submit issus to “GitHub - Seeed-Studio/ArduinoCore-mbed”.

I also tried with BSP2.9.1+patch, but it does not seem to be able to maintain Sleep mode.
Is there any difference in how to check?

  1. Click the button three times.
  2. “SLEEP!” is displayed.
  3. The display will disappear after about 5 seconds.
  4. LED flashes in the order of G->R->B
  5. Acceleration and voltage will start to be displayed.

Yes with BSP 2.9.1 , when 3 button presses , and sleep is displayed it will stay off(asleep) until double tap is performed. System will reset on wakeup, ie. startup sound test and LED test R,G,B… flash!
GL :slight_smile: PJ

yes a GitHub - menu would be good.

I also tried with BSP2.9.1+patch, but it does not seem to be able to maintain Sleep mode.
bsp291+patch.zip (1.6 MB)

Sorry, I was checking with XIAO non Sense.
With BSP2.9.2, BSP2.9.1+patch, I confirmed that it cannot maintain sleep mode and wakes up after about 5 seconds.
BSP2.9.1(no patch) is good, but can’t read voltage.

//#define int2Pin PIN_LSM6DS3TR_C_INT1
#define int2Pin P0_11

It worked with BSP 2.9.2.

Hi there,
WOW , Indeed it does.
I’ll test some more, Awesome find my guy. :disguised_face:
GL :slight_smile: PJ :man_bowing: