Sensor MLX90614 With Arduino DUE

Bonjour,
Le sujet avait été abordé sur le post twimaster.cpp for I2C (to get a MLX90614 to work) .
J'ai testé ce code et il fonctionne bien sur l'I2C normal mais j'ai deux capteurs avec la même adresse (0x5A) , malgré différents essais et je n'ai pas les capacités informatiques pour en faire fonctionner un sur l'I2c normal et l'I2C 1 (SCL1, SDA1) en modifiant le code.
Merci de votre aide
Cordialement

Essayez de le faire par harware avec le circuit suivant, au moyen de deux broches que vous créez comme puce de sélection pour deux éléments avec la même adresse I2C

Bonsoir

Le besoin de gestion de plusieurs périphériques I2C de même adresse a engendré chez Texas Instruments un circuit intégré dédié à cette fonction : le TCA9548. On le trouve sur le module 2717 d'Adafruit

https://www.gotronic.fr/art-multiplexeur-i2c-ada2717-26051.htm

https://www.adafruit.com/product/2717

egalement sur des modules très bon marché via la plateforme Aliexpress
https://fr.aliexpress.com/wholesale?catId=0&initiative_id=SB_20211130143602&SearchText=TCA9548

Librairie pour TCA9548 : https://www.arduino.cc/reference/en/libraries/tca9548/

Bonsoir,
Ce serait une solution mais à l'origine la boucle 0 (static void Wire_Init(void)) et la boucle 1(static void Wire1_Init(void)) sont bien définies au début du sketch mais comment on fait fonctionner la boucle wire 1 définie ?
c'est cela qui est dommage de ne pas arriver à faire et je ne comprends pas le code même la définition "#define SYNC_PIN 2" je ne la comprends pas car il n'y a pas de fil attaché à la fiche 2 puisque SCL c'est 21 et SDA c'est 20 .
De plus cette voie 2 est mise à l'état 0 puis 1 dans la loop
Tout cela pour moi reste un grand mystère
Bonsoir et merci

Bonjour

As tu lancé un moteur de recherche avec comme mots clef ' Due second I2C bus' ?
Tu y découvrirais des chose intéressantes , en particulier le besoin de résistances de pul-up sur les llignes SCL1 et SDA1 de ce second bus I2C ( elles sont intégrées pour les lignes SCL0 et SDA0).
Ton problème est peut être matériel non logiciel.

Peu de participants de ce forum utilisant la carte DUE, et parmi eux rares sont ceux qui utilisent son second bus I2C. .... . IL existe un forum (anglophone) consacré à cette carte.
C'est sans doute le meilleur endroit pour avoir des détails sur l'utilisation de second bus I2C https://forum.arduino.cc/c/hardware/arduino-due/64

Bonjour,
Oui cette carte est peu utilisée mais pourtant très intéressante et puissante mais les librairies classiques bloquent souvent car ARM et non AVR.
J'ai déjà utilisé normalement le deuxième port I2C sans problème en modifiant les bibliothèques. mais dans ce cas là ou le programme est en "pur informatique de base" alors que je ne suis pas informaticien de formation je bloque.
Effectivement je vais faire un post sur le lien du forum DUE
Encore merci

Oui cette carte est peu utilisée mais pourtant très intéressante et puissante mais les librairies classiques bloquent souvent car ARM et non AVR.

il me semble que sur ce forum on est nombreux a avoir trouvé ailleurs la puissance , et plus la communauté d'utilisateurs est vaste, plus les adaptations de librairies sont nombreuses.

En posant la question de l'ajout de résistances de pull-up sur le second bus I2C je pose une question d'électronicien, pas d'informaticien !!
Tu dis maintenant avoir déjà utilisé avec succès le second bus I2C d ela DUE, ces résistances sont dont probablement présentes

Sur ce forum il ne faut pas lésiner sur les informations fournies pour avoir de l'aide
Prendre connaissance des Bonnes Pratiques du Forum

Re Bonjour,
Suite au précédent post je ne sais pas quoi ajouter comme information pour que vous puissiez m'aider davantage .
Électroniquement le bus I2C est OK mes tensions de bus à l’oscilloscope sont bonnes : 3.3V les résistances de pull-up sont présentes sur la carte mais je n'ai pas de trafic c'est le code que je ne maitrise pas (que je ne comprends pas) pour l'adapter et faire basculer sur le deuxième bus .
Je ne suis pas un expert de l'informatique et en essayant différentes cartes je me suis heurté souvent à un blocage des bibliothèques car non arduino et non AVR (teensy, etc..). C'est pour cela que j'ai abandonné et que je reste sur Arduino et que je suis passé sur la DUE pour davantage de capacité mais.... les problèmes sont toujours présents quelquefois.
Merci

le texte des Bonnes Pratiques n'est pas assez clair et détaillé ?

Donnez le maximum d'informations : indiquez bien sûr votre problème, postez le code source avec les balises de code, ajoutez les erreurs affichées par le logiciel (du texte se poste en texte, copier coller, pas de copie d'écran de texte). Décrivez les manipulations déjà effectuées pour débuguer ainsi que toute information pertinente (matériel concerné, OS utilisé, version IDE utilisée, schéma électronique du montage, alimentation, etc). Des liens clickables et des schémas clairs sont appréciés.

Je ne vois pas le 'code modifié' , posant problème , code posté conformément t à la partie 'Quelques outils' des 'Bonnes pratiques' .....et d'ailleurs sur mon smartphone ne vois pas non plus le code d'origine ni schéma du montage (j'y aurait vu les pull-up indispensables)

Pour être aidé il faut faciliter le travail des aidants (bénévoles) en leur donnant un accès direct et clair aux données du problème... eviter de leur donner un 'jeu de piste.....

ReBonjour,
Je vous mets le code dont je vous avais mis le lien du post complet traitant de ce problème dans mon premier message:

#include <Arduino.h>
#include <include/twi.h>

#define ADDR      0x5A

//EEPROM 32x16
#define TO_MAX    0x00
#define TO_MIN    0x01
#define PWM_CTRL  0x02

//RAM 32x16
#define RAW_IR_1  0x04
#define RAW_IR_2  0x05
#define TA        0x06
#define TOBJ_1    0x07
#define TOBJ_2    0x08

#define SYNC_PIN  2

static const uint32_t TWI_CLOCK = 100000;
static const uint32_t RECV_TIMEOUT = 100000;
static const uint32_t XMIT_TIMEOUT = 100000;

Twi *pTwi = WIRE_INTERFACE;

static void Wire_Init(void) {
  pmc_enable_periph_clk(WIRE_INTERFACE_ID);
  PIO_Configure(
  g_APinDescription[PIN_WIRE_SDA].pPort,
  g_APinDescription[PIN_WIRE_SDA].ulPinType,
  g_APinDescription[PIN_WIRE_SDA].ulPin,
  g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration);
  PIO_Configure(
  g_APinDescription[PIN_WIRE_SCL].pPort,
  g_APinDescription[PIN_WIRE_SCL].ulPinType,
  g_APinDescription[PIN_WIRE_SCL].ulPin,
  g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);

  NVIC_DisableIRQ(TWI1_IRQn);
  NVIC_ClearPendingIRQ(TWI1_IRQn);
  NVIC_SetPriority(TWI1_IRQn, 0);
  NVIC_EnableIRQ(TWI1_IRQn);
}

static void Wire1_Init(void) {
  	pmc_enable_periph_clk(WIRE1_INTERFACE_ID);
	PIO_Configure(
			g_APinDescription[PIN_WIRE1_SDA].pPort,
			g_APinDescription[PIN_WIRE1_SDA].ulPinType,
			g_APinDescription[PIN_WIRE1_SDA].ulPin,
			g_APinDescription[PIN_WIRE1_SDA].ulPinConfiguration);
	PIO_Configure(
			g_APinDescription[PIN_WIRE1_SCL].pPort,
			g_APinDescription[PIN_WIRE1_SCL].ulPinType,
			g_APinDescription[PIN_WIRE1_SCL].ulPin,
			g_APinDescription[PIN_WIRE1_SCL].ulPinConfiguration);

	NVIC_DisableIRQ(TWI0_IRQn);
	NVIC_ClearPendingIRQ(TWI0_IRQn);
	NVIC_SetPriority(TWI0_IRQn, 0);
	NVIC_EnableIRQ(TWI0_IRQn);
}*/

void setup() {
  Serial.begin(9600);

  pinMode(SYNC_PIN, OUTPUT);
  digitalWrite(SYNC_PIN, LOW);

  Wire_Init();
  // Disable PDC channel
  pTwi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
  TWI_ConfigureMaster(pTwi, TWI_CLOCK, VARIANT_MCK);
}

void loop() {
  uint16_t tempUK;
  float tempK;
  uint8_t hB, lB, pec;

  digitalWrite(SYNC_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(SYNC_PIN, LOW);

  TWI_StartRead(pTwi, ADDR, TOBJ_1, 1);

  lB = readByte();
  hB = readByte();
  
  //last read
  TWI_SendSTOPCondition(pTwi);
  pec = readByte();
  
  while (!TWI_TransferComplete(pTwi)) 
    ;
  //TWI_WaitTransferComplete(pTwi, RECV_TIMEOUT);

  tempUK = (hB << 8) | lB;
  if(tempUK & (1 << 16)) {
    Serial.print("Error !");
    Serial.println(tempK);
  } 
  else {
    tempK = ((float)tempUK * 2) / 100 ;
    Serial.print("Temp UK: ");
    Serial.print(tempUK);
    Serial.print(" C: ");
    Serial.println(tempK - 273.15);
  }
  Serial.print(hB, HEX);
  Serial.print(" : ");
  Serial.print(lB, HEX);
  Serial.print(" : ");
  Serial.println(pec, HEX);
  
  delay(2000);
}

uint8_t readByte() {
  //TWI_WaitByteReceived(pTwi, RECV_TIMEOUT);
  while (!TWI_ByteReceived(pTwi))
    ;
  return TWI_ReadByte(pTwi);
}

static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout) {
  while (!TWI_TransferComplete(_twi)) {
    if (TWI_FailedAcknowledge(_twi))
      return false;
    if (--_timeout == 0)
      return false;
  }
  return true;
}

static inline bool TWI_WaitByteReceived(Twi *_twi, uint32_t _timeout) {
  while (!TWI_ByteReceived(_twi)) {
    if (TWI_FailedAcknowledge(_twi))
      return false;
    if (--_timeout == 0)
      return false;
  }
  return true;
}

static inline bool TWI_FailedAcknowledge(Twi *pTwi) {
  return pTwi->TWI_SR & TWI_SR_NACK;
}type or paste code here

Ce code là fonctionne très bien en positionnant le capteur sur sda et scl classique mais ayant deux capteurs à la même adresse je voulais utiliser la "wire1"

et donc j'ai appelé Wire1_Init(); au lieu de Wire_Init() au niveau du setup; voir code modifié ci- dessous

#include <Arduino.h>
#include <include/twi.h>

#define ADDR      0x5A

//EEPROM 32x16
#define TO_MAX    0x00
#define TO_MIN    0x01
#define PWM_CTRL  0x02

//RAM 32x16
#define RAW_IR_1  0x04
#define RAW_IR_2  0x05
#define TA        0x06
#define TOBJ_1    0x07
#define TOBJ_2    0x08

#define SYNC_PIN  2

static const uint32_t TWI_CLOCK = 100000;
static const uint32_t RECV_TIMEOUT = 100000;
static const uint32_t XMIT_TIMEOUT = 100000;

Twi *pTwi = WIRE_INTERFACE;

static void Wire_Init(void) {
  pmc_enable_periph_clk(WIRE_INTERFACE_ID);
  PIO_Configure(
  g_APinDescription[PIN_WIRE_SDA].pPort,
  g_APinDescription[PIN_WIRE_SDA].ulPinType,
  g_APinDescription[PIN_WIRE_SDA].ulPin,
  g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration);
  PIO_Configure(
  g_APinDescription[PIN_WIRE_SCL].pPort,
  g_APinDescription[PIN_WIRE_SCL].ulPinType,
  g_APinDescription[PIN_WIRE_SCL].ulPin,
  g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);

  NVIC_DisableIRQ(TWI1_IRQn);
  NVIC_ClearPendingIRQ(TWI1_IRQn);
  NVIC_SetPriority(TWI1_IRQn, 0);
  NVIC_EnableIRQ(TWI1_IRQn);
}

static void Wire1_Init(void) {
  	pmc_enable_periph_clk(WIRE1_INTERFACE_ID);
	PIO_Configure(
			g_APinDescription[PIN_WIRE1_SDA].pPort,
			g_APinDescription[PIN_WIRE1_SDA].ulPinType,
			g_APinDescription[PIN_WIRE1_SDA].ulPin,
			g_APinDescription[PIN_WIRE1_SDA].ulPinConfiguration);
	PIO_Configure(
			g_APinDescription[PIN_WIRE1_SCL].pPort,
			g_APinDescription[PIN_WIRE1_SCL].ulPinType,
			g_APinDescription[PIN_WIRE1_SCL].ulPin,
			g_APinDescription[PIN_WIRE1_SCL].ulPinConfiguration);

	NVIC_DisableIRQ(TWI0_IRQn);
	NVIC_ClearPendingIRQ(TWI0_IRQn);
	NVIC_SetPriority(TWI0_IRQn, 0);
	NVIC_EnableIRQ(TWI0_IRQn);
}*/

void setup() {
  Serial.begin(9600);

  pinMode(SYNC_PIN, OUTPUT);
  digitalWrite(SYNC_PIN, LOW);

  Wire1_Init();
  // Disable PDC channel
  pTwi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
  TWI_ConfigureMaster(pTwi, TWI_CLOCK, VARIANT_MCK);
}

void loop() {
  uint16_t tempUK;
  float tempK;
  uint8_t hB, lB, pec;

  digitalWrite(SYNC_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(SYNC_PIN, LOW);

  TWI_StartRead(pTwi, ADDR, TOBJ_1, 1);

  lB = readByte();
  hB = readByte();
  
  //last read
  TWI_SendSTOPCondition(pTwi);
  pec = readByte();
  
  while (!TWI_TransferComplete(pTwi)) 
    ;
  //TWI_WaitTransferComplete(pTwi, RECV_TIMEOUT);

  tempUK = (hB << 8) | lB;
  if(tempUK & (1 << 16)) {
    Serial.print("Error !");
    Serial.println(tempK);
  } 
  else {
    tempK = ((float)tempUK * 2) / 100 ;
    Serial.print("Temp UK: ");
    Serial.print(tempUK);
    Serial.print(" C: ");
    Serial.println(tempK - 273.15);
  }
  Serial.print(hB, HEX);
  Serial.print(" : ");
  Serial.print(lB, HEX);
  Serial.print(" : ");
  Serial.println(pec, HEX);
  
  delay(2000);
}

uint8_t readByte() {
  //TWI_WaitByteReceived(pTwi, RECV_TIMEOUT);
  while (!TWI_ByteReceived(pTwi))
    ;
  return TWI_ReadByte(pTwi);
}

static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout) {
  while (!TWI_TransferComplete(_twi)) {
    if (TWI_FailedAcknowledge(_twi))
      return false;
    if (--_timeout == 0)
      return false;
  }
  return true;
}

static inline bool TWI_WaitByteReceived(Twi *_twi, uint32_t _timeout) {
  while (!TWI_ByteReceived(_twi)) {
    if (TWI_FailedAcknowledge(_twi))
      return false;
    if (--_timeout == 0)
      return false;
  }
  return true;
}

static inline bool TWI_FailedAcknowledge(Twi *pTwi) {
  return pTwi->TWI_SR & TWI_SR_NACK;
}

et là plus rien mes deux bus sont à 3.3v et pas de trafic (contrôlé à l'oscilloscope, état haut maintenu).
la carte du capteur intègre les résistances pullups pour alimenter le bus .
Le problème ensuite aurait été de faire fonctionner les deux à la fois (comment individualiser les commandes?) . Mais je n'ai pas pu arriver à ce stade.

Voilà pour tout ce que je peux dire.

Par contre suite au post de Gonpezzi (j'aime bien ces montages simples), je ne comprends pas son fonctionnement électrique car la carte contennant ses pullups je ne pourrai jamais faire tomber la tension sur les lignes 8 et 9 des scl? Comment se sert-on de ce montage?
Encore merci de m'éclairer pour tout cela.

Bonjour,
Suite à vos conseils et ayant d'autres capteurs en I2C à rajouter en plus, j'ai interfacé un TCA9548A et modifié le programme de test qui fonctionne maintenant parfaitement avec les deux capteurs de la famille MLX90614 sur Arduino DUE et peut être aussi les AVR? (non contrôlé)
J'ai laissé les lignes originelles commentées pour laisser apparaître les modifications.
Merci, à tous

#include <Arduino.h>
#include <include/twi.h>
#include <Wire.h>
#define ADDR      0x5A
#define TCAADDR 0x70
//EEPROM 32x16
#define TO_MAX    0x00
#define TO_MIN    0x01
#define PWM_CTRL  0x02

//RAM 32x16
#define RAW_IR_1  0x04
#define RAW_IR_2  0x05
#define TA        0x06
#define TOBJ_1    0x07
#define TOBJ_2    0x08

#define SYNC_PIN  2
int resetTa9548a = 24;
void tcaselect(uint8_t i) {

  if (i > 7) return;

  Wire.beginTransmission(TCAADDR);// engagement TCA9548A
  Wire.write(1 << i);
  Wire.endTransmission();
}
static const uint32_t TWI_CLOCK = 100000;
static const uint32_t RECV_TIMEOUT = 100000;
static const uint32_t XMIT_TIMEOUT = 100000;

Twi *pTwi = WIRE_INTERFACE;

/*static void Wire_Init(void) {
  pmc_enable_periph_clk(WIRE_INTERFACE_ID);
  PIO_Configure(
    g_APinDescription[PIN_WIRE_SDA].pPort,
    g_APinDescription[PIN_WIRE_SDA].ulPinType,
    g_APinDescription[PIN_WIRE_SDA].ulPin,
    g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration);
  PIO_Configure(
    g_APinDescription[PIN_WIRE_SCL].pPort,
    g_APinDescription[PIN_WIRE_SCL].ulPinType,
    g_APinDescription[PIN_WIRE_SCL].ulPin,
    g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);

  NVIC_DisableIRQ(TWI1_IRQn);
  NVIC_ClearPendingIRQ(TWI1_IRQn);
  NVIC_SetPriority(TWI1_IRQn, 0);
  NVIC_EnableIRQ(TWI1_IRQn);
  }

  static void Wire1_Init(void) {
  	pmc_enable_periph_clk(WIRE1_INTERFACE_ID);
	PIO_Configure(
			g_APinDescription[PIN_WIRE1_SDA].pPort,
			g_APinDescription[PIN_WIRE1_SDA].ulPinType,
			g_APinDescription[PIN_WIRE1_SDA].ulPin,
			g_APinDescription[PIN_WIRE1_SDA].ulPinConfiguration);
	PIO_Configure(
			g_APinDescription[PIN_WIRE1_SCL].pPort,
			g_APinDescription[PIN_WIRE1_SCL].ulPinType,
			g_APinDescription[PIN_WIRE1_SCL].ulPin,
			g_APinDescription[PIN_WIRE1_SCL].ulPinConfiguration);

	NVIC_DisableIRQ(TWI0_IRQn);
	NVIC_ClearPendingIRQ(TWI0_IRQn);
	NVIC_SetPriority(TWI0_IRQn, 0);
	NVIC_EnableIRQ(TWI0_IRQn);
  }*/

void setup() {
  pinMode (resetTa9548a, OUTPUT);
  digitalWrite(resetTa9548a, LOW);
  delay(50);
  digitalWrite(resetTa9548a, HIGH);
  delay(50);  // waits for a second
  Serial.begin(9600);
  Wire.begin(0x70);
  pinMode(SYNC_PIN, OUTPUT);
  digitalWrite(SYNC_PIN, LOW);

  // Wire_Init();
  // Disable PDC channel capteur 0
  tcaselect(0); // sortie 0 du TCA9548A
  pTwi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
  TWI_ConfigureMaster(pTwi, TWI_CLOCK, VARIANT_MCK);
  // Disable PDC channel capteur 1
  tcaselect(1); // sortie 1 du TCA9548A 
  pTwi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
  TWI_ConfigureMaster(pTwi, TWI_CLOCK, VARIANT_MCK);
}

void loop() {
  uint16_t tempUK;
  float tempK;
  uint8_t hB, lB, pec;
  uint16_t tempUK1;
  float tempK1;
  tcaselect(0);
  digitalWrite(SYNC_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(SYNC_PIN, LOW);

  TWI_StartRead(pTwi, ADDR, TOBJ_1, 1);

  lB = readByte();
  hB = readByte();

  //last read
  TWI_SendSTOPCondition(pTwi);
  pec = readByte();

  while (!TWI_TransferComplete(pTwi))
    ;
  //TWI_WaitTransferComplete(pTwi, RECV_TIMEOUT);

  tempUK = (hB << 8) | lB;
  if (tempUK & (1 << 16)) {
    Serial.print("Error !");
    Serial.println(tempK);
  }
  else {
    tempK = ((float)tempUK * 2) / 100 ;
    Serial.print("Temp UK: ");
    Serial.print(tempUK);
    Serial.print(" C: ");
    Serial.println(tempK - 273.15);
  }
  Serial.print(hB, HEX);
  Serial.print(" : ");
  Serial.print(lB, HEX);
  Serial.print(" : ");
  Serial.println(pec, HEX);

  delay(1000);

  tcaselect(1);
  digitalWrite(SYNC_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(SYNC_PIN, LOW);

  TWI_StartRead(pTwi, ADDR, TOBJ_1, 1);

  lB = readByte();
  hB = readByte();

  //last read
  TWI_SendSTOPCondition(pTwi);
  pec = readByte();

  while (!TWI_TransferComplete(pTwi))
    ;
  //TWI_WaitTransferComplete(pTwi, RECV_TIMEOUT);

  tempUK = (hB << 8) | lB;
  if (tempUK & (1 << 16)) {
    Serial.print("Error !");
    Serial.println(tempK);
  }
  else {
    tempK = ((float)tempUK * 2) / 100 ;
    Serial.print("Temp UK1: ");
    Serial.print(tempUK);
    Serial.print(" C1: ");
    Serial.println(tempK - 273.15);
  }
  Serial.print(hB, HEX);
  Serial.print(" : ");
  Serial.print(lB, HEX);
  Serial.print(" : ");
  Serial.println(pec, HEX);

  delay(1000);
}

uint8_t readByte() {
  //TWI_WaitByteReceived(pTwi, RECV_TIMEOUT);
  while (!TWI_ByteReceived(pTwi))
    ;
  return TWI_ReadByte(pTwi);
}

static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout) {
  while (!TWI_TransferComplete(_twi)) {
    if (TWI_FailedAcknowledge(_twi))
      return false;
    if (--_timeout == 0)
      return false;
  }
  return true;
}

static inline bool TWI_WaitByteReceived(Twi *_twi, uint32_t _timeout) {
  while (!TWI_ByteReceived(_twi)) {
    if (TWI_FailedAcknowledge(_twi))
      return false;
    if (--_timeout == 0)
      return false;
  }
  return true;
}

static inline bool TWI_FailedAcknowledge(Twi *pTwi) {
  return pTwi->TWI_SR & TWI_SR_NACK;
}

L'écriture sur ces broches 0.1 ou 1.0 bloque le signal d'horloge pour l'un ou l'autre périphérique I2C, en activant un seul d'entre eux. juste l'un d'entre eux.

Bonsoir,
merci pour tout je vais me stocker cette solution pour un autre projet.
Merci