I2C sending several bytes from Attiny84 to Atmega1284

Hello,

I've run into a bit of a problem; I would like to send nine bytes of information from an Attiny84 to an Atmega1284 via I2C.

I'm aware that Attinies can only send one byte at a time, but surely there has to be a way of sending several bytes in a row from an Attiny to an Atmega via I2C?

Here is my Attiny sketch in its entirety:

/*--------------------------------------------------------------------------------------------------------------------

  CarDuino Convenience Module Sketch

  This sketch monitors a variety of functions:

  - Outside and inside air temperature, using two DS18B20 sensors
  
  - Windscreen wash level; a lead inside the windscreen wash pump goes to GND when
    windscreen wash is low. This lead is connected to Attiny pin 10 in this sketch.
  
  - lamp check modules; these monitor headlamps, taillights and brake lights.
    If faults are present at either pairs of lights, a particular signal pin
    on the lamp check units will go high to +12V. This signal is then
    fed into the Attiny84 using a 22K/10K voltage divider to generate
    a logic HIGH.  


  This data is then sent over to the head unit module via I2C
  where it is displayed on the screen.

*/

#include <TinyWireS.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "SoftwareSerial.h"


// -------------------------------- Pin definitions; note CLOCKWISE pin assignment!

// status LED
#define led A0

// Serial connection... working without a problem
const byte rx = A2;
const byte tx = A3;

SoftwareSerial mySerial(rx, tx);

// lamp check pins... 
#define kb 5        //brake lights
#define ksb 7       //taillights
#define ksf 8       //headlamps

//DS18B20 temperature sensor pin
#define TempSensors 9
OneWire oneWire(TempSensors);
DallasTemperature sensors(&oneWire);

// windscreen wash pin
#define windscreenWash 10


//temperature sensor variables
volatile byte* outsideTempFloatPtr;
volatile byte* insideTempFloatPtr;

volatile float outsideTemp;
volatile float insideTemp;

// lamp check and windscreen wash info
byte errorStatus;

unsigned long serialStart = 0;
unsigned long ledStart = 0;

void setup() {

  mySerial.begin(115200);
  sensors.begin(); // Begin temperature sensors
  TinyWireS.begin(3); // join i2c bus with address #3
  TinyWireS.onRequest(requestEvent); // register event

  pinMode (windscreenWash, INPUT);

  pinMode (led, OUTPUT);

  pinMode(ksf, INPUT);
  pinMode(ksb, INPUT);
  pinMode(kb, INPUT);

  pinMode(rx, INPUT);
  pinMode(tx, OUTPUT);

  digitalWrite(led, HIGH);

}

void loop() {


  if (millis() - ledStart >= 250) digitalWrite(led, HIGH);

  //--------------------------------------------------------------------------------------------------------------------
  // -------------------------------- Gathering data


  // Outside and Inside Temperatures
  sensors.requestTemperatures();
  outsideTemp = sensors.getTempCByIndex(0);
  insideTemp = sensors.getTempCByIndex(1);

  // Windscreen Washer Level
  //windscreenWashError = digitalRead(windscreenWash);

  // Front Lamp Check Module


  errorStatus = digitalRead(ksf) <<0 | (digitalRead(ksb) << 1) | ( digitalRead(kb) << 2) | (digitalRead(windscreenWash) << 3);


  if (!serialStart) serialStart = millis();

  if (millis() - serialStart >= 2000) {

  
    mySerial.println(outsideTemp);
   
    mySerial.println(insideTemp);
   
    mySerial.println(errorStatus, BIN);

    mySerial.print("\n\n");

    serialStart = 0;
  }
}


void requestEvent() {


  // -------------------------------- I2C Request Event Definition

  digitalWrite(led, LOW);
  ledStart = millis();

  // send nine bytes
  byte sendData[9];

  // processing temperature sensor data
  // Outside
  outsideTempFloatPtr = (byte*) & outsideTemp;

  sendData[0] = outsideTempFloatPtr[0];
  sendData[1] = outsideTempFloatPtr[1];
  sendData[2] = outsideTempFloatPtr[2];
  sendData[3] = outsideTempFloatPtr[3];

  // Inside
  insideTempFloatPtr = (byte*) & insideTemp;

  sendData[4] = insideTempFloatPtr[0];
  sendData[5] = insideTempFloatPtr[1];
  sendData[6] = insideTempFloatPtr[2];
  sendData[7] = insideTempFloatPtr[3];

  // windscreen wash liquid level
  sendData[8] = errorStatus;



  for (int i = 0; i <= 8; i++) TinyWireS.send(sendData[i]);

}

tl;dr: This is the bit where I try to send my data:

 for (int i = 0; i <= 8; i++) TinyWireS.send(sendData[i]);

Is this the wrong way to do it? What happens is that when the ISR is called, the LED goes LOW, as it should according to my code. So the I2C connection itself looks like it's working in principle. But then the LED just stays low and I get a read error on the other end, at the Atmega. So effectively I guess no data is transferred. It kind of seems like the Attiny crashes on the request event, because the serial monitor then also goes dead, i.e. no more updates as implemented in my code.

Any help is appreciated.

Can you send and receive one byte successfully?

Nope, hat doesn't seem to work either.

Here's how I request the data on the master side:

 // ---------------------- Requesting data from convenience module at slave address #3

    Wire.requestFrom(3, 9); //Requesting 9 bytes of data from convenience module

    if (!Wire.available())  enterErrorTable(13);    // if i²C connection is down, an error is entered into a
                                                    // table and a warning is given out
                                                    // that the convenience module is malfunctioning
    else {
      
      resetErrorTable(13);                             // if I²C works, the error table is reset in line 14

      byte i = 0;
      while (Wire.available())   // slave may send less than requested
      {
        convenienceModuleData[i] = Wire.read(); // receive a byte as character
        i++;
      }

What then happens is that the sketch goes through my errorTable one by one where those errors are stored and gives out a warning. And at present, I get a warning that the I²C connection is faulty because if (!Wire.available()) is true.

hm... I have now changed the request ISR on the Attiny84 to this:

  TinyWireS.send(sendData[i]);
  i++;
  if (i == 8) i = 0;

This has brought a slight improvement, as the Attiny now apparently actually gets polled a few times by the master; but after a few turns, the LED stays LOW again (in my sketch, the LED goes low when an I2C request is incoming) and then stays low. Normally, after the I2C transmission is complete, it should go back up to HIGH. So it seems it's crashing again on the I2C request.

Can you run the two basic multibyte send/receive sketches in the TinyWireS library?
TinyWireS_Stress_Master
TineWireS_Stress_Slave

I think I've made a few errors in soldering together the perfboard that the Attiny sits on. At the moment, it doesn't appear that I've wrongly soldered the SDA/SCL leads, but one or two input pins have definitely been wired the wrong way, so there is a chance that being off to the left or to the right by one solder spot may have created connections that aren't supposed to be there and that also affect the SDA and SCL lines.

But just in principle, so I will be able to eliminate that error source - what is the right way to send several different bytes in a row from an Attiny(84) slave to an Atmega master? How does the code have to look?

With regards to my Attiny sketch, is this the right way:

  TinyWireS.send(sendData[i]);
  i++;
  if (i == 8) i = 0;

or this?

for (int i = 0; i <= 8; i++) TinyWireS.send(sendData[i]);
for (int i = 0; i <= 8; i++) TinyWireS.send(sendData[i]);

The library example uses this form.

I've double checked my perfboard, there were actually a few missing connections, but the I2C lines are the way they should be.

Could there be a clue in my schematic as to why it's still not working?

Click on image for larger view.