Skip to content

Battery Monitoring System#1

Open
rob-robinson-14 wants to merge 2 commits into
health-monitoringfrom
health-monitoring-vbatt
Open

Battery Monitoring System#1
rob-robinson-14 wants to merge 2 commits into
health-monitoringfrom
health-monitoring-vbatt

Conversation

@rob-robinson-14
Copy link
Copy Markdown
Owner

@rob-robinson-14 rob-robinson-14 commented Feb 19, 2026

Initial review of health monitoring - Note WIP

Some comments in code to explain TODO / test features.

Implementation is in zephyr/soc/nordic/nrf54l/health_monitoring_nrfx

zephyr/soc/nordic/nrf54l/health_monitoring is a previous effort using zephyr rathen than nrfx but came into complications here.

To test / demo I have been using west build -b nrf54l15dk/nrf54l15/cpuapp ~/Documents/nrf7120/zephyr/samples/basic/blinky -DCONFIG_VBAT_MONITOR_ENABLE=y -p

/* TODO: If def around for measure battery and temp ADC
* Need to update to correct pins.
*/
NRFX_SAADC_DEFAULT_CHANNEL_SE(NRFX_ANALOG_EXTERNAL_AIN6, 0),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the reference voltage, gain, divider etc. be configurable? E.g if the battery voltage is above the reference.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup some more configuration will be required. For now just looking at overall functionality of the feature. atm its just on a GPIO pin rather than internal battery (so we can test) but at a later stage will move to battery and ensure adc is configured correctly with everything required and get you to review. - I shall add a TODO to reflect this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what's that best way to configure the ADC's considering you aren't using devicetree here

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above is what they show in NRFX examples but happy to consider other options.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have updated this to use a config_t - think it improves clarity and gives us flexibility if we need it


/** @brief SAADC channel to measure battery level */
static const nrfx_saadc_channel_t m_multiple_channels[] = {
/* TODO: If def around for measure battery and temp ADC
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does temp here mean temporary or temperature? Temperature uses a separate "temp" peripheral right?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there are two temperatures to be measured, one is its own peripheral. The other I am not exactly sure - need to sync with Mike on this to see if its part of a WiFi core or an ADC use.

Comment thread soc/nordic/nrf54l/health_monitoring_nrfx/Kconfig Outdated
Comment thread soc/nordic/nrf54l/health_monitoring_nrfx/health_monitoring.c Outdated
Comment thread soc/nordic/nrf54l/health_monitoring_nrfx/battery_monitor.c Outdated
*
* @return Pointer to atomic variable holding battery voltage
*/
atomic_t *get_battery_voltage_pointer(void)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of exposing the pointer in the API, can we expose a read api? something like:

int32_t battery_monitor_get_vbat_mv(void)
{
    return (int32_t)atomic_get(&battery_voltage);
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request from WiFi is to have a struct of pointers that they can access for each of these metrics directly.

Given the values are 32bit I think all reads / writes will be atomic by default, so atomic_t is just belts and braces.

However the reads and writes are from different cores and so I am not sure if we still have an issue here and which point perhaps we need ICMsg of some form.

Agreed that we should have a get function as well (although unlikely to be used on app core).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify, the Wi-Fi driver running on APP core will use these APIs and then inform the Wi-Fi firmware via IPC mechanism. So, we don't need multi-core access.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect, Wi-Fi driver is only passing the address, and Wi-Fi core (LMAC) is reading periodically, so, @rob-robinson-14 you are right that we need race-free multi-core access. I also don't know how it works when APP is in low power state (if Wi-Fi is active we cannot fully suspend, so, may not be a problem).

@rob-robinson-14 rob-robinson-14 force-pushed the health-monitoring-vbatt branch 6 times, most recently from 67d41a9 to 98d4071 Compare February 23, 2026 17:52
for (;;) {
k_sleep(K_SECONDS(2));
// uint32_t voltage = atomic_get(g_vbatt_value);
printk("Global ADC: %ld mV\n", *g_vbatt_value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the values are defined as atomic variables. Would it be more safe to get the value via atomic_get? Same for g_die_temp_value.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that would be more safe - I had similar comments from David. But in actual implementation we will be getting values from wifi core and so no Zephyr OS. Here I am just trying to prove that what I am doing is safe. Since the values are only 32bit the only impact from the race condition is that we get the old value - but for the context of wifi team, this is okay.

The only way to make this "safe" is to have some kind of ICMsg to request / notify updates but we are trying to reduce this overhead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Yeah, the RISC-V in Wi-Fi does not natively support atomic instruction anyway, and even then it is a different RTOS to Zephyr. Getting the old value from race condition does seem justified in that case.

Comment thread soc/nordic/nrf54l/health_monitoring_nrfx/temperature_monitor.c Outdated
Comment thread soc/nordic/nrf54l/health_monitoring_nrfx/health_monitoring.c Outdated
Comment thread soc/nordic/nrf54l/health_monitoring_nrfx/health_monitoring.c Outdated
@rob-robinson-14 rob-robinson-14 force-pushed the health-monitoring-vbatt branch 2 times, most recently from d699165 to 28e93be Compare February 25, 2026 12:22
@rob-robinson-14 rob-robinson-14 force-pushed the health-monitoring-vbatt branch from 28e93be to 0093763 Compare March 17, 2026 13:03
@github-actions
Copy link
Copy Markdown

rob-robinson-14 pushed a commit that referenced this pull request May 27, 2026
I'm at a loss here.  The feature this test case wants to see (on
ARMv7M, not ARMv6M) is the ability to take a irq_lock() inside a
system call and then see that future system calls from the same thread
continue to hold the lock.

That's not documented AFAICT.  It's also just a terrible idea because
either:

1. The obvious denial of service implications if user code is allowed
   to run in an unpreemptible mode, or:

2. The broken locking promise if this is implemented to release the
   lock and reacquire it in an attempt to avoid #1.

(FWIW: my read of the code is that #1 is the current implementation.
But hilariously the test isn't able to tell the difference!)

And in any case it's not how any of our other platforms work (or can
work, in some cases), making this a non-portable system call
API/feature at best.

Leave it in place for now out of conservatism, but disable with the
new arch_switch() code, whose behavior matches that of other Zephyr
userspaces.

Signed-off-by: Andy Ross <andyross@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants