scooterproject/arduino/scooter_wheel_spin/scooter_wheel_spin.ino
2019-04-18 23:02:20 +02:00

233 lines
4.9 KiB
C++

#include <NeoPixelBus.h>
#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<NeoGrbFeature, Neo800KbpsMethod> 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();
}