//
// Generic Control of MAX7219
// no additional library required
// yet - may not work with actual hardware
// https://www.analog.com/media/en/technical-documentation/data-sheets/max7219-max7221.pdf
// https://github.com/wayoda/LedControl/blob/master/src/LedControl.cpp
// 2023-11-11
//
// arbitrary selected pins
#define PIN_DIN 2
#define PIN_CS 3
#define PIN_CLK 4
// MAX7219 driver via software SPI
void maxBegin() {
digitalWrite(PIN_CS, LOW);
}
void maxEnd() {
digitalWrite(PIN_CS, HIGH);
}
void maxWrite16(uint16_t data) {
for (int i = 16; i; i--, data <<= 1) {
maxWrite1(data & 0x8000 ? HIGH : LOW);
}
}
// yes, shiftOut
void maxWrite8(uint8_t data) {
for (int i = 8; i; i--, data <<= 1) {
maxWrite1(data & 0x80 ? HIGH : LOW);
}
}
void maxWrite1(uint8_t data) {
digitalWrite(PIN_DIN, data & 1 ? HIGH : LOW);
digitalWrite(PIN_CLK, HIGH);
digitalWrite(PIN_CLK, LOW);
}
#define NUM_SEG 1
#define SX (8*NUM_SEG)
#define SY 8
// convert frame buffer (linear addressing) to LED HIGH/LOW state
void maxDisplay(byte* display) {
for (int y = 0; y < SY; y++) {
maxBegin();
for (int seg = 0; seg < NUM_SEG; seg++) {
maxWrite8(y + 1);
for (int x = 8; x; x--) {
maxWrite1(display[y * SX + x - 1 + seg * 8]);
}
}
maxEnd();
}
}
// global frame buffer
byte frame[SX * SY];
// per frame processing; takes current time as parameter
void (*process)(int) = nullptr;
#include "process_test1.h"
#include "process_life.h"
#include "process_fbird.h"
const char* alpha_fnt = "\
xx xxx xx xxx xxxx xxxx xxx x x xxx xxxx x x x x x x x xx xxx xx xxx xxx xxx x x x x x x x x x x xxxx \
x x x x x x x x x x x x x x x x x x xx xx xx x x x x x x x x x x x x x x x x x x x x x x \
xxxx xxx x x x xxx xxx x xx xxxx x x xxx x x x x x xx x x xxx x x xxx xx x x x x x x x x xx x x xx \
x x x x x x x x x x x x x x x x x x x x x x x x x x x xx x x x x x x x x x x x x x x x \
x x xxx xx xxx xxxx x xxx x x xxx xxx x x xxxx x x x x xx x xxx x x xxx x xx x x x x x xx xxxx \
";
int get_width(char c){
switch(c){
case 'I':
case 'T':
return 3;
case 'M':
case 'W':
return 5;
default:
return 4;
}
}
char *alpha_chr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void drawText(int ox, int oy, char *text){
int cur=0;
for(char *c=text;*c;c++,cur+=1){
int o = 0;
for(char *a=alpha_chr;*a;a++){
if (*a==*c){
break;
}
o+=get_width(*a)+1;
}
int w = get_width(*c);
//Serial1.print(*c); Serial1.print(' '); Serial1.print(w); Serial1.println("");
for(int x=0;x<w;x++)
for(int y=0;y<5;y++){
frame[ox+x+(oy+y)*SX+cur]=alpha_fnt[o+x+y*130]=='x';
}
cur+=w;
}
}
void process_textdemo(int time){
for(int o=0;o<SX*SY;o++){
frame[o]=0;
}
float k = time*0.005;
char *text = "BROWN";
char txt[2] = {0,0};
int o = 0;
for(char*c=text;*c;c++){
int i=c-text;
float fx = abs(sin(k-i*0.2));
int x= sqrt(fx)*7;
int y= 1;//cos(k+0.5)*2+2;
txt[0]=*c;
drawText(x+o,y,txt);
o+=get_width(*c)+1;
}
maxDisplay(frame);
}
void fbClear(byte* frame){
for(int o=0;o<SX*SY;o++){
frame[o]=0;
}
}
int fruit_x = random()%SX;
int fruit_y = random()%SY;
int snake[100];
int snake_respawn = 1;
int sx=4, sy=4;
int dx=1, dy=0;
int slen=2;
bool hit_snake(int p, int* snake, int slen){
for(int i=0;i<slen;i++){
if (snake[i]==p){
return true;
}
}
return false;
}
void process_snake(int time){
fbClear(frame);
frame[fruit_x+fruit_y*SX] = (time%200)>100;
if (snake_respawn){
slen=2;
for(int i=0;i<100;i++){ snake[i] = sx+sy*SX;}
snake_respawn = 0;
}
if (last_time<0){
last_time = time;
}
int dt = time-last_time;
if(dt>100){
last_time = time;
for(int i=99;i;i--){
snake[i]=snake[i-1];
}
sx = (sx+dx+SX)%SX;
sy = (sy+dy+SY)%SY;
if (hit_snake(sx+sy*SX,snake,slen)){
snake_respawn = 1;
return;
}
snake[0] = sx+sy*SX;
// got fruit!
if (sx==fruit_x && sy==fruit_y){
Serial1.println("got fruit!");
slen+=1;
while(1){
int fx = random()%SX;
int fy = random()%SY;
if (fx!=fruit_x && fy!=fruit_y && !hit_snake(fx+fy*SX,snake,slen) && fx!=sx && fy!=sy){
fruit_x = fx;
fruit_y = fy;
break;
}
}
}
// good sx, bad sy
if (sx==fruit_x && sy!=fruit_y){
//dy = sy-fruit_y>0?1:-1;
if (abs(fruit_y-sy)>abs(-fruit_y-sy)){
dy = -fruit_y-sy>0 ? 1 : -1;
}else{
dy = fruit_y-sy>0 ? 1 : -1;
}
dx = 0;
}
// good sy, bad sx
if (sy==fruit_y && sx!=fruit_x){
//dx = sx-fruit_x>0?1:-1;
if (abs(fruit_x-sx)>abs(-fruit_x-sx)){
dx = -fruit_x-sx>0 ? 1 : -1;
}else{
dx = fruit_x-sx>0 ? 1 : -1;
}
dy = 0;
}
}
for(int i=0;i<slen;i++){
frame[snake[i]] = 1;
}
maxDisplay(frame);
}
float dino_y = 0;
float dino_v = 0;
// frames, sx, sy
const byte spr_dino[] = {3,3,2,
0,1,0,
0,0,1,
0,1,0,
0,1,0,
0,1,0,
1,0,0,
};
const byte spr_cact[] = {1,3,5,
0,1,0,
1,1,1,
0,1,0,
1,1,1,
0,1,0,
};
const byte spr_cact2[] = {1,3,5,
0,1,0,
1,1,0,
0,1,1,
0,1,0,
0,1,0,
};
const byte spr_sun[] = {3,6,3,
0,0,0,1,1,1,
0,0,0,0,1,1,
0,0,0,0,1,0,
0,0,0,0,1,1,
0,0,0,1,1,1,
0,0,0,0,0,1,
0,0,0,0,1,1,
0,0,0,0,1,1,
0,0,0,1,0,0,
};
void pix(int x, int y, int c){
if (x>=0 && x<SX && y>=0 && y<SY){
frame[x+y*SX]=c;
}
}
void fbSprite(const byte *spr, int idx, int ox, int oy){
int sx = spr[1];
int sy = spr[2];
spr = spr+idx*sx*sy+3;
for(int x=0;x<sx;x++){
for(int y=0;y<sy;y++){
if (spr[x+y*sx]){
pix(ox+x,oy+y,spr[x+y*sx]?1:0);
}
}
}
}
void process_dino(int now){
fbClear(frame);
{
int idx = (now/100)%spr_sun[0];
fbSprite(spr_sun,idx,2,0);
}
{
int x = (now/1000)%8;
fbSprite(spr_cact,0,8-x,4);
}
{
int idx = (now/1000)%spr_dino[0];
fbSprite(spr_dino,idx,0,6);
}
maxDisplay(frame);
}
////////////////////////////////////////////////////////////////////
void setup() {
Serial1.begin(9600);
// setup output pints
pinMode(PIN_DIN, OUTPUT);
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
digitalWrite(PIN_CS, HIGH);
pinMode(PIN_BUTTON, INPUT_PULLUP);
//process = process_test1;
//process = process_life;
//process = process_textdemo;
//process = process_flappy;
//process = process_snake;
process = process_dino;
process(0);
}
////////////////////////////////////////////////////////////////////
void loop() {
delay(33); // 30fps ;)
//delay(100); // 10fps :/
//delay(16); // 60fps :D
int ts_now = millis();
if (process){
process(ts_now);
}
}