diff --git a/StarTrek/StarTrek.pde b/StarTrek/StarTrek.pde index aee6d15c..d16bcfb3 100644 --- a/StarTrek/StarTrek.pde +++ b/StarTrek/StarTrek.pde @@ -28,6 +28,7 @@ each nacelle. */ #include +#include #define NAV_LIGHTS A2 // Red/green navigational lights #define STROBE_LIGHT A3 // Strobe light @@ -43,11 +44,12 @@ each nacelle. #define NAV_LIGHTS_OFF 1000 #define STROBE_LIGHT_ON 70 #define STROBE_LIGHT_OFF 830 +#define NACELLE_CHASE_LEN 6 // Length of nacelle chase, 1..6 #define NACELLE_MIN_PERIOD 25 #define NACELLE_MAX_PERIOD 250 #define NACELLE_DIM_VALUE 32 // Value for dimming previous LED in chase, 0..255 -byte nacelleChase[6] = { +byte nacelleChasePins[6] = { NACELLE_1, NACELLE_2, NACELLE_3, @@ -55,63 +57,55 @@ byte nacelleChase[6] = { NACELLE_5, NACELLE_6 }; -byte nacelleChaseLen = 6; // Select the length of the nacelle chase: 1 to 6. -unsigned long startTime; -unsigned long lastNacelleTime = 0; -unsigned long nacellePeriod = 0; -byte index = 0; +class NacelleChaseLEDs : public ChaseLEDs +{ +public: + NacelleChaseLEDs(const byte *pins, int num); + +protected: + void advance(byte prevPin, byte nextPin); + +private: + void readChaseTime(); +}; BlinkLED navLights(NAV_LIGHTS, NAV_LIGHTS_ON, NAV_LIGHTS_OFF); BlinkLED strobeLight(STROBE_LIGHT, STROBE_LIGHT_ON, STROBE_LIGHT_OFF); +NacelleChaseLEDs nacelleChase(nacelleChasePins, NACELLE_CHASE_LEN); void setup() { - // Configure the outputs and set them to be initially LOW. - for (int index = 0; index < nacelleChaseLen; ++index) { - byte pin = nacelleChase[index]; - pinMode(pin, OUTPUT); - digitalWrite(pin, LOW); - } - // Turn off the status LED on the Arduino board (we don't need it). pinMode(13, OUTPUT); digitalWrite(13, LOW); +} +void loop() { + navLights.loop(); + strobeLight.loop(); + nacelleChase.loop(); +} + +NacelleChaseLEDs::NacelleChaseLEDs(const byte *pins, int num) + : ChaseLEDs(pins, num, 0) +{ // Initialize the analog input for the nacelle chaser rate. pinMode(NACELLE_RATE, INPUT); digitalWrite(NACELLE_RATE, LOW); - - // Record the starting time, for controlling the chase and blinks. - startTime = millis(); - index = nacelleChaseLen - 1; + readChaseTime(); } -// Note: there will be discontinuity in the chase/blink rate when millis() -// wraps around after 49 days. Should automatically resync after a second or two. - -void loop() { - // How long since the application started? - unsigned long sinceStart = millis() - startTime; - - // Update the navigation and strobe lights. - navLights.loop(); - strobeLight.loop(); - - // Update the nacelle lights - uniform LED chase of length 1 to 6. - if ((sinceStart - lastNacelleTime) >= nacellePeriod) { - // Advance to the next pin in sequence. - index = (index + 1) % nacelleChaseLen; - int currentPin = nacelleChase[index]; - int prevPin1 = nacelleChase[(index + nacelleChaseLen - 1) % nacelleChaseLen]; - int prevPin2 = nacelleChase[(index + nacelleChaseLen - 2) % nacelleChaseLen]; - digitalWrite(prevPin2, LOW); - analogWrite(prevPin1, NACELLE_DIM_VALUE); - digitalWrite(currentPin, HIGH); - - // Read the chase rate from the trimpot on A0 and determine the next timeout period. - lastNacelleTime = sinceStart; - int val = analogRead(NACELLE_RATE); - nacellePeriod = map(val, 0, 1023, NACELLE_MIN_PERIOD, NACELLE_MAX_PERIOD); - } +void NacelleChaseLEDs::advance(byte prevPin, byte nextPin) +{ + digitalWrite(previousPin(2), LOW); + analogWrite(prevPin, NACELLE_DIM_VALUE); + digitalWrite(nextPin, HIGH); + readChaseTime(); +} + +void NacelleChaseLEDs::readChaseTime() +{ + int val = analogRead(NACELLE_RATE); + setAdvanceTime(map(val, 0, 1023, NACELLE_MIN_PERIOD, NACELLE_MAX_PERIOD)); } diff --git a/libraries/BlinkLED/ChaseLEDs.cpp b/libraries/BlinkLED/ChaseLEDs.cpp new file mode 100644 index 00000000..a7eb6612 --- /dev/null +++ b/libraries/BlinkLED/ChaseLEDs.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "ChaseLEDs.h" +#include + +/** + * \class ChaseLEDs ChaseLEDs.h + * \brief Chase LED's on output pins in a defined sequence. + * + * The following example performs a LED chase over the 6 PWM outputs + * on the Arduino Uno, with a 150 millisecond delay between each LED: + * + * \code + * uint8_t pins[] = {3, 5, 6, 9, 10, 11}; + * ChaseLEDs chaser(pins, sizeof(pins), 150); + * + * void loop() { + * chaser.loop(); + * } + * \endcode + * + * After pin 11 is lit, the pattern will repeat at pin 3. To cause the + * chase to oscillate back and forth instead, extend the sequence as follows: + * + * \code + * uint8_t pins[] = {3, 5, 6, 9, 10, 11, 10, 9, 6, 5}; + * ChaseLEDs chaser(pins, sizeof(pins), 150); + * \endcode + */ + +/** + * \brief Initializes the LED chaser. + * + * The chase sequence consists of \a num pins, whose names are given by + * the \a pins array. Each LED is lit for \a advanceTime milliseconds + * before advancing to the next LED. + * + * This constructor configures all of the pins for output and sets their + * state to be LOW. The first LED will be lit when the program first + * calls loop(). + * + * \sa loop() + */ +ChaseLEDs::ChaseLEDs(const uint8_t *pins, int num, unsigned long advanceTime) + : _pins(pins) + , _numPins(num) + , _currentIndex(-1) + , _advanceTime(advanceTime) + , _lastChange(millis()) +{ + for (uint8_t index = 0; index < _numPins; ++index) { + pinMode(_pins[index], OUTPUT); + digitalWrite(_pins[index], LOW); + } +} + +/** + * Perform a single iteration of the control loop for this LED chaser. + */ +void ChaseLEDs::loop() +{ + if (_currentIndex >= 0) { + if ((millis() - _lastChange) >= _advanceTime) { + // Advance to the next LED in sequence. + _currentIndex = (_currentIndex + 1) % _numPins; + _lastChange += _advanceTime; + advance(previousPin(1), _pins[_currentIndex]); + } + } else { + // First time - light the first LED. + _currentIndex = 0; + _lastChange = millis(); + advance(previousPin(1), _pins[_currentIndex]); + } +} + +/** + * \fn unsigned long ChaseLEDs::advanceTime() const + * \brief Returns the number of milliseconds that each LED will be + * lit in the chase sequence. + * + * \sa setAdvanceTime(), advance() + */ + +/** + * \fn void ChaseLEDs::setAdvanceTime(unsigned long advanceTime) + * \brief Sets the number of milliseconds to advance between LED's to + * \a advanceTime. + * + * \sa advanceTime(), advance() + */ + +/** + * \brief Advances to the next LED in sequence, turning off \a prevPin, + * and turning on \a nextPin. + * + * The default implementation is equivalent to the following code: + * + * \code + * digitalWrite(prevPin, LOW); + * digitalWrite(nextPin, HIGH); + * \endcode + * + * This method may be overridden in subclasses to provide special effects. + * See the documentation for previousPin() for some example effects. + * + * \sa previousPin() + */ +void ChaseLEDs::advance(uint8_t prevPin, uint8_t nextPin) +{ + digitalWrite(prevPin, LOW); + digitalWrite(nextPin, HIGH); +} + +/** + * \fn uint8_t ChaseLEDs::previousPin(int n) const + * \brief Returns the pin that is \a n steps back in the sequence. + * + * If \a n is zero, then the current pin is returned; if \a n is 1, + * then the previous pin is returned; and so on. + * + * This function may be called by subclasses in their advance() method + * to manipulate pins that are further back in the chase sequence than + * the immediately previous pin. + * + * For example, the following code implements a LED chaser that lights + * two pins at a time: + * + * \code + * void DoubleChaser::advance(uint8_t prevPin, uint8_t nextPin) + * { + * digitalWrite(previousPin(2), LOW); + * digitalWrite(prevPin, HIGH); + * digitalWrite(nextPin, HIGH); + * } + * \endcode + * + * As another exmaple, the following code uses PWM outputs to fade out + * the previous pin rather than turn it off immediately: + * + * \code + * void FadingChaser::advance(uint8_t prevPin, uint8_t nextPin) + * { + * digitalWrite(previousPin(2), LOW); + * analogWrite(prevPin, 32); + * digitalWrite(nextPin, HIGH); + * } + * \endcode + * + * Note: it is possible to retrieve the \em following pin in sequence using + * previousPin(-1). This could be used to fade in the LED that follows + * \a nextPin. + * + * \sa advance() + */ diff --git a/libraries/BlinkLED/ChaseLEDs.h b/libraries/BlinkLED/ChaseLEDs.h new file mode 100644 index 00000000..49dfc2f4 --- /dev/null +++ b/libraries/BlinkLED/ChaseLEDs.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef ChaseLEDs_h +#define ChaseLEDs_h + +#include + +class ChaseLEDs +{ +public: + ChaseLEDs(const uint8_t *pins, int num, unsigned long advanceTime); + + void loop(); + + unsigned long advanceTime() const { return _advanceTime; } + void setAdvanceTime(unsigned long advanceTime) { _advanceTime = advanceTime; } + +protected: + virtual void advance(uint8_t prevPin, uint8_t nextPin); + uint8_t previousPin(int n) const + { return _pins[(_currentIndex + _numPins - n) % _numPins]; } + +private: + const uint8_t *_pins; + int _numPins; + int _currentIndex; + unsigned long _advanceTime; + unsigned long _lastChange; +}; + +#endif diff --git a/libraries/BlinkLED/keywords.txt b/libraries/BlinkLED/keywords.txt index 5b1365f3..574453aa 100644 --- a/libraries/BlinkLED/keywords.txt +++ b/libraries/BlinkLED/keywords.txt @@ -1,4 +1,5 @@ BlinkLED KEYWORD1 +ChaseLEDs KEYWORD1 onTime KEYWORD2 offTime KEYWORD2 @@ -10,3 +11,7 @@ setState KEYWORD2 pause KEYWORD2 resume KEYWORD2 isPaused KEYWORD2 + +advanceTime KEYWORD2 +setAdvanceTime KEYWORD2 +previousPin KEYWORD2