[SOLVED] I2C Communication is slow

Hi,

I am using the Photon to communicate with a Panasonic Grid Eye Sensor over I2C.
I have written a library to get temperature back from sensor but it is currently too slow. I need to read back data at 10Hz so each read needs to take less than 100ms. At the moment each read takes ~304ms but when I comment out the “Wire.endTransmission()” lines the read time is reduced to ~3ms.

I have used other micro controllers in the past reading data over I2C at 500Hz so it seems like it should be possible to read data back much more quickly.

Why does the “Wire.endTransmission()” line take so long as each time it is only sending one int?
Is there anything I can do to speed up the library?

EDIT:
I am using the latest 0.4.6 firmware.
I have tested the sensor with an mBed LCP1768 and it is able to read at 10Hz.
The sensor is connected to the I2C pins on the Photon with 4K7 pull up resistors.

The class is below:

//GridEye.cpp

#include "GridEye.h"

GridEye::GridEye()
{
    Wire.setSpeed(CLOCK_SPEED_400KHZ);
    Wire.begin();
    delay(100);
    initialReset();
    setFPS(true);
}

bool GridEye::writePacket(int addr, int data)
{
	Wire.beginTransmission(ADDRESS);
	Wire.write(addr);
	Wire.write(data);
	return Wire.endTransmission(true) == 0;
}

std::vector<byte> GridEye::readPacket(int addr, int datalength)
{
    std::vector<byte> data;
        
    if(datalength <= 32)
    {
        Wire.beginTransmission(ADDRESS);
        Wire.write(addr);
        Wire.endTransmission(false);
    
        Wire.requestFrom(ADDRESS, datalength, true);
        while(Wire.available()) data.push_back(Wire.read());
    }
    else
    {
        int dataRead = 0;
        
        while((datalength - dataRead) > 32)
        {
            Wire.beginTransmission(ADDRESS);
            Wire.write(addr + dataRead);
            Wire.endTransmission(false);
        
            Wire.requestFrom(ADDRESS, 32, false);
            while(Wire.available()) data.push_back(Wire.read());
        	
            dataRead += 32;
        }
        
        Wire.beginTransmission(ADDRESS);
        Wire.write(addr + dataRead);
        Wire.endTransmission(false);
    
        Wire.requestFrom(ADDRESS, (datalength - dataRead), true);
        while(Wire.available()) data.push_back(Wire.read());
    }
	
    return data;
}

std::vector<float> GridEye::readTemperatures()
{
	std::vector<byte> rawData = readPacket(0x80, 128);
	
	std::vector<float> data;

	for(int i=0; i < 64; i++) data.push_back((rawData[(2*i)+1] << 8) | rawData[2*i]);

	return data;
}

float GridEye::readCalibrationTemperature()
{
	std::vector<byte> rawData = readPacket(0x0E, 2);
	
	float temperature = (((rawData[1] << 8) | rawData[0]) * 0.0625);

	return temperature;
}

bool GridEye::setFPS(bool fps_set_10)
{
	int data = !fps_set_10;
	return writePacket(0x02, data);
}

bool GridEye::initialReset()
{
	int data = 0x3F;
	return writePacket(0x01, data);
}

The main code is below:

// This #include statement was automatically added by the Particle IDE.
#include "GridEye.h"

GridEye* _gridEye;

void setup()
{
    Serial.begin(115200);
    _gridEye = new GridEye();
}

void loop() 
{
    Serial.print("Grid-EYE:\r\n");
    
    unsigned long start = millis();
    std::vector<float> temps = _gridEye->readTemperatures();
    unsigned long end = millis();
    
    Serial.println("Time:" + String(end - start));
    
    for(int i=0; i < temps.size(); i++)
    {
        Serial.print(String(temps[i]));
        Serial.print(" ");
        if((i+1)%8 == 0) Serial.print("\r\n");

    }
    delay(100);
}

Thanks in advance.
Joe

Some I2C devices only support bus restarts when followed by a read. In your case for the 128 byte reads you are doing restarts for the reads and also for the 2nd, 3rd, and 4th write. The data sheet is very short on detail about its I2C interface. It would be worth testing to see whether this is the issue:

while((datalength - dataRead) > 32)
        {
            Wire.beginTransmission(ADDRESS);
            Wire.write(addr + dataRead);
            Wire.endTransmission(false);                        // no bus stop so we do a restart for the next read
        
            Wire.requestFrom(ADDRESS, 32, true);        // send a bus stop after the read
            while(Wire.available()) data.push_back(Wire.read());
        	
            dataRead += 32;
        }

Unless you have multiple master devices on your I2C bus there is nothing to be gained by holding the bus from arbitration

2 Likes

Thank you very much, removing the bus restarts solved the issue. :smile:

Hello Joe I am interested in your setup and your code…I am a newbie and have to work with the Spark core and the Grideye sensor for my work could you assist me?

Class is below - should give you a good starting point.

Grideye.cpp

#ifndef __GRIDEYE_CPP_
#define __GRIDEYE_CPP_

#include "GridEye.h"

GridEye::GridEye()
{
    _logger.logMessage(INFO_LOG, "Started Grid Eye initialisation");

    Wire.setSpeed(CLOCK_SPEED_400KHZ);
    Wire.begin();
    initialReset();
    setFPS(true);
    setAverage(true);

    _logger.logMessage(INFO_LOG, "Finished Grid Eye initialisation");
}

GridEye::~GridEye() {}

void GridEye::readFrame(QSMatrix<float>& frame)
{
    std::vector<byte> data;
    readPacket(data, 0x80, 128);

	for(int16_t i = 0; i < 64; ++i)
	{
	    int16_t row = i / 8;
        int16_t column = i % 8;
        float value = (((data[(2*i)+1] << 8) | data[2*i]) * 1) * 0.25;
        frame(row, column) = value;
	}
}

int16_t GridEye::readThermistor()
{
    std::vector<byte> data;
	readPacket(data, 0x0E, 2);
    float value = (((data[1] << 8) | data[0]) * 0.0625);
	return value * 100;
}

bool GridEye::setAverage(bool on)
{
	int16_t data = (on << 5);
	return writePacket(0x07, data);
}

bool GridEye::setFPS(bool fps_set_10)
{
	int16_t data = !fps_set_10;
	return writePacket(0x02, data);
}

bool GridEye::initialReset()
{
	int16_t data = 0x3F;
	return writePacket(0x01, data);
}

bool GridEye::writePacket(int16_t addr, int16_t data)
{
	Wire.beginTransmission(ADDRESS);
	Wire.write(addr);
	Wire.write(data);
	return Wire.endTransmission() == 0;
}

void GridEye::readPacket(std::vector<byte>& data, int16_t addr, int16_t datalength)
{
    if(datalength <= 32)
    {
        Wire.beginTransmission(ADDRESS);
        Wire.write(addr);
        Wire.endTransmission();

        Wire.requestFrom(ADDRESS, datalength);
        while(Wire.available()) data.push_back(Wire.read());
    }
    else
    {
        int16_t dataRead = 0;

        while((datalength - dataRead) > 32)
        {
            Wire.beginTransmission(ADDRESS);
            Wire.write(addr + dataRead);
            Wire.endTransmission();

            Wire.requestFrom(ADDRESS, 32);
            while(Wire.available()) data.push_back(Wire.read());

            dataRead += 32;
        }

        Wire.beginTransmission(ADDRESS);
        Wire.write(addr + dataRead);
        Wire.endTransmission();

        Wire.requestFrom(ADDRESS, (datalength - dataRead));
        while(Wire.available()) data.push_back(Wire.read());
    }
}

#endif

GridEye.h

#ifndef __GRIDEYE_H_
#define __GRIDEYE_H_

#include "Global.h"
#include "QSMatrix.h"

class GridEye
{
  public:
  GridEye();
  virtual ~GridEye();

  void readFrame(QSMatrix<float>& frame);
  int16_t readThermistor();

private:
  bool setFPS(bool fps_set_10);
  bool setAverage(bool on);
  bool initialReset();
  bool writePacket(int16_t addr, int16_t data);
  void readPacket(std::vector<byte>& data, int16_t addr, int16_t datalength);

  const int ADDRESS = 0x68;
};

#endif

okay thanks