Skip to content

Instantly share code, notes, and snippets.

@tobozo
Created November 2, 2017 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobozo/aa89a42f4f28f72c02c8051314165ef4 to your computer and use it in GitHub Desktop.
Save tobozo/aa89a42f4f28f72c02c8051314165ef4 to your computer and use it in GitHub Desktop.
/*
* 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