Published
Dependencies: BLE_API TLC5955 mbed nRF51822
Fork of BLE_LoopbackUART by
sequencer.cpp
- Committer:
- roysandberg
- Date:
- 2018-06-09
- Revision:
- 14:73923b07ae4a
File content as of revision 14:73923b07ae4a:
/* 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(); } }