Arduino: problems starting I2C (reading BMP180 preasure sensor)

Created on 11 Jul 2016  路  25Comments  路  Source: esp8266/Arduino

Basic Infos

Hardware

Hardware: ESP-12E
Core Version: 2.3.0

Description

Hey there, hope you can help. I have a nodeMCU v1.0 which works fine running several sketches. But when trying to read an I2C sensor I fail. I read to use Wire.begin(9, 10); to start the wire library with selected SDA and SCL. I have my sensor connected to the SD2 and SD3 pin of the nodeMCU which are GPIO 9 and 10. I uploaded the whole sketch below to have a look at, but thats only the BMP test example from adafruit. Even with nothing connected to the nodeMCU it shows the same behaviour (not the error that should be printed).
One strange thing is that I can not read anything at the specified 9600 Baud but only on 115200 Baud.
I use Arduino IDE to flash my ESP8266.

Settings in IDE

Module: nodeMCU v1.0
Flash Size: ?4MB/1MB?
CPU Frequency: 80Mhz
Flash Mode: ?qio?
Flash Frequency: ?40Mhz?
Upload Using: ?OTA / SERIAL?
Reset Method: ?ck / nodemcu?

Sketch

#include <Wire.h>
#include <Adafruit_BMP085.h>

/***************************************************
  This is an example for the BMP085 Barometric Pressure & Temp Sensor

  Designed specifically to work with the Adafruit BMP085 Breakout
  ----> https://www.adafruit.com/products/391

  These displays use I2C to communicate, 2 pins are required to
  interface
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

// Connect VCC of the BMP085 sensor to 3.3V (NOT 5.0V!)
// Connect GND to Ground
// Connect SCL to i2c clock - on '168/'328 Arduino Nano/Uno/Duemilanove/etc thats Analog 5
// Connect SDA to i2c data - on '168/'328 Arduino Nano/Uno/Duemilanove/etc thats Analog 4
// EOC is not used, it signifies an end of conversion
// XCLR is a reset pin, also not used here

Adafruit_BMP085 bmp;

void setup() {
  Wire.begin(9, 10);
  Serial.begin(9600);
  if (!bmp.begin()) {
    Serial.println("Could not find a valid BMP085 sensor, check wiring!");
    while (1) {}
  }
}

void loop() {
  Serial.print("Temperature = ");
  Serial.print(bmp.readTemperature());
  Serial.println(" *C");

  Serial.print("Pressure = ");
  Serial.print(bmp.readPressure());
  Serial.println(" Pa");

  // Calculate altitude assuming 'standard' barometric
  // pressure of 1013.25 millibar = 101325 Pascal
  Serial.print("Altitude = ");
  Serial.print(bmp.readAltitude());
  Serial.println(" meters");

  // you can get a more precise measurement of altitude
  // if you know the current sea level pressure which will
  // vary with weather and such. If it is 1015 millibars
  // that is equal to 101500 Pascals.
  Serial.print("Real altitude = ");
  Serial.print(bmp.readAltitude(101500));
  Serial.println(" meters");

  Serial.println();
  delay(500);
}

Debug Messages

All I get when using I2C is the following in a loop:

 ets Jan  8 2013,rst cause:4, boot mode:(3,4)

wdt reset
load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v60000318
~ld

 ets Jan  8 2013,rst cause:4, boot mode:(3,4)

wdt reset
load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v60000318
~ld

Most helpful comment

First, the wdt reset is a result of your while (1) {} loop in your setup when you fail to "connect" to the sensor. (btw: I don't understand why this loop could make any sense there, but anyway....) Note that the software watchdog bites every 3.2 seconds, and the hardware watchdog after ~ 6 seconds (typically 7s or if you are lucky after 8s). btw: If you should ever need to disable the software watchdog and live with the hardware watchdog alone, you can do it like this:

system_soft_wdt_stop(); // disable the software watchdog
system_soft_wdt_feed(); //this feeds the software AND the hardware watchdog
// now, you can place your code here -- it has ~ 6 seconds until the hardware watchdog bites
system_soft_wdt_feed(); // feed again
system_soft_wdt_restart(); // enable software watchdog again

Or, as a simple example, the basic sketch is:
(imagine busy_1second() is when your while loop executes for 1 second)

extern "C" {
    #include "user_interface.h"
}

int j = 0;
unsigned long a, b;

void setup() {
    delay(1000);
    Serial.begin(115200);
    Serial.println("Setup...");
}

void ICACHE_RAM_ATTR busy_1second() {
    unsigned int i, iterations;
    if (F_CPU == 160000000) {
        iterations = 13333332;
    }
    else {
        iterations = 6666666;
    }
    for (i = 0; i < iterations; i++) {
        asm(
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
        );
    }
}
void loop {
SEE NEXT ...
}

First, use for the loop:

void loop() {
    Serial.println("Getting busy in 3 seconds...");
    delay(3000);
    Serial.println("now!");
    system_soft_wdt_stop();
    system_soft_wdt_feed();
    system_soft_wdt_restart();
    for (j = 1; j < 20; j++) {
        a = micros();
        busy_1second();
        b = micros();
        Serial.println(b - a);
        if (j % 6 == 0) {
            system_soft_wdt_feed();
            Serial.println("HW Dog fed! Miam miam :-o ");
        }
    }
    // system_soft_wdt_feed();
    // system_soft_wdt_restart();
}

Aua! The Software Watchdog bites every 3 seconds!

Port open
Setup...
Getting busy in 3 seconds...
now!
1000039
1000012
1000000

Soft WDT reset

ctx: cont 
sp: 3ffef830 end: 3ffefa20 offset: 01b0

Now, we want to be bitten by the Hardware Watchdog instead, so:

void loop() {
    Serial.println("Getting busy in 3 seconds...");
    delay(3000);
    Serial.println("now!");
    system_soft_wdt_stop();
    system_soft_wdt_feed();
    // system_soft_wdt_restart();
    for (j = 1; j < 20; j++) {
        a = micros();
        busy_1second();
        b = micros();
        Serial.println(b - a);
        if (j % 9 == 0) {
            system_soft_wdt_feed();
            Serial.println("HW Dog fed! Miam miam :-o ");
        }
    }
    system_soft_wdt_feed();
    system_soft_wdt_restart();
}

Ouuuch!

Getting busy in 3 seconds...
now!
1000039
1000006
1000000
1000000
1000000
1000001
1000001
1000000

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1264, room 16 
tail 0
chksum 0x0f

Finally, we put the Software Watchdog asleep and are feeding the Hardware Watchdog every 6 seconds :-)

void loop() {
    Serial.println("Getting busy in 3 seconds...");
    delay(3000);
    Serial.println("now!");
    system_soft_wdt_stop();
    system_soft_wdt_feed();
    // system_soft_wdt_restart();
    for (j = 1; j < 20; j++) {
        a = micros();
        busy_1second();
        b = micros();
        Serial.println(b - a);
        if (j % 6 == 0) {
            system_soft_wdt_feed();
            Serial.println("HW Dog fed! Miam miam :-o ");
        }
    }
    system_soft_wdt_feed();
    system_soft_wdt_restart();
}

And hence now the loop runs forever, without any bites by the watchdogs:

Getting busy in 3 seconds...
now!
1000002
1000007
1000001
1000000
1000000
1000000
HW Dog fed! Miam miam :-o 
1000001
1000007
1000000
1000001
1000001
1000000
HW Dog fed! Miam miam :-o 
1000000
...

Second, if your sensor is not working with the wire library, you may give my own i2c implementation a try, I wrote it in assembly, so timing etc. should be correct, it's called brzo_i2c.. (You will have to adapt the i2c communication, but should not be that much of work ;-)

Third, since you are using adafruit's breakout board with an onboard regulator and those MOSFETs for the level shifting: It might be that this breakout board has the same hardware issures as the HTU21 breakout board, when using it with the esp8266 and 3.3V, see readme with brzo_i2c example.

All 25 comments

what happens if you use the default GPIO4 and 5?

I will be using this nodeMCU as a mysensors gateway. Unfortunatly the build instructions say that the radio has to be on D2 and I don't know where to change this.
But as far as I read this shouldn't be a problem as ESP8266 has I2C only implemented in software. And even with no Pins connected at all I don't get the error message as stated in void setup() but only a wdt reset.
I even tried to remove the costume pin selection and started the wire library with standard GPIO4 and 5, with the same result

Normally GPIO9 and 10 are used for the flash memory. When the flash is accessed in 4 bit mode these pins are used. Have a look at this schematic.

http://smarpl.com/sites/default/files/images/ESP8266-ESP-201-GPIO9-GPIO10-schematics.png

Your configuration above shows you are using quad mode so that makes use of 9 and 10.

If you need to use GPIO9 and 10 then read through this thread. http://www.esp8266.com/viewtopic.php?f=13&t=1216&sid=6bae8974defd709c2276dbb5d8ebca83

On the nodeMCU board you do not have access to the pins in order to modify the circuit so you can use GPIO9 and 10.

I would recommend using other available pins. GPIO 0,2,4,5,12,13,14,15 and maybe 16. 16 is a bit different. I have not tried 16 for I2C so I'm not sure that it works.

@RudyFiero thanks a lot for your suggestion. I tried again with my radio disconnected and only SCL at D3 and SDA at D4. This setup gives wdt reset:

void setup() {
  Wire.begin(0, 2);
  Serial.begin(9600);
  if (!bmp.begin()) {
    Serial.println("Could not find a valid BMP085 sensor, check wiring!");
    while (1) {}
  }
}
 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v60000318
~ld

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v60000318
~ld

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v60000318
~ld

Any further hints? I'm a litte curious why the mcu also does a reboot if no SDA and SCL are connected only by calling wire.begin(). When using this code on an Arduino I get the correct message "check wiring".

When I use Wire.begin(0, 2);
my connections SDA, SCL and based on the pin layout information for your board it looks like you have the SCL and SDA connections reversed. Without switching your connections, change to Wire.begin(2, 0);

And just to cover the bases I'll ask what pull up resistors you are using. I typically use 2k2 but I occasionally will use up to 4k7. 4K7 allows me to have a couple of 4k7 in parallel, one on the esp module and one on a connected board.

I always have SDA assigned to GPIO0. That way I can ground that line when programming without touching the SCL line. Without transitions on SCL then any I2C devices will ignore any activity on SDA/GPIO0.

Hey @RudyFiero, I tested both, Wire.begin(2,0); and Wire.begin(0,2); without success. I have a 4k7 at both lines as kind of standard pull-up. I also added a second set of them in parallel to bring them to 2ksomething, but still only wdt resets.
Maybe I will redo it completely and take everything apart and start all over...

It is really odd that you are getting resets. Maybe there is a line that is making an unexpected connection somewhere.

I have found that if the 3.3 volt supply doesn't have an adequate regulator and sufficient bypass caps that the ESP module can get resets due to the power supply dipping too low. But you have a decently made module so I didn't expect that to be an issue.

At this point I would do what you plan to do, and start over. Would you have another module that you can use in place of the one you have been using? Sometimes hardware fails. It usually is one of the last things I suspect but that is because I am a hardware designer and not a programmer. I have worked with a lot of programmers and many of them have a tendency to blame the hardware more than they should.

I hope you solve the problem soon. I know how frustrating it gets.

First, the wdt reset is a result of your while (1) {} loop in your setup when you fail to "connect" to the sensor. (btw: I don't understand why this loop could make any sense there, but anyway....) Note that the software watchdog bites every 3.2 seconds, and the hardware watchdog after ~ 6 seconds (typically 7s or if you are lucky after 8s). btw: If you should ever need to disable the software watchdog and live with the hardware watchdog alone, you can do it like this:

system_soft_wdt_stop(); // disable the software watchdog
system_soft_wdt_feed(); //this feeds the software AND the hardware watchdog
// now, you can place your code here -- it has ~ 6 seconds until the hardware watchdog bites
system_soft_wdt_feed(); // feed again
system_soft_wdt_restart(); // enable software watchdog again

Or, as a simple example, the basic sketch is:
(imagine busy_1second() is when your while loop executes for 1 second)

extern "C" {
    #include "user_interface.h"
}

int j = 0;
unsigned long a, b;

void setup() {
    delay(1000);
    Serial.begin(115200);
    Serial.println("Setup...");
}

void ICACHE_RAM_ATTR busy_1second() {
    unsigned int i, iterations;
    if (F_CPU == 160000000) {
        iterations = 13333332;
    }
    else {
        iterations = 6666666;
    }
    for (i = 0; i < iterations; i++) {
        asm(
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
            "NOP;"
        );
    }
}
void loop {
SEE NEXT ...
}

First, use for the loop:

void loop() {
    Serial.println("Getting busy in 3 seconds...");
    delay(3000);
    Serial.println("now!");
    system_soft_wdt_stop();
    system_soft_wdt_feed();
    system_soft_wdt_restart();
    for (j = 1; j < 20; j++) {
        a = micros();
        busy_1second();
        b = micros();
        Serial.println(b - a);
        if (j % 6 == 0) {
            system_soft_wdt_feed();
            Serial.println("HW Dog fed! Miam miam :-o ");
        }
    }
    // system_soft_wdt_feed();
    // system_soft_wdt_restart();
}

Aua! The Software Watchdog bites every 3 seconds!

Port open
Setup...
Getting busy in 3 seconds...
now!
1000039
1000012
1000000

Soft WDT reset

ctx: cont 
sp: 3ffef830 end: 3ffefa20 offset: 01b0

Now, we want to be bitten by the Hardware Watchdog instead, so:

void loop() {
    Serial.println("Getting busy in 3 seconds...");
    delay(3000);
    Serial.println("now!");
    system_soft_wdt_stop();
    system_soft_wdt_feed();
    // system_soft_wdt_restart();
    for (j = 1; j < 20; j++) {
        a = micros();
        busy_1second();
        b = micros();
        Serial.println(b - a);
        if (j % 9 == 0) {
            system_soft_wdt_feed();
            Serial.println("HW Dog fed! Miam miam :-o ");
        }
    }
    system_soft_wdt_feed();
    system_soft_wdt_restart();
}

Ouuuch!

Getting busy in 3 seconds...
now!
1000039
1000006
1000000
1000000
1000000
1000001
1000001
1000000

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1264, room 16 
tail 0
chksum 0x0f

Finally, we put the Software Watchdog asleep and are feeding the Hardware Watchdog every 6 seconds :-)

void loop() {
    Serial.println("Getting busy in 3 seconds...");
    delay(3000);
    Serial.println("now!");
    system_soft_wdt_stop();
    system_soft_wdt_feed();
    // system_soft_wdt_restart();
    for (j = 1; j < 20; j++) {
        a = micros();
        busy_1second();
        b = micros();
        Serial.println(b - a);
        if (j % 6 == 0) {
            system_soft_wdt_feed();
            Serial.println("HW Dog fed! Miam miam :-o ");
        }
    }
    system_soft_wdt_feed();
    system_soft_wdt_restart();
}

And hence now the loop runs forever, without any bites by the watchdogs:

Getting busy in 3 seconds...
now!
1000002
1000007
1000001
1000000
1000000
1000000
HW Dog fed! Miam miam :-o 
1000001
1000007
1000000
1000001
1000001
1000000
HW Dog fed! Miam miam :-o 
1000000
...

Second, if your sensor is not working with the wire library, you may give my own i2c implementation a try, I wrote it in assembly, so timing etc. should be correct, it's called brzo_i2c.. (You will have to adapt the i2c communication, but should not be that much of work ;-)

Third, since you are using adafruit's breakout board with an onboard regulator and those MOSFETs for the level shifting: It might be that this breakout board has the same hardware issures as the HTU21 breakout board, when using it with the esp8266 and 3.3V, see readme with brzo_i2c example.

Excellent information @pasko-zh
Could I just ask you to describe what a software and hardware watchdog is i.e. the difference between one and the other.

@pasko-zh could I also ask about delay() in relation to soft and hardware resets. I thought I had seen a fairly official statement (Espressif ?) that delay should be no longer than 20 milliseconds. How does this tie up with your 3.2s / 6 to 8s?

@pieman64 I will try to explain a bit more:

Software vs. Hardware Watchdog: Well, it is a terminology used in the context of the esp8266 (also, officially by esspressif). I don't know how they exactly implemented it or which circuit they are using. Obviously, there are two of them ;-) From the term "hardware" watchdog I am thinking of something like the MAXIM watchdog chips. So probably it is something in hardware, while the former lays in deep in the SDK (?)
To me, it was rather confusing at the beginning, because nowhere it was explained how they both work together...

Watchdogs intervals : I did the above mentioned expirements a rather long time ago. Then, later, I did some googeling and I found this official statement by esspressif. That's why I know that the software watchdog bites after 3.2 s (I had just 1s loops in my tests, so I thought of 3s). Then, for the hardware watchdog resets, the tests gave 7s--8s, whereas esspressif talks about 6s. So, to be on the safe side I would go for 6s for the interval of the hardware watchdog.

Feeding the software watchdog: You can do it in several ways

  1. With Calls to the SDK, like system_soft_wdt_stop(); system_soft_wdt_feed(); system_soft_wdt_restart();
  2. Use delay(.), but note that delayMicroseconds(.) does NOT feed it!
  3. Use yield(), this does a context switch from user to system context, and in the latter amongst other things, the software watchdog is fed
  4. Everytime you iterate over your main loop, i.e. loop { .... } is called
  5. Maybe other options I am not aware of ;-)

Thus, you have to make sure that one of these options is uesd within an interval <3.2s. Now, imagine you have a rather "long" loop and you are doing some time consuming stuff, like for instance a wifi scan, then you should explicitly use something from 1. -- 3. I suspect that also in this issue it is the case, where the wifi_scan already takes around 2.1s, behind the scenes this is a call to one (!) SDK function, wifi_station_scan(.).

Timing: There are a couple of "timings" we should take care of, when programming the esp. You can find these in the SDK docu on page 15. And in there, you will find for instance "please do not occupy CPU more than 15 ms." Not that delay(.) can be as long as you want, it is not a busy waiting. My comment above (if you want to use the cpu longer than 3.2s) was a bit missleading in that sense, I will correct it, thanks for that.

@pasko-zh,
Very interesting and informative description on ESP8266 watchdog. :+1:
Please consider adding it to documentation / FAQ, e.g. "Why ESP8266's watchdog is bugging me" :smile:
Krzysztof

@pasko-zh do you have any idea what would be reason for long time hardware reset , for example my code works about 20 minuets and then reset

@mkeyno, could you point me to your code? Without it, it's hard to tell. Is it an exception, or is it first a Soft WDT reset followed by Hard WDT reset?

@krzychb Of course, I am happy to document it. Just some questions: Where should I place that info:

  • Maybe here somewhere, with a new section "Watchdog"?
  • Or in the FAQ as you mentioned?

Thanks, Pa拧ko

@pasko-zh,

cc: @igrr / @me-no-dev to step in if required.

This is great! I was thinking you could expand on your first and second post to create a new FAQ . People are frequently coming here discovering one or both watchdogs and asking why they are resetting ESP. The goal would be to describe why watchdog is in place and how to let it do his task. This is by structuring the code without resorting to ...soft_wdt... functions.

If this is too big task, then what about compressing your both posts into shorter / more formal description that would fit into a new section "Watchdog" under Reference.

Krzysztof

P.S. Love your asm insertions! Would not go too much beyond nop so not to freak out less experienced audience :smile:

@krzychb, cc @igrr , @me-no-dev
Finally (was busy with my "other work" ;) I've updated the FAQ section about WDT resets. Please have a look at my changes in the pull request

Hello,
I have the same Problem as @AnduriI
Tried 3 different NodeMCUs and BME280 and SSD1306 with I2C.
I also tried differnt sketches (I2C Scanner for example) and differnt wiring.
All with the wire.h configuration.
And with the same result: No functionality, sometimes resets...

Any ideas... Thanks!

@TS-Tec :

  1. Are you using the NodeMCU Firmware or the Arduino Core? If it is the former, you may better ask your questions in the NodeMCU

  2. Is i2c not working in general or only with the two sensors BME280 and SSD1306? I.e. do you have at least one i2c device working?

Hello,

had the same Problem like Anduril in the first post. I also tryed to use ESP12-E (NodeMCU 1.0) with BMP180 Pressure Sensor and Arduino IDE and get the same problems of bootloops after setting "Wire.begin();" in the Sketch.

I solved the Problem by updating the "Wire.h" file of the Arduino-IDE manually. My preinstalled Wire.h looked like that you can find in here: https://projects.cs.uaf.edu/redmine/projects/cyberalaska/repository/revisions/master/entry/include/arduino/libraries/Wire/Wire.h

and i replaced it with the Wire.h you can find here: https://github.com/esp8266/Arduino/blob/master/libraries/Wire/Wire.h

So in the Notes of the preinstalled Wire.h you can read that the last modification was made from Todd Krein in 2012!!! In the Wire.h from Guthub the last modification was done by Hrsto Gochkov and Ivan Grokhotkov in 2014 / 2015. The important addition is that the Wire.h has now ESP8266 support. Now everything works fine and without any bootloops.

kind Regards
Sepp

Closing as user-error.

To solve this problem I just comment while (1) {}
in
if (!bmp.begin()) { Serial.println("Could not find a valid BMP085 sensor, check wiring!"); while (1) {} }
And now code continues working.

I'm facing the same issue with ESP-8266 and the BMP280. I'm using the I2C scanner to test various sensors, and it is capable of finding the address of all of them, except the BMP280. I have checked the file Wire.h, it contains the modifications done by Gochkov in 2015.

In my case: if i am connect bmp280 to gpio4+gpio5. Vcc and gnd on esp8266 - sensor not running.
But if i am connect vcc and gnd to arduino(5v) and scl, sda to gpio4/5 on esp - its works! Looks like some power issue. But the tester shows me 5V on esp power pins.

My case: I use esp01s so run Wire.begin(2,0) and don't forget to pull-up CSB pin of BMP280 to operate in I2C mode, and decide what I2C address will be used: 0x76 when SDO connected to GND, and 0x77 when SDO connected to VCC.

Was this page helpful?
0 / 5 - 0 ratings