Interference between ESP8266 WiFi and analogRead() instruction

Hello, I am developing a web server project to monitor my home electrical panel. I use an ESP-12E platform under Arduino IDE 2.0.3. To put it simply, if you start from the code ESP8266WebServer -> AdvancedWebServer example and add a very simple acquisition loop (here burst of 400 values), we see that the time of the analogRead instruction can vary from simple to double randomly. But in the same batch it is constant. Does anyone know why and how to fix it? Thanks. Here is the code which is that of the example in the Arduino IDE only completed with a sampling routine :

/*
   Copyright (c) 2015, Majenko Technologies
   All rights reserved.

   Redistribution and use in source and binary forms, with or without modification,
   are permitted provided that the following conditions are met:

 * * Redistributions of source code must retain the above copyright notice, this
     list of conditions and the following disclaimer.

 * * Redistributions in binary form must reproduce the above copyright notice, this
     list of conditions and the following disclaimer in the documentation and/or
     other materials provided with the distribution.

 * * Neither the name of Majenko Technologies nor the names of its
     contributors may be used to endorse or promote products derived from
     this software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const char *ssid = "WiFi network name"; 
const char *password = "Password"; 

ESP8266WebServer server(80);

const int led = 13;

//----------------------------------------------
void handleRoot() {
  digitalWrite(led, 1);
  char temp[400];
  int sec = millis() / 1000;
  int min = sec / 60;
  int hr = min / 60;

  snprintf(temp, 400,

           "<html>\
  <head>\
    <meta http-equiv='refresh' content='5'/>\
    <title>ESP8266 Demo</title>\
    <style>\
      body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
    </style>\
  </head>\
  <body>\
    <h1>Hello from ESP8266!</h1>\
    <p>Uptime: %02d:%02d:%02d</p>\
    <img src=\"/test.svg\" />\
  </body>\
</html>",

           hr, min % 60, sec % 60
          );
  server.send(200, "text/html", temp);
  digitalWrite(led, 0);
}

//----------------------------------------------------------------
void handleNotFound() {
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

//--------------------------------------------------------------------
void drawGraph() {
  String out;
  out.reserve(2600);
  char temp[70];
  out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
  out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
  out += "<g stroke=\"black\">\n";
  int y = rand() % 130;
  for (int x = 10; x < 390; x += 10) {
    int y2 = rand() % 130;
    sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
    out += temp;
    y = y2;
  }
  out += "</g>\n</svg>\n";

  server.send(200, "image/svg+xml", out);
}

//---------------------------------------------------------------
void sampling() {
int lecture[400];   
unsigned long times[400]; 
for (int k=0;k<400;k++){
    times[k] = micros(); 
    lecture[k] = analogRead(A0); 
    times[k] += micros(); 
    delayMicroseconds(10); 
  }  
Serial.print("Total duration for sampling = ");
Serial.println((times[399] - times[0])/2);
delay(2000);
}

//-----------------------------------------------------------
void setup(void) {
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);
  server.on("/test.svg", drawGraph);
  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });
  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

//-----------------------------------------------------------------
void loop(void) {
  sampling();
  server.handleClient();
  MDNS.update();
}

Replacing the analogRead() instruction for example with a delayMicroseconds() instruction causes the anomaly to disappear.

The WiFi component uses the same ADC that analogRead() uses. Intensive use of analogRead, as in your for loop, can cause the WiFi to have issues.

Do you need to sample so intensively? I note you have a 2 second delay in the sampling routine - if you replace this with a 5 ms delay in the for loop does the problem still exist?

1 Like

Thank you Red_car for this relevant information. The code here simplified compared to mine because the ADC also performs other acquisitions in bursts to then calculate TRMS values ​​on 50Hz currents (plus consumption statistics, cost in night and day rates, ...). The 2s tempo simulates these other tasks. I tried to play with the suggested delay. At 1ms the difference is well absorbed. But that only makes 20 samples per period, too small to calculate an RMS quantity. I think I will use an external ADC type MCP32xx, which will also allow me to remove the multiplexer since the ESP8266 has only one analog input. Do you know what the WiFi component does with the ADC ?

I believe it has something to do with sampling the power output of the signal.

If you use an ESP32 you will have a similar problem, but it can be avoided as there are multiple analog ports and only one is used by the WiFi... so if you use the correct pins then no issue.

https://arduino-esp8266.readthedocs.io/en/latest/reference.html#analog-input

I have just resumed my project after a few days of vacation. I completely abandon the use of the ADC of the ESP8266 because I had also noted some other malfunctions. This is the impossibility of using a precision reference for the ADC voltage, not even access to a measurement of the internal reference voltage of 1V which prevents any calibration. There are also significant conversion errors, when I put the ADC input to ground, it shows me a value of 12 to 14 ! The real resolution is at best 6 to 7 bit but not 10 bit. I'm going to use an MCP32xx family ADC, which will also simplify the problem of multiplexing while gaining speed and resolution for a very moderate cost. It's a shame because this platform is interesting, but the marketing is a bit misleading. Thank you again for your help.

so yes. it is a WiFi module not a general purpose MCU

Note that the ESP has an absolute A/D and the MCP32xx family has a ratiometric A/D.
Should match the type of A/D to what you are measuring.
The ADS1115 has an absolute A/D (with internal Aref), like the ESP.
Leo..

"Ratiometric" it's more a question of naming because basically it's the same. An A/D converter is always an electronic circuit which provides the digital ratio of the input voltage to a reference voltage, whether internal or external (single-ended mode). For the ESP8266 VREF is internal and not accessible, in principle 1V but with what precision ? And obviously there is a design problem since for VADC = GND we obtain a digital value of about 15, beyond the analog noise on the ground line and the conversion noise. The value of 1023 is obtained clearly below 1V, there seems to be an offset on the whole scale.

Just saying that if you want to measure a voltage with an MPC32xx, then you also must add a reference chip. The ADS1115 has one built-in.
Leo..

An ADC always measures in voltage which is then converted into a physical measurement. With an MCP32xx, 12-bit resolution ADC, there is no need for a $500 voltage reference. The Vcc voltage is the reference and a simple LM317 which has excellent characteristics in line regulation and temperature stability is sufficient. The ADS1115 has an internal non-reachable reference and displays 16 bit resolution UNRELATED to its accuracy. The data sheet is marketing, everything is expressed in LSB. But what is 1 LSB worth ? Don't believe for example that it is 2.048V/32536 +/- 15ppm. Its accuracy is at best 0.01% which is the accuracy on the gain. Moreover it is too slow for my application (860SPS).

Sure, a $0.20 reference or a stable regulated supply will do for a 12-bit A/D.
You should use the supply as reference if a ratiometric source is also powered from that supply.
But sometimes the supply is noisy or jumps with a blinking LED or is different when the Arduino is powered from different sources. Anyway, you seem to know what you're doing.
Leo..

There is no problem if you follow the basic principles of instrumentation and the rules for designing and installing analog and digital parts. Which is unfortunately almost never the case for most of the projects that we see with these Arduino, ESP and similar devices. And even worse with these famous breadboards !

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.