Arc Reactor Simulator
The project is a simulated arc rector using a ring shaped RGB LED module. There is a potentiometer to manually adjust the power as well as a button to "start up" or "shut down" the reactor.
Devices & Components
1
16x2 LCD display with I²C interface
1
Breadboard - 830 contacts
1
Arduino Nano
1
Ring with 12 RGB WS2812 LEDs and integrated driver
Software & Tools
Arduino IDE
Project description
Code
arc_reactor
cpp
Driver code for the Arc Reactor Simulator
1#include <Wire.h> 2#include <DIYables_LCD_I2C.h> 3#include <FastLED.h> 4 5#define POT_PIN A0 // Middle/wiper pin of the potentiometer 6#define BUTTON_PIN D2 // Push button pin; other side of button goes to GND 7#define LED_PIN D9 // Data pin for the RGB LED module, actually labeled D6 on the Arduino for some reason 8#define LED_COUNT 8 // Number of RGB LEDs on the module 9 10DIYables_LCD_I2C lcd(0x27, 16, 2); // LCD address 0x27, 16 columns, 2 rows 11CRGB leds[LED_COUNT]; // FastLED array holding each LED color 12 13int powerPercent = 0; // Current power level shown on LCD, 0 to 100 14 15bool sequenceActive = false; // True whilst button-controlled sequence is running 16int sequenceDirection = 1; // 1 means sequence goes up, -1 means sequence goes down 17 18// After a button sequence, ignore the potentiometer until it moves more than 5% 19bool potLocked = false; 20int lockedPotPercent = 0; 21 22// Debouncing prevents one physical button press from being read many times 23bool lastButtonReading = HIGH; 24bool stableButtonState = HIGH; 25unsigned long lastDebounceTime = 0; 26const unsigned long debounceDelay = 40; 27 28unsigned long lastsequenceTime = 0; 29const unsigned long sequenceDelay = 100; // 100 ms per 1%, so 0-100 takes 10 sec 30 31unsigned long lastFlickerTime = 0; 32const unsigned long flickerDelay = 10; // Updates LED flicker every 10 ms 33 34void setup() { 35 // Button uses the Nano's internal pullup resistor 36 // Not pressed = HIGH, pressed = LOW 37 pinMode(BUTTON_PIN, INPUT_PULLUP); 38 39 // ESP32 analog reads are 12-bit: 0 to 4095 40 analogReadResolution(12); 41 42 // Seeds Arduino's random number generator so flicker differs after reset 43 randomSeed(micros()); 44 45 // Starts the LCD 46 lcd.init(); 47 lcd.backlight(); 48 49 // Starts the LED module 50 FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, LED_COUNT); 51 FastLED.setBrightness(255); 52 FastLED.clear(); 53 FastLED.show(); 54 55 // Shows the starting power level 56 updatePower(0); 57} 58 59void loop() { 60 // Always checks the button first 61 handleButton(); 62 63 // If a button sequence is running, ignores the potentiometer 64 if (sequenceActive) { 65 handleSequence(); 66 } else { 67 handlePotentiometer(); 68 } 69 70 // Keeps the LEDs flickering based on the current power level 71 updateFlicker(); 72} 73 74void handleButton() { 75 bool reading = digitalRead(BUTTON_PIN); 76 77 // If the raw reading changed, restart the debounce timer 78 if (reading != lastButtonReading) { 79 lastDebounceTime = millis(); 80 } 81 82 // Only accepts the reading if it has stayed stable long enough 83 if ((millis() - lastDebounceTime) > debounceDelay) { 84 if (reading != stableButtonState) { 85 stableButtonState = reading; 86 87 if (stableButtonState == LOW && !sequenceActive) { 88 // Button only starts power-up from exactly 0% 89 if (powerPercent == 0) { 90 startSequence(1); 91 } 92 93 // Button only starts power-down from exactly 100% 94 else if (powerPercent == 100) { 95 startSequence(-1); 96 } 97 } 98 } 99 } 100 101 lastButtonReading = reading; 102} 103 104void startSequence(int direction) { 105 sequenceActive = true; 106 sequenceDirection = direction; 107 108 // Saves the current potentiometer position 109 // After the sequence, potentiomter input will not work until difference exceeds 5% 110 potLocked = false; 111 lockedPotPercent = readPotPercent(); 112 113 lastsequenceTime = millis(); 114 updatePower(powerPercent); 115} 116 117void handleSequence() { 118 // Only steps through the sequence after sequenceDelay milliseconds 119 if (millis() - lastsequenceTime >= sequenceDelay) { 120 lastsequenceTime = millis(); 121 122 // Adds 1 for power-up or subtracts 1 for power-down 123 powerPercent += sequenceDirection; 124 powerPercent = constrain(powerPercent, 0, 100); 125 126 updatePower(powerPercent); 127 128 // Stops the sequence once it reaches either endpoint 129 if (powerPercent == 0 || powerPercent == 100) { 130 sequenceActive = false; 131 potLocked = true; 132 } 133 } 134} 135 136void handlePotentiometer() { 137 int newPower = readPotPercent(); 138 139 // Ignores potentiometer noise after button sequence 140 if (potLocked) { 141 if (abs(newPower - lockedPotPercent) <= 5) { 142 return; 143 } 144 145 // Allows potentiometer control after 5% change 146 potLocked = false; 147 } 148 149 // Ignores jitter so the LCD does not flicker between values 150 if (abs(newPower - powerPercent) >= 1) { 151 powerPercent = newPower; 152 updatePower(powerPercent); 153 } 154} 155 156int readPotPercent() { 157 int rawValue = analogRead(POT_PIN); 158 159 //Snaps to 0% 160 if (rawValue >= 3900) { 161 return 0; 162 } 163 164 //Snaps to 100% 165 if (rawValue <= 195) { 166 return 100; 167 } 168 169 // Converts the raw value into 0-100 range 170 int percent = map(rawValue, 4095, 0, 0, 100); 171 percent = constrain(percent, 0, 100); 172 173 return percent; 174} 175 176void updatePower(int percent) { 177 // Clears the power percent 178 lcd.setCursor(0, 0); 179 lcd.print("Power: "); 180 181 // Prints the percentage value after "Power: " 182 lcd.setCursor(7, 0); 183 184 // Adds leading zeroes 185 if (percent < 10) { 186 lcd.print("00"); 187 } else if (percent < 100) { 188 lcd.print("0"); 189 } 190 191 lcd.print(percent); 192 lcd.print("%"); 193} 194 195void updateFlicker() { 196 // Updates the LEDs only every flickerDelay milliseconds. 197 if (millis() - lastFlickerTime < flickerDelay) { 198 return; 199 } 200 201 lastFlickerTime = millis(); 202 203 // Forces LEDs off at 0% 204 if (powerPercent == 0) { 205 FastLED.clear(); 206 FastLED.show(); 207 return; 208 } 209 210 // Converts 0-100% power into a 0-255 brightness-like strength. 211 float adjustedValue = map(powerPercent, 0, 100, 0, 255); 212 213 for (int i = 0; i < LED_COUNT; i++) { 214 // This makes the strength of each LED slightly random 215 float randValue = 1.0 - (random(0, 10000) / 10000.0 * 75.0); 216 float strength = randValue + adjustedValue; 217 218 if (strength < 0) { 219 strength = 0; 220 } 221 222 // Weights for color balance 223 float redStrength = strength / 10.0; 224 float greenStrength = strength / 3.0; 225 float blueStrength = strength; 226 227 228 // This makes each RGB value slightly random 229 if (strength > 0) { 230 redStrength += random(-4, 5); 231 greenStrength += random(-8, 9); 232 blueStrength += random(-12, 13); 233 } 234 235 // Keeps each color value inside the valid 0-255 range 236 byte red = constrain(redStrength, 0, 255); 237 byte green = constrain(greenStrength, 0, 255); 238 byte blue = constrain(blueStrength, 0, 255); 239 240 leds[i] = CRGB(red, green, blue); 241 } 242 243 // Finally sends the updated color data to the LED module 244 FastLED.show(); 245}
Documentation
Arc Reactor Arduino Guide
Basic guide
Arc Reactor Arduino Guide.pdf
Comments
Only logged in users can leave comments