Differet behavior between delay() and delayMicroseconds()

I’m using Arduino 1.6.8 on a WIN10 machine for a generic ESP8266 board

Using large delays in loops isn’t recommended. Instead of that I’m use a ticker event leaving the main loop complete empty – let the processor do whatever he is suppose to do.

The task of the program is to read out two sensors an AM2321 humidity sensor and a BMP180 air pressure sensor. Both connected via I2C. Yes, pullup resistors are in place.

First version of the Program using delay(2000) in the main loop works fine for both sensors. See the logic analyser screen shots named ‘with delay AM2321 ok.pdf’ and ‘with delay BMP189 ok’. Critical point is the gap after addressing the devices. Here 1 ms for the AM2321 and 5 ms for the BMP180 sensor.

Switching to the program version using a ticker event instead of the delay(1) in the main loop sensors reading give false results – there is almost no gap after addressing the device. See screen shot ‘with ticker delay AM2321 failed’. Just tinkering around I replaced delay(1) with delayMicroseconds(1000) and voila I got correct humidity and temperature readings from the AM2321 see screen shot ‘with ticker delayMicroseconds AM2321’.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#include <TimeLib.h>
#include <Ticker.h>
Ticker Tick;                                  // create a ticker instance named Tick
int intervall=2000;                           // ticker intervall 10000 eq to 10 sec   <<*****************************


// BMP085 pressure & temp stuff -------------------------------------------------------------
#include <Adafruit_BMP085.h>
Adafruit_BMP085 bmp;

int AM_trigger=2;                                            // logic analyser trigger test only at GPIO2************************************
int BP_trigger=4;                                            // logic analyser trigger test only at GPIO4************************************

void setup()   {  
  Tick.attach_ms(intervall, Tack);                           //config ticker instance "Tick"" to call "Tack" every intevall(milli seconds)
  pinMode(AM_trigger,OUTPUT); digitalWrite(AM_trigger,LOW);  //logic analyser trigger test only   
  pinMode(BP_trigger,OUTPUT); digitalWrite(BP_trigger,LOW);  //logic analyser trigger test only              
  digitalWrite(AM_trigger,LOW);                              //logic analyser trigger test only
  digitalWrite(BP_trigger,LOW);                              //logic analyser trigger test only
  Wire.begin(12,13);  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);                  // initialize and setup for text display
  display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); 
  display.setTextColor(WHITE,BLACK);display.clearDisplay();   // overwriting with black background!
  display.println("  AM2321 & BMP180 "); display.display();   // write some text to the display buffer and finally show it 
  bmp.begin();
  delay(1000); 
}

byte char_cnt = 0;                        // received char's counter
byte readback[6];                         // received data from  AM2321
char R_code;                              // return code from I2C transmission
char cmd[]={0x03,0x00,0x06};              // AM2321 cmd_string read registers/start at reg. no 0/read 6 registers
                                          // 1.=>cmd, 2.=>start address/3.+4.=>16 bit humidity*10/5.+6.=>16 bit temperature*10
float hum;                                // humidity
float temp;                               // temperature

void Tack(){                                     // called every timer tick
digitalWrite(AM_trigger,HIGH);             //logic analyser trigger test only**************************************** 
  Wire.beginTransmission(0x5C);          // empty write to wakeup / activate the AM2321 
  R_code = Wire.endTransmission();       // thanks go to Koepel from arduino forum   
  Wire.beginTransmission(0x5C);
  Wire.write(cmd,3);
  R_code = Wire.endTransmission();       // writes qued data  
//  delay(1);                              // requiered by AM2321
  delayMicroseconds(1000);
  char_cnt=0,
  Wire.requestFrom(0x5c, 6);             // request 6 bytes from slave device #2
  while(Wire.available())                // slave may send less than requested
  {
    readback[char_cnt] = Wire.read();    // receive a byte as character
    char_cnt++;
  }
  hum=float(readback[2]*256+readback[3])/10;
  temp=float(readback[4]*256+readback[5])/10;
  digitalWrite(AM_trigger,LOW);           //logic analyser trigger test only****************************************    
//  display.clearDisplay(); 
  display.setCursor(0,0);
  display.print(hum,1); display.print(" %   ");
  display.print(temp,1); display.println(" C   ");
  display.display(); 

//read pressure (& temp) from BMP085
  digitalWrite(BP_trigger,HIGH);          //logic analyser trigger test only****************************************  
  long pressure_414 = (bmp.readPressure() + 4750.0) / 100.0; // adopt seelevel correction
  digitalWrite(BP_trigger,LOW);           //logic analyser trigger test only****************************************
  int Pressure = pressure_414; 
  display.print(pressure_414); display.print(" hPa  ");  
  display.print(bmp.readTemperature(),1); display.println(char(222));                 //internal BMP085 temp
  display.display(); 
     
}

void loop() {
 // no delay here free to used by
 // processor background tasks
}

After that change the BMP180 still gives false readings, see screenshot ‘with ticker delayMicroseconds BMP180 failed’. There is almost no delay (app. 28 µs) after firs addressing the BMP180.
Looking for advice - very confused

with delay AM2321 ok.pdf (146 KB)

with delay BMP180 ok.pdf (130 KB)

With ticker delay AM2321 failed.pdf (146 KB)

With ticker delayMicroseconds AM2321 ok.pdf (147 KB)

With ticker delayMicroseconds BMP180 failed.pdf (159 KB)

I think that the ESP8266 uses it's own Wire library, which is a software I2C and is not very compatible with the original Arduino Wire library (as used for the Arduino Uno).

It seems that your I2C bus is barely working.
That specific delay is not normal.

I have a ESP8266 somewhere, but I never had time to use it. I'm afraid I can't help you. I'm very confused as well. Is there a newer version of the software for the ESP8266 ? Do you have an Arduino Uno to test the sensors and the sketch ?

Koepel thank you for quick reply!

I'm almost sure I2C bus is working because both sensors are working / giving plausible results for both of it readings: AM2321 humidity and temperature, BMP189 for air pressure and temperature. The difference in temperature readings is less then 0.2 centigrades. However only in the Loop-delay enviroment.

Changing to a ticker event driven program non of the sensors act in the right way. However changing in this enviromen delay(1) into delayMicroseconds(1000) the Am2321 sensor again give the right readings. The BMP180 still give back nonsens for airpressur - but temperature is ok too!

the really strange thing what confused me so much:

  • same bord and almost the same program
    only difference is delay(1) and delayMicroseconds(1000)
    that makes the difference

Yes I have installed all board and lib updates.
Behind all the effords is an IoT project so I like to stick at ESP8266-boards to realise the internet connection.

Arduino you rocks me really - is some one from arduinos staff listening? Will give all the information needed to get behind the issue.

Koepel thank you!

See here for some hints:

....you might consider adding a call to delay function to keep the WiFi stack running smoothly.

There is also a yield() function which is equivalent to delay(0). The delayMicroseconds function, on the other hand, does not yield to other tasks, so using it for delays more than 20 milliseconds is not recommended.

The delay function seems to be based on system ticks so that the delay time can be used for other tasks.
I guess the system timer runs with a resolution of 1 ms. That would mean the time for delay(1) can vary between one and almost zero milliseconds.

/Joe

Thank you Joe for your replay.
However changing delay(1) to delay(2) makes no difference at all. I'm quite sure there is a fundamentel difference between delay() and delayMicroseconds() and how it I2C protocoll is affected by this (difference). As an ordinary user I can not make a deeper investigation. This requieres mor knowledge I don't have e.g. what code the compiler generates, how the library handels I2C protocoll etc.

All I can do is hope someone of arduinos staff/volunteer will have a closer look at this issue and I will deliver all the related information as programm code logic analysers output ...
Regards Jochen

Why not get rid of the delay()s and delayMicroseconds() and use millis() to manage timing without blocking as illustrated in Several Things at a Time

...R

Hi Jochen

Have you checked the documentation on the GitHub page for the Arduino ESP8266 core?

You could also post an issue there. The developers usually respond quickly.

Regards

Ray

I don't know what Ticker does, but the main difference between delay and delayMicroseconds is the latter does not require interrupts. If interrupts become disabled for any reason, delay will quit working. Just a thought...

edit: Are you certain that Ticker does not take over the timer that delay uses?

Thank you all for usefull hints.

  • Robin2: I will try this asp and report here . At a first glance the ticker solution seems to be very clean and straightforward coded. No IF's, no CASE/Break ... just let the system take care of timing

  • Hackerscrible: I'll take this issue to the the Arduino ESP8266 core team. Looking forward for their response.

  • SurferTim: as an ordinary/newbie user I don't have an idea of internal behavior. I agree with you that ESP timer, interrupts, watchdog et all are hard to understand. More over if things are hidden in libs its more or less pray and beleive. Yes I'm with you it might be a conflict in timer usage

Thanks again
Jochen

MHz:

  • Robin2: I will try this asp and report here . At a first glance the ticker solution seems to be very clean and straightforward coded. No IF's, no CASE/Break ... just let the system take care of timing

What system? The only "system" is the one you program. It seems you are intent on hiding the errors from yourself. I like Robin2's suggestion of using the BlinkWithoutDelay solution.

edit: Basically, here is your "system".

int main( int argc, const char* argv[] )
{
    // set up a timer and interrupt for delay and millis here
    // ...then
    setup();

    while(1) {
        loop();
    }
}

Hi SurferTim,
what happens on the ESP-chip under the hood - and that's a lot of stuff - I call it a system. May be there is a better expression for.

No, I don't hiding my or anyone else errors I'm definitly after them. Thats the reason for my posting.

If the there is an construct called "ticker" what is the reason for it? In my view it let you code in a clear and structured manner. However programming is a kind of art, very induvidual.

Thanks again for your contribution

Jochen

I don't know anything about Ticker. There are lots of libraries out there. Some are compatible with other libraries, and some are not. Some are compatible with all Arduino boards, and some are not.

edit: It didn't take long to find it. Is this it?

That library apparently uses a timer and interrupts with a callback function. If it is hijacking the timer used by the delay function, can you see where your code might have problems?