-
Notifications
You must be signed in to change notification settings - Fork 1
/
Light.cpp
148 lines (120 loc) · 4.18 KB
/
Light.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include "Light.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <math.h>
// Helper function.
// Just like regular map, only it works on doubles.
double doubleMap(double x, double in_min, double in_max, double out_min, double out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Constructor
Light::Light(pin_t pin, int idle_min, int idle_max) {
_pin = pin;
_value = 0;
_direction = 1;
_count_to = 200;
_min_power = 5;
_max_power = 255;
_curr_max_power = _max_power;
_curr_min_power = _min_power;
_idle_min = idle_min;
_idle_max = idle_max;
_idle = 0;
// Initialise the hardware itself.
pinMode(_pin, OUTPUT);
analogWrite(_pin,0);
}
const int absolute_min_pwr = 0;
const int absolute_max_pwr = 255;
// Our lights follow a sin(x^n) curve with power. This
// is the 'n' in that curve. Higher values cause a slower
// initial increase in power, which compensates for the lights
// having greater sensitivity at lower power levels.
const double smoothing_power = 3;
// Updates our light. Intended to be called once per cycle of main code.
// Bias is added to all lines if provided.
void Light::update(int bias) {
// Update our light cycle.
_cycle();
set_power( map(_value, 0, _count_to, _curr_min_power, _curr_max_power) + bias );
}
// Cycles our lights. Intended to be called by update.
void Light::_cycle() {
// If we're idling, then keep doing that.
if (_idle > 0) {
_idle--;
return;
}
// Move our value in the direction we're going.
_value += _direction;
// Constrain our value, since directions larger than '1' can
// cause us to over or under-run.
_value = constrain(_value, 0, _count_to);
// If we've hit the bottom of our cycle, then idle.
if (_value == 0) {
_idle = random(_idle_min, _idle_max);
}
// Likewise if we've hit either end of our cycle, mark ourselves
// as heading the other way.
if (_value == 0 || _value == _count_to) {
_direction = -_direction;
}
}
// Sets power directly. Will not change internal state, so
// this will be overwritten to the next call to update().
//
// This auto-constrains to 0-255, but I'm not sure if we really need this.
void Light::set_power(int value) {
// This is our linear value. It just constrains us to 0-maximum power.
// Absolute minimum power is enforced later in this function.
int linear_value = constrain(value, 0, absolute_max_pwr);
// Now map this onto a sin(x^n) wave. Full power is reached at x = (pi/2)^(1/n),
// so we calculate that here.
double max_trig_value = pow(M_PI_2, 1/smoothing_power);
// Map our linear value onto an input suitable for handing to sin(x^n).
double smoothed_x = doubleMap(linear_value,0,absolute_max_pwr,0,max_trig_value);
// Pass that value through the smoothing function.
double smoothed_value = sin( pow(smoothed_x, smoothing_power) );
// Finally, map that output to our lights power.
analogWrite(_pin, doubleMap( smoothed_value, 0, 1, absolute_min_pwr, absolute_max_pwr ) );
}
// On and off set the light on and off as if it were a digital line.
void Light::on() { set_power(255); }
void Light::off() { set_power(0); }
// If passed a true argument, makes everything brighter by boosting the minimum
// brightness. If passed a false argument, decays the minimum brihgtness towards
// its original value.
void Light::brighten(bool brighten) {
if (brighten) {
_curr_min_power = min(_max_power, _curr_min_power+1);
}
else {
_curr_min_power = max(_min_power, _curr_min_power-1);
}
}
// If passed a true argument, drop the max power down until it reaches zero.
// This allows our suit to go competely dark. Otherwise restore power values
// back to normal.
void Light::quell(bool quell) {
if (quell) {
_curr_max_power = max(0, _curr_max_power-1);
_curr_min_power = 0;
}
else {
_curr_max_power = min(_max_power, _curr_max_power+1);
if (_curr_min_power < _min_power) {
_curr_min_power++;
}
}
}
// Causes lights to start a pulse if not already pulsing.
// On a false argument, does nothing.
// TODO: Have this *speed* pulses, too.
void Light::pulse(bool pulse) {
if (pulse) {
_idle = 0;
}
}