This commit is contained in:
Joel Spadin 2024-05-06 11:13:21 -03:00 committed by GitHub
commit 88d359f2b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 75 additions and 69 deletions

View file

@ -388,11 +388,17 @@ if ZMK_BATTERY_REPORTING
choice ZMK_BATTERY_REPORTING_FETCH_MODE choice ZMK_BATTERY_REPORTING_FETCH_MODE
prompt "Battery Reporting Fetch Mode" prompt "Battery Reporting Fetch Mode"
config ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE
bool "State of charge"
config ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE config ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE
bool "Lithium Voltage" bool "Lithium Voltage"
help
Estimate the battery level by reading the voltage of a Lithium battery.
zmk,battery chosen node should be a sensor driver that implements SENSOR_CHAN_VOLTAGE.
config ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE
bool "State of charge"
help
Read the battery level from a fuel gauge sensor.
zmk,battery chosen node should be a sensor driver that implements SENSOR_CHAN_GAUGE_STATE_OF_CHARGE.
endchoice endchoice
endif endif

View file

@ -85,4 +85,8 @@ if BOARD_CORNEISH_ZEN_V1_LEFT || BOARD_CORNEISH_ZEN_V1_RIGHT
config BQ274XX config BQ274XX
default y default y
choice ZMK_BATTERY_REPORTING_FETCH_MODE
default ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE
endchoice
endif # BOARD_CORNEISH_ZEN_V1_LEFT || BOARD_CORNEISH_ZEN_V1_RIGHT endif # BOARD_CORNEISH_ZEN_V1_LEFT || BOARD_CORNEISH_ZEN_V1_RIGHT

View file

@ -12,32 +12,14 @@
int battery_channel_get(const struct battery_value *value, enum sensor_channel chan, int battery_channel_get(const struct battery_value *value, enum sensor_channel chan,
struct sensor_value *val_out) { struct sensor_value *val_out) {
switch (chan) { switch (chan) {
case SENSOR_CHAN_GAUGE_VOLTAGE: case SENSOR_CHAN_VOLTAGE:
val_out->val1 = value->millivolts / 1000; val_out->val1 = value->millivolts / 1000;
val_out->val2 = (value->millivolts % 1000) * 1000U; val_out->val2 = (value->millivolts % 1000) * 1000U;
break; break;
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
val_out->val1 = value->state_of_charge;
val_out->val2 = 0;
break;
default: default:
return -ENOTSUP; return -ENOTSUP;
} }
return 0; return 0;
} }
uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) {
// Simple linear approximation of a battery based off adafruit's discharge graph:
// https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
if (bat_mv >= 4200) {
return 100;
} else if (bat_mv <= 3450) {
return 0;
}
return bat_mv * 2 / 15 - 459;
}

View file

@ -12,10 +12,7 @@
struct battery_value { struct battery_value {
uint16_t adc_raw; uint16_t adc_raw;
uint16_t millivolts; uint16_t millivolts;
uint8_t state_of_charge;
}; };
int battery_channel_get(const struct battery_value *value, enum sensor_channel chan, int battery_channel_get(const struct battery_value *value, enum sensor_channel chan,
struct sensor_value *val_out); struct sensor_value *val_out);
uint8_t lithium_ion_mv_to_pct(int16_t bat_mv);

View file

@ -31,8 +31,7 @@ struct vddh_data {
static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) { static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) {
// Make sure selected channel is supported // Make sure selected channel is supported
if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE && if (chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_ALL) {
chan != SENSOR_CHAN_ALL) {
LOG_DBG("Selected channel is not supported: %d.", chan); LOG_DBG("Selected channel is not supported: %d.", chan);
return -ENOTSUP; return -ENOTSUP;
} }
@ -56,10 +55,8 @@ static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan)
} }
drv_data->value.millivolts = val * VDDHDIV; drv_data->value.millivolts = val * VDDHDIV;
drv_data->value.state_of_charge = lithium_ion_mv_to_pct(drv_data->value.millivolts);
LOG_DBG("ADC raw %d ~ %d mV => %d%%", drv_data->value.adc_raw, drv_data->value.millivolts, LOG_DBG("ADC raw %u ~ %u mV", drv_data->value.adc_raw, drv_data->value.millivolts);
drv_data->value.state_of_charge);
return rc; return rc;
} }

View file

@ -41,8 +41,7 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
struct adc_sequence *as = &drv_data->as; struct adc_sequence *as = &drv_data->as;
// Make sure selected channel is supported // Make sure selected channel is supported
if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE && if (chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_ALL) {
chan != SENSOR_CHAN_ALL) {
LOG_DBG("Selected channel is not supported: %d.", chan); LOG_DBG("Selected channel is not supported: %d.", chan);
return -ENOTSUP; return -ENOTSUP;
} }
@ -74,11 +73,8 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm; uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm;
LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->value.adc_raw, val, millivolts); LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->value.adc_raw, val, millivolts);
uint8_t percent = lithium_ion_mv_to_pct(millivolts);
LOG_DBG("Percent: %d", percent);
drv_data->value.millivolts = millivolts; drv_data->value.millivolts = millivolts;
drv_data->value.state_of_charge = percent;
} else { } else {
LOG_DBG("Failed to read ADC: %d", rc); LOG_DBG("Failed to read ADC: %d", rc);
} }

View file

@ -35,7 +35,7 @@ static const struct device *battery;
#endif #endif
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE) #if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE)
static uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) { static uint8_t lithium_ion_mv_to_pct(int bat_mv) {
// Simple linear approximation of a battery based off adafruit's discharge graph: // Simple linear approximation of a battery based off adafruit's discharge graph:
// https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages // https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
@ -48,51 +48,60 @@ static uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) {
return bat_mv * 2 / 15 - 459; return bat_mv * 2 / 15 - 459;
} }
#endif // IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE) static int get_state_of_charge(uint8_t *result) {
int rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_VOLTAGE);
static int zmk_battery_update(const struct device *battery) {
struct sensor_value state_of_charge;
int rc;
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE)
rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE);
if (rc != 0) { if (rc != 0) {
LOG_DBG("Failed to fetch battery values: %d", rc); LOG_DBG("Failed to fetch sensor value: %d", rc);
return rc;
}
rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge);
if (rc != 0) {
LOG_DBG("Failed to get battery state of charge: %d", rc);
return rc;
}
#elif IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE)
rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_VOLTAGE);
if (rc != 0) {
LOG_DBG("Failed to fetch battery values: %d", rc);
return rc; return rc;
} }
struct sensor_value voltage; struct sensor_value voltage;
rc = sensor_channel_get(battery, SENSOR_CHAN_VOLTAGE, &voltage); rc = sensor_channel_get(battery, SENSOR_CHAN_VOLTAGE, &voltage);
if (rc != 0) { if (rc != 0) {
LOG_DBG("Failed to get battery voltage: %d", rc); LOG_DBG("Failed to get battery voltage: %d", rc);
return rc; return rc;
} }
uint16_t mv = voltage.val1 * 1000 + (voltage.val2 / 1000); int millivolts = voltage.val1 * 1000 + (voltage.val2 / 1000);
state_of_charge.val1 = lithium_ion_mv_to_pct(mv); *result = lithium_ion_mv_to_pct(millivolts);
LOG_DBG("Battery voltage = %d mv -> %u%%", millivolts, *result);
return 0;
}
#elif IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE)
static int get_state_of_charge(uint8_t *result) {
struct sensor_value state_of_charge;
int rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE);
if (rc != 0) {
LOG_DBG("Failed to fetch sensor value: %d", rc);
return rc;
}
rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge);
if (rc != 0) {
LOG_DBG("Failed to get battery state of charge: %d", rc);
return rc;
}
*result = state_of_charge.val1;
return 0;
}
LOG_DBG("State of change %d from %d mv", state_of_charge.val1, mv);
#else #else
#error "Not a supported reporting fetch mode" #error "Unsupported reporting fetch mode"
#endif #endif
if (last_state_of_charge != state_of_charge.val1) { static int zmk_battery_update(const struct device *battery) {
last_state_of_charge = state_of_charge.val1; uint8_t new_state_of_charge = 0;
int rc = get_state_of_charge(&new_state_of_charge);
if (rc != 0) {
return rc;
}
if (new_state_of_charge != last_state_of_charge) {
last_state_of_charge = new_state_of_charge;
#if IS_ENABLED(CONFIG_BT_BAS) #if IS_ENABLED(CONFIG_BT_BAS)
LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge); LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge);

View file

@ -16,9 +16,18 @@ Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/
| `CONFIG_ZMK_BATTERY_REPORTING` | bool | Enables/disables all battery level detection/reporting | n | | `CONFIG_ZMK_BATTERY_REPORTING` | bool | Enables/disables all battery level detection/reporting | n |
| `CONFIG_ZMK_BATTERY_REPORT_INTERVAL` | int | Battery level report interval in seconds | 60 | | `CONFIG_ZMK_BATTERY_REPORT_INTERVAL` | int | Battery level report interval in seconds | 60 |
If `CONFIG_ZMK_BATTERY_REPORTING` is enabled, exactly zero or one of the following options may be set to `y`. The first is used if none are set. This should be set to match the type of sensor that is selected for the `zmk,battery` chosen node.
| Config | Description |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE` | Read a sensor that supports `SENSOR_CHAN_VOLTAGE` and estimate state of charge for a Li-Ion or LiPo battery. |
| `CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE` | Read a sensor that supports `SENSOR_CHAN_GAUGE_STATE_OF_CHARGE`. |
ZMK does not yet support Zephyr's recently added fuel gauge API.
:::note[Default setting] :::note[Default setting]
While `CONFIG_ZMK_BATTERY_REPORTING` is disabled by default it is implied by `CONFIG_ZMK_BLE`, thus any board with BLE enabled will have this automatically enabled unless explicitly overriden. While `CONFIG_ZMK_BATTERY_REPORTING` is disabled by default, it is implied by `CONFIG_ZMK_BLE`, thus any board with BLE enabled will have this automatically enabled unless explicitly overriden.
::: :::
@ -47,20 +56,26 @@ Applies to: [`/chosen` node](https://docs.zephyrproject.org/3.5.0/build/dts/intr
| ------------- | ---- | --------------------------------------------- | | ------------- | ---- | --------------------------------------------- |
| `zmk,battery` | path | The node for the battery sensor driver to use | | `zmk,battery` | path | The node for the battery sensor driver to use |
## Battery Voltage Divider Sensor ## Voltage Divider Sensor
Driver for reading the voltage of a battery using an ADC connected to a voltage divider. Driver for reading the voltage of a battery using an ADC connected to a voltage divider.
Set `CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE=y` when using this driver.
### Devicetree ### Devicetree
Applies to: `compatible = "zmk,battery-voltage-divider"` Applies to: `compatible = "voltage-divider"`
See [Zephyr's voltage divider documentation](https://docs.zephyrproject.org/3.5.0/build/dts/api/bindings/iio/afe/voltage-divider.html). See [Zephyr's voltage divider documentation](https://docs.zephyrproject.org/3.5.0/build/dts/api/bindings/iio/afe/voltage-divider.html).
ZMK also provides a `compatible = "zmk,battery-voltage-divider"` driver, which is deprecated. Use the improved driver from Zephyr instead.
## nRF VDDH Battery Sensor ## nRF VDDH Battery Sensor
Driver for reading the voltage of a battery using a Nordic nRF52's VDDH pin. Driver for reading the voltage of a battery using a Nordic nRF52's VDDH pin.
Set `CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE=y` when using this driver.
### Devicetree ### Devicetree
Applies to: `compatible = "zmk,battery-nrf-vddh"` Applies to: `compatible = "zmk,battery-nrf-vddh"`