├── .gitignore ├── Brightness.xlsx ├── Diagram.png ├── anim.cpp ├── anim.h ├── animGlow.cpp ├── anim_bt.cpp ├── anim_fly.cpp ├── anim_pixiedust.cpp ├── anim_randcyc.cpp ├── anim_run.cpp ├── anim_sparkr.cpp ├── anim_spread.cpp ├── anim_stars.cpp ├── anim_start.cpp ├── arws2812.ino ├── brightness.h ├── button_classes.hpp ├── color.h ├── commands.h ├── palette.cpp ├── palette.h ├── readme.MD └── small_timer.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /Brightness.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vasil-Pahomov/ArWs2812/529b040a7a68b5e1e12319f81a9c7bb924244858/Brightness.xlsx -------------------------------------------------------------------------------- /Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vasil-Pahomov/ArWs2812/529b040a7a68b5e1e12319f81a9c7bb924244858/Diagram.png -------------------------------------------------------------------------------- /anim.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "color.h" 3 | #include "palette.h" 4 | #include "anim.h" 5 | #include "brightness.h" 6 | 7 | //Adafruit's class to operate strip 8 | Adafruit_NeoPixel pixels = Adafruit_NeoPixel(LEDS, PIN, NEO_GRB + NEO_KHZ800); 9 | 10 | 11 | 12 | Anim::Anim() 13 | { 14 | nextms = millis(); 15 | } 16 | 17 | void Anim::setPeriod(byte period) { 18 | this->period = period; 19 | } 20 | 21 | void Anim::setPalette(Palette * pal) { 22 | this->palette = pal; 23 | if (setUpOnPalChange) { 24 | setUp(); 25 | } 26 | pinMode(LED_BUILTIN, OUTPUT); 27 | } 28 | 29 | bool Anim::run() 30 | { 31 | if ( millis()<=nextms) { 32 | digitalWrite(LED_BUILTIN, LOW); 33 | return false; 34 | } 35 | digitalWrite(LED_BUILTIN, HIGH); 36 | nextms=millis() + period; 37 | 38 | if (runImpl != NULL) 39 | { 40 | (this->*runImpl)(); 41 | } 42 | 43 | //transition coef, if within 0..1 - transition is active 44 | //changes from 1 to 0 during transition, so we interpolate from current color to previous 45 | float transc = (float)((long)transms - (long)millis()) / TRANSITION_MS; 46 | Color * leds_prev = (leds == leds1) ? leds2 : leds1; 47 | 48 | if (transc > 0) { 49 | for(int i=0; i*setUpImpl)(); 90 | } 91 | } 92 | 93 | void Anim::doSetUp() 94 | { 95 | if (!setUpOnPalChange) { 96 | setUp(); 97 | } 98 | } 99 | 100 | void Anim::setAnim(byte animInd) 101 | { 102 | switch (animInd) { 103 | case 0: 104 | setUpImpl = &Anim::animRun_SetUp; 105 | runImpl = &Anim::animRun_Run; 106 | setUpOnPalChange = true; 107 | break; 108 | case 1: 109 | setUpImpl = &Anim::animPixieDust_SetUp; 110 | runImpl = &Anim::animPixieDust_Run; 111 | setUpOnPalChange = true; 112 | break; 113 | case 2: 114 | setUpImpl = &Anim::animSparkr_SetUp; 115 | runImpl = &Anim::animSparkr_Run; 116 | setUpOnPalChange = true; 117 | break; 118 | case 3: 119 | setUpImpl = &Anim::animRandCyc_SetUp; 120 | runImpl = &Anim::animRandCyc_Run; 121 | setUpOnPalChange = true; 122 | break; 123 | case 4: 124 | setUpImpl = &Anim::animStars_SetUp; 125 | runImpl = &Anim::animStars_Run; 126 | setUpOnPalChange = false; 127 | break; 128 | case 5: 129 | setUpImpl = &Anim::animSpread_SetUp; 130 | runImpl = &Anim::animSpread_Run; 131 | setUpOnPalChange = false; 132 | break; 133 | case 6: 134 | setUpImpl = &Anim::animFly_SetUp; 135 | runImpl = &Anim::animFly_Run; 136 | setUpOnPalChange = false; 137 | break; 138 | case 7: //special 139 | setUpImpl = &Anim::animBT_SetUp; 140 | runImpl = &Anim::animBT_Run; 141 | setUpOnPalChange = false; 142 | break; 143 | default: 144 | setUpImpl = &Anim::animStart_SetUp; 145 | runImpl = &Anim::animStart_Run; 146 | setUpOnPalChange = true; 147 | break; 148 | } 149 | } 150 | 151 | 152 | 153 | unsigned int rng() { 154 | static unsigned int y = 0; 155 | y += micros(); // seeded with changing number 156 | y ^= y << 2; y ^= y >> 7; y ^= y << 7; 157 | return (y); 158 | } 159 | 160 | byte rngb() { 161 | return (byte)rng(); 162 | } 163 | 164 | 165 | Color Anim::leds1[LEDS]; 166 | Color Anim::leds2[LEDS]; 167 | Color Anim::ledstmp[LEDS]; 168 | byte Anim::seq[LEDS]; 169 | -------------------------------------------------------------------------------- /anim.h: -------------------------------------------------------------------------------- 1 | #ifndef anim_h 2 | #define anim_h 3 | #include 4 | #include "palette.h" 5 | 6 | #define PIN 2 // WS2812 pin number 7 | #define LEDS 99 // number of LEDs in the strip. Not sure why, but 100 leds don't work with software serial! Works with hardware serial though 8 | #define BRIGHTNESS 256// brightness adjustment, up to 256 9 | 10 | #define TRANSITION_MS 1000 // transition time between animations, ms 11 | 12 | // brigthness animation amplitude shift. true BrA amplitude is calculated as (0..127) value shifted right by this amount 13 | #define BRA_AMP_SHIFT 1 14 | // brigthness animation amplitude offset 15 | #define BRA_OFFSET (222-64) 16 | 17 | //probability of spark when in idle plase 18 | #define SPARK_PROB 3 19 | 20 | class Anim { 21 | 22 | private: 23 | //Color arrays - two for making transition 24 | static Color leds1[LEDS]; 25 | static Color leds2[LEDS]; 26 | //auxiliary colors array 27 | static Color ledstmp[LEDS]; 28 | 29 | void animStart(); 30 | 31 | // length of animation timeslot (period) 32 | byte period; 33 | // array of Color to work with 34 | Color *leds; 35 | Palette *palette; 36 | 37 | // millis for next timeslot 38 | unsigned long nextms; 39 | // millis to transition end 40 | unsigned long transms; 41 | 42 | int phase; 43 | int pos; 44 | int inc; 45 | 46 | //whether to call SetUp on palette change 47 | //(some animations require full transition with fade, otherwise the colors would change in a step, some not) 48 | bool setUpOnPalChange; 49 | 50 | Color curColor = Color(0); 51 | Color prevColor = Color(0); 52 | 53 | Color sparkleColor = Color(0xFFFFFF); 54 | 55 | static byte seq[LEDS]; 56 | 57 | //brigthness animation (BrA) current initial phase 58 | byte braPhase; 59 | //braPhase change speed 60 | byte braPhaseSpd=5; 61 | //BrA frequency (spatial) 62 | byte braFreq=150; 63 | 64 | //glow animation setup 65 | void glowSetUp(); 66 | 67 | //glow animation - must be called for each LED after it's BASIC color is set 68 | //note this overwrites the LED color, so the glow assumes that color will be stored elsewhere (not in leds[]) 69 | //or computed each time regardless previous leds[] value 70 | void glowForEachLed(int i); 71 | 72 | //glow animation - must be called at the end of each animaton run 73 | void glowRun(); 74 | 75 | void setUp(); 76 | 77 | //run and setup handlers 78 | void (Anim::*runImpl)(); 79 | void (Anim::*setUpImpl)(); 80 | 81 | 82 | //animation implementations 83 | void animStart_SetUp(); 84 | void animStart_Run(); 85 | 86 | void animRun_SetUp(); 87 | void animRun_Run(); 88 | 89 | void animPixieDust_SetUp(); 90 | void animPixieDust_Run(); 91 | 92 | void animSparkr_SetUp(); 93 | void animSparkr_Run(); 94 | 95 | void animRandCyc_SetUp(); 96 | void animRandCyc_Run(); 97 | 98 | void animStars_SetUp(); 99 | void animStars_Run(); 100 | 101 | void animSpread_SetUp(); 102 | void animSpread_Run(); 103 | 104 | void animFly_SetUp(); 105 | void animFly_Run(); 106 | 107 | void animBT_SetUp(); 108 | void animBT_Run(); 109 | 110 | public: 111 | 112 | 113 | Anim(); 114 | void setPeriod(byte period); 115 | void setPalette(Palette * pal); 116 | void setAnim(byte animInd); 117 | bool run();//returns true if actual change has completed, or false if it's dummy call (previous call was too recent in time) 118 | void doSetUp(); 119 | 120 | }; 121 | 122 | unsigned int rng(); 123 | 124 | byte rngb(); 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /animGlow.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | 3 | void Anim::glowSetUp() 4 | { 5 | braPhaseSpd = random(8,13); 6 | braFreq = random(40,120); 7 | } 8 | 9 | void Anim::glowForEachLed(int i) 10 | { 11 | int bra = (char) (braPhase + i * braFreq); 12 | bra = BRA_OFFSET + (abs(bra) >> BRA_AMP_SHIFT); 13 | leds[i] = leds[i].brightness((int)bra); 14 | } 15 | 16 | void Anim::glowRun() 17 | { 18 | braPhase += braPhaseSpd; 19 | } 20 | -------------------------------------------------------------------------------- /anim_bt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "anim.h" 3 | #include "color.h" 4 | #include "palette.h" 5 | #include "commands.h" 6 | 7 | extern bool commandComplete; 8 | extern byte command[]; 9 | 10 | //width of "spot" controlled by phone, in leds 11 | #define SPOT_WIDTH 10 12 | 13 | void Anim::animBT_SetUp() { 14 | leds[10] = sparkleColor; 15 | } 16 | 17 | void Anim::animBT_Run() { 18 | Color col; 19 | int pos = -LEDS; 20 | if (commandComplete && command[0] == CMD_MPOS) { 21 | pos = command[1]*LEDS/256; 22 | byte cin = command[2]; 23 | if (cin <= 63) { 24 | col.r = cin*4; 25 | col.g = (63-cin)*4; 26 | col.b = 0; 27 | } else if (cin <= 127) { 28 | col.r = 255; 29 | col.g = (cin - 64)*4; 30 | col.b = col.g; 31 | } else if (cin <= 191) { 32 | col.r = (191 - cin) * 4; 33 | col.g = col.r; 34 | col.b = 255; 35 | } else { 36 | col.r = 0; 37 | col.g = (cin - 192) * 4; 38 | col.b = (255 - cin) * 4; 39 | } 40 | Serial.print('M');Serial.print(pos);Serial.print('-');Serial.println(cin); 41 | } 42 | 43 | 44 | for (int i=0;i=0) && ((i-pos) < SPOT_WIDTH)) { 47 | leds[i] = col; 48 | } 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /anim_fly.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | #include "color.h" 3 | #include "palette.h" 4 | 5 | 6 | void Anim::animFly_SetUp() { 7 | //length of particle tail 8 | pos = random(2, 15); 9 | //probability of the tail 10 | inc = random(5, 15); 11 | if (random(10) > 5) { 12 | inc = -inc; 13 | } 14 | phase = 0; 15 | } 16 | 17 | void Anim::animFly_Run() { 18 | 19 | byte launchpos; 20 | if (inc > 0) { 21 | launchpos = LEDS-1; 22 | for (int i=1;i=0;i--) { 28 | leds[i+1] = leds[i]; 29 | } 30 | } 31 | 32 | if (random(abs(inc)) == 0) { 33 | curColor = palette->getPalColor((float)rngb()/256); 34 | phase = pos; 35 | } 36 | 37 | leds[launchpos] = Color( (int)curColor.r * phase / pos, (int)curColor.g * phase / pos, (int)curColor.b * phase / pos) ; 38 | if (phase > 0) phase--; 39 | } 40 | -------------------------------------------------------------------------------- /anim_pixiedust.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | #include "color.h" 3 | #include "palette.h" 4 | 5 | #define DUST_LENGTH 20 6 | void Anim::animPixieDust_SetUp() { 7 | phase = 0; 8 | curColor = palette->getPalColor((float)rng()/256); 9 | prevColor = palette->getPalColor((float)rng()/256); 10 | inc = random(2)*2-1; 11 | if (inc > 0) { 12 | phase = -DUST_LENGTH/2; 13 | } else { 14 | phase = LEDS + DUST_LENGTH/2; 15 | } 16 | glowSetUp(); 17 | } 18 | 19 | void Anim::animPixieDust_Run() { 20 | 21 | if (inc > 0) { 22 | for (int i=0;i phase) ? prevColor : curColor; 24 | glowForEachLed(i); 25 | } 26 | phase++; 27 | if (phase >= 4*LEDS) { 28 | phase = -DUST_LENGTH/2; 29 | prevColor = curColor; 30 | curColor = palette->getPalColor((float)rngb()/256); 31 | } 32 | } else { 33 | for (int i=0;igetPalColor((float)rngb()/256); 43 | } 44 | } 45 | } 46 | glowRun(); 47 | 48 | for (int k = phase-DUST_LENGTH/2; k < (phase + DUST_LENGTH/2); k++ ) { 49 | if (k >= 0 && k < LEDS) { 50 | int mix = abs(k-phase) * 255 / DUST_LENGTH + random(-100, 100); 51 | if (mix < 0) { 52 | mix = 0; 53 | } else if (mix > 255) { 54 | mix = 255; 55 | } 56 | leds[k] = sparkleColor.interpolate(leds[k], (float)mix/255); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /anim_randcyc.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | 3 | void Anim::animRandCyc_SetUp() { 4 | for (int i=0;igetPalColor((float)seq[i] / 256); 12 | seq[i]+=rngb() >> 6; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /anim_run.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | #include "color.h" 3 | #include "palette.h" 4 | 5 | 6 | void Anim::animRun_SetUp() { 7 | pos = 0; 8 | inc = 1 + (rngb() >> 5); 9 | if (random(10) > 5) { 10 | inc = -inc; 11 | } 12 | } 13 | 14 | void Anim::animRun_Run() { 15 | 16 | int p = pos; 17 | for (int i=0;igetPalColor((float)p/256); 19 | leds[i] = c; 20 | 21 | p = p + inc; 22 | if (p >= 256) { 23 | p = p - 256; 24 | } else if (p < 0) { 25 | p = p + 256; 26 | } 27 | } 28 | pos = pos + 1; 29 | } 30 | -------------------------------------------------------------------------------- /anim_sparkr.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | 3 | void AnimSparkr_initSeq(byte * seq) 4 | { 5 | for (int i=0; igetPalColor((float)rngb()/256); 27 | prevColor = palette->getPalColor((float)rngb()/256); 28 | AnimSparkr_initSeq(seq); 29 | AnimSparkr_shuffleSeq(seq); 30 | 31 | 32 | } 33 | 34 | void Anim::animSparkr_Run() { 35 | for (int i=0;i phase) 39 | ? prevColor 40 | : (i == phase) ? sparkleColor : curColor; 41 | glowForEachLed(i); 42 | } 43 | glowRun(); 44 | 45 | if (phase > LEDS) { 46 | if (random(SPARK_PROB) == 0) { 47 | int i = (int)rngb() * LEDS / 256; 48 | leds[i] = sparkleColor; 49 | 50 | } 51 | } 52 | 53 | phase++; 54 | if (phase > 2*LEDS) { 55 | phase = 0; 56 | prevColor = curColor; 57 | while (prevColor.isCloseTo(curColor)) { 58 | curColor = palette->getPalColor((float)rngb()/256); 59 | } 60 | AnimSparkr_shuffleSeq(seq); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /anim_spread.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | #include "color.h" 3 | #include "palette.h" 4 | 5 | #define SPREAD_MAX_WIDTH 20 6 | 7 | void Anim::animSpread_SetUp() { 8 | inc = random(2,8); 9 | memset(seq, 0, LEDS); 10 | } 11 | 12 | void Anim::animSpread_Run() { 13 | memset(leds, 0, 3*LEDS); 14 | 15 | for (int i=0;i 0) { 17 | byte width = SPREAD_MAX_WIDTH - seq[i]; 18 | for (int j=i-width;j<=(i+width);j++) { 19 | Color c = ledstmp[i]; 20 | if (j>=0 && jgetPalColor((float)rngb()/256); 34 | seq[pos] = SPREAD_MAX_WIDTH; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /anim_stars.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | #include "color.h" 3 | #include "palette.h" 4 | 5 | //seq keeps phases: 0..127 increasing, 128..255 decreasing, ends at 255 (steady off) 6 | //ledstmp keeps color of stars 7 | 8 | void Anim::animStars_SetUp() { 9 | //inc is (average) interval between appearance of new stars 10 | inc = random (2, 5); 11 | 12 | //reset all phases 13 | memset(seq, 255, LEDS); 14 | } 15 | 16 | void Anim::animStars_Run() { 17 | for (byte i=0;i 250) { 37 | seq[pos] = 0; 38 | ledstmp[pos] = palette->getPalColor((float)rngb()/256); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /anim_start.cpp: -------------------------------------------------------------------------------- 1 | #include "anim.h" 2 | 3 | void Anim::animStart_SetUp() { 4 | phase = 0; 5 | } 6 | 7 | void Anim::animStart_Run() { 8 | if (phase < LEDS) { 9 | leds[phase].r = 255; 10 | leds[phase].g = 255; 11 | leds[phase].b = 255; 12 | for(int i=0; i= LEDS) 16 | { 17 | for(int i=0; i> 8); 19 | r = min(r,255); leds[i].r = (byte)max(r,0); 20 | int g = LEDS + 255 - phase + (rng() >> 8); 21 | g = min(g,255); leds[i].g = (byte)max(g,0); 22 | int b = LEDS + 255 - phase + (rng() >> 8); 23 | b = min(b,255); leds[i].b = (byte)max(b,0); 24 | } 25 | phase++; 26 | } 27 | 28 | phase++; 29 | } 30 | -------------------------------------------------------------------------------- /arws2812.ino: -------------------------------------------------------------------------------- 1 | // use button 2 | #define BUTTON_NEXT_EFF 3 | // use Bluetooth 4 | #define BT 5 | //whether to use hardware serial to communicate Bluetooth. Software serial is used otherwise 6 | //#define BTHS 7 | //if defined, debug data is output to hardware serial port. REMEMBER TO REMOVE this definition once BTHS is set 8 | //#define DEBUG 9 | 10 | #include 11 | 12 | #ifdef BT 13 | #define _SS_MAX_RX_BUFF 8 //lower SoftwareSerial's receive buffer to conserve some RAM 14 | #include 15 | #endif 16 | 17 | #include "palette.h" 18 | #include "anim.h" 19 | #include "commands.h" 20 | #include "small_timer.hpp" 21 | #ifdef BUTTON_NEXT_EFF 22 | #include "button_classes.hpp" 23 | #endif 24 | 25 | #define ANIMS 7 //number of animations 26 | #define PALS 7 //number of palettes 27 | #define INTERVAL 10000 //change interval, msec 28 | 29 | 30 | constexpr auto pinButtonNextEff = A0; // button pin for next effect 31 | constexpr bool buttonNextEffInverse = true; // options: inverse "button for next effect" 32 | 33 | Palette * pals[PALS] = {&PalRgb, &PalRainbow, &PalRainbowStripe, &PalParty, &PalHeat, &PalFire, &PalIceBlue}; 34 | 35 | Anim anim = Anim(); 36 | 37 | #ifdef BT 38 | #ifndef BTHS 39 | //11 and 12 are RX (from BT) and TX (to BT) pin numbers 40 | SoftwareSerial bt(11,12); 41 | #else 42 | #define bt Serial 43 | #endif 44 | #endif 45 | 46 | byte command[COMMAND_LENGTH-1]; //BT command buffer 47 | bool commandComplete; //flag indicating whether the command is complete or not 48 | byte cmdPos; //position inside command buffer; 0 means buffer is empty; 1 means command marker received; 2...5 means command data received. Value of 5 means the command is fully received 49 | 50 | #ifdef BUTTON_NEXT_EFF 51 | csButtonLongClicksDef<1000, 50, buttonNextEffInverse> btnNextEff; 52 | bool disableAutoChangeEffects = false; 53 | csTimerDef <1000> DetectTripleFastClickTimer; 54 | uint8_t DetectTripleFastClickCounter = 0; 55 | #else 56 | constexpr bool disableAutoChangeEffects = false; 57 | #endif // BUTTON_NEXT_EFF 58 | 59 | csTimerDef EffectAutoChangeTimer; // auto change animation effect, interval timer 60 | 61 | int paletteInd; 62 | int animInd = -1; 63 | 64 | int freeRam () { 65 | extern int __heap_start, *__brkval; 66 | int v; 67 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 68 | } 69 | 70 | extern Adafruit_NeoPixel pixels; 71 | 72 | void setup() { 73 | #ifdef DEBUG 74 | Serial.begin(9600); 75 | Serial.print(F("RAM="));Serial.println(freeRam()); 76 | #endif 77 | 78 | pixels.begin(); 79 | #ifdef BT 80 | bt.begin(9600); 81 | #endif 82 | randomSeed(analogRead(0)*analogRead(1)); 83 | paletteInd = random(PALS); 84 | anim.setAnim(animInd); 85 | anim.setPeriod(20); 86 | anim.setPalette(pals[0]); 87 | anim.doSetUp(); 88 | #ifdef BT 89 | #ifndef BTHS 90 | bt.listen(); 91 | #endif 92 | #endif 93 | 94 | #ifdef BUTTON_NEXT_EFF 95 | pinMode(pinButtonNextEff, INPUT_PULLUP); 96 | #endif // BUTTON_NEXT_EFF 97 | 98 | EffectAutoChangeTimer.start(10000); // 10000 for "release" AnimStart 99 | } 100 | 101 | void loop() { 102 | /* this piece of code checks for looping while trying to find different colors 103 | for (int pi=0;pi"));bt.print(animInd);bt.print(F("\t"));bt.println(paletteInd); 234 | #ifndef BTHS 235 | bt.listen(); 236 | #endif 237 | #endif 238 | } 239 | 240 | 241 | } 242 | 243 | -------------------------------------------------------------------------------- /brightness.h: -------------------------------------------------------------------------------- 1 | 2 | const byte PROGMEM BRI[] = { 3 | 0, 4 | 0, 5 | 0, 6 | 0, 7 | 0, 8 | 0, 9 | 0, 10 | 1, 11 | 1, 12 | 1, 13 | 1, 14 | 1, 15 | 1, 16 | 1, 17 | 1, 18 | 1, 19 | 1, 20 | 2, 21 | 2, 22 | 2, 23 | 2, 24 | 2, 25 | 2, 26 | 2, 27 | 2, 28 | 2, 29 | 2, 30 | 3, 31 | 3, 32 | 3, 33 | 3, 34 | 3, 35 | 3, 36 | 3, 37 | 3, 38 | 4, 39 | 4, 40 | 4, 41 | 4, 42 | 4, 43 | 4, 44 | 4, 45 | 5, 46 | 5, 47 | 5, 48 | 5, 49 | 5, 50 | 5, 51 | 6, 52 | 6, 53 | 6, 54 | 6, 55 | 6, 56 | 6, 57 | 7, 58 | 7, 59 | 7, 60 | 7, 61 | 7, 62 | 7, 63 | 8, 64 | 8, 65 | 8, 66 | 8, 67 | 8, 68 | 9, 69 | 9, 70 | 9, 71 | 9, 72 | 10, 73 | 10, 74 | 10, 75 | 10, 76 | 10, 77 | 11, 78 | 11, 79 | 11, 80 | 11, 81 | 12, 82 | 12, 83 | 12, 84 | 13, 85 | 13, 86 | 13, 87 | 13, 88 | 14, 89 | 14, 90 | 14, 91 | 15, 92 | 15, 93 | 15, 94 | 15, 95 | 16, 96 | 16, 97 | 16, 98 | 17, 99 | 17, 100 | 17, 101 | 18, 102 | 18, 103 | 19, 104 | 19, 105 | 19, 106 | 20, 107 | 20, 108 | 20, 109 | 21, 110 | 21, 111 | 22, 112 | 22, 113 | 22, 114 | 23, 115 | 23, 116 | 24, 117 | 24, 118 | 25, 119 | 25, 120 | 26, 121 | 26, 122 | 27, 123 | 27, 124 | 28, 125 | 28, 126 | 29, 127 | 29, 128 | 30, 129 | 30, 130 | 31, 131 | 31, 132 | 32, 133 | 32, 134 | 33, 135 | 34, 136 | 34, 137 | 35, 138 | 35, 139 | 36, 140 | 37, 141 | 37, 142 | 38, 143 | 39, 144 | 39, 145 | 40, 146 | 41, 147 | 42, 148 | 42, 149 | 43, 150 | 44, 151 | 45, 152 | 45, 153 | 46, 154 | 47, 155 | 48, 156 | 49, 157 | 49, 158 | 50, 159 | 51, 160 | 52, 161 | 53, 162 | 54, 163 | 55, 164 | 56, 165 | 57, 166 | 57, 167 | 58, 168 | 59, 169 | 60, 170 | 61, 171 | 63, 172 | 64, 173 | 65, 174 | 66, 175 | 67, 176 | 68, 177 | 69, 178 | 70, 179 | 71, 180 | 73, 181 | 74, 182 | 75, 183 | 76, 184 | 78, 185 | 79, 186 | 80, 187 | 82, 188 | 83, 189 | 84, 190 | 86, 191 | 87, 192 | 89, 193 | 90, 194 | 91, 195 | 93, 196 | 94, 197 | 96, 198 | 98, 199 | 99, 200 | 101, 201 | 102, 202 | 104, 203 | 106, 204 | 108, 205 | 109, 206 | 111, 207 | 113, 208 | 115, 209 | 117, 210 | 119, 211 | 121, 212 | 122, 213 | 124, 214 | 126, 215 | 129, 216 | 131, 217 | 133, 218 | 135, 219 | 137, 220 | 139, 221 | 142, 222 | 144, 223 | 146, 224 | 149, 225 | 151, 226 | 153, 227 | 156, 228 | 158, 229 | 161, 230 | 163, 231 | 166, 232 | 169, 233 | 171, 234 | 174, 235 | 177, 236 | 180, 237 | 183, 238 | 186, 239 | 189, 240 | 192, 241 | 195, 242 | 198, 243 | 201, 244 | 204, 245 | 208, 246 | 211, 247 | 214, 248 | 218, 249 | 221, 250 | 225, 251 | 228, 252 | 232, 253 | 236, 254 | 239, 255 | 243, 256 | 247, 257 | 251, 258 | 255 259 | }; 260 | -------------------------------------------------------------------------------- /button_classes.hpp: -------------------------------------------------------------------------------- 1 | // ver 3.0. 2019-12-XX. 2 | // constructor [ heX ] https://github.com/heX16 3 | #ifndef BUTTON_CLASSES_HEX_LIB 4 | #define BUTTON_CLASSES_HEX_LIB 5 | 6 | #include "small_timer.hpp" 7 | 8 | //NOTE: Все описанные ниже классы кнопок очень похожи друг на друга. 9 | // Я пробовал создать один базовый класс и придумать модель "ветвления" вариаций. 10 | // Но любой опробованный метод или расходует оперативную память, или не оптимально компилируется, или превращает исходник в хаос и черную магию. 11 | // Поэтому просто копирование - это тупо и очень просто. 12 | 13 | //todo: csButtonLongClicksFaster сделать копию класса с поддержкой ускорения кликов. это когда через пару секунд повторы кликов идут еще быстрее. 14 | 15 | /* Example: 16 | csButtonLongPress btn1; 17 | void loop() { 18 | btn1.run(digitalRead(2)); 19 | if (btn.flags.click) 20 | Serial.println(F("Click")) 21 | else if (btn.flags.longPress) 22 | Serial.println(F("longPress")); 23 | } 24 | */ 25 | 26 | // класс описывающий кнопку с поддержкой долгого нажатия 27 | class csButtonLongPress { 28 | public: 29 | csTimerShort tBtn; // антидребезг 30 | csTimerShort tLongPress; // таймер длительности длинного клика 31 | 32 | // структура для хранения флагов (каждый флаг занимает один бит) 33 | struct tFlags { 34 | unsigned click : 1; // обычный клик обнаружен (однакратная выдача! - флаг удерживается только один скан) 35 | unsigned longPress : 1; // длинное нажатие обнаружено (однакратная выдача! - флаг удерживается только один скан) 36 | unsigned _pushed : 1; // private! флаг-защелка нажатия на кнопку. по совместительству просто флаг того что кнопка вроде нажата (без выдержки времени!). 37 | unsigned _pushedShort : 1; // private! флаг-защелка 38 | unsigned _pushedLong : 1; // private! флаг-защелка 39 | } flags; 40 | 41 | csButtonLongPress() { 42 | flags.click = false; 43 | flags.longPress = false; 44 | flags._pushed = false; 45 | flags._pushedShort = false; 46 | flags._pushedLong = false; 47 | } 48 | 49 | // основной алгоритм. 50 | // @button - текущее состояние DI кнопки. True - нажата. False - отжата. 51 | // @timeClickDebounce - время минимального времени нажатия на кнопку. 52 | // @timeLongPress - время нажатия на кнопку которое будет считаться долгим нажатием. 53 | // P.S. алгоритм обработки кнопок получился не тривиальным... 54 | void run(const bool button, const uint16_t timeLongPress, const uint16_t timeClickDebounce = 50) { 55 | flags.click = false; 56 | flags.longPress = false; 57 | 58 | // обработка одинарного нажатия 59 | if (flags._pushedShort && ! button) { 60 | //LOG("button click"); 61 | flags.click = true; 62 | } 63 | // обработка длинного нажатия 64 | if (flags._pushedLong && ! button) { 65 | //LOG("button Long Clk"); 66 | flags.longPress = true; 67 | } 68 | 69 | // обработка физики (антидребезг и прочее) 70 | if (button) { 71 | if (flags._pushed == false) { 72 | //LOG("button pushed"); 73 | // запускаем оба таймера - после отпускания кнопки будем смотреть какой сработал 74 | tBtn.start(timeClickDebounce); 75 | tLongPress.start(timeLongPress); 76 | // взводим защелку чтобы избежать повторного запуска таймеров 77 | flags._pushed = true; 78 | } 79 | } else { 80 | // кнопку отпустили 81 | flags._pushed = false; 82 | flags._pushedShort = false; 83 | flags._pushedLong = false; 84 | tBtn.stop(); 85 | tLongPress.stop(); 86 | } 87 | 88 | // Кнопка еще нажата и отработал таймер - взводим короткого клика. Теперь если кнопку отпустят то может сработать "click". 89 | if(tBtn.run()) { 90 | flags._pushedShort = true; 91 | } 92 | 93 | // Кнопка еще нажата и прошло много времени - выдаем длинный клик. Флаг короткого клика сбрасываем. Теперь если кнопку отпустят то сработает "longPress". 94 | if(tLongPress.run()) { 95 | flags._pushedLong = true; 96 | flags._pushedShort = false; 97 | } 98 | } 99 | }; 100 | 101 | template 102 | class csButtonLongPressDef : public csButtonLongPress { 103 | public: 104 | using csButtonLongPress::run; 105 | 106 | inline void run(bool button) { 107 | if (inverse) 108 | csButtonLongPress::run( ! button, tpTimeLongPress, tpTimeClickDebounce); 109 | else 110 | csButtonLongPress::run(button, tpTimeLongPress, tpTimeClickDebounce); 111 | } 112 | }; 113 | 114 | //////////////////////////////////// 115 | 116 | // класс с поддержкой долгого нажатия с выдачей поторных кликов. интервал между повторами и стартом повторов одинаковый. 117 | class csButtonLongClicks { 118 | public: 119 | csTimerShort tBtn; // антидребезг 120 | csTimerShort tLongPress; // таймер длительности длинного клика 121 | 122 | // структура для хранения флагов (каждый флаг занимает один бит) 123 | struct tFlags { 124 | unsigned click : 1; // обычный клик обнаружен (однакратная выдача! - флаг удерживается только один скан) 125 | unsigned clickRepeat : 1; // доп флаг - чтобы можно было определить что нажатие идет в режиме повтора при удержании кнопки (однакратная выдача!) 126 | unsigned _pushed : 1; // private! флаг-защелка нажатия на кнопку. по совместительству просто флаг того что кнопка вроде нажата (без выдержки времени!). 127 | unsigned _pushedShort : 1; // private! флаг-защелка 128 | } flags; 129 | 130 | csButtonLongClicks() { 131 | flags.click = false; 132 | flags.clickRepeat = false; 133 | flags._pushed = false; 134 | flags._pushedShort = false; 135 | } 136 | 137 | // основной алгоритм. 138 | // @button - текущее состояние DI кнопки. True - нажата. False - отжата. 139 | // @timeClickDebounce - время минимального времени нажатия на кнопку. 140 | // @timeLongPress - время нажатия на кнопку которое будет считаться долгим нажатием. 141 | // P.S. алгоритм обработки кнопок получился не тривиальным... 142 | void run(const bool button, const uint16_t timeLongPress, const uint16_t timeClickDebounce = 50) { 143 | flags.click = false; 144 | flags.clickRepeat = false; 145 | 146 | // обработка одинарного нажатия 147 | if (flags._pushedShort && ! button) { 148 | //LOG("button click"); 149 | flags.click = true; 150 | } 151 | 152 | // обработка физики (антидребезг и прочее) 153 | if (button) { 154 | if (flags._pushed == false) { 155 | //LOG("button pushed"); 156 | // запускаем оба таймера - после отпускания кнопки будем смотреть какой сработал 157 | tBtn.start(timeClickDebounce); 158 | tLongPress.start(timeLongPress); 159 | // взводим защелку чтобы избежать повторного запуска таймеров 160 | flags._pushed = true; 161 | } 162 | } else { 163 | // кнопку отпустили 164 | flags._pushed = false; 165 | flags._pushedShort = false; 166 | tBtn.stop(); 167 | tLongPress.stop(); 168 | } 169 | 170 | // Кнопка еще нажата и отработал таймер - взводим короткого клика. Теперь если кнопку отпустят то может сработать "click". 171 | if(tBtn.run()) { 172 | flags._pushedShort = true; 173 | } 174 | 175 | // Кнопка еще нажата и прошло много времени - выдаем клик. 176 | if(tLongPress.run()) { 177 | //LOG("button Long Clk"); 178 | tLongPress.start(timeLongPress); 179 | // выдаем клик 180 | flags.click = true; 181 | // помечаем что этот клик от длинного нажатия 182 | flags.clickRepeat = true; 183 | // сбрасываем флаг-защелку - чтобы при отпускании кнопки не произошло лишнего нажатия 184 | flags._pushedShort = false; 185 | } 186 | } 187 | }; 188 | 189 | template 190 | class csButtonLongClicksDef : public csButtonLongClicks { 191 | public: 192 | using csButtonLongClicks::run; 193 | 194 | inline void run(bool button) { 195 | if (inverse) 196 | csButtonLongClicks::run( ! button, tpTimeLongPress, tpTimeClickDebounce); 197 | else 198 | csButtonLongClicks::run(button, tpTimeLongPress, tpTimeClickDebounce); 199 | } 200 | }; 201 | 202 | #endif // BUTTON_CLASSES_HEX_LIB 203 | -------------------------------------------------------------------------------- /color.h: -------------------------------------------------------------------------------- 1 | #ifndef color_h 2 | #define color_h 3 | #include 4 | 5 | struct Color 6 | { 7 | union 8 | { 9 | struct 10 | { 11 | byte r; 12 | byte g; 13 | byte b; 14 | }; 15 | byte raw[3]; 16 | }; 17 | 18 | inline Color() __attribute__((always_inline)) : r(0), g(0), b(0) {} 19 | 20 | // allow construction from R, G, B 21 | inline Color( uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline)) 22 | : r(ir), g(ig), b(ib) 23 | { 24 | } 25 | 26 | // allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code 27 | inline Color( uint32_t colorcode) __attribute__((always_inline)) 28 | : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) 29 | { 30 | } 31 | 32 | //interpolates between this color and provided. 33 | //x is from 0 to 1, 0 gives this color, 1 gives provided color, values between give interpolation 34 | Color interpolate(Color color, float x) 35 | { 36 | int r0 = x*(color.r - r) + r; 37 | int g0 = x*(color.g - g) + g; 38 | int b0 = x*(color.b - b) + b; 39 | return Color(r0, g0, b0); 40 | } 41 | 42 | //creates color with decreased brightness 43 | Color brightness(byte k) { 44 | int r0 = r * (int)k / 255; 45 | int g0 = g * (int)k / 255; 46 | int b0 = b * (int)k / 255; 47 | return Color(r0, g0, b0); 48 | } 49 | 50 | //fades (decreases all RGB channels brightness) this color by k 51 | void fade(byte k) 52 | { 53 | if (r>=k) { r=r-k; } else { r=0; } 54 | if (g>=k) { g=g-k; } else { g=0; } 55 | if (b>=k) { b=b-k; } else { b=0; } 56 | } 57 | 58 | //fades color separately for each channel 59 | void fade3(byte dr, byte dg, byte db) 60 | { 61 | if (r>=dr) { r=r-dr; } else { r=0; } 62 | if (g>=dg) { g=g-dg; } else { g=0; } 63 | if (b>=db) { b=b-db; } else { b=0; } 64 | } 65 | 66 | //checks whether this color is visually close to given one 67 | byte isCloseTo(Color c) { 68 | int diff = abs(r-c.r) + abs(g-c.g) + abs(b-c.b); 69 | return diff <= 220; //220 is magic number. Low values give "true" on closer colors, while higher can cause infinite loop while trying to find different color 70 | } 71 | 72 | void println() { 73 | Serial.print(F("r="));Serial.print(r); 74 | Serial.print(F("g="));Serial.print(g); 75 | Serial.print(F("b="));Serial.println(b); 76 | } 77 | } ; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /commands.h: -------------------------------------------------------------------------------- 1 | #define COMMAND_LENGTH 5 // total length of a command packet (in bytes) 2 | #define COMMAND_MARKER 255 //byte marking the command is going on 3 | #define CMD_SETAP '=' //set animation and palette 4 | #define CMD_MAGIC '!' //enter magic mode 5 | #define CMD_MPOS 'P' //data in magic mode 6 | 7 | /*command structure: 8 | byte 0 - command marker 9 | byte 1 - command code 10 | byte 2 - command data 1 11 | byte 3 - command data 2 12 | byte 4 - checksum (code+data1+data2 over a byte) 13 | */ 14 | -------------------------------------------------------------------------------- /palette.cpp: -------------------------------------------------------------------------------- 1 | #include "palette.h" 2 | 3 | // Red,Green,Blue sequence 4 | Color PalRgb_[] = { 0xFF0000, 0x00FF00, 0x0000FF }; 5 | Palette PalRgb = { 3, PalRgb_ }; 6 | 7 | // Rainbow colors 8 | Color PalRainbow_[] = 9 | { 10 | 0xFF0000, 0xAB5500, 0xABAB00, 0x00FF00, 11 | 0x00AB55, 0x0000FF, 0x5500AB, 0xAB0055 12 | }; 13 | Palette PalRainbow = { 8, PalRainbow_ }; 14 | 15 | // Rainbow colors with alternating stripes of black 16 | Color PalRainbowStripe_[] = 17 | { 18 | 0xFF0000, 0x000000, 0xAB5500, 0x000000, 0xABAB00, 0x000000, 0x00FF00, 0x000000, 19 | 0x00AB55, 0x000000, 0x0000FF, 0x000000, 0x5500AB, 0x000000, 0xAB0055, 0x000000 20 | }; 21 | Palette PalRainbowStripe = { 16, PalRainbowStripe_ }; 22 | 23 | 24 | // Blue purple ping red orange yellow (and back) 25 | // Basically, everything but the greens. 26 | // This palette is good for lighting at a club or party. 27 | Color PalParty_[] = 28 | { 29 | 0x5500AB, 0x84007C, 0xB5004B, 0xE5001B, 30 | 0xE81700, 0xB84700, 0xAB7700, 0xABAB00, 31 | 0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E, 32 | 0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9 33 | }; 34 | Palette PalParty = { 16, PalParty_ }; 35 | 36 | 37 | // Approximate "black body radiation" palette, akin to 38 | // the FastLED 'HeatColor' function. 39 | // Recommend that you use values 0-240 rather than 40 | // the usual 0-255, as the last 15 colors will be 41 | // 'wrapping around' from the hot end to the cold end, 42 | // which looks wrong. 43 | Color PalHeat_[] = 44 | { 45 | 0x700070, 0xFF0000, 0xFFFF00, 0xFFFFCC 46 | }; 47 | Palette PalHeat = { 4, PalHeat_ }; 48 | 49 | /* 50 | Color PalFire_[] = 51 | { 52 | 0x000000, 0x440000, 53 | 0x990000, 0xFF0000, 54 | 0xFF6600, 0xFFCC00 55 | }; 56 | */ 57 | Color PalFire_[] = 58 | { 59 | 0x000000, 0x220000, 60 | 0x880000, 0xFF0000, 61 | 0xFF6600, 0xFFCC00 62 | }; 63 | Palette PalFire = { 6, PalFire_ }; 64 | 65 | 66 | // palettes below are taken from http://www.color-hex.com/color-palettes/ (and modified) 67 | 68 | Color PalIceBlue_[] = 69 | { 70 | 0xffffff, 0x0000ff, 0x00ffff 71 | }; 72 | Palette PalIceBlue = { 3, PalIceBlue_}; 73 | -------------------------------------------------------------------------------- /palette.h: -------------------------------------------------------------------------------- 1 | #ifndef palette_h 2 | #define palette_h 3 | 4 | #include "color.h" 5 | 6 | 7 | struct Palette 8 | { 9 | int numColors; 10 | Color *colors; 11 | 12 | /** 13 | * Get the interpolated color from the palette. 14 | * The argument is a floating number between 0 and 1 15 | */ 16 | Color getPalColor(float i) 17 | { 18 | int i0 = (int)(i*numColors)%(numColors); 19 | int i1 = (int)(i*numColors+1)%(numColors); 20 | 21 | // decimal part is used to interpolate between the two colors 22 | float t0 = i*numColors - trunc(i*numColors); 23 | 24 | return colors[i0].interpolate(colors[i1], t0); 25 | } 26 | 27 | }; 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | // Palette definitions 31 | //////////////////////////////////////////////////////////////////////////////// 32 | extern Palette PalRgb; 33 | extern Palette PalRainbow; 34 | extern Palette PalRainbowStripe; 35 | extern Palette PalParty; 36 | extern Palette PalHeat; 37 | extern Palette PalFire; 38 | extern Palette PalIceBlue; 39 | 40 | #endif -------------------------------------------------------------------------------- /readme.MD: -------------------------------------------------------------------------------- 1 | # Управление гирляндой WS2812 2 | 3 | :exclamation: Проект для Ardunio не развивается, актуальный вариант для ESP8266 [здесь](https://github.com/Vasil-Pahomov/Liana). 4 | 5 | Вдохновлено проектом https://github.com/bportaluri/ALA 6 | В данном проекте реализовано управление гирляндой попиксельно адресуемых светодиодов WS2812 при помощи Arduino. В расширенной версии предполагается управление со смартфона на Android через Bluetooth при помощи отдельного [приложения](https://github.com/Vasil-Pahomov/AnWs2812) , однако скетч сохраняет свою работоспособность и без подключенного модуля Bluetooth. 7 | Схема состоит из трёх основных частей: 8 | 1. Гирлянды светодиодов WS2812 (подключение ведётся тремя контактами: D (данные), V+ (напряжение питания 5В) и V- (общий провод) 9 | 2. Модуля Arduino Pro Mini. Работать будет любой модуль Arduino (Uno, Nano и т.п.) с контроллером, имеющим минимум 2КБ ОЗУ и логическими уровнями 5В 10 | 3. Модуля Bluetooth. Работоспособность проверена на распространённом модуле HC-05, но теоретически будут работать и другие совместимые модули с последовательным интерфейсом (HC-06, модули на BK3231) 11 | > У Bluetooth-модуля уровни сигналов 3,3В, а у Arduino - 5В. Правильно использовать схему согласования уровней или хотя бы резистивный делитель в канале приёма, но работает и так 12 | 13 | ![enter image description here](https://raw.githubusercontent.com/Vasil-Pahomov/ArWs2812/master/Diagram.png) 14 | 15 | Модуль Bluetooth и гирлянда могут подключаться и к другим пинам Arduino. При смене пинов нужно поменять соответствующие константы в скетче. 16 | При подключении модуля Bluetooth также следует учитывать ограничения используемой библиотеки [SoftwareSerial](https://www.arduino.cc/en/Reference/SoftwareSerial). 17 | Без модуля Bluetooth гирлянда работает автономно, меняя эффекты и цветовую палитру случайным образом. Для избегания резкого "мигания" в скетче реализована плавная смена эффектов. 18 | 19 | # Кнопка 20 | Поддержка переключения эффекта кнопкой. Пин к которому подключена кнопка определяется переменной pinButtonNextEff. 21 | Длинное нажатие на кнопку начинает перебирать эффекты с интервалом в 1 секунду. 22 | После нажатия кнопки смена эффектов по времени отключается - будет постоянно работать выбранный эффект. 23 | Тройное нажатие на кнопку в течении одной секунды возвращает режим смены эффектов по времени. 24 | -------------------------------------------------------------------------------- /small_timer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Software small timer. 3 | Internal version 3.6 [ heX ] 2019 4 | Repository: https://github.com/heX16/small_timer 5 | */ 6 | #ifndef TIMER_HEX_LIB 7 | #define TIMER_HEX_LIB 8 | 9 | 10 | #ifndef TIMER_GET_TIME 11 | #ifdef Arduino_h 12 | // define for Arduino library 13 | #define TIMER_GET_TIME millis() 14 | #endif // Arduino_h 15 | 16 | #ifdef WINAPI 17 | #define TIMER_GET_TIME GetTickCount() 18 | #endif // WINAPI 19 | 20 | //todo: check ESP chipset 21 | //todo: add support - linux 22 | //todo: add support? - native ATmega chipset 23 | // for linux see: https://stackoverflow.com/questions/7729686/convert-gettickcount-vxworks-to-linux 24 | #endif // TIMER_GET_TIME 25 | 26 | #define TIMER_GETBIT(x,bit) ((x & (1ULL << (bit))) != 0) 27 | #define TIMER_SETBIT1(x,bit) {(x) |= (1ULL << (bit));} 28 | #define TIMER_SETBIT0(x,bit) {(x) &= ~(1ULL << (bit));} 29 | 30 | // timer template 31 | template < 32 | typename TTimer, // type Timer 33 | unsigned long MaxValue, // max time in Timer 34 | unsigned long BitMask, // mask of value 35 | unsigned int DisableBit, // timer disable bit number 36 | unsigned int UserFlagBit, // user flag bit number - In some implementations, you can use the custom flag 37 | unsigned int PrecDiv, // precision divider 38 | unsigned long InitValue // disabled timer value 39 | > 40 | class tpTimer { 41 | public: 42 | 43 | typedef TTimer DataType; 44 | 45 | // declare data - it's all data for this class 46 | TTimer timer; 47 | 48 | tpTimer() { 49 | init(); 50 | } 51 | 52 | // Start timer. If the timer is already running, the timer restarts. 53 | inline void start(const TTimer time) { 54 | // Note: this condition should be minimized by compiler optimization (but this optimizations is disabled during debugging mode) 55 | if (UserFlagBit == 0) { 56 | // "user flag" not using 57 | timer = (((TTimer)TIMER_GET_TIME / PrecDiv) + time) & BitMask; 58 | } else { 59 | // safe "user flag" 60 | bool f = getFlag(); 61 | timer = (((TTimer)TIMER_GET_TIME / PrecDiv) + time) & BitMask; 62 | setFlag(f); 63 | } 64 | } 65 | 66 | // Start timer. If the timer is already running then restarting does not occur. 67 | bool startOnce(const TTimer time) { 68 | if ( ! enabled()) { 69 | start(time); 70 | return true; 71 | } else 72 | return false; 73 | } 74 | 75 | // Timer stop. 76 | inline void stop() { 77 | TIMER_SETBIT1(timer, DisableBit); 78 | } 79 | 80 | // Init timer. 81 | inline void init() { 82 | timer = InitValue; 83 | } 84 | 85 | // Read the status of the timer. If started, returns True. 86 | inline bool enabled() { 87 | return ! TIMER_GETBIT(timer, DisableBit); 88 | } 89 | 90 | // Main function. Must be called in every cycle. If the timer has worked, then True is returned once. 91 | bool run() { 92 | if ( ! enabled()) 93 | return false; 94 | //TTimer temp = ((TTimer)((TTimer)TIMER_GET_TIME - timer) & 0x7FFF); 95 | if ( ((TTimer)((TTimer)(TIMER_GET_TIME / PrecDiv) - timer) & BitMask) < MaxValue) { 96 | stop(); 97 | return true; 98 | } else 99 | return false; 100 | } 101 | 102 | // Read user flag bit. If there is no flag support then False is always returned. 103 | // Some timer classes may contain a custom flag that the user use at his discretion. 104 | bool getFlag() { 105 | // Note: this condition should be minimized by compiler optimization 106 | if (UserFlagBit == 0) 107 | return false; 108 | else 109 | return TIMER_GETBIT(timer, UserFlagBit); 110 | } 111 | 112 | // Write to user flag bit. 113 | void setFlag(bool newValue) { 114 | // Note: this condition should be minimized by compiler optimization 115 | if (UserFlagBit == 0) 116 | return; 117 | else 118 | if (newValue) 119 | TIMER_SETBIT1(timer, UserFlagBit) 120 | else 121 | TIMER_SETBIT0(timer, UserFlagBit); 122 | } 123 | 124 | }; 125 | 126 | //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// 127 | 128 | template < 129 | typename TTimer, // type Timer 130 | unsigned long MaxValue, // max time in Timer 131 | unsigned long BitMask, // mask of value 132 | unsigned int DisableBit, // timer disable bit number 133 | unsigned int UserFlagBit, // user flag bit number - In some implementations, you can use the custom flag 134 | unsigned int PrecDiv, // precision divider 135 | unsigned long InitValue // disabled timer value 136 | > 137 | class tpTimerExternal: public tpTimer { 138 | public: 139 | 140 | inline void start(const TTimer time, TTimer externalTime) { 141 | this->timer = (((TTimer)externalTime / PrecDiv) + time) & BitMask; 142 | } 143 | 144 | bool startOnce(const TTimer time, TTimer externalTime) { 145 | if ( ! (this->enabled())) { 146 | this->start(time, externalTime); 147 | return true; 148 | } else 149 | return false; 150 | } 151 | 152 | bool run(TTimer externalTime) { 153 | if ( ! (this->enabled())) 154 | return false; 155 | 156 | if ( ((TTimer)((TTimer)(externalTime / PrecDiv) - (this->timer)) & BitMask) < MaxValue) { 157 | this->stop(); 158 | return true; 159 | } else 160 | return false; 161 | } 162 | 163 | }; 164 | 165 | //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// 166 | 167 | template < 168 | class T, 169 | unsigned long DefaultTime 170 | > 171 | class tpTimerDefaultValue : public T { 172 | inline void start() { T::start(DefaultTime); } 173 | inline bool startOnce() { return T::startOnce(DefaultTime); } 174 | }; 175 | 176 | //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// 177 | 178 | // Timer config examples: 179 | 180 | /* 181 | typename TTimer, // type Timer 182 | int MaxValue, // max time in Timer 183 | int BitMask, // mask of value 184 | int DisableBit, // timer disable bit number 185 | int UserFlagBit, // user flag bit number - In some implementations, you can use the custom flag 186 | int PrecDiv // precision divider 187 | int InitValue // disabled timer value 188 | */ 189 | 190 | // max 64 ms. 191 | typedef tpTimer 192 | csTimer8bit_64ms; 193 | 194 | // max 128 ms. div 4. jitter - 4 ms! And support user flag. 195 | typedef tpTimer 196 | csTimer8bit_128ms_J4ms_Flag; 197 | 198 | // max 64 second. div 256. jitter - 0.25 second! Recomended minimal - 10 second. Size - 1 byte. 199 | typedef tpTimer 200 | csTimer8bit_64sec_J256ms; 201 | 202 | // max 1 second. div 8. jitter - 8 ms. Size - 1 byte. 203 | typedef tpTimer 204 | csTimer8bit_1sec_J8ms; 205 | 206 | // max 16 second. 207 | typedef tpTimer 208 | csTimer16bit_16sec; 209 | 210 | // max 8 second. And support user flag. 211 | typedef tpTimer 212 | csTimer16bit_16sec_Flag; 213 | 214 | // max 16 min. div 64. jitter - 64 ms 215 | typedef tpTimer 216 | csTimer16bit_16min_J64ms; 217 | 218 | // max 279 min (4,5 hour). div 1024. jitter - 1 second. 219 | typedef tpTimer 220 | csTimer16bit_4hour_J1sec; 221 | 222 | // max 12,4 day. 223 | typedef tpTimer 224 | csTimer32bit_12day; 225 | 226 | // max 6 day. And support user flag. 227 | typedef tpTimer 228 | csTimer32bit_6day_Flag; 229 | 230 | // default timer: 231 | 232 | typedef csTimer32bit_12day csTimer; // big timer, max length - 12 day. Size - 4 byte. 233 | 234 | typedef csTimer16bit_16sec csTimerShort; // short time, max length - 16 second. Size - 2 byte. 235 | 236 | 237 | 238 | //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// 239 | 240 | // default timer with "default time" support 241 | 242 | template 243 | class csTimerDef : public csTimer { 244 | public: 245 | using csTimer::start; // - function overload in class 246 | using csTimer::startOnce; 247 | 248 | inline void start() { csTimer::start(DefaultTime); } 249 | inline bool startOnce() { return csTimer::startOnce(DefaultTime); } 250 | }; 251 | 252 | template 253 | class csTimerShortDef : public csTimerShort { 254 | public: 255 | using csTimerShort::start; 256 | using csTimerShort::startOnce; 257 | 258 | inline void start() { csTimerShort::start(DefaultTime); } 259 | inline bool startOnce() { return csTimerShort::startOnce(DefaultTime); } 260 | }; 261 | 262 | //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// //// 263 | 264 | // any timer with "default time" support 265 | 266 | template 267 | class csTimerSetDefault : public T { 268 | public: 269 | using typename T::DataType::start; 270 | using typename T::DataType::startOnce; 271 | 272 | inline void start() { T::start(DefaultTime); } 273 | inline bool startOnce() { return T::startOnce(DefaultTime); } 274 | }; 275 | 276 | // example: typedef csTimerSetDefault csTimer; 277 | 278 | 279 | #endif // TIMER_HEX_LIB 280 | --------------------------------------------------------------------------------