#include #define DOUBLE_SIDE 0 #define DOUBLE_SIDE_MIRROR 0 #define IMAGE_HEIGHT (7) #define IMAGE_WIDTH (48) #define N_ROT_SLICES (48) #define WS_PIN (8) #define WS_VCC (9) #define WS_GND (7) #define HALL_PIN (2) static const RgbColor off(0,0,0); #if DOUBLE_SIDE #define WS_PIXEL_COUNT (2*IMAGE_HEIGHT) #else #define WS_PIXEL_COUNT (IMAGE_HEIGHT) #endif #define IMAGE_FAIRYDUST //#define IMAGE_CYBER #ifdef IMAGE_CYBER #define SKEW_RATE (0) static const __restrict__ RgbColor PALETTE[] = { RgbColor(0,0,0), // black RgbColor(20,20,0) // yellow }; static const __restrict__ uint8_t IMAGE[IMAGE_WIDTH][IMAGE_HEIGHT] = { {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,0,1,0,0,1}, {1,0,1,0,1,1,1}, {1,0,0,0,0,0,1}, {1,1,1,1,1,1,1}, {1,0,1,0,1,0,1}, {1,0,1,0,1,0,1}, {1,0,0,0,0,0,1}, {1,1,1,1,1,1,1}, {1,1,0,1,0,1,1}, {1,0,1,0,1,0,1}, {1,0,0,0,0,0,1}, {1,1,1,1,1,1,1}, {1,0,0,1,1,1,1}, {1,1,1,0,0,0,1}, {1,0,0,1,1,1,1}, {1,1,1,1,1,1,1}, {1,0,1,1,1,0,1}, {1,0,1,1,1,0,1}, {1,1,0,0,0,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1}, {1,1,1,1,1,1,1} }; #elif defined(IMAGE_FAIRYDUST) #define SKEW_RATE (100) static const RgbColor PALETTE[] = { RgbColor(0,0,0), // black RgbColor(0,128,0), // green RgbColor(255,180,0), // orange RgbColor(255,0,0) // red }; static const __restrict__ uint8_t IMAGE[IMAGE_WIDTH][IMAGE_HEIGHT] = { {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,1,0,0,0}, {0,0,1,2,1,0,0}, {0,1,2,1,1,1,0}, {0,1,2,1,1,1,0}, {1,2,2,2,1,1,1}, {1,2,2,2,1,1,1}, {0,1,2,1,1,1,0}, {0,1,2,1,1,1,0}, {0,0,1,2,1,0,0}, {0,1,0,1,0,1,0}, {1,1,0,3,0,1,1}, {1,1,3,3,3,1,1}, {1,0,3,3,3,0,1}, {0,0,0,3,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0} }; #endif static NeoPixelBus ws2812(WS_PIXEL_COUNT, WS_PIN); static volatile uint32_t last_hall = 0UL; static volatile uint32_t last_hall_diff = 1000UL; #if SKEW_RATE static uint16_t skew = 0U; static uint32_t last_skew = 0UL; #endif void update_hall_timestamp() { uint32_t now = millis(); last_hall_diff = now - last_hall; last_hall = now; } void setup() { // Setup output pins pinMode(WS_PIN, OUTPUT); pinMode(HALL_PIN, INPUT); // Setup power supply for WS2812 strip // (Yes, I know I shouldn't supply power from the Arduino I/O pins, // but I'm running the LEDs at a fraction of their max power draw) pinMode(WS_VCC, OUTPUT); pinMode(WS_GND, OUTPUT); digitalWrite(WS_VCC, HIGH); digitalWrite(WS_GND, LOW); // Setup WS2812 strup ws2812.Begin(); ws2812.Show(); // Setup hall sensor interrupt attachInterrupt( digitalPinToInterrupt(HALL_PIN), update_hall_timestamp, FALLING ); } void loop() { // Get the current time uint32_t now = millis(); // Turn the LEDs off if no sensor input was received for 3s if ((now - last_hall) > 3000UL) { for (size_t i = 0; i < IMAGE_HEIGHT; ++i) { ws2812.SetPixelColor(i, off); #if DOUBLE_SIDE ws2812.SetPixelColor(IMAGE_HEIGHT + i, off); #endif } ws2812.Show(); // Only re-check after a second delay(1000); return; } // Get the slice to show at the current time, based on the time between the last two sensor events uint16_t slice = (uint16_t) (((now - last_hall) * N_ROT_SLICES / last_hall_diff) % N_ROT_SLICES); #if SKEW_RATE // Increment the skew offset when it's time for the next skew step if (now - last_skew > SKEW_RATE) { skew = (skew + 1) % N_ROT_SLICES; last_skew = now; } // Add the current skew offset to the slice index slice = (uint16_t) ((slice + skew) % N_ROT_SLICES); #endif // Display the current slice (row) of the image for (size_t i = 0; i < IMAGE_HEIGHT; ++i) { ws2812.SetPixelColor(i, PALETTE[IMAGE[slice][i]]); #if DOUBLE_SIDE #if DOUBLE_SIDE_MIRROR ws2812.SetPixelColor(IMAGE_HEIGHT+i, PALETTE[IMAGE[N_ROT_SLICES-slice][i]]); #else ws2812.SetPixelColor(IMAGE_HEIGHT+i, PALETTE[IMAGE[slice][i]]); #endif #endif } ws2812.Show(); }