Upgraded firmware for Simone Giertz's Every Day Calendar that links an ATProto-powered ESP32, for sync with goals.garden 🌱
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Animate wave when enabling a day

+93 -14
+93 -14
firmware/libraries/EverydayCalendar/EverydayCalendar_lights.cpp
··· 8 8 #define ARRAY_SIZE(array) (sizeof((array))/sizeof((array[0]))) 9 9 #define EEPROM_START_ADR 0x00000000 10 10 11 + static const int BRIGHTNESS_INITIAL_X10 = 2180; // x10 is to give us room to animate more smoothly without using floats 12 + static const int BRIGHTNESS_DARKEST_X10 = 2400; 13 + static const int BRIGHTNESS_LIGHTEST_X10 = 600; 14 + static const int BRIGHTNESS_SPEED = 20; 15 + static const int BRIGHTNESS_DELAY_DIST_TICKS = 40; 16 + 11 17 static const int csPin = 10; 12 18 static const int tickle_pin = 9; 13 19 static const int outputEnablePin = 8; 14 20 // set up the speed, data order and data mode 15 21 static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0); 16 - static const uint8_t maxBrightness = 255; 17 - static uint8_t brightness = maxBrightness/2; 18 22 static uint32_t ledValues[12] = {0}; // Months are the integers in range [0,11], Days are bits within the integers, in range [0,31] 23 + static bool loaded = false; 19 24 25 + enum AnimState { 26 + Still = 0, // rest state at medium bright 27 + Delayed = 1, // waiting a number of ticks 28 + Brightening = 2, // go up to max bright 29 + Dimming = 3, // go down to min bright 30 + Resetting = 4, // go back up to initial bright 31 + }; 32 + 33 + struct BrightnessRecord { 34 + int brightnessX10 = 0; 35 + int delayTicks = 0; 36 + AnimState state = Still; 37 + }; 38 + static BrightnessRecord brights[12]; 39 + static int animMonth = 0; 40 + 41 + static const int BRIGHT_SHIFT_TICKS = 1; 20 42 21 43 void EverydayCalendar_lights::configure(){ 22 44 // LED configurations ··· 32 54 TCCR2A = (TCCR2A & ~0x03) | 0x00; 33 55 TCCR2B = (TCCR2B & ~0x08) | 0x00; 34 56 TCCR2B = (TCCR2B & ~0x07) | 0x02; // selects a clock prescaler of 8. That's a frequency of 31372.55 35 - OCR2A = brightness; //128 57 + OCR2A = BRIGHTNESS_INITIAL_X10 / 10; 36 58 clearAllLEDs(); 37 59 } 38 60 ··· 53 75 54 76 if (enable){ 55 77 ledValues[month] = ledValues[month] | ((uint32_t)1 << day); 78 + 79 + if (loaded) { 80 + // Run animation for this month 81 + animMonth = month; 82 + for (size_t i = 0; i < 12; i++) 83 + { 84 + int dist = abs((int)month - (int)i); 85 + brights[i].brightnessX10 = dist == 0 ? BRIGHTNESS_LIGHTEST_X10 : BRIGHTNESS_INITIAL_X10; 86 + brights[i].delayTicks = dist * BRIGHTNESS_DELAY_DIST_TICKS; 87 + brights[i].state = dist == 0 ? Brightening : Delayed; 88 + } 89 + } 56 90 }else{ 57 91 ledValues[month] = ledValues[month] & ~((uint32_t)1 << day); 58 92 } ··· 92 126 Serial.print(" = "); 93 127 Serial.println(ledValues[i]); 94 128 } 129 + 130 + loaded = true; 95 131 } 96 132 97 133 void EverydayCalendar_lights::setBrightness(uint8_t b){ 98 - if(b > 200){ 99 - b = 200; 100 - } 101 - b = ~b; 102 - if((brightness == 255) && (b != 255)){ 134 + if(b > 0){ 103 135 TIMSK2 |= (1<<OCIE2A); 104 - } 105 - brightness = b; 106 - if(brightness == 255){ 136 + } else { 107 137 TIMSK2 &= ~(1<<OCIE2A); 108 138 } 109 139 } ··· 118 148 ISR(TIMER2_OVF_vect) { 119 149 static uint16_t activeColumn = 0; 120 150 static byte spiBuf[6]; 151 + size_t c = activeColumn; 121 152 122 153 digitalWrite(outputEnablePin, HIGH); // Disable 123 154 124 - OCR2A = brightness; 155 + 156 + // Use initial brightness by default 157 + int brightnessX10 = BRIGHTNESS_INITIAL_X10; 158 + 159 + // Otherwise animate wave 160 + if (brights[c].state == Delayed) { 161 + brights[c].delayTicks--; 162 + if (brights[c].delayTicks <= 0) { 163 + brights[c].state = Brightening; 164 + } 165 + } else if (brights[c].state != Still) { 166 + 167 + // Velocity & reversing 168 + int vel = -BRIGHTNESS_SPEED; // negative means going up since higher numbers are brighter 169 + if (brights[c].state == Dimming) { 170 + vel *= -1; 171 + } 172 + 173 + // Slow down darker (higher) velocity so there's balance between light & dark 174 + if (brights[c].brightnessX10 > 2100) { 175 + vel /= 4; 176 + } else if (brights[c].brightnessX10 > 1800) { 177 + vel /= 2; 178 + } 179 + 180 + // New brightness 181 + int prev = brights[c].brightnessX10; 182 + brights[c].brightnessX10 += vel; 183 + 184 + // Change states 185 + if (brights[c].state == Brightening && brights[c].brightnessX10 < BRIGHTNESS_LIGHTEST_X10) { 186 + brights[c].state = Dimming; 187 + } else if (brights[c].state == Dimming && brights[c].brightnessX10 > BRIGHTNESS_DARKEST_X10) { 188 + brights[c].state = Resetting; 189 + } else if (brights[c].state == Resetting && brights[c].brightnessX10 <= BRIGHTNESS_INITIAL_X10) { 190 + brights[c].state = Still; 191 + } 192 + 193 + // Use anim brightness 194 + brightnessX10 = brights[c].brightnessX10; 195 + 196 + // Special handling for anim month to always stay bright 197 + if (c == animMonth) { 198 + if (brights[c].state && brightnessX10 > BRIGHTNESS_INITIAL_X10) { 199 + brightnessX10 = BRIGHTNESS_INITIAL_X10; 200 + } 201 + } 202 + } 203 + 204 + OCR2A = brightnessX10 / 10; 125 205 126 206 // "Tickle" the watchdog circuit to keep the LEDs enabled 127 207 digitalWrite(tickle_pin, !digitalRead(tickle_pin)); ··· 142 222 SPI.endTransaction(); 143 223 digitalWrite (csPin, HIGH); 144 224 145 - activeColumn++; 146 - activeColumn %= 12; 225 + activeColumn = (activeColumn + 1) % 12; 147 226 }