flip.ino is working ish (terribly) on the uno mega
This commit is contained in:
parent
57d94407bb
commit
f5d1cccce6
3 changed files with 516 additions and 82 deletions
334
flip.ino
Normal file
334
flip.ino
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <lcdgfx.h>
|
||||||
|
#include <FastTrig.h>
|
||||||
|
|
||||||
|
// 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<<FIX_SHIFT)))
|
||||||
|
#define TO_FLOAT(x) ((x) / (1<<FIX_SHIFT))
|
||||||
|
|
||||||
|
struct Particle {
|
||||||
|
fixed_t x, y;
|
||||||
|
fixed_t vx, vy;
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
typedef float f32 __attribute__((aligned(4)));
|
||||||
|
struct Particle {
|
||||||
|
f32 x, y;
|
||||||
|
f32 vx, vy;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline float fast_sqrt(float x) {
|
||||||
|
union { float f; uint32_t i; } u;
|
||||||
|
u.f = x;
|
||||||
|
u.i = 0x5f375a86 - (u.i >> 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<NUM_PARTICLES; i++) {
|
||||||
|
#ifdef __AVR__
|
||||||
|
float x = TO_FLOAT(particles[i].x);
|
||||||
|
float y = TO_FLOAT(particles[i].y);
|
||||||
|
#else
|
||||||
|
float x = particles[i].x;
|
||||||
|
float y = particles[i].y;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int gx = constrain(x / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
|
int gy = constrain(y / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
|
|
||||||
|
if(grid[gx][gy].count < 10) {
|
||||||
|
grid[gx][gy].particles[grid[gx][gy].count++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// String str; // Hhack
|
||||||
|
display.begin();
|
||||||
|
display.clear();
|
||||||
|
canvas.setMode(CANVAS_MODE_TRANSPARENT);
|
||||||
|
|
||||||
|
// Serial.begin(115200);
|
||||||
|
// while (!Serial);
|
||||||
|
|
||||||
|
// Initialize particles in a droplet pattern
|
||||||
|
float cx = SIM_WIDTH/2;
|
||||||
|
float cy = SIM_HEIGHT/4;
|
||||||
|
|
||||||
|
#ifdef __AVR__
|
||||||
|
for(int i=0; i<NUM_PARTICLES; i++) {
|
||||||
|
float angle = random(360) * PI / 180.0f;
|
||||||
|
float radius = random(10);
|
||||||
|
|
||||||
|
float x_offset = icos(angle) * radius;
|
||||||
|
float y_offset = isin(angle) * radius;
|
||||||
|
|
||||||
|
// Constrain BEFORE fixed-point conversion
|
||||||
|
float xpos = constrain(cx + x_offset, 0.0f, SIM_WIDTH-1.0f);
|
||||||
|
float ypos = constrain(cy + y_offset, 0.0f, SIM_HEIGHT-1.0f);
|
||||||
|
|
||||||
|
particles[i].x = TO_FIXED(xpos);
|
||||||
|
particles[i].y = TO_FIXED(ypos);
|
||||||
|
|
||||||
|
//Serial.print(str+"Particle "+i+": (angle)"+angle+", (radius)"+radius+" (offsets)"+x_offset+","+y_offset+" -> (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<NUM_PARTICLES; i++) {
|
||||||
|
float angle = random(360) * PI / 180.0;
|
||||||
|
float radius = random(10);
|
||||||
|
particles[i].x = cx + icos(angle) * radius;
|
||||||
|
particles[i].y = cy + isin(angle) * radius;
|
||||||
|
particles[i].vx = random(-50,50)/25.0; // -2 to +2
|
||||||
|
particles[i].vy = random(-25,50)/25.0; // -1 to +2
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyPhysics() {
|
||||||
|
#ifdef __AVR__
|
||||||
|
// Fixed-point implementation
|
||||||
|
const fixed_t gravity = TO_FIXED(GRAVITY);
|
||||||
|
const fixed_t damping = TO_FIXED(DAMPING);
|
||||||
|
const fixed_t pressure_radius = TO_FIXED(PRESSURE_RADIUS);
|
||||||
|
const fixed_t max_vel = TO_FIXED(MAX_VEL);
|
||||||
|
|
||||||
|
// Convert spatial grid to fixed-point
|
||||||
|
buildSpatialGrid();
|
||||||
|
const int32_t pressure_radius_sq = ((int32_t)pressure_radius * pressure_radius) >> FIX_SHIFT;
|
||||||
|
|
||||||
|
// Particle interactions
|
||||||
|
for(int i=0; i<NUM_PARTICLES; i++) {
|
||||||
|
const int gx = TO_FLOAT(particles[i].x) / CELL_SIZE;
|
||||||
|
const int gy = TO_FLOAT(particles[i].y) / CELL_SIZE;
|
||||||
|
|
||||||
|
for(int dx=-1; dx<=1; dx++) {
|
||||||
|
for(int dy=-1; dy<=1; dy++) {
|
||||||
|
if(gx+dx < 0 || gx+dx >= GRID_SIZE) continue;
|
||||||
|
if(gy+dy < 0 || gy+dy >= GRID_SIZE) continue;
|
||||||
|
|
||||||
|
GridCell &cell = grid[gx+dx][gy+dy];
|
||||||
|
for(int c=0; c<cell.count; c++) {
|
||||||
|
const int j = cell.particles[c];
|
||||||
|
if(j <= i) continue;
|
||||||
|
|
||||||
|
const fixed_t dx = particles[j].x - particles[i].x;
|
||||||
|
const fixed_t dy = particles[j].y - particles[i].y;
|
||||||
|
const fixed_t dist_sq = (dx*dx + dy*dy) >> 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<NUM_PARTICLES; i++) {
|
||||||
|
// Apply gravity
|
||||||
|
particles[i].vy += gravity;
|
||||||
|
|
||||||
|
// Update position
|
||||||
|
particles[i].x += particles[i].vx;
|
||||||
|
particles[i].y += particles[i].vy;
|
||||||
|
|
||||||
|
// Velocity constraints
|
||||||
|
particles[i].vx = constrain(particles[i].vx, -TO_FIXED(MAX_VEL*2), TO_FIXED(MAX_VEL*2));
|
||||||
|
particles[i].vy = constrain(particles[i].vy, -TO_FIXED(MAX_VEL*2), TO_FIXED(MAX_VEL*2));
|
||||||
|
// Simple floor collision
|
||||||
|
// if(TO_FLOAT(particles[i].y) > 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<NUM_PARTICLES; i++) {
|
||||||
|
const int gx = particles[i].x / CELL_SIZE;
|
||||||
|
const int gy = particles[i].y / CELL_SIZE;
|
||||||
|
|
||||||
|
// Check 3x3 grid around particle
|
||||||
|
for(int dx=-1; dx<=1; dx++) {
|
||||||
|
for(int dy=-1; dy<=1; dy++) {
|
||||||
|
if(gx+dx < 0 || gx+dx >= GRID_SIZE) continue;
|
||||||
|
if(gy+dy < 0 || gy+dy >= GRID_SIZE) continue;
|
||||||
|
|
||||||
|
GridCell &cell = grid[gx+dx][gy+dy];
|
||||||
|
for(int c=0; c<cell.count; c++) {
|
||||||
|
const int j = cell.particles[c];
|
||||||
|
if(j <= i) continue; // Avoid duplicate pairs
|
||||||
|
|
||||||
|
const float dx = particles[j].x - particles[i].x;
|
||||||
|
const float dy = particles[j].y - particles[i].y;
|
||||||
|
const float dist_sq = dx*dx + dy*dy;
|
||||||
|
|
||||||
|
if(dist_sq < PRESSURE_RADIUS_SQ && dist_sq > 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<NUM_PARTICLES; i++) {
|
||||||
|
particles[i].vy += GRAVITY;
|
||||||
|
particles[i].x += particles[i].vx;
|
||||||
|
particles[i].y += particles[i].vy;
|
||||||
|
particles[i].vx = constrain(particles[i].vx, -MAX_VEL, MAX_VEL);
|
||||||
|
particles[i].vy = constrain(particles[i].vy, -MAX_VEL, MAX_VEL);
|
||||||
|
// X-axis
|
||||||
|
if(particles[i].x <= 0 || particles[i].x >= 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<NUM_PARTICLES; i++) {
|
||||||
|
#ifdef __AVR__
|
||||||
|
int x = static_cast<int>(TO_FLOAT(particles[i].x) + 0.5f);
|
||||||
|
int y = static_cast<int>(TO_FLOAT(particles[i].y) + 0.5f);
|
||||||
|
#else
|
||||||
|
int x = constrain(static_cast<int>(particles[i].x + 0.5f), 0, SIM_WIDTH-1);
|
||||||
|
int y = constrain(static_cast<int>(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);
|
||||||
|
}
|
||||||
|
}
|
113
sketch copy.ino
Normal file
113
sketch copy.ino
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include <lcdgfx.h>
|
||||||
|
#include <nano_engine_v2.h>
|
||||||
|
#include <nano_gfx_types.h>
|
||||||
|
|
||||||
|
#define BAUD_RATE 115200
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <lcdgfx.h>
|
||||||
|
// 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);
|
||||||
|
}
|
147
sketch.ino
147
sketch.ino
|
@ -1,13 +1,25 @@
|
||||||
#include <Arduino.h>
|
#include <lcdgfx.h>
|
||||||
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
|
#include <nano_engine_v2.h>
|
||||||
|
#include <nano_gfx_types.h>
|
||||||
|
|
||||||
// Config
|
#define BAUD_RATE 115200
|
||||||
#define PANEL_RES_X 64
|
#include <Arduino.h>
|
||||||
#define PANEL_RES_Y 32
|
#include <lcdgfx.h>
|
||||||
#define NUM_PANELS 2
|
// The parameters are RST pin, BUS number, CS pin, DC pin, FREQ (0 means default), CLK pin, MOSI pin
|
||||||
#define SIM_WIDTH (PANEL_RES_X * NUM_PANELS)
|
//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 SIM_HEIGHT PANEL_RES_Y
|
||||||
#define NUM_PARTICLES 100
|
#define NUM_PARTICLES 255
|
||||||
#define FIXED_SHIFT 8
|
#define FIXED_SHIFT 8
|
||||||
|
|
||||||
typedef int16_t fixed_t;
|
typedef int16_t fixed_t;
|
||||||
|
@ -18,109 +30,84 @@ struct Particle {
|
||||||
fixed_t x, y;
|
fixed_t x, y;
|
||||||
fixed_t vx, vy;
|
fixed_t vx, vy;
|
||||||
};
|
};
|
||||||
|
|
||||||
Particle particles[NUM_PARTICLES];
|
Particle particles[NUM_PARTICLES];
|
||||||
MatrixPanel_I2S_DMA *dma_display;
|
|
||||||
|
|
||||||
/*
|
const int canvasWidth = SIM_WIDTH; // Width must be power of 2, i.e. 16, 32, 64, 128...
|
||||||
// Wokwi-compatible pin configuration
|
const int canvasHeight = SIM_HEIGHT; // Height must be divided on 8, i.e. 8, 16, 24, 32...
|
||||||
HUB75_I2S_CFG::i2s_pins _pins = {
|
const fixed_t gravity = TO_FIXED(0.4);
|
||||||
.r1 = 25, .g1 = 26, .b1 = 27,
|
const fixed_t damping = TO_FIXED(0.6);
|
||||||
.r2 = 14, .g2 = 12, .b2 = 13,
|
const fixed_t border = TO_FIXED(0.1);
|
||||||
.a = 23, .b = 19, .c = 5, .d = 17,
|
const uint32_t frame_time = 33; // ~30 FPS
|
||||||
.e = -1, // Required for 64x64 panels
|
|
||||||
.lat = 4, .oe = 15, .clk = 16
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ESP32-S3-WROOM-1 HUB75 Pin Mapping
|
uint8_t canvasData[canvasWidth*(canvasHeight/8)];
|
||||||
HUB75_I2S_CFG::i2s_pins _pins = {
|
NanoCanvas1 canvas(canvasWidth, canvasHeight, canvasData);
|
||||||
.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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePhysics() {
|
void updatePhysics() {
|
||||||
const fixed_t gravity = TO_FIXED(0.15);
|
const fixed_t max_x = TO_FIXED(SIM_WIDTH-1);
|
||||||
const fixed_t damping = TO_FIXED(0.82);
|
const fixed_t max_y = TO_FIXED(SIM_HEIGHT-1);
|
||||||
const fixed_t border = TO_FIXED(2.0);
|
|
||||||
|
|
||||||
for (int i = 0; i < NUM_PARTICLES; i++) {
|
for (int i = 0; i < NUM_PARTICLES; i++) {
|
||||||
particles[i].vy += gravity;
|
particles[i].vy += gravity;
|
||||||
particles[i].x += particles[i].vx;
|
particles[i].x += particles[i].vx;
|
||||||
particles[i].y += particles[i].vy;
|
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
|
// 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].vx = -particles[i].vx * damping;
|
||||||
particles[i].x = constrain(particles[i].x, border, TO_FIXED(SIM_WIDTH) - border);
|
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].vy = -particles[i].vy * damping;
|
||||||
particles[i].y = constrain(particles[i].y, border, TO_FIXED(SIM_HEIGHT) - border);
|
particles[i].y = constrain(particles[i].y, border, TO_FIXED(SIM_HEIGHT) - border);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawParticles() {
|
void drawParticles() {
|
||||||
dma_display->fillScreen(0);
|
canvas.clear();
|
||||||
|
|
||||||
for (int i = 0; i < NUM_PARTICLES; i++) {
|
for (int i = 0; i < NUM_PARTICLES; i++) {
|
||||||
int x = constrain(TO_FLOAT(particles[i].x), 0, SIM_WIDTH-1);
|
int x = constrain(TO_FLOAT(particles[i].x), 0, SIM_WIDTH-1);
|
||||||
int y = constrain(TO_FLOAT(particles[i].y), 0, SIM_HEIGHT-1);
|
int y = constrain(TO_FLOAT(particles[i].y), 0, SIM_HEIGHT-1);
|
||||||
|
|
||||||
// Simplified color selection
|
canvas.putPixel(x, y);
|
||||||
uint16_t color = COLORS[(abs(particles[i].vx) + abs(particles[i].vy)) % NUM_COLORS];
|
}
|
||||||
|
display.drawCanvas(0,0,canvas);
|
||||||
|
}
|
||||||
|
|
||||||
dma_display->drawPixel(x, y, color);
|
// 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() {
|
// Loop forever
|
||||||
static uint32_t last_frame = 0;
|
void loop()
|
||||||
const uint32_t frame_time = 33; // ~30 FPS
|
{
|
||||||
|
// static uint32_t last_frame = 0;
|
||||||
|
|
||||||
if (millis() - last_frame >= frame_time) {
|
|
||||||
|
//if (millis() - last_frame >= frame_time) {
|
||||||
|
//Serial.print(display.getColor());
|
||||||
|
//Serial.print("Hi");
|
||||||
updatePhysics();
|
updatePhysics();
|
||||||
drawParticles();
|
drawParticles();
|
||||||
last_frame = millis();
|
//last_frame = millis();
|
||||||
}
|
//}
|
||||||
|
lcd_delay(frame_time);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue