From f5d1cccce68c20a135188327628c6b6cb146e5f0 Mon Sep 17 00:00:00 2001 From: zeyus Date: Mon, 24 Feb 2025 22:58:58 +0100 Subject: [PATCH] flip.ino is working ish (terribly) on the uno mega --- flip.ino | 334 ++++++++++++++++++++++++++++++++++++++++++++++++ sketch copy.ino | 113 ++++++++++++++++ sketch.ino | 151 ++++++++++------------ 3 files changed, 516 insertions(+), 82 deletions(-) create mode 100644 flip.ino create mode 100644 sketch copy.ino diff --git a/flip.ino b/flip.ino new file mode 100644 index 0000000..f3efc71 --- /dev/null +++ b/flip.ino @@ -0,0 +1,334 @@ +#include +#include +#include + +// this is working on the mega + +DisplaySSD1306_128x64_I2C display(-1); + +// Simulation constants +#define SIM_WIDTH 128 +#define SIM_HEIGHT 64 +#define NUM_PARTICLES 80 +#define PARTICLE_RADIUS 2 +#define GRAVITY 0.9f +#define DAMPING 0.92f +#define PRESSURE_RADIUS 4.5f +#define PRESSURE_FORCE 0.6f +#define MAX_VEL 10.0f + +#ifdef __AVR__ + typedef int16_t fixed_t; + #define FIX_SHIFT 6 + #define TO_FIXED(x) ((fixed_t)((x) * (1<> 1); + return u.f * (1.5f - 0.5f * x * u.f * u.f); +} + +Particle particles[NUM_PARTICLES]; +uint8_t canvasData[SIM_WIDTH*(SIM_HEIGHT/8)]; +NanoCanvas1 canvas(SIM_WIDTH, SIM_HEIGHT, canvasData); + + +#define GRID_SIZE 16 +#define CELL_SIZE (SIM_WIDTH/GRID_SIZE) + +struct GridCell { + uint8_t particles[10]; + uint8_t count; +}; + +GridCell grid[GRID_SIZE][GRID_SIZE]; + +void buildSpatialGrid() { + memset(grid, 0, sizeof(grid)); + + for(int i=0; i (x,y)"+particles[i].x+","+particles[i].y+"\n"); + + // Ensure velocities are within range + particles[i].vx = TO_FIXED(constrain((random(-50,50)/25.0f), -2.0f, 2.0f)); + particles[i].vy = TO_FIXED(constrain((random(-25,50)/25.0f), -1.0f, 2.0f)); + } + #else + for(int i=0; i> FIX_SHIFT; + + // Particle interactions + for(int i=0; i= GRID_SIZE) continue; + if(gy+dy < 0 || gy+dy >= GRID_SIZE) continue; + + GridCell &cell = grid[gx+dx][gy+dy]; + for(int c=0; c> FIX_SHIFT; + + if(dist_sq < pressure_radius_sq && dist_sq > TO_FIXED(0.01f)) { + // Convert to float for precise calculation + float fx = TO_FLOAT(dx); + float fy = TO_FLOAT(dy); + float dist = sqrt(fx*fx + fy*fy) + 0.001f; + + // Normalize direction vector + float nx = fx / dist; + float ny = fy / dist; + + // Calculate force magnitude + float force = PRESSURE_FORCE * (1.0f - dist/TO_FLOAT(pressure_radius)); + + // Apply force to both particles + float fx_impulse = force * nx; + float fy_impulse = force * ny * 0.7f; // Reduce vertical effect + + particles[i].vx -= TO_FIXED(fx_impulse); + particles[i].vy -= TO_FIXED(fy_impulse); + particles[j].vx += TO_FIXED(fx_impulse); + particles[j].vy += TO_FIXED(fy_impulse); + } + } + } + } + } + + // Physics update with constraints + for(int i=0; i SIM_HEIGHT-1) { + // particles[i].y = TO_FIXED(SIM_HEIGHT-1); + // particles[i].vy = -particles[i].vy * damping; + // } + // Boundary checks + const float x = TO_FLOAT(particles[i].x); + const float y = TO_FLOAT(particles[i].y); + + if(x <= 0 || x >= SIM_WIDTH-1) { + particles[i].vx = (-damping * particles[i].vx) >> FIX_SHIFT; + particles[i].x = TO_FIXED(constrain(x, 1, SIM_WIDTH-2)); + } + + if(y <= 0 || y >= SIM_HEIGHT-1) { + particles[i].vy = (-damping * particles[i].vy) >> FIX_SHIFT; + particles[i].y = TO_FIXED(constrain(y, 1, SIM_HEIGHT-2)); + } + } +#else + // Build spatial grid + buildSpatialGrid(); + + // Interactions using grid + const float PRESSURE_RADIUS_SQ = PRESSURE_RADIUS * PRESSURE_RADIUS; + + for(int i=0; i= GRID_SIZE) continue; + if(gy+dy < 0 || gy+dy >= GRID_SIZE) continue; + + GridCell &cell = grid[gx+dx][gy+dy]; + for(int c=0; c 0.01f) { + const float dist = fast_sqrt(dist_sq) + 0.001f; + const float force = PRESSURE_FORCE * (1.0f - dist/PRESSURE_RADIUS); + + // Only apply horizontal forces to preserve gravity + particles[i].vx -= force * dx/dist; + particles[j].vx += force * dx/dist; + + // Reduce vertical force impact + particles[i].vy -= force * dy/dist * 0.3f; + particles[j].vy += force * dy/dist * 0.3f; + } + } + } + } + } + + // Gravity and movement + for(int i=0; i= SIM_WIDTH-1) { + particles[i].vx *= -DAMPING; + particles[i].x = constrain(particles[i].x, 1, SIM_WIDTH-2); + } + + // Y-axis + if(particles[i].y <= 0 || particles[i].y >= SIM_HEIGHT-1) { + particles[i].vy *= -DAMPING; + particles[i].y = constrain(particles[i].y, 1, SIM_HEIGHT-2); + } + } +#endif +} + +// Direct canvas buffer access (replace drawParticles) +void drawParticles() { + // Clear canvas by direct memory access + memset(canvasData, 0, sizeof(canvasData)); + // canvas.clear(); + // bool occupied[SIM_WIDTH][SIM_HEIGHT] = {false}; + for(int i=0; i(TO_FLOAT(particles[i].x) + 0.5f); + int y = static_cast(TO_FLOAT(particles[i].y) + 0.5f); + #else + int x = constrain(static_cast(particles[i].x + 0.5f), 0, SIM_WIDTH-1); + int y = constrain(static_cast(particles[i].y + 0.5f), 0, SIM_HEIGHT-1); + #endif + + #if PARTICLE_RADIUS == 1 + canvas.putPixel(x, y); + #else + canvas.drawCircle(x,y,PARTICLE_RADIUS); + #endif + // if(!occupied[x][y]) { + // canvas.putPixel(x, y); + // occupied[x][y] = true; + + // // Add neighbor pixels if using 2x2 + // if(x < SIM_WIDTH-1 && !occupied[x+1][y]) { + // canvas.putPixel(x+1, y); + // occupied[x+1][y] = true; + // } + // if(y < SIM_HEIGHT-1 && !occupied[x][y+1]) { + // canvas.putPixel(x, y+1); + // occupied[x][y+1] = true; + // } + // } + } + + display.drawCanvas(0,0,canvas); +} + + +void loop() { + static uint32_t last_frame = 0; + //lcd_delay(5000); + drawParticles(); + //delay(1000); + if(millis() - last_frame >= 33) { + last_frame = millis(); + applyPhysics(); + } else { + lcd_delay(millis() - last_frame); + } +} diff --git a/sketch copy.ino b/sketch copy.ino new file mode 100644 index 0000000..a0e635c --- /dev/null +++ b/sketch copy.ino @@ -0,0 +1,113 @@ +#include +#include +#include + +#define BAUD_RATE 115200 +#include +#include +// The parameters are RST pin, BUS number, CS pin, DC pin, FREQ (0 means default), CLK pin, MOSI pin +//DisplaySSD1325_128x64_SPI display(3,{-1, 4, 5, 0,-1,-1}); // Use this line for Atmega328p (3=RST, 4=CE, 5=D/C) +//DisplaySSD1325_128x64_I2C display(-1); // or (-1,{busId, addr, scl, sda, frequency}). This line is suitable for most platforms by default +//DisplaySSD1325_128x64_SPI display(22,{-1, 5, 21, 0,-1,-1}); // Use this line for ESP32 (VSPI) (gpio22=RST, gpio5=CE for VSPI, gpio21=D/C) +//DisplaySSD1325_128x64_SPI display(4,{-1, -1, 5, 0,-1,-1}); // Use this line for ESP8266 Arduino style rst=4, CS=-1, DC=5 + // And ESP8266 RTOS IDF. GPIO4 is D2, GPIO5 is D1 on NodeMCU boards + + +DisplaySSD1306_128x64_I2C display(-1); + +#define PANEL_RES_X 128 +#define PANEL_RES_Y 64 +#define SIM_WIDTH (PANEL_RES_X) +#define SIM_HEIGHT PANEL_RES_Y +#define NUM_PARTICLES 255 +#define FIXED_SHIFT 8 + +typedef int16_t fixed_t; +#define TO_FIXED(x) ((fixed_t)((x) * (1 << FIXED_SHIFT))) +#define TO_FLOAT(x) ((float)(x) / (1 << FIXED_SHIFT)) + +struct Particle { + fixed_t x, y; + fixed_t vx, vy; +}; +Particle particles[NUM_PARTICLES]; + +const int canvasWidth = SIM_WIDTH; // Width must be power of 2, i.e. 16, 32, 64, 128... +const int canvasHeight = SIM_HEIGHT; // Height must be divided on 8, i.e. 8, 16, 24, 32... +const fixed_t gravity = TO_FIXED(0.4); +const fixed_t damping = TO_FIXED(0.6); +const fixed_t border = TO_FIXED(0.1); +const uint32_t frame_time = 33; // ~30 FPS + +uint8_t canvasData[canvasWidth*(canvasHeight/8)]; +NanoCanvas1 canvas(canvasWidth, canvasHeight, canvasData); + +void updatePhysics() { + const fixed_t max_x = TO_FIXED(SIM_WIDTH-1); + const fixed_t max_y = TO_FIXED(SIM_HEIGHT-1); + + for (int i = 0; i < NUM_PARTICLES; i++) { + particles[i].vy += gravity; + particles[i].x += particles[i].vx; + particles[i].y += particles[i].vy; + + particles[i].x = constrain(particles[i].x, TO_FIXED(0), max_x); + particles[i].y = constrain(particles[i].y, TO_FIXED(0), max_y); + + // Boundary collisions + if (TO_FLOAT(particles[i].x) <= TO_FLOAT(border) || TO_FLOAT(particles[i].x) >= (SIM_WIDTH - TO_FLOAT(border))) { + particles[i].vx = -particles[i].vx * damping; + particles[i].x = constrain(particles[i].x, border, TO_FIXED(SIM_WIDTH) - border); + } + if (TO_FLOAT(particles[i].y) < TO_FLOAT(border) || TO_FLOAT(particles[i].y) >= (SIM_HEIGHT - TO_FLOAT(border))) { + particles[i].vy = -particles[i].vy * damping; + particles[i].y = constrain(particles[i].y, border, TO_FIXED(SIM_HEIGHT) - border); + } + + } +} + +void drawParticles() { + canvas.clear(); + for (int i = 0; i < NUM_PARTICLES; i++) { + int x = constrain(TO_FLOAT(particles[i].x), 0, SIM_WIDTH-1); + int y = constrain(TO_FLOAT(particles[i].y), 0, SIM_HEIGHT-1); + + canvas.putPixel(x, y); + } + display.drawCanvas(0,0,canvas); +} + +// Setup, initialize +void setup() +{ + //Serial.begin(115200); + display.begin(); + display.clear(); + canvas.setMode( CANVAS_MODE_TRANSPARENT ); + // display.setColor( 65535 ); + + // Initialize particles + for (int i = 0; i < NUM_PARTICLES; i++) { + particles[i].x = TO_FIXED(random(SIM_WIDTH-1)); + particles[i].y = TO_FIXED(random(SIM_HEIGHT/2)); + particles[i].vx = TO_FIXED((random(-100, 100)/100.0) * 0.7); + particles[i].vy = TO_FIXED((random(-50, 50)/100.0)); + } +} + +// Loop forever +void loop() +{ + // static uint32_t last_frame = 0; + + + //if (millis() - last_frame >= frame_time) { + //Serial.print(display.getColor()); + //Serial.print("Hi"); + updatePhysics(); + drawParticles(); + //last_frame = millis(); + //} + lcd_delay(frame_time); +} diff --git a/sketch.ino b/sketch.ino index 79bcd8d..a0e635c 100644 --- a/sketch.ino +++ b/sketch.ino @@ -1,13 +1,25 @@ -#include -#include +#include +#include +#include -// Config -#define PANEL_RES_X 64 -#define PANEL_RES_Y 32 -#define NUM_PANELS 2 -#define SIM_WIDTH (PANEL_RES_X * NUM_PANELS) +#define BAUD_RATE 115200 +#include +#include +// The parameters are RST pin, BUS number, CS pin, DC pin, FREQ (0 means default), CLK pin, MOSI pin +//DisplaySSD1325_128x64_SPI display(3,{-1, 4, 5, 0,-1,-1}); // Use this line for Atmega328p (3=RST, 4=CE, 5=D/C) +//DisplaySSD1325_128x64_I2C display(-1); // or (-1,{busId, addr, scl, sda, frequency}). This line is suitable for most platforms by default +//DisplaySSD1325_128x64_SPI display(22,{-1, 5, 21, 0,-1,-1}); // Use this line for ESP32 (VSPI) (gpio22=RST, gpio5=CE for VSPI, gpio21=D/C) +//DisplaySSD1325_128x64_SPI display(4,{-1, -1, 5, 0,-1,-1}); // Use this line for ESP8266 Arduino style rst=4, CS=-1, DC=5 + // And ESP8266 RTOS IDF. GPIO4 is D2, GPIO5 is D1 on NodeMCU boards + + +DisplaySSD1306_128x64_I2C display(-1); + +#define PANEL_RES_X 128 +#define PANEL_RES_Y 64 +#define SIM_WIDTH (PANEL_RES_X) #define SIM_HEIGHT PANEL_RES_Y -#define NUM_PARTICLES 100 +#define NUM_PARTICLES 255 #define FIXED_SHIFT 8 typedef int16_t fixed_t; @@ -18,109 +30,84 @@ struct Particle { fixed_t x, y; fixed_t vx, vy; }; - Particle particles[NUM_PARTICLES]; -MatrixPanel_I2S_DMA *dma_display; -/* -// Wokwi-compatible pin configuration -HUB75_I2S_CFG::i2s_pins _pins = { - .r1 = 25, .g1 = 26, .b1 = 27, - .r2 = 14, .g2 = 12, .b2 = 13, - .a = 23, .b = 19, .c = 5, .d = 17, - .e = -1, // Required for 64x64 panels - .lat = 4, .oe = 15, .clk = 16 -}; -*/ +const int canvasWidth = SIM_WIDTH; // Width must be power of 2, i.e. 16, 32, 64, 128... +const int canvasHeight = SIM_HEIGHT; // Height must be divided on 8, i.e. 8, 16, 24, 32... +const fixed_t gravity = TO_FIXED(0.4); +const fixed_t damping = TO_FIXED(0.6); +const fixed_t border = TO_FIXED(0.1); +const uint32_t frame_time = 33; // ~30 FPS -// ESP32-S3-WROOM-1 HUB75 Pin Mapping -HUB75_I2S_CFG::i2s_pins _pins = { - .r1 = 1, .g1 = 2, .b1 = 3, - .r2 = 4, .g2 = 5, .b2 = 6, - .a = 7, .b = 8, .c = 9, - .d = 10, .e = -1, // 'e' only needed for 64x64 panels - .lat = 11, .oe = 12, .clk = 13 -}; - -// Color palette -const uint16_t COLORS[] = { - 0x001F, 0x03FF, 0x07FF, 0x7FE0, 0x7F80, 0xFFE0, 0xFD20, 0xF800 -}; -const int NUM_COLORS = sizeof(COLORS)/sizeof(COLORS[0]); - -void setup() { - - HUB75_I2S_CFG mxconfig( - PANEL_RES_X, - PANEL_RES_Y, - NUM_PANELS, - _pins, - HUB75_I2S_CFG::FM6126A, - false, - HUB75_I2S_CFG::HZ_10M, - true, - HUB75_I2S_CFG::SHIFTREG, - false, - 0 - ); - - mxconfig.double_buff = true; - dma_display = new MatrixPanel_I2S_DMA(mxconfig); - dma_display->begin(); - dma_display->setBrightness(255); - - // Initialize particles - for (int i = 0; i < NUM_PARTICLES; i++) { - particles[i].x = TO_FIXED(random(SIM_WIDTH)); - particles[i].y = TO_FIXED(random(SIM_HEIGHT/2)); - particles[i].vx = TO_FIXED((random(-100, 100)/100.0) * 0.7); - particles[i].vy = TO_FIXED((random(-50, 50)/100.0)); - } -} +uint8_t canvasData[canvasWidth*(canvasHeight/8)]; +NanoCanvas1 canvas(canvasWidth, canvasHeight, canvasData); void updatePhysics() { - const fixed_t gravity = TO_FIXED(0.15); - const fixed_t damping = TO_FIXED(0.82); - const fixed_t border = TO_FIXED(2.0); + const fixed_t max_x = TO_FIXED(SIM_WIDTH-1); + const fixed_t max_y = TO_FIXED(SIM_HEIGHT-1); for (int i = 0; i < NUM_PARTICLES; i++) { particles[i].vy += gravity; particles[i].x += particles[i].vx; particles[i].y += particles[i].vy; + particles[i].x = constrain(particles[i].x, TO_FIXED(0), max_x); + particles[i].y = constrain(particles[i].y, TO_FIXED(0), max_y); + // Boundary collisions - if (particles[i].x < border || particles[i].x >= TO_FIXED(SIM_WIDTH) - border) { + if (TO_FLOAT(particles[i].x) <= TO_FLOAT(border) || TO_FLOAT(particles[i].x) >= (SIM_WIDTH - TO_FLOAT(border))) { particles[i].vx = -particles[i].vx * damping; particles[i].x = constrain(particles[i].x, border, TO_FIXED(SIM_WIDTH) - border); } - if (particles[i].y < border || particles[i].y >= TO_FIXED(SIM_HEIGHT) - border) { + if (TO_FLOAT(particles[i].y) < TO_FLOAT(border) || TO_FLOAT(particles[i].y) >= (SIM_HEIGHT - TO_FLOAT(border))) { particles[i].vy = -particles[i].vy * damping; particles[i].y = constrain(particles[i].y, border, TO_FIXED(SIM_HEIGHT) - border); } + } } void drawParticles() { - dma_display->fillScreen(0); - + canvas.clear(); for (int i = 0; i < NUM_PARTICLES; i++) { int x = constrain(TO_FLOAT(particles[i].x), 0, SIM_WIDTH-1); int y = constrain(TO_FLOAT(particles[i].y), 0, SIM_HEIGHT-1); - // Simplified color selection - uint16_t color = COLORS[(abs(particles[i].vx) + abs(particles[i].vy)) % NUM_COLORS]; - - dma_display->drawPixel(x, y, color); + canvas.putPixel(x, y); + } + display.drawCanvas(0,0,canvas); +} + +// Setup, initialize +void setup() +{ + //Serial.begin(115200); + display.begin(); + display.clear(); + canvas.setMode( CANVAS_MODE_TRANSPARENT ); + // display.setColor( 65535 ); + + // Initialize particles + for (int i = 0; i < NUM_PARTICLES; i++) { + particles[i].x = TO_FIXED(random(SIM_WIDTH-1)); + particles[i].y = TO_FIXED(random(SIM_HEIGHT/2)); + particles[i].vx = TO_FIXED((random(-100, 100)/100.0) * 0.7); + particles[i].vy = TO_FIXED((random(-50, 50)/100.0)); } } -void loop() { - static uint32_t last_frame = 0; - const uint32_t frame_time = 33; // ~30 FPS - - if (millis() - last_frame >= frame_time) { +// Loop forever +void loop() +{ + // static uint32_t last_frame = 0; + + + //if (millis() - last_frame >= frame_time) { + //Serial.print(display.getColor()); + //Serial.print("Hi"); updatePhysics(); drawParticles(); - last_frame = millis(); - } + //last_frame = millis(); + //} + lcd_delay(frame_time); }