Passing variables to I2C function

I am building a greenhouse monitoring system that will measure temperatures (right now 10 probes), a humidity sensor and two byts worth of discreet data collected by a MEGA2560 and sent to a nodemcu. The nodemcuis the master and will eventually host a server to send the data on to a RBpi located in the house.

I have the example that send “hello” from the slave to the master working where the Wire.write is in a function “requestEvent”. Now I need to pass the data I need to send into the function requestEvent and have the nodemcu read. From what I have read I can’t do multiple Wire.write within the requestEvent function. Correct? I need to combine all the data into a structure and that structure size is limited to 32 bytes, correct?

How do I pass the structure to the function? Attached are the master and slave code.

Thanks.

master_reader.ino (762 Bytes)

GH_mega2560.ino (5.86 KB)

Welcome to the forums. Please take a few minutes and read the sticky post at the top of the forum about how to properly post your code using code tags. It will help people help you. Some folks will not bother downloading your attachments and opening them up

master_reader.ino

// Wire Master Reader
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Reads data from an I2C/TWI slave device
// Refer to the "Wire Slave Sender" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#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(8, 7);    // request 6 bytes from slave device #8

  while (Wire.available()) { // slave may send less than requested
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(1000);
}

GH_Mega2560.ino

/*
  This is the code for the mega2560 GH data collection.  It gathers the state of the equipment
  as well as temp.  It then sends the data to the nodemcdu when requesed via I2C

*/
// include the library code:
#include <LiquidCrystal.h>
#include <Wire.h>

// initialize the display library with the numbers of the interface pins
// LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// constants won't change.
// This sets the pin number for relay outputs:
const int led1Pin =  23;// the number of the LED pin
const int led2Pin =  25;// the number of the LED pin
const int led3Pin =  27;// the number of the LED pin
const int led4Pin =  29;// the number of the LED pin
const int led5Pin =  31;// the number of the LED pin
const int led6Pin =  33;// the number of the LED pin
const int led7Pin =  35;// the number of the LED pin
const int led8Pin =  37;// the number of the LED pin
const int led9Pin =  39;// the number of the LED pin
const int led10Pin =  41;// the number of the LED pin
const int led11Pin =  43;// the number of the LED pin
const int led12Pin =  45;// the number of the LED pin


// Define the pin number for binary inputs
const int button1 = 22;
const int button2 = 24;
const int button3 = 26;
const int button4 = 28;
const int button5 = 30;
const int button6 = 32;
const int button7 = 34;
const int button8 = 36;
const int button9 = 38;
const int button10 = 40;
const int button11 = 42;
const int button12 = 44;



void setup() {

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.setCursor(0, 0); // reset the cursor to top left
  lcd.clear();

  // Setup I2C
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event

  Serial.begin(9600); // open the serial port at 9600 bps:

  // set the digital pin as input:
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  pinMode(button3, INPUT);
  pinMode(button4, INPUT);
  pinMode(button5, INPUT);
  pinMode(button6, INPUT);
  pinMode(button7, INPUT);
  pinMode(button8, INPUT);
  pinMode(button9, INPUT);
  pinMode(button10, INPUT);
  pinMode(button11, INPUT);
  pinMode(button12, INPUT);

  // set the digital pin as input:
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(led3Pin, OUTPUT);
  pinMode(led4Pin, OUTPUT);
  pinMode(led5Pin, OUTPUT);
  pinMode(led6Pin, OUTPUT);
  pinMode(led7Pin, OUTPUT);
  pinMode(led8Pin, OUTPUT);
  pinMode(led9Pin, OUTPUT);
  pinMode(led10Pin, OUTPUT);
  pinMode(led11Pin, OUTPUT);
  pinMode(led12Pin, OUTPUT);

  //Set the initual state.  High is off
  int led1Pin = HIGH;
  int led2Pin = HIGH;
  int led3Pin = HIGH;
  int led4Pin = HIGH;
  int led5Pin = HIGH;
  int led6Pin = HIGH;
  int led7Pin = HIGH;
  int led8Pin = HIGH;
  int led9Pin = HIGH;
  int led10Pin = HIGH;
  int led11Pin = HIGH;
  int led12Pin = HIGH;


}

void loop() {

  int button1State = digitalRead(button1);
  int button2State = digitalRead(button2);
  int button3State = digitalRead(button3);
  int button4State = digitalRead(button4);
  int button5State = digitalRead(button5);
  int button6State = digitalRead(button6);
  int button7State = digitalRead(button7);
  int button8State = digitalRead(button8);
  int button9State = digitalRead(button9);
  int button10State = digitalRead(button10);
  int button11State = digitalRead(button11);
  int button12State = digitalRead(button12);

  // Reset the Button words
  int Button1Word = B10000000;
  int Button2Word = B10000000;

  // if the button is off = relay off:

  // set the LED with the ledState of the variable:
  digitalWrite(led1Pin, button1State);
  digitalWrite(led2Pin, button2State);
  digitalWrite(led3Pin, button3State);
  digitalWrite(led4Pin, button4State);
  digitalWrite(led5Pin, button5State);
  digitalWrite(led6Pin, button6State);
  digitalWrite(led7Pin, button7State);
  digitalWrite(led8Pin, button8State);
  digitalWrite(led9Pin, button9State);
  digitalWrite(led10Pin, button10State);
  digitalWrite(led11Pin, button11State);
  digitalWrite(led12Pin, button12State);

  // combine the button states into two 8 bit words
  // Button1Word has the first 7 switches
  // Button2Word has the next 5 switches
  if (button1State != 0) {
    Button1Word = Button1Word | B00000001;
  }

  if (button2State != 0) {
    Button1Word = Button1Word | B00000010;
  }
  if (button3State != 0) {
    Button1Word = Button1Word | B00000100;
  }

  if (button4State != 0) {
    Button1Word = Button1Word | B00001000;
  }
  if (button5State != 0) {
    Button1Word = Button1Word | B00010000;
  }

  if (button6State != 0) {
    Button1Word = Button1Word | B00100000;
  }
  if (button7State != 0) {
    Button1Word = Button1Word | B01000000;
  }

  // Start Button2Word
  if (button8State != 0) {
    Button2Word = Button2Word | B00000001;
  }
  if (button9State != 0) {
    Button2Word = Button2Word | B00000010;
  }
  if (button10State != 0) {
    Button2Word = Button2Word | B00000100;
  }
  if (button11State != 0) {
    Button2Word = Button2Word | B00001000;
  }
  if (button12State != 0) {
    Button2Word = Button2Word | B00010000;
  }


  Serial.print("Button 1 Word ");
  Serial.println(Button1Word, BIN);
  Serial.print("Button 2 Word ");
  Serial.println(Button2Word, BIN);
  Serial.println();
  delay(1000);

  //  This section reads the DS tempeture probe




  // Development code to wirte to an LCD display
}

//  This is the i2c section.  When the master requests data it sends all the data
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Serial.println("Got Request");
  Wire.write("hello "); // respond with message of 6 bytes
  Wire.write(Button1Word); //respond with the first set of switches
  // as expected by master
}

Your code is small enough to post in-line here. Please do so using Code Tags. If you don't know what they are, read this: Read this before posting a programming question ... (which you should have done first). Pay particular attention to Item #6.

With that out of the way.... your Button1Word variable is declared inside of loop() so you can not use it in your receiveEvent() function. You need to make it a global.

Also, an 'int' is 16 bits so you do not need Button1Word and Button2Word, just make 1 16-bit word (unsigned int or uint16_t). Inside receiveEvent() you will have to write the upper byte and lower byte as 2 separate Wire.write() calls.

You can also keep your Button1Word and Button2Word but declare them as uint8_t and make them global. Then just write() both values

You will also have to adjust your master reader to request 8 bytes. 6 for "hello " and 2 for your 16-bit word.

Note that the values received by the master are not going to be text, but the actual number which won't necessarily display on your Serial Monitor

Your code could also benefit for the use of arrays to hold all your values rather than var1, var2, var3, etc.

Something like this. Note - you should not call Serial.print() inside your recieveEvent() function since that function is called inside an interrupt service routine (ISR) as part of the Wire communication. You want that function to be as fast as possible and Serial also uses interrupts.
master

// Wire Master Reader
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Reads data from an I2C/TWI slave device
// Refer to the "Wire Slave Sender" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


struct {
  char msg[6];
  uint16_t buttonState;
  int DSTemp;
} dataPacket;


#include <Wire.h>

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

void loop() {

  int byteCount = Wire.requestFrom(8, sizeof(dataPacket));    // request data from slave device #8
  byte *ptr = (byte*) &dataPacket;

  if ( byteCount != sizeof(dataPacket)) {
    Serial.print( "Received bad packet.  Expected " );
    Serial.print(sizeof(dataPacket));
    Serial.print( " bytes but got " );
    Serial.println( byteCount );
  }
  else {
    for ( byte i = 0; i < sizeof(dataPacket); ++i ) {
      byte c = Wire.read(); // receive a byte as character
      ptr[i] = c;
    }
    Serial.println( "Received:");
    Serial.print( "msg: " );
    Serial.println( dataPacket.msg);
    Serial.print( "Button State: " );
    Serial.println( dataPacket.buttonState, BIN);
    Serial.print( "DSTemp: " );
    Serial.println( dataPacket.DSTemp );
  }

  delay(1000);
}

slave

/*
  This is the code for the mega2560 GH data collection.  It gathers the state of the equipment
  as well as temp.  It then sends the data to the nodemcdu when requesed via I2C

*/
// include the library code:
#include <LiquidCrystal.h>
#include <Wire.h>

// initialize the display library with the numbers of the interface pins
// LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const byte ledPin[] = { 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45 };
const byte ledCount = sizeof(ledPin) / sizeof(ledPin[0]);

const byte buttonPin[] = { 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44 };
const byte buttonCount = sizeof(buttonPin) / sizeof(buttonPin[0]);

struct {
  char msg[6] = "hello";
  uint16_t buttonState;
  int DSTemp;
} dataPacket;

void setup() {

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.setCursor(0, 0); // reset the cursor to top left
  lcd.clear();

  // Setup I2C
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event

  Serial.begin(9600); // open the serial port at 9600 bps:

  for( int i=0; i<buttonCount; ++i ) {
    pinMode(buttonPin[i], INPUT);
  }

  for( int i=0; i<ledCount; ++i ) {
    pinMode(ledPin[i], OUTPUT);
    digitalWrite(ledPin[i], HIGH);
  }
}

void loop() {

  uint16_t buttonState = 0;
  for( int i=0; i<buttonCount; ++i ) {
    if (digitalRead(buttonPin[i])) {
      // button is HIGH
      digitalWrite(ledPin[i], HIGH);
      bitSet(buttonState,i);
    }
    else {
      // button is LOW so LED off
      digitalWrite(ledPin[i], LOW);
    }
  }
  dataPacket.buttonState = buttonState;

  Serial.print("Button State ");
  Serial.println(buttonState, BIN);
  Serial.println();

  //  This section reads the DS tempeture probe

  dataPacket.DSTemp = random(100);


  // Development code to wirte to an LCD display

  delay(1000);

}

//  This is the i2c section.  When the master requests data it sends all the data
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  //Serial.println("Got Request");
  //Wire.write("hello "); // respond with message of 6 bytes
  Wire.write((byte*)&dataPacket, sizeof(dataPacket)); //send packet
  // as expected by master
}

The callback function specified by Wire.onRequest() runs as an ISR. Therefore, special attention is required.

  • The ISR should be short and fast.

  • Don't use Serial I/O, delay, or millis()-based timing in the ISR.

  • Global variables accessed by both ISR and non-ISR code must be declared 'volatile'.

  • Accesses to multi-byte shared variables by non-ISR code must be in protected sections with interrupts disabled. I'd use one such section and update the entire struct all at once.

  • Since you're transferring data from an 8-bit AVR to a 32-bit ESP, you'll need to take datatype size and struct packing into consideration. Lucky for you, at least they use the same Endianness.

Thanks for the replay and including recommended changes. I did read about how to post. Missed the part about including the code tags to include the code in the post as apposed to including as a file. Sorry. Also there is no editing bar above the posting area showing </> until I go to preview so I never saw them. Do I need to do something to get the bar outside of preview?

Have some follow on questions.

In the master;

int byteCount = Wire.requestFrom(8, sizeof(dataPacket)); // request data from slave device #8
byte ptr = (byte) &dataPacket;

int byteCount - Wire.requestFrom returns how many bytes are recieved
sizeof(dataPacket) - size of the structure defined earlier in the code so the code knows how much data to expect.

*ptr defines a pointer to the structure (defined earlier) that gets filled with the data one byte at a time later on in the code, correct?

for ( byte i = 0; i < sizeof(dataPacket); ++i ) {
byte c = Wire.read(); // receive a byte as character
ptr = c;
This reads the data one byte at a time and fills the structure pointed to by prt?
In the slave code where you define an array for the ledPin for example. I get the first line. you are defining an array and filling it with the values. I don’t understand the second line. Why “sizeof(ledPin) / sizeof(ledPin[0])”
const byte ledPin[] = { 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45 };
const byte ledCount = sizeof(ledPin) / sizeof(ledPin[0]);
Where the structure is sent to the master;
Wire.write((byte*)&dataPacket, sizeof(dataPacket)); //send packet
byte* - tells the function to write one byte at a time?
&dataPacket - pointer to the structure
sizeof(dataPacket) telling the function how many bytes to write
Correct?
Again, thanks.

You can see now why code tags are needed - your code snippet has been mangled and turned into italics.

richesonp:
Have some follow on questions.
In the master;

int byteCount = Wire.requestFrom(8, sizeof(dataPacket));    // request data from slave device #8

byte ptr = (byte) &dataPacket;



int byteCount - Wire.requestFrom returns how many bytes are received
sizeof(dataPacket) - size of the structure defined earlier in the code so the code knows how much data to expect.

*ptr defines a pointer to the structure (defined earlier) that gets filled with the data one byte at a time later on in the code, correct?

That is correct

for ( byte i = 0; i < sizeof(dataPacket); ++i ) {

byte c = Wire.read(); // receive a byte as character
    ptr[i] = c;




This reads the data one byte at a time and fills the structure pointed to by ptr?

also correct

In the slave code where you define an array for the ledPin for example. I get the first line. you are defining an array and filling it with the values. I don’t understand the second line. Why “sizeof(ledPin) / sizeof(ledPin[0])”

const byte ledPin[] = { 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45 };

const byte ledCount = sizeof(ledPin) / sizeof(ledPin[0]);

sizeof() returns the size of an object in bytes. In this case, since the array is of byte type, it is not really necessary. If you change the array to type int, sizeof() would return a number twice as large since each ‘int’ takes 2 bytes. That is why you divide by the size of an individual object (ledPin[0]). Read it like “the entire size of the array in bytes divided by the size of a single element of the array” which gives you your element count no matter what type of array you create

Where the structure is sent to the master;

Wire.write((byte*)&dataPacket, sizeof(dataPacket)); //send packet

byte* - tells the function to write one byte at a time?
&dataPacket - pointer to the structure
sizeof(dataPacket) telling the function how many bytes to write

Correct?

Not quite. The write() function demands a pointer of type ‘uint8_t’ which is also ‘byte’ so you have to cast the pointer to the structure to be the correct type. If you do not, the compiler will complain. Pointers must point to a type of object so if you increment them (ptr++) or use them as an array, the compiler will use the size of the object to calculate the address of index 0, index 1, etc. which would not be correct for a pointer to the struct since it is much larger than a single byte.

@gfvalvo: I thought millis() was non-blocking. Not so??

econjack:
@gfvalvo: I thought millis() was non-blocking. Not so??

It is not blocking, but it will not increment inside an ISR since interrupts are off

blh64:
It is not blocking, but it will not increment inside an ISR since interrupts are off

Are you saying that, if I write my own ISR and have a call to millis() within my ISR, millis() will not return a correct count?

It will return the millis count at the time the that interrupt occurred.

But, if you even think you want to do millis timing in an ISR, then you're thinking wrong. An ISR should finish its work in microseconds, not milliseconds.