firmata / arduino

Firmata firmware for Arduino
GNU Lesser General Public License v2.1
1.55k stars 516 forks source link

Multiple analog reads mess up ADC on Arduino UNO #334

Open webondevices opened 7 years ago

webondevices commented 7 years ago

There is a well-known problem with the Arduino UNO ADC (very likely affects other boards as well) that multiple analogRead() performed on different pins with no delay won't give enough time for the ADC to "cool down" and measurements will be completely wrong.

I discovered this issue while trying to make the LM35 temp sensor and an LDR measure correctly in the same circuit with the Johnny-Five library but they only work independently but not together.

In the Arduino community there are two solutions (hacks) to this:

I modified the StandardFirmata sketch by adding the dummy analogRead before the actual Firmata read and it solved all my problems:

dummyRead = analogRead(analogPin);
Firmata.sendAnalog(analogPin, analogRead(analogPin));
soundanalogous commented 7 years ago

That's strange because the analogRead code in Arduino blocks until the read has finished but I guess this doesn't account for an ADC channel change which can take a few microseconds.

The issue here is that the delay time is likely unique per microcontroller architecture (Firmata is supported on architectures other than AVR). I'm not sure how long a dummy read takes vs the minimum delay required to switch an ADC channel on a given microcontroller architecture. Until I have that data it's difficult to determine how to proceed.

soundanalogous commented 7 years ago

It may actually be most effective to stagger reads, so instead of reading all analog inputs in one shot, stagger them, one read per iteration of the main loop, with the first read occurring at the sampling interval, assuming that all reads could occur within the sampling interval. This would not incur any additional delays. The only problem would be if the loop time is < the necessary channel change duration.

webondevices commented 7 years ago

It's clear that the issue is not with the software implementation as the code blocks until the read has finished. This is most likely an issue with the ADC.

I wanted to highlight this thread on the Arduino forum where they are discussing this issue in a bit more detail:

http://forum.arduino.cc/index.php?topic=69675.0

webondevices commented 7 years ago

@soundanalogous , I'm currently writing a book on Johnny-Five and some of my examples have multiple analog sensors running the same time. I see no progress here so I have no other choice than to just provide the reader a link to my Github to download my modified StandardFirmata file.

Does the license allow me to publish a modified version of the StandardFirmata on my GitHub?

Also, is this really the best option I have?

soundanalogous commented 7 years ago

Why don't you submit a PR with your modified StandardFirmata file and we can go from there. I'm working on several Firmata issues in my limited spare time and this one isn't high priority for me at the moment.

soundanalogous commented 7 years ago

I'd really like to see a solution that doesn't involve delays, but I can help walk you through it if you are willing to put in some work.

jguille2 commented 7 years ago

Hi, I would like to share a comment with us(excuse me if it is not opportune).

I use StandardFirmata to read multiple analog inputs with Snap4Arduino. I only have had troubles with LM35 sensor. Reading it alone is ok... but when I read another pin (activing reporting) then its readings are wrong. The value of the other measurements affect the reading value. With an oscilloscope, you can see the noise of another signal.

Several people (with more knowledge than me in electronics) insist that the problem is in the soft (firmata)... but I think that the problem is in the conjunction (this sensor with our reading frequency).

Take a look at this LM35 info. Putting a 75Ω Resistor and a 1µF Capacitor between ground and signal pins, the noise disappears and the measurement returns to be stable (again independent of other measures

webondevices commented 7 years ago

@jguille2 , thanks for the input! I have tested these. I don't think the issue only happens because of the LM35 sensor. I have inconsistencies in the readings without that however the scale is definitely not that bad. I have tested this with a water sensor and an LDR in the same circuit. The change in the water sensor don't seem to affect the reading on the LDR, however the LDR can change the water sensor reading from 810 to 940.

On the other hand the resistor and capacitor have fixed my temperature sensor reading!

I wonder if my voltage divider circuit for the LDR misses a component that would fix my readings on that side as well?

Anyway, I'm going to close my pull request for now until we get to the end of this.

jguille2 commented 7 years ago

Ok, let's continue...

As I said, 'electronic people' insists me that LM35 is ok... and the problem is in firmata. Really, reading speed seems to cause side effects... at least in some devices. But if we can manage this with the hardware connections... the problem disappears... and we only have to document it.

I do not want to get away from this issue ... but I try to talk about a feature that might arrange this problem ... and I also needed in other scenes.

I would like to have two reading methods: the actual (turning on/of reporting) and an alternative making single readings. I try to explain this with some examples...

Can we have two methods to read pins? Maybe two different functions... or maybe a config var to choose the method.

Thanks for your patience... I know I'm asking a lot...

Joan

soundanalogous commented 7 years ago

@jguille2 see this proposal, it includes a way to read a single analog pin value. That way the user can control the rate at which it's read. The downside is the latency will be twice that of using reporting (due to the round trip communication over the transport). I may actually break that proposal out into multiple features including:

soundanalogous commented 7 years ago

I linked to the wrong proposal, that one is actually related, but this is the proposal I meant to link to: https://github.com/firmata/protocol/issues/59

jguille2 commented 7 years ago

Thanks Jeff,

Very interesting for me this proposal. If firmata keeps reporting method and adds a single reading method... it will be perfect. We'll choose the best method for each case.

The latency increase will not be a problem in some scenarios. And I think this will fix the problem of some sensors (like LM35)... and they must work with the alternative method.

I'll be waiting to test these changes.

Thank you very much for your great work!! I love firmata.

My best wishes for this new 2017!

Joan

soundanalogous commented 7 years ago

@webondevices were you able to resolve this issue with hardware?

webondevices commented 7 years ago

@soundanalogous , sorry for not sharing my results earlier.

The proposed solution in this article seems to have to solved my problem: https://heliosoph.wordpress.com/2012/03/21/connecting-an-arduino-with-a-lm35-temperature-sensor-special-issues/

Unfortunately, I don't have enough hardware knowledge to understand why this fixes the problem but the added resistor + capacitor combo have stabilised my readings.

jguille2 commented 7 years ago

Hi @soundanalogous ,

I think this can be closed, because it wasn't a general analog reading issue... and I think the problem with the lm35 has a hardware workaround. I only want to know if the proposal to have single readings is still alive.

Thanks, Joan

soundanalogous commented 7 years ago

@jguille2 that proposal is still alive but relatively low priority. What I may enable before that is the ability to query a single analog pin value, however I have not yet drafted a proposal for that feature.

jguille2 commented 7 years ago

Thanks @soundanalogous for all your work!

This ability to query a single analog/digital pin would be great.

Joan

senadj commented 7 years ago

I am using customized "firmata" for my arduino UNO and I am hitting the same problem with LM35 and another Hall-effect sensor. I found that readings are stable as long as I use this order in a loop:

  1. pin19: AnalogRead(Hall)
  2. pin18: AnalogRead(LM35)
  3. send serial data (+delay 15ms)

If I swap steps 1 and 2 then LM35 sensor data is floating somewhere down 30% and up to 180% above correct value. I say "correct", but even with stable readings, LM35 output shows almost 25% lower value when used together with Hall sensor (compared to being alone). But If I move LM35 to pin14 and leave Hall on pin19, that value is only 5% lower. With my code I can swap readings order on the fly, and this issue is consistently reproduced.

soundanalogous commented 7 years ago

@senadj have you tried adding a small delay after each analogRead? Also how often are you reading the analog pins? Every iteration of loop() or every 19ms (the default Firmata analog sampling frequency)?

senadj commented 7 years ago

I did just now (using 5,10,25,125ms). Now read order does not matter - in both cases LM35 sensor readouts floats. It seems the problem got worse, no stable reads anymore.

I checked my code again and realized I did not use delay call before. This is how it went: I was doing both analogReads one after another. Then serial write and waiting for serial buffer flush. Then repeat this sequence (in a loop) if more than 100ms have passed from last start of previous iteration.

Inserting dummy analogRead before the "real" one seems to fix the problem independently of sensor read order. That way I get read stability plus read values are identical to when using LM35 only. Putting delay between readouts doesn't change anything in this case. And considering all this, I am not sure if adding capacitor and resistor to LM35 as mentioned above is really needed.

soundanalogous commented 7 years ago

So if you need to call analogRead more than once, you need a dummy call to analogRead before each subsequent call to analogRead?

// do you need to do this?
analogRead(HALL_PIN); // dummy read
byte hallPinVal = analogRead(HALL_PIN);
analogRead(LM35_PIN); // dummy read
byte lm35PinVal = analogRead(LM35_PIN);
Serial.write(data...);
// or is this sufficient
// no dummy read for initial analog sensor
byte hallPinVal = analogRead(HALL_PIN);
analogRead(LM35_PIN); // dummy read only for additional analog sensors
byte lm35PinVal = analogRead(LM35_PIN);
Serial.write(data...);
senadj commented 7 years ago

Your second example is sufficient in my case. But you should use int datatype to store 10bit readouts (hallPinVal and lm35PinVal).

I would guess only particular sensors benefit from dummy analogRead operations.

soundanalogous commented 7 years ago

For StandardFirmata I would need a very generic solution since the firmware doesn't know anything about connected analog sensors, it only knows how many analogReads to make per sampling interval. Currently it makes them one after another with no delay and no dummy reads. So one solution is to add a dummy read before each subsequent call if there are more than 1 analogReads queue'd up.

lkasari commented 6 years ago

I've done dozens of Arduino projects but have never run into this problem until now. Currently I'm doing a rather large project which polls all 16 analog inputs of an Arduino Mega repeatedly. I was reading them all with only a single 1mS delay in the main loop and was noticing several odd analog reading problems, especially with A14 and A15. After looking at the comments in this thread I did two things. First I put a delay of 1mS between each read, and second I put all the reads in order, from 0 to 15. All the problems went away. I'm not sure putting the reads in order helped or not, but it didn't hurt.

I agree this is a bit of a kludge, but I was able to rewrite the main loop so it can still get all the analog inputs and deal with its other tasks in a timely manner.

Thanks for your comments. They helped a lot.

MoutatsuLai commented 10 months ago
  1. The problem of sampling twice to get the correct value. Is it related to the fact that Input MUX switching requires 25 ADC Clocks? adc_first_med

  2. ADC reading value error problem Dragon Knight_1123 I think the problem is the impedance at the input and the PCB trace capacitance. A.General situation If the resistance and capacitance values ​​are small enough, the MCU internal capacitance (14pF) can be fully charged within 13.5 ADC clock cycles and the correct value can be sampled. B. Error situation (CDS photocell or other high impedance sensor) If the resistance value is too high, after 13.5 ADC Clocks, the value of the capacitor that is not fully charged will be sampled. This should be the cause of the error.

Open Music Labs - ATmega ADC