LED code and receiving Serial seem to clash

My Arduino is reading information from my PC every second, but I also want a RGB LED to slowly fade through all the colours without being disrupted. But my Arduino cannot miss a reading either.
The PC triggers the reading, otherwise the Arduino won't look for a reading.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f, 16, 2);  // Adjust the I2C address if needed

unsigned long lastDataReceivedTime = 0;
const unsigned long backlightTimeout = 10000;  // 10 seconds in milliseconds

// Define the pins for the RGB LED
const int redPin = 11;    // Red LED connected to digital pin 9
const int greenPin = 9; // Green LED connected to digital pin 10
const int bluePin = 10;  // Blue LED connected to digital pin 11

// Delay between color transitions
const int transitionDelay = 10; // Adjust this for the speed of the transition
unsigned long lastLEDTime = 0;

void setup() {
  // Initialize the RGB pins as OUTPUT
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  digitalWrite(redPin, HIGH);
  digitalWrite(greenPin, HIGH);
  digitalWrite(bluePin, HIGH);
  lcd.init();
  lcd.clear();
  Serial.begin(9600);
}

void loop() {
  if (millis() - lastLEDTime >= transitionDelay) {
    fadeFromToColor(0, 255, 255, 255, 0, 255); // Fade from Red to Green
    fadeFromToColor(255, 0, 255, 255, 255, 0); // Fade from Green to Blue
    fadeFromToColor(255, 255, 0, 0, 255, 255); // Fade from Blue to Red
    lastLEDTime = millis();
  }
  if (Serial.available() > 0) {
    lastDataReceivedTime = millis();  // Reset the timer
    String cpuUsage = Serial.readStringUntil('\n');
    String ramUsage = Serial.readStringUntil('\n');
    String timeLog = Serial.readStringUntil('\n');

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(cpuUsage);

    lcd.setCursor(0, 1);
    lcd.print(ramUsage);
  }
  //else {
  int sensorValue = analogRead(A0);
  if (sensorValue < 300) {
    lcd.noBacklight();
  }
  else {
    if (millis() - lastDataReceivedTime >= backlightTimeout) {
      lcd.noBacklight();  // Turn off the backlight
    }
    else {
      lcd.backlight();
    }
  }
}

// Function to smoothly fade between two colors
void fadeFromToColor(int startRed, int startGreen, int startBlue, int endRed, int endGreen, int endBlue) {
  for (int i = 0; i <= 255; i++) {
    int redValue = map(i, 0, 255, startRed, endRed);
    int greenValue = map(i, 0, 255, startGreen, endGreen);
    int blueValue = map(i, 0, 255, startBlue, endBlue);

    analogWrite(redPin, redValue);
    analogWrite(greenPin, greenValue);
    analogWrite(bluePin, blueValue);

    //delay(transitionDelay);
  }
}

With that code the reading gets scrambled, and the LED goes through all the colours in a blink of an eye. I'm really lost with this.

This is a video for those interested:
386450645_6763448027075666_1721594536350718734_n.zip (985.9 KB)

You need a millis() based approach in fadeFromToColor. And call that repeatedly from loop().

Right, you have to add a delay between each step in fadeFromToColor(). Unfortunately the indicated solution

   //delay(transitionDelay);

will block all other operations, like receiving data on Serial.

Take BlinkWithoutDelay as a pattern for altering the LED settings without blocking other code. A look at state machines may help as well.

Should the millis function be inside or outside the for loop?

You have to replace that loop by a counter in loop() that is incremented every millis() interval.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f, 16, 2);  // Adjust the I2C address if needed

unsigned long lastDataReceivedTime = 0;
const unsigned long backlightTimeout = 10000;  // 10 seconds in milliseconds

// Define the pins for the RGB LED
const int redPin = 11;    // Red LED connected to digital pin 11
const int greenPin = 9;   // Green LED connected to digital pin 9
const int bluePin = 10;   // Blue LED connected to digital pin 10

// Duration for each color transition in milliseconds
const unsigned long transitionDuration = 1500; // 2 seconds per transition

unsigned long startTime = 0; // Time when the transition started
int startRed, startGreen, startBlue; // Starting color values
int endRed, endGreen, endBlue; // Ending color values

enum ColorTransitionState {
  RED_TO_GREEN,
  GREEN_TO_BLUE,
  BLUE_TO_RED
};

ColorTransitionState currentTransitionState = RED_TO_GREEN;
unsigned long transitionStartTime = 0;

bool isCPUFirst = true;

void setup() {
  // Initialize the RGB pins as OUTPUT
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  digitalWrite(redPin, HIGH);
  digitalWrite(greenPin, HIGH);
  digitalWrite(bluePin, HIGH);
  lcd.init();
  lcd.clear();
  Serial.begin(9600);
}

void loop() {
  ledloop();
  
  if (Serial.available() > 0) {
    lastDataReceivedTime = millis();  // Reset the timer
    String cpuUsage = Serial.readStringUntil('\n');
    String ramUsage = Serial.readStringUntil('\n');
    String timeLog = Serial.readStringUntil('\n');

    lcd.clear();
    lcd.setCursor(0, 0);

    if (isCPUFirst) {
      lcd.print(cpuUsage);
      lcd.setCursor(0, 1);
      lcd.print(ramUsage);
    } else {
      lcd.print(ramUsage);
      lcd.setCursor(0, 1);
      lcd.print(cpuUsage);
    }

    isCPUFirst = !isCPUFirst;
  }

  int sensorValue = analogRead(A0);
  if (sensorValue < 300) {
    lcd.noBacklight();
  } else {
    if (millis() - lastDataReceivedTime >= backlightTimeout) {
      lcd.noBacklight();  // Turn off the backlight
    } else {
      lcd.backlight();
    }
  }
}

void ledloop() {
  unsigned long currentTime = millis();

  if (currentTransitionState == RED_TO_GREEN &&
      currentTime - transitionStartTime >= transitionDuration) {
    currentTransitionState = GREEN_TO_BLUE;
    transitionStartTime = currentTime;
  } else if (currentTransitionState == GREEN_TO_BLUE &&
             currentTime - transitionStartTime >= transitionDuration) {
    currentTransitionState = BLUE_TO_RED;
    transitionStartTime = currentTime;
  } else if (currentTransitionState == BLUE_TO_RED &&
             currentTime - transitionStartTime >= transitionDuration) {
    currentTransitionState = RED_TO_GREEN;
    transitionStartTime = currentTime;
  }

  switch (currentTransitionState) {
    case RED_TO_GREEN:
      startRed = 0; startGreen = 255; startBlue = 255;
      endRed = 255; endGreen = 0; endBlue = 255;
      break;
    case GREEN_TO_BLUE:
      startRed = 255; startGreen = 0; startBlue = 255;
      endRed = 255; endGreen = 255; endBlue = 0;
      break;
    case BLUE_TO_RED:
      startRed = 255; startGreen = 255; startBlue = 0;
      endRed = 0; endGreen = 255; endBlue = 255;
      break;
  }

  float progress = (currentTime - transitionStartTime) / (float)transitionDuration;
  int redValue = startRed + (endRed - startRed) * progress;
  int greenValue = startGreen + (endGreen - startGreen) * progress;
  int blueValue = startBlue + (endBlue - startBlue) * progress;

  analogWrite(redPin, redValue);
  analogWrite(greenPin, greenValue);
  analogWrite(bluePin, blueValue);
}

Ok....
Serial is now steady, but the LED is very jerky.

This will start reading from Serial with the first character and will take until all requested characters are received. During that time no other code will run. Better wait with input processing until all characters are available.

If you don't know in advance how many characters have to be read then make up your own input handling, e.g. checking for '\n' in the Serial buffer before you decode the next input line.

Never done anything like this, is there a webpage you could point me to to learn about that?

For serial communication, you can study Robin's Serial Input Basics - updated tutorial.

2 Likes

Thanks every one, this is now working!

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f, 16, 2);  // Adjust the I2C address if needed

unsigned long lastDataReceivedTime = 0;
const unsigned long backlightTimeout = 10000;  // 10 seconds in milliseconds

// Define the pins for the RGB LED
const int redPin = 11;    // Red LED connected to digital pin 11
const int greenPin = 9;   // Green LED connected to digital pin 9
const int bluePin = 10;   // Blue LED connected to digital pin 10

// Duration for each color transition in milliseconds
const unsigned long transitionDuration = 1500; // 2 seconds per transition

unsigned long startTime = 0; // Time when the transition started
int startRed, startGreen, startBlue; // Starting color values
int endRed, endGreen, endBlue; // Ending color values

enum ColorTransitionState {
  RED_TO_GREEN,
  GREEN_TO_BLUE,
  BLUE_TO_RED
};

ColorTransitionState currentTransitionState = RED_TO_GREEN;
unsigned long transitionStartTime = 0;

String cpuUsage;
String ramUsage;
bool isCPURamDataReceived = false;

void setup() {
  // Initialize the RGB pins as OUTPUT
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  digitalWrite(redPin, HIGH);
  digitalWrite(greenPin, HIGH);
  digitalWrite(bluePin, HIGH);
  lcd.init();
  lcd.clear();
  Serial.begin(9600);
}

void loop() {
  ledloop();

  // Non-blocking Serial data reception
  while (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '\n') {
      processSerialData();
    } else {
      if (!isCPURamDataReceived) {
        // Append the characters to the CPU data string
        cpuUsage += c;
      } else {
        // Append the characters to the RAM data string
        ramUsage += c;
      }
    }
  }

  if (millis() - lastDataReceivedTime >= backlightTimeout) {
    lcd.noBacklight();  // Turn off the backlight
  } else {
    lcd.backlight();
  }
}

void processSerialData() {
  if (!isCPURamDataReceived) {
    // CPU data is received, switch to RAM data
    isCPURamDataReceived = true;
  } else {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(cpuUsage);
    lcd.setCursor(0, 1);
    lcd.print(ramUsage);

    // Reset data variables
    cpuUsage = "";
    ramUsage = "";
    isCPURamDataReceived = false;
  }

  lastDataReceivedTime = millis();
}

void ledloop() {
  unsigned long currentTime = millis();

  if (currentTransitionState == RED_TO_GREEN &&
      currentTime - transitionStartTime >= transitionDuration) {
    currentTransitionState = GREEN_TO_BLUE;
    transitionStartTime = currentTime;
  } else if (currentTransitionState == GREEN_TO_BLUE &&
             currentTime - transitionStartTime >= transitionDuration) {
    currentTransitionState = BLUE_TO_RED;
    transitionStartTime = currentTime;
  } else if (currentTransitionState == BLUE_TO_RED &&
             currentTime - transitionStartTime >= transitionDuration) {
    currentTransitionState = RED_TO_GREEN;
    transitionStartTime = currentTime;
  }

  switch (currentTransitionState) {
    case RED_TO_GREEN:
      startRed = 0; startGreen = 255; startBlue = 255;
      endRed = 255; endGreen = 0; endBlue = 255;
      break;
    case GREEN_TO_BLUE:
      startRed = 255; startGreen = 0; startBlue = 255;
      endRed = 255; endGreen = 255; endBlue = 0;
      break;
    case BLUE_TO_RED:
      startRed = 255; startGreen = 255; startBlue = 0;
      endRed = 0; endGreen = 255; endBlue = 255;
      break;
  }

  float progress = (currentTime - transitionStartTime) / (float)transitionDuration;
  int redValue = startRed + (endRed - startRed) * progress;
  int greenValue = startGreen + (endGreen - startGreen) * progress;
  int blueValue = startBlue + (endBlue - startBlue) * progress;

  analogWrite(redPin, redValue);
  analogWrite(greenPin, greenValue);
  analogWrite(bluePin, blueValue);
}
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.