Devices & Components
1
Breadboard - 840 contacts
1
Arduino Nano
1
PCB Board Prototype
1
MAX7219 8x8x8 Red
3
Female to Female Hex Spacer
1
SparkFun Triple Axis Accelerometer Breakout - ADXL345
8
Resistor 220 ohm
Hardware & Tools
1
Soldering iron (generic)
1
Solder Wire, Lead Free
Software & Tools
Arduino IDE
Project description
Code
Ripple _Desk_Widget
c
1// ============================================================================ 2// Enhanced animated lifelike character on 8x8 LED matrix 3// Expressions: happy, blink, amazed, funny, mischief, very happy 4// Includes fluid simulation mode with ADXL345 accelerometer 5// ============================================================================ 6 7#include "character_animations.h" 8#include "fluid_simulation.h" 9 10// ============================================================================ 11// HARDWARE CONFIGURATION 12// ============================================================================ 13const int colPins[8] = {9, 4, A2, 6, 10, A3, 11, A1}; // Columns (cathodes) 14const int rowPins[8] = {5, 12, 13, 8, A0, 7, 3, 2}; // Rows (anodes) 15 16// Mode switching via accelerometer gesture (shake detection) 17// Shake the device to switch between modes 18const float X_SHAKE_THRESHOLD = 1.5; // Acceleration threshold for shake detection (in g) 19const unsigned long SHAKE_DEBOUNCE_TIME = 1000; // Minimum time between mode switches (ms) 20 21// ============================================================================ 22// MODE DEFINITIONS 23// ============================================================================ 24enum DisplayMode { 25 MODE_EMOTIONS, 26 MODE_FLUID_SIMULATION 27}; 28 29// ============================================================================ 30// GLOBAL STATE VARIABLES 31// ============================================================================ 32DisplayMode currentMode = MODE_EMOTIONS; 33unsigned long lastModeSwitch = 0; 34byte fluidFrame[8]; // Buffer for fluid simulation frame 35byte characterFrame[8]; // Buffer for character animation frame 36 37// ============================================================================ 38// ARDUINO SETUP 39// ============================================================================ 40void setup() { 41 42 // Initialize row pins (anodes) - HIGH = off 43 for (int i = 0; i < 8; i++) { 44 pinMode(rowPins[i], OUTPUT); 45 digitalWrite(rowPins[i], HIGH); 46 } 47 48 // Initialize column pins (cathodes) - LOW = off 49 for (int i = 0; i < 8; i++) { 50 pinMode(colPins[i], OUTPUT); 51 digitalWrite(colPins[i], LOW); 52 } 53 54 // Initialize character animations 55 initCharacterAnimations(); 56 57 // Initialize fluid simulation 58 initFluidSimulation(); 59 60 lastModeSwitch = millis(); 61} 62 63// ============================================================================ 64// MODE SWITCHING FUNCTIONS (Accelerometer Gesture Detection) 65// ============================================================================ 66float lastAccelMagnitude = 0.0; 67 68// Calculate acceleration magnitude 69float getAccelMagnitude(float x, float y, float z) { 70 return sqrt(x * x + y * y + z * z); 71} 72 73// Check if mode should be switched via shake gesture 74void checkModeSwitch() { 75 // Read accelerometer data (always available since it's initialized in fluid mode) 76 readAccelerometer(); 77 78 // Calculate current acceleration magnitude 79 float currentMagnitude = accelX; 80 81 // Detect shake: rapid change in acceleration magnitude 82 // Shake occurs when acceleration magnitude exceeds threshold 83 // and enough time has passed since last mode switch 84 if (millis() - lastModeSwitch >= SHAKE_DEBOUNCE_TIME) { 85 // Check if acceleration magnitude exceeds threshold (shake detected) 86 if (currentMagnitude > X_SHAKE_THRESHOLD) { 87 // Switch mode 88 if (currentMode == MODE_EMOTIONS) { 89 currentMode = MODE_FLUID_SIMULATION; 90 initParticles(); // Reset particles when entering fluid mode 91 } else { 92 currentMode = MODE_EMOTIONS; 93 resetCharacterAnimation(); // Reset character animation when entering emotion mode 94 } 95 lastModeSwitch = millis(); 96 } 97 } 98 99 lastAccelMagnitude = currentMagnitude; 100} 101 102// ============================================================================ 103// MAIN LOOP 104// ============================================================================ 105unsigned long lastFluidUpdate = 0; 106const unsigned long FLUID_UPDATE_INTERVAL = 50; // Update fluid physics every 50ms 107 108void loop() { 109 // Always refresh display first for smoothness 110 if (currentMode == MODE_EMOTIONS) { 111 // Emotion animation mode 112 // Check for mode switch (read accelerometer for shake detection) 113 checkModeSwitch(); 114 115 byte* frame = getCharacterFrame(); 116 displayFrame(frame); 117 } 118 else { 119 // Fluid simulation mode 120 // Update physics less frequently, but refresh display continuously 121 if (millis() - lastFluidUpdate >= FLUID_UPDATE_INTERVAL) { 122 getFluidFrame(fluidFrame); 123 lastFluidUpdate = millis(); 124 125 // Check for mode switch (accelerometer already read in getFluidFrame) 126 // But we need to check it here since getFluidFrame updates the accelerometer 127 checkModeSwitch(); 128 } 129 displayFrame(fluidFrame); 130 } 131 132} 133 134// ============================================================================ 135// DISPLAY FUNCTIONS 136// ============================================================================ 137void displayFrame(byte* pattern) { 138 for (int row = 0; row < 8; row++) { 139 // Turn off all rows first 140 for (int i = 0; i < 8; i++) { 141 digitalWrite(rowPins[i], HIGH); 142 } 143 144 // Set column states based on pattern 145 for (int col = 0; col < 8; col++) { 146 bool ledOn = (pattern[row] & (1 << (7 - col))); 147 digitalWrite(colPins[col], ledOn ? HIGH : LOW); 148 } 149 150 // Activate current row (LOW = on for anode) 151 digitalWrite(rowPins[row], LOW); 152 delayMicroseconds(100); // Display time per row (optimized for refresh rate) 153 } 154}
character_animations
c
1// ============================================================================ 2// CHARACTER ANIMATIONS MODULE 3// Animated lifelike character with multiple emotions and smooth transitions 4// ============================================================================ 5 6#ifndef CHARACTER_ANIMATIONS_H 7#define CHARACTER_ANIMATIONS_H 8 9#include <Arduino.h> 10 11// ============================================================================ 12// BASE EMOTIONS 13// ============================================================================ 14 15// Happy - big expressive eyes + smile 16static byte happy[8] = { 17 B00000000, // ........ 18 B01100110, // .██..██. ← big round open eyes 19 B01100110, // .██..██. 20 B00000000, // ........ 21 B00000000, // ........ 22 B01000010, // .█....█. ← gentle smile 23 B00111100, // ..████.. 24 B00000000 // ........ 25}; 26 27// Blink sequence - smooth eye closing/opening 28static byte blink1[8] = { // Eyes closed 29 B00000000, 30 B00000000, 31 B00000000, 32 B00000000, 33 B00000000, 34 B01000010, 35 B00111100, 36 B00000000 37}; 38 39static byte blink2[8] = { // Eyes open 40 B00000000, // ........ 41 B01100110, // .██..██. ← big round open eyes 42 B01100110, // .██..██. 43 B00000000, // ........ 44 B00000000, // ........ 45 B01000010, // .█....█. ← gentle smile 46 B00111100, // ..████.. 47 B00000000 // ........ 48}; 49 50// Amazed - wide open eyes, raised eyebrows, open mouth 51static byte amazed[8] = { 52 B11100111, // ███..███ ← raised eyebrows 53 B10100101, // █.█..█.█ ← arched eyebrows 54 B11100111, // ███..███ ← very wide open round eyes 55 B00000000, // ........ 56 B00000000, // ........ 57 B00011000, // ...██... ← open mouth (O shape) 58 B00100100, // ..█..█.. ← O mouth sides 59 B00011000 // ...██... ← bottom of O mouth 60}; 61 62// Funny - big grin, playful expression 63static byte funny[8] = { 64 B00000000, // ........ 65 B01100110, // .██..██. ← normal eyes 66 B01100110, // .██..██. 67 B00000000, // ........ 68 B00000000, // ........ 69 B11000011, // ██....██ ← big wide smile 70 B01111110, // .██████. 71 B00000000 // ........ 72}; 73 74// Mischief - winking with sly smile 75static byte mischief[8] = { 76 B00000000, // ........ 77 B00000110, // .....██. ← one eye open 78 B00000110, // .....██. ← other eye closed (wink) 79 B00000000, // ........ 80 B00000000, // ........ 81 B01000010, // .█....█. ← sly smile 82 B00111100, // ..████.. 83 B00000000 // ........ 84}; 85 86// Very happy - extra big smile 87static byte veryHappy[8] = { 88 B00000000, // ........ 89 B00000000, // ........ 90 B00000000, // ........ 91 B00000000, // ........ 92 B00000000, // ........ 93 B10000001, // █......█ 94 B01111110, // .██████. ← big wide smile 95 B00000000 // ........ 96}; 97 98// ============================================================================ 99// TRANSITION FRAMES (for smooth morphing between emotions) 100// ============================================================================ 101 102// Happy -> Amazed transition 103static byte happyToAmazed1[8] = { 104 B11100111, // ███..███ 105 B10100101, // █.█..█.█ 106 B11100111, // ███..███ ← eyes widening 107 B00000000, // ........ 108 B00000000, // ........ 109 B00100100, // ..█..█.. ← mouth opening (O shape) 110 B00011000, // ...██... ← bottom of O mouth 111 B00000000 // ........ 112}; 113 114// Amazed -> Happy transition 115static byte amazedToHappy1[8] = { 116 B11100111, // ███..███ 117 B10100101, // █.█..█.█ 118 B11100111, // ███..███ ← eyes still wide 119 B00000000, // ........ 120 B00000000, // ........ 121 B00100100, // ..█..█.. ← mouth closing 122 B00111100, // ..████.. ← smile forming 123 B00000000 // ........ 124}; 125 126// Happy -> Funny transition 127static byte happyToFunny1[8] = { 128 B00000000, // ........ 129 B01100110, // .██..██. ← normal eyes 130 B01100110, // .██..██. 131 B00000000, // ........ 132 B00000000, // ........ 133 B10000001, // █......█ ← smile widening 134 B01111110, // .██████. 135 B00000000 // ........ 136}; 137 138// Funny -> Happy transition 139static byte funnyToHappy1[8] = { 140 B00000000, // ........ 141 B01100110, // .██..██. ← normal eyes 142 B01100110, // .██..██. 143 B00000000, // ........ 144 B00000000, // ........ 145 B10000001, // █......█ ← smile narrowing from big to normal 146 B00111100, // ..████.. ← back to normal smile 147 B00000000 // ........ 148}; 149 150// Happy -> Mischief transition 151static byte happyToMischief1[8] = { 152 B00000000, // ........ 153 B00000110, // .....██. ← left eye almost closed 154 B00000110, // .....██. 155 B00000000, // ........ 156 B00000000, // ........ 157 B01000010, // .█....█. 158 B00111100, // ..████.. 159 B00000000 // ........ 160}; 161 162// Mischief -> Happy transition 163static byte mischiefToHappy1[8] = { 164 B00000000, // ........ 165 B01100110, // .██..██. 166 B01100110, // .██..██. 167 B00000000, // ........ 168 B00000000, // ........ 169 B01000010, // .█....█. 170 B00111100, // ..████.. 171 B00000000 // ........ 172}; 173 174// Happy -> Very Happy transition 175static byte happyToVeryHappy1[8] = { 176 B00000000, // ........ 177 B01100110, // .██..██. 178 B01100110, // .██..██. 179 B00000000, // ........ 180 B00000000, // ........ 181 B10000001, // █......█ ← smile expanding 182 B01111110, // .██████. 183 B00000000 184}; 185 186// Very Happy -> Happy transition 187static byte veryHappyToHappy1[8] = { 188 B00000000, // ........ 189 B01100110, // .██..██. 190 B01100110, // .██..██. 191 B00000000, // ........ 192 B00000000, // ........ 193 B10000001, // █......█ ← smile contracting 194 B00111100, // ..████.. ← back to normal 195 B00000000 196}; 197 198// ============================================================================ 199// ANIMATION SEQUENCE 200// ============================================================================ 201 202static byte* frames[] = { 203 // Initial happy sequence with blink 204 happy, happy, happy, happy, 205 blink1, blink2, 206 happy, happy, happy, happy, happy, happy, 207 208 // Amazed emotion cycle 209 happyToAmazed1, // Transition to amazed 210 amazed, amazed, amazed, // Amazed expression 211 amazedToHappy1, // Transition back to happy 212 happy, happy, happy, happy, 213 blink1, blink2, // Blink during happy 214 happy, happy, happy, happy, happy, happy, 215 216 // Funny emotion cycle 217 happyToFunny1, // Transition to funny 218 funny, funny, funny, funny, // Funny expression 219 funnyToHappy1, // Transition back to happy 220 happy, happy, happy, happy, 221 blink1, blink2, // Blink during happy 222 happy, happy, happy, happy, happy, happy, 223 224 // Mischief emotion cycle 225 happyToMischief1, // Transition to mischief 226 mischief, mischief, mischief, mischief, // Mischief expression 227 mischiefToHappy1, // Transition back to happy 228 happy, happy, happy, happy, 229 blink1, blink2, // Blink during happy 230 happy, happy, happy, happy, happy, happy, 231 232 // Very happy emotion cycle 233 happyToVeryHappy1, // Transition to very happy 234 veryHappy, veryHappy, veryHappy, // Very happy expression 235 veryHappyToHappy1, // Transition back to happy 236 happy, happy, happy, happy, 237 blink1, blink2, // Blink during happy 238 happy, happy, happy, happy, happy, happy, 239 240 // Final blinks 241 blink1, blink2, blink1, blink2, 242 happy, happy, happy, happy 243}; 244 245// Frame durations (in milliseconds) 246static int frameDurations[] = { 247 // Initial happy sequence with blink 248 800, 800, 800, 800, 249 120, 200, // Blink during happy 250 800, 800, 800, 800, 800, 800, // More happy frames 251 252 // Amazed emotion cycle 253 100, // Transition frame (faster) 254 600, 600, 600, // Amazed 255 100, // Transition frame (faster) 256 800, 800, 800, 800, // Happy frames 257 120, 200, // Blink during happy 258 800, 800, 800, 800, 800, 800, // More happy frames 259 260 // Funny emotion cycle 261 100, // Transition frame (faster) 262 500, 500, 500, 500, // Funny 263 100, // Transition frame (faster) 264 800, 800, 800, 800, // Happy frames 265 120, 200, // Blink during happy 266 800, 800, 800, 800, 800, 800, // More happy frames 267 268 // Mischief emotion cycle 269 100, // Transition frame (faster) 270 600, 600, 600, 600, // Mischief 271 100, // Transition frame (faster) 272 800, 800, 800, 800, // Happy frames 273 120, 200, // Blink during happy 274 800, 800, 800, 800, 800, 800, // More happy frames 275 276 // Very happy emotion cycle 277 100, // Transition frame (faster) 278 700, 700, 700, // Very happy 279 100, // Transition frame (faster) 280 800, 800, 800, 800, // Happy frames 281 120, 200, // Blink during happy 282 800, 800, 800, 800, 800, 800, // More happy frames 283 284 // Final blinks 285 100, 400, 120, 200, 286 800, 800, 800, 800 287}; 288 289// ============================================================================ 290// CHARACTER ANIMATION STATE 291// ============================================================================ 292struct CharacterAnimationState { 293 int numFrames; 294 int currentFrame; 295 unsigned long lastFrameChange; 296}; 297 298static CharacterAnimationState charState = { 299 sizeof(frames) / sizeof(frames[0]), 300 0, 301 0 302}; 303 304// ============================================================================ 305// CHARACTER ANIMATION FUNCTIONS 306// ============================================================================ 307 308// Initialize character animations 309void initCharacterAnimations() { 310 charState.currentFrame = 0; 311 charState.lastFrameChange = millis(); 312} 313 314// Update character animation state 315void updateCharacterAnimation() { 316 if (millis() - charState.lastFrameChange >= frameDurations[charState.currentFrame]) { 317 charState.currentFrame = (charState.currentFrame + 1) % charState.numFrames; 318 charState.lastFrameChange = millis(); 319 } 320} 321 322// Get current character animation frame 323byte* getCharacterFrame() { 324 updateCharacterAnimation(); 325 return frames[charState.currentFrame]; 326} 327 328// Reset character animation to start 329void resetCharacterAnimation() { 330 charState.currentFrame = 0; 331 charState.lastFrameChange = millis(); 332} 333 334#endif // CHARACTER_ANIMATIONS_H
fluid_simulation
c
1// ============================================================================ 2// FLUID SIMULATION MODULE 3// Uses ADXL345 accelerometer to simulate fluid reacting to gravity 4// ============================================================================ 5 6#ifndef FLUID_SIMULATION_H 7#define FLUID_SIMULATION_H 8 9#include <Arduino.h> 10#include <Wire.h> 11#include <stdlib.h> 12#include <math.h> 13 14// ============================================================================ 15// ADXL345 ACCELEROMETER CONFIGURATION 16// ============================================================================ 17#define ADXL345_ADDRESS 0x53 // I2C address of ADXL345 18#define ADXL345_POWER_CTL 0x2D 19#define ADXL345_DATA_FORMAT 0x31 20#define ADXL345_DATAX0 0x32 21 22// ============================================================================ 23// FLUID SIMULATION PARAMETERS 24// ============================================================================ 25#define NUM_PARTICLES 12 // Number of fluid particles 26#define GRID_SIZE 8 // 8x8 LED matrix 27#define GRAVITY_SCALE 0.5 // Scale factor for gravity influence (reduced for less sensitivity) 28#define DAMPING 0.85 // Velocity damping factor 29#define MIN_VELOCITY 0.1 // Minimum velocity threshold 30#define COLLISION_DISTANCE 1.2 // Minimum distance between particles 31#define COLLISION_FORCE 0.3 // Repulsion force when particles collide 32 33// ============================================================================ 34// PARTICLE STRUCTURE 35// ============================================================================ 36struct Particle { 37 float x; // X position (0-7) 38 float y; // Y position (0-7) 39 float vx; // X velocity 40 float vy; // Y velocity 41}; 42 43// ============================================================================ 44// GLOBAL VARIABLES 45// ============================================================================ 46Particle particles[NUM_PARTICLES]; 47float accelX = 0.0, accelY = 0.0, accelZ = 0.0; 48 49// ============================================================================ 50// ACCELEROMETER FUNCTIONS 51// ============================================================================ 52 53// Initialize ADXL345 accelerometer 54void initAccelerometer() { 55 Wire.begin(); 56 57 // Enable measurement mode 58 Wire.beginTransmission(ADXL345_ADDRESS); 59 Wire.write(ADXL345_POWER_CTL); 60 Wire.write(8); // Bit D3 High for measuring enable 61 Wire.endTransmission(); 62 63 delay(10); 64 65 // Set data format to +/-2g range 66 Wire.beginTransmission(ADXL345_ADDRESS); 67 Wire.write(ADXL345_DATA_FORMAT); 68 Wire.write(0x00); // Full resolution, +/-2g 69 Wire.endTransmission(); 70 71 delay(10); 72} 73 74// Read accelerometer data 75void readAccelerometer() { 76 Wire.beginTransmission(ADXL345_ADDRESS); 77 Wire.write(ADXL345_DATAX0); 78 Wire.endTransmission(false); 79 Wire.requestFrom(ADXL345_ADDRESS, 6, true); 80 81 // Read X, Y, Z values (each is 16-bit, stored in 2 registers) 82 int16_t X_raw = (Wire.read() | Wire.read() << 8); 83 int16_t Y_raw = (Wire.read() | Wire.read() << 8); 84 int16_t Z_raw = (Wire.read() | Wire.read() << 8); 85 86 // Convert to g-force (for +/-2g range, divide by 256) 87 accelX = X_raw / 256.0; 88 accelY = Y_raw / 256.0; 89 accelZ = Z_raw / 256.0; 90} 91 92// ============================================================================ 93// FLUID SIMULATION FUNCTIONS 94// ============================================================================ 95 96// Initialize particles with random positions 97void initParticles() { 98 for (int i = 0; i < NUM_PARTICLES; i++) { 99 particles[i].x = random(0, GRID_SIZE); 100 particles[i].y = random(0, GRID_SIZE); 101 particles[i].vx = 0.0; 102 particles[i].vy = 0.0; 103 } 104} 105 106// Update particle physics based on gravity 107void updateParticles() { 108 // Read current accelerometer data 109 readAccelerometer(); 110 111 // Calculate gravity direction (inverted for display) 112 // When device tilts, gravity pulls in opposite direction 113 float gravityX = -accelX * GRAVITY_SCALE; 114 float gravityY = -accelY * GRAVITY_SCALE; 115 116 // First pass: Apply gravity and update positions 117 for (int i = 0; i < NUM_PARTICLES; i++) { 118 // Apply gravity to velocity 119 particles[i].vx += gravityX; 120 particles[i].vy += gravityY; 121 122 // Apply damping 123 particles[i].vx *= DAMPING; 124 particles[i].vy *= DAMPING; 125 126 // Stop very slow movement 127 if (abs(particles[i].vx) < MIN_VELOCITY) particles[i].vx = 0; 128 if (abs(particles[i].vy) < MIN_VELOCITY) particles[i].vy = 0; 129 130 // Update position 131 particles[i].x += particles[i].vx; 132 particles[i].y += particles[i].vy; 133 134 // Boundary collision with bounce 135 if (particles[i].x < 0) { 136 particles[i].x = 0; 137 particles[i].vx *= -0.5; // Bounce with energy loss 138 } 139 if (particles[i].x >= GRID_SIZE) { 140 particles[i].x = GRID_SIZE - 0.1; 141 particles[i].vx *= -0.5; 142 } 143 if (particles[i].y < 0) { 144 particles[i].y = 0; 145 particles[i].vy *= -0.5; 146 } 147 if (particles[i].y >= GRID_SIZE) { 148 particles[i].y = GRID_SIZE - 0.1; 149 particles[i].vy *= -0.5; 150 } 151 } 152 153 // Second pass: Handle particle-to-particle collisions 154 for (int i = 0; i < NUM_PARTICLES; i++) { 155 for (int j = i + 1; j < NUM_PARTICLES; j++) { 156 // Calculate squared distance between particles (avoid sqrt for performance) 157 float dx = particles[i].x - particles[j].x; 158 float dy = particles[i].y - particles[j].y; 159 float distanceSq = dx * dx + dy * dy; 160 float collisionDistSq = COLLISION_DISTANCE * COLLISION_DISTANCE; 161 162 // Check if particles are too close 163 if (distanceSq < collisionDistSq && distanceSq > 0.0001) { 164 // Calculate actual distance 165 float distance = sqrt(distanceSq); 166 167 // Normalize direction vector 168 float invDistance = 1.0 / distance; 169 float nx = dx * invDistance; 170 float ny = dy * invDistance; 171 172 // Calculate relative velocity 173 float relVx = particles[i].vx - particles[j].vx; 174 float relVy = particles[i].vy - particles[j].vy; 175 176 // Calculate separation needed 177 float separation = (COLLISION_DISTANCE - distance) * 0.5; 178 179 // Separate particles 180 particles[i].x += nx * separation; 181 particles[i].y += ny * separation; 182 particles[j].x -= nx * separation; 183 particles[j].y -= ny * separation; 184 185 // Apply repulsion force (elastic collision) 186 float dotProduct = relVx * nx + relVy * ny; 187 if (dotProduct < 0) { // Only if moving towards each other 188 float impulse = dotProduct * COLLISION_FORCE; 189 particles[i].vx -= impulse * nx; 190 particles[i].vy -= impulse * ny; 191 particles[j].vx += impulse * nx; 192 particles[j].vy += impulse * ny; 193 } 194 } 195 } 196 } 197} 198 199// Convert particle positions to LED matrix pattern 200// Ensures no two particles occupy the same pixel 201void particlesToPattern(byte* pattern) { 202 // Clear pattern 203 for (int i = 0; i < GRID_SIZE; i++) { 204 pattern[i] = 0; 205 } 206 207 // Track occupied pixels to prevent overlap 208 bool occupied[GRID_SIZE][GRID_SIZE] = {false}; 209 210 // Draw particles, ensuring no overlap 211 for (int i = 0; i < NUM_PARTICLES; i++) { 212 int x = (int)round(particles[i].x); 213 int y = (int)round(particles[i].y); 214 215 // Clamp to valid range 216 if (x < 0) x = 0; 217 if (x >= GRID_SIZE) x = GRID_SIZE - 1; 218 if (y < 0) y = 0; 219 if (y >= GRID_SIZE) y = GRID_SIZE - 1; 220 221 // If pixel is already occupied, try nearby pixels 222 if (occupied[y][x]) { 223 // Try adjacent pixels in a spiral pattern 224 bool placed = false; 225 for (int radius = 1; radius < GRID_SIZE && !placed; radius++) { 226 for (int dy = -radius; dy <= radius && !placed; dy++) { 227 for (int dx = -radius; dx <= radius && !placed; dx++) { 228 // Only check pixels on the edge of the radius 229 if (abs(dx) == radius || abs(dy) == radius) { 230 int newX = x + dx; 231 int newY = y + dy; 232 233 if (newX >= 0 && newX < GRID_SIZE && 234 newY >= 0 && newY < GRID_SIZE && 235 !occupied[newY][newX]) { 236 x = newX; 237 y = newY; 238 placed = true; 239 } 240 } 241 } 242 } 243 } 244 } 245 246 // Place particle if pixel is free 247 if (!occupied[y][x]) { 248 pattern[y] |= (1 << (7 - x)); 249 occupied[y][x] = true; 250 } 251 } 252} 253 254// Initialize fluid simulation 255void initFluidSimulation() { 256 initAccelerometer(); 257 initParticles(); 258} 259 260// Get current fluid simulation frame 261void getFluidFrame(byte* pattern) { 262 updateParticles(); 263 particlesToPattern(pattern); 264} 265 266#endif // FLUID_SIMULATION_H
Downloadable files
Gerber Files
Gerber.zip
Documentation
Schematics
8x8.pdf
Comments
Only logged in users can leave comments