Problem with I2C communication between PRO MICRO and NodeMCU V3

Hi. Please help me with this one. I've tried different approaches to send more than one byte over I2C, but it only works with just one. I must be messing with the data types, but I'm not able to see how.

This is the master:

#include <Wire.h>
byte buf[4];

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

void loop() {
  Wire.requestFrom(8,4);   
  
  buf[0] = Wire.read(); 
  buf[1] = Wire.read(); 
  buf[2] = Wire.read(); 
  buf[3] = Wire.read(); 

  Serial.print("buf[0]: ");
  Serial.println(buf[0]);
  Serial.print("buf[1]: ");
  Serial.println(buf[1]);
  Serial.print("buf[2]: ");
  Serial.println(buf[2]);
  Serial.print("buf[3]: ");
  Serial.println(buf[3]);

  delay(1000);  
}

And the slave:

#include<Wire.h>
byte buffer[4];

void setup() {
  Serial.begin(9600);
  Wire.begin(8);
  Wire.onRequest(requestEvent); 
}

void loop() {
}

void requestEvent()
{
  float firstValue = 7.5; 
  int secondValue = 75; 

  buffer[0] = (byte)firstValue;
  buffer[2] = (byte)secondValue;
  Wire.write(buffer, 4);
  
  Serial.print("firstValue: ");
  Serial.println(firstValue);
  Serial.print("secondValue: ");
  Serial.println(secondValue);
  Serial.print("buffer 0: ");
  Serial.println(buffer[0]);
  Serial.print("buffer 1: ");
  Serial.println(buffer[1]);
  Serial.print("buffer 2: ");
  Serial.println(buffer[2]);
  Serial.print("buffer 3: ");
  Serial.println(buffer[3]);
}

The output from the slave make sense to me:

firstValue: 7.50
secondValue: 75
buffer 0: 7
buffer 1: 0
buffer 2: 75
buffer 3: 0

But not the one from the master:

buf[0]: 1
buf[1]: 255
buf[2]: 255
buf[3]: 255

This is the circuit between both devices and the i2c logic level converter in between:

It works with just one byte without the array (tested with a sensor), but not with >1. In the example I use 4 bytes as int uses 2, but they should be converted to bytes with (byte), shouldn't they?

Apologies if the error is too obvious, still a lot to learn...

But not the one from the master:

It does to me. Remove all Serial.print()s from you interrupt handler on the slave and it will probably work. This usage of the Serial object inside an interrupt handler is a no-go anyway as it's function depends on interrupts to work which isn't the case inside an interrupt handler. If you fill the buffer you'll end in an endless loop.

The ATmega32U4 does clock stretching while the I2C interrupt to gather the data runs. Your example shows that the ESP8266 doesn't support clock stretching and just continues to send clock ticks and read the data.

I would bet that the return value of the Wire.requestFrom() is not 4 as it should be if there was no error in the transfer.

Thank you for your help, pylon.

I will remove the Serial.print() from the slave and verify the return value of Wire.requestFrom(). And defenitely will find more info about clock stretching and interrupt handlers, as they are new concepts to me.

Thanks a lot!

Your requestEvent() handler is called in interrupt context. During that all interrupts are disabled.

Removed the Serial.print() and the issue is fixed. Thank you for that, pylon!

I have no idea about interrupts. It's going to be fun learning about them.

I have applied the same solution to my project, where the values a differential pressure sensor and a MQ-2 sensor are sent to master, and after a lot of tests, I have noticed that the MQ-2 code was causing the same issue, maybe because it was taking 5 reads from the analogue pin connected to the MQ-2 with a delay of 50 in between them, and returning the average. When sent the raw value it worked. I will analyse the code and move it to the ESP8266.

Thank you again, pylon!

Are functions outside the requestEvent() function part of the interrupts disabled when this one is called, even though they are called from within requestEvent()?

void requestEvent()
{
  anyFunction();
  //some code
}

type anyFunction()
{
  //some code
}

Is anyFunction() disabled?

Is anyFunction() disabled?

No, the function is enabled, but you have to treat the code in anyFunction() as it would be inside requestEvent(), so no calls to stuff that needs interrupts enabled (p.e. Serial object) because during the complete run of requestEvent() (and anyFunction() is part of that) interrupts are disabled.