Go Down

Topic: Serial Control of LEDs with TLC5940 Millis (Read 692 times) previous topic - next topic

loujar

Hi! I am having some issues with my project controlling LEDs connected to a TLC5940 from the serial port. I am trying to control two motors at a time independently using millis, but the Interval value I send does not appear to impact the time the LEDs are on for. When a new piece of Serial data is received the previous LED switches off and the new one switches on. There should be some overlap.

Also, if I try and set the duration for a short amount of time, the LED continues to be on. I think there is an issue in how I am processing the Serial data and my implementation of millis. Any advice appreciated.





Code: [Select]
#include "SparkFun_Tlc5940.h"

unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;

unsigned long interval1 = 0;
unsigned long interval2 = 0;

boolean changed;
byte val = 0, cnt = 0;
byte scKey[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

int pwmVal = 0;

void setup() {
  Serial.begin(57600);    //baudrate must match in sc
  Tlc.init();
  Tlc.setAll(0);

}
void loop() {

  //--from sc
  while (Serial.available() > 0) {
    val = Serial.read();
    if (cnt == 0) {
      if (val == 10) { //beginning of message found
        cnt = 1; //start counter
      }
    }
    else if (cnt < 4) { //between 1 and 6 means message started and that bytes should be saved
      scKey[cnt - 1] = val; //saving incoming bytes in temporary data array
      cnt++;
    }
    else {
      if (val == 11) {
        //set timer
        unsigned long currentMillis1 = millis();
        //one
        //Select motors
        int channel = scKey[0];

        //Set vibration amplitude
        int pwm = scKey[1];
        //int pwmConstrain = constrain(scKey[1], 50, 400);
        int pwmVal = map(pwm, 0, 255, 1000, 4500);

        //set up times

        int dur = scKey[2];
        interval1 = dur;
       
        Tlc.set(channel, pwmVal);
        Tlc.update();


        if (currentMillis1 - previousMillis1 >= interval1) {
          previousMillis1 = currentMillis1;
          Tlc.set(channel, 0);
          Tlc.update();
         

        }

      }

      else if (val == 13) {
        //set timer
        unsigned long currentMillis2 = millis();
        //one
        //Select motors
        int channel2 = scKey[0];

        //Set vibration amplitude
        int pwm = scKey[1];
        //int pwmConstrain = constrain(scKey[1], 50, 400);
        int pwmVal = map(pwm, 0, 255, 1000, 4500);

        //set up times

        int dur2 = scKey[2];
        interval2 = dur2;

        Tlc.set(channel2, pwmVal);
        Tlc.update();


        if (currentMillis2 - previousMillis2 >= interval2) {
          Tlc.set(channel2, 0);
          Tlc.update();
          previousMillis2 = currentMillis2;

        }

      }

      else {
        //serial read error
      }
      cnt = 0; //reset byte counter
    }
  }

  delay(10);  //wait 10 milliseconds
}




PaulRB

#1
Mar 25, 2017, 11:45 am Last Edit: Mar 25, 2017, 11:51 am by PaulRB
Your code only ever checks mills() when there is serial data available. So that is the only time changes occur. You need to be checking millis() when there is no serial data available.

loujar

Yes! That makes sense, thank you. I have amended the code as you have suggested. I am now able to control on ON time of each LED. I am still having a problem with them overlapping. It I send data to the Arduino like below, I find that when LED2 data is received LED1 is cut off i.e

LED1 ON 1000ms
WAIT 750ms

LED2 ON 1000ms
WAIT 750ms

Updated code below:

Code: [Select]
#include "SparkFun_Tlc5940.h"

unsigned long currentMillis1 = 0, previousMillis1 = 0, interval1 = 0;
unsigned long currentMillis2 = 0, previousMillis2 = 0, interval2 = 0;

boolean changed;
byte val = 0, cnt = 0;
byte scKey[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};


int channel1 = 0, pwm1 = 0, pwmVal1 = 0, dur1 = 0;
int channel2 = 0, pwm2 = 0, pwmVal2 = 0, dur2 = 0;

void setup() {
  Serial.begin(115200);    //baudrate must match in sc
  Tlc.init();
  Tlc.setAll(0);

}
void loop() {
  unsigned long currentMillis2 = millis();

  if (currentMillis1 - previousMillis1 >= interval1) {
    previousMillis1 = currentMillis1;
    Tlc.set(channel1, 0);
    Tlc.update();
  }


  if (currentMillis2 - previousMillis2 >= interval2) {
    previousMillis2 = currentMillis2;
    Tlc.set(channel2, 0);
    Tlc.update();
  }

  while (Serial.available() > 0) {
    val = Serial.read();
    if (cnt == 0) {
      if (val == 10) { //beginning of message found
        cnt = 1; //start counter
      }
    }
    else if (cnt < 4) { //between 1 and 3 should be saved
      scKey[cnt - 1] = val; //saving incoming bytes in temporary data array
      cnt++;
    }

    else {
      if (val == 11) {
        //set timer
        currentMillis1 = millis();

        //set channel
        channel1 = scKey[0];

        //Set vibration amplitude
        pwm1 = scKey[1];
        pwmVal1 = map(pwm1, 0, 255, 1000, 4000);

        //set up times
        dur1 = scKey[2];
        interval1 = dur1 * 100;

        Tlc.set(channel1, pwmVal1);
        Tlc.update();
      }

      else if (val == 13) {
        //set timer
        currentMillis1 = millis();

        //set channel
        channel2 = scKey[0];

        //Set vibration amplitude
        pwm2 = scKey[1];
        pwmVal2 = map(pwm2, 0, 255, 1000, 4500);

        //set up times

        dur2 = scKey[2];
        interval2 = dur2 * 100;

        Tlc.set(channel2, pwmVal2);
        Tlc.update();

      }

      else {


      }
      cnt = 0; //reset byte counter
    }
  }

}




PaulRB

Yes, you still have some debugging to do. Have a look at currentMillis1 and currentMillis2 and where they are assigned to and read. Do you really need both?

loujar

Yes! I have too many currentMillis.. everywhere... fixed! I only need the one at the start of the loop.

I am finding the timing and duration of the LED ON and OFF across the Serial very jittery. I also find that the first 3-4 pieces of Serial data sent to the Arduino are missed. Outside the checking that I am doing, is there a better way of managing Serial data into Arduino?

The data I am sending looks like this:

Code: [Select]
~port.putAll([10,  channel, pwm, duration, 11]);

Where channel is 0 - 12; pwm is 0 - 255; duration is between 0.5 and 4.

Updated code:

Code: [Select]
#include "SparkFun_Tlc5940.h"

unsigned long currentMillis = 0;
unsigned long previousMillis1 = 0, interval1 = 0;
unsigned long previousMillis2 = 0, interval2 = 0;

boolean changed;
byte val = 0, cnt = 0;
byte scKey[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};


int channel1 = 0, pwm1 = 0, pwmVal1 = 0, dur1 = 0;
int channel2 = 0, pwm2 = 0, pwmVal2 = 0, dur2 = 0;

void setup() {
  Serial.begin(115200);    //baudrate must match in sc
  Tlc.init();
  Tlc.setAll(0);

}
void loop() {
  currentMillis = millis();

  if (currentMillis - previousMillis1 >= interval1) {
    previousMillis1 = currentMillis;
    Tlc.set(channel1, 0);
    Tlc.update();
  }


  if (currentMillis - previousMillis2 >= interval2) {
    previousMillis2 = currentMillis;
    Tlc.set(channel2, 0);
    Tlc.update();
  }

  while (Serial.available() > 0) {
    val = Serial.read();
    if (cnt == 0) {
      if (val == 10) { //beginning of message found
        cnt = 1; //start counter
      }
    }
    else if (cnt < 4) { //between 1 and 3 should be saved
      scKey[cnt - 1] = val; //saving incoming bytes in temporary data array
      cnt++;
    }

    else {
      if (val == 11) {
        //set channel
        channel1 = scKey[0];

        //Set vibration amplitude
        pwm1 = scKey[1];
        pwmVal1 = map(pwm1, 0, 255, 1000, 4000);

        //set up times
        dur1 = scKey[2];
        interval1 = dur1 * 1000;

        Tlc.set(channel1, pwmVal1);
        Tlc.update();
      }

      else if (val == 13) {

        //set channel
        channel2 = scKey[0];

        //Set vibration amplitude
        pwm2 = scKey[1];
        pwmVal2 = map(pwm2, 0, 255, 1000, 4500);

        //set up times

        dur2 = scKey[2];
        interval2 = dur2 * 1000;

        Tlc.set(channel2, pwmVal2);
        Tlc.update();

      }

      else {


      }
      cnt = 0; //reset byte counter
    }
  }

}



PaulRB

No idea what that command you showed does!

When is the serial data missed? After the Arduino is powered up or reset?

loujar

That is the data that I am sending from Supercollider. The first number is an identifier, the second is the channel number. The third is PWM value, the 4th duration on and final is another identifier.

It loses 3-4 pieces of data at the start when I first connect the external software (SuperCollider) and then occasionally throughout the code running

PaulRB

#7
Mar 26, 2017, 03:31 pm Last Edit: Mar 26, 2017, 03:33 pm by PaulRB
It's possible that tlc.update() is blocking interrupts for long enough to miss some serial data. Will there be gaps in the serial data? If you can arrange your code so that tlc.update() is only performed during the gaps that might help. If you know there will be gaps after a certain number of bytes have been received then only use tlc.update() after the last byte. Also, set a flag in your code to indicate that a data packet is currently being received. Only check millis() and switch of pwm outputs when that flag is not set.

Also try lower baud rates if possible. That may help but might make things worse if it means that the gaps in serial data are shorter.

Go Up