These are basically safety glasses from Home Depot with an RGB LED in each lens. The program runs in two phases. First, for half an hour, it will slowly brighten and dim the lights to random colors. This is meant to simulate hypnagogic imagery (the shapes and colors you see as you're falling asleep). The brightening and dimming gets progressively faster and the pause in between gets shorter as time goes on.
After the half hour has passed (hopefully you are asleep by this point) the second phase begins. In this phase the red LEDs flash for ten seconds every ten minutes. This is a signal to your dreaming self that you are in fact dreaming and free to do whatever you please.
(The photos are from before i got RGB LEDs, I was just using blue then)
They work pretty well but are a little bit uncomfortable. I wake up often and find that they are no longer on my face, which is strange because the strap seems tight enough that they shouldn't just fall off. I think I might be taking them off with my hands in my sleep.
Code follows. First is a task scheduler I wrote to schedule the lightening.
#ifndef TASK_SCHEDULER
#define TASK_SCHEDULER
void setupTaskScheduler(int _maxTasks, int _tickIntervalMs);
void scheduleTimer5Task(void (*action)(), int delayMs);
void startTicking();
#endif
const int prescaler = 1024; // How many clock cycles it takes before the underlying timer (Timer1) increments
const int timerHertz = 16000000 / prescaler; // How many times per second the underlying timer (Timer1) increments. 16,000,000 is the Arduino's clock speed
int maxTasks;
unsigned long tickIntervalMs;
int compareMatchRegister;
struct Task
{
void (*action)(void*);
void* argument;
unsigned long delayTicks;
};
struct Task** tasks;
void setupTaskScheduler(int _maxTasks, unsigned long _tickIntervalMs)
{
if (tasks != NULL)
{
for (int i = 0; i < maxTasks; i++)
{
Task* task = tasks[i];
free(task);
}
free(tasks);
}
tickIntervalMs = _tickIntervalMs;
double interruptHertz = 1000 / _tickIntervalMs; // interruptHertz = How many times per second the caller is asking to check for and execute due tasks
maxTasks = _maxTasks;
tasks = (Task**)calloc(maxTasks, sizeof(Task*));
compareMatchRegister = timerHertz / interruptHertz; // compareMatchRegister = when the underlying timer (Timer1) hits this value, it interrupts and then resets to zero
}
int scheduleTimer1Task(void (*action)(void*), void* argument, unsigned long delayMs)
{
for (int i = 0; i < maxTasks; i++)
{
if (tasks[i] == NULL)
{
Task* ptr = (Task*)malloc(sizeof(struct Task));
ptr->action = action;
ptr->argument = argument;
ptr->delayTicks = delayMs / tickIntervalMs;
tasks[i] = ptr;
return true; // Found an empty spot in the task list for this task
}
}
return false; // No spot for this task found, scheduling request rejected
}
void startSchedulerTicking()
{
noInterrupts();
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 1hz increments
OCR1A = compareMatchRegister;// = (16*10^6) / (1*1024) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS10 and CS12 bits for 1024 prescaler
TCCR1B |= (1 << CS12) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
interrupts();
}
// This is the timer interrupt
ISR( TIMER1_COMPA_vect )
{
for (int i = 0; i < maxTasks; i++)
{
if (tasks[i] != NULL)
{
Task* task = tasks[i];
unsigned long ticks = task->delayTicks;
if (ticks == 0)
{
void (*action)(void*) = task->action;
void* argument = task->argument;
free(task);
tasks[i] = NULL;
action(argument);
}
else
{
task->delayTicks--;
}
}
}
}
And the project-specific code:
int scheduleTimer1Task(void (*action)(void*), void* argument, unsigned long delayMs); // Don't know why putting this in a header file and including it doesn't work but I'll deal
const unsigned long DelayMs = 30;
const int AnalogOutMaxForHypnaMode = 30;
const int MaxFadeMs = 2000;
const int MinFadeMs = 200;
const unsigned long RampUpTimeMs = 1800000; // 30 minutes
const unsigned long RampUpStepIntervalMs = 10000; // 10 seconds
const unsigned int NumberOfLeds = 2;
const unsigned int NumberOfColorsPerLed = 3;
const unsigned long LdModeInitialDelayMs = 1200000; // 60 minutes
const unsigned long LdModeDelayBetweenPulsesMs = 600000; // 10 minutes
const unsigned long LdModePulseDurationMs = 10000; // 10 seconds
const int LedPins[NumberOfLeds][NumberOfColorsPerLed] =
{
{ 2, 3, 4 },
{ 5, 6, 7 },
};
double ledIncrements[NumberOfLeds][NumberOfColorsPerLed] =
{
{ 0, 0, 0 },
{ 0, 0, 0 },
};
double ledTargets[NumberOfLeds][NumberOfColorsPerLed] =
{
{ 0, 0, 0 },
{ 0, 0, 0 },
};
double ledCurrentVoltages[NumberOfLeds][NumberOfColorsPerLed] =
{
{ 0, 0, 0 },
{ 0, 0, 0 },
};
int fadeMs = MaxFadeMs;
int maxOffTimeMs = fadeMs * 5;
unsigned long nextLedUpdateTime = 0;
void setup()
{
randomSeed(analogRead(0));
setupTaskScheduler(NumberOfLeds * NumberOfColorsPerLed + 1, DelayMs);
startSchedulerTicking();
scheduleTimer1Task(&rampUp, NULL, RampUpStepIntervalMs);
for (int led = 0; led < NumberOfLeds; led++)
{
for (int color = 0; color < NumberOfColorsPerLed; color++)
{
pinMode(LedPins[led][color], OUTPUT);
}
scheduleLed(led);
}
}
void loop()
{
unsigned long mill = millis();
if (mill > LdModeInitialDelayMs)
{
ldMode();
}
if (mill > nextLedUpdateTime)
{
for (int led = 0; led < NumberOfLeds; led++)
{
if (ledDo(led))
{
scheduleLed(led);
}
}
nextLedUpdateTime = millis() + DelayMs;
}
}
void ldMode()
{
for (int led = 0; led < NumberOfLeds; led++)
{
for (int color = 0; color < NumberOfColorsPerLed; color++)
{
analogWrite(LedPins[led][color], LOW);
}
}
while (true)
{
unsigned long startMillis = millis();
while (millis() < startMillis + LdModePulseDurationMs)
{
analogWrite(2, AnalogOutMaxForHypnaMode);
analogWrite(6, AnalogOutMaxForHypnaMode);
delay(500);
analogWrite(2, LOW);
analogWrite(6, LOW);
delay(500);
}
delay(LdModeDelayBetweenPulsesMs);
}
}
void rampUp(void*)
{
unsigned long mill = millis();
if (mill > RampUpTimeMs)
{
return;
}
double multiplier = 1.0 - ((double)mill / (double)RampUpTimeMs);
fadeMs = (multiplier * (MaxFadeMs - MinFadeMs)) + MinFadeMs;
maxOffTimeMs = map(fadeMs, MinFadeMs, MaxFadeMs, 1000, maxOffTimeMs);
scheduleTimer1Task(&rampUp, NULL, RampUpStepIntervalMs);
}
// Sets the LED's increments so that it begins moving toward its target
void updateLed(void* _led)
{
int led = *(int*)_led;
free(_led);
double numberOfSteps = (double)fadeMs / (double)DelayMs;
for (int color = 0; color < NumberOfColorsPerLed; color++)
{
ledIncrements[led][color] = ledTargets[led][color] / numberOfSteps;
}
}
// Sets the LED's targets and schedules a call to updateLed to start its cycle
void scheduleLed(int led)
{
unsigned long offTimeMs = random(0, maxOffTimeMs);
for (int color = 0; color < NumberOfColorsPerLed; color++)
{
ledTargets[led][color] = random(1, AnalogOutMaxForHypnaMode);
}
void* ledVoid = malloc(sizeof(int));
memcpy(ledVoid, &led, sizeof(int));
scheduleTimer1Task(&updateLed, ledVoid, offTimeMs);
}
// Puts the LED back in its initial off state
void resetLed(int led)
{
for (int color = 0; color < NumberOfColorsPerLed; color++)
{
analogWrite(LedPins[led][color], 0);
ledCurrentVoltages[led][color] = 0;
ledIncrements[led][color] = 0;
}
}
// Moves the led a step toward its final state
// Returns true if the led has reached its final state and returned to its initial state (the led has cycled completely from off to on to off again)
boolean ledDo(int led)
{
boolean returnVal = false;
for (int color = 0; color < NumberOfColorsPerLed; color++)
{
double increment = ledIncrements[led][color];
int pin = LedPins[led][color];
double output = ledCurrentVoltages[led][color] + increment;
if (increment == 0)
{
continue;
}
if (output < 0 || returnVal)
{
resetLed(led);
return true;
}
else if (output > ledTargets[led][color])
{
ledIncrements[led][color] = -1 * increment;
}
else
{
analogWrite(pin, output);
ledCurrentVoltages[led][color] = output;
}
}
return false;
}