#include #include #include #define MODULE battery_module #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); #define VBATT_NODE DT_PATH(vbatt) #define IP5306_NODE DT_NODELABEL(ip5306) #define BATTERY_SAMPLE_INTERVAL K_SECONDS(1) BUILD_ASSERT(DT_NODE_HAS_STATUS(VBATT_NODE, okay), "Missing /vbatt voltage-divider node in devicetree"); BUILD_ASSERT(DT_NODE_HAS_STATUS(IP5306_NODE, okay), "Missing ip5306 node in devicetree"); static const struct device *const vbatt_dev = DEVICE_DT_GET(VBATT_NODE); static const struct device *const ip5306_dev = DEVICE_DT_GET(IP5306_NODE); static struct k_work_delayable battery_sample_work; static bool initialized; static bool running; static int sensor_value_to_mv(const struct sensor_value *value) { return (value->val1 * 1000) + (value->val2 / 1000); } static int measurement_enable(bool enable) { enum pm_device_action action = enable ? PM_DEVICE_ACTION_RESUME : PM_DEVICE_ACTION_SUSPEND; int err = pm_device_action_run(vbatt_dev, action); if (err && (err != -EALREADY) && (err != -ENOTSUP)) { LOG_ERR("Cannot %s vbatt sensor (%d)", enable ? "resume" : "suspend", err); return err; } return 0; } static void battery_sample_fn(struct k_work *work) { struct ip5306_status pmic_status; struct sensor_value voltage; int voltage_mv; int err; ARG_UNUSED(work); if (!running) { return; } err = sensor_sample_fetch(vbatt_dev); if (err) { LOG_WRN("Battery sample fetch failed (%d)", err); goto reschedule; } err = sensor_channel_get(vbatt_dev, SENSOR_CHAN_VOLTAGE, &voltage); if (err) { LOG_WRN("Battery channel get failed (%d)", err); goto reschedule; } err = ip5306_get_status(ip5306_dev, &pmic_status); if (err) { LOG_WRN("IP5306 status read failed (%d)", err); goto reschedule; } voltage_mv = sensor_value_to_mv(&voltage); LOG_INF("Battery: %d mV, charging=%d, full=%d", voltage_mv, pmic_status.charging, pmic_status.full); reschedule: if (running) { k_work_reschedule(&battery_sample_work, BATTERY_SAMPLE_INTERVAL); } } static int module_init(void) { if (!device_is_ready(vbatt_dev)) { LOG_ERR("vbatt device not ready"); return -ENODEV; } if (!device_is_ready(ip5306_dev)) { LOG_ERR("ip5306 device not ready"); return -ENODEV; } int err = ip5306_init(ip5306_dev); if (err) { LOG_ERR("ip5306 init failed (%d)", err); return err; } k_work_init_delayable(&battery_sample_work, battery_sample_fn); power_manager_restrict(MODULE_IDX(MODULE), POWER_MANAGER_LEVEL_SUSPENDED); return 0; } static int module_start(void) { int err; if (running) { return 0; } err = measurement_enable(true); if (err) { return err; } running = true; k_work_reschedule(&battery_sample_work, K_NO_WAIT); return 0; } static void module_pause(void) { if (!running) { return; } (void)k_work_cancel_delayable(&battery_sample_work); (void)measurement_enable(false); running = false; } static bool app_event_handler(const struct app_event_header *aeh) { if (is_module_state_event(aeh)) { const struct module_state_event *event = cast_module_state_event(aeh); if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { int err; if (!initialized) { err = module_init(); if (err) { module_set_state(MODULE_STATE_ERROR); return false; } initialized = true; } err = module_start(); if (err) { module_set_state(MODULE_STATE_ERROR); } else { module_set_state(MODULE_STATE_READY); } } return false; } if (is_power_down_event(aeh)) { if (initialized) { module_pause(); module_set_state(MODULE_STATE_STANDBY); } return false; } if (is_wake_up_event(aeh)) { if (initialized) { int err = module_start(); if (err) { module_set_state(MODULE_STATE_ERROR); } else { module_set_state(MODULE_STATE_READY); } } return false; } __ASSERT_NO_MSG(false); return false; } APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);