Notifications
Clear all

Pico W ADC always reads zero. (Soil moisture project)

6 Posts
3 Users
0 Reactions
1,758 Views
(@dk55432)
Member
Joined: 3 years ago
Posts: 3
Topic starter  

Hello Forum members!  I recently picked up a few Pico W boards and have been successfully running the example programs inside the Arduino IDE.  I then picked up some soil moisture capacitive sensors with the goal of recreating Bill's IoT plant moisture sensor system.  But I'm stuck at the calibration step/sketch.  No matter what I do, the ADC reads zero, no exception.  I've even probed the pins with my voltmeter and I can see varying voltages 0.8 - 1.9v (between ground and ADC0 pins) as I bring my arm near the probe.  (Soil theremin, LOL.)  I've tried both my pico W boards, so it seems more likely that I'm doing something wrong.  Any suggestions?

For what it's worth, I've experimented using ADC0 vs 1 vs 2 (pins 31,32,34);  using 3v3_OUT (pin 36) vs ADC_VREF (pin 35) for power;  jumpering pins 35 & 36 together. 

I've googled around as usual, but haven't found a solution yet.  

Thanks for your help!



   
Quote
THRandell
(@thrandell)
Brain Donor
Joined: 5 years ago
Posts: 311
 

Posted by: @dk55432

(Soil theremin, LOL.) 

Ha, good one!

I'm curious, what voltage are you gettting from the ADC_VREF pin.

Tom


To err is human.
To really foul up, use a computer.


   
ReplyQuote
(@dk55432)
Member
Joined: 3 years ago
Posts: 3
Topic starter  

@thrandell Pin 35 (ADC_VREF) is +3.26v above pin 33 (GND).  Pins 35 and 36 (3v3(OUT)) appear identical on my voltmeter.



   
ReplyQuote
(@dk55432)
Member
Joined: 3 years ago
Posts: 3
Topic starter  

An update:  I got something working a different way, but I still lack understanding...

To simplify my problem, I removed all the probe stuff, and tried just reading ADC4 (the pico w's built-in temperature reading).  Again, thru the Arduino IDE, everything came back zeroes.

But then I followed a MicroPython tutorial, and this worked. The "adc = machine.ADC(4)" followed by "voltage = adc.read_u16()" gave me a reasonable and always-slightly-changing number.  Bingo!

I then used this same approach to read "machine.ADC(0)" (my soil probe), and to blink an LED when the probe is "too dry" (based on my wet/dry calibration measurements.)   This appears to be working fine.

So why didn't the Arduino IDE method of "sensorval = analogRead(0)" work?  It's almost like it's addressing the wrong pin..?   (I'm always uneasy about hardcoding numbers like "0", instead of using manufacturer-provided, board-specific constants like "ADC0" or "GPIO15")..      I'd appreciate whatever insight anyone can offer.  Thanks!

(p.s. I can post my microPython code if anyone wants it, but it essentially just follows the linked tutorials above.)



   
ReplyQuote
THRandell
(@thrandell)
Brain Donor
Joined: 5 years ago
Posts: 311
 

Hi @dk55432

This is something that I got to work in C.  Sorry about not paring it down to the basics, you can ignore all the LED stuff.  Hope if helps.

Tom 

/*

   Read the voltage divider on ADC2 and display the results on the three LEDs
   red = whole volts
   yellow = tenths of a volt
   green = hundredths of a volt

   I'm getting about 3.90V - 3.93V from the battery

*/

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"

#define LED_GN  14
#define LED_YW   9
#define LED_RD   8


uint32_t pwm_set_freq_duty(uint slice_num, uint chan, uint32_t f, int d)
{
  uint32_t clock = 125000000;
  uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0);
  if (divider16 / 16 == 0)
    divider16 = 16;
  uint32_t wrap = clock * 16 / divider16 / f - 1;
  pwm_set_clkdiv_int_frac(slice_num, divider16 / 16, divider16 & 0xF);
  pwm_set_wrap(slice_num, wrap);
  pwm_set_chan_level(slice_num, chan, wrap * d / 100);
  return wrap;
}

uint32_t pwm_get_wrap(uint slice_num)
{
  valid_params_if(PWM, slice_num >= 0 && slice_num < NUM_PWM_SLICES);
  return pwm_hw->slice[slice_num].top;
}

void pwm_set_duty(uint slice_num, uint chan, int d)
{
  pwm_set_chan_level(slice_num, chan, pwm_get_wrap(slice_num) * d / 100);
}

int main() {
  stdio_init_all();
  gpio_set_function(LED_GN, GPIO_FUNC_PWM);
  gpio_set_function(LED_YW, GPIO_FUNC_PWM);
  gpio_set_function(LED_RD, GPIO_FUNC_PWM);
  uint slice_LED1  = pwm_gpio_to_slice_num(LED_GN); // slice 7
  uint slice_LED23 = pwm_gpio_to_slice_num(LED_RD); // slice 4

  uint chan_LED_GN = pwm_gpio_to_channel(LED_GN); // channel 7A
  uint chan_LED_YW = pwm_gpio_to_channel(LED_YW); // channel 4B
  uint chan_LED_RD = pwm_gpio_to_channel(LED_RD); // channel 4A

  pwm_set_freq_duty(slice_LED1, chan_LED_GN, 60, 0);  // that's 60Hz for the LEDs
  pwm_set_freq_duty(slice_LED23, chan_LED_RD, 60, 0);  // that's 60Hz for the LEDs
  pwm_set_duty(slice_LED23, chan_LED_YW, 0);
  pwm_set_enabled(slice_LED1, true);
  pwm_set_enabled(slice_LED23, true);

  adc_init();
  adc_gpio_init(28);   // Make sure GPIO is high-impedance, no pullups etc
  adc_select_input(2); // Select ADC input 2 (GPIO28)

    while (1) {
        // 12-bit conversion, assume max value == ADC_VREF == 3.3 V
        const float conversion_factor = 3.3f / (1 << 12);  // shift the number 1 to the left 12 bits = 4096, so 0.00080566
        uint16_t result = adc_read();
        // float converted = result * conversion_factor * 2.0;  // the voltage divider gives halve the true battery voltage
        float converted = result * conversion_factor;  //  for testing of leds
        // this all seems kind of silly
        uint8_t whole_number = converted;
        uint8_t tenths = (converted - whole_number) * 10;
        uint8_t hundreds = ((converted - whole_number) * 100) - (tenths * 10);
        printf("result = %d, converted voltage = %f, whole number = %d, tenths = %d and hundreds = %d \n",result,converted,whole_number,tenths,hundreds);
sleep_ms(500);
        pwm_set_duty(slice_LED1, chan_LED_GN, 0);
        pwm_set_duty(slice_LED23, chan_LED_YW, 0);
        pwm_set_duty(slice_LED23, chan_LED_RD, 0);
        for (int x = 0; x < whole_number; x++) {
          pwm_set_duty(slice_LED23, chan_LED_RD, 50);
          sleep_ms(400);
          pwm_set_duty(slice_LED23, chan_LED_RD, 0);
          sleep_ms(400);
        }
        for (int y = 0; y < tenths; y++) {
          pwm_set_duty(slice_LED23, chan_LED_YW, 50);
          sleep_ms(400);
          pwm_set_duty(slice_LED23, chan_LED_YW, 0);
          sleep_ms(400);
        }
        for (int z = 0; z < hundreds; z++) {
          pwm_set_duty(slice_LED1, chan_LED_GN, 50);
          sleep_ms(400);
          pwm_set_duty(slice_LED1, chan_LED_GN, 0);
          sleep_ms(400);
        }
    }
  return 1;
}


 

 


To err is human.
To really foul up, use a computer.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 6 years ago
Posts: 8047
 

@dk55432 As a rule using supplied constants is advised over hard coded numbers. Not sure why you were fooling around with some of those other pins and shorting pins together, Bill's sketch and wiring diagram is very simple.


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote