1

I am currently working on a project and want to transmit the information from a distance sensor from one arduino the another, but what i receive in the master board are not the correct measurements.

Slave Code

#include <Wire.h>

const int trig = 12; 
const int echo = 13;

int state = 0;
int count = 0; 
int contor = 0;
int set_distance = 5;
long duration, Distanceincm;

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT);
  Serial.begin(9600);           // start serial for output
}

void loop()
{
 digitalWrite(trig, HIGH);
  delay(15);
  digitalWrite(trig, LOW);
  duration = pulseIn(echo, HIGH);
  Distanceincm = (duration / 58);
  if (state == 0 && (Distanceincm < set_distance) ) {
    state = 1;
    count++;
    contor++;
    Serial.print("Distance=");
    Serial.println(Distanceincm);
    Serial.print("Count=");    
    Serial.println(count);
    Serial.print("ConuntDay=");    
    Serial.println(contor);
  }
  if (Distanceincm > set_distance) {
    state = 0;
  }
  delay(100);
}



// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // loop through all but the last
  {

  Wire.write(Distanceincm);    
  Wire.write(count);
  Wire.write(contor);
}

Master Code

#include <Wire.h>

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start Serial for output
}

void loop()
{
  Wire.requestFrom(2, 6);    // request 6 bytes from slave device #2
  while(Wire.available())    // slave may send less than requested
  { 
    long a = Wire.read(); 
    Serial.print ("Distance = ");
    Serial.println(a);   

    int b = Wire.read(); 
    Serial.print ("Count = ");
    Serial.println(b);

    int c = Wire.read(); 
    Serial.print ("CountDay = ");
    Serial.println(c);
  }

  delay(500);
}

What i get on the serial monitor is this : enter image description here

6
  • Your receive event should be reading some data. If you don't read any data then that while loop becomes an infinite loop.
    – Delta_G
    May 30 '20 at 20:26
  • 1
    You should also note that wire.read gets you a byte, not a long or an int.
    – Delta_G
    May 30 '20 at 20:29
  • 2
    What I see right away is, that you assign address 4 to the slave, but the master reads from address 2.
    – Sim Son
    May 30 '20 at 20:29
  • You have data coming in over I2C. Compared to the speed of the processor, that data is coming in really slowly. When the first byte of the data arrives, wire.available() becomes 1. Now your code enters that loop. It tries to read a bunch of stuff from wire, but only the 1 byte has shown up so far. When you call read and there's nothing available it returns -1 which (as a byte) looks like 255 when it gets stuffed into a byte. I bet something like that is happening but I don't have time to look at Wire and see how it returns.
    – Delta_G
    May 30 '20 at 20:33
  • 1
    @Delta_G Wire.requestFrom() is a blocking call. When it exits the whole I2C message was received. It also returns the number of received bytes, I think.
    – chrisl
    May 30 '20 at 20:52
1

Your code has some flaws, that I will address here:

  • The main problem, that you see here, is that you only let the slave write data into the buffer, when it is receiving data. But you never send data to it. So there is never anything in the buffer.

    In the slave code you have

    void receiveEvent(int howMany)
    {
      while(1 < Wire.available()) // loop through all but the last
      {
    
      Wire.write(Distanceincm);    
      Wire.write(count);
      Wire.write(contor);
    }
    

    Besides the unbalanced parentheses (which let's me think, that this might not be the actual code, that you are running currently), you are writing to the buffer only inside the receiveEvent(). That only get's called, when the master does a Master-Write transmission (via Wire.beginTransmission() and Wire.endTransmission()). In the master code you are only using Wire.requestFrom(). If you want to send back data on a request, you need to use the onRequest callback of the Wire library. You need to set the appropriate callback function:

    Wire.onRequest(onRequestEvent);
    

    And then write data inside that callback function:

    void onRequestEvent(){
        Wire.write(...)
    }
    

    I will explain how to write the values correctly in a different point below.

  • In the receiveEvent() function you want to loop through all received bytes but the last. But you are not doing anything with the data. You aren't even reading it. As your code currently stands, this line does not make any sense.

  • In your master code you are requesting data from the slave with the address 2. Your slave code initiates the Wire library with the address 4. From that point there should never be a successful I2C transmission, unless you have another slave on the bus with the address 2 (another point, where I suspect, that these codes are not exactly the ones used for the test).

  • In the master code you are using

    while(Wire.available())
    

    because the slave might have send less data, than requested. You are testing, that the number of available bytes is not zero. But then you are reading 3 bytes without checking again, which means, when you received 2 byte, you still would try to read 3 bytes, which cannot give good results.

    The condition should be Wire.available() >= 3, to check, if at least 3 bytes are in the buffer, because you are reading 3 bytes from it. But also the loop does not make much sense to me. As you have a binary protocol here, how would you treat additional data (meaning excess data, send after the first valid data frame)? You want to receive 3 values per transmission and you are reading 3 values in one iteration of the loop. If there where more data in the buffer, you would be reading the next 3 bytes, throwing away the first 3 bytes. I don't think, that is what you want.

    Instead use the return value of Wire.requestFrom(), which is the number of received bytes. Use it to distinguish valid transmissions, from those, which are too short:

    uint8_t n = Wire.requestFrom(4, 3);
    if(n >= 3){ // at least 3 bytes where received
        uint8_t value1 = Wire.read();
        uint8_t value2 = Wire.read();
        uint8_t value3 = Wire.read();
    }
    

    Any transmission, that is too small, will get ignored. Excess data in too long messages will also be ignored. Note, that I used uint8_t, which is an unsigned integer with 8 bits (1 byte) here for simplicity of that points logic. More about your data types and their implications on your code below.

  • For the to send datatypes on the slave you have int and long. int is a signed integer with 16 bits, long is a signed long integer with 32 bits. (I suspect you don't need a long for the distance, as ultrasonic sensors mostly reach only 3m, but that is up to you). In contrast the argument of Wire.write() is an uint8_t, which has only 8 bits. That call is used to put exactly 1 byte into the buffer, not more. When you are using

    Wire.write(Distanceincm);
    

    you are putting the least significant byte of Distanceincm into the buffer. The rest will just be thrown away (this happens at the cast from long to uint8_t). This works as long, as the value is between 0 and 127 (which is the upper part of the int8_t value range). With higher (or lower) values your received data will be garbage. Instead you need to write every byte of your data into the buffer:

    Wire.write(Distanceincm  & 0x000000FF);
    Wire.write((Distanceincm & 0x0000FF00) >> 8);
    Wire.write((Distanceincm & 0x00FF0000) >> 16);
    Wire.write((Distanceincm & 0xFF000000) >> 24);
    

    With bitwise ANDing (&) of the hexadecimal value 0x000000FF, we are deleting all bits, beside the least significant byte (analog with 0x0000FF00 for the second least significant byte, and so on). Then we are shifting the corresponding byte to the position of the least significant byte (8 bits for the seconds least significant byte, 16 bits for the third least significant byte, and so on), which then gets used by Wire.write(). To better understand this, read about bitwise operators on the web.

    You have to send both integer values in a similar fashion (though they both have only 2 bytes instead of 4).

  • Now that we are sending more bytes, we also need to receive and read all these bytes correctly. First we need to request the correct number of bytes. 4 bytes for the long value and 2 bytes for both int values --> 8 bytes. So you need to request 8 bytes:

    Wire.requestFrom(4, 8);
    

    Then you need to read these bytes into the corresponding variables:

    long distance = Wire.read()
                    | (Wire.read() << 8)
                    | (Wire.read() << 16)
                    | (Wire.read() << 24);
    

    We are using the bitwise OR (|) to put the bytes together, shifting every byte to its correct position in the long value. In a similar way you are putting together the 2 int values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.