![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
Published
Dependencies: BLE_API TLC5955 mbed nRF51822
Fork of BLE_LoopbackUART by
Diff: sequencer.cpp
- Revision:
- 14:73923b07ae4a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sequencer.cpp Sat Jun 09 23:23:06 2018 +0000 @@ -0,0 +1,913 @@ +/* + +Control lighting sequences + + +*/ + +#include "TLC5955.h" +#include <ctype.h> +#include "sequencer.h" +#include "color.h" + +DigitalOut led1(LED1); + +// zero-based time, starts at 7:30PM +volatile uint32_t TheElapsedTime = (1000*60*60*6) + (1000*60*30); + + +// THE FOLLOWING CHANNELS (ZERO-BASED) ARE BROKEN, AND ARE SKIPPED: +// 4,6,14, 35-46 + + +// Each row address indivual panels starting at the front right panel and moving clockwise as viewed from below +// These are the two middle (internal-use) channels: 12, 34 + +int PANELS[ROWS][COLUMNS] = { + {0, 1, 2, 3, 5, 7} , // Lowest row of panels + {8, 9, 10, 11, 13, 15} , + {16, 17, 18, 19, 20, 21} , + {22, 23, 24, 25, 26, 27} , + {28, 29, 31, 32, 33, 34} // Highest row of panels + }; + +//int PANELS[5][6] = { +// {0, 1, 2, 3, 4, 5} , // Lowest row of panels +// {6, 7, 8, 9, 10, 11} , +// {12, 13, 14, 15, 16, 17} , +// {18, 19, 20, 21, 22, 23} , +// {24, 25, 26, 27, 28, 29} // Highest row of panels +//}; + +int CHANNEL_MAPPING[] = { 8, 12, 9, 13, 14, 10, 15, 11, 7, 3, 6, 2, 1, 5, 0, 4 }; + +uint16_t debugRedOut[NUMBER_OF_PANELS]; +uint16_t debugGreenOut[NUMBER_OF_PANELS]; +uint16_t debugBlueOut[NUMBER_OF_PANELS]; + +volatile int channelColorSettings[NUMBER_OF_PANELS]; +volatile int Settings[NUMBER_OF_PANELS]; +volatile int channelColorPointer[2][NUMBER_OF_PANELS]; +volatile int randomBaseTimeOffset[NUMBER_OF_PANELS]; +volatile int randomMovementTimeOffset[NUMBER_OF_PANELS]; + +volatile uint16_t UserAdjustableMovementInterval = 2000; + +extern TLC5955* theChip; + +volatile int TheBaseDitherMode; +volatile int TheBaseEffectType; +volatile int TheBaseTimeConstant; +volatile uint32_t TheBaseEffectStartTime; +const int* TheBaseColorList; + +volatile int TheMovementPosition; +volatile int TheMoveEndPosition; +volatile int TheMovementNumberOfLevels; +volatile int NumberOfMovesSoFar; + +void (*TheMovementAddressModePtr)(int,const int*,int); + +volatile int TheMovementMode; +volatile int TheDitherMode; +volatile int TheMovementFill; +volatile int TheEffectType; +const int* TheColorList; +volatile int TheTimeConstant; +volatile int TheMoveTimeConstant; +volatile uint32_t TheEffectStartTime; +volatile int TheMode=1; // lighting effect mode. zero is off. +volatile bool inMinuteEffect = FALSE; +volatile bool inHourEffect = FALSE; + +// https://developer.mbed.org/questions/2886/Why-is-the-rand-function-not-the-least-b/ +unsigned int m_z=12434,m_w=33254; +unsigned int rnd() { + m_z = 36969 * (m_z & 65535) + (m_z >>16); + m_w = 18000 * (m_w & 65535) + (m_w >>16); + return ((m_z <<16) + m_w); +} + +void setMode(int mode) { + TheMode = mode; +} + +int getMode() { + return TheMode; +} + + +int movementToEnum(char* input) { + if (!strcmp(input,"v-")) { + return VERTICAL_UP; + } else if (!strcmp(input,"v+")) { + return VERTICAL_DOWN; + } else if (!strcmp(input,"h+")) { + return HORIZONTAL_FORWARD; + } else if (!strcmp(input,"h-")) { + return HORIZONTAL_BACKWARDS; + } else if (!strcmp(input,"r+")) { + return RADIAL_CLOCKWISE; + } else if (!strcmp(input,"r-")) { + return RADIAL_COUNTERCLOCKWISE; + } else if (!strcmp(input,"b+")) { + return BARREL_CLOCKWISE; + } else if (!strcmp(input,"b-")) { + return BARREL_COUNTERCLOCKWISE; + } else if (!strcmp(input,"al")) { + return ALL; + } else if (!strcmp(input,"of")) { + return OFF; + } else { + return -1; + } +} + +int ditherToEnum(char* input) { + if (!strcmp(input,"f")) { + return FIXED; + } else if (!strcmp(input,"c")) { + return CROSSFADE; + } else if (!strcmp(input,"i")) { + return FADE_IN; + } else if (!strcmp(input,"o")) { + return FADE_OUT_AND_IN; + } else if (!strcmp(input,"s")) { + return FADE_IN_AND_OUT; + } else if (!strcmp(input,"p")) { + return PULSED_INTENSITY; + } else { + return -1; + } +} + +int fillToEnum(char* input) { + if (!strcmp(input,"l")) { + return LINE; + } else if (!strcmp(input,"f")) { + return FILL; + } else { + return -1; + } +} + +int effectToEnum(char* input) { + if (!strcmp(input,"c")) { + return CONSTANT; + } else if (!strcmp(input,"r")) { + return RANDOM; + } else if (!strcmp(input,"s")) { + return SEQUENCE; + } else if (!strcmp(input,"f")) { + return FIXED_RANDOM; + } else { + return -1; + } +} + +// only compare first two characters. The rest are unused. +const int* colorListToPointer(char* input) { + char shortInput[3] ; + shortInput[0] = input[0]; + shortInput[1] = input[1]; + shortInput[2] = 0; + for (int i=0; i< USER_COLOR_CHOICES_SIZE; i++) { + char shortColor[3]; + shortColor[0] = listOfColorLists[i].colorListName[0]; + shortColor[1] = listOfColorLists[i].colorListName[1]; + shortColor[2] = 0; + if (!strcmp (shortColor, shortInput)) { + return listOfColorLists[i].colorList; + } + } + return NULL; +} + +// The channels on the board are numbered differently than the channels in software, so this corrects for it +int channelMapping(int channel) { + int valueToMap = channel % 16; + int channelOffset = channel / 16; + return (channelOffset*16) + CHANNEL_MAPPING[valueToMap]; +} + +int getColorArraySize(const int* colorArray) { + int i=0; + while (colorArray[i] != -1) { + i++; + } + return i; +} + +int pickRandomColorFromArray(const int* colorArray) { + int n = getColorArraySize(colorArray); + int random = colorArray[rnd() % n]; + //printf("n=%d, s=%d, r=%d\n\r",n, rnd(), random); + return random; +} + +void setArrayColor(int panel, int color, bool isBaseEffect) { + channelColorPointer[isBaseEffect][panel] = color; +} + +int getArrayColor(int panel, bool isBaseEffect) { + return channelColorPointer[isBaseEffect][panel]; +} + +int pickNextColorFromArray(int panel, const int* colorArray, bool isBaseEffect) { + channelColorPointer[isBaseEffect][panel]++; + int n = getColorArraySize(colorArray); + channelColorPointer[isBaseEffect][panel] = channelColorPointer[isBaseEffect][panel] % n; + return colorArray[channelColorPointer[isBaseEffect][panel]]; +} + +void setChannelToColorName(int channel, int colorName) { + debugRedOut[channel] = color_data[colorName].rgb.r; + debugGreenOut[channel] = color_data[colorName].rgb.g; + debugBlueOut[channel] = color_data[colorName].rgb.b; + + channel = channelMapping(channel); + theChip->setChannel(channel, + color_data[colorName].rgb.r, + color_data[colorName].rgb.g, + color_data[colorName].rgb.b); +} + +// 8 bit r,g,b for 24 bit color +void setChannelToRGB(int channel, uint16_t r, uint16_t g, uint16_t b) { + + debugRedOut[channel] = r; + debugGreenOut[channel] = g; + debugBlueOut[channel] = b; + + channel = channelMapping(channel); + theChip->setChannel(channel,r,g,b); +} + +void setStartingMovePosition() { + switch(TheMovementMode) { + case VERTICAL_UP: + TheMovementPosition=-1; + TheMoveEndPosition=4; + break; + case VERTICAL_DOWN: + TheMovementPosition=5; + TheMoveEndPosition=0; + break; + case HORIZONTAL_FORWARD: + TheMovementPosition=-1; + TheMoveEndPosition=2; + break; + case HORIZONTAL_BACKWARDS: + TheMovementPosition=3; + TheMoveEndPosition=0; + break; + case RADIAL_CLOCKWISE: + TheMovementPosition=-1; + TheMoveEndPosition=5; + + break; + case RADIAL_COUNTERCLOCKWISE: + TheMovementPosition=6; + TheMoveEndPosition=0; + break; + case BARREL_CLOCKWISE: + TheMovementPosition=-1; + TheMoveEndPosition=9; + break; + case BARREL_COUNTERCLOCKWISE: + TheMovementPosition=10; + TheMoveEndPosition=0; + break; + case ALL: + case OFF: + TheMovementPosition=-1; + TheMoveEndPosition=2; + default: + break; + } +} + +void setPanelTargetColor(int channel, int aColor) { + channelColorSettings[channel] = aColor; +} + +int getPanelTargetColor(int channel) { + return channelColorSettings[channel]; +} + +void setPanelStartingColor(int channel, int aColor) { + Settings[channel] = aColor; +} + +int getPanelStartingColor(int channel) { + return Settings[channel]; +} + + +const char* getNameFromColorNumber(int color) { + return color_data[color].name; +} + + +void setBaseEffect( int ditherMode, int effectType, const int* colorList, int timeConstant ) { + TheBaseDitherMode = ditherMode; + TheBaseEffectType = effectType; + TheBaseColorList = colorList; + TheBaseTimeConstant = timeConstant; // controls internal fades, blinking, etc. + TheBaseEffectStartTime = TheElapsedTime; + + //printf ("New Base Effect. t=%d:%d dither=%d, effect=%d, firstColor=%s, timeConstant=%d\n\r", + //(TheElapsedTime/(1000*60*60))+1, (TheElapsedTime%(1000*60*60))/(60*1000), + //ditherMode, effectType, getNameFromColorNumber(colorList[0]), timeConstant); + + if (TheBaseEffectType == CONSTANT) { + // set all panels to a randomly selected color + int theColor = pickRandomColorFromArray(colorList); + //printf ("Using constant base color=(%d), %s\n\r", theColor, getNameFromColorNumber(theColor)); + for (int i=0; i< NUMBER_OF_PANELS; i++) { + setPanelStartingColor(i, getPanelTargetColor(i)); + setArrayColor(i, theColor,1); + } + } else if (TheBaseEffectType == FIXED_RANDOM) { + // set each panel to a randomly selected color that doesn't change for the duration of the effect + for (int i=0; i< NUMBER_OF_PANELS; i++) { + setPanelStartingColor(i, getPanelTargetColor(i)); + setArrayColor(i, pickRandomColorFromArray(colorList),1); + } + } + + for (int i=0; i< NUMBER_OF_PANELS; i++) { + randomBaseTimeOffset[i] = rnd() % TheBaseTimeConstant/2; + } +} + + +void setOverlayEffect( int movementMode, int ditherMode, int movementFill, int effectType, const int* colorList, int timeConstant, int moveTimeConstant ) { + TheMovementMode = movementMode; + TheDitherMode = ditherMode; + TheMovementFill = movementFill; + TheEffectType = effectType; + TheColorList = colorList; + TheTimeConstant = timeConstant; // controls internal fades, blinking, etc. + TheMoveTimeConstant = moveTimeConstant; + TheEffectStartTime = TheElapsedTime; + NumberOfMovesSoFar = 0; + setStartingMovePosition(); + //printf ("New Movement Effect. move=%d, dither=%d, effect=%d, firstColor=%s, timeConstant=%d\n\r", + //movementMode, ditherMode, effectType, getNameFromColorNumber(colorList[0]), timeConstant); + + if (TheEffectType == CONSTANT) { + // set all panels to a randomly selected color + int theColor = pickRandomColorFromArray(colorList); + //printf ("Using constant overlay color=(%d), %s\n\r", theColor, getNameFromColorNumber(theColor)); + for (int i=0; i< NUMBER_OF_PANELS; i++) { + setPanelStartingColor(i, getPanelTargetColor(i)); + setArrayColor(i, theColor,0); + } + } else if (TheEffectType == FIXED_RANDOM) { + // set each panel to a randomly selected color that doesn't change for the duration of the effect + for (int i=0; i< NUMBER_OF_PANELS; i++) { + setPanelStartingColor(i, getPanelTargetColor(i)); + setArrayColor(i, pickRandomColorFromArray(colorList),0); + } + } + for (int i=0; i< NUMBER_OF_PANELS; i++) { + randomMovementTimeOffset[i] = rnd() % TheTimeConstant/2; + } +} + +inline void crossFade(int startingR, int startingG, int startingB, int endingR, int endingG, int endingB, uint32_t timeConstant, uint32_t currentTime, uint16_t* r, uint16_t* g, uint16_t* b) { + currentTime = currentTime % timeConstant; + + //printf("crossFrade current=%d, total=%d\n\r", currentTime, timeConstant); + + //if (currentTime > timeConstant) currentTime = timeConstant; + + *r = (uint16_t) ((((uint32_t)startingR * (uint32_t)(timeConstant - currentTime)) + ( (uint32_t)endingR * (uint32_t) currentTime)) / ((uint32_t) timeConstant)); + *g = (uint16_t) ((((uint32_t)startingG * (uint32_t)(timeConstant - currentTime)) + ( (uint32_t)endingG * (uint32_t) currentTime)) / ((uint32_t) timeConstant)); + *b = (uint16_t) ((((uint32_t)startingB * (uint32_t)(timeConstant - currentTime)) + ( (uint32_t)endingB * (uint32_t) currentTime)) / ((uint32_t) timeConstant)); + + //printf ("t=%d, c=%d, start=(%x,%x,%x), ending=(%x,%x,%x), now=%x,%x,%x\n\r", currentTime, timeConstant, startingR, startingG, startingB, endingR, endingG, endingB, *r, *b, *g ); + //printf("s={%d,%d,%d}, e={%d,%d,%d}, c={%d,%d,%d}\n\r", startingR, startingG, startingB, endingR, endingG, endingB, *r, *g, *b); +} + +inline void crossFade(int startingColor, int endingColor, uint32_t timeConstant, uint32_t currentTime, uint16_t* r, uint16_t* g, uint16_t* b) { + int startingR, startingG, startingB; + int endingR, endingG, endingB; + + startingR = color_data[startingColor].rgb.r; + startingG = color_data[startingColor].rgb.g; + startingB = color_data[startingColor].rgb.b; + + endingR = color_data[endingColor].rgb.r; + endingG = color_data[endingColor].rgb.g; + endingB = color_data[endingColor].rgb.b; + + crossFade(startingR, startingG, startingB, endingR, endingG, endingB, timeConstant, currentTime, r, g, b); +} + +void manageDither(int totalTimeForEffect, int timeIntoEffect, int startingColor, int endingColor, int panel) { + uint16_t r,g,b; + + switch (TheBaseDitherMode) { + case FIXED: + setChannelToColorName(panel, endingColor); + break; + case CROSSFADE: + crossFade(startingColor, endingColor, totalTimeForEffect, timeIntoEffect, &r, &g, &b); + setChannelToRGB(panel,r,g,b); + break; + case FADE_IN: + crossFade(COLOR_BLACK, endingColor, totalTimeForEffect, timeIntoEffect, &r, &g, &b); + setChannelToRGB(panel,r,g,b); + break; + case FADE_OUT_AND_IN: + if (timeIntoEffect < totalTimeForEffect/2) { + crossFade(startingColor, COLOR_BLACK, totalTimeForEffect/2, timeIntoEffect, &r, &g, &b); + } else { + crossFade(COLOR_BLACK, endingColor, totalTimeForEffect/2, timeIntoEffect - (totalTimeForEffect/2), &r, &g, &b); + } + setChannelToRGB(panel,r,g,b); + break; + case FADE_IN_AND_OUT: + if (timeIntoEffect < totalTimeForEffect/2) { + crossFade(COLOR_BLACK, endingColor, totalTimeForEffect/2, timeIntoEffect, &r, &g, &b); + } else { + crossFade(endingColor, COLOR_BLACK, totalTimeForEffect/2, timeIntoEffect - (totalTimeForEffect/2), &r, &g, &b); + } + setChannelToRGB(panel,r,g,b); + break; + case PULSED_INTENSITY: + if (timeIntoEffect < totalTimeForEffect/2) { + crossFade( color_data[endingColor].rgb.r/4, color_data[endingColor].rgb.g/4, color_data[endingColor].rgb.b/4, + color_data[endingColor].rgb.r, color_data[endingColor].rgb.g, color_data[endingColor].rgb.b, + totalTimeForEffect/2, timeIntoEffect, &r, &g, &b); + } else { + crossFade( color_data[endingColor].rgb.r, color_data[endingColor].rgb.g, color_data[endingColor].rgb.b, + color_data[endingColor].rgb.r/4, color_data[endingColor].rgb.g/4, color_data[endingColor].rgb.b/4, + totalTimeForEffect/2, timeIntoEffect, &r, &g, &b); + } + setChannelToRGB(panel,r,g,b); + break; + default: + break; + } +} + + +int isTimeForNewColor(int timeConstant, int timeSoFar) { + if (timeSoFar % timeConstant < CLOCK_GRANULARITY) { + //printf("isTime! soFar=%d, c=%d\n\r", timeSoFar, timeConstant); + return true; + } else { + return false; + } +} + + +void manageEffectType(int panel, const int* colorList, int isBaseEffect) { + uint32_t startTime, timeConstant; + int effectType; + + if (isBaseEffect) { + effectType = TheBaseEffectType; + timeConstant = TheBaseTimeConstant - (effectType == RANDOM) ? randomBaseTimeOffset[panel] : 0; + startTime = TheBaseEffectStartTime; + } else { + effectType = TheEffectType; + timeConstant = TheTimeConstant - (effectType == RANDOM) ? randomMovementTimeOffset[panel] : 0; + startTime = TheEffectStartTime; + } + + + + switch (effectType) { + case CONSTANT: + case FIXED_RANDOM: + // the colors are selected when the effect is first created + setPanelStartingColor(panel, getArrayColor(panel, isBaseEffect)); + setPanelTargetColor(panel, getArrayColor(panel, isBaseEffect)); + break; + case RANDOM: + // TODO: Change to a random number that is picked so that the average length will be elapsed time. (probability at elapsedTime is 50% that it will be time) + if (isTimeForNewColor(timeConstant, TheElapsedTime-startTime)) { + setPanelStartingColor(panel, getPanelTargetColor(panel)); + setPanelTargetColor(panel, pickRandomColorFromArray(colorList)); + if (isBaseEffect) { + randomBaseTimeOffset[panel] = rnd() % TheBaseTimeConstant / 2; + } else { + randomMovementTimeOffset[panel] = rnd() % TheTimeConstant / 2; + } + } + break; + case SEQUENCE: + if (isTimeForNewColor(timeConstant, TheElapsedTime-startTime)) { + setPanelStartingColor(panel, getPanelTargetColor(panel)); + int nextColor = pickNextColorFromArray(panel,colorList,isBaseEffect); + setPanelTargetColor(panel, nextColor); + } + // Dither from old sequence color to new sequence color + break; + case RANDOM_STROBE: + // fluctuate intensity randomly + break; + default: + break; + } + manageDither(timeConstant, TheElapsedTime-startTime, getPanelStartingColor(panel), getPanelTargetColor(panel), panel); +} + +// top-to-bottom - 5 levels +void addressVertically (int location, const int* colorList, int isBaseEffect) { + if (location >= 0 && location < 5) { + for (int i = 0; i < 6; i++) { + manageEffectType(PANELS[location][i], colorList, isBaseEffect); + } + } +} + +// front-to-back - 3 levels +void addressHorizontally (int location, const int* colorList, int isBaseEffect) { + if (location >= 0 && location < 3) { + for (int i = 0; i < 5; i++) { + manageEffectType(PANELS[i][location], colorList, isBaseEffect); + manageEffectType(PANELS[i][5-location], colorList, isBaseEffect); + } + } +} + +void addressHorizontalRadially (int location, const int* colorList, int isBaseEffect) { + if (location >= 0 && location < 6) { + for (int i = 0; i < 5; i++) { + manageEffectType(PANELS[i][location], colorList, isBaseEffect); + } + } +} + +void addressVerticallyRadially(int location, const int* colorList, int isBaseEffect) { + if (location >= 0 && location < 5) { + for (int i = 0; i < 3; i++) { + manageEffectType(PANELS[location][i], colorList, isBaseEffect); + } + } + if (location >= 5 && location < 10) { + for (int i = 3; i < 6; i++) { + manageEffectType(PANELS[9-location][i], colorList, isBaseEffect); + } + } +} + +int getMovementLevels(int aMovementMode) { + switch (aMovementMode) { + case VERTICAL_UP: + return ROWS; + case VERTICAL_DOWN: + return ROWS; + case HORIZONTAL_FORWARD: + return 3; + case HORIZONTAL_BACKWARDS: + return 3; + case RADIAL_CLOCKWISE: + return 6; + case RADIAL_COUNTERCLOCKWISE: + return 6; + case BARREL_CLOCKWISE: + return 10; + case BARREL_COUNTERCLOCKWISE: + return 10; + case ALL: + return 1; + case OFF: + return 1; + default: + return 1; + } +} + +void manageMovement() { + // Track which panels get painted with the Base information, and which get the effect information + + // cycle through moves at TheMoveTimeConstant rate + //printf (">> t=%d, c=%d, %d <? %d\n\r", (TheElapsedTime-TheEffectStartTime), TheMoveTimeConstant, (TheElapsedTime-TheEffectStartTime) % TheMoveTimeConstant, CLOCK_GRANULARITY); + + if ((TheElapsedTime-TheEffectStartTime) % TheMoveTimeConstant < CLOCK_GRANULARITY) { + //printf ("Moving. Mode=%d\n\r", TheMovementMode); + switch(TheMovementMode) { + case VERTICAL_UP: + TheMovementPosition++; + TheMovementNumberOfLevels = ROWS; + TheMovementAddressModePtr = &addressVertically; + break; + case VERTICAL_DOWN: + TheMovementPosition--; + TheMovementNumberOfLevels = ROWS; + TheMovementAddressModePtr = &addressVertically; + break; + case HORIZONTAL_FORWARD: + TheMovementPosition++; + TheMovementNumberOfLevels = 3; + TheMovementAddressModePtr = &addressHorizontally; + break; + case HORIZONTAL_BACKWARDS: + TheMovementPosition--; + TheMovementNumberOfLevels = 3; + TheMovementAddressModePtr = &addressHorizontally; + break; + case RADIAL_CLOCKWISE: + TheMovementNumberOfLevels = 6; + TheMovementPosition++; + TheMovementAddressModePtr = &addressHorizontalRadially; + break; + case RADIAL_COUNTERCLOCKWISE: + TheMovementNumberOfLevels = 6; + TheMovementPosition--; + TheMovementAddressModePtr = &addressHorizontalRadially; + break; + case BARREL_CLOCKWISE: + TheMovementPosition++; + TheMovementNumberOfLevels = 10; + TheMovementAddressModePtr = &addressVerticallyRadially; + break; + case BARREL_COUNTERCLOCKWISE: + TheMovementPosition--; + TheMovementNumberOfLevels = 10; + TheMovementAddressModePtr = &addressVerticallyRadially; + break; + case ALL: + TheMovementPosition = 0; + TheMovementNumberOfLevels = 3; + TheMovementAddressModePtr = &addressHorizontally; + break; + case OFF: + TheMovementPosition = 0; + TheMovementNumberOfLevels = 3; + TheMovementAddressModePtr = &addressHorizontally; + break; + default: + break; + } + if (TheMovementMode != OFF) { + NumberOfMovesSoFar++; + //printf ("Incrementing moves to: %d\n\r", NumberOfMovesSoFar); + } + } + // end the move after it has reached its end + if (NumberOfMovesSoFar > TheMovementNumberOfLevels) { + //printf ("TURNING MOVE OFF.\n\r"); + TheMovementMode = OFF; + NumberOfMovesSoFar = 0; + } + + if (TheMovementPosition < 0) TheMovementPosition = TheMovementNumberOfLevels-1; + TheMovementPosition = TheMovementPosition % TheMovementNumberOfLevels; + + + //printf("mode=%d, pos=%d, levels=%d, numMoves=%d\n\r",TheMovementMode, TheMovementPosition, TheMovementNumberOfLevels, NumberOfMovesSoFar); + + // paint all the subeffects at 40Hz (or whatever the refresh rate is) + for (int i=0; i < TheMovementNumberOfLevels; i++) { + if (TheMovementMode != OFF && (i < TheMovementPosition && TheMovementFill == FILL || (TheMovementMode == ALL))) { + TheMovementAddressModePtr (i, TheColorList, false); + } else if (i == TheMovementPosition && (TheMovementMode != OFF)){ + TheMovementAddressModePtr (i, TheColorList, false); + } else { + TheMovementAddressModePtr (i, TheBaseColorList, true); + } + } + + // Latch the current color settings into the hardware + theChip->latchData(); +} + +const int* getRandomMovementColorList() { + return movementColors[rnd() % MOVEMENT_COLOR_LIST_SIZE]; +} + +const int* getRandomBaseColorList() { + return baseColors[rnd() % BASE_COLOR_LIST_SIZE]; +} + +const int* getRandomColorList() { + return listOfColorLists[rnd() % USER_COLOR_CHOICES_SIZE].colorList; +} + +void generateBaseEffect(bool isBackgroundYellow) { + if (TheMode == USER_BASE || TheMode == USER_MOVEMENT_AND_BASE) { + return; // already set by user + } else { + int randomDitherMode = rnd() % 6; + int randomEffectType = rnd() % 4; + const int* randomColorList; + if (isBackgroundYellow) { + randomColorList = getRandomBaseColorList(); + } else { + randomColorList = getRandomMovementColorList(); + } + int timeConstant = 300 + (rnd() % 8000); + setBaseEffect( randomDitherMode, randomEffectType, randomColorList, timeConstant ); + } +} + +void generateMovementEffect(int durationMS) { + + // select a random movement effect to overlay onto the base effect + if (TheMode == USER_MOVEMENT || TheMode == USER_MOVEMENT_AND_BASE) { + return; // already set by user + } else { + int randomMovementMode = rnd() % 9; + int randomDitherMode = rnd() % 6; + int randomMovementFill = rnd() % 3; + int randomEffectType = rnd() % 4; + const int* randomColorList = getRandomMovementColorList(); + int moveTimeConstant = (durationMS*(3+(rnd()%5))/8) / getMovementLevels(randomMovementMode); + //int timeConstant = ((durationMS*7)/8) / getMovementLevels(randomMovementMode); + int timeConstant = 200 + (rnd() % (durationMS-200)); + setOverlayEffect( randomMovementMode, randomDitherMode, randomMovementFill, randomEffectType, randomColorList, timeConstant, moveTimeConstant); + //printf ("New Movement Effect. move=%d, dither=%d, effect=%d, firstColor=%s, timeConstant=%d\n\r", + } +} + +// hour is zero based, starting at midnight +void generateHourlyMovement(int hour, int msIntoEffect) { + inHourEffect = TRUE; + + if (msIntoEffect < 2000) { + // turn off for two seconds + setChannelToColorName (BEAK, COLOR_BLACK); + setChannelToColorName (EYES, COLOR_BLACK); + setBaseEffect( CROSSFADE, CONSTANT, Black, 500 ); + setOverlayEffect( ALL, FIXED, LINE, CONSTANT, Black, 2000, 2000); + } else if (msIntoEffect < 3000 + (hour*1000)) { + if (msIntoEffect%1000 < 500) { + //normal + setChannelToColorName (BEAK, COLOR_ORANGE_WEB_COLOR); + setChannelToColorName (EYES, COLOR_RED); + setBaseEffect( FIXED, CONSTANT, OneYellow, 500 ); + setOverlayEffect( OFF, FIXED, LINE, CONSTANT, Black, 500, 500); + } else { + //weird + setChannelToColorName (BEAK, COLOR_HUNTER_GREEN); + setChannelToColorName (EYES, COLOR_BLUE); + setBaseEffect( FIXED, RANDOM, Electrics, 100 ); + setOverlayEffect( OFF, FIXED, LINE, CONSTANT, Black, 500, 500); + } + + } else if (msIntoEffect < 4000 + (hour*1000)) { + // turn off for one seconds + setChannelToColorName (BEAK, COLOR_BLACK); + setChannelToColorName (EYES, COLOR_BLACK); + setChannelToColorName(INTERNAL1, COLOR_RED); + setChannelToColorName(INTERNAL2, COLOR_RED); + setBaseEffect( CROSSFADE, CONSTANT, Black, 500 ); + setOverlayEffect( ALL, FIXED, LINE, CONSTANT, Black, 2000, 2000); + } else { + inHourEffect = FALSE; + // select a new yellow (normal duck) base effect + generateBaseEffect(true); + setChannelToColorName (BEAK, COLOR_ORANGE_WEB_COLOR); + setChannelToColorName (EYES, COLOR_BLUE); + setChannelToColorName(INTERNAL1, COLOR_WHITE); + setChannelToColorName(INTERNAL2, COLOR_WHITE); + } +} + +void generatePerMinuteMovement(int msIntoEffect) { + inMinuteEffect = TRUE; + + // end after 4 seconds + if (msIntoEffect >= 4000) { + inMinuteEffect = FALSE; + // select a new yellow (normal duck) base effect + generateBaseEffect(true); + setChannelToColorName (BEAK, COLOR_ORANGE_WEB_COLOR); + setChannelToColorName (EYES, COLOR_BLUE); + setChannelToColorName(INTERNAL1, COLOR_WHITE); + setChannelToColorName(INTERNAL2, COLOR_WHITE); + } else { + if (msIntoEffect % 500 < CLOCK_GRANULARITY) { + // do some number of movement effects in a row, twice a second + generateBaseEffect(false); + generateMovementEffect(500); + setChannelToColorName (BEAK, COLOR_HUNTER_GREEN); + setChannelToColorName (EYES, COLOR_RED); + setChannelToColorName(INTERNAL1, COLOR_RED); + setChannelToColorName(INTERNAL2, COLOR_RED); + } + } +} + + +// This could also be used to beat match, hence the duration +void generatePerSecondMovement(int durationMS) { + // single movement effect + // if the movement effect is a fill, it will be restored in the next cycle + generateMovementEffect(durationMS); +} + + +void manageTimedInteractions() { + TheElapsedTime += CLOCK_GRANULARITY; // called every 25mS + if (TheElapsedTime % 43200000 < CLOCK_GRANULARITY) { + TheElapsedTime = 0; // reset timer at midnight + } else if (TheElapsedTime % 3600000 < CLOCK_GRANULARITY || inHourEffect) { + // hourly tasks (reset timer) + generateHourlyMovement(TheElapsedTime / 3600000, TheElapsedTime % 3600000); + } else if (TheElapsedTime % 60000 < CLOCK_GRANULARITY || inMinuteEffect) { + // one minute tasks + generatePerMinuteMovement(TheElapsedTime % 60000); + } else if (TheElapsedTime % UserAdjustableMovementInterval < CLOCK_GRANULARITY) { + // one second tasks + generatePerSecondMovement(UserAdjustableMovementInterval); + } + + // This manages all the effects + manageMovement(); +} + +// called prior to effects to initialize everything +void SequencerConfig() { + for (int i=0;i<NUMBER_OF_PANELS;i++) { + channelColorPointer[0][i]=0; // default to first color in list + channelColorPointer[1][i]=0; // default to first color in list + channelColorSettings[i] = COLOR_BLACK; + Settings[i] = COLOR_BLACK; + } + + TheMovementPosition = 0; + TheMovementNumberOfLevels = 3; + TheMovementAddressModePtr = &addressHorizontally; + + // Set a default base effect + //setBaseEffect( CROSSFADE, RANDOM, Electrics, 300 ); + //setBaseEffect( CROSSFADE, RANDOM, KulorSunsetCamping, 500 ); + //setBaseEffect( CROSSFADE, RANDOM, TestColors, 2000 ); + setBaseEffect( FIXED, CONSTANT, GoodYellows, 5000 ); + + // Set overlay effect to a reasonable neutral setting (OFF) + setOverlayEffect(OFF , FIXED , LINE, CONSTANT, All, 1000, 1000 ); + setChannelToColorName (BEAK, COLOR_ORANGE_WEB_COLOR); + setChannelToColorName (EYES, COLOR_BLUE); + setChannelToColorName(INTERNAL1, COLOR_WHITE); + setChannelToColorName(INTERNAL2, COLOR_WHITE); + +} + +void convertStringToLowercase(const char* input, char*output) { + for (int i=0; i<strlen(input);i++) { + output[i] = tolower(input[i]); + } + output[strlen(input)] = 0; +} + +int getColorByName(char* colorName) { + char lowerCaseInput[64]; + char lowerCaseSearch[64]; + + convertStringToLowercase(colorName, lowerCaseInput); + for (int i=0; i< COLOR_NAMES_MAX; i++) { + convertStringToLowercase(color_data[i].name, lowerCaseSearch); + if (!strcmp(lowerCaseSearch, lowerCaseInput)) { + return i; + } + } + return -1; +} + +void debugInteractions() { + // Display panel settings using a text output + printf ("t=%d\n\r", TheElapsedTime); + for (int i=0;i<ROWS;i++) { + for (int j=0;j<COLUMNS;j++) { + //printf ("-%d", (channelColorSettings[PANELS[i][j]])); + //printf ("-%u:%u:%u", debugRedOut[PANELS[i][j]], debugGreenOut[PANELS[i][j]], debugBlueOut[PANELS[i][j]]); + printf ("-%s", getNameFromColorNumber(channelColorSettings[PANELS[i][j]])); + } + printf ("\n\r"); + } + printf("\n\r\n\r"); +} + +// called at 40Hz from main's Ticker +uint16_t timeCount=0; +uint16_t count=0; +void Sequencer() { + led1 = !led1; + + if (TheMode == TEST) { + return; // mode zero turns the sequencer off. Useful for testing or setting up a static display + } else if (TheMode == SUN) { + // sun mode + for (int i=0; i< NUMBER_OF_PANELS;i++) { + setChannelToColorName(i, COLOR_WHITE); + } + theChip->latchData(); + return; + } + + // mode 1 is the standard mode + manageTimedInteractions(); + if (SEQUENCER_RATE >= 0.25) { + // only show debug output if the sequencer is running slow enough + debugInteractions(); + } +} \ No newline at end of file