4051 + TLC 5940: LEDs fading according to INPUT

Hello everyone and happy holidays.
I am developing my thesis project and thanks to this forum I got quite far till now. But still a long way to get the intended final result.

I have (now) 1x 4051 and 1x TLC 5940 in order to control 8 INPUTS and 8 LEDs. The behaviour I would like to have for my LEDs is the following:

  1. button switched off–> LED off
  2. button switched on–> LED fades in from 0 to top
  3. button still on–> LED remains to top
  4. button switched off → LED fades out from top to 0
  5. button still switched off–> back to point 1.

Unfortunately I am very new to coding and so far I had this kind of behavoiur:

  1. button switched off–> LED fades out from top to 0, repetedly
  2. button switched on–> LED fades in from 0 to top, repetedly

I was hoping to have some suggestion on the code in order to achive this first part of the LEDs behaviour.

Below is the code, I hope some of you can help me. Thank you.

//**********OUTPUTS VARIABLES**********//
#include "Tlc5940.h"
#include "tlc_fades.h"
//int b;
int fadeUpmode = 0;
int fadeDownmode = 0;
int maxValue = 4000;
int ch;

boolean fade = false;
int firstCount = 0;
int previousInValue = 0;

TLC_CHANNEL_TYPE channel[16] = {
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

uint16_t duration = 4000;
uint32_t startMillis;
uint32_t endMillis ;
int fadeMillis;
int counter = 0;
int downcounter = 0;
//***********************************//

///////////INPUTS VARIABLES////////////
int s0 = 7;
int s1 = 6;
int s2 = 5;

int sensorIn;

int inPin_1to8 = 0;  
int inPin_9to16 = 5;
int inPin_17to24 = 4;
int threshold = 1000;
int result_1to8;
int lastresult_1to8 = 0;
int result_9to16;
int result_17to24;
//////////////////////////////////////


//MAIN STRUCTURE//
int communicationMode = 0;

void setup(){
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  Tlc.clear();
  Tlc.init();
  Serial.begin(9600);
}

void loop(){
  switch(communicationMode){

  case 0:
    {  
      firstTouch();
      break;
    }

  case 1:
    {
      break;
    }

  case 2:
    {
      break;
    }
  }//main switch end    
}//loop end


void firstTouch(){ 
  for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));

    result_1to8 = analogRead(inPin_1to8);


    for(int e=0; e<1; e++) {
      sensorIn = (i+8*e);
      ch = sensorIn;

      if (e == 0) {
        if(ch == 0 && result_1to8 < threshold) {
          fade = false;
          fadeDown();
        }

        else if(ch == 0 && result_1to8 > threshold){
          fade = true;
          fadeUp();
        }
      }
    }//4051s for end
  }//main (bitshifting) for end
}//void end



void fadeUp(){
  switch(fadeUpmode){
  case 0:
    {
      if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
        if (!tlc_isFading(channel[0])) {
          startMillis= millis() + 50;
          endMillis = startMillis + duration;
          tlc_addFade(channel[0], 0, maxValue, startMillis, endMillis);
          counter++;
        }
      }
      tlc_updateFades();
      break;
    }
  }
}

void fadeDown(){
  switch(fadeDownmode){
  case 0:
    if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
      if (!tlc_isFading(channel[0])) {

        startMillis= millis() + 50;
        endMillis = startMillis + duration;
        tlc_addFade(channel[0], maxValue, 0, startMillis, endMillis);

      }
    }
    tlc_updateFades(); 
    break;
  }
}

Variable names should convey meaning.

    result_1to8 = analogRead(inPin_1to8);

inPin_1to8 doesn't tell me anything about the device that is connected. result_1to8 doesn't tell me anything about the expected output or the meaning of that value.

I see nowhere where you are reading the state of a switch. That would be done using digitalRead.

  1. button switched off--> LED off
  2. button switched on--> LED fades in from 0 to top
  3. button still on--> LED remains to top
  4. button switched off --> LED fades out from top to 0
  5. button still switched off--> back to point 1.

The loop function is called in an endless loop. There are things you want to do when the state of a switch changes, from pressed to released and from released to pressed. To know when the state changes, you need to read the switch state often (on every pass through loop) and compare the current state with the previous state. When the current state and the previous state are not the same, a transition has occurred. Whether that is from pressed to released or from released to pressed is defined by the current state.

Or, you could make life easy for yourself, and use the button library.

I see nowhere where you are reading the state of a switch. That would be done using digitalRead.

Using a 4051 as a multiplexer it is needed an analogRead, from what I understood so far. The input is given by this piece of code

sensorIn = (i+8*e);

But thanks for the button library suggestion. I'll try to find out a way to use it, and keep posted with further developments... or issues

Actually I think I cannot use the button library using the 4051 ;(

Using a 4051 as a multiplexer it is needed an analogRead, from what I understood so far.

According to the playground article, http://www.arduino.cc/playground/Learning/4051, the 4051 is an analog multiplexer, not a digital multiplexer. Why did you choose that particular multiplexer, instead of the proper digital multiplexer?

In any case, the value read from the multiplexer is stored in result_1to8.

    for(int e=0; e<1; e++) {

How many times will this loop execute? What values will e have during each execution?

Honestly I used the 4051 because I didn't know if I was going to use analog sensors (such as photoresistors) or buttons, and I thought that, while I can use an analog multiplexer for buttons, I couldn't use a digital multiplexer for photoresistors.

I am not entirely sure I got your question. But the for loop executes every time and the e value corresponds to which multiplexer I am checking. Now I have only one 4051, but in the end they will be three, because I have to handle 24 INPUTS.

I am trying to get the logic behind the different value that you were explaining before, but I am not there I guess :(

Honestly I used the 4051 because I didn’t know if I was going to use analog sensors (such as photoresistors) or buttons, and I thought that, while I can use an analog multiplexer for buttons, I couldn’t use a digital multiplexer for photoresistors.

Fair enough. It can be made to work.

But the for loop executes every time and the e value corresponds to which multiplexer I am checking.

The for loop consists of three parts - an initialization section, a comparison section, and an increment section.

Before the loop executes, the initialization section occurs, one time. In your case, e is set to 0.

Before each execution of the loop, the conditional(s) in the comparison section are evaluated. In you case, e is checked to see that it is less than one.

If the comparison section yields true, the body is executed. Then, the increment section is performed. In your case, e is incremented by 1.

So, e is set to 0. 0 IS less than 1, so the body is executed. Then, e is incremented by 1, to 1.

e is no longer less than 1, so the body of the loop is skipped, and processing continues after the loop.

So, your loop is executed exactly once, with e = 0. Is that what you were trying to do?

This statement:

sensorIn = (i+8*e);

will only ever set sensorIn to i.

sensorIn is then stuffed in ch. Again, ch conveys no meaning.

      if (e == 0) {

The only value of e inside the loop that causes anything to happen is the only value that e can take on. I’m not seeing the purpose of the loop or the if test.

How are the digital switches connected to the multiplexer? Do you have a schematic that you are using? How many digital switches are connected to the multiplexer?

Create a simple sketch to set the multiplexer channel to read, and print out the value read from each channel. The values, when digital devices are properly connected, should be 0 or 1023 for each channel. Do the values read match one of these values? Do they correlate to the pressed/released status of the switches?

What I was trying to do was to see which value was read for each pin of the 4051. The ch is indeed meaningfulless now, because I was thinking to use the ch in order to identify which channel of the tlc5940 to give the outputs. But now I was using just one input and one output in order to see how it works.

It is true that now the for loop is executed only once, when e = 0. That’s because now I have only one 4051, but since I will have 3 of them, in the end I thought it will be

for(e=0; e<2; e++){}

At the moment I have only one pushbotton on the y0 pin of the 4051.

Before writing the code I previously posted, I had 8 photoresistors attached and apparently it worked. I used to have this code in order to see if anything was working.

int s0 = 7;
int s1 = 6;
int s2 = 5;
int inPin_1to8 = 0;  
int threshold = 1017;

int result_1to8;


void setup(){
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));
    //bitRead(x, n) 
    //x: the number from which to read 
    //n: which bit to read, starting at 0 for the least-significant (rightmost) bit 


    result_1to8 = analogRead(inPin_1to8);

    for(int e=0; e<1; e++) {
      Serial.print("sensor nr: ");
      Serial.print((i+1)+8*e);
      Serial.print(" is ");


      if (e == 0) {
        if(result_1to8 < threshold) {
          Serial.print(result_1to8);
          Serial.println (" so it is 0");
       }
        else {
          Serial.println(result_1to8);
          Serial.print (" so it is 1");
         }
      }     
    }
  }
   delay(2000);
}//end loop

Does it make sense? :-[

The loop to read from multiple multiplexers will define which pins to toggle to read from the multiplexers.

There will be an inner loop, to set the pins to define which values to write to the pins, to read specific channels.

for(e=0; e<2; e++){}

This will loop twice, with e = 0 and with e = 1. This is the inner loop in your previous code, and has nothing to do with which multiplexer was read from, or which channel of that multiplexer.

I used to have this code in order to see if anything was working.

int inPin_1to8 = 0;

I would name this multiplexerOneInPin. I’d have multiplexerTwoInPin and multiplexerThreeInPin, too. These names convey precise concepts to me.

Does it make sense?

The more important question is does it work?

I think I didn’t relly understand.
If I am not wrong the i value from

  for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));
[...]
}

sets the loop to toggle the pins. Is that correct?
Then,

 analogRead(multiplexerOneInPin);

will read the value from the first 4051.
I got till here.

But then, how would it work the LEDs behaviour? That I still don’t get it.
From what I got, the button library doesn’t work in this case.

Am I on the right path? Is that correct what I summarized in this post? (to me it seemed to work before :o)

Any suggestion, expecially on the LEDs behaviour, is very welcomed…

Is that correct?

Yes, it is. For one multiplexer. If you have more than one, you need another loop, outside of this one, to choose the pins to set.

But then, how would it work the LEDs behaviour?

One step at a time.

Are you able to get consistent HIGH and LOW values for a switch, when you read the channel that the switch is attached to?

If so, the behavior of the LEDs is exactly the same as if you had the switch(es) connected to digital pins and were using digitalRead to directly read the pin state(s).

int currState = LOW;
int prevState = LOW;

void loop()
{
   currState = digitalRead(switchPin);
   if(currState != prevState) // Switch was pressed or released
   {
      if(currState == HIGH) // Switch is pressed
      {
         // Fade LED on
      }
      else // Switch is released
      {
         // Fade LED off
      }
   }
   else
   {
      // Button is still pressed or still released. No change in LEDs required
   }
   prevState = currState;
}

Are you able to get consistent HIGH and LOW values for a switch, when you read the channel that the switch is attached to?

If you mean having consistent 0 or 1023 values, then the answer is yes.
If you mean having HIGH or LOW values using

result_mux4051OneInPin = digitalRead(mux4051OneInPin);

then, the answer is no. I only get HIGH, but I don’t think I can have anything else using a 4051, because I cannot use digitalRead, I have to use analogRead.

Then, the code you posted is pretty meaningful to me, and I think I got it perferctly. The main issue, which is the one that stops me, is that I don’t know how to tell the TLC5940 “no change in LEDs required”.

I don’t know how to make the brightness of the LEDs stops at a certain level, either top or 0. I passed through the tlc library(http://students.washington.edu/acleone/codes/tlc5940arduino/html_r012/group__CoreFunctions.html#gc712fcd944dd3f5f4f0ff158d754ea06) and the tlc fade library (http://students.washington.edu/acleone/codes/tlc5940arduino/html_r012/tlc__fades_8h.html)as well, but I cannot understand how to get to my result.

I refined the code according to your suggestions and I post the two versions of it that do not manage the “stop at the top”+ “stop at 0” issue. But they work exactely the same

CODE 1:

//**********OUTPUTS VARIABLES**********//
#include "Tlc5940.h"
#include "tlc_fades.h"
int maxValue = 4000;
int LEDchannel;

boolean fade = false;


TLC_CHANNEL_TYPE channel[16] = {
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

uint16_t duration = 1900;
uint32_t startMillis;
uint32_t endMillis ;
int counter = 0;
int downcounter = 0;
//***********************************//

///////////INPUTS VARIABLES////////////
int s0 = 7;
int s1 = 6;
int s2 = 5;


int mux4051OneInPin = 0;  
int threshold = 1000;
int result_mux4051OneInPin;
//////////////////////////////////////


//MAIN STRUCTURE//

void setup(){
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  Tlc.init();
  Serial.begin(9600);
}

void loop(){
  for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));

    result_mux4051OneInPin = analogRead(mux4051OneInPin);


    LEDchannel = i;
    if(result_mux4051OneInPin < threshold) { // no contact
      fadeDown();
    }

    else if(result_mux4051OneInPin > threshold){
      fadeUp();
    }
  }//main (bitshifting) for end
}//void end



void fadeUp()
    {
      if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
        if (!tlc_isFading(channel[LEDchannel])) {

          startMillis= millis() + 50;
          endMillis = startMillis + duration;
          tlc_addFade(channel[LEDchannel], 0, maxValue, startMillis, endMillis);
        }
      }
      tlc_updateFades(); 
    
}//void end

void fadeDown(){
    if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
      if (!tlc_isFading(channel[LEDchannel])) {

        startMillis= millis() + 50;
        endMillis = startMillis + duration;
        tlc_addFade(channel[LEDchannel], maxValue, 0, startMillis, endMillis);
      }
    }
    tlc_updateFades(); 
}//void end

CODE 2:

//**********OUTPUTS VARIABLES**********//
#include "Tlc5940.h"
#include "tlc_fades.h"
int fadeUpmode = 0;
int fadeDownmode = 0;
int maxValue = 4000;
int LEDchannel;

boolean fade = false;


TLC_CHANNEL_TYPE channel[16] = {
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

uint16_t duration = 1900;
uint32_t startMillis;
uint32_t endMillis ;
//***********************************//

///////////INPUTS VARIABLES////////////
int s0 = 7;
int s1 = 6;
int s2 = 5;


int mux4051OneInPin = 0;  
int threshold = 1000;
int result_mux4051OneInPin;
//////////////////////////////////////


//MAIN STRUCTURE//

int currState = LOW;
int prevState = LOW;

void setup(){
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  Tlc.init();
  Serial.begin(9600);
}

void loop(){
  for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));

    LEDchannel = i;
    result_mux4051OneInPin = analogRead(mux4051OneInPin);
    currState = result_mux4051OneInPin;

    if (currState != prevState){

      if(result_mux4051OneInPin > threshold){
        fadeUp();
      }

      else if(result_mux4051OneInPin < threshold) { // no contact
        fadeDown();
      }

    }

    else {
    }
    prevState = currState;
  }//main (bitshifting) for end
}//void end



void fadeUp(){
  switch(fadeUpmode){
  case 0:
    {
      if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
        if (!tlc_isFading(channel[LEDchannel])) {

          startMillis= millis() + 50;
          endMillis = startMillis + duration;
          tlc_addFade(channel[LEDchannel], 0, maxValue, startMillis, endMillis);
        }
      }
      tlc_updateFades(); 
      break;
    }
  }
}

void fadeDown(){
  switch(fadeDownmode){
  case 0:
    if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
      if (!tlc_isFading(channel[LEDchannel])) {

        startMillis= millis() + 50;
        endMillis = startMillis + duration;
        tlc_addFade(channel[LEDchannel], maxValue, 0, startMillis, endMillis);
      }
    }
    tlc_updateFades(); 

    break;
  }

}//loop end

If you can help me understanding how to use the library properly,please, it would be amazingly helpful.

If you mean having consistent 0 or 1023 values, then the answer is yes.

I meant consistent values of any sort. 0 and 1023 are good values. For an analog pin with no voltage applied (switch open) 0 is expected. For an analog pin with 5V applied (switch closed) 1023 is expected, though anything above 1000 would not be surprising.

The main issue, which is the one that stops me, is that I don't know how to tell the TLC5940 "no change in LEDs required".

You tell it that no change is required by not telling it to change anything.

Use Serial.print() to print out currState and prevState, with appropriate descriptors: Serial.print("prevState: "); Serial.println(prevState); Serial.print("currState: "); Serial.println(currState);

    result_mux4051OneInPin = analogRead(mux4051OneInPin);
    currState = result_mux4051OneInPin;

There is no reason to have two variables to hold the same value. Get rid of one of them.

You have 8 channels on the multiplexer, with 8 switches (not all of which are used). Yet you are trying to manage current state and previous state of all the switches using one pair of variables. I'd ask how that's working for you, but I already know the answer.

You need to have currState and prevState as arrays, and reference the appropriate position in the array for each channel.

Mhmm I am struggling to understand. Sorry, I am a very newbie to coding and Arduino.

Anyway if I am not wrong, it’s better to use something like

 for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));

result_mux4051OneInPin[i] = analogRead(mux4051OneInPin);
...
}

Printing now the values it seems that they are not as consistent as some hours ago. I mean that the 0 sometimes gets to 1 an the 1023 sometimes reach just 1021 or 1022… Is there a way to solve this out with the code? It would be extremely helpful, because I could also be able to understand the mechanism for analog sensors, which (maybe not this time) but soon can be very helpful. I will need a kind of calibration, right?

For the digital switches, you could say that any value under 100 is NOT pressed, and any other value IS pressed.

it's better to use something like

Yes, assuming that you have a corresponding array for previous values.

Yes, assuming that you have a corresponding array for previous values

I do have an an array for previous values.

I have now connected all the 8 pushbuttons and tested them in order to see if they work correctly. It seems so, because when I press them the get values 1020 to 1023 and when they are released the return values 0 to 1.

I have tried with the HIGH/LOW but it still doesn’t work.
Actually with the code below, what happens is the following:

  1. When no button is pressed all the LEDs are off (which is good).

  2. I press one of the buttons

  3. The corrisponding LED starts fading, from 0 IN to the maxValue.
    At the same time, all the other LEDs fade from the maxValue OUT to 0.
    While I keep having the button pressed, the corrisponding LED gets to the top, then back to 0 and starts fading in to maxValue in loop.
    All the other LEDs fade OUT from maxValue to 0 and keep looping while one button is pressed.

  4. If I release the button while the corrisponding LED it’s still fading as well as all the other LEDs get stuck on the amount of light that is currently going on the fade.

This is the code:

//**********OUTPUTS VARIABLES**********//
#include "Tlc5940.h"
#include "tlc_fades.h"
int fadeUpmode = 0;
int fadeDownmode = 0;
int maxValue = 4090;
int LEDchannel;

boolean fade = false;


TLC_CHANNEL_TYPE channel[16] = {
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

uint16_t duration = 1800;
uint32_t startMillis;
uint32_t endMillis ;
//***********************************//

///////////INPUTS VARIABLES////////////
int s0 = 7;
int s1 = 6;
int s2 = 5;


int mux4051OneInPin = 0;  
int threshold = 1000;
//int result_mux4051OneInPin[8] = {
//  0,0,0,0,0,0,0,0};
//////////////////////////////////////


//MAIN STRUCTURE//
int currState[8] = {};

int prevState[8] = {};

void setup(){
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  Tlc.init();
  Serial.begin(9600);
}

void loop(){
  for(int i=0; i<8; i++){
    digitalWrite(s0,bitRead(i, 0));
    digitalWrite(s1,bitRead(i, 1));
    digitalWrite(s2,bitRead(i, 2));

    LEDchannel = i;
    currState[i] = analogRead(mux4051OneInPin);
    if (currState[i] != prevState[i]){

      if(currState[i] > threshold){
        fade = true;
        fadeUp();
      } //if the button state is higher than 1000, fade the LED in

      else { // no contact
        fade = false;
        fadeDown();
      }// if the button state if lower than 1000, fade the LED out
    }

    else if (currState[i] == prevState[i]){      
    } //don't do anything


    if (currState[i] < 100){
      currState[i] = LOW;
    }

    else {
      currState[i] = HIGH;
    }
    prevState[i] = currState[i];
  }//main (bitshifting) for end
}//void end



void fadeUp(){
  switch(fadeUpmode){
  case 0:
    {
      if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
        if (!tlc_isFading(channel[LEDchannel])) {

          startMillis= millis() + 50;
          endMillis = startMillis + duration;
          tlc_addFade(channel[LEDchannel], 0, maxValue, startMillis, endMillis);
        }
      }
      tlc_updateFades(); 
      break;
    }
  }
}

void fadeDown(){
  switch(fadeDownmode){
  case 0:
    if (tlc_fadeBufferSize < TLC_FADE_BUFFER_LENGTH - 2) {
      if (!tlc_isFading(channel[LEDchannel])) {

        startMillis= millis() + 50;
        endMillis = startMillis + duration;
        tlc_addFade(channel[LEDchannel], maxValue, 0, startMillis, endMillis);
      }
    }
    tlc_updateFades(); 

    break;
  }

}//loop end

Each pushbutton has a 100ohm resitor.
The TLC has a 10kOhm resistor on the BLANK (connected to 5V on the other part) and a 2k2Ohm on the Tlc pin 20 connected to Gnd.

I feel really lost. If someone can help me please. This was supposed to be the “easy” part. :’(
Thank you everyone (expecially PaulS :slight_smile: )