Overview
The project is to have a user set the behavior of LEDs from five different "modes" using a rotary encoder.
Issue(s)
There are two issues: (1) the LEDs flicker at three "transition points" when the user changes the color of the LEDs (Mode 1) and (2) a single LED at the beginning of the LED strands remains lit up white while the user "moves" the red LED along the strand (Mode 3 & 4).
Topic
How can the project be modified so the LEDs no longer flicker so the colors change smoothly when the user uses the encoder and so the single LED at the beginning of the LED strands is not lit up while the user "moves" the red LED along the strand?
Hardware
2x WS2801 LED strands of 35, 12mm
Arduino Mega 2560
EN11 Rotary Encoder
5V = 10A 50W Adapter
Connector Terminal
Jumper Wires
Code
#include <Encoder.h>
#include <Adafruit_WS2801.h>
const int NUM_LEDS = 50; // number of leds in strand
const int DATA_PIN = 11; // data pin for WS2801 strand
const int CLOCK_PIN = 13; // clock pin for WS2801 strand
const int BRIGHTNESS = 255; // brightness of all leds
const int WHEEL_SIZE = 256; // how many entries in the color wheel
const boolean MOVE_LIGHT = false; // move one light around or keep all lights on
const int ENCODER_PIN_1 = 2;
const int ENCODER_PIN_2 = 3;
const int ENCODER_BUTTON = 4; // Button pin for the encoder
const int MIN_LED_INDEX = 1; // Define the range of LEDs
const int MAX_LED_INDEX = 50;
bool rainbowDisplayed = false; // Global variable to track if rainbow pattern has been displayed
Adafruit_WS2801 strip = Adafruit_WS2801(NUM_LEDS, DATA_PIN, CLOCK_PIN);
Encoder encoder(ENCODER_PIN_1, ENCODER_PIN_2);
int mode = 0;
long lastPush = 0;
int autoPosition = 0;
int position = 0; // Current position of the LED
int encoderPos = encoder.read(); // Read encoder position
int ledIndex = map(encoderPos, 0, 1023, MIN_LED_INDEX, MAX_LED_INDEX); // Map encoder position to LED index
// Define a threshold for encoder position change
const int ENCODER_THRESHOLD = 5; // Adjust as needed
// Variable to store the previous encoder position
int prevEncoderPos = 0;
// Function prototype for interpolateColor
uint32_t interpolateColor(uint32_t startColor, uint32_t endColor, int step, int totalSteps);
void initializeToBlack() {
for (int i =0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, 0);
}
}
void setup() {
Serial.begin(9600);
// Print a test message to the serial monitor
//Serial.println("Test message");
pinMode(ENCODER_BUTTON, INPUT);
digitalWrite(ENCODER_BUTTON, HIGH); //turn pullup resistor on
strip.begin();
initializeToBlack();
strip.show();
}
long normalize(long value, long radix) {
long rval = value % radix;
if (rval < 0) return radix + rval;
else return rval;
}
void loop() {
// Read button state
int buttonState = digitalRead(ENCODER_BUTTON);
// Change mode only when the button is pressed
if (buttonState == LOW && millis() - lastPush > 500) {
// Increment mode cyclically
mode = (mode + 1) % 5;
lastPush = millis(); // Record the time of the last button press
// Reset any mode-specific flags or variables here
rainbowDisplayed = false; // Reset the flag for rainbow pattern
// If the mode is 2 (display rainbow pattern), set the flag to true
if (mode == 2) {
displayRainbowPattern(); // Display the rainbow pattern
rainbowDisplayed = true; // Set the flag to true after the rainbow pattern is displayed
}
}
// Read encoder position
int encoderPos = encoder.read();
// Check if the absolute difference between current and previous positions exceeds the threshold
if (abs(encoderPos - prevEncoderPos) >= ENCODER_THRESHOLD) {
// Update LED color only if the threshold is exceeded
// Your LED color update code here
// For example:
// setLedColorBasedOnEncoder();
// Update prevEncoderPos to current position
prevEncoderPos = encoderPos;
}
// Turn off all LEDs
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, 0, 0, 0); // Set LED color to black
}
// Set the color of the LED corresponding to the encoder position
strip.setPixelColor(ledIndex, 255, 255, 255); // Set LED color to white
/* // Print current mode
Serial.print("Mode: ");
Serial.println(mode);
// Add debug statement
Serial.println("Calling displayRainbowPattern()"); */
// Handle different modes
switch (mode) {
case 0:
// Mode 0: Turn off all LEDs
initializeToBlack();
break;
case 1:
// Mode 1: Control LED color with rotary encoder
// Implement code to set LED color based on encoder position
// For example:
setLedColorBasedOnEncoder();
break;
case 2:
// Mode 2: Display rainbow pattern on all LEDs
displayRainbowPattern();
break;
case 3:
// Mode 3: Move one LED along the strip
// Implement code to move LED along the strip
// For example:
moveSingleLED();
break;
case 4:
// Mode 4: Move multiple LEDs along the strip
// Implement code to move multiple LEDs along the strip
// For example:
// moveMultipleLEDs();
break;
default:
// Handle unknown mode
break;
}
// Show the updated LED colors
strip.show();
/* // Print out the encoder reading
Serial.print("Encoder: ");
Serial.println(encoder.read());
// Print out the state of the encoder button
Serial.print("Button state: ");
Serial.println(digitalRead(ENCODER_BUTTON));
// Delay to avoid flooding the serial monitor
delay(500); // Adjust the delay as needed */
// Remaining loop code remains unchanged
}
// Function declaration for colorWheel()
void colorWheel(byte wheelPos, uint16_t pixelIndex) {
if (wheelPos < 85) {
strip.setPixelColor(pixelIndex, wheelPos * 3, 255 - wheelPos * 3, 0);
} else if (wheelPos < 170) {
wheelPos -= 85;
strip.setPixelColor(pixelIndex, 255 - wheelPos * 3, 0, wheelPos * 3);
} else {
wheelPos -= 170;
strip.setPixelColor(pixelIndex, 0, wheelPos * 3, 255 - wheelPos * 3);
}
}
// fill the dots one after the other with said color
// good for testing purposes
void colorWipe(uint32_t c, uint8_t wait) {
int i;
for (i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c); // Set color for each pixel
}
strip.show(); // Show the updated LED colors
delay(wait); // Delay for the specified time
}
uint32_t hsvToRgb(uint8_t hue, uint8_t saturation, uint8_t value) {
byte r, g, b;
unsigned char region, remainder, p, q, t;
if (saturation == 0) {
r = g = b = value;
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
region = hue / 43;
remainder = (hue - (region * 43)) * 6;
p = (value * (255 - saturation)) >> 8;
q = (value * (255 - ((saturation * remainder) >> 8))) >> 8;
t = (value * (255 - ((saturation * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0:
r = value; g = t; b = p;
break;
case 1:
r = q; g = value; b = p;
break;
case 2:
r = p; g = value; b = t;
break;
case 3:
r = p; g = q; b = value;
break;
case 4:
r = t; g = p; b = value;
break;
default:
r = value; g = p; b = q;
break;
}
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
// Function definition for displayRainbowPattern()
void displayRainbowPattern() {
for (int i = 0; i < NUM_LEDS; i++) {
// Calculate color based on position in the rainbow
long colorValue = normalize(i * 5, WHEEL_SIZE);
// Set LED color
colorWheel(colorValue, i); // Corrected line
// Debugging output
/* Serial.print("Setting LED ");
Serial.print(i);
Serial.print(" to color (R, G, B): ");
Serial.print(strip.getPixelColor(i) >> 16 & 0xFF); // Red
Serial.print(", ");
Serial.print(strip.getPixelColor(i) >> 8 & 0xFF); // Green
Serial.print(", ");
Serial.println(strip.getPixelColor(i) & 0xFF); // Blue */
}
}
// Function definition for setLedColorBasedOnEncoder()
void setLedColorBasedOnEncoder() {
// Read encoder position
int encoderPos = encoder.read();
// Map encoder position to hue (0-255)
byte hue = map(encoderPos, 0, 1023, 0, 255);
// Convert HSV to RGB for starting and ending colors
uint32_t startColor = hsvToRgb(hue, 255, 255);
uint32_t endColor = hsvToRgb(hue + 1, 255, 255); // Increment hue slightly for smooth transition
uint32_t color = hsvToRgb(hue, 255, 255);
// Smoothly transition from startColor to endColor
for (int i = 0; i < NUM_LEDS; i++) {
// Calculate intermediate color
uint32_t intermediateColor = interpolateColor(startColor, endColor, i, NUM_LEDS);
// Set LED color based on intermediate color
strip.setPixelColor(i, intermediateColor);
}
}
void moveSingleLED() {
// Read encoder position
int encoderDelta = encoder.read(); // Read the encoder position change
// Calculate the new position for the LED based on the encoder movement
position += encoderDelta;
// Constrain the position within the LED strip boundaries
position = constrain(position, 0, NUM_LEDS - 1);
// Illuminate the LED at the new position
strip.setPixelColor(position, 255, 0, 0);
// Update the color of the second LED
if (position > 0 && position < NUM_LEDS) {
// Read the color of the first LED
uint32_t color = strip.getPixelColor(position - 1);
// Set the color of the second LED to match the color of the first LED
strip.setPixelColor(position - 1, color);
} else if (position == 0) {
// If the second LED is at the start of the strip, turn it off
strip.setPixelColor(position, 0, 0, 0);
}
// Show the updated LED colors
strip.show();
// Reset encoder position to zero after each movement
encoder.write(0);
}
// Function definition for interpolateColor()
uint32_t interpolateColor(uint32_t startColor, uint32_t endColor, int step, int totalSteps) {
// Extract RGB components of startColor
byte startRed = (startColor >> 16) & 0xFF;
byte startGreen = (startColor >> 8) & 0xFF;
byte startBlue = startColor & 0xFF;
// Extract RGB components of endColor
byte endRed = (endColor >> 16) & 0xFF;
byte endGreen = (endColor >> 8) & 0xFF;
byte endBlue = endColor & 0xFF;
// Interpolate RGB components
byte interpolatedRed = startRed + ((endRed - startRed) * step / totalSteps);
byte interpolatedGreen = startGreen + ((endGreen - startGreen) * step / totalSteps);
byte interpolatedBlue = startBlue + ((endBlue - startBlue) * step / totalSteps);
// Combine interpolated RGB components into a single color
return ((uint32_t)interpolatedRed << 16) | ((uint32_t)interpolatedGreen << 8) | interpolatedBlue;
}