Adafruit_Learning_System_Gu.../2020_shake/2020_shake.ino
2023-06-13 16:52:54 -07:00

205 lines
6.9 KiB
C++

// SPDX-FileCopyrightText: 2020 Limor Fried for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Adafruit_LIS3DH.h> // For accelerometer
#include <Adafruit_PixelDust.h> // For simulation
#include <Adafruit_Protomatter.h> // For LED matrix
#include "2020.h" // 2020 bitmap data
#include "2021.h" // 2021 bitmap data
#include "2022.h" // etc.
#include "2023.h"
#include "2024.h"
#include "2025.h"
#include "2026.h"
#define BITMAP_WIDTH 64 // All the year bitmaps are a fixed size
#define BITMAP_HEIGHT 32
#define THIS_YEAR_BITMAP bitmap_2021 // Name of current/next year bitmap
#define NEXT_YEAR_BITMAP bitmap_2022 // arrays in header files
bool show_new_year = true;
#define SHAKE_ACCEL_G 2.9 // Force (in Gs) to trigger shake
#define SHAKE_ACCEL_MS2 (SHAKE_ACCEL_G * 9.8) // Convert to m/s^2
#define SHAKE_ACCEL_SQ (SHAKE_ACCEL_MS2 * SHAKE_ACCEL_MS2) // Avoid sqrt() in accel check
#define SHAKE_EVENTS 30 // Number of accel readings to trigger sand
#define SHAKE_PERIOD 2000 // Period (in ms) when SHAKE_EVENTS must happen
#define SAND_TIME 6000 // Time (in ms) to run simulation before restarting
#if defined(_VARIANT_MATRIXPORTAL_M4_) // MatrixPortal M4
uint8_t rgbPins[] = {7, 8, 9, 10, 11, 12};
uint8_t addrPins[] = {17, 18, 19, 20, 21};
uint8_t clockPin = 14;
uint8_t latchPin = 15;
uint8_t oePin = 16;
#else // MatrixPortal ESP32-S3
uint8_t rgbPins[] = {42, 41, 40, 38, 39, 37};
uint8_t addrPins[] = {35, 36, 48, 45, 21};
uint8_t clockPin = 2;
uint8_t latchPin = 47;
uint8_t oePin = 14;
#endif
// 64x32 pixel matrix, 6-bit depth
Adafruit_Protomatter matrix(
64, 6, 1, rgbPins, 4, addrPins, clockPin, latchPin, oePin, true);
Adafruit_LIS3DH accel = Adafruit_LIS3DH(); // Accelerometer
#define MAX_FPS 60 // Maximum redraw rate, frames/second
uint32_t prevTime = 0; // For frames-per-second throttle
uint16_t n_grains = 0; // Number of sand grains (counted on startup)
Adafruit_PixelDust *sand; // Sand object (allocated in setup())
// Error handler used by setup()
void err(int x) {
uint8_t i;
pinMode(LED_BUILTIN, OUTPUT); // Using onboard LED
for(i=1;;i++) { // Loop forever...
digitalWrite(LED_BUILTIN, i & 1); // LED on/off blink to alert user
delay(x);
}
}
// SETUP - RUNS ONCE AT PROGRAM START --------------------------------------
void setup(void) {
uint8_t i, j, bytes;
Serial.begin(115200);
//while (!Serial);
ProtomatterStatus status = matrix.begin();
Serial.printf("Protomatter begin() status: %d\n", status);
// Count number of 'on' pixels (sand grains) in THIS_YEAR_BITMAP
for (int i=0; i<sizeof(THIS_YEAR_BITMAP); i++) {
for (int b=0; b<8; b++) {
if (THIS_YEAR_BITMAP[i] & (1 << b)) {
n_grains++;
}
}
}
Serial.printf("Bitmap has %d grains\n", n_grains);
// Allocate sand object based on matrix size and bitmap 'on' pixels
sand = new Adafruit_PixelDust(matrix.width(), matrix.height(), n_grains, 1);
if (!sand->begin()) {
Serial.println("Couldn't start sand");
err(1000); // Slow blink = malloc error
}
if (!accel.begin(0x19)) {
Serial.println("Couldn't find accelerometer");
err(250); // Fast bink = I2C error
}
Serial.println("Accelerometer OK");
accel.setRange(LIS3DH_RANGE_8_G);
}
void loop() {
Serial.print("Tick");
uint16_t sandColor = show_new_year ? 0xF800 : 0xFFFF; // Red or white
// Set initial sand pixel positions and draw initial matrix state
sand->clear();
matrix.fillScreen(0);
int grain = 0, pixel = 0; // Sand grain and pixel indices
for (int i=0; i<sizeof(THIS_YEAR_BITMAP); i++) {
for (int b=0; b<8; b++, pixel++) {
if (THIS_YEAR_BITMAP[i] & (1 << (7-b))) {
int x = pixel % BITMAP_WIDTH;
int y = pixel / BITMAP_WIDTH;
//Serial.printf("Set pixel %d @ (%d, %d)\n", grain, x, y);
sand->setPosition(grain++, x, y);
matrix.drawPixel(x, y, sandColor);
}
}
}
matrix.show();
// Wait for shake
uint32_t first_event_time = millis() - SHAKE_PERIOD * 2, last_event_time = first_event_time;
uint8_t num_events = 0;
sensors_event_t event;
for (;;) {
uint32_t t = millis(); // Current time
accel.getEvent(&event);
float mag2 = event.acceleration.x * event.acceleration.x +
event.acceleration.y * event.acceleration.y +
event.acceleration.z * event.acceleration.z;
if (mag2 >= SHAKE_ACCEL_SQ) { // Accel exceeds shake threshold
if ((t - last_event_time) > SHAKE_PERIOD) { // Long time since last event?
first_event_time = t; // Start of new count
num_events = 1;
} else if ((t - first_event_time) < SHAKE_PERIOD) { // Still in shake interval?
if (++num_events >= SHAKE_EVENTS) { // Enough events?
break;
}
}
last_event_time = t;
}
}
// Run sand simulation for a few seconds
uint32_t elapsed, sandStartTime = millis();
while((elapsed = (millis() - sandStartTime)) < SAND_TIME) {
// Limit the animation frame rate to MAX_FPS.
uint32_t t;
while(((t = micros()) - prevTime) < (1000000L / MAX_FPS));
prevTime = t;
// Read accelerometer...
sensors_event_t event;
accel.getEvent(&event);
// Run one frame of the simulation
sand->iterate(event.acceleration.x * 1024, event.acceleration.y * 1024, event.acceleration.z * 1024);
if (elapsed > SAND_TIME * 3 / 4) {
float scale = 1.0 - (float)(elapsed - (SAND_TIME * 3 / 4)) / (float)(SAND_TIME / 4);
if (scale < 0.0) scale = 0.0;
else if (scale > 1.0) scale = 1.0;
scale = pow(scale, 2.6);
uint16_t rb = (int)(31.0 * scale + 0.5);
uint16_t g = (int)(63.0 * scale + 0.5);
if (show_new_year)
sandColor = (rb * 0b100000000000); // Just show red
else
sandColor = (rb * 0b100000000001) + (g << 5);
}
// Update pixel data in LED driver
matrix.fillScreen(0);
dimension_t x, y;
for(int i=0; i<n_grains ; i++) {
sand->getPosition(i, &x, &y);
matrix.drawPixel(x, y, sandColor);
}
matrix.show();
}
// If the show_new_year flag is set, don't return to shake detect,
// instead switch to sparkly display forever (reset to start over)
if (show_new_year) {
uint16_t frame = 0;
matrix.fillScreen(0);
for(;;) {
int pixel = 0;
for (int i=0; i<sizeof(NEXT_YEAR_BITMAP); i++) {
for (int b=0; b<8; b++, pixel++) {
if (NEXT_YEAR_BITMAP[i] & (1 << (7-b))) {
int x = pixel % BITMAP_WIDTH;
int y = pixel / BITMAP_WIDTH;
matrix.drawPixel(x, y, (random() & 1) ? ((((x - y + frame) / 8) & 1) ? 0xFFFF : 0x001F) : 0);
}
}
}
matrix.show();
delay(18);
frame++;
}
}
}