Wire.requestFrom() from master I2C hangs in slave I2C -- not this topic again!

i know this topic has been beat to death. i've included fully a executable test case.
hope it helps. don't recall anyone has gone this far.

"am i wasting my time?" if yes, i have to ask "why?" i've spent 3 years and
considerable funds on the production code. be a shame to scrap it. i should mention
that, during development of producton code, this has worked from time-to-time.
recently, it never works. i always update boards and libs when asked. perhaps a
recent update to one or the other is causing the problem. anyone have any insite
into this posibility?

the attached sketches, in terms if I2C call sequence, are literally cut'n'paste from
the production code. the flashing led's were added.

attachments:
-- master sketch:
-- sends a command to the slave that instructs it to flash it's led.
(number of times the slave flashes it's led is up to the slave).
-- sends a request to the slave asking it how many times it flashed.
-- slave sketch:
-- upon receiving the command, flashes the led.
-- upon receiving the request, replies with the number of times.

here-in lies the problem. the due hangs on the Wire.requestFrom().

the uno and mega have no problem replying.

-- fritz
mega(master) -> uno(slave)
-> mega(slave)
-> due_1(slave)
-> due_2(slave)
boards are powered off the hp(tm) usb ports via sabren(tm) usb hubs.

i wish it were as simple as replaceing the due's with mega's to avoid the problem.
its not. the due's are a required interface to the 10.1" displays i'm using.

fyi - the breadboard looking thing on the fritz is actually an 8-position dual-row
barrier strip - radio/shack #2740670. it's divided in half via a 4-position jumper.
master comes in on one side and feeds the 4 slaves off the other. sda/scl each.

btw, except for the uno, i bought all new boards for this test.

lee_i2c_master_test.ino (5.14 KB)

lee_i2c_slave_test.ino (4.52 KB)

was told during post that .fzz files are not allowed as an attachment. any suggestions on how to post them?

was told during post that .fzz files are not allowed as an attachment. any suggestions on how to post them?

Fritzing breadboard diagrams are discouraged because they are usually hard to follow, do not show component pin outs correctly and are usually incomplete. A hand drawn and photographed schematic is preferred if a nice CAD drawn schematic is not available. Or the Fritzing schematic view if cleaned up.

If you must post a Fritz, I think that you can save the image with File, Export, As Image and save as .JPG.

Also, please read the "how to post code" sicky at the top of the forum.

thanks for the pointer to the export option. I don't think this fritz is hard to read so i'm just going to go ahead and attach it. if you disagree, let me know.

will read "how to post code" - sorry.

//
#include <Wire.h>
//
const int I2C_due_1 = 124;    // slave I2C addreess
const int I2C_due_2 = 125;
const int I2C_uno   = 126;
const int I2C_mega  = 127;
//
const int num_slaves = 4;
//
// same as above in array form
const int int_slave[num_slaves] = { I2C_due_1, I2C_due_2, I2C_uno, I2C_mega };
//
const int heartbeat_led = 12;  // this blinks whenever i request slave data
const int command_led = A0;    // currently not really used in the master.
                               // flashed in the slaves when the master sends the flash command
//
const int flash = 11;    // flash command
                         // number itself has no meaning except it's the number of my 
                         // favorite nascar driver
int state_array[num_slaves] = { 0, 0, 0, 0 };    // number of times the slave decided to 
                                                 // flash it's command led after receiving
                                                 // the flash command
// function prototypes
//
void send_to_slave( char *calling_function,
                    int slave,
                    int command );
int state_check( int slave );

//
int I2C_trace = true;          // serial out trace flags
int function_trace = true;
int trace = true;

//
//
void setup() {

  //
  Serial.begin( 9600 );
  Serial.println( "setup(): Serial port open: 9600 baud" );

  Wire.begin();
  Serial.println( "setup(): Wire.begin() successful" );

  // flash led's so we know they're working
  //
  Serial.println( "setup(): flash heartbeat led" );
  pinMode( heartbeat_led, OUTPUT );
  digitalWrite( heartbeat_led, HIGH );
  delay( 250 );
  digitalWrite( heartbeat_led, LOW );
  //
  Serial.println( "setup(): flash command led" );
  pinMode( command_led, OUTPUT );
  digitalWrite( command_led, HIGH );
  delay( 250 );
  digitalWrite( command_led, LOW );
}

void loop() {

  // tell slaves to flash their command led
  //
  for( int i=0; i<num_slaves; i++ ) {
    send_to_slave( (const char *)"loop(): ", int_slave[i], (const int)flash );
  }

  // wait 2 seconds and then ask the slave how many times it flashed it's led.
  // the delay has to be long enough to accomodate max number of flashes.
  // -- slave will flash its led a max of 4 times as follows:
  //    ( set HIGH, delay(0.2), set LOW, delay(0.2) ) x 4(max) = 1.6 secs max
  // -- 2 seconds is more than enough
  //
  delay( 2000 );
  for( int i=0; i<num_slaves; i++ ) {
    state_array[i] = state_check( int_slave[i] );
    if( trace ) {
      Serial.print( "loop(): received state " );
      Serial.print( state_array[i] );
      Serial.print( " from slave " );
      Serial.println( int_slave[i] );
    }
  }
}

//
//
const char *wire_error[5] = {
           "success", 
           "data too long to fit in transmit buffer", 
           "received NACK on transmit of address",
           "received NACK on transmit of data", 
           "other error",
};
void send_to_slave( char *calling_function,
                    int slave,
                    int command ) {
  //
  if( function_trace ) {           
    Serial.print( "start send_to_slave(): calling function: " );
    Serial.println( calling_function );
  }
  //
  int rc = 0;                
  int n_bytes;
  //
  Wire.beginTransmission( (byte)slave );
  if( I2C_trace ) {
    Serial.print( ": send_to_slave(): Wire.beginTransmission(): slave: " );
    Serial.println( (byte)slave );
  }
  //
  n_bytes = Wire.write( (byte)command );
  //
  if( I2C_trace ) {
    Serial.print( ": send_to_slave(): Wire.write(): n_bytes: " );
    Serial.print( n_bytes );
    Serial.print( " data: " );
    Serial.println( command );
  }
  //
  rc = Wire.endTransmission( true );
  if( I2C_trace ) {
    Serial.print( ": send_to_slave(): Wire.endTransmission(): " );
    Serial.println( wire_error[rc] );
  }
  //
  if( function_trace )
    Serial.println( "end:" );
}

//
//
int state_check( int slave ) {

  //
  if( function_trace )
    Serial.println( "start state_check():" );
    
  // HACK! bypass requestFrom() to due's for now
  //       -- command hangs in slave for due's 100%
  //
  if( (slave == I2C_due_1) || (slave == I2C_due_2) ) {
    if( function_trace ) {
      Serial.print( "state_check(): bypassing requestFrom() call to due slave: " );
      Serial.println( slave );
    }
    return( -1 );
  }
  //
  int state;
  byte n_bytes;
  //
  digitalWrite( heartbeat_led, HIGH );
  //
  n_bytes = Wire.requestFrom( slave, 1, true );
  if( I2C_trace ) {
    Serial.print( "state_check(): received " );
    Serial.print( n_bytes );
    Serial.print( " bytes from slave " );
    Serial.println( slave );
  }
  delayMicroseconds( 50 );
  if( I2C_trace )
    Serial.print( "         data: " );
  while( Wire.available() ) {
    state = Wire.read();
    if( I2C_trace ) {
      Serial.print( state );
      Serial.print( " " );
    }
  }
  if( I2C_trace )
    Serial.println();
  //
  delay( 150 );    // delay so we can actually see the blink
  digitalWrite( heartbeat_led, LOW );
  //
  if( function_trace )
    Serial.println( "end:" );

  return( state );
}

How to post images.

I see no pullups on the I2C bus. There needs to be pullup resistors from SDA to Vcc and SCL to Vcc. The Mega has 10K pullups, but may be too weak to do the job.

How long are the wires connecting all of those I2C pins? I2C bus is for interconnecting devices on the same or very close by boards. You can decrease the pullup resistance (stronger pullups), but the length of the wires is limited by the bus capacitance.

Where are the current limit resistors for the LEDs. You risk burning out pins and/or LEDs the way you have them connected.

//#include Arduino.h
//#include <ephemera_common.h>
//#include <time.h>
//
#include <Wire.h>
//
const int I2C_due_1 = 124;    // slave I2C addreess
const int I2C_due_2 = 125;
const int I2C_uno   = 126;
const int I2C_mega  = 127;
//
// ######## IMPORTANT ######## IMPORTANT ######## IMPORTANT ######## IMPORTANT ########
//
// BEFORE COMPILING/UPLOADING YOU MUST KNOW WHICH IS THE DESTINATION SLAVE, AND THEN
// SET THE IT VIA "this_slave" BELOW
// -- YOU'LL, AS USUAL, ALSO NEED TO KNOW TO WHICH USB PORT THE BOARD IS CONNECTED.
//
//const int this_slave = I2C_due_1; const char *slave_name = "I2C_due_1";
const int this_slave = I2C_due_2; const char *slave_name = "I2C_due_2";
//const int this_slave = I2C_uno;   const char *slave_name = "I2C_uno";
//const int this_slave = I2C_mega;  const char *slave_name = "I2C_mega";
//
// ######## IMPORTANT ######## IMPORTANT ######## IMPORTANT ######## IMPORTANT ########
//
const int heartbeat_led = 12;  // this blinks whenever we get an I2C_request() event
const int command_led = A0;    // this flases whenever we get an I2C_receive() event.
                               // number of blinks is random()
//
const int flash = 11;     // the flash command
int flash_event = false;  // set to default
long flash_count = 0;     // set to default
//
int I2C_trace = true;          // serial out trace flags
int function_trace = true;
int trace = true;

//
//
void setup() {

  //
  Serial.begin( 9600 );
  Serial.println( "setup(): Serial port open: 9600 baud" );

  //
  Wire.begin( this_slave );
  Wire.onReceive( I2C_receive );
  Wire.onRequest( I2C_request );
  Serial.println( "setup(): Wire.begin() successful" );

  // don't care what the web says -- this don't work
  //   -- in fact, with this set i can't even receive commands.
  //      no data on wireAvailavle()
  /*
  if( (this_slave == I2C_due_1) || (this_slave == I2C_due_2) ) {
    pinMode( SDA, INPUT_PULLUP );
    pinMode( SCL, INPUT_PULLUP );
  }
  */

  // flash led's so we know they're working
  //
  Serial.println( "setup(): flash heartbeat led" );
  pinMode( heartbeat_led, OUTPUT );
  digitalWrite( heartbeat_led, HIGH );
  delay( 250 );
  digitalWrite( heartbeat_led, LOW );
  //
  Serial.println( "setup(): flash command led" );
  pinMode( command_led, OUTPUT );
  digitalWrite( command_led, HIGH );
  delay( 250 );
  digitalWrite( command_led, LOW );
}

//
//
void loop() {

  // if flash_event is set via an I2C_request() event
  //   -- get flash_count for this request
  //   -- flash the command_led
  //   -- reset flash_event
  //   -- save flash_count for query from master
  //
  if( flash_event ) {
    flash_count = random( 1, 5 );
    if( trace ) {
      Serial.print( "loop(): " );
      Serial.print( slave_name );
      Serial.print( "(): flashing led's at flash count: " ) ;
      Serial.println( flash_count );
    }
    for( int i=0; i<flash_count; i++ ) {
      digitalWrite( command_led, HIGH );
      delay( 200 );
      digitalWrite( command_led, LOW );
      delay( 200 );
    }
    flash_event = false;
  }
  //
  delayMicroseconds( 50 );    // give loop a rest
}

// receive commands from the master
//
void I2C_receive( int n_chars ) {
  //
  if( I2C_trace ) {
    Serial.print( "I2C_receive: n_chars: " );
    Serial.print( n_chars );
  }
  //
  while( Wire.available() ) {
    //
    int event = Wire.read();
    //
    if( I2C_trace ) {
      Serial.print( ", event: " );
      Serial.println( event );
    }
    //
    switch( event ) {    // yes, this could certainly be an "if".
                         // it's a cut'n'paste from production code that had many "cases"
      case flash:
        flash_event = true;
        break;
      default:
        Serial.print( "I2C_receive(): HARD ERROR: receivied unknown event from master: " );
        Serial.println( event );
        break;
    }
  }
  //
  if( I2C_trace )
    Serial.println( "I2C_receive: end: " );
}

// handle request event from the master
// -- return current flash count
//
void I2C_request() {
  //
  digitalWrite( heartbeat_led, HIGH );
  //
  if( I2C_trace )
    Serial.print( "I2C_request:" );
  //
  Wire.write( (byte)flash_count );    // return the current flash state here
  //
  if( I2C_trace ) {
    Serial.print( " event: " );
    Serial.println( flash_count );
  }
  //
  delay( 150 );    // delay so we can actually see the blink
  digitalWrite( heartbeat_led, LOW );
  //
  if( I2C_trace )
    Serial.println( "I2C_request: end:" );
}

Unlike other Arduino boards, the Arduino Due board runs at 3.3V. The maximum voltage that the I/O pins can tolerate is 3.3V. Providing higher voltages, like 5V to an I/O pin could damage the board.

I think that connecting a 3.3V I2C bus to a 5V I2C bus without level shifters is a mistake.

easy answer first:
yes, you caught me being lazy. even though i have bags of them, i decided to bypass
the resistors for this simple test case. since i also have bags of led's, i didn't
care if i burnt a few out. i didn't know this practice could cause damage to the pins.
thanks for the warning. i'll add the resistors.

how long are the wires?:
the fritz is an exact representation of the configuration. the boards are screwed,
in the same order as the fritz, to a 1x4 piece of pine that's about 16" long.
max distance from the master (left most mega) to the right most due is about 12".

"You can decrease the pullup resistance (stronger pullups)" -- i get that, "but the
length of the wires is limited by the bus capacitance." -- i don't get that. how do i
know what the bus capacitance is?
as an aside, shouldn't the wire library handle this?

pullup resistors:
i've read information about pullup resistors until my eyes crossed and i got a migraine.
some say yes. some say no. some say, especially no, in regards to due since it already
has pullups. adding more can cause pin failure. some have used hot tweezers to remove
them in favor of forcing the issue in some other way???. some say add INPUT_PULLUP to the
code for due pins. i tried that. it makes matters worse. see comments in slave code.

you mention, "The Mega has 10K pullups, but may be too weak to do the job."
i have no problem, nor ever did, with the mega or uno. it's the due that's kicking
my butt.

based on what i've read, i'm a bit nervous about adding the resistors to the circiut.
however, if it's your recomendation, i'm willing to try. i assume that the connection
to Vcc will be daisy chain style across boards, and insert the resistors just off the
master sda/scl wires. or, should i bypass the uno and mega slaves since they work.

in either case i'm going to need a value for the resistors. i'll wait for you reply.

i'll wait the required 5 minutes and post the fritz that shows the new wiring.

thanks

twimc, can the code window on the forum be any smaller? i have a hard time reading it
in a window that small and i wrote it.

...

level shifters:
you have my attention. how many do I need? would you be willing to provide a wiring diagram for my specific
configuration?

No...

The Due is a 3.3volt processor, and will get damaged (maybe already is) if you pull it's I2C lines up to 5volt.
You MUST use I2C level converters (small breakout boards with tiny fets on them).

Why this (silly) construction of five processors.
Couldn't you use port expanders?

What are these LEDs doing between pin12 and ground or A0 and ground.
LEDs without current limiting resistor will damage pins.
I hope the pin is not set to OUTPUT.

Don't use external pull up resistors.
Both Megas have buildin 10k pull up resistors, and the Uno has software pull up.
That combined pull up (~4k) more than enough for that setup.
Leo..

"Why this (silly) construction of five processors.
Couldn't you use port expanders?"

I have no idea what a port expander is in this context. let me describe my architecture. it's a slot car lap counter which has evolved into much more than that. after reading this, if you think port expanders are better suited, let me know.

from left to right on the fritz: (highly abbreviated)

master(mega): responsible for:
-- general program flow. it knows when to tell the 2 dues attached to the leader board when to display the:
-- practice display
-- select car display (I have 24 cars. only 4 can race)
-- fireworks (yes I have fireworks)
-- actual leader board display (start race -boogity, boogity, boogity)
-- victory lane
-- if that's not enough, it's responsible for determining the exact order that cars cross the finish line.
it tells the leader board, who is responsible for determining laps, lap speed, and best lap.
-- based on response from the leader board, it tells the announcer when to announce thing like,
-- driver 11 denny Hamlin takes the lead lap
-- driver 20 tony stewart get's the lucky dog

slave(uno) :responsible for:
-- simply, throwing caution. it relays that information to the master on request.

slave(mega): the announcer board connected to an emic 2.
-- simply announces whatever the master tells it to.

slave(due's - 2 of them) - each executing the same exact code (we alluded to this earlier)
-- responsible for writing to the display whatever the master tells it to.
-- it also knows things like current lap, last lap, final lap, lineup, which it has to relay that information
to the master on request.

so, although the master has control, it's not omnivident . it relies on obtaining information from the
slaves. all works fine until I ask a due.

in a nut shell:
master --> to slaves --> do this
master --> to slaves <-- tell me this

see what I mean about pullup resistors. we have a difference of opinion on this single thread. i'm getting a migraine again.

"so, which is it young fella? you want I should freeze, or drop to the ground?"

Not a "youg fella" here.
Pull up resistor value is irrelevant in this case, with the Arduinos that close together.
No external resistors work, 470 ohm resistors also will work.
Different story if there was 5 meters of interconnect cable involved.

But... you CAN't connect a 5volt Arduino and a 3.3volt Arduino together like that.
Connecting a 5volt output (from the Mega and/or Uno) to a 3.3volt Due pin will fry the Due chip.

We can't advice you what type of port expanders you need without knowing what you want to drive.
Maybe you don't even need them with I2C (potentially drive 127 devices), three spare serial ports (Mega) and SPI.
Leo..

Delta_G:
Seems to me like that could all be done with one Arduino. Take your pick of the Mega or the Due.

That would mainly depend on sensors and on what needs to be driven.
Most 'older' sensors are 5volt only. Newer ones are 3.3volt only.
Leo..

What I'm saying is that you should select a Mega/Due based on the least amount of level shifters you have to use.
Leo..

Delta_G:
Seems to me like that could all be done with one Arduino. Take your pick of the Mega or the Due.

you're forgetting a previous post. i have 2 10.1" displays, each of which require a due snapped on to it. there's no way around this. add 1 board for the master for control, ad we've got at least 3.