Boron Low ADC Readings

Tags: #<Tag:0x00007fe21fb83b08>


@rickkas7 - should we expect any variance from 4095 when analogRead()ing a pin grabbing 3.3V from itself?


@marekparticle, the ADC reference is the 3.3v rail so measuring 3.3v on an analog pin should result in only a one bit error at most.


@peekay123, understood. But, I’m struck by the fact that this has occurred on more than one Boron. I… must be missing something.


@marekparticle, it is incredibly odd. It may be worth looking at the 3.3v rail to make sure it is clean. It might be worth testing with a known-clean external 3.3v source. You may also want to look at the boards for any manufacturing anomalies like excess flux, etc. My understanding is that all things being equal (DeviceOS, app), “older” Borons work fine but now these “new” ones. So software issues can most likely be eliminated.


@Rftop - can you PM me device IDs so I can check mfg date?


I tried this out on a Boron LTE running 1.4.2. I connected A2 to 3V3 and the values are not 4095.

0000012309 [app] INFO: ADC: 4090
0000019311 [app] INFO: ADC: 4087
0000026312 [app] INFO: ADC: 4092
0000033314 [app] INFO: ADC: 4094
0000040316 [app] INFO: ADC: 4087
0000047318 [app] INFO: ADC: 4091
0000054319 [app] INFO: ADC: 4093
0000061321 [app] INFO: ADC: 4092
0000068323 [app] INFO: ADC: 4094
0000075325 [app] INFO: ADC: 4089
0000082326 [app] INFO: ADC: 4095
0000089327 [app] INFO: ADC: 4087

This was also the case with the other ADC inputs, and also using D2 as an output.

By the way, using a GPIO as a voltage reference is not guaranteed to work with the nRF52, because the output voltage spec says VOH,SD can be from VDD-0.4 to VDD volts. In other words, GPIO HIGH may not be exactly VDD.

However, after forcing the calibration of the nRF52 SAADC, I got much better values. There were a few outliers, but they were generally close to 4095 and less frequent:

0000003070 [app] INFO: calibration complete
0000003072 [app] INFO: A2=4095
0000004074 [app] INFO: A2=4095
0000005076 [app] INFO: A2=4095
0000006077 [app] INFO: A2=4094
0000007079 [app] INFO: A2=4095
0000008080 [app] INFO: A2=4095
0000009082 [app] INFO: A2=4079
0000010084 [app] INFO: A2=4095
0000011085 [app] INFO: A2=4095
0000012087 [app] INFO: A2=4093
0000013088 [app] INFO: A2=4095
0000014090 [app] INFO: A2=4095
0000015091 [app] INFO: A2=4095
0000016093 [app] INFO: A2=4095
0000017095 [app] INFO: A2=4095
0000018097 [app] INFO: A2=4095
0000019099 [app] INFO: A2=4095
0000020101 [app] INFO: A2=4095
0000021102 [app] INFO: A2=4095
0000022104 [app] INFO: A2=4093
0000023106 [app] INFO: A2=4095

Here’s the code:

#include "Particle.h"

#include "nrfx_saadc.h"

SerialLogHandler logHandler;


void adcCalibrate();

void setup() {

	// Wait for a USB serial connection for up to 10 seconds
	waitFor(Serial.isConnected, 10000);

	// Calibrate the ADC at startup. You might want to do this after large temperature
	// changes as well.

void loop() {
	int a2 = analogRead(A2);"A2=%d", a2);

static void adcCallback(nrfx_saadc_evt_t const *event) {
	// We just poll for done during calibration instead of using this event but the
	// callback still needs to be defined.

void adcCalibrate() {
	nrfx_err_t err;

	// IMPORTANT: You must detach the IRQ handler from the system firmware and re-attach it to
	// user firmware, or this won't work right.
	attachInterruptDirect(SAADC_IRQn, nrfx_saadc_irq_handler, false);

	nrfx_saadc_config_t saadcConfig = NRFX_SAADC_DEFAULT_CONFIG;
	saadcConfig.low_power_mode = false;
	saadcConfig.resolution = NRF_SAADC_RESOLUTION_12BIT;
	saadcConfig.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
	saadcConfig.interrupt_priority = APP_IRQ_PRIORITY_LOW;

	// Initialize SAADC
	err = nrfx_saadc_init(&saadcConfig, adcCallback);
	if (err) {
		Log.error("nrfx_saadc_init err=%lu", err);

	// Now calibrate it. This does not require a channel be initialized first.
	err = nrfx_saadc_calibrate_offset();
	if (err == NRFX_SUCCESS) {
		while(nrfx_saadc_is_busy()) {
		}"calibration complete");
	else {"calibrate failed err=%lu", err);

We should probably put ADC calibration right into Device OS, but this is probably a reasonable thing to play around with and see if it helps in your case to be sure that’s what the problem is.


@rickkas7 @marekparticle
I tried the code on two different Borons and still am getting mixed results:
Boron #1
0000002139 [app] INFO: A2=4028
0000003140 [app] INFO: A2=4023
0000004141 [app] INFO: A2=4026
0000005143 [app] INFO: A2=4029
0000006144 [app] INFO: A2=4026
0000007146 [app] INFO: A2=4028
0000008147 [app] INFO: A2=4019
0000009148 [app] INFO: A2=4026
0000010149 [app] INFO: A2=4023
0000011150 [app] INFO: A2=4025
0000012152 [app] INFO: A2=4024
0000013153 [app] INFO: A2=4020
0000014155 [app] INFO: A2=4023

Boron #2
0000002058 [app] INFO: A2=4067
0000003059 [app] INFO: A2=4067
0000004060 [app] INFO: A2=4070
0000005061 [app] INFO: A2=4072
0000006062 [app] INFO: A2=4067
0000007064 [app] INFO: A2=4070
0000008065 [app] INFO: A2=4068
0000009066 [app] INFO: A2=4066
0000010067 [app] INFO: A2=4063
0000011069 [app] INFO: A2=4065
0000012070 [app] INFO: A2=4066



Is it printing out the calibration complete message before outputting the samples? Or is there an error code? It looks like it didn’t successfully calibrate.


I cut that piece of the output by accident. I just ran it again:
0000009280 [app] INFO: calibration complete
0000009282 [app] INFO: A2=4066
0000010283 [app] INFO: A2=4067
0000011284 [app] INFO: A2=4065
0000012286 [app] INFO: A2=4070
0000013287 [app] INFO: A2=4064
0000014288 [app] INFO: A2=4069
0000015289 [app] INFO: A2=4068
0000016291 [app] INFO: A2=4063
0000017292 [app] INFO: A2=4066

Any ideas?