TCA9548A Multiplexer - Arduino Mega

TCA9548A multiplexer with Arduino

This is the first time I have setup a MUX system. And I need your help to identify where I am going wrong! Bear with me.

A non-responsive system, I get as far as a serial output up to the first sensor initiation ā€“ which fails.

I have individually and successfully tested all sensory systems are working - separate from the MUX setup.

Attached is a GitHub package of the project scope including electrical schematics, Arduino code and pictures.

I have located the addresses of all the devices using the scanner code successfully. As you can see I have connected 3 peripheral devices ā€“ clock, compass and pressure sensor. Along with a SD card reader using SPI communications.

I am using 10k pull up resistors to 5v bus line for both the SDA and SCL channels, with Arduino uno.

I have added the code into one repo as there is 400 lines there.

this is a smaller test system of what is to come. I plan to incorporate many more I2c devices through one arduino MEGA.

your help is much appreciated!

// clock channel identify 0x68
// The I2C address of the DS3231 is 0x68. 0x57 is the I2C address of the AT24C32 EEPROM on your ZS-042 RTC module

// Following is for an I2C Multiplexor system.
// Arduino to LCA9548A
// 10k PULL UP RESISTORS

// Using 3 Peripheral I2C channels: 1,2,3
// NOTE: SD writing - working, BMP serial monitor displaying - working, magnetic compass function serial - working 
// INDIVIUDALLY TESTED AND WORKING PERIPHERALS
//

// MAGNETIC COMPASS
//
// magnetic compass x,y,z recordings serial print out + write to SD card
// SD_BMP180_Clock_QMC5583L

#include <SFE_BMP180.h>
#include <Wire.h>
#include <Regexp.h>
SFE_BMP180 pressure;
#define ALTITUDE 10 // Altitude of (Ajax, Ontario, Canada)

////////////////////

#include <Wire.h>
#include <RTC.h>

DS3231 RTC;
#include <SD.h>
#include <SPI.h>
File myFile;

int pinCS = 53; // chip select 53 mega >> 10 uno
int modeState = 1;

// to merge to main code below 

String liveDirectory;
String fileNameAllocation = "dive0.txt";
int logNum = 0;
String logDirectory = "/LOG#1";
int diveNumber = 0;
int logCount = 0;
String liveFullDirectory = "";

#include <QMC5883LCompass.h>
QMC5883LCompass compass;
//#define TotalSamples 100;

/////////////////////////////////////////////////////////////////////  FUNCTION CALLS

void BMP180_check();
void compassCheck();
void writeSD_compassData(int x,int y,int z,String t);
void writeSD_BMP(float temp,float absolutePressure,float computedAltitude);
void logProfileNameAllocation(); // can be created at the initiation of dive arming
void tcaSelect(uint8_t bus);
void instantiateLogNum();
void logProfileNameAllocation();

////////////////////////////////////////////////////////////////////
       
//  TCA9548 module I2C address:  0x70 >> MULTIPLEXOR

//  I2C device found at address 0x0D  ! QMC5883L >> COMPASS
//  I2C device found at address 0x68  ! DS3231 RTC >> CLOCK LINE 
//  I2C device found at address 0x77  ! BMP180 >> PRESSURE BAROMETER

#define addr1   0x68       // CLOCK device 3 (I2C bus 2)
#define addr2   0x0D       // QMC device 1 (I2C bus 1)
#define addr3   0x77       // BMP device 4 (I2C bus 2)
#define TCA_Address 0x70   // address of I2C switch module

#define bus1    1          // CLOCK
#define bus2    2          // COMPASS
#define bus3    3          // BMP



void setup() {

////////////////////////////////////////////////////////////////////////// CLOCK ACTIVATE
  
  
  pinMode (pinCS,OUTPUT); // cs pin needs to be in a low setting for SPI communication to work correctly
  digitalWrite(pinCS, HIGH);
  
  Serial.begin(38400); // serial boot
  delay(200);
  Wire.begin(); // initialize i2c comms
  delay(500);

  Serial.println("PROGRAM LIVE");
  Serial.println("***************************************");
  Serial.println();
  
  tcaSelect(bus1);               // enable I2C channel 1
  
  delay(100);
  
  if (RTC.begin()){
    Serial.println("RTC init success");
  }
  else
  {
    // Oops, something went wrong, this is usually a connection problem,
    // see the comments at the top of this sketch for the proper connections.

    Serial.println("RTC init fail\n\n");
    while(1); // Pause forever.
  }
 
//////////////////////////////////////////////////////////////////////////// COMPASS ACTIVATE

tcaSelect(bus2);               // enable I2C channel 2
    
compass.init();

Serial.println("Compass init success");

//  }
//  else
//  {
//    // Oops, something went wrong, this is usually a connection problem,
//    // see the comments at the top of this sketch for the proper connections.
//
//    Serial.println("Compass init fail\n\n");
//    while(1); // Pause forever.
//  }

  
  compass.setCalibration(-428, 656, -2357, 0, 0, 980);
  Serial.println("Compass Calibration Set");
 

//////////////////////////////////////////////////////////////////////////// BMP180 ACTIVATE - TEMP/PRESSURE

  tcaSelect(bus3);               // enable I2C channel 3
  
  if (pressure.begin()){
    Serial.println("BMP180 init success");
  }
  else
  {
    // Oops, something went wrong, this is usually a connection problem,
    // see the comments at the top of this sketch for the proper connections.

    Serial.println("BMP180 init fail\n\n");
    while(1); // Pause forever.
  }
 
  
//////////////////////////////////////////////////////////////////////////// SPI COMMS, SD INITIALIZE, CS

  
  // pinMode(pinCS, OUTPUT);
  
  // SD Card Initialization
  if (SD.begin())
  {
    Serial.println("SD card is ready to use.");
  } else
  {
    Serial.println("SD card initialization failed");
    return;
  }

////////////////////////////////////////////////// BOOT UP FUNCTIONS - SINGULAR

instantiateLogNum();

logProfileNameAllocation(); // call to scan card if file exists otherwise break and write file as the default setting dive0

} // END OF SETUP ***************************************************************************************************************************************************************

          void loop() {                          
                         
                          compassCheck();
                        
                          BMP180_check();
                        
                          delay(2000); 
          
          }


void instantiateLogNum(){

                        // created only at bootup, as root directory.
                        // traverse log folder directory until the latest can be appended

                       
                        while(SD.exists(logDirectory)) // check if folder structure/ directory exists log
                        {
                                                   
                         logCount++;
                         String logCountCastToString = String(logCount);
                         String logPrefix = "/LOG#";
                         logDirectory = logPrefix + logCountCastToString;
                         Serial.print(logDirectory);
                         
                        }                     
                        
                        SD.mkdir(logDirectory);
                        Serial.println("New Folder Log made: ");
                        Serial.print(logDirectory);
                        Serial.println();


  
}


// running program should save data to live set profile, LOG#1/dive1/dive0.txt >> dive1.txt >> dive2.txt

void logProfileNameAllocation(){ // using SPI comms - command should be called as the dive is armed moments before break-away.
                          
                       // see if the directory exists, create it if not. construct from the live dive profile denotation

                        String mScast = String(modeState); // convert into string
                        
                        liveDirectory = logDirectory + "/diveProfile" + mScast; // >> /LOG#1/diveProfile1
                                                  
                        if(!SD.exists(liveDirectory)) // check if folder structure/ directory exists
                        {
                          if(SD.mkdir(liveDirectory)){
                            Serial.print("Directory Created: ");
                            Serial.print(liveDirectory);                      
                          }
                        }
                        else {
                                      ("path already exists, directory not created");
                                  }                        

                                 liveFullDirectory = liveDirectory  + "/" + fileNameAllocation; // >> /LOG#1/diveProfile1/dive0.txt
                                 
                                 while(SD.exists(liveFullDirectory))  {

                                        diveNumber ++;        
                                        String diveNumberString = String(diveNumber);
                                        fileNameAllocation = "dive" + diveNumberString + ".txt"; // concatanate an iteration one up from the existing global scoped variable/existing file

                                        liveFullDirectory = liveDirectory + "/" + fileNameAllocation;
                                        
                                        }                   
                                                                              
                                                                                                                   
                                                          myFile = SD.open(liveFullDirectory, FILE_WRITE);

                                                                              if (myFile) { 

                                                                                myFile.print("\n\n"); 
                                                                                myFile.print("New File: ");
                                                                                myFile.print(fileNameAllocation);    
                                                                                Serial.println();
                                                                                Serial.println("***************************************");
                                                                                Serial.println("Success >> New Log text printed");                                    
                                                                                myFile.close(); // close the file
                                                                                
                                                                                                              }       
                                                                                                              else {
                                                                                                                            Serial.println("error opening SD file");
                                                                                                                          }
                                                                                                                                   
}



// **********************************************************************************************************************************************************************





void tcaSelect(uint8_t bus) {
  
              if (bus > 7) return;
              Wire.beginTransmission(TCA_Address);
              Wire.write(1 << bus);
              Wire.endTransmission();
              Serial.print("bus live: ");
              Serial.print(bus);
              Serial.println();
  
}


void compassCheck(){

           tcaSelect(bus1);               // enable I2C channel 1, busline switch to clockline function call
                                                                                        
                                                          String absoluteTime = ""; 
                                                         
                                                          absoluteTime = absoluteTime + RTC.getHours() + ":" +  RTC.getMinutes()+ ":" + RTC.getSeconds();
                                                          Serial.println();
                                                          Serial.print("Time: ");
                                                          Serial.println(absoluteTime);

            tcaSelect(bus2);               // enable I2C channel 2
                    
            int compass_x, compass_y, compass_z, a, b;
            char myArray[3];
          
            compass.read();
            Serial.println();
            compass_x = compass.getX();
            compass_y = compass.getY();
            compass_z = compass.getZ();
            Serial.print("X: ");
            Serial.print(",");
            Serial.print(compass_x);
            Serial.print(",");
            Serial.print(" Y: ");
            Serial.print(compass_y);
            Serial.print(",");
            Serial.print(" Z: ");
            Serial.print(compass_z);
            
            a = compass.getAzimuth();
            b = compass.getBearing(a);
            compass.getDirection(myArray, a);
            
                                                Serial.println();
                                                Serial.print("Azimuth: ");
                                                Serial.print(a);
                                                Serial.println();
                                                Serial.print("Bearing: ");
                                                Serial.print(b);
                                                Serial.println();
                                                Serial.print("Direction: ");
                                                Serial.print(myArray[0]);
                                                Serial.print(myArray[1]);
                                                Serial.print(myArray[2]);                                              
                                                Serial.println();

                                                /////////////////////////////////////////

                                                writeSD_compassData(compass_x,compass_y,compass_z,absoluteTime);
                                                                                        
}
  
  
void writeSD_compassData(int x,int y,int z,String t){

                                    Serial.print("write to directory: ");
                                    Serial.print(liveFullDirectory);
                                    Serial.println();
                                    myFile = SD.open(fileNameAllocation, FILE_WRITE);
                                    if (myFile) {  
                                      
                                                    myFile.println();
                                                    myFile.print("X: ");
                                                    myFile.print(",");
                                                    myFile.print(x);
                                                    myFile.print(",");
                                                    myFile.print(" Y: ");
                                                    myFile.print(",");
                                                    myFile.print(y);
                                                    myFile.print(" Z: ");
                                                    myFile.print(",");
                                                    myFile.print(z);
                                                    myFile.println();
                                                    myFile.print("Time: ");
                                                    myFile.print(t);
                                                    Serial.println();
                                                    
                                                    Serial.println("Success >> Compass data write to card");
                                                    
                                    myFile.close(); // close the file
                                                    }
                                                                                                              // if the file didn't open, print an error:
                                                                            else {
                                                                              Serial.println("error opening SD file");
                                                                            }


}






void BMP180_check(){

  tcaSelect(bus3);               // enable I2C channel 3
 

  char status;
  double T,P,p0,J;

  // Loop here getting pressure readings every 10 seconds.

  // If you want sea-level-compensated pressure, as used in weather reports,
  // you will need to know the altitude at which your measurements are taken.
  // We're using a constant called ALTITUDE in this sketch:
  
  Serial.println();
  Serial.print("provided altitude: ");
  Serial.print(ALTITUDE,0);
  Serial.print(" meters, ");
  Serial.print(ALTITUDE*3.28084,0);
  Serial.println(" feet");
  
  // If you want to measure altitude, and not pressure, you will instead need
  // to provide a known baseline pressure. This is shown at the end of the sketch.

  // You must first get a temperature measurement to perform a pressure reading.
  
  // Start a temperature measurement:
  // If request is successful, the number of ms to wait is returned.
  // If request is unsuccessful, 0 is returned.

  status = pressure.startTemperature();
  if (status != 0)
  {
    // Wait for the measurement to complete:
    delay(status);

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Function returns 1 if successful, 0 if failure.

    status = pressure.getTemperature(T);
    if (status != 0)
    {
      // Print out the measurement:
      Serial.print("temperature: ");
      Serial.print(T,2);
      Serial.print(" deg C, ");
      Serial.print((9.0/5.0)*T+32.0,2);
      Serial.println(" deg F");
      
      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      status = pressure.startPressure(3);
      if (status != 0)
      {
        // Wait for the measurement to complete:
        delay(status);

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        status = pressure.getPressure(P,T);
        if (status != 0)
        {
          // Print out the measurement:
          Serial.print("absolute pressure: ");
          Serial.print(P,2);
          Serial.print(" mb, ");
          Serial.print(P*0.0295333727,2);
          Serial.println(" inHg");

          // The pressure sensor returns abolute pressure, which varies with altitude.
          // To remove the effects of altitude, use the sealevel function and your current altitude.
          // This number is commonly used in weather reports.
          
          // Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
          // Result: p0 = sea-level compensated pressure in mb

          p0 = pressure.sealevel(P,ALTITUDE); // we're at 90 meters (Boulder, CO)
          Serial.print("relative (sea-level) pressure: ");
          Serial.print(p0,2);
          Serial.print(" mb, ");
          Serial.print(p0*0.0295333727,2);
          Serial.println(" inHg");

          // On the other hand, if you want to determine your altitude from the pressure reading,
          // use the altitude function along with a baseline pressure (sea-level or other).
          
          // Parameters: P = absolute pressure in mb, p0 = baseline pressure in mb.
          // Result: a = altitude in m.

          J = pressure.altitude(P,p0);
          Serial.print("computed altitude: ");
          Serial.print(J,0);
          Serial.print(" meters, ");
          Serial.print(J*3.28084,0);
          Serial.println(" feet");
        }
        else Serial.println("error retrieving pressure measurement\n");
      }
      else Serial.println("error starting pressure measurement\n");
    }
    else Serial.println("error retrieving temperature measurement\n");
  }
  else Serial.println("error starting temperature measurement\n");

writeSD_BMP(T,P,J);

}


void writeSD_BMP(float temp,float absolutePressure,float computedAltitude){

                                    myFile = SD.open(fileNameAllocation, FILE_WRITE);
                                    if (myFile) {  
                                      
                                                    myFile.println();
                                                    myFile.print("Temperature: ");
                                                    myFile.print(temp);
                                                    myFile.print(",");
                                                    myFile.print(" Absolute Pressure: ");
                                                    myFile.print(absolutePressure);
                                                    myFile.print(",");
                                                    myFile.print(" Computed Altitude: ");
                                                    myFile.print(computedAltitude);
                                                    Serial.println();
                                                    
                                                    Serial.println("Success >> BMP data write to card");
                                                    
                                    myFile.close(); // close the file
                                                    }
                                                                                                              // if the file didn't open, print an error:
                                                                            else {
                                                                              Serial.println("error opening SD file");
                                                                            }
  
}

Sketch uses 26640 bytes (10%) of program storage space. Maximum is 253952 bytes.
Global variables use 2292 bytes (27%) of dynamic memory, leaving 5900 bytes for local variables. Maximum is 8192 bytes.

Please post your code (please don't forget to use code tags) and image of the schematic here; it's a lot easier, Especially as you seem to have forgotten to post the link to the github page :wink:

Your topic has been moved to a more suitable location on the forum. Installation and Troubleshooting is not for problems with (nor for advise on) your project :wink: See About the Installation & Troubleshooting category.

The code You added is invisable.... And, post the entire code.

Apparently not. :roll_eyes:

added :slight_smile:

thank you!

added :slight_smile:

Before we continue, I want to ask why you are using the iĀ²c multiplexer in this circuit. It does not appear to be necessary.

As long as each iĀ²c device has a different address, you won't need a multiplexer.

1 Like

You can post as much code as you want on this forum, I don't think there is a limit. You must of course use code tags. You can also post schematics, error messages and output from serial monitor, as text, between code tags.

Often forum members do not like to click links to see code, schematics etc. These links could contain ads, pornography, who knows. In this case you used GitHub, which we do trust, I think, but still there is no need to do that, everything can be posted here in the forum.

One reason to attach a link like this is so that you can ask for help on 2 or more forums and post the same link on each forum. This would be against forum rules, it's called "cross-posting" and wastes the time and effort of those who would be willing to help you, even if those people are on completely different forums.

You should not post screenshots of serial monitor output, error messages, code, or anything else that is simply text. Copy the text and paste it into your forum post between code tags. Certainly do not post high megapixel camera photos of the serial monitor on your laptop screen so that every LCD pixel can be clearly seen! You will have used about a million times more data to communicate the text than was necessary!

I have 3x i2c devices.. but please understand I am expanding a lot more! as in probably too 20x i2c devices. I just want to get a small multiplexer system working first. not sure why this is not working.. I am guessing its something in my code methodology I am doing incorrectly that I cannot spot.

okay, understand thanks for the tips.. this is the my first post ever.. so i thought i would bundle everything.. and put it in as much information as possible.. I am certainty not cross posting. ive injected the code now directly to the thread.. though im not sure what the system is doing to it.. format seems weird.. anyway hope you can find my problem. cheers.

The libraries (BMP180, RTC, QMC) use the Wire library functions, you don't have to call 'Wire.beginTransmission()' and 'Wire.endTransmission()'. Remove all of them, but keep them in the function 'tcaSelect()' because that function uses the Wire library functions.

Did you know that the 'Wire.beginTransmission()' does not start something on the I2C bus and it does not claim the I2C bus. The 'Wire.endTransmission()' may not be used to end or stop something. The 'Wire.write()' does not write data to the I2C bus.

A define for "bus1" which is "1" is not really adding more information.

void setup()
{
  Wire.begin();      // initialize the Arduino I2C bus

  tcaSelect(busRTC);   // select the (sub) I2C bus for the RTC
  RTC.begin();

  tcaSelect(busCompass);  // select the (sub) I2C bus for the compass
  compass.init();

  tcaSelect(busPressure);
  pressure.begin();
  ...


void compassCheck()
{
  tcaSelect(busCompass);
  compass.read();
  ...

Do you know how weak the I2C bus is ? It is not designed to go into a cable. The pullup may not be too little and not too much. The Wire library is blocking, it waits until the I2C sesssion has finished. That means everything slows down a lot with many I2C devices.

Understand but choosing to ignore?

...because you didn't use code tags.

excuse my lack of knowledge but what are code tags?

You need to read the instructions. :roll_eyes:

I thought you used wire.beginTransmission(address) to basically open up the route line to that I2c device? so if I delete them all how do i call them up or denote a chosen address.

from all the youtube videos Ive watched they specify the exact address of the device ... followed by all the code/instructions they want to send to it.. then move onto the next device..to repeat the process

how can i do this? @Koepel

or am i missing something here.. like that i dont need to call up the address.. just the busline is good enough?

There are two things:

1
If you write your own code and read and write data from/to registers of a sensor, then you have to use the functions of the Wire library.
Write something: beginTransmission() ... write() ... endTransmission()
Read something: requestFrom() ... then maybe read() or available()

2
If you use a library, then the library does all of that. Sometimes a library uses a fixed I2C address, sometimes the I2C address can be set with a 'init' function, sometimes it is even possible to select the I2C bus in case there are multiple I2C buses.
There are no rules for that, you might have to read the source code of a library to see where they got the I2C address from.

Have you read the instructions ? We would like to see your new sketch between code tags.

Oh. I see several videos of OLED displays using multiplexer systems, makes sense if they are the same product using the same address. I believe my sensors are all different addresses.. hmm

though is there not extra advantages with using a MUX system? especially in a circuit that is going to expand with many more I2C devices? will it help with efficiency of the overall system?

and If I do eliminate the MUX would the system wiring connect similarly? all slave i2c's connecting into the SCL SDA bus lines of the arduino? and in terms of coding? I would not need to address any specific code to the slaves, apart from including the wire library? cheers

There is no simple answer.

With many I2C devices, they all must have a different I2C address, that is not always possible. Some sensor have the same I2C address, but luckily some sensor have extra pins to set a different I2C address.

A 5V I2C bus is not the same as a 3.3V I2C bus. There are I2C level shifters, but they also weaken the bus.

The I2C bus is not supposed to go into a cable or wires. If everything is wrong then a cable of 20cm is the maximum length. If everything is just right, then 2 meters (or more) is possible.
Those 2 meters is the total length. Suppose you have ten I2C devices, then each device can have wires of 20cm, no more.

Your three sensor might already be the maximum.

Things are better with a multiplexer. By turning off the majority of sensors and wires, only one (sub) I2C is active and the rest has no influence on the I2C bus.

Thank you for that, new code has been added. system is now working! :slight_smile: I played with the MUX master address with A0,A1,A2 pins also working well.

Makes sense on the wire length front, this is a small sub unit for an underwater drone of length 1m so these wires are spanning internally from place to place with the I2C. I will keep the MUX in place.

something I am still trying to understand is: if the master MUX address is called up and the bus line is switched.. why is there a need to specify the address of the peripheral sensor/slave if it is different from its neighbors address on the same bus line.

as seen in the you tube clip below on bus line 7.

a function sendbyte specifies the exact address of the slave, is this neccessary in my case if i were to expand multiple slaves along one particular bus line?

or is the example above just the nature of the GPIO expanders