done
This commit is contained in:
parent
c9b897d180
commit
fa487a0ca9
16 changed files with 19898 additions and 1395 deletions
14
.vscode/c_cpp_properties.json
vendored
14
.vscode/c_cpp_properties.json
vendored
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Mac",
|
|
||||||
"includePath": [
|
|
||||||
"${workspaceFolder}/**"
|
|
||||||
],
|
|
||||||
"defines": [],
|
|
||||||
"compilerPath": "/usr/bin/clang",
|
|
||||||
"intelliSenseMode": "macos-clang-arm64"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version": 4
|
|
||||||
}
|
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.rmd": "rmd",
|
|
||||||
"*.Rmd": "rmd",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"print": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -29,4 +29,8 @@ I'm going to use the following components:
|
||||||
- MPU6050 6-axis accelerometer/gyro
|
- MPU6050 6-axis accelerometer/gyro
|
||||||
- 500mAh LiPo battery (402035) - hopefully enough, I can always upgrade it, but I want to try and make it as small as possible
|
- 500mAh LiPo battery (402035) - hopefully enough, I can always upgrade it, but I want to try and make it as small as possible
|
||||||
- AMS1117 3.3V voltage regulator
|
- AMS1117 3.3V voltage regulator
|
||||||
- TBD: Design and 3D print a case
|
- ~TBD: Design and 3D print a case~~
|
||||||
|
- 3D print
|
||||||
|
- - [OBJ file (lid and case)](./case/Case%20Design.obj)
|
||||||
|
- - [3MF Lid](./case/Lid_blank.3mf)
|
||||||
|
- - [3MF Case](./case/Case.3mf)
|
||||||
|
|
5
case/Case Design.mtl
Normal file
5
case/Case Design.mtl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# WaveFront *.mtl file (generated by Autodesk ATF)
|
||||||
|
|
||||||
|
newmtl ABS_(White)_(1)
|
||||||
|
Kd 0.682353 0.047059 0.047059
|
||||||
|
|
19705
case/Case Design.obj
Normal file
19705
case/Case Design.obj
Normal file
File diff suppressed because it is too large
Load diff
BIN
case/Case.3mf
Normal file
BIN
case/Case.3mf
Normal file
Binary file not shown.
BIN
case/Lid_blank.3mf
Normal file
BIN
case/Lid_blank.3mf
Normal file
Binary file not shown.
108
fluid_sim.cpp
108
fluid_sim.cpp
|
@ -6,7 +6,7 @@
|
||||||
#include "MPU6050_6Axis_MotionApps612.h"
|
#include "MPU6050_6Axis_MotionApps612.h"
|
||||||
|
|
||||||
// comment out to disable debug output / serial / led
|
// comment out to disable debug output / serial / led
|
||||||
#define DEBUG
|
// #define DEBUG
|
||||||
// LED for debugging interrupts
|
// LED for debugging interrupts
|
||||||
#define LED 17
|
#define LED 17
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@
|
||||||
// Simulation constants
|
// Simulation constants
|
||||||
#define SIM_WIDTH 128
|
#define SIM_WIDTH 128
|
||||||
#define SIM_HEIGHT 64
|
#define SIM_HEIGHT 64
|
||||||
#define NUM_PARTICLES 250
|
#define NUM_PARTICLES 100
|
||||||
#define PARTICLE_RADIUS 2
|
#define PARTICLE_RADIUS 2
|
||||||
#define GRAVITY 1.0f
|
#define GRAVITY 9.0f
|
||||||
#define DAMPING 0.3f
|
#define DAMPING 0.6f
|
||||||
#define PRESSURE_RADIUS 3.5f
|
#define PRESSURE_RADIUS 2.5f
|
||||||
#define PRESSURE_FORCE 0.9f
|
#define PRESSURE_FORCE 0.9f
|
||||||
#define MAX_VEL 2.0f
|
#define MAX_VEL 4.0f
|
||||||
#define GRID_SIZE 16
|
#define GRID_SIZE 16
|
||||||
#define CELL_SIZE (SIM_WIDTH/GRID_SIZE)
|
#define CELL_SIZE (SIM_WIDTH/GRID_SIZE)
|
||||||
|
|
||||||
|
@ -127,6 +127,40 @@ void DMPDataReady() {
|
||||||
MPUInterrupt = true;
|
MPUInterrupt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void goToSleep() {
|
||||||
|
// detachInterrupt(GPIO_NUM_27); // Disable wake-up interrupt
|
||||||
|
// print goodbye message on display
|
||||||
|
memset(canvasData, 0, sizeof(canvasData));
|
||||||
|
canvas.setMode(CANVAS_MODE_TRANSPARENT);
|
||||||
|
canvas.setFixedFont(ssd1306xled_font6x8);
|
||||||
|
canvas.printFixed(30, 30, "Goodbye! <3", STYLE_NORMAL);
|
||||||
|
display.drawCanvas(0, 0, canvas);
|
||||||
|
// Add a brief delay to ensure display updates
|
||||||
|
delay(5000);
|
||||||
|
// Prepare MPU6050 for motion detection
|
||||||
|
mpuMotionDetectMode();
|
||||||
|
|
||||||
|
// Force a read to clear any pending interrupts
|
||||||
|
mpu.getIntStatus();
|
||||||
|
|
||||||
|
// Add a brief delay to ensure MPU settles
|
||||||
|
delay(100);
|
||||||
|
display.m_i2c.displayOff();
|
||||||
|
// Configure the wake-up source (active LOW)
|
||||||
|
esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, 1);
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Debug message
|
||||||
|
#ifdef DEBUG
|
||||||
|
Serial.println("Going to deep sleep now");
|
||||||
|
Serial.flush();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enter deep sleep
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
|
|
||||||
// Setup serial communication
|
// Setup serial communication
|
||||||
// only for debugging
|
// only for debugging
|
||||||
void initSerial() {
|
void initSerial() {
|
||||||
|
@ -288,8 +322,10 @@ void buildSpatialGrid() {
|
||||||
int gy = constrain(particles[i].y / CELL_SIZE, 0, GRID_SIZE-1);
|
int gy = constrain(particles[i].y / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
int cellIndex = gy * GRID_SIZE + gx;
|
int cellIndex = gy * GRID_SIZE + gx;
|
||||||
int insertPos = gridCellStart[cellIndex] + gridCellCount[cellIndex];
|
int insertPos = gridCellStart[cellIndex] + gridCellCount[cellIndex];
|
||||||
gridParticles[insertPos] = i;
|
if(insertPos < NUM_PARTICLES) {
|
||||||
gridCellCount[cellIndex]++;
|
gridParticles[insertPos] = i;
|
||||||
|
gridCellCount[cellIndex]++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,6 +379,7 @@ void setup() {
|
||||||
// DMPReady = true;
|
// DMPReady = true;
|
||||||
mpuActiveMonitorMode();
|
mpuActiveMonitorMode();
|
||||||
pinMode(EXT0_PIN, INPUT);
|
pinMode(EXT0_PIN, INPUT);
|
||||||
|
pinMode(GPIO_NUM_27, INPUT_PULLUP); // wake up pin
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
pinMode(LED, OUTPUT);
|
pinMode(LED, OUTPUT);
|
||||||
|
@ -352,6 +389,7 @@ void setup() {
|
||||||
Serial.println(F(")..."));
|
Serial.println(F(")..."));
|
||||||
#endif
|
#endif
|
||||||
attachInterrupt(digitalPinToInterrupt(EXT0_PIN), DMPDataReady, FALLING);
|
attachInterrupt(digitalPinToInterrupt(EXT0_PIN), DMPDataReady, FALLING);
|
||||||
|
// attachInterrupt(GPIO_NUM_27, goToSleep, FALLING);
|
||||||
MPUIntStatus = mpu.getIntStatus();
|
MPUIntStatus = mpu.getIntStatus();
|
||||||
|
|
||||||
// Initialize sleep timer (start counting from device boot/wake)
|
// Initialize sleep timer (start counting from device boot/wake)
|
||||||
|
@ -417,7 +455,7 @@ void applyPhysics() {
|
||||||
const float dist = fast_sqrt(dist_sq) + 0.001f;
|
const float dist = fast_sqrt(dist_sq) + 0.001f;
|
||||||
const float force = PRESSURE_FORCE * (1.0f - dist/PRESSURE_RADIUS);
|
const float force = PRESSURE_FORCE * (1.0f - dist/PRESSURE_RADIUS);
|
||||||
|
|
||||||
// Only apply horizontal forces to preserve gravity
|
// Apply pressure forces (original behavior)
|
||||||
particles[i].vx -= force * dx/dist;
|
particles[i].vx -= force * dx/dist;
|
||||||
particles[j].vx += force * dx/dist;
|
particles[j].vx += force * dx/dist;
|
||||||
|
|
||||||
|
@ -454,6 +492,7 @@ void applyPhysics() {
|
||||||
|
|
||||||
// Apply stronger damping during high movement
|
// Apply stronger damping during high movement
|
||||||
float adaptiveDamping = DAMPING * (1.0f - accelBoost * 0.2f);
|
float adaptiveDamping = DAMPING * (1.0f - accelBoost * 0.2f);
|
||||||
|
|
||||||
// X-axis
|
// X-axis
|
||||||
if(particles[i].x <= 0 || particles[i].x >= SIM_WIDTH-1) {
|
if(particles[i].x <= 0 || particles[i].x >= SIM_WIDTH-1) {
|
||||||
particles[i].vx *= -adaptiveDamping;
|
particles[i].vx *= -adaptiveDamping;
|
||||||
|
@ -474,8 +513,17 @@ void drawParticles() {
|
||||||
memset(canvasData, 0, sizeof(canvasData));
|
memset(canvasData, 0, sizeof(canvasData));
|
||||||
// canvas.clear();
|
// canvas.clear();
|
||||||
for(int i=0; i<NUM_PARTICLES; i++) {
|
for(int i=0; i<NUM_PARTICLES; i++) {
|
||||||
int x = constrain(static_cast<int>(particles[i].x + 0.5f), 0, SIM_WIDTH-1);
|
// Reset corrupted particles
|
||||||
int y = constrain(static_cast<int>(particles[i].y + 0.5f), 0, SIM_HEIGHT-1);
|
if(particles[i].x < 0 || particles[i].x > SIM_WIDTH || particles[i].y < 0 || particles[i].y > SIM_HEIGHT ||
|
||||||
|
isnan(particles[i].x) || isnan(particles[i].y) || isnan(particles[i].vx) || isnan(particles[i].vy)) {
|
||||||
|
particles[i].x = random(SIM_WIDTH/4, 3*SIM_WIDTH/4);
|
||||||
|
particles[i].y = random(SIM_HEIGHT/4, 3*SIM_HEIGHT/4);
|
||||||
|
particles[i].vx = 0;
|
||||||
|
particles[i].vy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
#if PARTICLE_RADIUS == 1
|
#if PARTICLE_RADIUS == 1
|
||||||
canvas.putPixel(x, y);
|
canvas.putPixel(x, y);
|
||||||
|
@ -549,31 +597,7 @@ void reportIMU() {
|
||||||
MPUInterrupt = false;
|
MPUInterrupt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToSleep() {
|
|
||||||
display.m_i2c.displayOff();
|
|
||||||
// detach interrupt
|
|
||||||
// detachInterrupt(digitalPinToInterrupt(EXT0_PIN));
|
|
||||||
// Prepare MPU6050 for motion detection
|
|
||||||
mpuMotionDetectMode();
|
|
||||||
|
|
||||||
// Force a read to clear any pending interrupts
|
|
||||||
mpu.getIntStatus();
|
|
||||||
|
|
||||||
// Add a brief delay to ensure MPU settles
|
|
||||||
delay(100);
|
|
||||||
|
|
||||||
// Configure the wake-up source (active LOW)
|
|
||||||
esp_sleep_enable_ext0_wakeup(GPIO_NUM_34, 0);
|
|
||||||
|
|
||||||
// Debug message
|
|
||||||
#ifdef DEBUG
|
|
||||||
Serial.println("Going to deep sleep now");
|
|
||||||
Serial.flush();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enter deep sleep
|
|
||||||
esp_deep_sleep_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleepTimer() {
|
void sleepTimer() {
|
||||||
if (zMotInterrupt && millis() - lastZMot > SLEEP_AFTER_MS) {
|
if (zMotInterrupt && millis() - lastZMot > SLEEP_AFTER_MS) {
|
||||||
|
@ -581,12 +605,20 @@ void sleepTimer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sleepCheck() {
|
||||||
|
int buttonState = digitalRead(GPIO_NUM_27);
|
||||||
|
if (buttonState == HIGH) {
|
||||||
|
// Button pressed, go to sleep
|
||||||
|
goToSleep();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static uint32_t last_frame = 0;
|
static uint32_t last_frame = 0;
|
||||||
drawParticles();
|
drawParticles();
|
||||||
reportIMU();
|
reportIMU();
|
||||||
if(millis() - last_frame >= 33) {
|
if(millis() - last_frame >= 3) {
|
||||||
sleepTimer();
|
sleepCheck();
|
||||||
last_frame = millis();
|
last_frame = millis();
|
||||||
applyPhysics();
|
applyPhysics();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "MPU6050_6Axis_MotionApps612.h"
|
#include "MPU6050_6Axis_MotionApps612.h"
|
||||||
|
|
||||||
// comment out to disable debug output / serial / led
|
// comment out to disable debug output / serial / led
|
||||||
#define DEBUG
|
// #define DEBUG
|
||||||
// LED for debugging interrupts
|
// LED for debugging interrupts
|
||||||
#define LED 17
|
#define LED 17
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@
|
||||||
// Simulation constants
|
// Simulation constants
|
||||||
#define SIM_WIDTH 128
|
#define SIM_WIDTH 128
|
||||||
#define SIM_HEIGHT 64
|
#define SIM_HEIGHT 64
|
||||||
#define NUM_PARTICLES 250
|
#define NUM_PARTICLES 100
|
||||||
#define PARTICLE_RADIUS 2
|
#define PARTICLE_RADIUS 2
|
||||||
#define GRAVITY 1.0f
|
#define GRAVITY 9.0f
|
||||||
#define DAMPING 0.3f
|
#define DAMPING 0.6f
|
||||||
#define PRESSURE_RADIUS 3.5f
|
#define PRESSURE_RADIUS 2.5f
|
||||||
#define PRESSURE_FORCE 0.9f
|
#define PRESSURE_FORCE 0.9f
|
||||||
#define MAX_VEL 2.0f
|
#define MAX_VEL 4.0f
|
||||||
#define GRID_SIZE 16
|
#define GRID_SIZE 16
|
||||||
#define CELL_SIZE (SIM_WIDTH/GRID_SIZE)
|
#define CELL_SIZE (SIM_WIDTH/GRID_SIZE)
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ MPU6050 mpu;
|
||||||
// I2C device found at address 0x68 ! // IMU
|
// I2C device found at address 0x68 ! // IMU
|
||||||
|
|
||||||
typedef float f32 __attribute__((aligned(4)));
|
typedef float f32 __attribute__((aligned(4)));
|
||||||
struct Particle {
|
struct __attribute__((packed)) Particle {
|
||||||
f32 x, y;
|
f32 x, y;
|
||||||
f32 vx, vy;
|
f32 vx, vy;
|
||||||
};
|
};
|
||||||
|
@ -127,6 +127,40 @@ void DMPDataReady() {
|
||||||
MPUInterrupt = true;
|
MPUInterrupt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void goToSleep() {
|
||||||
|
// detachInterrupt(GPIO_NUM_27); // Disable wake-up interrupt
|
||||||
|
// print goodbye message on display
|
||||||
|
memset(canvasData, 0, sizeof(canvasData));
|
||||||
|
canvas.setMode(CANVAS_MODE_TRANSPARENT);
|
||||||
|
canvas.setFixedFont(ssd1306xled_font6x8);
|
||||||
|
canvas.printFixed(30, 30, "Goodbye! <3", STYLE_NORMAL);
|
||||||
|
display.drawCanvas(0, 0, canvas);
|
||||||
|
// Add a brief delay to ensure display updates
|
||||||
|
delay(5000);
|
||||||
|
// Prepare MPU6050 for motion detection
|
||||||
|
mpuMotionDetectMode();
|
||||||
|
|
||||||
|
// Force a read to clear any pending interrupts
|
||||||
|
mpu.getIntStatus();
|
||||||
|
|
||||||
|
// Add a brief delay to ensure MPU settles
|
||||||
|
delay(100);
|
||||||
|
display.m_i2c.displayOff();
|
||||||
|
// Configure the wake-up source (active LOW)
|
||||||
|
esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, 1);
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Debug message
|
||||||
|
#ifdef DEBUG
|
||||||
|
Serial.println("Going to deep sleep now");
|
||||||
|
Serial.flush();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enter deep sleep
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
|
|
||||||
// Setup serial communication
|
// Setup serial communication
|
||||||
// only for debugging
|
// only for debugging
|
||||||
void initSerial() {
|
void initSerial() {
|
||||||
|
@ -252,26 +286,45 @@ void mpuActiveMonitorMode() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct GridCell {
|
// Optimized spatial grid using single array + cell indices
|
||||||
uint8_t particles[10];
|
uint8_t gridParticles[NUM_PARTICLES]; // All particles in grid order
|
||||||
uint8_t count;
|
uint16_t gridCellStart[GRID_SIZE * GRID_SIZE]; // Start index for each cell
|
||||||
};
|
uint8_t gridCellCount[GRID_SIZE * GRID_SIZE]; // Count for each cell
|
||||||
|
|
||||||
GridCell grid[GRID_SIZE][GRID_SIZE];
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool blinkState = true;
|
bool blinkState = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void buildSpatialGrid() {
|
void buildSpatialGrid() {
|
||||||
memset(grid, 0, sizeof(grid));
|
// Clear cell counts
|
||||||
|
memset(gridCellCount, 0, sizeof(gridCellCount));
|
||||||
for(int i=0; i<NUM_PARTICLES; i++) {
|
|
||||||
|
// Count particles per cell
|
||||||
|
for(int i = 0; i < NUM_PARTICLES; i++) {
|
||||||
int gx = constrain(particles[i].x / CELL_SIZE, 0, GRID_SIZE-1);
|
int gx = constrain(particles[i].x / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
int gy = constrain(particles[i].y / CELL_SIZE, 0, GRID_SIZE-1);
|
int gy = constrain(particles[i].y / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
|
int cellIndex = gy * GRID_SIZE + gx;
|
||||||
if(grid[gx][gy].count < 10) {
|
gridCellCount[cellIndex]++;
|
||||||
grid[gx][gy].particles[grid[gx][gy].count++] = i;
|
}
|
||||||
|
|
||||||
|
// Calculate start indices (prefix sum)
|
||||||
|
gridCellStart[0] = 0;
|
||||||
|
for(int i = 1; i < GRID_SIZE * GRID_SIZE; i++) {
|
||||||
|
gridCellStart[i] = gridCellStart[i-1] + gridCellCount[i-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset counts for filling
|
||||||
|
memset(gridCellCount, 0, sizeof(gridCellCount));
|
||||||
|
|
||||||
|
// Fill particle indices
|
||||||
|
for(int i = 0; i < NUM_PARTICLES; i++) {
|
||||||
|
int gx = constrain(particles[i].x / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
|
int gy = constrain(particles[i].y / CELL_SIZE, 0, GRID_SIZE-1);
|
||||||
|
int cellIndex = gy * GRID_SIZE + gx;
|
||||||
|
int insertPos = gridCellStart[cellIndex] + gridCellCount[cellIndex];
|
||||||
|
if(insertPos < NUM_PARTICLES) {
|
||||||
|
gridParticles[insertPos] = i;
|
||||||
|
gridCellCount[cellIndex]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,6 +379,7 @@ void setup() {
|
||||||
// DMPReady = true;
|
// DMPReady = true;
|
||||||
mpuActiveMonitorMode();
|
mpuActiveMonitorMode();
|
||||||
pinMode(EXT0_PIN, INPUT);
|
pinMode(EXT0_PIN, INPUT);
|
||||||
|
pinMode(GPIO_NUM_27, INPUT_PULLUP); // wake up pin
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
pinMode(LED, OUTPUT);
|
pinMode(LED, OUTPUT);
|
||||||
|
@ -335,7 +389,12 @@ void setup() {
|
||||||
Serial.println(F(")..."));
|
Serial.println(F(")..."));
|
||||||
#endif
|
#endif
|
||||||
attachInterrupt(digitalPinToInterrupt(EXT0_PIN), DMPDataReady, FALLING);
|
attachInterrupt(digitalPinToInterrupt(EXT0_PIN), DMPDataReady, FALLING);
|
||||||
|
// attachInterrupt(GPIO_NUM_27, goToSleep, FALLING);
|
||||||
MPUIntStatus = mpu.getIntStatus();
|
MPUIntStatus = mpu.getIntStatus();
|
||||||
|
|
||||||
|
// Initialize sleep timer (start counting from device boot/wake)
|
||||||
|
lastZMot = millis();
|
||||||
|
zMotInterrupt = false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
@ -380,9 +439,12 @@ void applyPhysics() {
|
||||||
if(gx+dx < 0 || gx+dx >= GRID_SIZE) continue;
|
if(gx+dx < 0 || gx+dx >= GRID_SIZE) continue;
|
||||||
if(gy+dy < 0 || gy+dy >= GRID_SIZE) continue;
|
if(gy+dy < 0 || gy+dy >= GRID_SIZE) continue;
|
||||||
|
|
||||||
GridCell &cell = grid[gx+dx][gy+dy];
|
int cellIndex = (gy+dy) * GRID_SIZE + (gx+dx);
|
||||||
for(int c=0; c<cell.count; c++) {
|
int cellStart = gridCellStart[cellIndex];
|
||||||
const int j = cell.particles[c];
|
int cellCount = gridCellCount[cellIndex];
|
||||||
|
|
||||||
|
for(int c = 0; c < cellCount; c++) {
|
||||||
|
const int j = gridParticles[cellStart + c];
|
||||||
if(j <= i) continue; // Avoid duplicate pairs
|
if(j <= i) continue; // Avoid duplicate pairs
|
||||||
|
|
||||||
const float dx = particles[j].x - particles[i].x;
|
const float dx = particles[j].x - particles[i].x;
|
||||||
|
@ -393,7 +455,7 @@ void applyPhysics() {
|
||||||
const float dist = fast_sqrt(dist_sq) + 0.001f;
|
const float dist = fast_sqrt(dist_sq) + 0.001f;
|
||||||
const float force = PRESSURE_FORCE * (1.0f - dist/PRESSURE_RADIUS);
|
const float force = PRESSURE_FORCE * (1.0f - dist/PRESSURE_RADIUS);
|
||||||
|
|
||||||
// Only apply horizontal forces to preserve gravity
|
// Apply pressure forces (original behavior)
|
||||||
particles[i].vx -= force * dx/dist;
|
particles[i].vx -= force * dx/dist;
|
||||||
particles[j].vx += force * dx/dist;
|
particles[j].vx += force * dx/dist;
|
||||||
|
|
||||||
|
@ -430,6 +492,7 @@ void applyPhysics() {
|
||||||
|
|
||||||
// Apply stronger damping during high movement
|
// Apply stronger damping during high movement
|
||||||
float adaptiveDamping = DAMPING * (1.0f - accelBoost * 0.2f);
|
float adaptiveDamping = DAMPING * (1.0f - accelBoost * 0.2f);
|
||||||
|
|
||||||
// X-axis
|
// X-axis
|
||||||
if(particles[i].x <= 0 || particles[i].x >= SIM_WIDTH-1) {
|
if(particles[i].x <= 0 || particles[i].x >= SIM_WIDTH-1) {
|
||||||
particles[i].vx *= -adaptiveDamping;
|
particles[i].vx *= -adaptiveDamping;
|
||||||
|
@ -450,8 +513,17 @@ void drawParticles() {
|
||||||
memset(canvasData, 0, sizeof(canvasData));
|
memset(canvasData, 0, sizeof(canvasData));
|
||||||
// canvas.clear();
|
// canvas.clear();
|
||||||
for(int i=0; i<NUM_PARTICLES; i++) {
|
for(int i=0; i<NUM_PARTICLES; i++) {
|
||||||
int x = constrain(static_cast<int>(particles[i].x + 0.5f), 0, SIM_WIDTH-1);
|
// Reset corrupted particles
|
||||||
int y = constrain(static_cast<int>(particles[i].y + 0.5f), 0, SIM_HEIGHT-1);
|
if(particles[i].x < 0 || particles[i].x > SIM_WIDTH || particles[i].y < 0 || particles[i].y > SIM_HEIGHT ||
|
||||||
|
isnan(particles[i].x) || isnan(particles[i].y) || isnan(particles[i].vx) || isnan(particles[i].vy)) {
|
||||||
|
particles[i].x = random(SIM_WIDTH/4, 3*SIM_WIDTH/4);
|
||||||
|
particles[i].y = random(SIM_HEIGHT/4, 3*SIM_HEIGHT/4);
|
||||||
|
particles[i].vx = 0;
|
||||||
|
particles[i].vy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
#if PARTICLE_RADIUS == 1
|
#if PARTICLE_RADIUS == 1
|
||||||
canvas.putPixel(x, y);
|
canvas.putPixel(x, y);
|
||||||
|
@ -502,11 +574,15 @@ void reportIMU() {
|
||||||
lastAccel = aa;
|
lastAccel = aa;
|
||||||
// Calculate magnitude of acceleration for impulse detection
|
// Calculate magnitude of acceleration for impulse detection
|
||||||
lastAccelMagnitude = accelMagnitude;
|
lastAccelMagnitude = accelMagnitude;
|
||||||
accelMagnitude = sqrt(aa.x*aa.x + aa.y*aa.y + aa.z*aa.z);
|
accelMagnitude = fast_sqrt(aa.x*aa.x + aa.y*aa.y + aa.z*aa.z);
|
||||||
|
|
||||||
// Detect sudden changes (ignoring gravity component of ~16384)
|
// Detect sudden changes (ignoring gravity component of ~16384)
|
||||||
float accelDelta = abs(accelMagnitude - lastAccelMagnitude);
|
float accelDelta = abs(accelMagnitude - lastAccelMagnitude);
|
||||||
maxAccelImpulse = max(maxAccelImpulse * 0.9f, accelDelta);
|
maxAccelImpulse = max(maxAccelImpulse * 0.9f, accelDelta);
|
||||||
|
|
||||||
|
// Reset sleep timer on ANY motion/DMP data (device is active)
|
||||||
|
lastZMot = millis();
|
||||||
|
zMotInterrupt = false; // Clear zero motion flag since we have motion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -521,31 +597,7 @@ void reportIMU() {
|
||||||
MPUInterrupt = false;
|
MPUInterrupt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToSleep() {
|
|
||||||
display.m_i2c.displayOff();
|
|
||||||
// detach interrupt
|
|
||||||
// detachInterrupt(digitalPinToInterrupt(EXT0_PIN));
|
|
||||||
// Prepare MPU6050 for motion detection
|
|
||||||
mpuMotionDetectMode();
|
|
||||||
|
|
||||||
// Force a read to clear any pending interrupts
|
|
||||||
mpu.getIntStatus();
|
|
||||||
|
|
||||||
// Add a brief delay to ensure MPU settles
|
|
||||||
delay(100);
|
|
||||||
|
|
||||||
// Configure the wake-up source (active LOW)
|
|
||||||
esp_sleep_enable_ext0_wakeup(GPIO_NUM_34, 0);
|
|
||||||
|
|
||||||
// Debug message
|
|
||||||
#ifdef DEBUG
|
|
||||||
Serial.println("Going to deep sleep now");
|
|
||||||
Serial.flush();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enter deep sleep
|
|
||||||
esp_deep_sleep_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleepTimer() {
|
void sleepTimer() {
|
||||||
if (zMotInterrupt && millis() - lastZMot > SLEEP_AFTER_MS) {
|
if (zMotInterrupt && millis() - lastZMot > SLEEP_AFTER_MS) {
|
||||||
|
@ -553,19 +605,23 @@ void sleepTimer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sleepCheck() {
|
||||||
|
int buttonState = digitalRead(GPIO_NUM_27);
|
||||||
|
if (buttonState == HIGH) {
|
||||||
|
// Button pressed, go to sleep
|
||||||
|
goToSleep();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static uint32_t last_frame = 0;
|
static uint32_t last_frame = 0;
|
||||||
drawParticles();
|
drawParticles();
|
||||||
reportIMU();
|
reportIMU();
|
||||||
if(millis() - last_frame >= 33) {
|
if(millis() - last_frame >= 3) {
|
||||||
sleepTimer();
|
sleepCheck();
|
||||||
last_frame = millis();
|
last_frame = millis();
|
||||||
applyPhysics();
|
applyPhysics();
|
||||||
} else {
|
} else {
|
||||||
lcd_delay(millis() - last_frame);
|
lcd_delay(millis() - last_frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO
|
|
||||||
// 1. add proper collision / overlap constraints (so no line of balls)
|
|
||||||
// 2. adjust phsyics further, make it more natural
|
|
||||||
// 3. adjust particle direction from impulse / accel interaction (gravity is correct, impulse is flipped)
|
|
334
old/flip.ino
334
old/flip.ino
|
@ -1,334 +0,0 @@
|
||||||
#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);
|
|
||||||
}
|
|
||||||
}
|
|
106
old/fluid.cpp
106
old/fluid.cpp
|
@ -1,106 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Simulation constants
|
|
||||||
const int SIM_WIDTH = 64;
|
|
||||||
const int SIM_HEIGHT = 32;
|
|
||||||
const int NUM_PARTICLES = 100;
|
|
||||||
|
|
||||||
struct Particle {
|
|
||||||
double x, y;
|
|
||||||
double vx, vy;
|
|
||||||
};
|
|
||||||
|
|
||||||
vector<Particle> particles(NUM_PARTICLES);
|
|
||||||
|
|
||||||
void initializeParticles() {
|
|
||||||
for (auto& p : particles) {
|
|
||||||
p.x = rand() % SIM_WIDTH;
|
|
||||||
p.y = rand() % (SIM_HEIGHT / 2);
|
|
||||||
p.vx = (rand() % 200 - 100) / 100.0 * 0.7;
|
|
||||||
p.vy = (rand() % 100 - 50) / 100.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePhysics() {
|
|
||||||
const double gravity = 0.15;
|
|
||||||
const double damping = 0.82;
|
|
||||||
const double border = 2.0;
|
|
||||||
|
|
||||||
for (auto& p : particles) {
|
|
||||||
p.vy += gravity;
|
|
||||||
p.x += p.vx;
|
|
||||||
p.y += p.vy;
|
|
||||||
|
|
||||||
// Horizontal boundary collisions
|
|
||||||
if (p.x < border || p.x >= SIM_WIDTH - border) {
|
|
||||||
p.vx = -p.vx * damping;
|
|
||||||
p.x = max(border, min(p.x, SIM_WIDTH - border - 0.1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertical boundary collisions
|
|
||||||
if (p.y < border || p.y >= SIM_HEIGHT - border) {
|
|
||||||
p.vy = -p.vy * damping;
|
|
||||||
p.y = max(border, min(p.y, SIM_HEIGHT - border - 0.1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawFrame() {
|
|
||||||
vector<vector<char> > grid(SIM_HEIGHT, vector<char>(SIM_WIDTH, ' '));
|
|
||||||
|
|
||||||
// Plot particles
|
|
||||||
for (const auto& p : particles) {
|
|
||||||
int x = static_cast<int>(p.x);
|
|
||||||
int y = static_cast<int>(p.y);
|
|
||||||
x = max(0, min(SIM_WIDTH - 1, x));
|
|
||||||
y = max(0, min(SIM_HEIGHT - 1, y));
|
|
||||||
grid[y][x] = 'o';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear screen and reset cursor
|
|
||||||
cout << "\033[H";
|
|
||||||
|
|
||||||
// Draw grid
|
|
||||||
for (const auto& row : grid) {
|
|
||||||
for (char c : row) {
|
|
||||||
cout << c;
|
|
||||||
}
|
|
||||||
cout << '\n';
|
|
||||||
}
|
|
||||||
cout << flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
srand(time(nullptr));
|
|
||||||
initializeParticles();
|
|
||||||
|
|
||||||
// Hide cursor
|
|
||||||
cout << "\033[?25l";
|
|
||||||
|
|
||||||
auto last_frame = chrono::steady_clock::now();
|
|
||||||
const chrono::milliseconds frame_delay(33);
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
auto now = chrono::steady_clock::now();
|
|
||||||
if (now - last_frame >= frame_delay) {
|
|
||||||
updatePhysics();
|
|
||||||
drawFrame();
|
|
||||||
last_frame = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
// Show cursor before exiting
|
|
||||||
cout << "\033[?25h";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore cursor
|
|
||||||
cout << "\033[?25h";
|
|
||||||
return 0;
|
|
||||||
}
|
|
92
old/fluid.py
92
old/fluid.py
|
@ -1,92 +0,0 @@
|
||||||
import time
|
|
||||||
import random
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Simulation constants
|
|
||||||
SIM_WIDTH = 128
|
|
||||||
SIM_HEIGHT = 64
|
|
||||||
NUM_PARTICLES = 200
|
|
||||||
GRAVITY = 0.5
|
|
||||||
DAMPING = 0.82
|
|
||||||
BORDER = 2.0
|
|
||||||
FRAME_DELAY = 0.033 # ~30 FPS
|
|
||||||
|
|
||||||
class Particle:
|
|
||||||
def __init__(self):
|
|
||||||
self.x = random.uniform(BORDER, SIM_WIDTH - BORDER)
|
|
||||||
self.y = random.uniform(BORDER, SIM_HEIGHT/2 - BORDER)
|
|
||||||
self.vx = random.uniform(-1, 1) * 0.7
|
|
||||||
self.vy = random.uniform(-0.5, 0.5)
|
|
||||||
|
|
||||||
def initialize_particles(num_particles):
|
|
||||||
return [Particle() for _ in range(num_particles)]
|
|
||||||
|
|
||||||
def update_physics(particles):
|
|
||||||
for p in particles:
|
|
||||||
# Apply gravity
|
|
||||||
p.vy += GRAVITY
|
|
||||||
|
|
||||||
# Update position
|
|
||||||
p.x += p.vx
|
|
||||||
p.y += p.vy
|
|
||||||
|
|
||||||
# Horizontal boundary collisions
|
|
||||||
if p.x < BORDER or p.x >= SIM_WIDTH - BORDER:
|
|
||||||
p.vx *= -DAMPING
|
|
||||||
p.x = max(BORDER, min(p.x, SIM_WIDTH - BORDER - 0.1))
|
|
||||||
|
|
||||||
# Vertical boundary collisions
|
|
||||||
if p.y < BORDER or p.y >= SIM_HEIGHT - BORDER:
|
|
||||||
p.vy *= -DAMPING
|
|
||||||
p.y = max(BORDER, min(p.y, SIM_HEIGHT - BORDER - 0.1))
|
|
||||||
|
|
||||||
def draw_frame(particles):
|
|
||||||
# Initialize empty grid
|
|
||||||
grid = [[' ' for _ in range(SIM_WIDTH)] for _ in range(SIM_HEIGHT)]
|
|
||||||
|
|
||||||
# Plot particles
|
|
||||||
for p in particles:
|
|
||||||
x = int(p.x)
|
|
||||||
y = int(p.y)
|
|
||||||
if 0 <= x < SIM_WIDTH and 0 <= y < SIM_HEIGHT:
|
|
||||||
grid[y][x] = 'o'
|
|
||||||
|
|
||||||
# Build frame buffer
|
|
||||||
buffer = []
|
|
||||||
for row in grid:
|
|
||||||
buffer.append(''.join(row))
|
|
||||||
|
|
||||||
# Clear screen and move cursor to top-left
|
|
||||||
sys.stdout.write('\033[H\033[J')
|
|
||||||
sys.stdout.write('\n'.join(buffer))
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
particles = initialize_particles(NUM_PARTICLES)
|
|
||||||
|
|
||||||
# Hide cursor
|
|
||||||
sys.stdout.write('\033[?25l')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
start_time = time.monotonic()
|
|
||||||
|
|
||||||
update_physics(particles)
|
|
||||||
draw_frame(particles)
|
|
||||||
|
|
||||||
# Frame rate control
|
|
||||||
elapsed = time.monotonic() - start_time
|
|
||||||
sleep_time = FRAME_DELAY - elapsed
|
|
||||||
if sleep_time > 0:
|
|
||||||
time.sleep(sleep_time)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
# Show cursor before exiting
|
|
||||||
sys.stdout.write('\033[?25h')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,113 +0,0 @@
|
||||||
#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);
|
|
||||||
}
|
|
113
old/sketch.ino
113
old/sketch.ino
|
@ -1,113 +0,0 @@
|
||||||
#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);
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
#include <Arduino.h>
|
|
||||||
#include <FastTrig.h>
|
|
||||||
#include "I2Cdev.h"
|
|
||||||
// #include "MPU6050_6Axis_MotionApps20.h"
|
|
||||||
#include "MPU6050_6Axis_MotionApps612.h"
|
|
||||||
|
|
||||||
|
|
||||||
MPU6050 mpu;
|
|
||||||
|
|
||||||
#define DEBUG
|
|
||||||
#define IMU_ADDRESS 0x68
|
|
||||||
#define LED 17
|
|
||||||
// External interrupt source
|
|
||||||
#define EXT0_PIN 34
|
|
||||||
|
|
||||||
/*---MPU6050 Control/Status Variables---*/
|
|
||||||
bool DMPReady = false; // Set true if DMP init was successful
|
|
||||||
uint8_t MPUIntStatus; // Holds actual interrupt status byte from MPU
|
|
||||||
uint8_t devStatus; // Return status after each device operation (0 = success, !0 = error)
|
|
||||||
uint16_t packetSize; // Expected DMP packet size (default is 42 bytes)
|
|
||||||
uint8_t FIFOBuffer[64]; // FIFO storage buffer
|
|
||||||
/*---Orientation/Motion Variables---*/
|
|
||||||
Quaternion q; // [w, x, y, z] Quaternion container
|
|
||||||
VectorInt16 aa; // [x, y, z] Accel sensor measurements
|
|
||||||
VectorInt16 gy; // [x, y, z] Gyro sensor measurements
|
|
||||||
VectorInt16 aaReal; // [x, y, z] Gravity-free accel sensor measurements
|
|
||||||
VectorInt16 aaWorld; // [x, y, z] World-frame accel sensor measurements
|
|
||||||
VectorFloat gravity; // [x, y, z] Gravity vector
|
|
||||||
uint16_t fifoCount;
|
|
||||||
float euler[3]; // [psi, theta, phi] Euler angle container
|
|
||||||
float ypr[3]; // [yaw, pitch, roll] Yaw/Pitch/Roll container and gravity vector
|
|
||||||
|
|
||||||
/*------Interrupt detection routine------*/
|
|
||||||
volatile bool MPUInterrupt = true; // Indicates whether MPU6050 interrupt pin has gone high
|
|
||||||
void DMPDataReady() {
|
|
||||||
MPUInterrupt = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool blinkState = true;
|
|
||||||
|
|
||||||
// Setup serial communication
|
|
||||||
|
|
||||||
void initSerial() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
while (!Serial) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initI2C() {
|
|
||||||
Wire.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void initMpu() {
|
|
||||||
// set up MPU
|
|
||||||
mpu.reset();
|
|
||||||
delay(100);
|
|
||||||
mpu.resetSensors();
|
|
||||||
delay(100);
|
|
||||||
mpu.setClockSource(MPU6050_CLOCK_PLL_XGYRO);
|
|
||||||
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
|
|
||||||
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
|
|
||||||
mpu.setSleepEnabled(false);
|
|
||||||
mpu.setStandbyXAccelEnabled(false);
|
|
||||||
mpu.setStandbyYAccelEnabled(false);
|
|
||||||
mpu.setStandbyZAccelEnabled(false);
|
|
||||||
mpu.setExternalFrameSync(MPU6050_EXT_SYNC_DISABLED);
|
|
||||||
|
|
||||||
// See https://github.com/ElectronicCats/mpu6050/blob/master/src/MPU6050.cpp
|
|
||||||
mpu.setDLPFMode(MPU6050_DLPF_BW_42);
|
|
||||||
delay(100);
|
|
||||||
mpu.setDHPFMode(MPU6050_DHPF_HOLD);
|
|
||||||
|
|
||||||
mpu.setMotionDetectionThreshold(10);
|
|
||||||
mpu.setMotionDetectionDuration(2);
|
|
||||||
mpu.setInterruptMode(MPU6050_INTMODE_ACTIVEHIGH);
|
|
||||||
mpu.setAccelerometerPowerOnDelay(2); //max
|
|
||||||
mpu.setInterruptLatch(MPU6050_INTLATCH_WAITCLEAR);
|
|
||||||
mpu.setInterruptLatchClear(MPU6050_INTCLEAR_STATUSREAD);
|
|
||||||
mpu.setIntEnabled(1 << MPU6050_INTERRUPT_MOT_BIT);
|
|
||||||
mpu.setInterruptDrive(MPU6050_INTDRV_PUSHPULL);
|
|
||||||
mpu.setStandbyXAccelEnabled(false);
|
|
||||||
mpu.setStandbyYAccelEnabled(false);
|
|
||||||
mpu.setStandbyZAccelEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
initSerial();
|
|
||||||
initI2C();
|
|
||||||
initMpu();
|
|
||||||
pinMode(LED, OUTPUT);
|
|
||||||
pinMode(EXT0_PIN, INPUT);
|
|
||||||
digitalWrite(LED, blinkState);
|
|
||||||
Serial.println("Initializing MPU...");
|
|
||||||
|
|
||||||
/*Enable Arduino interrupt detection*/
|
|
||||||
Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
|
|
||||||
Serial.println(F(")..."));
|
|
||||||
attachInterrupt(digitalPinToInterrupt(EXT0_PIN), DMPDataReady, RISING);
|
|
||||||
// attachInterrupt(digitalPinToInterrupt(EXT0_PIN), DMPDataReady, RISING);
|
|
||||||
MPUIntStatus = mpu.getIntStatus();
|
|
||||||
// mpu.setInterruptLatchClear(true);
|
|
||||||
Serial.println(MPUIntStatus);
|
|
||||||
// Serial.println(devStatus);
|
|
||||||
/* Set the DMP Ready flag so the main loop() function knows it is okay to use it */
|
|
||||||
Serial.println(F("DMP ready! Waiting for first interrupt..."));
|
|
||||||
DMPReady = true;
|
|
||||||
// packetSize = mpu.dmpGetFIFOPacketSize(); //Get expected DMP packet size for later comparison
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void reportIMU() {
|
|
||||||
if (!DMPReady) return; // Stop the program if DMP programming fails.
|
|
||||||
if (!MPUInterrupt) return;
|
|
||||||
blinkState = !blinkState;
|
|
||||||
digitalWrite(LED, blinkState);
|
|
||||||
MPUIntStatus = mpu.getIntStatus();
|
|
||||||
// mpu.setInterruptLatchClear(true);
|
|
||||||
// mpu.setInterruptLat
|
|
||||||
MPUInterrupt = false;
|
|
||||||
return;
|
|
||||||
/* Read a packet from FIFO */
|
|
||||||
fifoCount = mpu.getFIFOCount();
|
|
||||||
// check for overflow (this should never happen unless our code is too inefficient)
|
|
||||||
if ((MPUIntStatus & 0x10) || fifoCount == 1024) {
|
|
||||||
// reset so we can continue cleanly
|
|
||||||
mpu.resetFIFO();
|
|
||||||
Serial.println(F("FIFO overflow!, giro descompensat!!"));
|
|
||||||
Serial.print("&");
|
|
||||||
// otherwise, check for DMP data ready interrupt (this should happen frequently)
|
|
||||||
}
|
|
||||||
else if (MPUIntStatus & 0x02) {
|
|
||||||
if (mpu.dmpGetCurrentFIFOPacket(FIFOBuffer)) { // Get the Latest packet
|
|
||||||
mpu.dmpGetQuaternion(&q, FIFOBuffer);
|
|
||||||
mpu.dmpGetGravity(&gravity, &q);
|
|
||||||
}
|
|
||||||
} else if (MPUIntStatus & (1<<MPU6050_INTERRUPT_MOT_BIT))
|
|
||||||
//mpu.setInterruptLatchClear(true);
|
|
||||||
MPUInterrupt = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
static uint32_t last_frame = 0;
|
|
||||||
//lcd_delay(5000);
|
|
||||||
//delay(1000);
|
|
||||||
if(millis() - last_frame >= 33) {
|
|
||||||
reportIMU();
|
|
||||||
last_frame = millis();
|
|
||||||
}
|
|
||||||
// else {
|
|
||||||
// delay(millis() - last_frame);
|
|
||||||
//}
|
|
||||||
}
|
|
361
old/trash.ino
361
old/trash.ino
|
@ -1,361 +0,0 @@
|
||||||
#include <Arduino.h>
|
|
||||||
#include <lcdgfx.h>
|
|
||||||
#include <FastTrig.h>
|
|
||||||
#include "I2Cdev.h"
|
|
||||||
// #include "MPU6050_6Axis_MotionApps20.h"
|
|
||||||
#include "MPU6050_6Axis_MotionApps612.h"
|
|
||||||
|
|
||||||
DisplaySSD1306_128x64_I2C display(-1);
|
|
||||||
MPU6050 mpu;
|
|
||||||
|
|
||||||
// Simulation constants
|
|
||||||
#define SIM_WIDTH 128
|
|
||||||
#define SIM_HEIGHT 64
|
|
||||||
#define NUM_PARTICLES 1000
|
|
||||||
#define PARTICLE_RADIUS 2
|
|
||||||
#define GRAVITY 1.0f
|
|
||||||
#define DAMPING 0.4f
|
|
||||||
#define PRESSURE_RADIUS 3.5f
|
|
||||||
#define PRESSURE_FORCE 0.9f
|
|
||||||
#define MAX_VEL 1.0f
|
|
||||||
#define IMU_ADDRESS 0x68
|
|
||||||
#define OUTPUT_READABLE_YAWPITCHROLL
|
|
||||||
#define LED 2
|
|
||||||
|
|
||||||
// I2C device found at address 0x3C ! // OLED
|
|
||||||
// I2C device found at address 0x68 ! // IMU
|
|
||||||
|
|
||||||
typedef float f32 __attribute__((aligned(4)));
|
|
||||||
struct Particle {
|
|
||||||
f32 x, y;
|
|
||||||
f32 vx, vy;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
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)]; // because of 1bit display, not RGB
|
|
||||||
NanoCanvas1 canvas(SIM_WIDTH, SIM_HEIGHT, canvasData);
|
|
||||||
|
|
||||||
|
|
||||||
#define GRID_SIZE 16
|
|
||||||
#define CELL_SIZE (SIM_WIDTH/GRID_SIZE)
|
|
||||||
|
|
||||||
int const INTERRUPT_PIN = 18;
|
|
||||||
/*---MPU6050 Control/Status Variables---*/
|
|
||||||
bool DMPReady = false; // Set true if DMP init was successful
|
|
||||||
uint8_t MPUIntStatus; // Holds actual interrupt status byte from MPU
|
|
||||||
uint8_t devStatus; // Return status after each device operation (0 = success, !0 = error)
|
|
||||||
uint16_t packetSize; // Expected DMP packet size (default is 42 bytes)
|
|
||||||
uint8_t FIFOBuffer[64]; // FIFO storage buffer
|
|
||||||
/*---Orientation/Motion Variables---*/
|
|
||||||
Quaternion q; // [w, x, y, z] Quaternion container
|
|
||||||
VectorInt16 aa; // [x, y, z] Accel sensor measurements
|
|
||||||
VectorInt16 gy; // [x, y, z] Gyro sensor measurements
|
|
||||||
VectorInt16 aaReal; // [x, y, z] Gravity-free accel sensor measurements
|
|
||||||
VectorInt16 aaWorld; // [x, y, z] World-frame accel sensor measurements
|
|
||||||
VectorFloat gravity; // [x, y, z] Gravity vector
|
|
||||||
uint16_t fifoCount;
|
|
||||||
float euler[3]; // [psi, theta, phi] Euler angle container
|
|
||||||
float ypr[3]; // [yaw, pitch, roll] Yaw/Pitch/Roll container and gravity vector
|
|
||||||
|
|
||||||
/*------Interrupt detection routine------*/
|
|
||||||
volatile bool MPUInterrupt = true; // Indicates whether MPU6050 interrupt pin has gone high
|
|
||||||
void DMPDataReady() {
|
|
||||||
MPUInterrupt = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct GridCell {
|
|
||||||
uint8_t particles[10];
|
|
||||||
uint8_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
GridCell grid[GRID_SIZE][GRID_SIZE];
|
|
||||||
|
|
||||||
bool blinkState;
|
|
||||||
|
|
||||||
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
|
|
||||||
Serial.begin(38400);
|
|
||||||
while (!Serial) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
Serial.println("Initializing MPU...");
|
|
||||||
/*--Start I2C interface--*/
|
|
||||||
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
|
|
||||||
Wire.begin();
|
|
||||||
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
|
|
||||||
Fastwire::setup(400, true);
|
|
||||||
#endif
|
|
||||||
mpu.initialize();
|
|
||||||
pinMode(INTERRUPT_PIN, INPUT);
|
|
||||||
pinMode(LED,OUTPUT);
|
|
||||||
Serial.println("Testing MPU6050 connection...");
|
|
||||||
// if(mpu.testConnection() == false){
|
|
||||||
// Serial.println("MPU6050 connection failed");
|
|
||||||
// while(true);
|
|
||||||
// }
|
|
||||||
// else{
|
|
||||||
devStatus = mpu.dmpInitialize();
|
|
||||||
Serial.println("MPU6050 connection successful");
|
|
||||||
|
|
||||||
mpu.setSleepEnabled(false);
|
|
||||||
mpu.setStandbyXAccelEnabled(false);
|
|
||||||
mpu.setStandbyYAccelEnabled(false);
|
|
||||||
mpu.setStandbyZAccelEnabled(false);
|
|
||||||
mpu.setDHPFMode(0);
|
|
||||||
mpu.setIntMotionEnabled(true);
|
|
||||||
mpu.setMotionDetectionDuration(1);
|
|
||||||
mpu.setMotionDetectionThreshold(20);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mpu.setXGyroOffset(-69);
|
|
||||||
mpu.setYGyroOffset(-48);
|
|
||||||
mpu.setZGyroOffset(-19);
|
|
||||||
mpu.setXAccelOffset(-5158);
|
|
||||||
mpu.setYAccelOffset(-4576);
|
|
||||||
mpu.setZAccelOffset(7687);
|
|
||||||
|
|
||||||
mpu.CalibrateAccel(6); // Calibration Time: generate offsets and calibrate our MPU6050
|
|
||||||
mpu.CalibrateGyro(6);
|
|
||||||
Serial.println("These are the Active offsets: ");
|
|
||||||
mpu.PrintActiveOffsets();
|
|
||||||
Serial.println(F("Enabling DMP...")); //Turning ON DMP
|
|
||||||
mpu.setDMPEnabled(true);
|
|
||||||
|
|
||||||
delay(100);
|
|
||||||
|
|
||||||
mpu.setDHPFMode(7);
|
|
||||||
mpu.setWakeFrequency(5);
|
|
||||||
mpu.setWakeCycleEnabled(true);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*Enable Arduino interrupt detection*/
|
|
||||||
Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
|
|
||||||
Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
|
|
||||||
Serial.println(F(")..."));
|
|
||||||
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), DMPDataReady, RISING);
|
|
||||||
MPUIntStatus = mpu.getIntStatus();
|
|
||||||
Serial.println(MPUIntStatus);
|
|
||||||
Serial.println(devStatus);
|
|
||||||
/* Set the DMP Ready flag so the main loop() function knows it is okay to use it */
|
|
||||||
Serial.println(F("DMP ready! Waiting for first interrupt..."));
|
|
||||||
DMPReady = true;
|
|
||||||
packetSize = mpu.dmpGetFIFOPacketSize(); //Get expected DMP packet size for later comparison
|
|
||||||
// }
|
|
||||||
// setup display
|
|
||||||
display.begin();
|
|
||||||
display.clear();
|
|
||||||
canvas.setMode(CANVAS_MODE_TRANSPARENT);
|
|
||||||
|
|
||||||
// Initialize particles in a droplet pattern
|
|
||||||
float cx = SIM_WIDTH/2;
|
|
||||||
float cy = SIM_HEIGHT/4;
|
|
||||||
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
|
|
||||||
//Serial.print("\nParticle ");
|
|
||||||
//Serial.print(i);
|
|
||||||
//Serial.print(" done");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyPhysics() {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct canvas buffer access (replace drawParticles)
|
|
||||||
void drawParticles() {
|
|
||||||
// Clear canvas by direct memory access
|
|
||||||
memset(canvasData, 0, sizeof(canvasData));
|
|
||||||
// canvas.clear();
|
|
||||||
for(int i=0; i<NUM_PARTICLES; i++) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
#if PARTICLE_RADIUS == 1
|
|
||||||
canvas.putPixel(x, y);
|
|
||||||
#else
|
|
||||||
canvas.drawCircle(x,y,PARTICLE_RADIUS-1);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
display.drawCanvas(0,0,canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportIMU() {
|
|
||||||
if (!DMPReady) return; // Stop the program if DMP programming fails.
|
|
||||||
if (!MPUInterrupt) return;
|
|
||||||
MPUIntStatus = mpu.getIntStatus();
|
|
||||||
/* Read a packet from FIFO */
|
|
||||||
fifoCount = mpu.getFIFOCount();
|
|
||||||
// check for overflow (this should never happen unless our code is too inefficient)
|
|
||||||
if ((MPUIntStatus & 0x10) || fifoCount == 1024) {
|
|
||||||
// reset so we can continue cleanly
|
|
||||||
mpu.resetFIFO();
|
|
||||||
Serial.println(F("FIFO overflow!, giro descompensat!!"));
|
|
||||||
Serial.print("&");
|
|
||||||
// otherwise, check for DMP data ready interrupt (this should happen frequently)
|
|
||||||
}
|
|
||||||
else if (MPUIntStatus & 0x02) {
|
|
||||||
if (mpu.dmpGetCurrentFIFOPacket(FIFOBuffer)) { // Get the Latest packet
|
|
||||||
#ifdef OUTPUT_READABLE_YAWPITCHROLL
|
|
||||||
/* Display Euler angles in degrees */
|
|
||||||
mpu.dmpGetQuaternion(&q, FIFOBuffer);
|
|
||||||
mpu.dmpGetGravity(&gravity, &q);
|
|
||||||
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
|
|
||||||
Serial.print("ypr\t");
|
|
||||||
Serial.print(ypr[0] * 180/M_PI);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.print(ypr[1] * 180/M_PI);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.println(ypr[2] * 180/M_PI);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef OUTPUT_READABLE_QUATERNION
|
|
||||||
/* Display Quaternion values in easy matrix form: [w, x, y, z] */
|
|
||||||
mpu.dmpGetQuaternion(&q, FIFOBuffer);
|
|
||||||
Serial.print("quat\t");
|
|
||||||
Serial.print(q.w);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.print(q.x);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.print(q.y);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.println(q.z);
|
|
||||||
#endif
|
|
||||||
#ifdef OUTPUT_READABLE_WORLDACCEL
|
|
||||||
/* Display initial world-frame acceleration, adjusted to remove gravity
|
|
||||||
and rotated based on known orientation from Quaternion */
|
|
||||||
mpu.dmpGetQuaternion(&q, FIFOBuffer);
|
|
||||||
mpu.dmpGetAccel(&aa, FIFOBuffer);
|
|
||||||
mpu.dmpGetGravity(&gravity, &q);
|
|
||||||
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
|
|
||||||
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
|
|
||||||
Serial.print("aworld\t");
|
|
||||||
Serial.print(aaWorld.x);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.print(aaWorld.y);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.println(aaWorld.z);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Blink LED to indicate activity */
|
|
||||||
blinkState = !blinkState;
|
|
||||||
digitalWrite(LED, blinkState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MPUInterrupt = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
static uint32_t last_frame = 0;
|
|
||||||
//lcd_delay(5000);
|
|
||||||
drawParticles();
|
|
||||||
//delay(1000);
|
|
||||||
if(millis() - last_frame >= 33) {
|
|
||||||
last_frame = millis();
|
|
||||||
applyPhysics();
|
|
||||||
reportIMU();
|
|
||||||
} else {
|
|
||||||
lcd_delay(millis() - last_frame);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue