Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion QBeadVisualisation.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
</div>

<h3>Live Output</h3>
<p id="batteryLevel"></p>
<div id="output" class="output" style="display: inline;">
<div id="content"></div>
<div id="status"></div>
Expand Down Expand Up @@ -313,14 +314,16 @@ <h3>Live Output</h3>
var accCharacteristic;
var sphCharacteristic;
var colCharacteristic;
var batteryLevelCharacteristic;

function onStartButtonClick() {
let serviceUuid = "e30c1fc6-359c-12be-2544-63d6aa088d45";

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(
{
Expand All @@ -338,6 +341,13 @@ <h3>Live Output</h3>
.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 => {
Expand Down Expand Up @@ -365,6 +375,17 @@ <h3>Live Output</h3>
log('Argh! ' + error);
});
}
if (batteryLevelCharacteristic) {
batteryLevelCharacteristic.stopNotifications()
.then(_ => {
log('> Notifications stopped');
batteryLevelCharacteristic.removeEventListener('characteristicvaluechanged',
handleBatteryNotifications);
})
.catch(error => {
log('Argh! ' + error);
});
}
}

function onPhiRangeChange() {
Expand Down Expand Up @@ -408,6 +429,12 @@ <h3>Live Output</h3>
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} `);
}
</script>


Expand Down
2 changes: 2 additions & 0 deletions examples/Tap_to_Measure/Tap_to_Measure.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
33 changes: 32 additions & 1 deletion src/Qbead.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <math.h>

#include <bluefruit.h>
#include <QbeadBattery.h>


// default configs
// TODO make them ifndef
Expand Down Expand Up @@ -288,19 +290,24 @@ 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;
BLECharacteristic blecharcol;
BLECharacteristic blecharsph;
BLECharacteristic blecharacc;
BLECharacteristic blechartap;
BLECharacteristic blecharbatlvl;
uint8_t connection_count = 0;

const uint8_t nsections;
Expand All @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down
45 changes: 45 additions & 0 deletions src/QbeadBattery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <Arduino.h>
#include <bluefruit.h>

#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; }