uscire da richiesta dati I2C dopo n secondi

Ciao ragazzi, oggi mi sono imbattuto in un piccolo problemino, ho dato un'occhiata in giro con google ma non ho trovato molto...
Ho due arduino UNO che comunicano tra di loro attraverso I2C...A chiede a B l'invio di 3 float ("scomposti" con la union) il che funziona alla perfezione, se non fosse che nel caso in cui B non fosse acceso, A attende una risposta praticamente all'infinito...
Vorrei capire se c'è un modo per modificare o interrompere la funzione Wire.requestFrom() dopo un determinato tempo....per progetto avrei necessità che dopo tot tempo se non c'è risposta A prosegua con "il suo lavoro"...
vi allego i code

MASTER (A)

void setup() {
  Serial.begin(9600);    
  Wire.begin(0x11); //inizializzazione master Fadec1

}

void loop() { 
    richiestaDati();
    Serial.println(Temp0_Val2);
    Serial.println(TempEGT_Val2);
    Serial.println(N1_Val2);
    Serial.println(fadecs);

    /* TANTO ALTRO LAVORO DA FARE PER A*/

}

void richiestaDati()
{
  int i=0, j;
  byte dati[dataCount];
  
  Wire.requestFrom(slaveAddress, dataCount);    // richiesta di 12bytes (3float);
  while(Wire.available())    // slave may send less than requested
  { 
    dati[i]=Wire.read();
    i++;
  }
  
  for(i=0;i<dataCount/3;i++)
  {
    j=i*3;
    Temp0_Val2_S.b[i] =dati[j];
    TempEGT_Val2_S.b[i] = dati[j+1];
    N1_Val2_S.b[i] = dati[j+2];
  }
  
  Temp0_Val2=Temp0_Val2_S.f;
  TempEGT_Val2=TempEGT_Val2_S.f;
  N1_Val2=N1_Val2_S.f;
  
  if(i==dataCount/3)
  {
    fadecs=1;
  }
  else
  {
    fadecs=0;
  }
  delay(100);
  
}

SLAVE (B)

void setup() {
  Serial.begin(9600);
  Wire.begin(0x13); //inizializzazione slave Fadec2 address 0x09
  Wire.onRequest(requestEvent);  // register event
  
}

void loop() {
  delay(100);
}

void requestEvent()
{
  
  Temp0_Val_S.f=10.6;
  TempEGT_Val_S.f=11.6;
  N1_Val_S.f=12.6;
  
  byte dati[dataCount];
  int i,j;
  
  for(i=0; i<4;i++)
  {
    j=i*3;
    dati[j]=Temp0_Val_S.b[i];
    dati[j+1]=TempEGT_Val_S.b[i];
    dati[j+2]=N1_Val_S.b[i];
  }
  Wire.write(dati, dataCount);
  
  delay(100);
  LastEventM= millis();
}

Grazie mille per l'aiuto
Max

L'utente lesto tempo fa si era messo al lavoro per una versione della libreria Wire che gestisce l'I2C che non fosse bloccante perché l'attuale si può bloccare in attesa di dati:
http://forum.arduino.cc/index.php?topic=117426.0
Non so a che punto è il lavoro.

Richiesta dati come è scritta ?
Io scrivetei li un timeout, se non riceve risposta deve uscire dalla funzione

Il problema è la lib Wire, che ha dei cicli while da cui non esce se non legge lo stato di ready, cioè di lettura effettuata. Credo che lesto lavorasse a quello.

Si ricordo la libreria, se funge va bene cosi, la mia idea sarebbe quella di creare un timeout basato su timer per bypassare il while bloccante

Basterebbe mettere un while con 2 condizioni, timeout e stato del byte. IL timeout basterebbe implementarlo con la millis. Sarebbe più che sufficiente. Però non so se e come lavora la lib. Andrebbe chiesto a lesto, che la studiò a fondo (a onor del vero, non ricordo neanche la soluzione che proponeva :sweat_smile: )

Io intendo un interrupt su timer overflow che bypassa del tutto il problema, anche l'attuale lib bloccante

Avevo capito cosa intendevi ma mi pare un pò drastica come soluzione. E come lo riprendi il controllo del codice?
Va creato un sistema abbastanza affidabile. Devi salvarti il punto da cui ripartire, attivare il timer, chiamare la funzione, aspettare che vada in timeout, lanciare la ISR da cui inserire l'indirizzo nel Program Counter da cui far riprendere il codice (altrimenti ripartiresti nel while bloccante).
Siccome il while legge lo stato di un byte, non è nulla di complesso infilare nel while anche un controllo su millis. Molto più semplice.

Drastica ma inbloccabile :slight_smile:

Quello che dici tu obbliga ad appoggiarsi ad una lib non bloccante giusto ?
Se la lib di lesto è funzionante in realtà non serve nemmeno il millis, perché appunto non essendo bloccante andrà avanti senza restituire nulla.

La modifica che dicevo io era da apportare a livello di libreria, cosa che anche lesto fece a suo tempo.
Che cosa fece non lo so, non ho guardato il suo codice.

Ok, capisco
Nemmeno io l'ho mai usata \ letta

Leggendo le vostre risposte mi è venuto un po' di sconforto dato che temevo una risposta del genere...tuttavia, armato di un po' di pazienza mi sono messo a sbirciare tra le librerie...vi scrivo un po' le considerazioni a cui sono giunto io e vediamo se riusciamo a farne qualcosa di buono.. xD

La libreria Wire fa riferimento all'header wire.h in C:\Program Files (x86)\Arduino\libraries\Robot_Control\arch\avr che a sua volta si lega a wire.cpp sempre in C:\Program Files (x86)\Arduino\libraries\Robot_Control\arch\avr
all'interno della funzione requestFrom viene richiamata la funzione

// perform blocking read into buffer
  uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop);

twi_readFrom è parte del file twi.c questa volta in C:\Program Files (x86)\Arduino\libraries\Robot_Control\arch\avr\utility

la funzione originale effettivamente NON prevede l'uscita dal ciclo durante il check della linea

Funzione originale

/* 
 * Function twi_readFrom
 * Desc     attempts to become twi bus master and read a
 *          series of bytes from a device on the bus
 * Input    address: 7bit i2c device address
 *          data: pointer to byte array
 *          length: number of bytes to read into array
 *          sendStop: Boolean indicating whether to send a stop at the end
 * Output   number of bytes read
 */
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{  
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver
  while(TWI_READY != twi_state){ 
    continue;
  }
  
  twi_state = TWI_MRX;
  twi_sendStop = sendStop;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length-1;  // This is not intuitive, read on...
  // On receive, the previously configured ACK/NACK setting is transmitted in
  // response to the received byte before the interrupt is signalled. 
  // Therefor we must actually set NACK when the _next_ to last byte is
  // received, causing that NACK to be sent in response to receiving the last
  // expected byte of data.

  // build sla+w, slave device address + w bit
  twi_slarw = TW_READ;
  twi_slarw |= address << 1;

  if (true == twi_inRepStart) {
    // if we're in the repeated start state, then we've already sent the start,
    // (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
    // We need to remove ourselves from the repeated start state before we enable interrupts,
    // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
    // up. Also, don't enable the START interrupt. There may be one pending from the 
    // repeated start that we sent outselves, and that would really confuse things.
    twi_inRepStart = false;			// remember, we're dealing with an ASYNC ISR
    TWDR = twi_slarw;
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);	// enable INTs, but not START
  }
  else
    // send start condition
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

  // wait for read operation to complete
  while(TWI_MRX == twi_state){
    continue;
  }

  if (twi_masterBufferIndex < length)
    length = twi_masterBufferIndex;

  // copy twi buffer to data
  for(i = 0; i < length; ++i){
    data[i] = twi_masterBuffer[i];
  }
	
  return length;
}

la modifica che ho pensato riguarda la prima parte e cioè

uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{
  unsigned long LastEvent = millis(); //by max10891
  unsigned long StartEvent = millis(); //by max10891
  
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver --- exit if 200millis of nothing
  while(TWI_READY != twi_state  && LastEvent-StartEvent>200){
	LastEvent=millis(); //by max10891
        continue;
  }
  
//by max10891
  if(LastEvent-StartEvent>200)
	return 0;
.........
.......
........
........
.......
}

in questo modo passati 200 millisencondi senza la linea impiegata dovrebbe uscire dal while e successivamente ritornare 0 come lunghezza...

non ho potuto provarla ora, dovrei provarla nel pomeriggio, secondo voi può avere un senso? se così funzionasse sarebbe utile implementarlo anche nelle altre funzioni tipo la read...ora come ora io ho modificato solo twi_readFrom.....

A più tardi, se avete suggerimenti ben venga!
Max

Perché usare 2 variabili?

uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{
  unsigned long startPolling;
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver --- exit if 200millis of nothing
  startPolling = millis()
  while((TWI_READY != twi_state)  && (millis() - startPolling < 200)){
        continue;
  }
  
  if(millis() - startPolling > 200)
	return 0;
  }
  ......
}

era semplicemente per richiamare meno volte la millis()...non sapevo quale delle due necessiti di più tempo se richiamare la funzione ad ogni ciclo o utilizzare la variabile...tutto qua :slight_smile:

x Max

while(TWI_READY != twi_state  && LastEvent-StartEvent>200)

Questa e' sbagliata, perche' all'inizio Last/Start sono simili, quindi False, ed avendo usato un AND esce dal while subito

La soluzione di Leo mi sembra OK, manca solo una graffa sull'ultimo return 0 se non sbaglio

Ottima Analisi del problema Max, bravo
Karma+1 :wink:

perdonami hai perfettamente ragione ci va il minore....grazie del +1, più tardi lo provo e controllo! :wink: eventualmente c'è un modo per segnalare la cosa a qualcuno? così magari alla prossima versione della libreria viene integrata e anche chi non legge la sezione italiana può avere una mano..poi non so magari non serve a nessuno... xD

A dopo per la prova finale :slight_smile:

uhm...sto provando il tutto c'è forse qualche passaggio strano per far ricaricare la libreria o all'IDE o allo sketch? sembra non andare, come se non ci fosse la modifica...mi perdo qualcosa??

le segnalazioni le puoi fare direttamente da github, nel profilo di Arduino

Per la lib, devi modificare la lib a IDE chiuso, e poi riavviare altrimenti le modifiche possono non essere viste.

guarda ho modificato ad ide chiuso ma sembra non andare....addirittura se tolgo i file twi.c e twi.h dalla cartella l'ide non fa una piega.....bhooo cosa può essere?

ho trovato questo ma non mi è stato molto di aiuto....anche perchè appunto togliendo i file manco se ne accorge :zipper_mouth_face:
http://forum.arduino.cc/index.php?topic=65138.15

  1. sei sicuro stai usando la wire che e' in Robot Control e non la Wire che di base e' nella cartella libraries ?
  2. non e' che hai una cartella libraries anche nella tua sketchbook ?