diff --git a/QBeadVisualisation.html b/QBeadVisualisation.html
index 62486b1..d3f74a8 100644
--- a/QBeadVisualisation.html
+++ b/QBeadVisualisation.html
@@ -27,6 +27,7 @@
@@ -313,6 +314,7 @@
Live Output
var accCharacteristic;
var sphCharacteristic;
var colCharacteristic;
+ var batteryLevelCharacteristic;
function onStartButtonClick() {
let serviceUuid = "e30c1fc6-359c-12be-2544-63d6aa088d45";
@@ -320,7 +322,8 @@
Live Output
let accUuid = "e30c1fc9-359c-12be-2544-63d6aa088d45";
let sphUuid = "e30c1fc8-359c-12be-2544-63d6aa088d45";
let colUuid = "e30c1fc7-359c-12be-2544-63d6aa088d45";
-
+ let batUuid = "00002a19-0000-1000-8000-00805f9b34fb";
+
log('Requesting Bluetooth Device...');
navigator.bluetooth.requestDevice(
{
@@ -338,6 +341,13 @@
Live Output
.then(service => {
log('Getting Characteristics...');
service.getCharacteristic(colUuid).then(ch => {colCharacteristic=ch;});
+ service.getCharacteristic(batUuid).then(ch => {
+ batteryLevelCharacteristic=ch;
+ return batteryLevelCharacteristic.startNotifications().then(_ => {
+ batteryLevelCharacteristic.addEventListener('characteristicvaluechanged',
+ handleBatteryNotifications);
+ })
+ });
return service.getCharacteristic(accUuid);
})
.then(characteristic => {
@@ -365,6 +375,17 @@
Live Output
log('Argh! ' + error);
});
}
+ if (batteryLevelCharacteristic) {
+ batteryLevelCharacteristic.stopNotifications()
+ .then(_ => {
+ log('> Notifications stopped');
+ batteryLevelCharacteristic.removeEventListener('characteristicvaluechanged',
+ handleBatteryNotifications);
+ })
+ .catch(error => {
+ log('Argh! ' + error);
+ });
+ }
}
function onPhiRangeChange() {
@@ -408,6 +429,12 @@
Live Output
window.z = z;
log(`> ${x.toFixed(3)} ${y.toFixed(3)} ${z.toFixed(3)} ${t.toFixed(0)} ${p.toFixed(0)} `);
}
+
+ function handleBatteryNotifications(event) {
+ let batteryPercentage = event.target.value.getInt8(0);
+ document.getElementById("batteryLevel").textContent = `Battery Level: ${batteryPercentage}%`;
+ // log(`> ${batteryPercentage} `);
+ }
diff --git a/examples/Tap_to_Measure/Tap_to_Measure.ino b/examples/Tap_to_Measure/Tap_to_Measure.ino
index 5f4c099..4fbb75b 100644
--- a/examples/Tap_to_Measure/Tap_to_Measure.ino
+++ b/examples/Tap_to_Measure/Tap_to_Measure.ino
@@ -28,6 +28,8 @@ void setup() {
// The loop function is called repeatedly until the Qbead is powered off.
// It is used to read the IMU and update the current state of the game.
void loop() {
+ bead.measureBattery();
+
static long last_tap = 0;
static uint32_t tap_color = white;
diff --git a/src/Qbead.h b/src/Qbead.h
index 77715cc..f0c9e6a 100644
--- a/src/Qbead.h
+++ b/src/Qbead.h
@@ -8,6 +8,8 @@
#include
#include
+#include
+
// default configs
// TODO make them ifndef
@@ -288,12 +290,16 @@ class Qbead {
blecharcol(QB_UUID_COL_CHAR),
blecharsph(QB_UUID_SPH_CHAR),
blecharacc(QB_UUID_ACC_CHAR),
- blechartap(QB_UUID_TAP_CHAR)
+ blechartap(QB_UUID_TAP_CHAR),
+ blecharbatlvl(UUID16_CHR_BATTERY_LEVEL)
{}
static Qbead *singletoninstance; // we need a global singleton static instance because bluefruit callbacks do not support context variables -- thankfully this is fine because there is indeed only one Qbead in existence at any time
LSM6DS3 imu;
+
+ XiaoBattery battery;
+
Adafruit_NeoPixel pixels;
BLEService bleservice;
@@ -301,6 +307,7 @@ class Qbead {
BLECharacteristic blecharsph;
BLECharacteristic blecharacc;
BLECharacteristic blechartap;
+ BLECharacteristic blecharbatlvl;
uint8_t connection_count = 0;
const uint8_t nsections;
@@ -309,6 +316,7 @@ class Qbead {
const uint8_t phi_quant;
const uint8_t ix, iy, iz;
const bool sx, sy, sz;
+ uint8_t bat_buff[1];
float rbuffer[3];
float whentapped_buffer[3];
float x_whentapped, y_whentapped, z_whentapped; // set when wasTapped is called
@@ -425,9 +433,32 @@ class Qbead {
blechartap.setFixedLen(3*sizeof(float));
blechartap.begin();
blechartap.write(zerobuffer20, 3*sizeof(float));
+ // BLE Characteristic battery level
+ blecharbatlvl.setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
+ blecharbatlvl.setPermission(SECMODE_OPEN, SECMODE_OPEN);
+ blecharbatlvl.setUserDescriptor("battery level");
+ blecharbatlvl.setFixedLen(1*sizeof(uint8_t));
+ blecharbatlvl.begin();
+ blecharbatlvl.write(zerobuffer20, sizeof(uint8_t));
startBLEadv();
}
+ void measureBattery(){
+ // if you measure the voltage twice in a row, the second time, the voltage will be 0.2V lower.
+ float percentage = battery.GetBatteryLevel();
+
+ float voltage = battery.GetBatteryVoltage();
+ bool charging = battery.IsChargingBattery();
+ bat_buff[0] = uint8_t(percentage);
+ Serial.println("battery voltage");
+ Serial.print(voltage);
+ Serial.print(", ");
+ Serial.print(charging);
+ Serial.print(", ");
+ Serial.println(percentage);
+ blecharbatlvl.notify(bat_buff, sizeof(uint8_t));
+ }
+
void clear() {
pixels.clear();
}
diff --git a/src/QbeadBattery.h b/src/QbeadBattery.h
new file mode 100644
index 0000000..2961bbe
--- /dev/null
+++ b/src/QbeadBattery.h
@@ -0,0 +1,45 @@
+#include
+#include
+
+#define BAT_HIGH_CHARGE 22 // HIGH for 50mA, LOW for 100mA
+#define BAT_CHARGE_STATE 23 // LOW for charging, HIGH not charging
+
+class XiaoBattery {
+public:
+ XiaoBattery();
+ float GetBatteryVoltage();
+ bool IsChargingBattery();
+ float GetBatteryLevel();
+private:
+ // define min and max voltage for battery level determination
+ float maxVoltage = 4.11;
+ float minVoltage = 3.06;
+};
+
+XiaoBattery::XiaoBattery() {
+ pinMode(VBAT_ENABLE, OUTPUT);
+ pinMode(BAT_CHARGE_STATE, INPUT);
+
+ digitalWrite(BAT_HIGH_CHARGE, LOW); // charge with 100ma if LOW, else 50mA
+}
+
+#define VBAT_MV_PER_LBS (0.003395996F)
+
+float XiaoBattery::GetBatteryVoltage() {
+ digitalWrite(VBAT_ENABLE, LOW);
+ // might want to wait very briefly for the voltage to settle
+ uint32_t adcCount = analogRead(PIN_VBAT);
+ float adcVoltage = adcCount * VBAT_MV_PER_LBS;
+ float vBat = adcVoltage * (1510.0 / 510.0);
+
+ digitalWrite(VBAT_ENABLE, HIGH);
+
+ return vBat;
+}
+
+float XiaoBattery::GetBatteryLevel() {
+ float batPercentage = (GetBatteryVoltage()-minVoltage)/(maxVoltage - minVoltage) * 100;
+ return batPercentage;
+}
+
+bool XiaoBattery::IsChargingBattery() { return digitalRead(BAT_CHARGE_STATE) == LOW; }
\ No newline at end of file