Skip to content

Commit 5228cf2

Browse files
committed
nicla-system: Display charging and power status.
1 parent abbc54f commit 5228cf2

File tree

4 files changed

+129
-9
lines changed

4 files changed

+129
-9
lines changed

libraries/Nicla_System/examples/NiclaSenseME_BatteryStatus/NiclaSenseME_BatteryStatus.ino

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
#include <ArduinoBLE.h>
33

44
constexpr auto printInterval { 4000ul };
5-
constexpr auto batteryMeasureInterval { 5000ul };
5+
constexpr auto batteryUpdateInterval { 4000ul };
66
int8_t batteryChargeLevel = -1;
77
int8_t batteryPercentage = -1;
88
float batteryVoltage = -1.0f;
9+
int8_t runsOnBattery = -1; // Using an int to be able to represent an unknown state.
10+
int8_t batteryIsCharging = -1; // Using an int to be able to represent an unknown state.
911

1012

1113
#define DEVICE_NAME "NiclaSenseME"
@@ -15,10 +17,39 @@ BLEService service(DEVICE_UUID("0000"));
1517
BLEIntCharacteristic batteryPercentageCharacteristic(DEVICE_UUID("1001"), BLERead | BLENotify);
1618
BLEFloatCharacteristic batteryVoltageCharacteristic(DEVICE_UUID("1002"), BLERead | BLENotify);
1719
BLEIntCharacteristic batteryChargeLevelCharacteristic(DEVICE_UUID("1003"), BLERead | BLENotify);
20+
BLEBooleanCharacteristic runsOnBatteryCharacteristic(DEVICE_UUID("1004"), BLERead | BLENotify);
21+
BLEBooleanCharacteristic isChargingCharacteristic(DEVICE_UUID("1005"), BLERead | BLENotify);
1822

19-
bool updateBatteryLevel(bool enforceNewReading = false) {
23+
bool updateBatteryStatus(){
2024
static auto updateTimestamp = millis();
21-
bool intervalFired = millis() - updateTimestamp >= batteryMeasureInterval;
25+
bool intervalFired = millis() - updateTimestamp >= batteryUpdateInterval;
26+
bool isFirstReading = runsOnBattery == -1 || batteryIsCharging == -1;
27+
28+
if (intervalFired || isFirstReading) {
29+
Serial.println("Checking the battery status...");
30+
updateTimestamp = millis();
31+
int8_t isCharging = nicla::getOperatingStatus() == OperatingStatus::Charging;
32+
int8_t batteryPowered = nicla::runsOnBattery();
33+
bool valueUpdated = false;
34+
35+
if (batteryIsCharging != isCharging) {
36+
batteryIsCharging = isCharging;
37+
valueUpdated = true;
38+
}
39+
40+
if (runsOnBattery != batteryPowered) {
41+
runsOnBattery = batteryPowered;
42+
valueUpdated = true;
43+
}
44+
45+
return valueUpdated;
46+
}
47+
48+
return false;
49+
}
50+
51+
static auto updateTimestamp = millis();
52+
bool intervalFired = millis() - updateTimestamp >= batteryUpdateInterval;
2253
bool isFirstReading = batteryPercentage == -1 || batteryVoltage == -1.0f;
2354

2455
if (intervalFired || isFirstReading || enforceNewReading) {
@@ -112,6 +143,22 @@ void onBatteryChargeLevelCharacteristicRead(BLEDevice central, BLECharacteristic
112143
batteryChargeLevelCharacteristic.writeValue(batteryChargeLevel);
113144
}
114145

146+
void onRunsOnBatteryCharacteristicRead(BLEDevice central, BLECharacteristic characteristic) {
147+
Serial.println("Checking if device runs on battery...");
148+
updateBatteryStatus();
149+
Serial.print("Runs on battery: ");
150+
Serial.println(runsOnBattery == 1 ? "Yes" : "No");
151+
runsOnBatteryCharacteristic.writeValue(runsOnBattery == 1);
152+
}
153+
154+
void onIsChargingCharacteristicRead(BLEDevice central, BLECharacteristic characteristic) {
155+
Serial.println("Checking if battery is charging...");
156+
updateBatteryStatus();
157+
Serial.print("Battery is charging: ");
158+
Serial.println(batteryIsCharging == 1 ? "Yes" : "No");
159+
isChargingCharacteristic.writeValue(batteryIsCharging == 1);
160+
}
161+
115162
void onCharacteristicSubscribed(BLEDevice central, BLECharacteristic characteristic) {
116163
Serial.println("Device subscribed to characteristic: " + String(characteristic.uuid()));
117164
}
@@ -150,6 +197,16 @@ void setupBLE() {
150197
batteryChargeLevelCharacteristic.setEventHandler(BLESubscribed, onCharacteristicSubscribed);
151198
batteryChargeLevelCharacteristic.writeValue(batteryChargeLevel);
152199

200+
service.addCharacteristic(runsOnBatteryCharacteristic);
201+
runsOnBatteryCharacteristic.setEventHandler(BLERead, onRunsOnBatteryCharacteristicRead);
202+
runsOnBatteryCharacteristic.setEventHandler(BLESubscribed, onCharacteristicSubscribed);
203+
runsOnBatteryCharacteristic.writeValue(runsOnBattery == 1);
204+
205+
service.addCharacteristic(isChargingCharacteristic);
206+
isChargingCharacteristic.setEventHandler(BLERead, onIsChargingCharacteristicRead);
207+
isChargingCharacteristic.setEventHandler(BLESubscribed, onCharacteristicSubscribed);
208+
isChargingCharacteristic.writeValue(batteryIsCharging == 1);
209+
153210
BLE.addService(service);
154211
BLE.advertise();
155212
}
@@ -177,6 +234,7 @@ void loop()
177234

178235
if (BLE.connected()) {
179236
bool newBatteryLevelAvailable = updateBatteryLevel();
237+
bool newBatteryStatusAvailable = updateBatteryStatus();
180238

181239
if (batteryPercentageCharacteristic.subscribed() && newBatteryLevelAvailable) {
182240
Serial.print("Battery Percentage: ");
@@ -195,7 +253,19 @@ void loop()
195253
Serial.println(batteryChargeLevel);
196254
batteryChargeLevelCharacteristic.writeValue(batteryChargeLevel);
197255
}
198-
256+
257+
if(runsOnBatteryCharacteristic.subscribed() && newBatteryStatusAvailable) {
258+
Serial.print("Runs on battery: ");
259+
Serial.println(runsOnBattery == 1 ? "Yes" : "No");
260+
runsOnBatteryCharacteristic.writeValue(runsOnBattery == 1);
261+
}
262+
263+
if(isChargingCharacteristic.subscribed() && newBatteryStatusAvailable) {
264+
Serial.print("Battery is charging: ");
265+
Serial.println(batteryIsCharging == 1 ? "Yes" : "No");
266+
isChargingCharacteristic.writeValue(batteryIsCharging == 1);
267+
}
268+
199269
return;
200270
}
201271

libraries/Nicla_System/extras/BatteryMonitor/app.js

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const connectButton = document.getElementById('connect');
22
const batteryLevelElement = document.getElementById('battery-level');
33
const batteryLabel = document.getElementById('battery-label');
4+
const chargingIconElement = document.getElementById('charging-icon');
5+
const externalPowerIconElement = document.getElementById('external-powered-icon');
46

57
const serviceUuid = '19b10000-0000-537e-4f6c-d104768a1214';
68
let pollIntervalID;
@@ -41,6 +43,27 @@ let data = {
4143
const colors = ["#ffffff", "#ff2d2d", "#fc9228", "#ffea00", "#adfd5c", "#00c600"];
4244
return colors[value];
4345
}
46+
},
47+
"runsOnBattery": {
48+
"name": "Runs on Battery",
49+
"value": false,
50+
"unit": "",
51+
"characteristic": null,
52+
"characteristicUUID": "19b10000-1004-537e-4f6c-d104768a1214",
53+
"extractData": function(dataView) {
54+
return dataView.getUint8(0) == 1;
55+
}
56+
},
57+
58+
"isCharging": {
59+
"name": "Is Charging",
60+
"value": false,
61+
"unit": "",
62+
"characteristic": null,
63+
"characteristicUUID": "19b10000-1005-537e-4f6c-d104768a1214",
64+
"extractData": function(dataView) {
65+
return dataView.getUint8(0) == 1;
66+
}
4467
}
4568
};
4669

@@ -93,7 +116,7 @@ async function connectToPeripheralDevice(usePolling = false, pollInterval = 5000
93116

94117
connectButton.addEventListener('click', async () => {
95118
try {
96-
await connectToPeripheralDevice(true);
119+
await connectToPeripheralDevice();
97120
connectButton.disabled = true;
98121
connectButton.style.opacity = 0.5;
99122
} catch (error) {
@@ -106,7 +129,7 @@ connectButton.addEventListener('click', async () => {
106129
}
107130
});
108131

109-
function displayBatteryLevel() {
132+
function displayBatteryData() {
110133
const batteryPercentage = data.batteryPercentage.value;
111134
const batteryVoltage = data.batteryVoltage.value;
112135
const regulatedVoltage = (batteryVoltage / batteryPercentage * 100).toFixed(2);
@@ -116,6 +139,9 @@ function displayBatteryLevel() {
116139
batteryLevelElement.style.width = `${batteryPercentageMapped * 0.56}px`; // Scale the battery level to the width of the battery div
117140
batteryLevelElement.style.backgroundColor = data.batteryChargeLevel.getColor(data.batteryChargeLevel.value);
118141
batteryLabel.textContent = `${batteryVoltage.toFixed(2)}V (${batteryPercentage}% of ${regulatedVoltage}V)`;
142+
143+
chargingIconElement.style.display = data.isCharging.value ? "block" : "none";
144+
externalPowerIconElement.style.display = data.runsOnBattery.value ? "none" : "block";
119145
}
120146

121147
async function readCharacteristicsData() {
@@ -127,7 +153,7 @@ async function readCharacteristicsData() {
127153
console.log(item.name + ": " + item.value + item.unit);
128154
})
129155
);
130-
displayBatteryLevel();
156+
displayBatteryData();
131157
}
132158

133159
function handleCharacteristicChange(event) {
@@ -136,6 +162,6 @@ function handleCharacteristicChange(event) {
136162
let dataView = event.target.value;
137163
dataItem.value = dataItem.extractData(dataView);
138164

139-
console.log(dataItem.name + " changed: " + dataItem.value + dataItem.unit);
140-
displayBatteryLevel();
165+
console.log(`'${dataItem.name}' changed: ${dataItem.value}${dataItem.unit}`);
166+
displayBatteryData();
141167
}

libraries/Nicla_System/extras/BatteryMonitor/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@ <h1>WebBLE Battery Monitor 🔋</h1>
1919
<div class="battery-level" id="battery-level"></div>
2020
</div>
2121
<p id="battery-label"></p>
22+
<div id="battery-status">
23+
<div id="charging-icon">
24+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
25+
<path d="M23 31H9C8.73478 31 8.48043 30.8946 8.29289 30.7071C8.10536 30.5196 8 30.2652 8 30V5C8 4.73478 8.10536 4.48043 8.29289 4.29289C8.48043 4.10536 8.73478 4 9 4H12V2C12 1.73478 12.1054 1.48043 12.2929 1.29289C12.4804 1.10536 12.7348 1 13 1H19C19.2652 1 19.5196 1.10536 19.7071 1.29289C19.8946 1.48043 20 1.73478 20 2V4H23C23.2652 4 23.5196 4.10536 23.7071 4.29289C23.8946 4.48043 24 4.73478 24 5V30C24 30.2652 23.8946 30.5196 23.7071 30.7071C23.5196 30.8946 23.2652 31 23 31ZM10 29H22V6H19C18.7348 6 18.4804 5.89464 18.2929 5.70711C18.1054 5.51957 18 5.26522 18 5V3H14V5C14 5.26522 13.8946 5.51957 13.7071 5.70711C13.5196 5.89464 13.2652 6 13 6H10V29Z" fill="black"/>
26+
<path d="M19.87 16.48L14.87 25.48C14.8201 25.6028 14.7465 25.7145 14.6534 25.8089C14.5603 25.9032 14.4496 25.9783 14.3274 26.0298C14.0808 26.1339 13.803 26.1358 13.555 26.035C13.4322 25.9851 13.3205 25.9115 13.2261 25.8184C13.1318 25.7253 13.0567 25.6146 13.0052 25.4924C12.9011 25.2458 12.8992 24.968 13 24.72L14.67 19H13C12.8448 19 12.6916 18.9639 12.5528 18.8944C12.4139 18.825 12.2931 18.7242 12.2 18.6C12.1069 18.4758 12.0439 18.3316 12.0161 18.1789C11.9884 18.0261 11.9965 17.869 12.04 17.72L14.04 10.72C14.1006 10.5123 14.2269 10.3298 14.4 10.2C14.5731 10.0702 14.7836 10 15 10H18C18.1638 9.99975 18.3252 10.0397 18.4699 10.1165C18.6146 10.1932 18.7383 10.3043 18.83 10.44C18.9212 10.5751 18.978 10.7305 18.9954 10.8926C19.0129 11.0547 18.9904 11.2186 18.93 11.37L17.48 15H19C19.1735 15 19.3441 15.0452 19.4949 15.1311C19.6457 15.217 19.7715 15.3407 19.86 15.49C19.9475 15.64 19.9944 15.8101 19.9961 15.9837C19.9979 16.1573 19.9544 16.3283 19.87 16.48Z" fill="black"/>
27+
</svg>
28+
</div>
29+
<div id="external-powered-icon">
30+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
31+
<path d="M28 8H26V4C26 3.73478 25.8946 3.48043 25.7071 3.29289C25.5196 3.10536 25.2652 3 25 3C24.7348 3 24.4804 3.10536 24.2929 3.29289C24.1054 3.48043 24 3.73478 24 4V8H20V4C20 3.73478 19.8946 3.48043 19.7071 3.29289C19.5196 3.10536 19.2652 3 19 3C18.7348 3 18.4804 3.10536 18.2929 3.29289C18.1054 3.48043 18 3.73478 18 4V8H16C15.7348 8 15.4804 8.10536 15.2929 8.29289C15.1054 8.48043 15 8.73478 15 9C15 9.26522 15.1054 9.51957 15.2929 9.70711C15.4804 9.89464 15.7348 10 16 10V14C16.0014 15.4169 16.5042 16.7875 17.4194 17.8692C18.3345 18.9509 19.6029 19.6739 21 19.91V22C21 23.0609 20.5786 24.0783 19.8284 24.8284C19.0783 25.5786 18.0609 26 17 26C15.9391 26 14.9217 25.5786 14.1716 24.8284C13.4214 24.0783 13 23.0609 13 22V12.5C13 11.3065 12.5259 10.1619 11.682 9.31802C10.8381 8.47411 9.69347 8 8.5 8C7.30653 8 6.16193 8.47411 5.31802 9.31802C4.47411 10.1619 4 11.3065 4 12.5V28C4 28.2652 4.10536 28.5196 4.29289 28.7071C4.48043 28.8946 4.73478 29 5 29C5.26522 29 5.51957 28.8946 5.70711 28.7071C5.89464 28.5196 6 28.2652 6 28V12.5C6 11.837 6.26339 11.2011 6.73223 10.7322C7.20107 10.2634 7.83696 10 8.5 10C9.16304 10 9.79893 10.2634 10.2678 10.7322C10.7366 11.2011 11 11.837 11 12.5V22C11 23.5913 11.6321 25.1174 12.7574 26.2426C13.8826 27.3679 15.4087 28 17 28C18.5913 28 20.1174 27.3679 21.2426 26.2426C22.3679 25.1174 23 23.5913 23 22V19.91C24.3971 19.6739 25.6655 18.9509 26.5806 17.8692C27.4958 16.7875 27.9986 15.4169 28 14V10C28.2652 10 28.5196 9.89464 28.7071 9.70711C28.8946 9.51957 29 9.26522 29 9C29 8.73478 28.8946 8.48043 28.7071 8.29289C28.5196 8.10536 28.2652 8 28 8ZM26 14C26 15.0609 25.5786 16.0783 24.8284 16.8284C24.0783 17.5786 23.0609 18 22 18C20.9391 18 19.9217 17.5786 19.1716 16.8284C18.4214 16.0783 18 15.0609 18 14V10H26V14Z" fill="black"/>
32+
</svg>
33+
</div>
34+
</div>
2235
<button id="connect">Connect</button>
2336
</div>
2437
</body>

libraries/Nicla_System/extras/BatteryMonitor/style.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,14 @@ button:hover {
5757
width: 0;
5858
height: 26px;
5959
}
60+
61+
#battery-status {
62+
display: flex;
63+
flex-direction: row;
64+
margin: 20px 0;
65+
justify-content: center;
66+
}
67+
68+
#battery-status > div {
69+
display: none;
70+
}

0 commit comments

Comments
 (0)