RESOLVED: Re: Please will someone decypher a small piece of code for me.

Dear all,
I have been trying to learn about the CD4051 multiplexer (mux?) and have read an article by Nick Gammon on his blog, though I admit I cannot fathom out a few lines of his code, and I have listed the lines in question below.
For completeness the full code is at the end of this post.

The lines I have problems with are here:

int readSensor (const byte which)
  {
  // select correct MUX channel
  digitalWrite (addressA, (which & 1) ? HIGH : LOW);  // low-order bit
  digitalWrite (addressB, (which & 2) ? HIGH : LOW);
  digitalWrite (addressC, (which & 4) ? HIGH : LOW);  // high-order bit
  // now read the sensor
  return analogRead (sensor);
  }  // end of readSensor

In particular, what does digitalWrite (addressA, (which & 1) ? HIGH : LOW); // low-order bit do? The Arduino reference does not appear to cover this.

Please will someone explain in plain english what is happening here, and perhaps why.

GM

Full code below:

// Example of using the 74HC4051 multiplexer/demultiplexer

// Author: Nick Gammon
// Date:   14 March 2013

const byte sensor = A0;  // where the multiplexer in/out port is connected

// the multiplexer address select lines (A/B/C)
const byte addressA = 6; // low-order bit
const byte addressB = 5;
const byte addressC = 4; // high-order bit

void setup ()
  {
  Serial.begin (115200);
  Serial.println ("Starting multiplexer test ...");
  pinMode (addressA, OUTPUT); 
  pinMode (addressB, OUTPUT); 
  pinMode (addressC, OUTPUT); 
  }  // end of setup

int readSensor (const byte which)
  {
  // select correct MUX channel
  digitalWrite (addressA, (which & 1) ? HIGH : LOW);  // low-order bit
  digitalWrite (addressB, (which & 2) ? HIGH : LOW);
  digitalWrite (addressC, (which & 4) ? HIGH : LOW);  // high-order bit
  // now read the sensor
  return analogRead (sensor);
  }  // end of readSensor
  
void loop ()
  {
  // show all 8 sensor readings
  for (byte i = 0; i < 7; i++)
    {
    Serial.print ("Sensor ");
    Serial.print (i);
    Serial.print (" reads: ");
    Serial.println (readSensor (i));
    }
  delay (1000);
  }  // end of loop

a byte has 8 bits. in can store a binary number with 8 positions

0000 0000 is 0
0000 0001 is 1
0000 0010 is 2
0000 0011 is 3
...
0000 1010 is 10 or 0x0A (hexa has digits 0, 1, 2, 3 ,4 ,5, 6, 7 ,8, 9, A, B, C, D, E, F so it can map four bits into one digit)
0000 1011 is 11 or 0x0B
...
0000 1111 is 15 or 0x0F
...
1111 1111 is 255 or 0xFF

now & is a binary operator: 0 and 0 is 0; 1 and 1 is 1; 0 and 1 is 0; 0 and 1 is 0 too
(| is operator 'or': 0 or 0 is 0; 1 or 1 is 1; 0 or 1 is 1; 0 or 1 is 1 too)

now if you want to know if a byte has some bit set you & it with a byte which has only that one bit set
00001111 & 00000001 is 00000001 but 00001010 & 00000001 is 00000000
and 2 is 0000 0010
and 4 is 0000 0100
so the code uses them to detect if a bit is set

then the code evaluates the result with the ternary operator which returns the second value if the value before ? is 0

digitalWrite (addressA, (which & 0b00000001) ? HIGH : LOW);
digitalWrite (addressB, (which & 0b00000010) ? HIGH : LOW);
digitalWrite (addressC, (which & 0b00000100) ? HIGH : LOW);

the bit positions decimal values are always the previous bit decimal value multiplied with 2. starting with 1 for the first bit
1 * 2 = 2
2 * 2 = 4
4 * 2 = 8
16, 32, 64, 128

and 1 + 2 + 4 + 8 + 16 + 64 + 128 = 255 (0b11111111)

the // low-order bit and // high-order comment must be some reference to the sensors datasheet

Thank you for your answer.

It seems that the line I'm stuggling with is a 'digitalWrite()' statement, so I looked it up on the Arduino reference.

The syntax is digitalWrite(pin, value)

I can see how the pin value is obtained, AddressA B or C, but it's the part after the comma that confuses me.

I can see that a constant byte 'which' is (as you have explained)bitwise logical ANDed with binary value 00000001 to give a result of 1 or 0 (true or false?), so in my mind one of those two values is the term after the comma, but then there is the '?'. I can't find ? in the reference, but I take it from your explanation the following meaning:

'If the result of the logical bitwise AND is 1 then use the first value after the ?, and if it's 0 then use the second value'.

Is that correct? Equally does it mean that if I were to reverse the order of 'HIGH' and 'LOW' then I would select the opposite value for the output pin?

Anyhow, thank you for your patient explanation, I'm sure that the fog of confusion in my head will clear with practice!

UPDATE: The sunlight may be breaking through....

int readSensor (const byte which) {....return analogRead (sensor);}

is a FUNCTION, hence why it is allowed to be outside the loop(), and this function returns the value of analog input pin A0 every time it is called.

The constant byte value of 'which' is also i (as a binary value)...

By jove I think I've got it.
A little refresher reading up on functions is required by me.

That's really great, and thanks for your help...I can experiment with this code to see what values come up on the serial monitor if I manipulate the values.

I have further questions regarding large numbers of sensors (50 or so), but they can wait until I am better able to understand the answers!

GM

ternary operator ? :

and sorry, I meant "then the code evaluates the result with the ternary operator which returns the second value after ?, if the value before ? is 0"

Thank-you, it is much clearer now.
I built up the circuit detailed on Nick Gammon's site, and played with the settings, but found that it only displayed the values of inputs 0 to 6 inclusive.

The code

  // show all 8 sensor readings
  for (byte i = 0; i <7; i++)

was altered to

  // show all 8 sensor readings
  for (byte i = 0; i <=7; i++)

and all is well.

I will continue to experiment until I fully appreciate the code, but in the meantime the problem is solved, I'm very pleased, and thanks for your help.

GM

Glorymill:
Thank-you, it is much clearer now.
I built up the circuit detailed on Nick Gammon's site, and played with the settings, but found that it only displayed the values of inputs 0 to 6 inclusive.

The code

  // show all 8 sensor readings

for (byte i = 0; i <7; i++)



was altered to 

// show all 8 sensor readings
  for (byte i = 0; i <=7; i++)



and all is well.

I will continue to experiment until I fully appreciate the code, but in the meantime the problem is solved, I'm very pleased, and thanks for your help.

GM

usual is i < 8