Skip to content

Commit

Permalink
Add E.getPowerUsage() to get estimated power usage of a device based …
Browse files Browse the repository at this point in the history
…on what internal hardware is powered on and CPU use
  • Loading branch information
gfwilliams committed Apr 24, 2024
1 parent d361bd4 commit fa73e31
Show file tree
Hide file tree
Showing 22 changed files with 303 additions and 41 deletions.
27 changes: 27 additions & 0 deletions libs/banglejs/jswrap_bangle.c
Expand Up @@ -6240,3 +6240,30 @@ void jsbangle_push_event(JsBangleEvent type, uint16_t value) {
evt.data.chars[2] = (char)(value & 0xFF);
jshPushEvent(&evt);
}

/*JSON{
"type" : "powerusage",
"generate" : "jswrap_banglejs_powerusage"
}*/
void jswrap_banglejs_powerusage(JsVar *devices) {
// https://www.espruino.com/Bangle.js2#power-consumption
#ifdef BANGLEJS_F18
if (jswrap_banglejs_isLCDOn())
jsvObjectSetChildAndUnLock(devices, "LCD", jsvNewFromInteger(40000));
#endif
#ifdef BANGLEJS_Q3
if (jswrap_banglejs_isBacklightOn())
jsvObjectSetChildAndUnLock(devices, "LCD_backlight", jsvNewFromInteger(16000));
if (!jswrap_banglejs_isLocked())
jsvObjectSetChildAndUnLock(devices, "LCD_touch", jsvNewFromInteger(2500));
#endif
if (jswrap_banglejs_isHRMOn())
jsvObjectSetChildAndUnLock(devices, "HRM", jsvNewFromInteger(700));
if (jswrap_banglejs_isGPSOn())
jsvObjectSetChildAndUnLock(devices, "GPS", jsvNewFromInteger(20000));
if (jswrap_banglejs_isCompassOn())
jsvObjectSetChildAndUnLock(devices, "compass", jsvNewFromInteger(600));
if (jswrap_banglejs_isBarometerOn())
jsvObjectSetChildAndUnLock(devices, "baro", jsvNewFromInteger(200));
jsvObjectSetChildAndUnLock(devices, "polling", jsvNewFromInteger(15 * 1000 / pollInterval));
}
1 change: 1 addition & 0 deletions libs/banglejs/jswrap_bangle.h
Expand Up @@ -103,3 +103,4 @@ void jsbangle_exec_pending(IOEvent *event);
/// queue an event for Bangle.js (usually called from inside an IRQ)
void jsbangle_push_event(JsBangleEvent type, uint16_t value);

void jswrap_banglejs_powerusage(JsVar *devices);
9 changes: 6 additions & 3 deletions libs/bluetooth/bluetooth.h
Expand Up @@ -212,7 +212,10 @@ extern volatile BLEStatus bleStatus;
/// Filter to use when discovering BLE Services/Characteristics
extern ble_uuid_t bleUUIDFilter;

extern uint16_t bleAdvertisingInterval; /**< The advertising interval (in units of 0.625 ms). */
/// The advertising interval (in units of 0.625 ms)
extern uint16_t bleAdvertisingInterval;
/// The interval for the current peripheral connection (in units of 0.625 ms)
extern uint16_t blePeriphConnectionInterval;

extern volatile uint16_t m_peripheral_conn_handle; /**< Handle of the current connection. */
#if CENTRAL_LINK_COUNT>0
Expand Down Expand Up @@ -319,7 +322,7 @@ void jsble_setup_advdata(ble_advdata_t *advdata);
#define TAG_HEADER_LEN 0x0A

/*
TT = Tag Type
TT = Tag Type
ML = NDEF Message Length
RT = Record Type
TF = TNF and Flags
Expand All @@ -331,7 +334,7 @@ IC = URI Identifier Code
"\x00\x00\x00\x00" /* | UID/BCC | */ \
"\x00\x00\xFF\xFF" /* | UID/BCC | LOCK | */ \
"\xE1\x11\x7C\x0F" /* | Cap. Container | */ \
"\x03\x00\x00\x00" /* | TT | ML (1 or 3 bytes)| */
"\x03\x00\x00\x00" /* | TT | ML (1 or 3 bytes)| */

#define NDEF_HEADER_LEN_SHORT 0x12 /* with 1 byte length */
#define NDEF_HEADER_LEN_LONG 0x14 /* with 3 byte length */
Expand Down
24 changes: 24 additions & 0 deletions libs/bluetooth/jswrap_bluetooth.c
Expand Up @@ -4566,3 +4566,27 @@ JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_stopNotifications(JsVar *cha
return 0;
#endif
}


/*JSON{
"type" : "powerusage",
"generate" : "jswrap_ble_powerusage"
}*/
void jswrap_ble_powerusage(JsVar *devices) {
#ifdef NRF5X
// https://devzone.nordicsemi.com/power/w/opp/2/online-power-profiler-for-bluetooth-le
if (jsble_has_peripheral_connection()) {
int perSec = 1600 / blePeriphConnectionInterval; // blePeriphConnectionInterval is in units of 0.625ms
jsvObjectSetChildAndUnLock(devices, "BLE_periph", jsvNewFromInteger(6*perSec)); // ~6uA per connection
}
if (jsble_has_central_connection()) {
jsvObjectSetChildAndUnLock(devices, "BLE_central", jsvNewFromInteger(500));
// Could do finer grained central connection
}
if (bleStatus & BLE_IS_ADVERTISING) {
int perSec = 1600 / bleAdvertisingInterval; // bleAdvertisingInterval is in units of 0.625ms
jsvObjectSetChildAndUnLock(devices, "BLE_advertise", jsvNewFromInteger(12*perSec)); // ~12uA per advertisement
// Could try and take advertising length into account?
}
#endif
}
2 changes: 2 additions & 0 deletions libs/bluetooth/jswrap_bluetooth.h
Expand Up @@ -158,3 +158,5 @@ JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_writeValue(JsVar *characteri
JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_readValue(JsVar *characteristic);
JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_startNotifications(JsVar *characteristic);
JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_stopNotifications(JsVar *characteristic);

void jswrap_ble_powerusage(JsVar *devices);
22 changes: 22 additions & 0 deletions libs/joltjs/jswrap_jolt.c
Expand Up @@ -644,3 +644,25 @@ bool jswrap_jolt_idle() {
}
return busy;
}


/*JSON{
"type" : "powerusage",
"generate" : "jswrap_jolt_powerusage"
}*/
void jswrap_jolt_powerusage(JsVar *devices) {
if (!(driverMode[0]==JDM_OFF || driverMode[0]==JDM_AUTO))
jsvObjectSetChildAndUnLock(devices, "driver0", jsvNewFromInteger(1000));
if (!(driverMode[1]==JDM_OFF || driverMode[1]==JDM_AUTO))
jsvObjectSetChildAndUnLock(devices, "driver1", jsvNewFromInteger(1000));
int v;
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+0) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin0_internal_resistance", jsvNewFromInteger(v));
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+2) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin2_internal_resistance", jsvNewFromInteger(v));
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+4) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin4_internal_resistance", jsvNewFromInteger(v));
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+6) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin6_internal_resistance", jsvNewFromInteger(v));

}
1 change: 1 addition & 0 deletions libs/joltjs/jswrap_jolt.h
Expand Up @@ -25,3 +25,4 @@ void jswrap_jolt_hwinit();
void jswrap_jolt_init();
void jswrap_jolt_kill();
bool jswrap_jolt_idle();
void jswrap_jolt_powerusage(JsVar *devices);
10 changes: 10 additions & 0 deletions libs/pixljs/jswrap_pixljs.c
Expand Up @@ -33,6 +33,7 @@
#include "lcd_arraybuffer.h"

const Pin PIXL_IO_PINS[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
bool lcdIsOn;

/*JSON{
"type": "class",
Expand Down Expand Up @@ -349,6 +350,7 @@ static bool pixl_selfTest() {
}*/
void jswrap_pixljs_init() {
// LCD Init 1
lcdIsOn = true;
jshPinOutput(LCD_SPI_CS,0);
jshPinOutput(LCD_SPI_DC,0);
jshPinOutput(LCD_SPI_SCK,0);
Expand Down Expand Up @@ -730,3 +732,11 @@ E.showAlert("These are\nLots of\nLines","My Title").then(function() {
To remove the window, call `E.showAlert()` with no arguments.
*/

/*JSON{
"type" : "powerusage",
"generate" : "jswrap_pixljs_powerusage"
}*/
void jswrap_pixljs_powerusage(JsVar *devices) {
jsvObjectSetChildAndUnLock(devices, "LCD", jsvNewFromInteger(lcdIsOn ? 170 : 20));
}
1 change: 1 addition & 0 deletions libs/pixljs/jswrap_pixljs.h
Expand Up @@ -21,3 +21,4 @@ void jswrap_pixljs_init();
void jswrap_pixljs_kill();
bool jswrap_pixljs_idle();
JsVarInt jswrap_pixljs_getBattery();
void jswrap_pixljs_powerusage(JsVar *devices);
70 changes: 43 additions & 27 deletions libs/puckjs/jswrap_puck.c
Expand Up @@ -58,11 +58,13 @@ const Pin PUCK_IO_PINS[] = {1,2,4,6,7,8,23,24,28,29,30,31};
#endif

bool mag_enabled = false; //< Has the magnetometer been turned on?
uint16_t mag_power; // est mag power in uA
int16_t mag_reading[3]; //< magnetometer xyz reading
//int mag_zero[3]; //< magnetometer 'zero' reading, only for Puck 2.1 right now
volatile bool mag_data_ready = false;

bool accel_enabled = false; //< Has the accelerometer been turned on?
uint16_t accel_power; // est mag power in uA
int16_t accel_reading[3];
int16_t gyro_reading[3];

Expand Down Expand Up @@ -278,21 +280,22 @@ void mag_pin_on() {
bool mag_on(int milliHz, bool instant) {
//jsiConsolePrintf("mag_on\n");
mag_pin_on();
mag_power = 0;
if (puckVersion == PUCKJS_1V0) { // MAG3110
jshDelayMicroseconds(2500); // 1.7ms from power on to ok
if (instant) milliHz = 80000;
int reg1 = 0;
if (milliHz == 80000) reg1 |= (0x00)<<3; // 900uA
else if (milliHz == 40000) reg1 |= (0x04)<<3; // 550uA
else if (milliHz == 20000) reg1 |= (0x08)<<3; // 275uA
else if (milliHz == 10000) reg1 |= (0x0C)<<3; // 137uA
else if (milliHz == 5000) reg1 |= (0x10)<<3; // 69uA
else if (milliHz == 2500) reg1 |= (0x14)<<3; // 34uA
else if (milliHz == 1250) reg1 |= (0x18)<<3; // 17uA
else if (milliHz == 630) reg1 |= (0x1C)<<3; // 8uA
else if (milliHz == 310) reg1 |= (0x1D)<<3; // 8uA
else if (milliHz == 160) reg1 |= (0x1E)<<3; // 8uA
else if (milliHz == 80) reg1 |= (0x1F)<<3; // 8uA
if (milliHz == 80000) { reg1 |= (0x00)<<3; mag_power = 900; }
else if (milliHz == 40000) { reg1 |= (0x04)<<3; mag_power = 550; }
else if (milliHz == 20000) { reg1 |= (0x08)<<3; mag_power = 275; }
else if (milliHz == 10000) { reg1 |= (0x0C)<<3; mag_power = 137; }
else if (milliHz == 5000) { reg1 |= (0x10)<<3; mag_power = 69; }
else if (milliHz == 2500) { reg1 |= (0x14)<<3; mag_power = 34; }
else if (milliHz == 1250) { reg1 |= (0x18)<<3; mag_power = 17; }
else if (milliHz == 630) { reg1 |= (0x1C)<<3; mag_power = 8; }
else if (milliHz == 310) { reg1 |= (0x1D)<<3; mag_power = 8; }
else if (milliHz == 160) { reg1 |= (0x1E)<<3; mag_power = 8; }
else if (milliHz == 80) { reg1 |= (0x1F)<<3; mag_power = 8; }
else return false;

jshDelayMicroseconds(2000); // 1.7ms from power on to ok
Expand All @@ -303,16 +306,16 @@ bool mag_on(int milliHz, bool instant) {
if (instant) milliHz = 80000;
bool lowPower = false;
int reg1 = 0x80; // temp sensor, low power
if (milliHz == 80000) reg1 |= 7<<2; // 900uA
else if (milliHz == 40000) reg1 |= 6<<2; // 550uA
else if (milliHz == 20000) reg1 |= 5<<2; // 275uA
else if (milliHz == 10000) reg1 |= 4<<2; // 137uA
else if (milliHz == 5000) reg1 |= 3<<2; // 69uA
else if (milliHz == 2500) reg1 |= 2<<2; // 34uA
else if (milliHz == 1250) reg1 |= 1<<2; // 17uA
if (milliHz == 80000) { reg1 |= 7<<2; mag_power = 900; }
else if (milliHz == 40000) { reg1 |= 6<<2; mag_power = 550; }
else if (milliHz == 20000) { reg1 |= 5<<2; mag_power = 275; }
else if (milliHz == 10000) { reg1 |= 4<<2; mag_power = 137; }
else if (milliHz == 5000) { reg1 |= 3<<2; mag_power = 69; }
else if (milliHz == 2500) { reg1 |= 2<<2; mag_power = 34; }
else if (milliHz == 1250) { reg1 |= 1<<2; mag_power = 17; }
else if (milliHz <= 630) { /*if (milliHz == 630 || milliHz == 625)*/
// We just go for the lowest power mode
reg1 |= 0<<2; // 8uA
reg1 |= 0<<2; mag_power = 8;
lowPower = true;
}
else return false;
Expand Down Expand Up @@ -498,17 +501,19 @@ bool accel_on(int milliHz) {
// CTRL1_XL / CTRL2_G
int reg = 0;
bool gyro = true;
accel_power = 0;
if (milliHz<12500) { // 1.6Hz, no gyro
reg = 11<<4;
gyro = false;
} else if (milliHz==12500) reg=1<<4; // 12.5 Hz (low power)
else if (milliHz==26000) reg=2<<4; // 26 Hz (low power)
else if (milliHz==52000) reg=3<<4; // 52 Hz (low power)
else if (milliHz==104000) reg=4<<4; // 104 Hz (normal mode)
else if (milliHz==208000) reg=5<<4; // 208 Hz (normal mode)
else if (milliHz==416000) reg=6<<4; // 416 Hz (high performance)
else if (milliHz==833000) reg=7<<4; // 833 Hz (high performance)
else if (milliHz==1660000) reg=8<<4; // 1.66 kHz (high performance)
accel_power = 40;
} else if (milliHz==12500) { reg=1<<4; accel_power = 350; } // 12.5 Hz (low power)
else if (milliHz==26000) { reg=2<<4;accel_power = 450; } // 26 Hz (low power)
else if (milliHz==52000) { reg=3<<4; accel_power = 600; }// 52 Hz (low power)
else if (milliHz==104000) { reg=4<<4; accel_power = 1700; }// 104 Hz (normal mode)
else if (milliHz==208000) { reg=5<<4; accel_power = 3000; }// 208 Hz (normal mode)
else if (milliHz==416000) { reg=6<<4; accel_power = 5300; }// 416 Hz (high performance)
else if (milliHz==833000) { reg=7<<4; accel_power = 5500; }// 833 Hz (high performance)
else if (milliHz==1660000) { reg=8<<4; accel_power = 5500; }// 1.66 kHz (high performance)
else return false;


Expand Down Expand Up @@ -1830,3 +1835,14 @@ bool jswrap_puck_idle() {
}
return busy;
}

/*JSON{
"type" : "powerusage",
"generate" : "jswrap_puck_powerusage"
}*/
void jswrap_puck_powerusage(JsVar *devices) {
if (mag_enabled)
jsvObjectSetChildAndUnLock(devices, "magnetometer", jsvNewFromInteger(mag_power));
if (accel_enabled)
jsvObjectSetChildAndUnLock(devices, "accelerometer", jsvNewFromInteger(accel_power));
}
1 change: 1 addition & 0 deletions libs/puckjs/jswrap_puck.h
Expand Up @@ -57,3 +57,4 @@ bool jswrap_puck_selfTest();
void jswrap_puck_init();
void jswrap_puck_kill();
bool jswrap_puck_idle();
void jswrap_puck_powerusage(JsVar *devices);
9 changes: 9 additions & 0 deletions scripts/build_jswrapper.py
Expand Up @@ -729,6 +729,15 @@ def removeBlacklistForWrapper(blacklistfile,datas):
codeOut(" "+jsondata["generate"]+"();")
codeOut('}')

codeOut("/** When called with an Object, fields are added for each device that is used with estimated power usage in uA */")
codeOut('void jswGetPowerUsage(JsVar *devices) {')
for jsondata in jsondatas:
if "type" in jsondata and jsondata["type"]=="powerusage":
codeOut(" "+jsondata["generate"]+"(devices);")
codeOut('}')



codeOut("/** Tasks to run when a character event is received */")
codeOut('bool jswOnCharEvent(IOEventFlags channel, char charData) {')
codeOut(' NOT_USED(channel);')
Expand Down
3 changes: 2 additions & 1 deletion scripts/common.py
Expand Up @@ -61,14 +61,15 @@ def f(*popenargs, **kwargs):
#
# Comments look like:
#
#/*JSON{ "type":"staticmethod|staticproperty|constructor|method|property|function|variable|class|library|idle|init|kill|EV_xxx",
#/*JSON{ "type":"staticmethod|staticproperty|constructor|method|property|function|variable|class|library|idle|init|kill|EV_xxx|powerusage",
# // class = built-in class that does not require instantiation
# // library = built-in class that needs require('classname')
# // idle = function to run on idle regardless
# // hwinit = function to run on Hardware Initialisation (called once at boot time, after jshInit, before jsvInit/etc)
# // init = function to run on Initialisation (eg boot/load/reset/after save/etc)
# // kill = function to run on Deinitialisation (eg before save/reset/etc)
# // EV_xxx = Something to be called with a character in an IRQ when it is received (eg. EV_SERIAL1)
# // powerusage = fn(JsVar*) called with an object, and should insert fields for deviec names and estimated power usage in uA
# "class" : "Double", "name" : "doubleToIntBits",
# "needs_parentName":true, // optional - if for a method, this makes the first 2 args parent+parentName (not just parent)
# "generate_full|generate|wrap" : "*(JsVarInt*)&x", // if generate=false, it'll only be used for docs
Expand Down
3 changes: 3 additions & 0 deletions src/jshardware.h
Expand Up @@ -453,6 +453,9 @@ unsigned int jshGetRandomNumber();
* speed in Hz though. */
unsigned int jshSetSystemClock(JsVar *options);

/* Adds the estimated power usage of the microcontroller in uA to the 'devices' object. The CPU should be called 'CPU' */
void jsvGetProcessorPowerUsage(JsVar *devices);

/// Perform a proper hard-reboot of the device
void jshReboot();

Expand Down
5 changes: 5 additions & 0 deletions src/jshardware_common.c
Expand Up @@ -159,4 +159,9 @@ void jshKickSoftWatchDog() {
if (execInfo.execute & EXEC_CTRL_C_WAIT) {
execInfo.execute = (execInfo.execute & ~EXEC_CTRL_C_WAIT) | EXEC_CTRL_C;
}
}

/* Returns the estimated power usage of the microcontroller */
__attribute__((weak))void jsvGetProcessorPowerUsage(JsVar *devices) {
// not implemented by default
}

0 comments on commit fa73e31

Please sign in to comment.