Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: unexpected behavior multiplexing 1602 LCD backpacks with a PCA9546 multiplexer (Adafruit) #46

Open
shaunhurley opened this issue Sep 20, 2024 · 2 comments

Comments

@shaunhurley
Copy link

Been playing and have run into something that may be an expected behavior, or possibly an issue. I'm trying to multiplex two 1602 I2C backpacked LCDs through an Adafruit PCA9546 (4 Channel) multiplexor.

Both of the LCD's work perfectly when used individually directly from the Arduino. Both LCD's work perfectly when used individually through the multiplexer, on any of the four channels i.e. the channel select and communication is working.

Both LCD's are detected on separate channels (simple I2C scan) when connected concurrently.

When I try to initialize both LCD while connected concurrently, the second LCD to be addressed always returns a -4 error code from the begin() call. It doesn't matter which two multiplexor channels are used, nor which order the channels are selected / devices are initialized, it's always the second device to be initialized that fails.

Appreciate any thoughts....

#include "Wire.h"
#include <hd44780.h>           // Main hd44780 library
#include <hd44780ioClass/hd44780_I2Cexp.h>  // I2C expander i/o class

#define PCAADDR 0x70

// create two instances of the LCD
hd44780_I2Cexp lcd1;
hd44780_I2Cexp lcd2;

// Function to reinitialize the I2C bus after switching channels
void reinitI2CBus() {
  Wire.end();
  delay(50);  // Short delay
  Wire.begin();
}

void pcaselect(uint8_t i) {
  if (i > 3) return;
 
  Wire.beginTransmission(PCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();
}


void setup()
{
  while (!Serial);
  delay(1000);

  Serial.begin(115200);
  Serial.println("Multi-LCD Test!");

  Wire.begin();
 
  for (uint8_t t=0; t<4; t++) {
    pcaselect(t);
    Serial.print("PCA Port #"); Serial.println(t);

    for (uint8_t addr = 0; addr<=127; addr++) {
      if (addr == PCAADDR) continue;

      Wire.beginTransmission(addr);
      if (!Wire.endTransmission()) {
        Serial.print("Found I2C 0x");  Serial.println(addr,HEX);
      }
    }
  }


  Serial.println("Initializing LCDs...");

  // define the port on the PCA9546 for the first sensor
  pcaselect(1);
  reinitI2CBus();

  // Initialize the LCD and check for errors
  int status1 = lcd1.begin(16, 2);  // 16 columns, 2 rows
  if (status1) {
    // Print an error if initialization fails
    Serial.print("LCD1 initialization failed, status: ");
    Serial.println(status1);
    while (1);  // Stop the program if initialization fails
  }
  Serial.println("LCD1 initialization succeeded!");

  lcd1.setCursor(0,0);
  lcd1.print("Display 1");
  lcd1.setCursor(0,1);
  lcd1.print("Testing");


  // define the port on the PCA9546 for the 2nd sensor
  pcaselect(2);
  reinitI2CBus();

  // Initialize the LCD and check for errors
  int status2 = lcd2.begin(16, 2);  // 16 columns, 2 rows
  if (status2) {
    // Print an error if initialization fails
    Serial.print("LCD2 initialization failed, status: ");
    Serial.println(status2);
    while (1);  // Stop the program if initialization fails
  }
  Serial.println("LCD2 initialization succeeded!");

  lcd2.setCursor(0,0);
  lcd2.print("Display 2");
  lcd2.setCursor(0,1);
  lcd2.print("Testing");

  Serial.println("Finished!");
}

void loop() {
}

Output:

Multi-LCD Test!
PCA Port #0
PCA Port #1
Found I2C 0x27
PCA Port #2
Found I2C 0x27
PCA Port #3
Initializing LCDs...
LCD1 initialization succeeded!
LCD2 initialization failed, status: -4
@bperrybap
Copy link
Contributor

Well, this is an interesting issue.
Here is what is happening. The library never expected the i2c bus to swapped out underneath it with multiple lcd devices using the same i2c address on the multiple busses. This breaks the auto location capability.
When you use auto location, by not specifying an i2c address, the library will look for a free device for each object that wants its address to be automatically located.
The way it currently does this is it scans the bus looking for a PCF8574 for each lcd object.
In your case it finds 0x27 for lcd1, for lcd2 it looks for a second device on the bus but can't find one.
You could make it work by assigning the i2c address to each lcd object so that the auto location code is not used.
The library will accept your i2c address and the code currently does not check to see if there is a duplicate address.
Note: I had on my list of things for the library to do this check, but I'm going to think about it some more
to allow for a multi bus scenario with duplicate addresses like you are using.

If you are not aware how specify an i2c address, it is easy.
Just specify the address in the constructor:

// create two instances of the LCD
hd44780_I2Cexp lcd1(0x27);
hd44780_I2Cexp lcd2(0x27);

@shaunhurley
Copy link
Author

Thanks for looking into it! Can't believe I missed the auto-addressing, all the other libraries I was testing with I was manually assigned the I2C address.

I wasn't able to get things working with the New-LiquidCrystal library, but I ultimately did get things functioning with Rob Tillart's I2C_LCD.

The LCD's I'm using aren't manually configurable with different addresses. I haven't played with shutting them down / trying a temporary assignment, going with the MUX was the lazy solution :)

I'll circle back and try out HD44780 again with the manual addresses, it has some additional functionality that I'd like to have access to :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants