0% found this document useful (0 votes)
3 views17 pages

Fast Led

The document is a C++ program for simulating a traffic light system with cars using the FastLED library. It includes definitions for traffic light pins, car properties, and traffic light phases, as well as functions for car movement, collision detection, and traffic light control. The program allows for dynamic adjustments of traffic light durations and car spawn rates based on user input and queue sizes.

Uploaded by

NOUSSAIBA OMANI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views17 pages

Fast Led

The document is a C++ program for simulating a traffic light system with cars using the FastLED library. It includes definitions for traffic light pins, car properties, and traffic light phases, as well as functions for car movement, collision detection, and traffic light control. The program allows for dynamic adjustments of traffic light durations and car spawn rates based on user input and queue sizes.

Uploaded by

NOUSSAIBA OMANI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

#include <FastLED.

h>

#define LED_PIN 3
#define NUM_LEDS 265
#define CAR_LENGTH 3
#define MAX_CARS 10

// Traffic Light Pins for Lanes 1 & 2


#define RED_LIGHT_PIN 8
#define GREEN_LIGHT_PIN 9

// Traffic Light Pins for Lanes 3 & 4


#define RED_LIGHT_PIN2 7
#define GREEN_LIGHT_PIN2 6

CRGB leds[NUM_LEDS];
int carPositions[MAX_CARS];
CRGB carColors[MAX_CARS];
int carLanes[MAX_CARS]; // To track which lane each car is in
int numCars = 0;

unsigned long spawnTimer = 0;


unsigned long spawnInterval = 2000;

// Traffic Light State and Timing


// 0: Main (1&2) GREEN, Side (3&4) RED
// 1: ALL RED (Intersection Clear)
// 2: Main (1&2) RED, Side (3&4) GREEN
int trafficLightPhase = 0;
unsigned long trafficLightTimer = 0;
unsigned long mainGreenDuration = 5000; // Duration when Main (1&2) is
GREEN
unsigned long sideGreenDuration = 5000; // Duration when Side (3&4) is
GREEN
unsigned long allRedDuration = 2000; // Duration when ALL are RED

// Stop Zones for straight traffic at red light


int lane1StopZoneStart = 22;
int lane1StopZoneEnd = 25;
int lane2StopZoneStart = 89;
int lane2StopZoneEnd = 92;
int lane3StopZoneStart = 218; // Adjust as needed
int lane3StopZoneEnd = 221; // Adjust as needed
int lane4StopZoneStart = 155; // Adjust as needed
int lane4StopZoneEnd = 158; // Adjust as needed
// Turn Decision Points (where a car chooses to turn)
int lane1RightTurnDecision = 28;
int lane1LeftTurnDecision = 36;
int lane2RightTurnDecision = 95;
int lane2LeftTurnDecision = 103;
int lane3RightTurnDecision = 224;
int lane3LeftTurnDecision = 232;
int lane4RightTurnDecision = 161;
int lane4LeftTurnDecision = 169;

// Turn Start Paths (where the car appears after turning)


int lane1RightTurnStart = 170;
int lane1LeftTurnStart = 225;
int lane2RightTurnStart = 233;
int lane2LeftTurnStart = 162;
int lane3RightTurnStart = 38;
int lane3LeftTurnStart = 96;
int lane4RightTurnStart = 105;
int lane4LeftTurnStart = 30;

// Intersection Wait Points (where left-turning cars stop to yield)


int lane1LeftWaitPoint = 231; // LED where Lane 1 left turn waits before
crossing Lane 3's path
int lane2LeftWaitPoint = 168; // LED where Lane 2 left turn waits before
crossing Lane 1's path
int lane3LeftWaitPoint = 102;
int lane4LeftWaitPoint = 35;

// End of Lanes (for car removal)


int Lane1End = 127;
int Lane2End = 193;
int Lane3End = 63; // This will be the END of lane 3, starting from 199
and going positive.
int Lane4End = 258;

// Array to store previous car positions for clearing LEDs


int previousCarPositions[MAX_CARS];

// Array to track turn state (0: no turn, 1: turning)


int turnState[MAX_CARS];

// Flag for clearing LEDs at the decision point after a turn


int turnClear[MAX_CARS];

// Buttons
#define BTN_SLOW 4
#define BTN_FAST 5

#define BTN_MAIN_UP 10
#define BTN_MAIN_DOWN 11
#define BTN_SIDE_UP 12
#define BTN_SIDE_DOWN 13

#define BTN_SPAWN_FASTER A0
#define BTN_SPAWN_SLOWER A1

#define BTN_AUTO_TOGGLE A3

bool autoTimingEnabled = false;

unsigned long dynamicMainDuration = 0;


unsigned long dynamicSideDuration = 0;

int simulationDelay = 100; // Default delay (ms)


const int minDelay = 20; // Fastest simulation
const int maxDelay = 500; // Slowest simulation

const unsigned long minGreen = 2000;


const unsigned long maxGreen = 10000;

unsigned long lastButtonPress = 0;


const unsigned long debounceTime = 200; // Debounce delay in ms

const unsigned long minSpawnInterval = 500;


const unsigned long maxSpawnInterval = 8000;

unsigned long carsClearedCounter = 0;


unsigned long lastStatsPrint = 0;
const unsigned long statsInterval = 5000; // 5 seconds

// Adjust dynamic durations based on queue sizes


const unsigned long baseGreen = 2000;
const unsigned long greenPerCar = 1000;
const unsigned long autoMinGreen = 3000;
const unsigned long autoMaxGreen = 10000;

// moveCar function: Handles individual car movement, collision, and


rendering
// turnStart parameter: -1 means straight movement, otherwise it's the
start LED of a turn path
bool moveCar(int position, CRGB color, bool stopAtRed, int turnStart, int
previousPosition, bool turningLeft, int waitPoint) {
bool collision = false;
if (!stopAtRed) { // Only check for collisions if the car is *trying* to
move
for (int j = 0; j < numCars; j++) {
if (j != position) { // Don't check collision with itself
// Check if car 'j' is in the space immediately in front of
'position'
if (carPositions[j] >= position + CAR_LENGTH && carPositions[j] <
position + CAR_LENGTH + CAR_LENGTH) {
collision = true;
break;
}
}
}
}

if (!collision) {
// Clear LEDs at the previous position (for straight movement)
if (turnStart == -1) { // If it's a straight segment
for (int i = 0; i < CAR_LENGTH; i++) {
if (previousPosition + i < NUM_LEDS && previousPosition + i >= 0) {
leds[previousPosition + i] = CRGB::Black;
}
}
}

// Set LEDs at the new position


if (turnStart != -1) {
// Turn movement (car has just started or is on a turn path)
for (int i = 0; i < CAR_LENGTH; i++) {
if (turnStart + i < NUM_LEDS) {
leds[turnStart + i] = color;
}
}
} else {
// Straight movement
for (int i = 0; i < CAR_LENGTH; i++) {
if (position + i < NUM_LEDS) {
leds[position + i] = color;
}
}
}
return true; // Car moved
} else {
return false; // Car didn't move
}
}

int countCarsBehind(int lane, int frontPos, int margin = 20) {


int count = 0;
for (int i = 0; i < numCars; i++) {
if (carLanes[i] == lane && carPositions[i] < frontPos &&
carPositions[i] >= frontPos - margin) {
count++;
}
}
return count;
}

void setup() {
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.clear();
FastLED.setBrightness(255); // Set brightness here (adjust as needed)
FastLED.show();

pinMode(BTN_SLOW, INPUT_PULLUP);
pinMode(BTN_FAST, INPUT_PULLUP);

pinMode(BTN_MAIN_UP, INPUT_PULLUP);
pinMode(BTN_MAIN_DOWN, INPUT_PULLUP);
pinMode(BTN_SIDE_UP, INPUT_PULLUP);
pinMode(BTN_SIDE_DOWN, INPUT_PULLUP);

pinMode(BTN_SPAWN_FASTER, INPUT_PULLUP);
pinMode(BTN_SPAWN_SLOWER, INPUT_PULLUP);

pinMode(BTN_AUTO_TOGGLE, INPUT_PULLUP);

pinMode(RED_LIGHT_PIN, OUTPUT);
pinMode(GREEN_LIGHT_PIN, OUTPUT);
pinMode(RED_LIGHT_PIN2, OUTPUT);
pinMode(GREEN_LIGHT_PIN2, OUTPUT);

// Initial light state: Main (1&2) GREEN, Side (3&4) RED


digitalWrite(RED_LIGHT_PIN, LOW);
digitalWrite(GREEN_LIGHT_PIN, HIGH);
digitalWrite(RED_LIGHT_PIN2, HIGH);
digitalWrite(GREEN_LIGHT_PIN2, LOW);

trafficLightTimer = millis();
// Initialize previous car positions
for (int i = 0; i < MAX_CARS; i++) {
previousCarPositions[i] = -1; // -1 indicates no previous position
turnState[i] = 0; // No turn initially
turnClear[i] = 0; // No clear initially
carLanes[i] = 0; // Initialize car lanes
}
}

void loop() {
if (autoTimingEnabled) {
int mainQueue = 0;
int sideQueue = 0;

// LANE 1
for (int i = 0; i < numCars; i++) {
if (carLanes[i] == 1 && carPositions[i] >= lane1StopZoneStart &&
carPositions[i] <= lane1StopZoneEnd) {
mainQueue += 1 + countCarsBehind(1, carPositions[i]);
break;
}
}

// LANE 2
for (int i = 0; i < numCars; i++) {
if (carLanes[i] == 2 && carPositions[i] >= lane2StopZoneStart &&
carPositions[i] <= lane2StopZoneEnd) {
mainQueue += 1 + countCarsBehind(2, carPositions[i]);
break;
}
}

// LANE 3
for (int i = 0; i < numCars; i++) {
if (carLanes[i] == 3 && carPositions[i] >= lane3StopZoneStart &&
carPositions[i] <= lane3StopZoneEnd) {
sideQueue += 1 + countCarsBehind(3, carPositions[i]);
break;
}
}

// LANE 4
for (int i = 0; i < numCars; i++) {
if (carLanes[i] == 4 && carPositions[i] >= lane4StopZoneStart &&
carPositions[i] <= lane4StopZoneEnd) {
sideQueue += 1 + countCarsBehind(4, carPositions[i]);
break;
}
}
dynamicMainDuration = baseGreen + greenPerCar * mainQueue;
dynamicSideDuration = baseGreen + greenPerCar * sideQueue;

if (dynamicMainDuration > autoMaxGreen) dynamicMainDuration =


autoMaxGreen;
if (dynamicMainDuration < autoMinGreen) dynamicMainDuration =
autoMinGreen;
if (dynamicSideDuration > autoMaxGreen) dynamicSideDuration =
autoMaxGreen;
if (dynamicSideDuration < autoMinGreen) dynamicSideDuration =
autoMinGreen;

// Optional debug output


Serial.print("Auto Main Duration: ");
Serial.print(dynamicMainDuration);
Serial.print(" | Auto Side Duration: ");
Serial.println(dynamicSideDuration);
}

if (millis() - lastButtonPress > debounceTime) {


if (digitalRead(BTN_AUTO_TOGGLE) == LOW) {
autoTimingEnabled = !autoTimingEnabled;
lastButtonPress = millis();
Serial.print("Auto Timing Mode: ");
Serial.println(autoTimingEnabled ? "ON" : "OFF");
}
}

// Step 1: Simulation Speed Control


if (millis() - lastButtonPress > debounceTime) {
if (digitalRead(BTN_SLOW) == LOW) {
simulationDelay += 20;
if (simulationDelay > maxDelay) simulationDelay = maxDelay;
lastButtonPress = millis();
Serial.print("Simulation delay increased: ");
Serial.println(simulationDelay);
} else if (digitalRead(BTN_FAST) == LOW) {
simulationDelay -= 20;
if (simulationDelay < minDelay) simulationDelay = minDelay;
lastButtonPress = millis();
Serial.print("Simulation delay decreased: ");
Serial.println(simulationDelay);
}
}

// Step 2: Traffic Light Duration Control


if (millis() - lastButtonPress > debounceTime) {
// Main Green Duration Adjustment
if (digitalRead(BTN_MAIN_UP) == LOW) {
mainGreenDuration += 500;
if (mainGreenDuration > maxGreen) mainGreenDuration = maxGreen;
lastButtonPress = millis();
Serial.print("Main Green Duration increased: ");
Serial.println(mainGreenDuration);
} else if (digitalRead(BTN_MAIN_DOWN) == LOW) {
if (mainGreenDuration >= 500 + minGreen) mainGreenDuration -= 500;
lastButtonPress = millis();
Serial.print("Main Green Duration decreased: ");
Serial.println(mainGreenDuration);
}

// Side Green Duration Adjustment


if (digitalRead(BTN_SIDE_UP) == LOW) {
sideGreenDuration += 500;
if (sideGreenDuration > maxGreen) sideGreenDuration = maxGreen;
lastButtonPress = millis();
Serial.print("Side Green Duration increased: ");
Serial.println(sideGreenDuration);
} else if (digitalRead(BTN_SIDE_DOWN) == LOW) {
if (sideGreenDuration >= 500 + minGreen) sideGreenDuration -= 500;
lastButtonPress = millis();
Serial.print("Side Green Duration decreased: ");
Serial.println(sideGreenDuration);
}
}

// Traffic Light Control


unsigned long currentPhaseDuration = 0;
if (trafficLightPhase == 0) {
currentPhaseDuration = autoTimingEnabled ? dynamicMainDuration :
mainGreenDuration;
} else if (trafficLightPhase == 1 || trafficLightPhase == 3) {
currentPhaseDuration = allRedDuration;
} else if (trafficLightPhase == 2) {
currentPhaseDuration = autoTimingEnabled ? dynamicSideDuration :
sideGreenDuration;
}

if (millis() - trafficLightTimer > currentPhaseDuration) {


trafficLightPhase = (trafficLightPhase + 1) % 4; // Cycle through 0, 1,
2 , 3
trafficLightTimer = millis();

// Control physical lights based on new phase


if (trafficLightPhase == 0) { // Main (1&2) GREEN, Side (3&4) RED
digitalWrite(RED_LIGHT_PIN, LOW);
digitalWrite(GREEN_LIGHT_PIN, HIGH);
digitalWrite(RED_LIGHT_PIN2, HIGH);
digitalWrite(GREEN_LIGHT_PIN2, LOW);
} else if (trafficLightPhase == 1) { // ALL RED
digitalWrite(RED_LIGHT_PIN, HIGH);
digitalWrite(GREEN_LIGHT_PIN, LOW);
digitalWrite(RED_LIGHT_PIN2, HIGH);
digitalWrite(GREEN_LIGHT_PIN2, LOW);
} else if (trafficLightPhase == 2) { // Main (1&2) RED, Side (3&4)
GREEN
digitalWrite(RED_LIGHT_PIN, HIGH);
digitalWrite(GREEN_LIGHT_PIN, LOW);
digitalWrite(RED_LIGHT_PIN2, LOW);
digitalWrite(GREEN_LIGHT_PIN2, HIGH);
}else if (trafficLightPhase == 3) { // ALL RED
digitalWrite(RED_LIGHT_PIN, HIGH);
digitalWrite(GREEN_LIGHT_PIN, LOW);
digitalWrite(RED_LIGHT_PIN2, HIGH);
digitalWrite(GREEN_LIGHT_PIN2, LOW);
}
}

// Step 3: Car Spawn Rate Adjustment


if (millis() - lastButtonPress > debounceTime) {
if (digitalRead(BTN_SPAWN_FASTER) == LOW) {
if (spawnInterval > minSpawnInterval + 500) spawnInterval -= 500;
lastButtonPress = millis();
Serial.print("Spawn interval decreased (faster traffic): ");
Serial.println(spawnInterval);
} else if (digitalRead(BTN_SPAWN_SLOWER) == LOW) {
if (spawnInterval < maxSpawnInterval - 500) spawnInterval += 500;
lastButtonPress = millis();
Serial.print("Spawn interval increased (slower traffic): ");
Serial.println(spawnInterval);
}
}

// Spawn Cars in all 4 Lanes


if (millis() - spawnTimer > spawnInterval && numCars < MAX_CARS - 3) { //
Ensure space for four potential cars if spawning simultaneously
// Spawn car in Lane 1
carPositions[numCars] = 2;
carLanes[numCars] = 1;
carColors[numCars] = CHSV(random(255), 255, 255);
numCars++;

// Spawn car in Lane 2


carPositions[numCars] = 69;
carLanes[numCars] = 2;
carColors[numCars] = CHSV(random(255), 255, 255);
numCars++;

// Spawn car in Lane 3 (NEW STARTING POSITION)


carPositions[numCars] = 199; // User specified start at LED 199
carLanes[numCars] = 3;
carColors[numCars] = CHSV(random(255), 255, 255);
numCars++;

// Spawn car in Lane 4


carPositions[numCars] = 134;
carLanes[numCars] = 4;
carColors[numCars] = CHSV(random(255), 255, 255);
numCars++;

spawnTimer = millis();
} else if (millis() - spawnTimer > spawnInterval && numCars < MAX_CARS) {
// If fewer than 4 slots available, try to spawn in a random lane
int randomLane = random(4); // 0, 1, 2, 3 -> Map to 1, 2, 3, 4
int spawnPos = 0;
if (randomLane == 0) spawnPos = 2; // Lane 1
else if (randomLane == 1) spawnPos = 69; // Lane 2
else if (randomLane == 2) spawnPos = 199; // Lane 3 (Updated spawn
position)
else if (randomLane == 3) spawnPos = 134; // Lane 4

carPositions[numCars] = spawnPos;
carLanes[numCars] = randomLane + 1;
carColors[numCars] = CHSV(random(255), 255, 255);
numCars++;
spawnTimer = millis();
}

// Process Cars
for (int i = 0; i < numCars; i++) {
int currentLane = carLanes[i];
int currentPosition = carPositions[i];
bool canMove = true; // Assume car can move unless a condition says
otherwise

// ---- Traffic Light and Left Turn Waiting Logic ----

// 1. Check for straight traffic stopping at red light


// Car stops if its specific light is RED OR if it's the ALL RED phase
if (currentLane == 1 || currentLane == 2) { // Main vertical lanes
if (trafficLightPhase == 1 || trafficLightPhase == 2) { // ALL RED or
Main RED phase
if ((currentLane == 1 && currentPosition >= lane1StopZoneStart &&
currentPosition <= lane1StopZoneEnd) ||
(currentLane == 2 && currentPosition >= lane2StopZoneStart &&
currentPosition <= lane2StopZoneEnd)) {
canMove = false;
}
}
} else if (currentLane == 3 || currentLane == 4) { // Side horizontal
lanes
if (trafficLightPhase == 0 || trafficLightPhase == 1) { // ALL RED or
Side RED (Main GREEN) phase
if ((currentLane == 3 && currentPosition >= lane3StopZoneStart &&
currentPosition <= lane3StopZoneEnd) ||
(currentLane == 4 && currentPosition >= lane4StopZoneStart &&
currentPosition <= lane4StopZoneEnd)) {
canMove = false;
}
}
}

// 2. Check for left-turning cars waiting at their specific wait points


if (currentLane == 1) { // Lane 1 Left Turn
// Lane 1 is GREEN during phase 0. Yield if phase is 0 (main green)
AND a conflict exists.
if (trafficLightPhase == 0 && currentPosition >= lane1LeftWaitPoint -
2 && currentPosition <= lane1LeftWaitPoint) {
canMove = false;
}
} else if (currentLane == 2) { // Lane 2 Left Turn
// Lane 2 is GREEN during phase 0. Yield if phase is 0 AND a conflict
exists.
if (trafficLightPhase == 0 && currentPosition >= lane2LeftWaitPoint -
2 && currentPosition <= lane2LeftWaitPoint) {
canMove = false;
}
} else if (currentLane == 3) { // Lane 3 Left Turn
// Lane 3 is GREEN during phase 2. Yield if phase is 2 AND a conflict
exists.
if (trafficLightPhase == 2 && currentPosition >= lane3LeftWaitPoint -
2 && currentPosition <= lane3LeftWaitPoint) {
canMove = false;
}
} else if (currentLane == 4) { // Lane 4 Left Turn
// Lane 4 is GREEN during phase 2. Yield if phase is 2 AND a conflict
exists.
if (trafficLightPhase == 2 && currentPosition >= lane4LeftWaitPoint -
2 && currentPosition <= lane4LeftWaitPoint) {
canMove = false;
}
}
// ---- End of Waiting Logic ----

if (canMove) {
// Turning Logic and Movement
if (currentLane == 1) {
if (currentPosition == lane1RightTurnDecision) {
if (random(2) == 0) { // 50% chance to turn right
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane1RightTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane1RightTurnStart;
} else { // Go straight
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
} else if (currentPosition == lane1LeftTurnDecision) {
if (random(2) == 0) { // 50% chance to turn left
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane1LeftTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane1LeftTurnStart;
} else { // Go straight
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
} else { // Moving straight in lane 1, no turn decision yet
if (moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0) && currentPosition < NUM_LEDS -
CAR_LENGTH) {
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
if (turnState[i] == 1 && (currentPosition == lane1RightTurnStart
|| currentPosition == lane1LeftTurnStart)) {
turnState[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane1LeftTurnStart +
1) {
leds[lane1LeftTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane1RightTurnStart +
1) {
leds[lane1RightTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
}
} else if (currentLane == 2) { // Logic for Lane 2
if (currentPosition == lane2RightTurnDecision) {
if (random(2) == 0) { // 50% chance to turn right
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane2RightTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane2RightTurnStart;
} else { // Go straight
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
} else if (currentPosition == lane2LeftTurnDecision) {
if (random(2) == 0) { // 50% chance to turn left
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane2LeftTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane2LeftTurnStart;
} else { // Go straight
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
} else { // Moving straight in lane 2, no turn decision yet
if (moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0) && currentPosition < NUM_LEDS -
CAR_LENGTH) {
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
if (turnState[i] == 1 && (currentPosition == lane2RightTurnStart
|| currentPosition == lane2LeftTurnStart)) {
turnState[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane2LeftTurnStart +
1) {
leds[lane2LeftTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane2RightTurnStart +
1) {
leds[lane2RightTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
}
} else if (currentLane == 3) { // Logic for Lane 3 (Movement now
always positive)
if (currentPosition == lane3RightTurnDecision) {
if (random(2) == 0) { // 50% chance to turn right
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane3RightTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane3RightTurnStart;
} else { // Go straight (Now increments for Lane 3)
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++; // CHANGED to increment
}
} else if (currentPosition == lane3LeftTurnDecision) {
if (random(2) == 0) { // 50% chance to turn left
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane3LeftTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane3LeftTurnStart;
} else { // Go straight (Now increments for Lane 3)
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++; // CHANGED to increment
}
} else { // Moving straight in lane 3, no turn decision yet
// Check bounds for incrementing
if (moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0) && currentPosition < NUM_LEDS -
CAR_LENGTH) { // CHANGED bound check
previousCarPositions[i] = currentPosition;
carPositions[i]++; // CHANGED to increment
}
if (turnState[i] == 1 && (currentPosition == lane3RightTurnStart
|| currentPosition == lane3LeftTurnStart)) {
turnState[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane3LeftTurnStart +
1) {
leds[lane3LeftTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane3RightTurnStart +
1) {
leds[lane3RightTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
}
} else if (currentLane == 4) { // Logic for Lane 4
if (currentPosition == lane4RightTurnDecision) {
if (random(2) == 0) { // 50% chance to turn right
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane4RightTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane4RightTurnStart;
} else { // Go straight
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
} else if (currentPosition == lane4LeftTurnDecision) {
if (random(2) == 0) { // 50% chance to turn left
for (int j = 0; j < CAR_LENGTH; j++) {
leds[lane4LeftTurnDecision - j] = CRGB::Black;
}
turnState[i] = 1;
turnClear[i] = 1;
carPositions[i] = lane4LeftTurnStart;
} else { // Go straight
moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0);
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
} else { // Moving straight in lane 4, no turn decision yet
if (moveCar(currentPosition, carColors[i], false, -1,
previousCarPositions[i], false, 0) && currentPosition < NUM_LEDS -
CAR_LENGTH) {
previousCarPositions[i] = currentPosition;
carPositions[i]++;
}
if (turnState[i] == 1 && (currentPosition == lane4RightTurnStart
|| currentPosition == lane4LeftTurnStart)) {
turnState[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane4LeftTurnStart +
1) {
leds[lane4LeftTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
if (turnClear[i] == 1 && currentPosition == lane4RightTurnStart +
1) {
leds[lane4RightTurnDecision + 1] = CRGB::Black;
turnClear[i] = 0;
}
}
}
} else {
// Car is stopped at red light OR waiting to turn left due to
conflicting traffic
moveCar(currentPosition, carColors[i], true, -1,
previousCarPositions[i], false, 0); // stopAtRed is true
}

// Car Removal (when it reaches the end of its path)


// All lanes now move in the positive direction.
if ((currentPosition >= Lane1End && currentPosition <= Lane1End + 2) ||
(currentPosition >= Lane2End && currentPosition <= Lane2End + 2) ||
(currentPosition >= Lane3End && currentPosition <= Lane3End + 2) ||
// Adjusted for Lane 3 (now positive flow)
(currentPosition >= Lane4End && currentPosition <= Lane4End + 2)) {

for (int j = 0; j < CAR_LENGTH + 1; j++) { // Clear the car's LEDs


// All lanes now move in the positive direction, so clearing is
uniform
if (currentPosition - j >= 0 && currentPosition - j < NUM_LEDS) {
leds[currentPosition - j] = CRGB::Black;
}
}
// Shift remaining cars in the array to fill the gap
for (int j = i; j < numCars - 1; j++) {
carPositions[j] = carPositions[j + 1];
carColors[j] = carColors[j + 1];
previousCarPositions[j] = previousCarPositions[j + 1];
turnState[j] = turnState[j + 1];
turnClear[j] = turnClear[j + 1];
carLanes[j] = carLanes[j + 1];
}
numCars--; // Decrease count of active cars
i--; // Decrement i to re-check the new car at this index in
the next iteration
carsClearedCounter++;
}
}

FastLED.show(); // Update the LEDs on the strip


// Step 4: Print performance metrics every 5 seconds
if (millis() - lastStatsPrint >= statsInterval) {
Serial.print("Cars cleared so far: ");
Serial.println(carsClearedCounter);
lastStatsPrint = millis();
}
delay(simulationDelay); // Control simulation speed
}

You might also like