// This code is 100% AI generated using GitHub Copilot
#include <FastLED.h>
#define NUM_LEDS 50
#define DATA_PIN 3
CRGB leds[NUM_LEDS];
const int trigPin = 4;
const int echoPin = 5;
const float SPEED_OF_SOUND_MM_PER_US = 0.34;
const float INCH_TO_MM = 25.4 * 7; //gwm scale *7 for wokwi range of 400cm
const int NUM_PATTERNS = 6;
const uint8_t WHITE_INSTEAD_OF_HUE = 0;
const int DIST_BRIGHT_MM = 2 * INCH_TO_MM;
const int DIST_DIM_MM = 7 * INCH_TO_MM;
const int HUE_LOW_MM = 9 * INCH_TO_MM;
const int HUE_HIGH_MM = 14 * INCH_TO_MM;
const int HUE_WHITE_MM = 14.5 * INCH_TO_MM;
const int PATTERN_FIRST_MM = 16 * INCH_TO_MM;
const int PATTERN_LAST_MM = 21 * INCH_TO_MM;
struct WINK_STRUCT {
uint8_t pixelNum;
uint8_t brightness;
int8_t direction;
CRGB color;
};
enum PatternActionEnum { Initialize, Run };
enum LightStateEnum { Off, On };
LightStateEnum LightState = Off;
uint8_t Brightness, Hue, Hue2, Pattern;
CRGB PrimaryColor, SecondaryColor;
bool PatternChanged = false;
void setup() {
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 1000);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.begin(9600); // gwm added
}
void loop() {
ReadDistance();
PrintInfo();
if (LightState == On) {
RunPattern(Run);
}
}
void ReadDistance() {
static long nextDistance;
long distance;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
nextDistance = duration * SPEED_OF_SOUND_MM_PER_US / 2;
distance = nextDistance; // this is to throw out the last in-range reading before going out-of-range
if (nextDistance > PATTERN_LAST_MM) {
distance = nextDistance;
}
if (distance < DIST_BRIGHT_MM) {
LightOff();
} else if (distance >= DIST_BRIGHT_MM && distance < DIST_DIM_MM) {
Brightness = map(distance, DIST_BRIGHT_MM, DIST_DIM_MM, 0, 255);
LightOn();
} else if (distance >= DIST_DIM_MM && distance < HUE_LOW_MM) {
LightOff();
} else if (distance >= HUE_LOW_MM && distance < HUE_HIGH_MM) {
Hue = map(distance, HUE_LOW_MM, HUE_HIGH_MM, 0, 255);
LightOn();
} else if (distance >= HUE_HIGH_MM && distance < HUE_WHITE_MM) {
Hue = WHITE_INSTEAD_OF_HUE;
Hue2 = Hue + 128; // It's okay to roll over
LightOn();
} else if (distance >= HUE_WHITE_MM && distance < PATTERN_FIRST_MM) {
LightOff();
} else if (distance >= PATTERN_FIRST_MM && distance <= PATTERN_LAST_MM) {
static uint8_t PreviousPattern = 255;
Pattern = map(distance, PATTERN_FIRST_MM, PATTERN_LAST_MM, 0, NUM_PATTERNS - 1);
if (Pattern != PreviousPattern) {
RunPattern(Initialize);
PreviousPattern = Pattern;
}
LightOn();
}
}
void PrintInfo() {
EVERY_N_MILLISECONDS(500) {
Serial.print("LightState: ");
Serial.print(LightState);
Serial.print(", Brightness: ");
Serial.print(Brightness);
Serial.print(", Hue: ");
Serial.print(Hue);
Serial.print(", Pattern: ");
Serial.println(Pattern);
}
}
void LightOff() {
if (LightState == On) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
LightState = Off;
}
}
void LightOn() {
LightState = On;
PrimaryColor = CHSV(Hue, (Hue == WHITE_INSTEAD_OF_HUE) ? 0 : 255, Brightness);
SecondaryColor = CHSV(Hue2, (Hue == WHITE_INSTEAD_OF_HUE) ? 0 : 255, Brightness);
}
void RunPattern(PatternActionEnum patternAction) {
switch (Pattern) {
case 0:
SolidLightPattern(patternAction);
break;
case 1:
RandomDotPattern(patternAction);
break;
case 2:
RandomMulticolorDotPattern(patternAction);
break;
case 3:
WinkOnOffPattern(patternAction);
break;
case 4:
ChaseWormsPattern(patternAction);
break;
default:
SolidLightPattern(patternAction);
break;
}
}
void SolidLightPattern(PatternActionEnum patternAction) {
EVERY_N_MILLISECONDS(50) {
fill_solid(leds, NUM_LEDS, PrimaryColor);
FastLED.show();
}
}
void RandomDotPattern(PatternActionEnum patternAction) {
EVERY_N_MILLISECONDS(10) {
int pixel = random(NUM_LEDS);
leds[pixel] = PrimaryColor;
FastLED.show();
leds[pixel] = CRGB::Black;
}
}
void RandomMulticolorDotPattern(PatternActionEnum patternAction) {
EVERY_N_MILLISECONDS(10) {
leds[random(NUM_LEDS)] = CHSV(random(255), random(50, 255), Brightness);
FastLED.show();
}
}
void WinkOnOffPattern(PatternActionEnum patternAction) {
static const int WINK_SIZE = 6;
static WINK_STRUCT winkArray[WINK_SIZE];
if (patternAction == Initialize) {
for (int i = 0; i < WINK_SIZE; ++i) {
winkArray[i].brightness = random(255);
winkArray[i].pixelNum = random(NUM_LEDS);
winkArray[i].direction = +1;
winkArray[i].color = random(0, 2) ? PrimaryColor : SecondaryColor;
}
}
EVERY_N_MILLISECONDS(80) {
for (int i = 0; i < WINK_SIZE; ++i) {
leds[winkArray[i].pixelNum] = winkArray[i].color;
leds[winkArray[i].pixelNum].fadeToBlackBy(255 - winkArray[i].brightness);
winkArray[i].brightness += winkArray[i].direction;
if (winkArray[i].direction == -1 && winkArray[i].brightness == 0) {
winkArray[i].direction = +1;
winkArray[i].pixelNum = random(NUM_LEDS);
}
if (winkArray[i].brightness == 255) {
winkArray[i].direction = -1;
}
}
FastLED.show();
}
}
void ChaseWormsPattern(PatternActionEnum patternAction) {
static uint8_t worm1[8] = {0};
static uint8_t worm2[4] = {0};
static bool movement = false;
EVERY_N_MILLISECONDS(80) {
movement = true;
for (int i = 7; i > 0; --i) {
worm1[i] = worm1[i - 1];
}
worm1[0] = worm1[1] + 1;
if (worm1[0] >= NUM_LEDS) worm1[0] = 0;
}
EVERY_N_MILLISECONDS(45) {
movement = true;
for (int i = 3; i > 0; --i) {
worm2[i] = worm2[i - 1];
}
worm2[0] = worm2[1] - 1;
if (worm2[0] >= NUM_LEDS) worm2[0] = NUM_LEDS - 1; // Using >= to handle uint8_t underflow
}
if (movement) {
for (uint8_t i : worm1) {
leds[i] = PrimaryColor;
}
for (uint8_t i : worm2) {
leds[i] = SecondaryColor;
}
FastLED.show();
fill_solid(leds, NUM_LEDS, CRGB::Black); // Clear after showing
movement = false;
}
}