-->
Page 1 of 2

analogRead() in loop{} 8x faster than using Timer0

PostPosted: Tue Mar 29, 2016 4:37 pm
by gdhgdh
Hi,

I've been experimenting with sampling analogue audio from an electret microphone with MAX4466 op-amp and sending it over the WiFi using UDP to another machine. This works surprisingly well when all of the work is done in the loop {} - I get approx 8500Hz - i.e. about telephone quality.

I want to remove this code from the loop {} and drive the sampling using timer0 intervals. The problem is that when I do that, I can't set the interval any shorter than about 70000 cycles (~500ms) else I get no network traffic at all. Thus the loop{} driven code is about 8 times faster.

Can someone point me in the right direction?

Here is the Timer0-based code with the speed problem:

Code: Select all#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

unsigned int udppacketcount=0;
unsigned int adcvalue;

const char* ssid = "acentral-office";
const char* password = "xxxxxxxxxxx";
boolean wifiConnected = false;

IPAddress ip(10, 0, 0, 31);

WiFiUDP UDP;
char ADCBuffer[500] = "";

boolean connectWifi() {
  boolean state = true;
  int i = 0;
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }
  if (state) {
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("");
    Serial.println("Connection failed.");
  }
  return state;
}

void inline handler(void) {
    adcvalue=analogRead(A0);

    ADCBuffer[udppacketcount] = adcvalue;
//    Serial.print(ADCBuffer[udppacketcount]);
//    Serial.print(" ");

    udppacketcount++;

    if (udppacketcount == 500) {
//      Serial.println();
      UDP.beginPacket(ip, 8989);
      UDP.write(ADCBuffer, 500);
      UDP.endPacket();
     
      udppacketcount = 0;
    }

    timer0_write(ESP.getCycleCount() + 70000);
}

void setup() {
  Serial.begin(115200);
  wifiConnected = connectWifi();

  noInterrupts();
  timer0_isr_init();
  timer0_attachInterrupt(handler);
  timer0_write(ESP.getCycleCount() + 70000);
  interrupts();
}

void loop() {
}


... and here's the working loop{} based code which samples + sends UDP at about 8kHz

Code: Select all#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

unsigned int udppacketcount=0;
unsigned int adcvalue;

const char* ssid = "acentral-office";
const char* password = "xxxxxxxxx";
boolean wifiConnected = false;

IPAddress ip(10, 0, 0, 31);

WiFiUDP UDP;
char ADCBuffer[500] = "";

boolean connectWifi() {
  boolean state = true;
  int i = 0;
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 10) {
      state = false;
      break;
    }
    i++;
  }
  if (state) {
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("");
    Serial.println("Connection failed.");
  }
  return state;
}

void setup() {
  Serial.begin(115200);
  wifiConnected = connectWifi();
}

void loop() {
    adcvalue=analogRead(A0);

    ADCBuffer[udppacketcount] = adcvalue;
    udppacketcount++;

    if (udppacketcount == 500) {
      UDP.beginPacket(ip, 8989);
      UDP.write(ADCBuffer, 500);
      Serial.print(UDP.endPacket());
      udppacketcount = 0;
      delay(10); // XXX - no network traffic unless this is here!
    }
   
    yield();
}



With this I get about 16 x 500-byte packet payloads per second. If I take the delay(10) out of the loop{}, then the network traffic is very sporadic. Of course if I try to put a delay() into the Timer0 handler code, the esp8266 will 'wdt reset' immediately.

Any ideas? I'm using Arduino 1.6.8 with ESP8266 2.1.0 installed through the Boards Manager.

Cheers,
Gavin.

Re: analogRead() in loop{} 8x faster than using Timer0

PostPosted: Wed Mar 30, 2016 2:31 am
by krzychb
Hi Gavin,

ADC is used by ESP for gauging internal voltage to adjust output power of Wi-Fi.
Therefore, as you observed, it cannot be sampled continuously or Wi-Fi communication will break.

Basing on my observation maximum sample rate of ADC using esp/Arduino framework is about 12 samples/ms for the period of up to 60ms. This gives maximum of about 720 samples that then should be transmitted out over Wi-Fi. After that you can collect another packet of up to 720 samples and so on. This way Wi-Fi connection is not broken.

I do not have experience with using timer and interrupts for this purpose. Instead you may consider using a state machine sequence as described in this post. Below is a screenshot from this application.


Krzysztof

f9d8e742-e234-11e5-9dfb-b91b5744a3ff.png

Re: analogRead() in loop{} 8x faster than using Timer0

PostPosted: Wed Mar 30, 2016 7:04 am
by gdhgdh
Ah that's an interesting read and the Websocket project looks really cool, too :)

The thing that doesn't add up for me is that if use the loop{} based approach (i.e.sample as fast as possible) with only a delay(10) between each 500-sample batch, then I'm able to sample, send (and receive on the host PC) traffic at over 8000 samples per second - enough for telephone quality audio.

If I drop the delay(10) then nothing is sent, so I understand that the ESP8266 OS needs some headroom - it seems that my experiences allow a faster and longer 'burst' of samples than yours, which doesn't make much sense given that we're both using version 2.1.0 of the ESP8266 'board' for Arduino. (older versions were about a quarter of the speed)

Re: analogRead() in loop{} 8x faster than using Timer0

PostPosted: Wed Mar 30, 2016 7:05 am
by danbicks
Hi Gavin,

Do you have an example of the output wiring for the audio side on the remote receiver, would like to knock one of these up and have a dabble with it. Are you using an external dac or pwm output method.

Cheers

Dans