Bonjour,

Désolé si mon titre est un peu vague, je ne sais pas trop comment exprimer mon problème.

En gros, j'essaye de faire communiquer un Arduino Uno avec un ATTiny85 via i2c.
L'Uno est maître, le tiny est esclave.

Sur le tiny, j'utilise TinyWire comme bibliothèque.
Sur l'Uno, celle de base.

Ma problématique est la suivante : jusqu'à quel point je dois envisager les échecs de communication entre les deux µC ?

J'ai déjà fait des programmes qui communiquent en TCP via socket pour échanger des trames de longueur arbitraire (entre autre des logs), et pour distinguer quand je devais m'arrêter de lire, nous avions convenue d'un protocole maison.
Grossièrement, les 4 premiers octet servaient à transmettre la longueur du message.

J'ai voulu appliquer le même principe, mais il semblerait que ce ne soit pas possible entièrement.

Quand mon Uno communique avec mon tiny et qu'il attends une réponse de 3 byte, j'utilise "Wire.requestFrom(I2C_ADRESS_SLAVE, 3)".
Le hic, c'est que si je fais un delay dans mon tiny ou si je n'envoie absolument rien sur l'i2c, la fonction me renvoie quand même 3, la fonction "Wire.available()" aussi (comme si j'avais bien écrit ces 3 bytes alors que pas du tout) et les 3 "Wire.read()" me renvoie 255.

Comment suis-je supposer aborder cette problématique ?

Dois-t-on partir du principe que tant que la connexion i2c est active, alors il n'y a aucune erreur possible ?
Que si je suis supposé avoir X bytes à lire, alors ils sont forcement là ?


Si mon code peut aider à voir si j'ai fait une erreur, le voici :

Fichier commun "Configuration.h"
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
#define I2C_ADRESS_SLAVE  0x00 /* Adresse i2c pour la communication avec l'esclave */
 
/* "header" est une variable de type "Byte" : c'est la première valeur reçu lors d'une transmission i2c 
 *  Les 3 octets de poids faible sont le nombre de byte à lire après le header 
 *  Les 5 octets suivant (de poids fort) sont l'ordre */
 
#define I2C_CREATE_HEADER(nb_bytes_to_send, order) ((nb_bytes_to_send & 7) + (order & 31) * 8)
 
#define I2C_HEADER_GET_NB_BYTES_TO_READ(header) (header & 7)
#define I2C_HEADER_GET_ORDER(header) (header / 8)
 
 
#define I2C_ORDER_ASK_NUMBER    0 // On demande le nombre  
                                  // 0 bytes transmit après
#define I2C_ORDER_GIVE_NUMBER   1 // On transmet le nombre
                                  // 2 bytes transmis après
Fichier du Uno (maitre) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "Configuration.h"
 
#include "Wire.h"
 
bool button1State; // Etat du bouton
 
void setup()
{
	// Initialisation i2c
	Wire.begin(); // On rejoint i2c en tant que maitre
 
	// Bouton
	pinMode(A1, INPUT_PULLUP);
 
	// DEBUG
	Serial.begin(9600); //Initialisation de la communication avec la console
	button1State = HIGH; // Boutton non pressé
}
 
void loop()
{
	int etat = digitalRead(A1);
 
	if (etat != button1State) {
		// Je passe de HIGH (buton non appuyé) à LOW (bouton appuyé)
		if (etat == LOW) {
			// Transmission a l'esclave
			Wire.beginTransmission(I2C_ADRESS_SLAVE);
			Wire.write(I2C_CREATE_HEADER(I2C_ORDER_ASK_NUMBER, 0));
			Wire.endTransmission(); 
 
			// Ici, on devrait normalement attendre  500ms avant d'avoir la vrai réponse (voir code du tiny)
			Serial.print("Wire.requestFrom = ");
			Serial.print(Wire.requestFrom(I2C_ADRESS_SLAVE, 3));
			Serial.print("\n");
 
			Serial.print("Wire.available   = ");
			Serial.print(Wire.available());
			Serial.print("\n");
 
			Serial.print("1 Wire.read()    = ");
			Serial.print(Wire.read());
			Serial.print("\n");
 
			Serial.print("2 Wire.read()    = ");
			Serial.print(Wire.read());
			Serial.print("\n");
 
			Serial.print("3 Wire.read()    = ");
			Serial.print(Wire.read());
			Serial.print("\n");
		}
		button1State = etat;
	}
}

Fichier du tiny (esclave) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include "Configuration.h"
#include "TinyWireS.h"
 
/*   ATTiny85 (Clock 20MHz)
 
             +-\/-+
    A0 PB5  1|    |8  Vcc
    A3 PB3  2|    |7  PB2 D0 A1
    A2 PB4  3|    |6  PB1 D1
       GND  4|    |5  PB0 D2
             +----+        
*/
 
#define __AVR_ATtiny85__
 
 // On déclare l'adresse de la LED de debug sur la pin 2
#define LED1_PIN PB3
 
// Simple compteur de test ...
byte count; 
 
void setup()
{
	// On rejoint le bus i2c en tant qu'esclave
	TinyWireS.begin(I2C_ADRESS_SLAVE); 
 
	// A but de test
	pinMode(LED1_PIN, OUTPUT);
	count = 0;
}
 
void loop()
{
	// Si on recoit quelque chose sur le bus I2C
	if (TinyWireS.available()) {
		byte header;        // Premier bit : il s'agit du header (composé du nombre de byte à lire (3 bit) + du numero d'ordre (5 bit) 
		byte order;         // Numero d'ordre : il s'agit d'une action que le µC dois faire
		byte nbByteToRead;  
 
		// On recupere le header
		header = TinyWireS.read();
		// On recupere l'ordre et le nombre de byte à lire
		nbByteToRead = I2C_HEADER_GET_NB_BYTES_TO_READ(header);
		order = I2C_HEADER_GET_ORDER(header);
 
		// On execute l'ordre envoyé 
		switch (order) {
			case I2C_ORDER_ASK_NUMBER:
				i2cDiscardRead(nbByteToRead, 100); // nbByteToRead devrait être égale à 0
 
				delay(500); // Simulation d'un probleme de transmission : "normalement", le maitre i2c devrait "voir" que je n'ai pas encore fais les TinyWireS.write 
 
				TinyWireS.write(I2C_CREATE_HEADER(2, I2C_ORDER_GIVE_NUMBER));
				TinyWireS.write(0);
				TinyWireS.write(count);
 
				// Limitation de count dans l'intervalle [0:100]
				++count; 
				if (100 < count) {
				count = 0;
				}
				break;
 
			default:
				// Ordre non pris en charge : On consumme tout
				i2cDiscardRead(nbByteToRead, 100);
				break;
		}
	}
}
 
 
#define DELAY_BETWEEN_READ_TENTATIVE 5 /* en milliseconde */
 
/* +------------------------------------------------------------------------------------+ */
/* | Tente de lire `nbReadToDiscard` byte pendant `timeout` millisecondes               | */
/* +------------------------------------------------------------------------------------+ */
/* | [IN]     Timeout : nombre de milliseconde avant d'être considéré en timeout        | */
/* | [RETURN] true si nbReadToDiscard lu avant timeout, false sinon                     | */
/* +------------------------------------------------------------------------------------+ */
 
bool i2cDiscardRead(int nbReadToDiscard, int timeout)
{
  for (int i = 0; i < nbReadToDiscard; ++i) {
    if (!i2cRead(NULL, timeout)) {
      Blink(LED1_PIN, 1000); // DEBUG (en cas d'erreur, led allumé pendant 1s)
      return (false);
    }
  }
  Blink(LED1_PIN, 20); // DEBUG
  return (true);
}
 
 
/* +------------------------------------------------------------------------------------+ */
/* | Tente de lire un byte pendant `timeout` millisecondes                              | */
/* +------------------------------------------------------------------------------------+ */
/* | [OUT]    result : byte lu                                                          | */
/* | [IN]     Timeout : nombre de milliseconde avant d'être considéré en timeout        | */
/* | [RETURN] true si byte lu avant timeout, false sinon                                | */
/* +------------------------------------------------------------------------------------+ */
 
bool i2cRead(byte *result, int timeout)
{
  /* Attente de timeout milliseconde max */
  while (!TinyWireS.available()) {
    delay(DELAY_BETWEEN_READ_TENTATIVE);
    timeout -= DELAY_BETWEEN_READ_TENTATIVE;
    if (timeout <= 0) {
      return (false);
    }
  }
  /* Lecture */
  if (result) {
    result = TinyWireS.read();
  } else {
    TinyWireS.read();
  }
 
  return (true);
}
 
 
/* +------------------------------------------------------------------------------------+ */
/* | Blink une led via delay : pour but de test uniquement                              | */
/* +------------------------------------------------------------------------------------+ */
/* | [IN]     led : port de la led                                                      | */
/* | [IN]     duration : temps d'eclairage en millisecondes                             | */
/* +------------------------------------------------------------------------------------+ */
 
void Blink(byte led, int duration)
{
  digitalWrite(led, HIGH);
  delay(duration);
  digitalWrite(led, LOW);
  delay(duration);
}