4067 Multiplexer code question

Edit: I have mistaken in the forum. I intended to post it in 'Programming Questions'
Hi,
I rewritten the following code which is heavily based on the code from this page by Adam Meyer.

For the moment my code uses a delay so it is not a non-blocking code. I would like to transfer it to be a non blocking code so it will constantly read the values from the 4067 inputs but will print out only when a value for pot x is changed from the previously reading.

here is the full code:

//Mux control pins
int s0 = 8;
int s1 = 9;
int s2 = 10;
int s3 = 11;

//Mux in "SIG" pin
int SIG_pin = 0;

//Mux inputs
#define NUM_INPUTS 3

unsigned long currentMillis = millis();

void setup(){
  pinMode(s0, OUTPUT); 
  pinMode(s1, OUTPUT); 
  pinMode(s2, OUTPUT); 
  pinMode(s3, OUTPUT); 

  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

  Serial.begin(115200);
}


void loop(){

MuxValue();

}



int readMux(int channel){
  int controlPin[] = {s0, s1, s2, s3};

  int muxChannel[NUM_INPUTS][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
  };

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);

  //return the value
  return val;
}

int MuxValue(){
  for(int i = 0; i < NUM_INPUTS; i ++){
    Serial.print("Value at channel ");
    Serial.print(i);
    Serial.print("is : ");
    Serial.println(readMux(i));
    delay(1000);
  
}
}

and here is the function I think I should change:

int MuxValue(){
  for(int i = 0; i < NUM_INPUTS; i ++){
    Serial.print("Value at channel ");
    Serial.print(i);
    Serial.print("is : ");
    Serial.println(readMux(i));
    delay(1000);
  
}
}

how can I make it into a non blocking code printing only when a change in value has happened?

my first thought is to use millis() and read the 4067 inputs only at a certain interval but this will not be a good idea (?) because it might have been that a value is changed within that interval.
How should I impliment the above?

Thanks

I tried to change that part of code:


  //read the value at the SIG pin
  int val = analogRead(SIG_pin);
  int oldVal;
  if(oldVal != val)
  {
    oldVal = val;
  //return the value
  return val;
  
  }

}

the problem I think I have in here is that I return val after I make oldVal = val; . I try to switch between the two and it is also not working (I guess because the return val; is taking me out of the function before I make oldVal = val;

You should write out your exact algorithm so we know exactly what is required.

For example:

  • Read Analog input A0 connected to the analog mux, print this value.
  • Set up the read for the next input and set up a 20ms timer to let the next mux input settle out.
  • When the timer has expired, repeat.

Edit
Always show us a good schematic of your proposed circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.

My wish from the algorithm is the following:

constantly reading the value from the input pins at the 4067 multiplexer (without using any delay) >>
printing out only when the value has changed so if pot 0 had a value of 555 and at the next reading the value is still 555 it won't print to the serial.

my code now is as the following and yet it printed the value of the pots even if the value has not changed.

//Mux control pins
int s0 = 8;
int s1 = 9;
int s2 = 10;
int s3 = 11;

//Mux in "SIG" pin
int SIG_pin = 0;

//Mux inputs
#define NUM_INPUTS 3

unsigned long currentMillis = millis();

void setup(){
  pinMode(s0, OUTPUT); 
  pinMode(s1, OUTPUT); 
  pinMode(s2, OUTPUT); 
  pinMode(s3, OUTPUT); 

  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

  Serial.begin(115200);
}


void loop(){

MuxValue();

}



int readMux(int channel){
  int controlPin[] = {s0, s1, s2, s3};

  int muxChannel[NUM_INPUTS][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
  };

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);
  int oldVal;
  if(oldVal != val)
  {
    oldVal = val;
  //return the value
  return val;
  
  }

}

int MuxValue(){
  for(int i = 0; i < NUM_INPUTS; i ++){

    Serial.print("Value at channel ");
    Serial.print(i);
    Serial.print("is : ");
    Serial.println(readMux(i));
    delay(1000);
  
}
}

where my mistake is?

Here is another attempt without a success:

//Mux control pins
int s0 = 8;
int s1 = 9;
int s2 = 10;
int s3 = 11;

//Mux in "SIG" pin
int SIG_pin = 0;

//Mux inputs
#define NUM_INPUTS 3


void setup(){
  pinMode(s0, OUTPUT); 
  pinMode(s1, OUTPUT); 
  pinMode(s2, OUTPUT); 
  pinMode(s3, OUTPUT); 

  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

  Serial.begin(115200);
}


void loop(){

  int oldVal[NUM_INPUTS] = {0};
  for(int i = 0; i < NUM_INPUTS; i ++){
    if(oldVal[i] != readMux(i)){
    Serial.print("Value at channel ");
    Serial.print(i);
    Serial.print("is : ");
    Serial.println(readMux(i));
    oldVal[i] = readMux(i);
    }
    delay(1000);
  }

}


int readMux(int channel){
  int controlPin[] = {s0, s1, s2, s3};

  int muxChannel[3][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
   
  };

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);

  //return the value
  return val;
}

This is not tested but should be a start:

//Mux inputs
#define NUM_INPUTS                3

#define ENABLED                   true
#define DISABLED                  false

//                                     s0 s1  s2  s3
const byte addressPin[]              = {8, 9, 10, 11};

const byte muxChannel[NUM_INPUTS][4] =
{
  {0, 0, 0, 0}, //channel 0
  {1, 0, 0, 0}, //channel 1
  {0, 1, 0, 0}, //channel 2
};

const byte heartbeatLED       = 13;

//Mux in "SIG" pin
const byte  SIG_pin           = A0;

boolean smapleFlag            = DISABLED;

byte muxAddress               = 0;

int muxValue;

//timing stuff
unsigned long heartbeatMillis;
unsigned long readMuxMillis;
unsigned long settlingMillis;

//********************************************^************************************************
void setup()
{
  Serial.begin(115200);

  pinMode(heartbeatLED, OUTPUT);

  for (byte x = 0; x <= 3; x++)
  {
    pinMode(addressPin[x], OUTPUT);
    digitalWrite(addressPin[x], LOW);
  }

} //END of   setup()

//********************************************^************************************************
void loop()
{
  //*********************************                                heartbeat TIMER
  //is it time to toggle the heartbeatLED ?
  if (millis() - heartbeatMillis >= 500ul)
  {
    //restart this TIMER
    heartbeatMillis = millis();

    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //*********************************                                readMux TIMER
  //has 1 second gone by ?
  if (millis() - readMuxMillis >= 1000ul)
  {
    //restart this TIMER
    readMuxMillis = millis();

    //start the reading process for this mux input
    readMux(muxAddress);
  }

  //*********************************                                settling TIMER
  //as the analog setting time expired ?
  if (smapleFlag == ENABLED && millis() - settlingMillis >= 10ul)
  {
    //we are now finished with this setting delay
    smapleFlag = DISABLED;

    //read the value at the SIG pin
    muxValue = analogRead(SIG_pin);

    Serial.print("Value at channel ");
    Serial.print(muxAddress);
    Serial.print(" is : ");
    Serial.println(muxValue);

    //prepare for the next mux address
    muxAddress++;

    //have we read all the inputs ?
    if (muxAddress > NUM_INPUTS - 1)
    {
      muxAddress = 0;
    }
  }

  //*********************************
  // other non blocking code goes here
  //*********************************

} //END of    loop()


//********************************************^************************************************
void readMux(byte channel)
{
  //send the address to the CD4067
  for (byte i = 0; i < 4; i ++)
  {
    digitalWrite(addressPin[i], muxChannel[channel][i]);
  }

  //enable the analog settling TIMER
  smapleFlag = ENABLED;

  //restart the analog settling TIMER
  settlingMillis = millis();

} //END of   readMux(int channel)


Thanks, but it still printing the value even when it was not changed:

I would expect it to print only once the value of channel 0, 1 and 2 as they did not changed (stayed at 1023)

That is correct, that section is not in the sketch yet.

I do not have a 4067 to try things out.
Before we add that option, does the sketch sample all 3 mux inputs correctly?

Not tested:

//Mux inputs
#define NUM_INPUTS                3

#define ENABLED                   true
#define DISABLED                  false

//                                     s0 s1  s2  s3
const byte addressPin[]              = {8, 9, 10, 11};

const byte muxChannel[NUM_INPUTS][4] =
{
  {0, 0, 0, 0}, //channel 0
  {1, 0, 0, 0}, //channel 1
  {0, 1, 0, 0}, //channel 2
};

const byte heartbeatLED       = 13;

//Mux in "SIG" pin
const byte  SIG_pin           = A0;

boolean smapleFlag            = DISABLED;

byte muxAddress               = 0;

int muxValue;
int lastValue[NUM_INPUTS];

//timing stuff
unsigned long heartbeatMillis;
unsigned long readMuxMillis;
unsigned long settlingMillis;

//********************************************^************************************************
void setup()
{
  Serial.begin(115200);

  pinMode(heartbeatLED, OUTPUT);

  for (byte x = 0; x <= 3; x++)
  {
    pinMode(addressPin[x], OUTPUT);
    digitalWrite(addressPin[x], LOW);
  }

} //END of   setup()

//********************************************^************************************************
void loop()
{
  //*********************************                                heartbeat TIMER
  //is it time to toggle the heartbeatLED ?
  if (millis() - heartbeatMillis >= 500ul)
  {
    //restart this TIMER
    heartbeatMillis = millis();

    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //*********************************                                readMux TIMER
  //has 1 second gone by ?
  if (millis() - readMuxMillis >= 1000ul)
  {
    //restart this TIMER
    readMuxMillis = millis();

    //start the reading process for this mux input
    readMux(muxAddress);
  }

  //*********************************                                settling TIMER
  //has the analog setting time expired ?
  if (smapleFlag == ENABLED && millis() - settlingMillis >= 10ul)
  {
    //we are now finished with this setting delay
    smapleFlag = DISABLED;

    //read the value at the SIG pin
    muxValue = analogRead(SIG_pin);

    //has analog value changed since the last time ?
    if (lastValue[muxAddress] != muxValue)
    {
      Serial.print("Value at channel ");
      Serial.print(muxAddress);
      Serial.print(" is : ");
      Serial.println(muxValue);

      //update to the new value
      lastValue[muxAddress] = muxValue;
    }

    //prepare for the next mux address
    muxAddress++;

    //have we read all the inputs ?
    if (muxAddress > NUM_INPUTS - 1)
    {
      //back to the first address
      muxAddress = 0;
    }
  }

  //*********************************
  // other non blocking code goes here
  //*********************************

} //END of    loop()


//********************************************^************************************************
void readMux(byte channel)
{
  //send the address to the CD4067
  for (byte i = 0; i < 4; i ++)
  {
    digitalWrite(addressPin[i], muxChannel[channel][i]);
  }

  //enable the analog settling TIMER
  smapleFlag = ENABLED;

  //restart the analog settling TIMER
  settlingMillis = millis();

} //END of   readMux(int channel)


EDIT
Added comments

EDIT
Your CD4067 should have a de-coupling capacitor on the VDD pin.

1 Like

It's probably best to stay the course with @LarryD.

And it's hard to be sure about your snippet, but one thing I can say is that you may have been close, and made an error concerning variable scope, a concept you must master if you haven't. Yet.

  int val = analogRead(SIG_pin);
  static int oldVal;
  if(oldVal != val)
  {
    oldVal = val;
  //return the value
    return val;
  }

By making oldVal static, the variable never dies, so to speak, but lives on through multiple calls into a function where it might be, for example.

Otherwise, oldVal is created anew every time, always has 0 as its value, and thus will usually not match newVal.

If you were doing that in you printing loop, you would have to have an array of oldVal values to compare to the new values one by one and react appropriately.

Another problem is that anakogRead() might return a value that differs only by a small amount due to noise or whatever. In a case of comparing values that might have some slop in them, it is typical to see a test of the difference between old and new. The absolute value function available does the job:

if (abs(oldVal - newVal) > 2) {    // really different…

The engineers call it hysteresis and there are various ways of dealing with it. I would recommend that if the word is new to you that you settle in and google it and see better explanations.

Arduino hysteresis

will lead you to its use in typical projects.

HTH

a7

1 Like

Thank you very much! it seems to work just fine - I wander what is this part:

 //*********************************                                heartbeat TIMER
  //is it time to toggle the heartbeatLED ?
  if (millis() - heartbeatMillis >= 500ul)
  {
    //restart this TIMER
    heartbeatMillis = millis();

    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

what is the function of boolean smapleFlag = DISABLED;
has in here:

 //has the analog setting time expired ?
  if (smapleFlag == ENABLED && millis() - settlingMillis >= 10ul)
  {
    //we are now finished with this setting delay
    smapleFlag = DISABLED;

how can I add a slop for the Analog read as @alto777 suggested ?
I guess it should be added in here:

//has analog value changed since the last time ?
    if (lastValue[muxAddress] != (muxValue ))
    {
      Serial.print("Value at channel ");
      Serial.print(muxAddress);
      Serial.print(" is : ");
      Serial.println(muxValue);

      //update to the new value
      lastValue[muxAddress] = muxValue;
    }

This value is discarded every time through loop() because it's declared inside loop. Move the declaration to a place outside any function (make it a "global variable").

I have put int oldVal; outside the function, make it a global variable but still the printing is happening also when no change in value has made:

//Mux control pins
int s0 = 8;
int s1 = 9;
int s2 = 10;
int s3 = 11;

//Mux in "SIG" pin
int SIG_pin = 0;

//Mux inputs
#define NUM_INPUTS 3

unsigned long currentMillis = millis();

int oldVal;


void setup(){
  pinMode(s0, OUTPUT); 
  pinMode(s1, OUTPUT); 
  pinMode(s2, OUTPUT); 
  pinMode(s3, OUTPUT); 

  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

  Serial.begin(115200);
}


void loop(){

MuxValue();

}



int readMux(int channel){
  int controlPin[] = {s0, s1, s2, s3};

  int muxChannel[NUM_INPUTS][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
  };

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);
  if(oldVal != val)
  {
    oldVal = val;
  //return the value
  return val;
  
  }

}

int MuxValue(){
  for(int i = 0; i < NUM_INPUTS; i ++){

    Serial.print("Value at channel ");
    Serial.print(i);
    Serial.print("is : ");
    Serial.println(readMux(i));
    delay(1000);
  
}
}

I might just stick to LarryD code

Your function always returns some value. If it's not a changed value, it still returns the unchanged value. This logic is flawed.

I tried this line to filter out the noise but I think it is not working has intended -

if (abs(muxValue - lastValue[muxAddress] > 3 )){
      Serial.print("Value at channel ");
      Serial.print(muxAddress);
      Serial.print(" is : ");
      Serial.println(muxValue);

      //update to the new value
      lastValue[muxAddress] = muxValue;
    }

Bad balance of parenthesis! A mistake I never make. :wink:

abs(muxValue - lastValue[muxAddress] ) > 3

You want, in the expression, to compare the absolute difference between the two values abs(whatever) to 3, so

if (abs(muxValue - lastValue[muxAddress] ) > 3) {

Can't comment on the validity of it in context, but you def want the parens like I show above in that test.

a7

1 Like

Works!

This is a piece of code used to visualize if there is code blocking happening; the heartbeat LED should toggle every 500ms.

Suggest you incorporate this in sketches to give you confidence that things are chugging along fine.

We use Flags to allow sections of code to run.

When we are finished with a TIMER for example, we can disable that TIMER by disabling a control Flag.



First, change these:
int muxValue;
int lastValue[NUM_INPUTS];

Change to:

unsigned int muxValue;
unsigned int lastValue[NUM_INPUTS];
byte hysteresis = 20; //whatever you want

Then try this change:

if (lastValue[muxAddress] != (muxValue ))

Change to:
if (lastValue[muxAddress] - muxValue > hysteresis)

The above should be very close for slop.

Dontcha still need to use the absolute value function on the difference?

a7

1 Like

You are right :open_mouth:

Updated version:


//https://forum.arduino.cc/t/4067-multiplexer-code-question/1019084

#define ENABLED                        true
#define DISABLED                       false

#define NUM_INPUTS                     3

//                                     s0 s1  s2  s3
const byte addressPin[]              = {8, 9, 10, 11};

const byte muxChannel[NUM_INPUTS][4] =
{
  {0, 0, 0, 0}, //channel 0
  {1, 0, 0, 0}, //channel 1
  {0, 1, 0, 0}, //channel 2
};

const byte heartbeatLED       = 13;

//Mux in "SIG" pin
const byte  SIG_pin           = A0;

boolean sampleFlag            = DISABLED;

byte muxAddress;
byte hysteresis               = 20; //whatever is needed

int muxValue;
int lastValue[NUM_INPUTS];

//timing stuff
unsigned long heartbeatMillis;
unsigned long readMuxMillis;
unsigned long settlingMillis;

//********************************************^************************************************
void setup()
{
  Serial.begin(115200);

  pinMode(heartbeatLED, OUTPUT);

  for (byte x = 0; x <= 3; x++)
  {
    pinMode(addressPin[x], OUTPUT);
    digitalWrite(addressPin[x], LOW);
  }

} //END of   setup()

//********************************************^************************************************
void loop()
{
  //*********************************                                heartbeat TIMER
  //is it time to toggle the heartbeatLED ?
  if (millis() - heartbeatMillis >= 500ul)
  {
    //restart this TIMER
    heartbeatMillis = millis();

    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //*********************************                                readMux TIMER
  //has 1 second gone by ?
  if (millis() - readMuxMillis >= 1000ul)
  {
    //restart this TIMER
    readMuxMillis = millis();

    //start the reading process for this mux input
    readMux(muxAddress);
  }

  //*********************************                                settling TIMER
  //has the analog setting time expired ?
  if (sampleFlag == ENABLED && millis() - settlingMillis >= 10ul)
  {
    //we are now finished with this setting delay
    sampleFlag = DISABLED;

    //read the value at the SIG pin
    muxValue = analogRead(SIG_pin);

    //has analog value changed more than the hysteresis amount ?
    if (abs(lastValue[muxAddress] - muxValue) > hysteresis)
    {
      Serial.print("Value at channel ");
      Serial.print(muxAddress);
      Serial.print(" is : ");
      Serial.println(muxValue);

      //update to the new value
      lastValue[muxAddress] = muxValue;
    }

    //prepare for the next mux address
    muxAddress++;

    //have we read all the inputs ?
    if (muxAddress > NUM_INPUTS - 1)
    {
      //back to the first address
      muxAddress = 0;
    }
  }

  //*********************************
  // other non blocking code goes here
  //*********************************

} //END of    loop()


//********************************************^************************************************
void readMux(byte channel)
{
  //send the address to the CD4067
  for (byte i = 0; i < 4; i ++)
  {
    digitalWrite(addressPin[i], muxChannel[channel][i]);
  }

  //enable the analog settling TIMER
  sampleFlag = ENABLED;

  //restart the analog settling TIMER
  settlingMillis = millis();

} //END of   readMux(byte channel)