1

I need to interface an ATTiny88 MCU configured as an I²C slave with a Raspberry Pi configured as an I²C master. I may be using the wrong approach, but I have a structure consisting of four different fixed length data types I want to pass back to the Master whenever it requests data.

Source Code:

#include <Wire.h>
struct Response // 7 bytes
  {
      byte Command;         // 1 byte
      float Temperature;    // 4 bytes
      byte FanValue;        // 1 byte
      bool CoolStat;        // 1 byte
  };

struct Response Reply; bool True = 1; bool False = 0

void setup() { pinMode(ADDR1, INPUT_PULLUP); // Address Offset 0 or 1 pinMode(ADDR2, INPUT_PULLUP); // Address Offset 0 or 2 I2C = (2 * digitalRead(ADDR1)) + digitalRead(ADDR1) + 8; // Set I2C address 8 - 11 Wire.begin(I2C); Wire.onRequest(requestEvent); // Call requestEvent when data requested

Reply.Command = 1;
Reply.Temperature = 35.62;
Reply.FanValue = 255
Reply.CoolStat = True

}

// Function that executes whenever data is requested from master void requestEvent() { Wire.write(Reply); // respond with message as expected by master Responded = True; }

This does not work, of course, because the library does not like the fact I am passing a structure, rather than a simple null terminated string. I am not sure how best to pass the structure to the Wire.write() function. I like the ability to change the variables in the structure to different values of different data types at different points in the code, but would I be better served to take a different approach and manually update the binary values in the string?

LesRhorer
  • 115
  • 8

1 Answers1

3

There is an overload of Wire.write that lets you send an arbitrary array of bytes:

virtual size_t write(const uint8_t *, size_t);

In order to use it, you have to cast the address of your structure to a pointer to uint8_t:

Wire.write((uint8_t *) &Reply, sizeof Reply);

Note, however, that this method, although very common, is not really conformant to the C++ standard. I suggest you instead use a union that lets you access the memory of the structure as a simple array of bytes:

union Response {
    struct {
        byte Command;         // 1 byte
        float Temperature;    // 4 bytes
        byte FanValue;        // 1 byte
        bool CoolStat;        // 1 byte
    };
    uint8_t bytes[7];
};

This can then be sent with:

Wire.write(Reply.bytes, sizeof Reply);

However: You may have a hard time parsing the structure back on the Pi. Different computing platforms have different data-type sizes and different alignments constraints. The structure you are using may be 7-bytes long on an Arduino Uno, but it will be larger on an ARM-based Arduino, and also on a Raspberry Pi: for alignment reasons, you will have three padding bytes before Temperature. Also, the CoolStat field may be larger than one byte, and add some padding of its own. To make things easier, I recommend you define the structure in a way that its layout is platform-independent: use only fixed-size types, put the larger items first, and manually pad to a multiple of the larger item's size:

union Response {
    struct {
        float Temperature;  // 4 bytes
        uint8_t Command;    // 1 byte
        uint8_t FanValue;   // 1 byte
        uint8_t CoolStat;   // 1 byte
        uint8_t padding;    // 1 byte
    };
    uint8_t bytes[8];
};

This should have the exact same layout on any Arduino, and also on a Raspberry Pi.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81