Merge pull request 'organism' (#3) from organism into master

Reviewed-on: #3
This commit is contained in:
Felix Pankratz 2024-12-15 15:00:49 +01:00
commit 11f2fa3a9e
13 changed files with 468 additions and 0 deletions

21
organism/Makefile Normal file
View File

@ -0,0 +1,21 @@
# Uncomment lines below if you have problems with $PATH
#SHELL := /bin/bash
#PATH := /usr/local/bin:$(PATH)
all:
pio.exe -f -c vim run
upload:
pio.exe -f -c vim run --target upload
clean:
pio.exe -f -c vim run --target clean
program:
pio.exe -f -c vim run --target program
uploadfs:
pio.exe -f -c vim run --target uploadfs
update:
pio.exe -f -c vim update

22
organism/platformio.ini Normal file
View File

@ -0,0 +1,22 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:adafruit_matrixportal_esp32s3]
platform = espressif32
board = adafruit_matrixportal_esp32s3
framework = arduino
monitor_speed = 115200
lib_deps =
adafruit/Adafruit GFX Library
adafruit/Adafruit BusIO
Wire
fastled/FastLED
https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA.git

105
organism/src/blur.cpp Normal file
View File

@ -0,0 +1,105 @@
#include <Arduino.h>
#include "constants.h"
#include "config.h"
#include "util.h"
void setupGaussianKernel(float* kernel, int width, float sigma) {
float sum = 0.0;
for(int i = 0; i < width; i++) {
//kernel[width+i] = exp( -(i*i) / (2 * sigma * sigma)) / (PI * 2 * sigma * sigma);
kernel[i] = exp(-0.5f * (i * i) / (sigma * sigma));
sum += kernel[width];
}
for(int i; i < width; i++) {
kernel[i] = sum/width;
}
}
//float first_pass[PANEL_WIDTH][PANEL_HEIGHT];
void gaussian_blur(float *field[PANEL_WIDTH][PANEL_HEIGHT]) {
float kernel[GAUSS_WIDTH];
float first_pass[PANEL_WIDTH][PANEL_HEIGHT];
memset(first_pass, 0.0, sizeof first_pass);
setupGaussianKernel(kernel, GAUSS_WIDTH, GAUSS_SIGMA);
// horizontal pass
for (int x = 0; x < PANEL_WIDTH; x++) {
for(int y = 0; y < PANEL_HEIGHT; y++) {
float sum = *field[x][y] * DECAY_FACTOR * kernel[0]; //0.0;
int additions = 1;
for (int x_offset = 1; x_offset < GAUSS_WIDTH; x_offset++) {
if (is_in_bounds(x+x_offset, y)) {
sum += *field[x + x_offset][y] * kernel[x_offset];
additions++;
}
if (is_in_bounds(x-x_offset, y)) {
sum += *field[x - x_offset][y] * kernel[x_offset];
additions++;
}
}
first_pass[x][y] = sum/GAUSS_WIDTH;
}
}
// vertical pass
for (int x = 0; x < PANEL_WIDTH; x++) {
for(int y = 0; y < PANEL_HEIGHT; y++) {
float sum = first_pass[x][y] * kernel[0]; //0.0;
int additions = 1;
for (int y_offset = 1; y_offset < GAUSS_WIDTH; y_offset++) {
if (is_in_bounds(x, y + y_offset)) {
sum += first_pass[x][y + y_offset] * kernel[y_offset];
additions++;
}
if (is_in_bounds(x, y - y_offset)) {
sum += first_pass[x][y - y_offset] * kernel[y_offset];
additions++;
}
}
*field[x][y] = sum/GAUSS_WIDTH;
}
}
}
void box_blur(float field[PANEL_WIDTH][PANEL_HEIGHT]) {
auto first_pass = new float[PANEL_WIDTH][PANEL_HEIGHT]();
// horizontal pass
for (int x = 0; x < PANEL_WIDTH; x++) {
for(int y = 0; y < PANEL_HEIGHT; y++) {
float sum;
sum = field[x][y] * DECAY_FACTOR; //0.0;
int additions = 1;
for (int x_offset = 1; x_offset < BLUR_KERNEL_SIZE; x_offset++) {
if (is_in_bounds(x+x_offset, y)) {
sum += field[x + x_offset][y];
additions++;
}
if (is_in_bounds(x-x_offset, y)) {
sum += field[x - x_offset][y];
additions++;
}
}
first_pass[x][y] = (sum/additions);
}
}
// vertical pass
for (int x = 0; x < PANEL_WIDTH; x++) {
for(int y = 0; y < PANEL_HEIGHT; y++) {
float sum = first_pass[x][y]; //0.0;
int additions = 1;
for (int y_offset = 1; y_offset < BLUR_KERNEL_SIZE; y_offset++) {
if (is_in_bounds(x, y + y_offset)) {
sum += first_pass[x][y + y_offset];
additions++;
}
if (is_in_bounds(x, y - y_offset)) {
sum += first_pass[x][y - y_offset];
additions++;
}
}
float result = (sum/additions); // * DECAY_FACTOR;
//attractant[x][y] = (((8.0*attractant[x][y]) + result) / 9.0);
field[x][y] = result;
}
}
delete[] first_pass;
}

3
organism/src/blur.h Normal file
View File

@ -0,0 +1,3 @@
#include "constants.h"
void gaussian_blur(float *field[PANEL_WIDTH][PANEL_HEIGHT]);
void box_blur(float field[PANEL_WIDTH][PANEL_HEIGHT]);

18
organism/src/config.h Normal file
View File

@ -0,0 +1,18 @@
// gaussian blur
#define GAUSS_WIDTH 1
#define GAUSS_SIGMA 1.0
#define DECAY_FACTOR 0.9
// box blur
#define BLUR_KERNEL_SIZE 1
// agent params
#define NUM_AGENTS_MIN 64
#define NUM_AGENTS_MAX 301
#define AGENT_DROP_AMOUNT 10
#define AGENT_SENSOR_DISTANCE_MIN 2.0
#define AGENT_SENSOR_DISTANCE_MAX 3.5
// general params
#define NUM_ITERATIONS 500
#define SPAWN_MIN_X 2
#define SPAWN_MAX_X 63
#define SPAWN_MIN_Y 2
#define SPAWN_MAX_Y 31

26
organism/src/constants.h Normal file
View File

@ -0,0 +1,26 @@
#define R1 42
#define G1 40
#define BL1 41
#define R2 38
#define G2 37
#define BL2 39
#define CH_A 45
#define CH_B 36
#define CH_C 48
#define CH_D 35
#define CH_E 21
#define CLK 2
#define LAT 47
#define OE 14
#define PIN_E 21
#define PANEL_WIDTH 64
#define PANEL_HEIGHT 32 // Panel height of 64 will required PIN_E to be defined.
#define PANELS_NUMBER 1
#define PANE_WIDTH PANEL_WIDTH * PANELS_NUMBER
#define PANE_HEIGHT PANEL_HEIGHT
#define NUM_LEDS PANE_WIDTH*PANE_HEIGHT
#define ONBOARD_LED 13

177
organism/src/main.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <Arduino.h>
#include "xtensa/core-macros.h"
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include "main.h"
#include "constants.h"
#include "config.h"
#include "overlay.h"
#include "util.h"
#include "visual.h"
#include "blur.h"
#include <vector>
MatrixPanel_I2S_DMA *matrix = nullptr;
struct SlimeAgent {
int x_position;
int y_position;
float heading;
};
int NUM_AGENTS;
int AGENT_MOVE_DISTANCE;
float AGENT_SENSOR_DISTANCE;
float AGENT_MOVE_ANGLE;
float AGENT_SENSOR_ANGLE;
float attractant[PANEL_WIDTH][PANEL_HEIGHT] = {};
int iterations = 0;
std::vector<SlimeAgent> agents;
void init_attractant() {
memset(attractant, 0.0, sizeof attractant);
// draw a circle out of attractant
//for (int p = 0; p < PANEL_WIDTH; p++) {
// float angle = ((PI * 2) / PANEL_WIDTH) * p;
// int x = PANEL_WIDTH/2 + round(10 * sin(angle));
// int y = PANEL_WIDTH/2 + round(10 * cos(angle));
// attractant[x][y] = 15.0;
//}
// draw a bitmap
//for(int x = 0; x < PANEL_WIDTH; x++) {
// for(int y = 0; y < PANEL_HEIGHT; y++) {
// int pixel_num = (y * PANEL_WIDTH) + x;
// if(overlay[ pixel_num / 8 ] & (1 << (7 - (pixel_num % 8)) ) ){
// attractant[x][y] = 255.0;
// }
// }
//}
}
void init_agents() {
AGENT_MOVE_DISTANCE = 1;
AGENT_MOVE_ANGLE = 0.175;//random(0, 334) / 1000.0;
//if (random(0, 2) > 0) {
// AGENT_SENSOR_ANGLE = 0.175;
//} else {
AGENT_SENSOR_ANGLE = 0.0875;
//}
AGENT_SENSOR_DISTANCE = random(AGENT_SENSOR_DISTANCE_MIN * 1000.0, AGENT_SENSOR_DISTANCE_MAX * 1000.0) / 1000.0;
NUM_AGENTS = random(NUM_AGENTS_MIN, NUM_AGENTS_MAX);
agents.clear();
for(int a = 0; a < NUM_AGENTS; a++) {
float angle = ((PI * 2) / NUM_AGENTS) * a;
//agents[a].x_position = 31 + round(10*sin(angle));
//agents[a].y_position = 15 + round(10*cos(angle));
// agents[a].heading = ((PI * 2) / NUM_AGENTS) * );
agents.push_back({
random(SPAWN_MIN_X, SPAWN_MAX_X),
random(SPAWN_MIN_Y, SPAWN_MAX_Y),
(random(0, 100) / 100.0) * (PI*2)
});
}
}
void setup(){
Serial.begin(BAUD_RATE);
//while (!Serial) {
// ; // wait for serial port to connect. Needed for native USB
//}
pinMode(ONBOARD_LED, OUTPUT);
// redefine pins if required
HUB75_I2S_CFG::i2s_pins _pins={R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK};
HUB75_I2S_CFG mxconfig(PANEL_WIDTH, PANEL_HEIGHT, PANELS_NUMBER, _pins);
mxconfig.gpio.e = PIN_E;
mxconfig.driver = HUB75_I2S_CFG::FM6126A; // for panels using FM6126A chips
mxconfig.latch_blanking = 4;
mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M;
mxconfig.clkphase = false;
mxconfig.double_buff = true;
matrix = new MatrixPanel_I2S_DMA(mxconfig);
matrix->begin();
matrix->setBrightness8(64);
matrix->fillScreenRGB888(0, 0, 0);
init_agents();
init_attractant();
}
void loop() {
matrix ->flipDMABuffer();
delay(10);
matrix->fillScreenRGB888(15, 0, 10);
// draw background and organism
draw_bg(matrix);
draw_attractant(matrix, attractant);
for(int a = 0; a < NUM_AGENTS; a++) {
//matrix->drawPixel(agents[a].x_position, agents[a].y_position, matrix->color565(0, 0, attractant[agents[a].x_position][agents[a].y_position]));
// motor step: check if agent can move forward
// if yes, do so, if not, turn randomly
float target_x = agents[a].x_position + cos(agents[a].heading) * AGENT_MOVE_DISTANCE;
float target_y = agents[a].y_position + sin(agents[a].heading) * AGENT_MOVE_DISTANCE;
int move_x = round(target_x);
int move_y = round(target_y);
if (is_in_bounds(move_x, move_y)) {
agents[a].x_position = move_x;
agents[a].y_position = move_y;
if (attractant[move_x][move_y] < 255.0 - AGENT_DROP_AMOUNT) {
attractant[move_x][move_y] += AGENT_DROP_AMOUNT;
}
} else {
agents[a].heading = (random(0, 100)/100.0) * (PI*2);
}
}
// sample step - check the 3 positions
for(int a = 0; a < NUM_AGENTS; a++) {
//matrix->drawPixel(agents[a].x_position, agents[a].y_position, matrix->color565(0, 0, attractant[agents[a].x_position][agents[a].y_position]));
int front_x = round(agents[a].x_position + cos(agents[a].heading) * AGENT_SENSOR_DISTANCE);
int front_y = round(agents[a].y_position + sin(agents[a].heading) * AGENT_SENSOR_DISTANCE);
int left_x = round(agents[a].x_position + cos(agents[a].heading - (AGENT_SENSOR_ANGLE * (PI * 2))) * AGENT_SENSOR_DISTANCE);
int left_y = round(agents[a].y_position + sin(agents[a].heading - (AGENT_SENSOR_ANGLE * (PI * 2))) * AGENT_SENSOR_DISTANCE);
int right_x = round(agents[a].x_position + cos(agents[a].heading + (AGENT_SENSOR_ANGLE * (PI * 2))) * AGENT_SENSOR_DISTANCE);
int right_y = round(agents[a].y_position + sin(agents[a].heading + (AGENT_SENSOR_ANGLE * (PI * 2))) * AGENT_SENSOR_DISTANCE);
float front_value = attractant[front_x][front_y];
float left_value = attractant[left_x][left_y];
float right_value = attractant[right_x][right_y];
if (front_value > right_value and front_value > left_value) {
;
} else if (front_value < right_value and front_value < left_value) {
agents[a].heading += (random(0, 2) * 2 - 1) * (AGENT_MOVE_ANGLE / 100.0) * (PI*2) ;
} else if (left_value > right_value) {
agents[a].heading -= AGENT_MOVE_ANGLE * (PI * 2);
} else {
agents[a].heading += AGENT_MOVE_ANGLE * (PI * 2);
}
}
iterations++;
if (iterations % 2 == 0) { //64 && iterations % 64 == 0) { // (( == 0) {
box_blur(attractant);
//gaussian_blur();
;
}
if (iterations >= NUM_ITERATIONS) {
//cleanup();
if (cleanup(matrix, &iterations)) {
init_attractant();
init_agents();
}
}
}

10
organism/src/main.h Normal file
View File

@ -0,0 +1,10 @@
#include <FastLED.h>
#define BAUD_RATE 115200 // serial debug port baud rate
void buffclear(CRGB *buf);
uint16_t XY16( uint16_t x, uint16_t y);
void mxfill(CRGB *leds);
uint16_t colorWheel(uint8_t pos);
void drawText(int colorWheelOffset);

19
organism/src/overlay.h Normal file
View File

@ -0,0 +1,19 @@
const unsigned char overlay [] PROGMEM = {
// 'bw2, 64x32px
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00,
0x01, 0xf8, 0x00, 0x00, 0x00, 0x1c, 0x03, 0x80, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x1c, 0x03, 0x80,
0x01, 0xff, 0x00, 0x00, 0x00, 0x1c, 0x03, 0x80, 0x01, 0xc7, 0x80, 0x00, 0x00, 0x1c, 0x00, 0x00,
0x01, 0xc3, 0x80, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x01, 0xc3, 0x8f, 0xe1, 0xbe, 0x1c, 0x7b, 0x80,
0x01, 0xc3, 0x8f, 0xe1, 0xff, 0x1c, 0x73, 0x80, 0x01, 0xc3, 0x88, 0x71, 0xe7, 0x1c, 0xe3, 0x80,
0x01, 0xc3, 0x80, 0x71, 0xc3, 0x9d, 0xe3, 0x80, 0x01, 0xff, 0x00, 0x71, 0xc3, 0x9d, 0xc3, 0x80,
0x01, 0xff, 0x03, 0xf1, 0xc3, 0x9f, 0x83, 0x80, 0x01, 0xfc, 0x0f, 0xf1, 0xc3, 0x9f, 0xc3, 0x80,
0x01, 0xc0, 0x1f, 0x71, 0xc3, 0x9f, 0xc3, 0x80, 0x01, 0xc0, 0x3c, 0x71, 0xc3, 0x9d, 0xe3, 0x80,
0x01, 0xc0, 0x38, 0x71, 0xc3, 0x9c, 0xe3, 0x80, 0x01, 0xc0, 0x38, 0x71, 0xc3, 0x9c, 0xf3, 0x80,
0x01, 0xc0, 0x1c, 0xf1, 0xc3, 0x9c, 0x73, 0x80, 0x01, 0xc0, 0x1f, 0xf1, 0xc3, 0x9c, 0x7b, 0x80,
0x01, 0xc0, 0x0f, 0xb1, 0x83, 0x9c, 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

5
organism/src/util.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "constants.h"
bool is_in_bounds(int x, int y) {
return (x < PANEL_WIDTH) and (y < PANEL_HEIGHT) and (x >= 0) and (y >= 0);
}

2
organism/src/util.h Normal file
View File

@ -0,0 +1,2 @@
bool is_in_bounds(int x, int y);

54
organism/src/visual.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "constants.h"
#include "visual.h"
void draw_bg(MatrixPanel_I2S_DMA *matrix) {
int h_lines = round(PANEL_HEIGHT / 7);
int v_lines = round(PANEL_WIDTH / 7);
for(int v = 0; v <= v_lines; v++) {
matrix->drawFastVLine(v*7+2, 0, PANEL_HEIGHT, matrix->color565(41, 17, 76));
}
for(int h = 0; h <= h_lines; h++) {
matrix->drawFastHLine(0, 7*h + 1, PANEL_WIDTH, matrix->color565(41, 17, 76));
}
}
void draw_attractant(MatrixPanel_I2S_DMA *matrix, float attractant[PANEL_WIDTH][PANEL_HEIGHT]) {
for(int x = 0; x<PANEL_WIDTH; x++) {
for(int y = 0; y<PANEL_HEIGHT; y++){
if (attractant[x][y] > 15.0) {
matrix->drawPixel(x, y, matrix->color565(
int((255.0 / 255.0) * round(attractant[x][y])),
int((80.0 / 255.0) * round(attractant[x][y])),
int((83.0 / 255.0) * round(attractant[x][y]))
)
);
}
}
}
}
int cleanup_x = 0;
bool cleanup(MatrixPanel_I2S_DMA *matrix, int* iterations) {
int progress = *iterations - NUM_ITERATIONS;
matrix->drawFastVLine(cleanup_x, 0, PANEL_HEIGHT, matrix->color565(254, 242, 255));
if (cleanup_x > 0) {
matrix->fillRect(0, 0, cleanup_x, PANEL_HEIGHT, matrix->color565(16, 0, 16));
}
int h_lines = round(PANEL_HEIGHT / 7);
int v_lines = round(cleanup_x / 7);
for(int v = 0; v < cleanup_x; v++) {
if (v%7 == 2) {
matrix->drawFastVLine(v, 0, PANEL_HEIGHT, matrix->color565(41, 17, 76));
}
}
for(int h = 0; h <= h_lines; h++) {
matrix->drawFastHLine(0, 7*h + 1, cleanup_x-1, matrix->color565(41, 17, 76));
}
cleanup_x++;
if (cleanup_x > PANEL_WIDTH) {
*iterations = 0;
cleanup_x = 0;
return true;
}
return false;
}

6
organism/src/visual.h Normal file
View File

@ -0,0 +1,6 @@
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include "constants.h"
#include "config.h"
void draw_bg(MatrixPanel_I2S_DMA *matrix);
void draw_attractant(MatrixPanel_I2S_DMA *matrix, float attractant[PANEL_WIDTH][PANEL_HEIGHT]);
bool cleanup(MatrixPanel_I2S_DMA *matrix, int* iterations);