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.
    • N Offline
      Nick
      last edited by sakis

      Hi Guys,

      Has anyone has any success getting the ADCs to work? I thought I'd share some things here which may be helpful for others. I also have some questions that someone may be able to help with.

      • I assume that GND pin on the board is the reference 0V for the ADC. (Can someone confirm this?)
      • By default, It looks like the ADC is set up to operate from 0-1V (from experimentation)
      • Even though the specs say 12 bit, it looks like it is currently implemented at 10 bit (Max reading 1023)
      from machine import ADC
      adc = ADC(0)
      ram_pressure = adc.channel(pin='P13')
      ram_pressure.value()
      

      Given the above, everything works as expected - except for the fact that given a constant voltage applied to an ADC pin, I get big variations if I poll for the value. For example, Given a constant 357mV applied between GND and P13, I get ADC values anywhere between 293 and 312. I'm not sure if something is wrong with my board, or I'm missing something - but any help here would be appreciated. So far the ADCs don't seem very useful for real world applications.

      I believe the ADC on the ESP32 is highly configurable so the initial implementation on the LoPy may not represent it's full capabilities just yet, but at present seems very inaccurate...

      Please let me know if anyone else has tested this, along with your results, or any suggestions.

      Cheers,
      Nick

      1 Reply Last reply Reply Quote 3
      • J Offline
        Jiemde Pybytes Beta
        last edited by

        Hi Nick,
        As an electronic engineer, I immediately think that it can be the alimentation of the board itself, because for an Analog to digital conversion a référence is setting from the power supply, so if the ripple on this power can be the result of the fluctuating value .
        Could you mesure this ripple ?

        Best regards

        jiemde

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

          Hi jiemde,

          Thanks for the reply. I have just now connected a battery to the ADC instead of a power supply. (Perhaps the power supply I was using to connect to the ADC may have had some noise). I'm now testing the ADC using a flat AAA battery that is around 550mV. When I read the ADC value now, I'm getting anywhere between 490 and 520, which is a similar fluctuation to before.

          Can anyone confirm if I'm right in assuming the GND on the board is the 0V ref for the ADC?

          I will try to set up an oscilloscope tomorrow and see if anything strange is happening...

          Nick

          1 Reply Last reply Reply Quote 0
          • rcolisteteR Offline
            rcolistete
            last edited by rcolistete

            I confim by testing that currently firmware (0.9.6.b1) implements the ADC with range 0.0-1.0V and 10 bits (0-1023 values). While ESP32 specs shows 12 bits and selectable ranges 0-1V, 0-1.4V, 0-2V, or 0-4V. So I think is some Pycom choice to implement this simplified configuration in these beta releases.

            About the ADC noise, I have tested this script on WiPy 2 (LoPy should be the same) :

            import machine
            import math
            numADCreadings = const(100)
            def ADCloopMeanStdDev():
                adc = machine.ADC(0)
                adcread = adc.channel(pin='P13')
                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-1000 mV) = %15.13f" % (meanADC*1000/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)))
                print("Standard deviation of ADC readings = %15.13f" % math.sqrt(varianceADC))
            

            A typical run, reading 3.3V (from MCU board connected to USB cable) with voltage divider (82K + 33K resistors), is :

            >>> ADCloopMeanStdDev()
            100 ADC readings :
            [908, 905, 903, 912, 907, 906, 908, 908, 909, 908, 911, 907, 906, 910, 907, 907, 903, 905, 909, 905, 908, 908, 909, 908, 911, 905, 907, 905, 901, 901, 908, 905, 913, 904, 906, 908, 911, 908, 911, 911, 910, 907, 906, 907, 908, 913, 906, 929, 868, 911, 915, 940, 916, 909, 905, 909, 904, 907, 920, 901, 909, 912, 903, 909, 915, 911, 915, 904, 911, 947, 913, 910, 849, 912, 913, 912, 908, 900, 905, 908, 901, 906, 911, 908, 907, 902, 911, 905, 907, 907, 911, 915, 904, 909, 909, 909, 907, 906, 913, 905]
            Mean of ADC readings (0-1023) = 908.0200195312500
            Mean of ADC readings (0-1000 mV) = 886.7383003234863
            Variance of ADC readings = 94.4035530090332
            10**6*Variance/(Mean**2) of ADC readings = 113.9999985694885
            Standard deviation of ADC readings = 9.7161493301392
            

            But putting a 100nF capacitor between the ADC pin and GND, the noise is decresead a lot :

            >>> ADCloopMeanStdDev()
            100 ADC readings :
            [909, 908, 906, 909, 908, 911, 909, 908, 907, 908, 908, 910, 908, 913, 909, 908, 909, 907, 909, 909, 908, 907, 909, 909, 909, 909, 909, 908, 909, 911, 895, 907, 908, 908, 909, 906, 905, 908, 913, 907, 911, 907, 909, 909, 908, 908, 908, 909, 908, 909, 908, 908, 908, 908, 908, 907, 908, 904, 908, 906, 909, 908, 907, 908, 913, 911, 909, 910, 908, 907, 909, 907, 909, 908, 911, 906, 911, 908, 908, 908, 911, 908, 914, 910, 908, 906, 911, 905, 908, 906, 908, 906, 908, 908, 907, 908, 908, 909, 908, 908]
            Mean of ADC readings (0-1023) = 908.2599639892578
            Mean of ADC readings (0-1000 mV) = 886.9726181030273
            Variance of ADC readings = 4.5781769752502
            10**6*Variance/(Mean**2) of ADC readings = 5.0000000000000
            Standard deviation of ADC readings = 2.1396675109863
            

            By comparison :

            • WiPy 1.0 ADC (12 bits, 0-1.467V) without capacitor gives "10^6*Variance/(Mean^2)" between 8-50, with capacitor < 1;
            • WiPy 2 ADC (10 bits, 0-1V), 52-189 and 3-7, respectively;
            • Pyboard v1.1 ADC (12 bits, 0-3.3V), 22-65 and 3-4, respectively;
            • WeMos D1 Mini/ESP8266 ADC (10 bits, 0-3.2V), 0-1 and 0, respectively.

            So WiPy 2/LoPy ADC currently has more noise (with or without capacitor) than WiPy 1, Pyboard and WeMos D1 Mini/ESP8266.

            For WiPy and its MCU, CC3200, there is Texas Instruments documentation about the internal capacitance of CC3200 ADC, recommended configuration of external capacitor, etc.

            For ESP32 I've not yet seen any similar documentation.

            PS.: improved the above code a little.

            My "MicroPython Samples". My "MicroPython Firmwares" with many options (double precision, ulab, etc).

            B 1 Reply Last reply Reply Quote 2
            • 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