1

I am trying to communicate with two i2c components using an arduino nano - some ADS1115s and some i2c flow sensors each connected to a different i2c multiplexer, but they seem to be conflicting.

Some of the ADS1115s are 0x49 addresses, the same as all the flow sensors. The full code works, including constructing and initialising the ads1115s, but if I then try to read from them also, I don't get almost any data from the flow sensors (they just read 0 or close to when they should be reading around 8192 for zero flowrate). Any help or suggestions much appreciated!

I've put the full code at the bottom as its quite long and I keep changing it anyway but the important parts are here:

ADS1115:

Adafruit_ADS1115();  //Construct an instance of an ADS1115 with the default address (0x48)
ads1115.begin();  // Initialize ads1115
int16_t readADC_Differential_0_1(void);  //Perform a differential analog to digital conversion on the voltage between channels 0 and 1.

tca9548a multiplexer:

#define TCAADDR 0x70

void tcaselect(uint8_t i) {
  if (i > 7) return;

  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

tcaselect(i);

Zephyr flow sensors:

void getflows (int sensor, float range, float flowreading) {
Wire.requestFrom(0x49, 2);
while (Wire.available())   // slave may send less than requested
  {
    int flowdata = Wire.read();    // receive a byte as character
    //Serial.println(flowdata);
    flowdata = flowdata << 8;
    flowdata |= Wire.read();    // receive a byte as character
    //Serial.println(flowdata);
    flowreading = range * ((flowdata / 16384.0) - 0.5) / 0.4;
    //Serial.print("sensor 3 flow = "); Serial.println(flow3);
    //Serial.println();         // print the character
    //Serial.println();
  }
  delay(20);

}

The full code:

#include <ModbusRtu.h>
#include <Wire.h>
#include <Adafruit_ADS1015.h>
#include <SPI.h>
#include <PID_v1.h>
#include <math.h>

//modbus
#define ID          1
#define TXEN        2

//valve TTL and DAC CS pins - nos correspond to valve driver numberings in rig box
//Current connections - O2high = 1 = A, CO2 = 2 = B, O2low = 3 = C
#define ATTL       5
#define CTTL       4
#define BTTL       3

#define BCS         7
#define CCS         8
#define ACS         9

#define tempfreq    5
#define tempnum     3
///////////////////////////////////////////
//Sensor ranges - USER DEFINED
float DACrange = 3500.0; //ml/min

int POutrange = 2000; //mbar abs
int PInrange = 2000; //mbar abs
///////////////////////////////////////////

//ADC multiplexer
#define TCAADDR      0x70
#define TCA2ADDR      0x77

//Input resistor values
float         T1R2  =  2320.0;
float         FPR1  =  3090.0;
float         FPR2  =  12010.0;

//max variation from setpoint before PID cycle
int maxdif    =  5;
//max variation from setpoint at end of PID cycle
int PIDdif    =  1;

///////////////////////////////////////////
//MATHS
//scale factor
long FPscale = 10000000;
long flowscale = 10000000;
//vrefs - with scale factors applied
float vref   = 2.5; //tscaled
float Tref   = 1.0; //tscaled
long FPref  = 20000000; //FPscaled
//divisions - integrated to sensor range in long format for calculations
float FPdiv = (FPR1 + FPR2) / FPR2;
int Vmax = 5;

int Poutfac = (int)((POutrange / Vmax) * FPdiv);
int Pinfac = (int)((PInrange / Vmax) * FPdiv);

//ADC mv/bit
float Tbit = 0.00003125;
long FPbit = 625; //- with scale factor of 10,000,000

//temp sensor steinhart constants
double sA = 0.001462146;
double sB = 0.0002393355;
double sC = 0.0000000963049;

//declare holding constants
int16_t hT1 = 0;
int16_t hT2 = 0;
int16_t hT3 = 0;
int16_t hT4 = 0;
int16_t hT5 = 0;
int16_t hT6 = 0;
int16_t hT7 = 0;
int16_t hT8 = 0;

int16_t hFP1 = 0;
int16_t hFP2 = 0;
int16_t hFP3 = 0;
int16_t hFP4 = 0;
int16_t hFP5 = 0;
int16_t hFP6 = 0;
int16_t hFP7 = 0;
int16_t hFP8 = 0;

//intermediates
int i = 0;
int j = 0;
int b[3];
int loopnum = tempfreq;
float tres = 0.0;
double treslog = 0.0;
long lconv = 0;
int16_t O2dif = 0;
int16_t CO2dif = 0;
int16_t N2dif = 0;
int16_t gasdif = 0;
int gasreading = 0;
int hFP = 0;
uint16_t val = 0;
bool truePID;
bool trueAPID = false;
bool trueBPID = false;
bool trueCPID = false;
int (*gasreadfunc)();

int flowdata = 0;
float flow3 = 0.0;
float flow2 = 0.0;
float flow4 = 0.0;
float flow5 = 0.0;
long reading = 0;

uint16_t Setpt;
int CS;
int TTL;

//initialise nested functions
//void eachPID(int16_t (*)(), int16_t, int, bool);

enum
{
  Error1,    //CHANNEL 0  //0
  Error2,    //CHANNEL 1
  FP2a,      //CHANNEL 2  //1
  FP2b,      //CHANNEL 3
  T1a,       //CHANNEL 4  //2
  T1b,       //CHANNEL 5
  T2a,       //CHANNEL 6  //3
  T2b,       //CHANNEL 7
  T3a,       //CHANNEL 8  //4
  T3b,       //CHANNEL 9
  T4a,       //CHANNEL 10 //5
  T4b,       //CHANNEL 11
  Flow2a,    //CHANNEL 12 //6
  Flow2b,    //CHANNEL 13
  Flow3a,    //CHANNEL 14 //7
  Flow3b,    //CHANNEL 15
  Flow4a,    //CHANNEL 16 //8
  Flow4b,    //CHANNEL 17
  Flow5a,    //CHANNEL 18 //9
  Flow5b,    //CHANNEL 19 
  A_set,     //CHANNEL 20
  B_set,     //CHANNEL 21
  C_set,     //CHANNEL 22
  HOLDING_REGS_SIZE
};

unsigned int holdingRegs[HOLDING_REGS_SIZE];

/**
    Modbus object declaration
    u8id : node id = 0 for master, = 1..247 for slave
    u8serno : serial port (use 0 for Serial)
    u8txenpin : 0 for RS-232 and USB-FTDI
                 or any pin number > 1 for RS-485
*/
Modbus slave(1, 0, TXEN); // this is slave @1 and RS-485

//Construct ADCs at addresses
Adafruit_ADS1115 adcOne(0x48);
Adafruit_ADS1115 adcTwo(0x49);
Adafruit_ADS1115 adcThree(0x4B);
Adafruit_ADS1115 adcFour(0x4A);

// SPI - set up the speed, data order and data mode
SPISettings settings(4000000, MSBFIRST, SPI_MODE0);

//PID
//Define Variables we'll be connecting to
double Output, Setpoint, Input;

//Define the aggressive and conservative Tuning Parameters
double aggKp = 4, aggKi = 0.2, aggKd = 1;
double consKp = 1, consKi = 0.05, consKd = 0.25;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, DIRECT);

///////////////////////////////////////////////////////////////////////////////////////////////
void tcaselect(uint8_t i, int address) {
  if (i > 7) return;

  Wire.beginTransmission(address);
  Wire.write(1 << i);
  Wire.endTransmission();
}

void setup() {
  ///////////////////////////////////////////////////////////////////////////////////////////////
  //DAC
  pinMode (BCS, OUTPUT);
  digitalWrite(BCS, HIGH);
  pinMode (CCS, OUTPUT);
  digitalWrite(CCS, HIGH);
  pinMode (ACS, OUTPUT);
  digitalWrite(ACS, HIGH);

  pinMode (ATTL, OUTPUT);
  digitalWrite(ATTL, LOW);
  pinMode (CTTL, OUTPUT);
  digitalWrite(CTTL, LOW);
  pinMode (BTTL, OUTPUT);
  digitalWrite(BTTL, LOW);
  ///////////////////////////////////////////////////////////////////////////////////////////////
  //ADC
  adcOne.begin();
  adcTwo.begin();
  adcThree.begin();
  adcFour.begin();

  // 2/3x gain +/- 6.144V  1 bit = 0.1875mV (default)
  // ads1015.setGain(GAIN_ONE);     // 1x gain   +/- 4.096V  1 bit = 0.125mV
  // ads1015.setGain(GAIN_TWO);     // 2x gain   +/- 2.048V  1 bit = 0.0625mV
  // ads1015.setGain(GAIN_FOUR);    // 4x gain   +/- 1.024V  1 bit = 0.03125mV
  // ads1015.setGain(GAIN_EIGHT);   // 8x gain   +/- 0.512V  1 bit = 0.015625mV
  // ads1015.setGain(GAIN_SIXTEEN); // 16x gain  +/- 0.256V  1 bit = 0.0078125mV

  //////////////////////////////////////////////////////////////////////////////////////////////
  //Flow sensor serial number readouts
  getflows(2, 750.0, flow2);
  getflows(3, 400.0, flow3);
  getflows(4, 50.0, flow4);
  getflows(5, 400.0, flow5);

  //////////////////////////////////////////////////////////////////////////////////////////////
  //PID
  //initialize the variables we're linked to
  Input = 0;
  Setpoint = 0;

  //Define the aggressive and conservative Tuning Parameters
  float aggKp = 4, aggKi = 0.2, aggKd = 1;
  float consKp = 1, consKi = 0.05, consKd = 0.25;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  //////////////////////////////////////////////////////////////////////////////////////////////
  // start modbus communication
  slave.begin(115200);

  // initialize SPI:
  SPI.begin();

  //Serial.begin(115200);
  gettemperatures();

  holdingRegs[Error2] = 0;
}

void loop() {
  //each tempfreq no of loops, it reads the temp sensors also
  loopnum++;
  if (loopnum > tempfreq) {
    gettemperatures();
    loopnum = 0;
  }

  holdingRegs[Error1] = 0;//set to 0 for normal operation, 65535 = PID control
  //Serial.print("Holding Regs Error1 "); Serial.print(holdingRegs[Error1]); Serial.print(", holdingRegs[Error2]"); Serial.println(holdingRegs[Error2]);

  //flow and pressure sensors
  tcaselect(0, TCAADDR);
  adcOne.setGain(GAIN_TWO);
  //adcTwo.setGain(GAIN_TWO);
  //adcThree.setGain(GAIN_TWO);
  //adcFour.setGain(GAIN_TWO);

  //hFP1 = adcOne.readADC_Differential_0_1();
  hFP2 = adcOne.readADC_Differential_2_3();
  //hFP3 = adcTwo.readADC_Differential_0_1();
  //hFP4 = adcTwo.readADC_Differential_2_3();
  //hFP5 = adcThree.readADC_Differential_0_1();
  //hFP6 = adcThree.readADC_Differential_2_3();
  //hFP7 = adcFour.readADC_Differential_0_1();
  //hFP8 = adcFour.readADC_Differential_2_3();

  //FPconv(hFP1, &holdingRegs[FP1a], &holdingRegs[FP1b], Pinfac);
  //Serial.print("hFP1: "); Serial.println(hFP1);
  FPconv(hFP2, &holdingRegs[FP2a], &holdingRegs[FP2b], Poutfac);
  //Serial.print("hFP2: "); Serial.println(hFP2);
  //FPconv(hFP3, &holdingRegs[FP3a], &holdingRegs[FP3b], CO2fac);
  //Serial.print("hFP3: "); Serial.println(hFP3);
  //FPconv(hFP4, &holdingRegs[FP4a], &holdingRegs[FP4b], O2fac);
  //Serial.print("hFP4: "); Serial.println(hFP4);
  //FPconv(hFP5, &holdingRegs[FP5a], &holdingRegs[FP5b], Finfac);
  //Serial.print("hFP5: "); Serial.println(hFP5);
  //FPconv(hFP6, &holdingRegs[FP6a], &holdingRegs[FP6b], N2fac);
  //Serial.print("hFP6: "); Serial.println(hFP6);
  //FPconv(hFP7, &holdingRegs[FP7a], &holdingRegs[FP7b], Finfac);
  //Serial.print("hFP7: "); Serial.println(hFP7);
  //FPconv(hFP8, &holdingRegs[FP8a], &holdingRegs[FP8b], Finfac);
  //Serial.print("hFP8: "); Serial.println(hFP8);
  //Serial.println("");

  getflows(2, 750.0, flow2);
  getflows(3, 400.0, flow3);
  getflows(4, 50.0, flow4);
  getflows(5, 400.0, flow5);

  flowconvert();

  //modbus communication
  slave.poll( holdingRegs, HOLDING_REGS_SIZE );

  checkgasflow(1);
  checkgasflow(2);
  checkgasflow(3);

  //delay(1000);
}

void gasread(int gastoread) {
  switch (gastoread) {
    case 1: //O2high
      getflows(3, 400.0, flow3);
      gasreading = flow2;
      Setpt = A_set;
      CS = ACS;
      TTL = ATTL;
      truePID = trueAPID;
      //return gasreading;
      break;
    case 2: //CO2
      getflows(4, 50.0, flow4);
      Setpt = B_set;
      CS = BCS;
      TTL = BTTL;
      truePID = trueBPID;
      //return gasreading;
      break;
    case 3: //O2low
      getflows(5, 400.0, flow5);
      Setpt = C_set;
      CS = CCS;
      TTL = CTTL;
      truePID = trueCPID;
      //return gasreading;
      break;
  }
}

void checkgasflow(int gastoread) {
  gasread(gastoread);
  Input = (double)gasreading;
  Setpoint = (double)Setpt;
  gasdif = Input - Setpoint;
  if (abs(gasdif) < PIDdif) {
    truePID = 1;
    return;
  }
  else {
    GasPID();
  }
}

void GasPID() {
  holdingRegs[Error1] = 65535; //set to 0 for normal operation, 65535 = PID control

  //modbus communication
  slave.poll( holdingRegs, HOLDING_REGS_SIZE );

  do {
    eachPID(1);
    eachPID(2);
    eachPID(3);
  } while (!trueAPID || !trueBPID || !trueCPID);

  holdingRegs[Error1] = 0;
}

void eachPID(int gastoread) {
  gasread(gastoread);
  Input = (double)gasreading;
  Setpoint = (double)Setpt;
  gasdif = Input - Setpoint;
  if (abs(gasdif) < PIDdif) {
    truePID = 1;
    return;
  }
  else if (abs(gasdif) < 10)
  { //we're close to setpoint, use conservative tuning parameters
    truePID = 0;
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {
    //we're far from setpoint, use aggressive tuning parameters
    truePID = 0;
    myPID.SetTunings(aggKp, aggKi, aggKd);
  }
  myPID.Compute();
  val = (uint16_t)Output;
  DACspi(CS, val, TTL);
}

void FPconv (int16_t FPin, uint16_t *hRega, uint16_t *hRegb, int FPfac) {
  lconv = (FPfac * (FPref + (long)(FPbit * FPin)));
  memcpy((uint8_t *)hRega, (uint8_t *)&lconv, 2);
  memcpy((uint8_t *)hRegb, (uint8_t *)&lconv + 2, 2);
}

void Flowconv (float FPin, uint16_t *hRega, uint16_t *hRegb) {
  lconv = (long)(FPin*flowscale);
  memcpy((uint8_t *)hRega, (uint8_t *)&lconv, 2);
  memcpy((uint8_t *)hRegb, (uint8_t *)&lconv + 2, 2);
}

void Tconv (int16_t Tin, uint16_t *hRega, uint16_t *hRegb) {
  tres = T1R2 * ((vref / (Tref + (Tbit * Tin))) - 1.0);
  treslog = log(tres);
  lconv = lround(10000000.0 * ((1.0 / (sA + sB * treslog + sC * (pow(treslog, 3.0)))) - 273.15));
  memcpy((uint8_t *)hRega, (uint8_t *)&lconv, 2);
  memcpy((uint8_t *)hRegb, (uint8_t *)&lconv + 2, 2);
}

void DACspi(uint16_t CS, uint16_t val, int TTL) {
  digitalWrite(CS, LOW);
  if (val > 0) {
    digitalWrite(TTL, HIGH);
  }
  else {
    digitalWrite(TTL, LOW);
  }
  SPI.transfer16(val);
  digitalWrite(CS, HIGH);
}

void gettemperatures() {
  tcaselect(6, TCAADDR);
  adcOne.setGain(GAIN_FOUR);
  adcTwo.setGain(GAIN_FOUR);
  adcThree.setGain(GAIN_FOUR);
  adcFour.setGain(GAIN_FOUR);
  //temp sensors - 2 consecutive readings to allow pins to warm up first
  for (i = 0; i < tempnum; i++) {
    hT1 = adcOne.readADC_Differential_0_1();
  }
  for (i = 0; i < tempnum; i++) {
    hT2 = adcOne.readADC_Differential_2_3();;
  }
  for (i = 0; i < tempnum; i++) {
    hT3 = adcTwo.readADC_Differential_0_1();
  }
  for (i = 0; i < tempnum; i++) {
    hT4 = adcTwo.readADC_Differential_2_3();
  }
  //for (i = 0; i < tempnum; i++) {
  //  hT5 = adcThree.readADC_Differential_0_1();
 // }
  //for (i = 0; i < tempnum; i++) {
  //  hT6 = adcThree.readADC_Differential_2_3();
  //}
  //for (i = 0; i < tempnum; i++) {
  //  hT7 = adcFour.readADC_Differential_0_1();
  //}
  //for (i = 0; i < tempnum; i++) {
  //  hT8 = adcFour.readADC_Differential_2_3();
  //}

  Tconv(hT1, &holdingRegs[T1a], &holdingRegs[T1b]);
  //Serial.print("hT1: "); Serial.println(hT1);
  Tconv(hT2, &holdingRegs[T2a], &holdingRegs[T2b]);
  //Serial.print("hT2: "); Serial.println(hT2);
  Tconv(hT3, &holdingRegs[T3a], &holdingRegs[T3b]);
  //Serial.print("hT3: "); Serial.println(hT3);
  Tconv(hT4, &holdingRegs[T4a], &holdingRegs[T4b]);
  //Serial.print("hT4: "); Serial.println(hT4);
  //Tconv(hT5, &holdingRegs[T5a], &holdingRegs[T5b]);
  //Serial.print("hT5: "); Serial.println(hT5);
  //Tconv(hT6, &holdingRegs[T6a], &holdingRegs[T6b]);
  //Serial.print("hT6: "); Serial.println(hT6);
  //Tconv(hT7, &holdingRegs[T7a], &holdingRegs[T7b]);
  //Serial.print("hT7: "); Serial.println(hT7);
  //Tconv(hT8, &holdingRegs[T8a], &holdingRegs[T8b]);
  //Serial.print("hT8: "); Serial.println(hT8);
}

void getflows (int sensor, float range, float flowreading) {
  tcaselect(sensor, TCA2ADDR);
  Wire.requestFrom(0x49, 2);

  while (Wire.available())   // slave may send less than requested
  {
    int flowdata = Wire.read();    // receive a byte as character
    //Serial.println(flowdata);
    flowdata = flowdata << 8;
    flowdata |= Wire.read();    // receive a byte as character
    //Serial.println(flowdata);
    flowreading = range * ((flowdata / 16384.0) - 0.5) / 0.4;
    //Serial.print("sensor 3 flow = "); Serial.println(flow3);
    //Serial.println();         // print the character
    //Serial.println();
  }
  delay(20);
}

void flowconvert (){

  Flowconv(flow2, &holdingRegs[Flow2a], &holdingRegs[Flow2b]);
  //Serial.print("hFP1: "); Serial.println(hFP1);
  Flowconv(flow3, &holdingRegs[Flow3a], &holdingRegs[Flow3b]);
  //Serial.print("hFP1: "); Serial.println(hFP1);
  Flowconv(flow4, &holdingRegs[Flow4a], &holdingRegs[Flow4b]);
  //Serial.print("hFP1: "); Serial.println(hFP1);
  Flowconv(flow5, &holdingRegs[Flow5a], &holdingRegs[Flow5b]);
  //Serial.print("hFP1: "); Serial.println(hFP1);
}

/*void FPconv (int16_t FPin, uint16_t *hRega, uint16_t *hRegb, int FPfac, long FPcal) {
  lconv = (FPfac * (FPcal + FPref + (long)(FPbit * FPin)));
  memcpy((uint8_t *)hRega, (uint8_t *)&lconv, 2);
  memcpy((uint8_t *)hRegb, (uint8_t *)&lconv + 2, 2);
  }*/

/*void GasPID() {
  holdingRegs[Error1] = 65535; //set to 0 for normal operation, 65535 = PID control

  //modbus communication
  slave.poll( holdingRegs, HOLDING_REGS_SIZE );

  tcaselect(0);

  do {
    eachPID(O2read, O2_set, BCS, trueAPID);
    eachPID(CO2read, CO2_set, CCS, trueBPID);
    eachPID(N2read, N2_set, ACS, trueCPID);
  } while (!trueAPID || !trueBPID || !trueCPID);

  trueAPID = !trueAPID;
  trueBPID = !trueBPID;
  trueCPID = !trueCPID;
  }*/

/*
  void eachPID(int16_t gasReadfunc(), int16_t Setpt, int CS, bool truePID, int TTL) {
  Setpoint = (double)Setpt;

  for (i = 0; i < 3; i++) {
    hFP = (*gasreadfunc)();
  }
  Input = (double)hFP;
  gasdif = Input - Setpoint;
  if (abs(gasdif) < PIDdif) {
    truePID = 1;
    return;
  }
  else if (abs(gasdif) < 10)
  { //we're close to setpoint, use conservative tuning parameters
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {
    //we're far from setpoint, use aggressive tuning parameters
    myPID.SetTunings(aggKp, aggKi, aggKd);
  }
  myPID.Compute();
  val = (uint16_t)Output;
  DACspi(CS, val, TTL);
  }*/

/*//Connections
  int16_t O2read() {
  gasread = adcOne.readADC_Differential_0_1();
  return gasread;
  }
  int16_t CO2read() {
  gasread = adcOne.readADC_Differential_2_3();
  return gasread;
  }
  int16_t N2read() {
  gasread = adcTwo.readADC_Differential_0_1();
  return gasread;
  }*/

/*// SPI communication with DAC
  DACspi(BCS, holdingRegs[CO2_set], BTTL); //CO2 controller
  DACspi(CCS, holdingRegs[N2_set], CTTL); //N2 controller
  DACspi(ACS, holdingRegs[O2_set], ATTL); //O2 controller*/
  • did you run the I2CScanner sketch? – Juraj Apr 15 at 11:49
  • 1
    @juraj no, just did! That's it! I'm opening the first multiplexer then opening the second and not closing the first - many thanks – Oliver Walters Apr 15 at 12:36
  • @OliverWalters Please write an answer to your question, so that other can profit from this. – chrisl Apr 15 at 14:55
  • You have another question on that topic, this is solved now as well, I guess? – Sim Son Apr 15 at 16:15
  • @SimSon The other topic is different, relating to one multiplexer which isn't functioning – Oliver Walters Apr 16 at 9:06
0

I had not realised that once opened, the tca9548a channels stay open until either POR or the RESET pin is brought low. Hence when using two multiplexers I just needed to use an extra wire to the RESET pin on each from the nano.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.