Send accelerographs data to a master I2C

Hi,
I have two arduinos connected by I2C. The master board is a MEGA and the slave, a UNO, I’m trying to send the raw data of the accelerograph MPU GY-521 who is connected to a slave.

I don’t have problem getting the data of the sensor, and also sending data to the master, no problem with separate code, when I merge the codes, the arduino master don’t receives anything. This are the codes:

Master

#include <Wire.h>

const byte slaveAddress = 2;
const byte dataCount = 3;

union
  {
  float floatData [dataCount];
  byte  rawData [dataCount * sizeof (float)];
  } myData;

unsigned long lastSerialPrint = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  digitalWrite(SDA,LOW);
  digitalWrite(SCL,LOW);
}  // end of setup

void loop()
{
    if (Wire.requestFrom (slaveAddress, sizeof myData) == sizeof myData)
      {
      for (int i = 0; i < sizeof myData; i++)
         myData.rawData [i] = Wire.read ();
      }  // end if
         
    if(millis() - lastSerialPrint > 1000) //Like the Blink without delay example, true once a second
      {
      for (int i = 0; i < dataCount; i++)
        {
          int axis = (int) myData.floatData[i];
          Serial.print ("var ");
          Serial.print (i);
          Serial.print (" Value: ");
          Serial.println ();  
        } // end for
      lastSerialPrint = millis(); //Snapshot of when this happened, in milli seconds
      }  // end if
}  // ene of loop

Slave

#include <Wire.h>
#include "I2Cdev.h"
#include "MPU6050.h"

MPU6050 accelgyro;// class default

int16_t ax, ay, az;//acelerograph

#define OUTPUT_READABLE_ACCELGYRO//values in decimal
//#define OUTPUT_BINARY_ACCELGYRO//values in binary (16-bits)

volatile byte* Float1ArrayPtr; 
volatile byte* Float2ArrayPtr; 
volatile byte* Float3ArrayPtr;  

int Address = 2;  //This slave is address number 2
float floatData[3]; 

void setup()
{
  Wire.begin(Address);
  digitalWrite(SDA,LOW);
  digitalWrite(SCL,LOW);
  Wire.onRequest(requestEvent); // register event
  
  Serial.begin(38400);

  // initialize device
  Serial.println("Initializing I2C devices...");
  accelgyro.initialize();

  // verify connection
  Serial.println("Testing device connections...");
  Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  
}

void loop()
{
  accelgyro.getAcceleration(&ax, &ay, &az);
 
  floatData[0] = ax;    //axis x
  floatData[1] = ay;    //axis y
  floatData[2] = az;    //axis z
}

void requestEvent()
{
  byte* Data;
  Float1ArrayPtr = (byte*) &floatData[0];
  Data[0] = Float1ArrayPtr[0]; 
  Data[1] = Float1ArrayPtr[1]; 
  Data[2] = Float1ArrayPtr[2]; 
  Data[3] = Float1ArrayPtr[3]; 
  Float2ArrayPtr = (byte*) &floatData[1];
  Data[4] = Float2ArrayPtr[0];
  Data[5] = Float2ArrayPtr[1];
  Data[6] = Float2ArrayPtr[2];
  Data[7] = Float2ArrayPtr[3];
  Float3ArrayPtr = (byte*) &floatData[2];
  Data[8] = Float3ArrayPtr[0]; 
  Data[9] = Float3ArrayPtr[1]; 
  Data[10] = Float3ArrayPtr[2]; 
  Data[11] = Float3ArrayPtr[3]; 
  Wire.write(Data,12); //Send the 12 bytes (3 floats)
}

Someone note where can be my problem?

EDIT:
the sending code was taken from:

The Wire.begin() function initializes all the hardware and software for the I2C bus, so also for the SDA and SCL pins. The I2C bus is a open-collector bus, when a Master or a Slave wants to do something on the I2C bus, it makes the SDA or SCL low. No one can make it high, only the pullup resistors make the SDA and SCL high.

You see where I'm getting at ? After Wire.begin() you make SDA and SCL low (low means active), and thus blocking the whole I2C bus. Remove that.

Hi Peter_n,
you are right, I was having problem with the sensor, and someone suggested me add this, but thinking now, it doesn’t have sense.

So, I removed the code, but I keep with the problem, I realise that when I add

accelgyro.getAcceleration(&ax, &ay, &az);

the master let to receive information. I added to the previous code, and I set manually the 3 axes variables like this:

accelgyro.getAcceleration(&ax, &ay, &az);

floatData[0] = 12345.56; 
floatData[1] = 1234.12;
floatData[2] = 12345.56;

the master show a nan value

when I delete

accelgyro.getAcceleration(&ax, &ay, &az);

the information is back

EDIT:

I found a short way to get the raw data from the axis, but even with this can’t send to the master the master gets nothing:

#include<Wire.h>
const int MPU=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ;
void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}
void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  delay(333);
}

but even with this can't send to the master:

You can not send anything to a master. A slave has to respond to a request from a master in order to transfer data to it.

A slave can not initiate a request.

It seems to me like you want the Uno to be a slave to the Mega but a master to the accelerometer.

Grumpy_Mike

I just wrote wrong, but at this moment, as you can see in the previus code, the master is requesting the data, and the slave is sending.

that you wrote sound better to me, would be like this?

master slave MEGA-----UNO master | MPU slave

I didn't know it was possible, can you explain me how can I do it, or send me a page with this info? I didn't found it until now

thanks in advance

There are a number of problems with your Master and Slave sketch. The Master requests data without delay, the pData points to nothing, and so on. But that is not the problem.

Let's start all over from the start....

Is the Master and Slave and sensor all connected to the same I2C bus ? How will you avoid collisions ? A collision may not happen, so what you want is not possible. Let the Master request the data from the sensor.

When you have a Uno as Slave, and non-I2C sensors connected to the Uno (like DS18B20, DHT22, LDR, voltage meter, and so on) then this is possible. The Uno collects all data in its own time and can filter it or average it, and the Master requests now and then the filtered results.

When you do want to continue with everything on the same bus, you have to make very strict rules who may using the I2C bus and for how long. That will not be pretty.

  byte* Data;

This pointer points to nothing. You can NOT use it like an array until you point to some allocated memory.

You can only do that if your UNO has two I2C busses. As the hardware only has one bus then the second will have to be done in software. Look up the soft twi libary.

Alternately you can have a multi master I2C bus, however They are a lot more involved and can lead to latch up, depending on how they are implemented.

PaulS:

  byte* Data;

This pointer points to nothing. You can NOT use it like an array until you point to some allocated memory.

but without that pointer the IDE show me the error: invalid types ‘byte[int]’

Grumpy_Mike
I bought a new accelerograph the MMA7361 it seem to works better, however I still have a problem, and I’m sure it’s because I’m not understanding something.

now the code works, the master, request the info, and the slave gives, but just one time, after that, the slave freezes
what I want to do is the slave working all the time, it will save always the axis info in the SD, and when the master request some data it send to them, but I don’t know why it freezes.

this is the slave code

#include <Wire.h>
#include <AcceleroMMA7361.h>

AcceleroMMA7361 accelero;
int x;
int y;
int z;

volatile byte* Float1ArrayPtr;  //Float 1 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float2ArrayPtr;  //Float 2 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float3ArrayPtr;  //Float 3 Array Pointer (series of bytes, 4 bytes each float)

int Address = 2;  //This slave is address number 2
float floatData[3]; //Your float array

void setup()
{
  Serial.begin(9600);
  accelero.begin(13, 12, 11, 10, A0, A1, A2);
  accelero.setARefVoltage(5);                   //sets the AREF voltage to 3.3V
  accelero.setSensitivity(LOW);                   //sets the sensitivity to +/-6G
  accelero.calibrate();
  Wire.begin(Address);
  Wire.onRequest(requestEvent); // register event  
}

void loop()
{
  x = accelero.getXAccel();
  y = accelero.getYAccel();
  z = accelero.getZAccel();
  
  //Your code with putting values into your float array
  floatData[0] = (float) x;    //your code here
  floatData[1] = (float) y;    //your code here
  floatData[2] = (float) z;   //your code here
  
  Serial.print("\nx: ");
  Serial.print(x);
  Serial.print(" \ty: ");
  Serial.print(y);
  Serial.print(" \tz: ");
  Serial.print(z);
  
}
void requestEvent()
{
  byte Data[5];
  Float1ArrayPtr = (byte*) &floatData[0];
  Data[0] = Float1ArrayPtr[0]; 
  Data[1] = Float1ArrayPtr[1]; 
  Float2ArrayPtr = (byte*) &floatData[1];
  Data[2] = Float2ArrayPtr[0];
  Data[3] = Float2ArrayPtr[1];
  Float3ArrayPtr = (byte*) &floatData[2];
  Data[4] = Float3ArrayPtr[0]; 
  Data[5] = Float3ArrayPtr[1]; 
  Wire.write(Data,6); //Send the 12 bytes (3 floats)
}

the master code is the same, but with a delay of 200

We are trying to say that it is not possible. The Master could request data from the Slave, at the moment the Slave is requesting data from the sensor. You can not have two I2C sessions on the I2C bus at the same time.

but without that pointer the IDE show me the error: invalid types 'byte[int]'

The fact that it is a pointer is not the problem. That fact that it does not point to allocated memory is the problem. I am almost certain that you want an array there, NOT a pointer.

I also can’t see anywhere where you initialise the Float1ArrayPtr bunch of variables.

Peter_n: We are trying to say that it is not possible. The Master could request data from the Slave, at the moment the Slave is requesting data from the sensor. You can not have two I2C sessions on the I2C bus at the same time.

Now I'm only have I2C to connect the arduinos, the sensor (MMA7361) it's analog, so I shouldn't have the same problem like in the beginning. Right?

PaulS:

but without that pointer the IDE show me the error: invalid types 'byte[int]'

The fact that it is a pointer is not the problem. That fact that it does not point to allocated memory is the problem. I am almost certain that you want an array there, NOT a pointer.

I see, I edited the last code, I forgot to mention that code I took from http://forum.arduino.cc/index.php?topic=273328.0;topicseen

I'm just looking for a way to the master gets that 3 variables of the accelerometer, when it's required. is a more easy way to do it?

What I'm seeing now, is the slave starts over again every time the master ask for the data, I think this because every x time, the slave calibrate the sensor again and again

Grumpy_Mike: I also can't see anywhere where you initialise the Float1ArrayPtr bunch of variables.

it's almost at the beginning

volatile byte* FloatXArrayPtr;

gepd: Now I'm only have I2C to connect the arduinos, the sensor (MMA7361) it's analog, so I shouldn't have the same problem like in the beginning. Right?

I'm sorry, I missed that. That is okay of course.

That code from the forum is not the easiest code with the FloatArrayPtr and so. Can you write code that you understand ? Perhaps you can tell what you want to do, and maybe show us your code once more.

Is it like this ? The Slave collects the acceleration data all the time. It calculates the result and stores it in a global variable. That are 3 float numbers. The Master can request the data at any time. For myself, I prefer the same struct with the 3 float numbers in the Slave and in the Master. But you can use what you think is best, I just happen to like stucts and unions.

thinking now, I guess is better to start again with the code.

the idea is the slave read the data all the time, from the accelerometer, and save it in a SD card, I doesn't have problem with that, but at the same time, if the master ask for a data, the slave sends.

that is basically what I want, I saw a example where a guy used a map function. I think that way more easy, I'll test it

Can you make a test sketch for the Slave.
Have three numbers in global variables (float or integers) and send them when the Master requests it.

it's almost at the beginning

That statement declares a variable that is a pointer. It does NOT make ti point to anything.

You need to get it into your head that a pointer MUST point to allocated memory. It is almost certain that you should be defining an array, NOT a pointer.

Thanks PaulS I’ll read more about it, to consider next time.

now I have a more simple code:

Slave

#include <Wire.h>

AcceleroMMA7361 accelero;

int table[]={0,0,0};
int x,y,z;

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

 //ACCELEROGRAPH
  accelero.begin(13, 12, 11, 10, A0, A1, A2);
  accelero.setARefVoltage(5);   
  accelero.setSensitivity(HIGH);  
  accelero.calibrate();

//I2C
  Wire.begin(2); 
  Wire.onRequest(requestEvent);
}

void loop()
{
  table[0]=accelero.getXAccel();
  table[1]=accelero.getXAccel();
  table[2]=accelero.getXAccel();

  Serial.print(table[0]);
  Serial.print('\t');
  Serial.print(table[1]);
  Serial.print('\t');
  Serial.print(table[2]);
  Serial.print('\n');

}

void requestEvent()
{
  uint8_t Buffer[3];
  Buffer[0] = table[0];
  Buffer[1]  = table[1];
  Buffer[2] = table[2];
  
  Wire.write(Buffer,3); 
   
 }

Master

#include <Wire.h>
int table[]={0,0,0};

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

}

void loop()
{
  Wire.requestFrom(2, 3);

    for(int i=0;i<3;i++){ 
      int c    = Wire.read();
      table[i] = c;   
    }
     Serial.print('\n');
     Serial.print(table[0]);
     Serial.print('\t'); 
     Serial.print(table[1]);
     Serial.print('\t'); 
     Serial.print(table[2]);
     Serial.print('\n');
      
  delay(500);
}

the only thing is the slave send for example two digit int but the master show me three digit
I guess it’s because uint8_t conversion?

EDIT:it’s not, the problem is the accelerometer is giving a negative value, so I have to modify the data and use a map function

The table is three integers. An integer is 16-bit or 2 bytes. That means that array is 6 bytes.

You can use ‘6’ or sizeof(table).
The “sizeof” is a function for the compiler, the compiler is going to determine the size of that element and fills in the number.

In the Slave, you can forget that Buffer, and transmit the table directly. But you have to remember that you transmitted 3 integers. That is 6 bytes.

void requestEvent()
{
  Wire.write(table,6);
}

You also have to temporarely disable the interrupt, to avoid half-written integers.
To reduce the time that the interrups are disabled, a temporary second buffer is used.

void loop()
{
  int temporary[3];
  temporary[0]=accelero.getXAccel();
  temporary[1]=accelero.getYAccel();
  temporary[2]=accelero.getZAccel();

  // Set the value to the global variables.
  // Disable the interrupts for a short time.
  noInterrupts();
  table[0]=temporary[0];
  table[1]=temporary[1];
  table[2]=temporary[2];
  interrupts();
  ...

In the Master you have to request now 3 integers instead of three bytes. So request 6 bytes.

void loop()
{
  Wire.requestFrom(2, 6);

  byte buffer[6];
  for(int i=0;i<6;i++) { 
    buffer[i] = Wire.read();
  }

  // Convert the buffer with bytes to 16-bit integers.
  table[0] = word(buffer[1], buffer[0]);
  table[1] = word(buffer[3],buffer[2]);
  table[2] = word(buffer[5],buffer[4]);
  ...

I did not check the code for casting signed integers to bytes to word and vica versa. Perhaps a some extra casting is needed. I also hope that I have the right order and that buffer[1] is the high byte and buffer[0] the low byte.
http://arduino.cc/en/Reference/WordCast

Thanks Peter_n, I'm testing it now, and I'm getting a error

sketch_oct20a:50: error: no matching function for call to 'TwoWire::write(int [3], int)'

the line 50 is

Wire.write(table,6);

what can be wrong?