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*/