Bonjour
Je me suis intéressé au i2c hier, j'ai fait un petit tuto pour les LCD i2c.
Aujourd'hui, je me suis calé en tête de faire communiquer deux arduino.
Le Maitre UNO:
Il affiche sur LCD une valeur Luminosite qu'il demande à l'esclave
L'esclave NANO:
Il lit les valeurs analogiques de deux capteurs, en fait une moyenne et, lorsqu'une requête lui arrive, il renvoie cette moyenne. Tout ça en i2c.
Je ne dois pas être loin. Dans un premier temps c'est 1 qui permet de déclencher cet échange, plus tard j'essaierai avec la lettre L...
Actuellement, ça ne fonctionne pas: Luminosite=-1, et c'est pas ce que j'attends.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// indiquer (adresse i2c, nombre de colonnes, nombre de lignes)
LiquidCrystal_I2C lcd(0x3F, 16, 2);
void setup()
{
// initialise l'afficheur LCD
lcd.begin();
// efface l'ecran et place le curseur en 0,0
lcd.clear();
// ecrire à la position par defaut
lcd.print("Arduino i2c comm");
lcd.setCursor(2, -1);
lcd.print("kammo making");
// petite pause
delay(1000);
// faire clignoter le block
lcd.blink_on();
// petite pause
delay(2000);
lcd.blink_off();
lcd.clear();
lcd.print("Luminosite");
}
void loop()
{
lcd.setCursor(12, -1);
lcd.print(millis());
delay(100);
lcd.setCursor(12, -1);
lcd.print(" ");
}
Esclave:
#include <Wire.h> // blibliothèque de comm i2c
int SomLum; // la somme des lectures
int MoyLum; // la moyenne des lectures
int A1Lum; // lecture sur A1
int A2Lum; // lecture sur A2
void setup() {
Wire.begin(4); // adresse de l'esclave
Wire.onReceive(RecepEvent); // fonction de réception
}
void loop() {
for (int i = 0; i < 10; i++) { // on effectue 10 lectures
A1Lum = analogRead(A1); // on lit A1
A2Lum = analogRead(A2); // on lit A2
SomLum = SomLum + A1Lum + A2Lum; // on fait la somme
}
MoyLum = SomLum / 20; // 2 capteurs, 10 lectures, ça fait 20
SomLum = 0; // remise à zero des variables
MoyLum = 0;
}
void RecepEvent(int Q) {// fonction qui se lance si un evenement a lieu
byte Requete = Wire.read(); // recevoir la requete
if (Requete == 1) { // si la requete est la lettre L (comme Luminosite)
Wire.write(MoyLum);
} // envoyer MoyLum en reponse
}
Selon la spécification I²C, les adresses 0-->7 et 78-->7F sont réservées.
Ensuite, dans le programme du maître on ne voit aucune requête à destination de l'esclave.
Dans le programme de l'esclave, juste après avoir calculé la moyenne, tu la remets à zéro.
si row est négatif, on va chercher un élément de tableau d'indice négatif, ça craint.
Par rapport à la réponse de fdufnews, en effet, il vaudrait mieux mettre les acquisitions et le calcul de la moyenne dans la fonction RecepEvent :
void loop() {
}
void RecepEvent(int Q) {// fonction qui se lance si un evenement a lieu
byte Requete = Wire.read(); // recevoir la requete
if (Requete == 1) { // si la requete est la lettre L (comme Luminosite)
SomLum = 0; // remise à zero des variables
MoyLum = 0;
for (int i = 0; i < 10; i++) { // on effectue 10 lectures
A1Lum = analogRead(A1); // on lit A1
A2Lum = analogRead(A2); // on lit A2
SomLum = SomLum + A1Lum + A2Lum; // on fait la somme
}
MoyLum = SomLum / 20; // 2 capteurs, 10 lectures, ça fait 20
Wire.write(MoyLum);
} // envoyer MoyLum en reponse
}
En adresse j'ai mis 14... un peu au pif... est-ce que je peux faire plus intelligent?
En ce qui concerne le -1 du LCD, aucun souci, si je remplace Luminosite par millis() j'ai bien un compteur qui défile. En plus je ne comprends pas ton code ^^
Pour écarter une erreur de calcul de la part de l'esclave, j'ai remplacé la moyenne par 123.
J'ai toujours -1 qui s'affiche sur l'écran LCD
Est-ce que ça vient de l'adresse? vu que j'ai mis ça au pif
Est-ce que ça vient de la commande de requete, de l'écriture (slve->mastr)?
Est-ce un problème de lecture (Mstr sur Slve) comme on peut en avoir avec les fonctions?
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
int Luminosite; // variable qui recupere la luminosite sur esclave
// indiquer (adresse i2c, nombre de colonnes, nombre de lignes)
LiquidCrystal_I2C lcd(0x3F, 16, 2);
void setup()
{
Wire.begin(); // se connecter au bus i2c, le maitre n'a pas besoin d'adresse
lcd.begin(); // initialise l'afficheur LCD
lcd.clear(); // efface lcd
// petite intro:
lcd.print("Arduino i2c comm");
lcd.setCursor(2, -1);
lcd.print("kammo making");
delay(1000); // petite pause
// faire clignoter le block
lcd.blink_on();
// petite pause
delay(2000);
lcd.blink_off();
lcd.clear();
lcd.print("Luminosite");
}
void loop()
{
GetData(); // va chercher les donnees
LCDPrint(); // afficher ces données
delay(300); // temporisation
}
void GetData() { // fonction pour recuperer les donnees
Wire.beginTransmission(14); // requete adresse 14
Wire.write(1); // envoyer requete
Luminosite = Wire.read(); // récupérer la valeur dans Luminosite
}
void LCDPrint() { // fonction pour afficher sur lcd
// combine du pauvre pour effacer la ligne
lcd.setCursor(12, -1);
lcd.print(" ");
// ecrire la valeur
lcd.setCursor(12, -1); // place le curseur
lcd.print(Luminosite); // ecrit la valeur de Luminosite
}
Je m'étais emmêlé les pinceaux, j'avais ouvert deux fois le code et corrigé un seul...
Slave:
#include <Wire.h> // blibliothèque de comm i2c
int SomLum; // la somme des lectures
int MoyLum; // la moyenne des lectures
int A1Lum; // lecture sur A1
int A2Lum; // lecture sur A2
void setup() {
Wire.begin(14); // adresse de l'esclave
Wire.onReceive(RecepEvent); // fonction de réception
}
void loop() {
for (int i = 0; i < 10; i++) { // on effectue 10 lectures
A1Lum = analogRead(A1); // on lit A1
A2Lum = analogRead(A2); // on lit A2
SomLum = SomLum + A1Lum + A2Lum; // on fait la somme
}
MoyLum = SomLum / 20; // 2 capteurs, 10 lectures, ça fait 20
SomLum = 0; // remise à zero des variables
}
void RecepEvent() {// fonction qui se lance si un evenement a lieu
byte Requete = Wire.read(); // recevoir la requete
if (Requete == 1) { // si la requete est la lettre L (comme Luminosite)
SomLum = 0; // remise à zero des variables
MoyLum = 0;
for (int i = 0; i < 10; i++) { // on effectue 10 lectures
A1Lum = analogRead(A1); // on lit A1
A2Lum = analogRead(A2); // on lit A2
SomLum = SomLum + A1Lum + A2Lum; // on fait la somme
}
MoyLum = SomLum / 20; // 2 capteurs, 10 lectures, ça fait 20
Wire.write(123); // 123 en attendant que ça communique
} // envoyer MoyLum en reponse
}
J'ai ajouté un flash led lors de l'envoi de '123' de slave vers master, et plusieurs plus rapides lorsque la requete est repérée.
Ca ne marche pas, ca ne s'allume pas (la led s'allume une seconde au démarrage pour etre sur qu'elle fonctionne)
#include <Wire.h> // blibliothèque de comm i2c
int SomLum; // la somme des lectures
int MoyLum; // la moyenne des lectures
int A1Lum; // lecture sur A1
int A2Lum; // lecture sur A2
void setup() {
Wire.begin(14); // adresse de l'esclave
Wire.onReceive(RecepEvent); // fonction de réception
pinMode(2,OUTPUT);
digitalWrite(2,HIGH);
delay(1000);
digitalWrite(2,LOW);
}
void loop() {
for (int i = 0; i < 10; i++) { // on effectue 10 lectures
A1Lum = analogRead(A1); // on lit A1
A2Lum = analogRead(A2); // on lit A2
SomLum = SomLum + A1Lum + A2Lum; // on fait la somme
}
MoyLum = SomLum / 20; // 2 capteurs, 10 lectures, ça fait 20
SomLum = 0; // remise à zero des variables
}
void RecepEvent() {// fonction qui se lance si un evenement a lieu
byte Requete = Wire.read(); // recevoir la requete
if (Requete == 1) { // si la requete est la lettre L (comme Luminosite)
for (int i=0; i<4; i++) {
digitalWrite(2,HIGH);
delay(50);
digitalWrite(2,LOW);
delay(50);}
SomLum = 0; // remise à zero des variables
MoyLum = 0;
for (int i = 0; i < 10; i++) { // on effectue 10 lectures
A1Lum = analogRead(A1); // on lit A1
A2Lum = analogRead(A2); // on lit A2
SomLum = SomLum + A1Lum + A2Lum; // on fait la somme
}
MoyLum = SomLum / 20; // 2 capteurs, 10 lectures, ça fait 20
Wire.write(123); // 123 en attendant que ça communique
digitalWrite(2,HIGH);
delay(200);
digitalWrite(2,LOW);
} // envoyer MoyLum en reponse
}
Tu t'enmêles effectivement les pinceaux…
En I2C, le maître envoie des octets OU demande des octets, toi tu mélanges les deux…
Et l'escalve du coup reçoit des octets OU renvoie des octets.
Ce sont deux choses bien distinctes.
Dans un cas très simple, si l'esclave doit toujour renvoyer la mesure de luminosité, le maître n'a pas besoin d'envoyer des infos, il doit juste réclamer à l'esclave:
Maître:
setup ()
{
Wire.begin ();
Serial.begin (115200);
}
loop ()
{
short mesure;
Wire.requestFrom (50, sizeof (short)); // addresse au pif, mais la même que plus dans le code de l'esclave - on demande 2 octets à l'esclave
mesure = 0;
while (Wire.available ())
mesure = mesure * 256 + Wire.read ();
Serial.print ("mesure: ");
Serial.print (mesure);
delay (1000);
}
Esclave:
setup ()
{
Wire.begin (50); // adresse au pif, mais la même que plus dans le code du maître
Wire.onRequest (requestHandler);
}
loop ()
{
}
void requestHandler (void)
{
// bien que le maître ait demandé explicitement 2 octets, l'esclave n'en sait rien… la lib Wire est un peu bizarre…
short mesure;
mesure = analogRead (A1);
Wire.write (&mesure, sizeof (mesure)); // on envoie 2 octets
}
Si tout se passe bien, chaque seconde le maître réclame des données à l'esclave, qui les lui envoie.
mesure = mesure * 256 + Wire.read ();
quelques explications:
Wire.read () renvoie UN octet
la valeur que tu veux lire va de 0 à 1023 (puisque issue de analogRead), et ça ne tient pas sur 1 octet. Donc il en faut 2.
Ces deux octets on va devoir les lire un par un, et comme chaque octet peut aller de 0 à 255, on reconstruit la valeur finale à partir des deux octets lus.
Dans ton projet tu veux j'imagine envoyer des commandes différentes, c'est ça ? Et que l'esclave renvoie une réponse en fonction de la commande reçue ? Ça complique un peu, car l'esclave va SEPAREMENT recevoir la commande (dans un fonction précisée avec Wire.onReceive) et la demande de la réponse (dans un fonction précisée avec Wire.onRequest). L'esclave devra donc mémoriser la commande reçue pour pouvoir répondre après…
Laisse moi quelques minutes, je vais essayer de te faire un truc…
Oui je voudrais que le maitre puisse faire plusieurs requetes, de préférence avec des lettres, c'est plus simple de s'y retrouver ensuite (côté humain)
Le but c'est de faire un module i2c qui contienne différents types de capteurs (infrarouge, ultrasons)
Le maitre envoie la requete des capteurs dont il souhaite récupérer la valeur: 'i' ou 'u'.
Le but c'est de gagner des pins sur le maitre et de s'amuser avec une sorte de multiplexage...
Ca rentre dans mon projet de robot sumo+line tracker+contournement d'obstacles
La lib Wire a quand même des bizarreries, j'ai un peu galéré. Je dirai même plus, elle est bugguée…
Donc voici le code du maître:
//
// maitre.cpp
//
// Created by Christian Brandt on 19/06/18.
//
//
#include <Wire.h>
#define ADRESSE_ESCLAVE 14
void envoiCommande (char *commande);
void lectureReponse (char *reponse);
//--------------------------------------------------------------------------------------------------------------------------------------------
void setup (void)
{
Wire.begin ();
Serial.begin (9600);
Serial.println ();
Serial.println ();
Serial.println ();
Serial.println ("----------------------------");
Serial.println (" MAITRE ");
Serial.println ("----------------------------");
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void loop (void)
{
char reponse[32]; // max 32 octets pour la réponse
Serial.println ("----------------------------");
envoiCommande ("luminosite");
lectureReponse (reponse);
Serial.print ("luminosité: ");
if (strlen (reponse) == 0)
Serial.println (" - pas de réponse -");
else
Serial.println (reponse);
envoiCommande ("millis");
lectureReponse (reponse);
Serial.print ("millis: ");
if (strlen (reponse) == 0)
Serial.println (" - pas de réponse -");
else
Serial.println (reponse);
delay (1000);
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void envoiCommande (char *commande)
{
// on envoie des octets à l'esclave, ils seront reçus par l'esclave dans la fonction précisée avec Wire.onReceive
Wire.beginTransmission (ADRESSE_ESCLAVE);
Wire.print (commande);
Wire.endTransmission ();
Serial.print ("[MAITRE] commande envoyée: ");
Serial.println (commande);
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void lectureReponse (char *reponse)
{
int taille;
int c;
// on demande des octets à l'esclave, la demande sera reçue dans la fonction précisée avec Wire.onRequest
Wire.requestFrom (ADRESSE_ESCLAVE, 32); // on demande 32 octets, mais l'esclave pourra en renvoyer moins
// la réponse fera toujour le nombre d'octets demandés, même si l'esclave en renvoie moins
taille = 0;
do
{
c = Wire.read ();
reponse[taille++] = c;
}
while (c != '\n' && c != -1 && taille < 32);
reponse[taille--] = 0;
Serial.print ("[MAITRE] réponse reçue (");
Serial.print (taille);
Serial.print (" octets): ");
Serial.println (reponse);
}
//--------------------------------------------------------------------------------------------------------------------------------------------
et voici l'esclave:
//
// esclave.cpp
//
// Created by Christian Brandt on 19/06/18.
//
//
#include <Wire.h>
#define ADRESSE_ESCLAVE 14
char commande[32];
void receptionCommande (int longueur);
void envoiReponse (void);
int lectureLuminosite (void);
//--------------------------------------------------------------------------------------------------------------------------------------------
void setup (void)
{
Wire.begin (ADRESSE_ESCLAVE);
Wire.onReceive (receptionCommande);
Wire.onRequest (envoiReponse);
Serial.begin (9600);
Serial.println ();
Serial.println ();
Serial.println ();
Serial.println ("----------------------------");
Serial.println (" ESCLAVE ");
Serial.println ("----------------------------");
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void loop (void)
{
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void receptionCommande (int longueur)
{
int taille;
// on lit tous les octets envoyés par le maître (on ignore 'longueur')
taille = 0;
while (Wire.available ())
commande[taille++] = Wire.read ();
commande[taille] = 0;
// Serial.print ("[ESCLAVE] commande reçue: ");
// Serial.println (commande);
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void envoiReponse (void)
{
unsigned long ms;
int lum;
char reponse[32];
// on traite la commande précédemment reçue
if (strcmp (commande, "luminosite") == 0)
{
lum = lectureLuminosite ();
snprintf (reponse, 32, "%d\n", lum);
Wire.write (reponse);
Serial.print ("[ESCLAVE] commande 'luminosite', réponse envoyée: ");
Serial.print (reponse);
}
else if (strcmp (commande, "millis") == 0)
{
ms = millis ();
snprintf (reponse, 32, "%d\n", ms);
Wire.write (reponse);
Serial.print ("[ESCLAVE] commande 'millis', réponse envoyée: ");
Serial.print (reponse);
}
}
//--------------------------------------------------------------------------------------------------------------------------------------------
int lectureLuminosite (void)
{
// ici, faire les analogRead et le calcul de la moyenne
return 123;
}
//--------------------------------------------------------------------------------------------------------------------------------------------
Quelques notes:
le maître demande toutes les secondes la luminosité et (à titre d'exemple) le temps passé par l'esclave depuis la mise en route (fonction 'millis ()')
l'esclave ne fait rien dans le loop () et répond aux requêtes
les commandes et réponses sont au format texte lisible par un humain, ça prend quelques microsecondes de plus à envoyer/recevoir, mais ça rend les programmes plus lisibles.
dans l'esclave, pour cause de bug de la lib Wire, il faut construire la réponse à part, et l'envoyer avec UN SEUL Wire.write ou Wire.print - sinon seul les données du dernier Wire.write ou Wire.print seront reçues par le maître. Ne pas utiliser Wire.println, car ça fait au final deux Wire.write (voir le code de la lib Wire elle-même).
Donc bien utiliser snprintf pour construire la réponse, sans oublier le \n dans la réponse - c'est ce charactère que le maître attend pour savoir que la réponse est complète. Si on avait que des réponses de la même taille on pourrait simplifier, mais là c'est plus flexible.
Déclarer les fonctions avant l'endroit où elles sont utilisées est normalement obligatoire. Cependant l'IDE arduino ajoute automatiquement les déclarations.
indique bien un pointeur... ça devient vite indispensable de les maîtriser ! Mais ce n'est pas aussi compliqué que ça en a l'air