BMP280 sensor not found when using virtualwire

For my weatherstation project I want to send data from an arduino nano connected to a BMP280 and a 433MHz tx to a arduino uno with 433 rx and a display and show the data on this display. The problem that occurs, however, is that I am able to read sensor data only as I exclude all lines relative to sending data over 433 with the virtualwire library. Does anyone know where this problem comes from? I read about changing the send/receive pin which I did by vw_set_tx_pin(7) instead of pin 12. Did not work. When I run the code the serial monitor shows that it cannot find my BMP sensor although I have been able to use it before without virtualwire included. It seems that somehow its address is being captured.

The same thing actually happens for the arduino display. Somehow the display will not work anymore when using virtualwire.

I hope someone can help me out. Thanks in advance. I added my code so you can look at it. As you can see in the transmitter code i only want to send temperature and pressure.

Receiver code (it also has a dht22 but that’s irrelevant):

// Weather Station
// Arduino Uno
// OLED display and DHT22 sensor

#include <DHT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <VirtualWire.h>
#include <SPI.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

float t,h;

char temperatureChar[10];
char pressureChar[10];

struct package
{
  float temperature = 0.0;
  float pressure = 0.0;
};

typedef struct package Package;
Package data;

// SETUP
void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }

  vw_set_rx_pin(7);
  vw_setup(500);
  vw_rx_start();
    
  delay(2000);
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 30);
  display.println("Weather Station v1.0");
  display.setCursor(0, 40);
  display.println("J.R. de Haan");
  display.setCursor(0, 50);
  display.println("Copyright 2020");
  display.setCursor(0, 10);
  display.println("Initializing.");
  display.display();
  delay(1000);
  display.setCursor(0, 10);
  display.println("Initializing..");
  display.display();
  delay(1000);
  display.setCursor(0, 10);
  display.println("Initializing...");
  display.display();
  delay(1000);
  
  dht.begin();
}

void loop() {
  t = dht.readTemperature();
  h = dht.readHumidity();
  if (isnan(t) || isnan(h)) {
    Serial.print("DHT values not read!");
    return;
  }

// DISPLAY DATA
  display.clearDisplay();
  
  // TEMPERATURE
  display.setTextSize(1);
  display.setCursor(0, 10);
  display.println("Temperature indoor: ");
  display.setTextSize(2);
  display.setCursor(0, 20);
  display.println(t);
  display.setCursor(100, 20);
  display.println((char)247);
  display.setCursor(115, 20);
  display.println("C");
  
  // HUMIDITY
  display.setTextSize(1);
  display.setCursor(0, 40);
  display.println("Humidity indoor: ");
  display.setTextSize(2);  
  display.setCursor(0, 50);
  display.println(h);
  display.setCursor(115, 50);
  display.println("%");

  display.display();
  delay(1000);
  
  // OUTDOOR 433 MHz
  uint8_t buf[sizeof(data)];
  uint8_t buflen = sizeof(data);
  if (vw_have_message())  // Is there a packet for us? 
    {
      vw_get_message(buf, &buflen);
      memcpy(&data,&buf,buflen);
      Serial.print("\nPackage:");
      Serial.print(data.temperature);
      String temperatureString = String(data.temperature,1);
      temperatureString.toCharArray(temperatureChar,10);
      
      String pressureString = String(data.pressure,1);
      pressureString.toCharArray(pressureChar,10);
      
      Serial.print("\n");
      Serial.println(data.pressure);
    }


  delay(10000);
  display.clearDisplay();

  display.setTextSize(1);
  display.setCursor(0, 10);
  display.println("Temperature outdoor: ");
  display.setTextSize(2);
  display.setCursor(0, 20);
  display.println(data.temperature);
  display.setCursor(100, 20);
  display.println((char)247);
  display.setCursor(115, 20);
  display.println("C");
  display.setTextSize(1);
  display.setCursor(0, 40);
  display.println("Pressure outdoor: ");
  display.setTextSize(2);  
  display.setCursor(0, 50);
  display.println(data.pressure);
  display.setCursor(115, 50);
  display.println("hPa");
  display.display();
  delay(10000);
}

Transmitter code:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <VirtualWire.h>

#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BMP280 bme;

struct package
{
  float temperature ;
  float pressure ;
};

typedef struct package Package;
Package data;

void setup() {
  Serial.begin(115200);

  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  vw_set_tx_pin(12);
  vw_set_ptt_inverted(true);
  vw_setup(500);
}

void loop() {
  Serial.print("Temperature = ");
  data.temperature = bme.readTemperature() - 2;
  float temp = bme.readTemperature() - 2;
  Serial.print(temp);
  Serial.println("*C");

  Serial.print("Pressure = ");
  data.pressure = bme.readPressure() / 100.0F;
  Serial.print(bme.readPressure() / 100.0F);
  Serial.println("hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println("m");

  Serial.println();
  delay(1000);

  vw_send((uint8_t *)&data, sizeof(data));
  vw_wait_tx();
  delay(2000);
}

Add a comment section where you carefully explain which pins are used, even the “dummy” pins.

The VirtualWire always uses the ‘rx’, ‘tx’, and ‘ptt’ pins. When you ignore them, they will be set to default pins. See the source of the library for that.

What I do, is to always set all three pins for the VirtualWire library. I set the pins that I don’t use to unused “dummy” pins.
I never use the vw_set_ptt_inverted() function, and I think that no one does. You can remove that.

Why do you include <SPI.h> in the Transmitter ? If you don’t use it, please remove that.

The sweet spot for the speed is 2000. When lowering that value, it takes too long and there is more chance that noise will disturb it. When using a value higher than 2000, the 433MHz modules might have a worse signal and the receiver might have more trouble keeping up with the timing. So use 2000 instead of 500.

The DHT library will prevent that the VirtualWire library will work in receiver mode. The DHT library turns off the interrupts for some time and the Receiver needs to handle many interrupts to be able to receive and decode the signal.

Can you get rid of the DHT sensor ? Perhaps you can replace the BMP280 with a BME280. But be sure that you have fixed the voltage levels for the I2C bus.

Why do you include <SPI.h> in the Receiver ? If you don’t use it, please remove that.

The BMP280 is a 3.3V sensor and the Arduino Nano is a 5V. Do you have a module with I2C level shifters for SDA and SCL ?

An OLED display operates at 3.3V and uses a 3.3V I2C bus. Do you have a OLED module (for example from Adafruit) that has I2C level shifters for SDA and SCL ?
Even if you do, the OLED displays can disturb the I2C bus for others, that is what they are known for.

Those two Adafruit libraries need a lot of memory. I don’t know if you can combine it with VirtualWire and a BMP280/BME280 library and the sketch. Can you add the function at the very bottom of this page: Arduino Playground - AvailableMemory and call it as the last thing in setup().

There is something that I don’t know. The display via I2C generates a lot of I2C interrupts. I don’t know if the Receiver can receive something via VirtualWire with so many I2C interrupts.

Try to replace this:

struct package
{
  float temperature = 0.0;
  float pressure = 0.0;
};

typedef struct package Package;
Package data;

with this:

struct Package
{
  float temperature = 0.0;
  float pressure = 0.0;
};

Package data;

The “struct Package” is already a typedef in C++.

First of all, thanks for your useful comments. As you can understand, I am quite a newbie in the Arduino world. Especially in terms of understanding some communication of the interfaces and the limitations of the hardware.

Koepel:
What I do, is to always set all three pins for the VirtualWire library. I set the pins that I don’t use to unused “dummy” pins.

Why do you include <SPI.h> in the Transmitter ? If you don’t use it, please remove that.

Can you get rid of the DHT sensor ? Perhaps you can replace the BMP280 with a BME280. But be sure that you have fixed the voltage levels for the I2C bus.

Why do you include <SPI.h> in the Receiver ? If you don’t use it, please remove that.

The BMP280 is a 3.3V sensor and the Arduino Nano is a 5V. Do you have a module with I2C level shifters for SDA and SCL ?

An OLED display operates at 3.3V and uses a 3.3V I2C bus. Do you have a OLED module (for example from Adafruit) that has I2C level shifters for SDA and SCL ?
Even if you do, the OLED displays can disturb the I2C bus for others, that is what they are known for.

Those two Adafruit libraries need a lot of memory. I don’t know if you can combine it with VirtualWire and a BMP280/BME280 library and the sketch. Can you add the function at the very bottom of this page: Arduino Playground - AvailableMemory and call it as the last thing in setup().

To shortly comment on some questions you asked:
I can remove SPI.h from the transmitter. I put it in the receiver code because somehow the display needed to have this included in order to function.

I can remove the DHT sensor. I have already ordered a few BME280 sensors to use. My question here is: is there a difference between I2C busses in terms of voltage? I thought I could run the BMP280 of a 5V or 3.3V from the nano because it has a voltage regulator on board which regulates it to 3.3V. I thought the SDA and SCL are only for data, but this data is then sent with 3.3V or 5V?

Finally, these libraries need a lot of memory I can understand, but isn’t this checked before uploading to the arduino?

Memory

When the code fits in the Flash memory, then that is okay. However, the sram is the problem. In the lower memory is the heap growing upwards and the stack is growing downwards. The compiler will tell how many Flash and sram memory is used, but that is static memory. Some libraries allocate a lot of memory from the heap.

This is explanation for the Arduino memory: https://learn.adafruit.com/memories-of-an-arduino/arduino-memories.

The function freeMemory() from Adafruit is basicly the same as the one I wrote about: https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory#sram-370031-5.

I2C bus voltage levels

A Arduino Uno has a ATmega328P microcontroller running a 5V. It has (weak) internal pullup resistors for SDA and SCL. The ATmega328P needs 3.0V to see it as a 'high' for a normal digital input, but it needs 3.5V for the SDA and SCL.

When a 3.3V sensor is connected to the Arduino Uno and that sensor uses almost no current or is in sleep-mode, then the (weak) internal resistors of the Arduino Uno can left the voltage of SDA and SCL to 5V and damage the sensor.

When using a strong pullup resistor to 3.3V, then the voltage does not get a lot higher than 3.3V, but then the ATmega328P has not enough voltage to see a 'high' level.

The I2C bus was originally a open-collector bus, but every microcontroller and sensor has clamping diodes to protect against voltage spikes. A high voltage on SDA and SCL will leak towards the power voltage and might lift the voltage of the sensor.

A "I2C level shifter" will solve this problem, but a I2C level shifter makes the signal also a little weaker (put two I2C level shifters after each other and the I2C bus might no longer work).

When using sensors, it is easier to use a 3.3V Arduino board with a SAMD M0+ processor (as used in most MKR boards) and forget all this voltage level problems.

SPI bus

I don't know what including the SPI.h does. Can you avoid the SPI pins ? Set the unused "dummy" pins of VirtualWire to other pins please.

A note

Many I2C modules are sold as 5V-compatible, but that is when only that single module is used on the I2C bus and it might work outside the specifications in the datasheet. Combined with other modules, things can go hairy. Some SPI modules don't even allow any other SPI module ! Some libraries assume that they are the only thing that is used.

There is a lot of cheap hardware (and bad software) that do not go well with other hardware and software. That is why it is always better to buy from a seller who actually think about what they are doing: Adafruit, Sparkfun, Pololu, and so on. They have good explanation, they are not afraid to tell the downside, they have tutorials and a forum to ask questions.

Thanks for your elaborate answer, this helps me out a lot.

As you can imagine I am looking for the functionality to have a small board have a bme/bmp280 connected and send this data over a 433 MHz transmitter. As receiver I want a board that can host an oled display and also have a temperature sensor such that I can display indoor and outdoor temperature on the oled. I was not aware there were different i2c voltages but I am now aware of this. What would you recommend for me to make this combination of sensors work?

Thanks in advance

That depends on the modules that you have. Some have a I2C level shifter on the module and some don't. I don't know if the Adafruit libraries together with a BME280/BMP280 will use too much sram memory. Does the outdoor Arduino have 5V or is it battery powered ?

The DHT, OneWire, Neopixel and FastLED libraries turn off the interrupts for some time. They will cause problems with receiving with VirtualWire. I hope that the OLED display with many I2C interrupts together with a VirtualWire receiver will work.

Hey, sorry for my late reply. I am quite busy with my master thesis.

The plan is the power the outdoor arduino with a battery pack over the Vin port. I do not know if this makes any difference for the I2C functionality?

Regarding the DHT library. My plan is to remove this library such that I can replace the sensor with a BME. Or is there a library that does not turn off interrupts and can read DHT22?

The modules that I currently own are a Arduino Nano and Uno (which I initially plannend to use for this project). Then the I2C problem arised and a friend suggested to maybe use a STM32 for the outdoor part. Do you have any experience with this? And indoor, maybe something with multiple I2C busses is necessary to both receive data from the BME and send to the OLED?

Some parts of the timing-protocol for the DHT22 are very strict, so there is often a small part with interrupts disabled. I have taken the files from a DHT library, and removed the disabling of interrupts. When I get the wrong data, then I ignore it. So hopefully I am able to get the right values now and then. That not very reliable, but it was good enough for me 8)

I don't know the STM32. The Arduino libraries for the STM32 were not 100% compatible in the past. Maybe that has been improved.

For a battery operated Arduino, have a look at the MKR boards. They have also a RTC inside to keep the time and date running. Adafruit has "Feather" boards to be used with a battery. I think the MKR boards were made when the "Feather" boards turned out to be a success. A Arduino board has the best long-term support.

Using a Arduino Uno with a battery at VIN will drain the battery very quick. The Nano not so fast, but that is still far from ideal. A bare-bone ATmega328P can run 1 or 2 years on three AA batteries: https://www.gammon.com.au/power. Instead of a bare-bone, it is easier to use a 8MHz Pro Mini board and remove the led, and the voltage regulator and power it with the three AA batteries to VCC. The disadvantage is that you need a programmer or usb-serial module to upload a sketch. And when you want to change the sketch, then you can't find the programmer or the usb-serial module (if you are like me ;) ).

Suppose you have a 12V battery. Then you could buy a DC/DC-converter for 7.5V and feed that into the VIN pin. Or one with 5V output and feed that to the USB connector. It not recommended to power an Arduino via its 5V output pin, but that is allowed as long as the 5V power is not too strong, because a peak could destroy the onboard voltage regulator :astonished:

You should not ignore that you may not connect the SDA and SCL of the BME280 to the SDA and SCL of a 5V Arduino board :stuck_out_tongue_closed_eyes: In an other thread someone gave a link so I could check it and it was the right one.