Denbo:
Chucktodd thanks for the feedback. I am happy to hear you confirm it works well between two Arduinos. What I am planning on however is to act as master/slave on a bus connecting to a different device altogether (a Vex IQ).
Morgan I never said to call begin() again and drop back to master mode. I simply said call begin with your slave address but still call beginTransmission/endtransmission to act as a master within the loop function.
Below is the code I was talking about it was copied from https://www.microchip.com/forums/m765549.aspx
I am still curious if this completely acceptable or is this a bad thing to do. I've done a bit with I2C and Arduino/NXT but I don't know if there are good reasons to avoid doing this. I wanted to know before I went down this road.
#include <Wire.h>
#define RXLED 17
#define BUTTON 10
#define THIS_ADDRESS 0x9
#define OTHER_ADDRESS 0x8
boolean last_state = HIGH;
void setup() {
pinMode(RXLED, OUTPUT);
digitalWrite(RXLED, LOW); //RXLED Off
pinMode(BUTTON, INPUT);
Wire.begin(THIS_ADDRESS); //This modules I2C Slave address
Wire.onReceive(receiveEvent); //Setup a interupt when I2C data arrives
}
void loop() {
if (digitalRead(BUTTON) != last_state){ //Only when a change in BUTTON occurs
last_state = digitalRead(BUTTON); //Read and store the BUTTON state
TXLED1; 'TXLED On Macro
delay(1000); ' for one second to signal that a button change occurred and a transmission to OTHER_ADRESS begins
TXLED0; 'TXLED Off Macro
Wire.beginTransmission(OTHER_ADDRESS); //Setup I2C transmission to OTHER ADDRESS (Be a MASTER?)
Wire.write(last_state); //Send the byte of data
Wire.endTransmission(); //End the transmission
}
}
void receiveEvent(int howMany) { //Here an I2C message arrives. howMany holds the number of bytes
while (Wire.available() > 0){
//While bytes are available
boolean b = Wire.read(); //Read a byte. In this case we know it is a boolean value
Serial.print(b, DEC); //Send it to the debug port
digitalWrite(RXLED, !b); //Change the LED to On or Off according to the boolean value.
}
Serial.println(); //Send linefeed to debug
TXLED1; // TXLed on Macro
delay(100); //for 100ms to signal a message was received.
TXLED0; //TXLed Off MAcro
}
In the onReceive() function, DO NOT Serial print, or DELAY or anything other than access private variables.
The call to Delay would hang because the delay() function relies on the timer interrupt, Since we are already in an interrupt, NO Other interrupt can happen until this one completes.
The OnReceive() function is an interrupt service routine. It will interrupt any executing code. Think about what would happen if the foreground process was in the middle of a Serial.print() and you tried to do inject another Serial.print() into the middle? Bad Things! None of the Arduino library routines are interrupt safe. So, the only things you can safely do in the onReceive() function is change private variables, atomically access isolated hardware.
Here is how I would rewrite your onReceive() function:
// The volatile predicate, indicates to the compiler that this value can change in an interrupt.
// The compiler tries to optimize how it stores and accesses data, sometimes it keeps the current value
// of a variable in a CPU register, and only updates the RAM location when it needs to reuse the CPU
// register for something else, This works great, unless the value is modified by an ISR, if the ISR
// updates the RAM storage location, but the compiler thinks its register value is 'current' it will overwrite
// the RAM value, loosing any changes made by the ISR.
volatile bool receivedData =false; // flag to indicate new data has arrived
volatile bool overRun =false; // flag to indicate the received data was not process before the next
// next block came in.
volatile byte buffer[32]; // i2c transmissions are limited to a max of 32 byte by the Wire.h library
volatile byte bufLen=0;
void receiveEvent(int howMany) { //Here an I2C message arrives. howMany holds the number of bytes
if(!receivedData) { //buffer is clear to use
bufLen = 0; // start at beginning of buffer
while (Wire.available() > 0){
buffer[bufLen++]=Wire.read(); // receive data from I2C, inc bufLen to next
}
receivedData = true; // mark the buffer as active;
}
else { // New data came in, but there was already data in the buffer!, an overrun
overRun = True;
}
}
void loop(){
if(receivedData){ // ISR got a block from the other I2C master
uint8_t index = 0;
while(index<bufLen){ // walk through buffer
Serial.print(buf[index], DEC); //Send it to the debug port
digitalWrite(RXLED, !buf[index++]); //Change the LED to On or Off according to the boolean value.
}
Serial.println(); //Send linefeed to debug
TXLED1; // TXLed on Macro
delay(100); //for 100ms to signal a message was received.
TXLED0; //TXLed Off MAcro
receivedData = false; // buffer can now be reused!
}
if(overRun){ // did not service the buffer fast enough!
Serial.println("Buffer OverRun!");
overRun = false;
}
if (digitalRead(BUTTON) != last_state){ //Only when a change in BUTTON occurs
last_state = digitalRead(BUTTON); //Read and store the BUTTON state
TXLED1; //TXLED On Macro
delay(1000); //for one second to signal that a button change occurred and a transmission to OTHER_ADRESS begins
TXLED0; //TXLED Off Macro
Wire.beginTransmission(OTHER_ADDRESS); //Setup I2C transmission to OTHER ADDRESS (Be a MASTER?)
Wire.write(last_state); //Send the byte of data
uint8_t err =Wire.endTransmission(); //End the transmission
/*
0:success
1:data too long to fit in transmit buffer
2:received NACK on transmit of address
3:received NACK on transmit of data
4:other error
*/
If(err){ // not Zero
if(err==1) Serial.println(" Databuffer too Long!"); //kinda impossible because you are only sending one byte?
if(err==2) Serial.println(" Slave did not Answer!, Address NACK");
if(err==3) Serial.println(" Slave did not accept data? busy? ");
if(err==4) Serial.println(" I2C net was busy, Other master has not released bus");
}
}
}
}
I did not compile this code, there may be syntax errors or open {}, But, you should get the gist.
Chuck.