Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    LoPy ADCs

    Scheduled Pinned Locked Moved Discussion
    17 Posts 11 Posters 12.8k Views 14 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B Offline
      brotherdust @rcolistete
      last edited by

      @rcolistete I was working on setting up battery monitoring today. I have a 3.7v Li-Ion battery (nominal 4v). The voltage divider built in to the expansion board brings this down to ~1.4v for the ADC on P16. By default, the attenuation on an ADC pin is 0. So, when above 1v, the ADC just reads 1023. Kinda useless, right?

      Your mention of selectable ADC ranges the ESP32 offers pointed me in the right direction. I found an "undocumented feature" that Pycom put in there, but hasn't documented yet.

      Here's the relevant section from the source: https://github.com/pycom/pycom-micropython/blob/master/esp32/mods/analog.h#L56

      typedef enum {
          ADC_ATTEN_0DB = 0,
          ADC_ATTEN_3DB, // 1
          ADC_ATTEN_6DB, // 2
          ADC_ATTEN_12DB, // 3
          ADC_ATTEN_MAX, // 4
      } adc_atten_t;
      

      So, to set this up:

      from machine import ADC
      adc = ADC()
      batt = adc.channel(attn=1, pin='P16')
      batt.value()
      

      3dB attenuation should be sufficient for our purposes. The readings have the same variability that @rcolistete noticed. I adjusted his function for 3dB attenuation (rounded to 1400mV).

      import machine
      numADCreadings = const(100)
      def ADCloopMeanStdDev():
          adc = machine.ADC(0)
          adcread = adc.channel(attn=1, pin='P16')
          samplesADC = [0.0]*numADCreadings; meanADC = 0.0
          i = 0
          while (i < numADCreadings):
              adcint = adcread()
              samplesADC[i] = adcint
              meanADC += adcint
              i += 1
          meanADC /= numADCreadings
          varianceADC = 0.0
          for adcint in samplesADC:
              varianceADC += (adcint - meanADC)**2
          varianceADC /= (numADCreadings - 1)
          print("%u ADC readings :\n%s" %(numADCreadings, str(samplesADC)))
          print("Mean of ADC readings (0-1023) = %15.13f" % meanADC)
          print("Mean of ADC readings (0-1400 mV) = %15.13f" % (meanADC*1400/1024))
          print("Variance of ADC readings = %15.13f" % varianceADC)
          print("10**6*Variance/(Mean**2) of ADC readings = %15.13f" % ((varianceADC*10**6)//(meanADC**2)))
      

      And the output:

      >>> ADCloopMeanStdDev()
      100 ADC readings :
      [1013, 1015, 1012, 1015, 1013, 1014, 1015, 1017, 1019, 1017, 1015, 1013, 1021, 1013, 1018, 1017, 1015, 1015, 1012, 1017, 1013, 1012, 1013, 1013, 1015, 1020, 1013, 1017, 1013, 1012, 1014, 1018, 1015, 1019, 1020, 1017, 1015, 1016, 1017, 1015, 1007, 1014, 1017, 1017, 1015, 1012, 1014, 1015, 1012, 1012, 1015, 1020, 1018, 1019, 1012, 1019, 1017, 1013, 1017, 1014, 1015, 1012, 1003, 1015, 1013, 1015, 1012, 1015, 1013, 1019, 1015, 1015, 1015, 1012, 1016, 1013, 1018, 1014, 1015, 1017, 1021, 1017, 1014, 1015, 1017, 1013, 1018, 1019, 1013, 1015, 1014, 1019, 1015, 1020, 1011, 1013, 1013, 1017, 1013, 1013]
      Mean of ADC readings (0-1023) = 1015.0399208068847
      Mean of ADC readings (0-1400 mV) = 1387.7500343322753
      Variance of ADC readings = 8.0589838027954
      10**6*Variance/(Mean**2) of ADC readings = 7.0000000000000
      

      At time of measurement, my Fluke 179 MM read 1.367V. So, pretty close, I'd say!

      Hopefully this has been helpful! Now, if I may ask a question in return: given these measurements, how can I get a reasonably accurate idea of remaining battery capacity? I'm sure there are libraries out there that do this already, but wanted to see if any of you had some suggestions first!

      Thanks!

      bmarkusB A 2 Replies Last reply Reply Quote 5
      • bmarkusB Offline
        bmarkus @brotherdust
        last edited by bmarkus

        @brotherdust said in LoPy ADCs:

        Hopefully this has been helpful! Now, if I may ask a question in return: given these measurements, how can I get a reasonably accurate idea of remaining battery capacity? I'm sure there are libraries out there that do this already, but wanted to see if any of you had some suggestions first!

        Usually they are measuring the characteristics discharging battery from fully charge to zero (OK, it is not zero but to a minimum allowed voltage) and using collected data for estimation. It is a seamless process, but e.g. on Android there are so called battery calibrators too for users.

        1 Reply Last reply Reply Quote 0
        • N Offline
          Nick
          last edited by

          Thanks for all the replies.
          After much more testing I have reverted back to a pyboard for the particular project I was working on, losing 2 bits of resolution is a pretty big limitation, so hopefully this gets sorted at some point. Thanks @brotherdust for pointing out about the attentuation settings, that will be helpful in the future.

          Also in regards to @Jiemde initial suggestion, that the ref for the ADC is probably the power supply on the board - I think this is probably the answer to the fluctuating value. When I come back to the lopy I will experiment with some capacitors on the powersupply to the board and run the ADCloopMeanStdDev() function and compare results to the ones already posted here.

          Cheers

          A 1 Reply Last reply Reply Quote 0
          • A Offline
            abilio @Nick
            last edited by

            Hi @Nick, 10 bit ADC is a known limitation at the moment. We're working with the ESP32 manufacturer on enabling 12 bits plus the internal attenuation (will enable measurements of up to 4.4V). As soon as this changes we will release a firmware upgrade. I'm going to modify the documentation to include this as a note in the ADC section. Thanks.

            B M Y 3 Replies Last reply Reply Quote 2
            • B Offline
              brotherdust @abilio
              last edited by

              @abilio said in LoPy ADCs:

              Hi @Nick, 10 bit ADC is a known limitation at the moment. We're working with the ESP32 manufacturer on enabling 12 bits plus the internal attenuation (will enable measurements of up to 4.4V). As soon as this changes we will release a firmware upgrade. I'm going to modify the documentation to include this as a note in the ADC section. Thanks.

              @abilio, may I make some suggestions on the next iteration of the expansion board (2.1?) (as well as the PySense and PyTrack)?

              1. Add a hard switch in-line with the battery (either a slider or latching), or change the board layout slightly and leave the traces so it can be added after-market.
              2. Add something like a BQ27421-G1 for battery monitoring functions. This would be MUCH better than monitoring it through ADC as all the engineering (good engineering, I might add) has already been done. Why re-invent the wheel? =)
              1 Reply Last reply Reply Quote 0
              • M Offline
                manu @abilio
                last edited by

                @abilio Do you have an update on the 10bits - 12bits problem. We have a project were the 12bits ADC is mandatory.
                Where does the problem come from ?
                Do you have an idea when the problem might be solved ?

                Thanks for the update.

                A 1 Reply Last reply Reply Quote 0
                • Y Offline
                  yahia @abilio
                  last edited by

                  @abilio
                  Regarding the fluctuation of the ADC values, is there a reason behind it and will it be fixed?

                  1 Reply Last reply Reply Quote 0
                  • A Offline
                    abilio @manu
                    last edited by abilio

                    @manu, sorry for the silence, after your post I went into checking what could be done to get 12 bit reads.

                    The good news is that it seems to be working now. Tomorrow there will be a release, that includes the fix to the issue we found :)

                    L Y 2 Replies Last reply Reply Quote 3
                    • L Offline
                      livius @abilio
                      last edited by

                      @abilio
                      this is really good news :)
                      ADC is really important for us

                      think about people first, then computers

                      1 Reply Last reply Reply Quote 0
                      • A Offline
                        acheesehead @brotherdust
                        last edited by

                        The attenuation of 4 does not work. 0 to 3 do.

                        apin=adc.channel(pin='P13', attn=4)
                        Traceback (most recent call last):
                        File "<stdin>", line 1, in <module>
                        ValueError: invalid argument(s) value

                        @brotherdust said in LoPy ADCs:

                        @rcolistete I was working on setting up battery monitoring today. I have a 3.7v Li-Ion battery (nominal 4v). The voltage divider built in to the expansion board brings this down to ~1.4v for the ADC on P16. By default, the attenuation on an ADC pin is 0. So, when above 1v, the ADC just reads 1023. Kinda useless, right?

                        Your mention of selectable ADC ranges the ESP32 offers pointed me in the right direction. I found an "undocumented feature" that Pycom put in there, but hasn't documented yet.

                        Here's the relevant section from the source: https://github.com/pycom/pycom-micropython/blob/master/esp32/mods/analog.h#L56

                        typedef enum {
                            ADC_ATTEN_0DB = 0,
                            ADC_ATTEN_3DB, // 1
                            ADC_ATTEN_6DB, // 2
                            ADC_ATTEN_12DB, // 3
                            ADC_ATTEN_MAX, // 4
                        } adc_atten_t;
                        

                        So, to set this up:

                        from machine import ADC
                        adc = ADC()
                        batt = adc.channel(attn=1, pin='P16')
                        batt.value()
                        

                        3dB attenuation should be sufficient for our purposes. The readings have the same variability that @rcolistete noticed. I adjusted his function for 3dB attenuation (rounded to 1400mV).

                        import machine
                        numADCreadings = const(100)
                        def ADCloopMeanStdDev():
                            adc = machine.ADC(0)
                            adcread = adc.channel(attn=1, pin='P16')
                            samplesADC = [0.0]*numADCreadings; meanADC = 0.0
                            i = 0
                            while (i < numADCreadings):
                                adcint = adcread()
                                samplesADC[i] = adcint
                                meanADC += adcint
                                i += 1
                            meanADC /= numADCreadings
                            varianceADC = 0.0
                            for adcint in samplesADC:
                                varianceADC += (adcint - meanADC)**2
                            varianceADC /= (numADCreadings - 1)
                            print("%u ADC readings :\n%s" %(numADCreadings, str(samplesADC)))
                            print("Mean of ADC readings (0-1023) = %15.13f" % meanADC)
                            print("Mean of ADC readings (0-1400 mV) = %15.13f" % (meanADC*1400/1024))
                            print("Variance of ADC readings = %15.13f" % varianceADC)
                            print("10**6*Variance/(Mean**2) of ADC readings = %15.13f" % ((varianceADC*10**6)//(meanADC**2)))
                        

                        And the output:

                        >>> ADCloopMeanStdDev()
                        100 ADC readings :
                        [1013, 1015, 1012, 1015, 1013, 1014, 1015, 1017, 1019, 1017, 1015, 1013, 1021, 1013, 1018, 1017, 1015, 1015, 1012, 1017, 1013, 1012, 1013, 1013, 1015, 1020, 1013, 1017, 1013, 1012, 1014, 1018, 1015, 1019, 1020, 1017, 1015, 1016, 1017, 1015, 1007, 1014, 1017, 1017, 1015, 1012, 1014, 1015, 1012, 1012, 1015, 1020, 1018, 1019, 1012, 1019, 1017, 1013, 1017, 1014, 1015, 1012, 1003, 1015, 1013, 1015, 1012, 1015, 1013, 1019, 1015, 1015, 1015, 1012, 1016, 1013, 1018, 1014, 1015, 1017, 1021, 1017, 1014, 1015, 1017, 1013, 1018, 1019, 1013, 1015, 1014, 1019, 1015, 1020, 1011, 1013, 1013, 1017, 1013, 1013]
                        Mean of ADC readings (0-1023) = 1015.0399208068847
                        Mean of ADC readings (0-1400 mV) = 1387.7500343322753
                        Variance of ADC readings = 8.0589838027954
                        10**6*Variance/(Mean**2) of ADC readings = 7.0000000000000
                        

                        At time of measurement, my Fluke 179 MM read 1.367V. So, pretty close, I'd say!

                        Hopefully this has been helpful! Now, if I may ask a question in return: given these measurements, how can I get a reasonably accurate idea of remaining battery capacity? I'm sure there are libraries out there that do this already, but wanted to see if any of you had some suggestions first!

                        Thanks!

                        A 1 Reply Last reply Reply Quote 0
                        • Y Offline
                          yahia @abilio
                          last edited by

                          @abilio This is great news! thanks!

                          1 Reply Last reply Reply Quote 0
                          • A Offline
                            abilio @acheesehead
                            last edited by

                            @acheesehead said in LoPy ADCs:

                            The attenuation of 4 does not work. 0 to 3 do.

                            Yes, ADC_ATTEN_MAX is just there as a constant for internal use of the C code (like comparing if the attenuation parameter is right). 12 dB is the highest achievable attenuation. Hope this clarifies the point. :)

                            1 Reply Last reply Reply Quote 1
                            • O Offline
                              obertuckerz112
                              last edited by

                              Some LoPy specs and performance that it would be useful to know, theoretically and measured :

                              • flash memory available for MicroPython user;
                              • LoRa speed and range in real user cases;
                              • Bluetooth speed and range in real user cases.
                              1 Reply Last reply Reply Quote 0

                              Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                              Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                              With your input, this post could be even better 💗

                              Register Login
                              • 1 / 1
                              • First post
                                Last post

                              Pycom on Twitter

                              Pycom