Created
November 2, 2017 16:06
-
-
Save tobozo/aa89a42f4f28f72c02c8051314165ef4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* SSD1306 Multiple monitor Demo | |
* Copyleft (c+) toboz Nov 2017 | |
* | |
*/ | |
#include <SPI.h> | |
#include <Wire.h> | |
#include <U8g2lib.h> | |
#define NUMSPRITES 16 | |
#define NUMSCREENS 8 | |
#define XPOS 0 | |
#define YPOS 1 | |
#define DELTAY 2 | |
#define DIR 3 | |
#define SPRITE 4 | |
#define TCAADDR 0x70 // I2C Multiplexer ADDR | |
#define LOGO16_GLCD_HEIGHT 16 | |
#define LOGO16_GLCD_WIDTH 16 | |
#ifndef _swap_int16_t | |
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } | |
#endif | |
int16_t icons[NUMSPRITES][5]; | |
uint16_t hspritepos = 0; | |
uint16_t maxhpos = 0; | |
uint16_t maxvpos = 0; | |
/* adafruit logo */ | |
const uint8_t logo16_glcd_bmp[] = { | |
B11000000, B00000000, | |
B11000000, B00000001, | |
B11000000, B00000001, | |
B11100000, B00000011, | |
B11100000, B11110011, | |
B11111000, B11111110, | |
B11111111, B01111110, | |
B10011111, B00110011, | |
B11111100, B00011111, | |
B01110000, B00001101, | |
B10100000, B00011011, | |
B11100000, B00111111, | |
B11110000, B00111111, | |
B11110000, B01111100, | |
B01110000, B01110000, | |
B00110000, B00000000, | |
}; | |
/* smiley */ | |
const uint8_t logo16_glcd2_bmp[] = { | |
B11100000, B00000111, | |
B00011000, B00011000, | |
B00000100, B00100000, | |
B00000010, B01000000, | |
B01100010, B01000110, | |
B01100001, B10000110, | |
B00000001, B10000000, | |
B00000001, B10000000, | |
B00000001, B10000000, | |
B00001001, B10010000, | |
B00010001, B10001000, | |
B11100010, B01000111, | |
B00000010, B01000000, | |
B00000100, B00100000, | |
B00011000, B00011000, | |
B11100000, B00000111, | |
}; | |
/* smiley bored */ | |
const uint8_t logo_smiley_bored[] = { | |
B11100000, B00000111, | |
B00011000, B00011000, | |
B00000100, B00100000, | |
B00000010, B01000000, | |
B00000010, B01000000, | |
B01111001, B10011110, | |
B00000001, B10000000, | |
B00000001, B10000000, | |
B00000001, B10000000, | |
B00000001, B10000000, | |
B00000001, B10000000, | |
B11100010, B01000111, | |
B00000010, B01000000, | |
B00000100, B00100000, | |
B00011000, B00011000, | |
B11100000, B00000111, | |
}; | |
/* -pumpkin */ | |
const uint8_t hzv_logo[] = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x03, 0x40, 0x00, 0x00, 0x02, | |
0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x78, 0x00, 0x00, 0x1e, | |
0x08, 0x00, 0xfc, 0x11, 0x08, 0x00, 0xfc, 0x11, 0x08, 0x00, 0xfc, 0x11, | |
0x88, 0x3f, 0xfc, 0x11, 0x88, 0x3f, 0xfc, 0x11, 0x88, 0x3f, 0xfc, 0x11, | |
0x88, 0x3f, 0xfc, 0x11, 0x08, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x10, | |
0x08, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x10, 0x78, 0x00, 0x00, 0x1e, | |
0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x3c, 0x3c, 0x02, | |
0x40, 0x24, 0x24, 0x02, 0x40, 0x24, 0x24, 0x02, 0x40, 0x24, 0x24, 0x02, | |
0xc0, 0xe7, 0xe7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
const uint8_t halloween_bits[] = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, | |
0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, | |
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xff, 0xff, 0x01, | |
0xc0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x1f, | |
0xf8, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xcf, 0x3f, | |
0xfc, 0xff, 0xc7, 0x7f, 0xfc, 0xc7, 0xc3, 0x73, 0xfc, 0x87, 0xc1, 0x73, | |
0xfc, 0x8f, 0xf1, 0x71, 0xfc, 0xff, 0xff, 0x70, 0xfc, 0xff, 0x7f, 0x60, | |
0x3c, 0xff, 0x3f, 0x60, 0x3c, 0xfe, 0x0f, 0x60, 0x3c, 0x30, 0x00, 0x70, | |
0x3c, 0x00, 0x00, 0x30, 0x78, 0xf8, 0xff, 0x39, 0x70, 0xfe, 0xff, 0x1f, | |
0xf0, 0xff, 0xff, 0x1f, 0xe0, 0xff, 0xff, 0x0f, 0x80, 0xff, 0xff, 0x03, | |
0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
struct logo { | |
const uint8_t *binary; | |
uint8_t width; | |
uint8_t height; | |
}; | |
logo logoArray[5] = { | |
{ logo16_glcd_bmp, 16, 16 }, | |
{ logo16_glcd2_bmp, 16, 16 }, | |
{ logo_smiley_bored, 16, 16 }, | |
{ hzv_logo, 32, 32 }, | |
{ halloween_bits, 32, 32 } | |
}; | |
// all display types | |
U8G2_SSD1306_64X48_ER_F_HW_I2C display64x48(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); | |
U8G2_SSD1306_128X64_NONAME_F_HW_I2C display128x64(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, 4, 5); | |
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C display128x32(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); | |
struct display { | |
U8G2 *display; | |
const u8g2_cb_t *rotation; | |
uint16_t position; | |
uint8_t width; | |
uint8_t height; | |
}; | |
/* display array, order matters */ | |
display OLEDS[NUMSCREENS] = { | |
{ &display128x32, U8G2_R3 }, | |
{ &display64x48, U8G2_R0 }, | |
{ &display64x48, U8G2_R0 }, | |
{ &display64x48, U8G2_R0 }, | |
{ &display128x64, U8G2_R0 }, | |
{ &display128x32, U8G2_R3 }, | |
{ &display128x32, U8G2_R3 }, | |
{ &display128x32, U8G2_R3 } | |
}; | |
/* I2C multiplexer */ | |
void tcaselect(uint8_t i) { | |
if (i > 7) return; | |
Wire.beginTransmission(TCAADDR); | |
Wire.write(1 << i); | |
Wire.endTransmission(); | |
} | |
#define MATRIX_SIZE 256 | |
bool matrix[MATRIX_SIZE][MATRIX_SIZE]; | |
void setMatrix(uint8_t depth) { | |
float matrixwidth = MATRIX_SIZE; | |
float matrixheight = MATRIX_SIZE; | |
//Serial.println("Setting Matrix"); | |
for(uint8_t y=0;y<matrixheight;y++) { | |
bool ystate = y%depth == 0; | |
for(uint8_t x=0;x<matrixwidth;x++) { | |
bool xstate = x%depth == 0; | |
matrix[x][y] = ystate ? xstate : !xstate; | |
//Serial.print(xstate); | |
} | |
//Serial.println(); | |
} | |
//Serial.println(); | |
} | |
int txlast; | |
int tylast; | |
void matrixMoveTo(int x, int y) { | |
txlast = x; | |
tylast = y; | |
} | |
// draw helpers, just a copy of drawLine, calling mySetPixel() instead of setPixel() | |
// to handle depth, mainly here to compensate the lack of fill/stroke | |
void drawMatrixLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { | |
int16_t steep = abs(y1 - y0) > abs(x1 - x0); | |
if (steep) { | |
_swap_int16_t(x0, y0); | |
_swap_int16_t(x1, y1); | |
} | |
if (x0 > x1) { | |
_swap_int16_t(x0, x1); | |
_swap_int16_t(y0, y1); | |
} | |
int16_t dx, dy; | |
dx = x1 - x0; | |
dy = abs(y1 - y0); | |
int16_t err = dx / 2; | |
int16_t ystep; | |
if (y0 < y1) { | |
ystep = 1; | |
} else { | |
ystep = -1; | |
} | |
for (; x0<=x1; x0++) { | |
if (steep) { | |
matrix[y0][x0] = true; | |
//Serial.println(String(x0) + ":" + String(y0)); | |
} else { | |
matrix[x0][y0] = true; | |
//Serial.println(String(y0) + ":" + String(x0)); | |
} | |
err -= dy; | |
if (err < 0) { | |
y0 += ystep; | |
err += dx; | |
} | |
} | |
} | |
void matrixLineTo(int x, int y) { | |
if(x>=MATRIX_SIZE) return; | |
if(y>=MATRIX_SIZE) return; | |
if(x<0) return; | |
if(y<0) return; | |
drawMatrixLine(txlast, tylast, x, y); | |
txlast = x; | |
tylast = y; | |
} | |
float maximum(float *in, int size) { | |
float maxout = -(2^16); | |
for(int i=0;i<size;i++) { | |
if(in[i]>maxout) maxout = in[i]; | |
} | |
return maxout; | |
} | |
float minimum(float *in, int size) { | |
float minout = 2^16; | |
for(int i=0;i<size;i++) { | |
if(in[i]>minout) minout = in[i]; | |
} | |
return minout; | |
} | |
//point in polygon algorithm for filling polyons | |
void fill_polygon(float *vx, float *vy, float polyCorners){ | |
int nodes, nodeX[1000], pixelX, pixelY, i, j, swap,top,bot,right,left; | |
//polyCorners = vx->used; | |
if(polyCorners<=2){ | |
return; | |
} | |
bot = maximum(vy, polyCorners); | |
top = minimum(vy, polyCorners); | |
right = maximum(vx, polyCorners); | |
left = minimum(vx, polyCorners); | |
/*printf("Top %d - Bot %d - Left %d - Right %d \n",top,bot,left,right); | |
wait(40);*/ | |
for (pixelY=top; pixelY<bot; pixelY++) { | |
// building list of nodes | |
nodes=0; j=polyCorners-1; | |
for (i=0; i<polyCorners; i++) { | |
if (vy[i]< pixelY && vy[j]>=pixelY | |
|| vy[j]< pixelY && vy[i]>= pixelY) { | |
nodeX[nodes++]=(int) (vx[i]+(pixelY-vy[i])/(double)(vy[j]-vy[i])*(vx[j]-vx[i])); | |
} | |
j=i; | |
} | |
// bubble sort | |
i=0; | |
while (i<nodes-1) { | |
if (nodeX[i]>nodeX[i+1]) { | |
swap=nodeX[i]; nodeX[i]=nodeX[i+1]; nodeX[i+1]=swap; if (i) i--; | |
} | |
else { | |
i++; | |
} | |
} | |
// filling pixels between nodes | |
for (i=0; i<nodes; i+=2) { | |
if (nodeX[i ]>=right) break; | |
if (nodeX[i+1]> left ) { | |
if (nodeX[i ]< left ) nodeX[i ]=left ; | |
if (nodeX[i+1]> right) nodeX[i+1]=right; | |
for (pixelX=nodeX[i]; pixelX<nodeX[i+1]; pixelX++){ | |
//plot_pixel(pixelX,pixelY,current_color); | |
matrix[pixelX][pixelY] = true; | |
} | |
} | |
} | |
} | |
} | |
//draw | |
/* | |
void drawPath(int tx[], int ty[]) { | |
matrixMoveTo(tx[0], ty[0]); | |
for (int p = 0; p < num; p++) { | |
matrixLineTo(tx[p], ty[p], tz[p]); | |
} | |
}*/ | |
float hexHeight, | |
hexRadius, | |
hexRectangleHeight, | |
hexRectangleWidth, | |
hexagonAngle = 0.523598776, // 30 degrees in radians | |
sideLength = 8 | |
//boardWidth = 8, | |
//boardHeight = 8 | |
; | |
void matrixDrawHexagon(float x, float y, bool fill) { | |
float xpoly[6]; | |
float ypoly[6]; | |
xpoly[0] = x + hexRadius; | |
ypoly[0] = y; | |
xpoly[1] = x + hexRectangleWidth; | |
ypoly[1] = y + hexHeight; | |
xpoly[2] = x + hexRectangleWidth; | |
ypoly[2] = y + hexHeight + sideLength; | |
xpoly[3] = x + hexRadius; | |
ypoly[3] = y + hexRectangleHeight; | |
xpoly[4] = x; | |
ypoly[4] = y + sideLength + hexHeight; | |
xpoly[5] = x; | |
ypoly[5] = y + hexHeight; | |
matrixMoveTo(xpoly[0], ypoly[0]); | |
matrixLineTo(xpoly[1], ypoly[1]); | |
matrixLineTo(xpoly[2], ypoly[2]); | |
matrixLineTo(xpoly[3], ypoly[3]); | |
matrixLineTo(xpoly[4], ypoly[4]); | |
matrixLineTo(xpoly[5], ypoly[5]); | |
if(fill) { | |
fill_polygon(xpoly, ypoly, 6); | |
} | |
/* | |
matrixMoveTo(x + hexRadius, y); | |
matrixLineTo(x + hexRectangleWidth, y + hexHeight); | |
matrixLineTo(x + hexRectangleWidth, y + hexHeight + sideLength); | |
matrixLineTo(x + hexRadius, y + hexRectangleHeight); | |
matrixLineTo(x, y + sideLength + hexHeight); | |
matrixLineTo(x, y + hexHeight); | |
*/ | |
/* | |
canvasContext.matrixMoveTo(x + hexRadius, y); | |
canvasContext.matrixLineTo(x + hexRectangleWidth, y + hexHeight); | |
canvasContext.matrixLineTo(x + hexRectangleWidth, y + hexHeight + sideLength); | |
canvasContext.matrixLineTo(x + hexRadius, y + hexRectangleHeight); | |
canvasContext.matrixLineTo(x, y + sideLength + hexHeight); | |
canvasContext.matrixLineTo(x, y + hexHeight); | |
*/ | |
} | |
void hexTileMatrix(uint16_t width, uint16_t height) { | |
hexHeight = sin(hexagonAngle) * sideLength; | |
hexRadius = cos(hexagonAngle) * sideLength; | |
hexRectangleHeight = sideLength + 2 * hexHeight; | |
hexRectangleWidth = 2 * hexRadius; | |
//drawBoard(); | |
float i, | |
j; | |
for(i = 0; i < width; ++i) { | |
for(j = 0; j < height; ++j) { | |
matrixDrawHexagon( | |
i * hexRectangleWidth + ((((int)j) % 2) * hexRadius), | |
j * (sideLength + hexHeight), | |
((int)j)%2==0 | |
); | |
} | |
} | |
} | |
void projectMatrix(uint8_t dnum, float zoom, float angle, float width, float height, float offsetx, float offsety) { | |
float ratio = 1/zoom; | |
float virtualwidth = (float)MATRIX_SIZE*zoom; | |
float virtualheight = (float)MATRIX_SIZE*zoom; | |
float virtualcenterx = virtualwidth/2; | |
float virtualcentery = virtualheight/2; | |
float vectorx = cos(angle); | |
float vectory = sin(angle); | |
float marginx = (virtualwidth - width) / 2;// + offsetx; | |
float marginy = (virtualheight - height) / 2;// + offsety; | |
tcaselect(dnum); | |
OLEDS[dnum].display->clearBuffer(); | |
for( uint8_t i=0; i<height; i++ ) { | |
float x1 = marginx; | |
float y1 = marginy+i; | |
float x2 = virtualwidth-marginx; | |
float y2 = marginy+i; | |
for( uint8_t j=0; j<width; j++ ) { | |
float x = round( x1+(x2-x1)*j/width ); | |
float y = round( y1+(y2-y1)*j/width ); | |
float nx = (vectorx * (x - virtualcenterx)) + (vectory * (y - virtualcentery)) + virtualcenterx + offsetx; | |
float ny = (vectorx * (y - virtualcentery)) - (vectory * (x - virtualcenterx)) + virtualcentery + offsety; | |
uint8_t mx = nx*ratio; | |
uint8_t my = ny*ratio; | |
//Serial.println(String(x) + " " + String(y) + " / " + String() + " " + String(ny*ratio)); | |
if(matrix[mx][my]) { | |
OLEDS[dnum].display->drawPixel(j, i); | |
} | |
} | |
} | |
OLEDS[dnum].display->sendBuffer(); | |
} | |
void drawDisplay(uint8_t dnum) { | |
display curDisplay = OLEDS[dnum]; | |
U8G2 *display = curDisplay.display; | |
display->clearBuffer(); | |
float w = display->getDisplayWidth(); | |
float h = display->getDisplayHeight(); | |
float wratio = 128 / w; | |
float hratio = 64 / h; | |
for (uint8_t f=0; f<NUMSPRITES; f++) { | |
logo curLogo = logoArray[icons[f][SPRITE]]; | |
if( icons[f][XPOS]+(curLogo.width/2) > curDisplay.position | |
&& icons[f][XPOS]-(curLogo.width/2) < curDisplay.position + curDisplay.width ) { | |
display->drawXBM(icons[f][XPOS] - curDisplay.position - (curLogo.width/2), icons[f][YPOS], curLogo.width, curLogo.height, curLogo.binary); | |
} | |
} | |
display->sendBuffer(); | |
} | |
void setup() { | |
Serial.begin(115200); | |
while (!Serial); // Leonardo: wait for serial monitor | |
//Wire.begin(5, 4); | |
uint16_t pos = 0; | |
//for(uint8_t i=0;i<NUMSCREENS;i++) { | |
int i = 4; | |
//tcaselect(i); | |
OLEDS[i].display->begin(); | |
OLEDS[i].display->setDisplayRotation( OLEDS[i].rotation ); | |
OLEDS[i].width = OLEDS[i].display->getDisplayWidth(); | |
OLEDS[i].height = OLEDS[i].display->getDisplayHeight(); | |
OLEDS[i].position = pos; | |
pos += OLEDS[i].width; | |
if(OLEDS[i].height > maxvpos) maxvpos = OLEDS[i].height; | |
Serial.print(i); | |
Serial.print("\t"); | |
Serial.print(OLEDS[i].width); | |
Serial.print("\t"); | |
Serial.print(OLEDS[i].height); | |
Serial.print("\t"); | |
Serial.print(OLEDS[i].position); | |
Serial.println(); | |
//} | |
maxhpos = pos; | |
Serial.println("Display Setup Complete"); | |
//setMatrix(2); | |
hexTileMatrix(16, 16); | |
} | |
void loop() { | |
for(float i=-PI*12;i<PI*12;i=i+0.2) { | |
//setMatrix(cos(i)*2+16); | |
//projectMatrix(/*displaynum*/4, /*zoom*/32 + cos(i)*16, /*angle*/i, /*width*/128, /*height*/64, /*offsetx*/cos(i/12)*160, /*offsety*/sin(i/12)*160); | |
projectMatrix(/*displaynum*/4, /*zoom*/2 + cos(i), /*angle*/i, /*width*/128, /*height*/64, /*offsetx*/0, /*offsety*/0); | |
} | |
return; | |
// initialize sprites | |
for (uint8_t f=0; f< NUMSPRITES; f++) { | |
icons[f][XPOS] = random(maxhpos) - (LOGO16_GLCD_WIDTH/2); | |
icons[f][YPOS] = random(maxvpos) - (LOGO16_GLCD_HEIGHT/2); | |
icons[f][DELTAY] = random(5) + 1; | |
icons[f][DIR] = random(8) - 4; | |
icons[f][SPRITE] = random(5); | |
} | |
while (1) { | |
// draw displays | |
for (uint8_t f=0; f< NUMSCREENS; f++) { | |
tcaselect( f ); | |
//u8g2 = OLEDS[f].display; | |
drawDisplay(f); | |
} | |
// move sprites | |
for (uint8_t f=0; f< NUMSPRITES; f++) { | |
icons[f][YPOS] += icons[f][DELTAY]; | |
icons[f][XPOS] += icons[f][DIR]; | |
if(icons[f][XPOS]>maxhpos) { | |
icons[f][XPOS] = 0; | |
} | |
if(icons[f][XPOS]<0) { | |
icons[f][XPOS] = maxhpos; | |
} | |
// if its gone, reinit | |
if (icons[f][YPOS] > maxvpos) { | |
icons[f][XPOS] = random(maxhpos) - (LOGO16_GLCD_WIDTH/2); | |
icons[f][YPOS] = -15; | |
icons[f][DELTAY] = random(5) + 1; | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment