Skip to content

Instantly share code, notes, and snippets.

@jessherzog
Last active March 14, 2016 03:00
Show Gist options
  • Save jessherzog/ef2b6c7f9dafdfc47002 to your computer and use it in GitHub Desktop.
Save jessherzog/ef2b6c7f9dafdfc47002 to your computer and use it in GitHub Desktop.
/*
Draws a 3d rotating cube on the freetronics OLED screen.
Original code was found at http://forum.freetronics.com/viewtopic.php?f=37&t=5495
Thanks to Adafruit at http://www.adafruit.com for the great display and sensor libraries
*/
#include <SPI.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <Wire.h>
//MPU
const int MPU = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
//Display
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
// OLED I2C bus address
#define OLED_address 0x3c
float xx, xy, xz;
float yx, yy, yz;
float zx, zy, zz;
float fact;
int Xan, Yan;
int Xoff;
int Yoff;
int Zoff;
struct Point3d
{
int x;
int y;
int z;
};
struct Point2d
{
int x;
int y;
};
int LinestoRender; // lines to render.
int OldLinestoRender; // lines to render just in case it changes. this makes sure the old lines all get erased.
struct Line3d
{
Point3d p0;
Point3d p1;
};
struct Line2d
{
Point2d p0;
Point2d p1;
};
Line3d Lines[12]; //Number of lines to render
Line2d Render[12];
Line2d ORender[12];
// Sets the global vars for the 3d transform. Any points sent through "process" will be transformed using these figures.
// only needs to be called if Xan or Yan are changed.
void SetVars(void)
{
float Xan2, Yan2, Zan2;
float s1, s2, s3, c1, c2, c3;
Xan2 = Xan / fact; // convert degrees to radians.
Yan2 = Yan / fact;
// Zan is assumed to be zero
s1 = sin(Yan2);
s2 = sin(Xan2);
c1 = cos(Yan2);
c2 = cos(Xan2);
xx = c1;
xy = 0;
xz = -s1;
yx = (s1 * s2);
yy = c2;
yz = (c1 * s2);
zx = (s1 * c2);
zy = -s2;
zz = (c1 * c2);
}
// processes x1,y1,z1 and returns rx1,ry1 transformed by the variables set in SetVars()
// fairly heavy on floating point here.
void ProcessLine(struct Line2d *ret, struct Line3d vec)
{
float zvt1;
int xv1, yv1, zv1;
float zvt2;
int xv2, yv2, zv2;
int rx1, ry1;
int rx2, ry2;
int x1;
int y1;
int z1;
int x2;
int y2;
int z2;
int Ok;
x1 = vec.p0.x;
y1 = vec.p0.y;
z1 = vec.p0.z;
x2 = vec.p1.x;
y2 = vec.p1.y;
z2 = vec.p1.z;
Ok = 0; // defaults to not OK
xv1 = (x1 * xx) + (y1 * xy) + (z1 * xz);
yv1 = (x1 * yx) + (y1 * yy) + (z1 * yz);
zv1 = (x1 * zx) + (y1 * zy) + (z1 * zz);
zvt1 = zv1 - Zoff;
if ( zvt1 < -5) {
rx1 = 256 * (xv1 / zvt1) + Xoff;
ry1 = 256 * (yv1 / zvt1) + Yoff;
Ok = 1; // ok we are alright for point 1.
}
xv2 = (x2 * xx) + (y2 * xy) + (z2 * xz);
yv2 = (x2 * yx) + (y2 * yy) + (z2 * yz);
zv2 = (x2 * zx) + (y2 * zy) + (z2 * zz);
zvt2 = zv2 - Zoff;
if ( zvt2 < -5) {
rx2 = 256 * (xv2 / zvt2) + Xoff;
ry2 = 256 * (yv2 / zvt2) + Yoff;
} else
{
Ok = 0;
}
if (Ok == 1) {
ret->p0.x = rx1;
ret->p0.y = ry1;
ret->p1.x = rx2;
ret->p1.y = ry2;
}
// The ifs here are checks for out of bounds. needs a bit more code here to "safe" lines that will be way out of whack, so they dont get drawn and cause screen garbage.
}
void setup() {
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3c); // initialize with the I2C addr 0x3D (for the 128x64)
display.clearDisplay(); // clears the screen and buffer
Wire.begin();
fact = 180 / 3.14159265358979323846264338327950; // conversion from degrees to radians.
Xoff = 90; // positions the center of the 3d conversion space into the center of the OLED screen. This is usually screen_x_size / 2.
Yoff = 32; // screen_y_size /2
Zoff = 750; //Size of cube, larger no. = smaller cube
// line segments to draw a cube. basically p0 to p1. p1 to p2. p2 to p3 so on.
// Front Face.
Lines[0].p0.x = -50;
Lines[0].p0.y = -50;
Lines[0].p0.z = 50;
Lines[0].p1.x = 50;
Lines[0].p1.y = -50;
Lines[0].p1.z = 50;
Lines[1].p0.x = 50;
Lines[1].p0.y = -50;
Lines[1].p0.z = 50;
Lines[1].p1.x = 50;
Lines[1].p1.y = 50;
Lines[1].p1.z = 50;
Lines[2].p0.x = 50;
Lines[2].p0.y = 50;
Lines[2].p0.z = 50;
Lines[2].p1.x = -50;
Lines[2].p1.y = 50;
Lines[2].p1.z = 50;
Lines[3].p0.x = -50;
Lines[3].p0.y = 50;
Lines[3].p0.z = 50;
Lines[3].p1.x = -50;
Lines[3].p1.y = -50;
Lines[3].p1.z = 50;
//back face.
Lines[4].p0.x = -50;
Lines[4].p0.y = -50;
Lines[4].p0.z = -50;
Lines[4].p1.x = 50;
Lines[4].p1.y = -50;
Lines[4].p1.z = -50;
Lines[5].p0.x = 50;
Lines[5].p0.y = -50;
Lines[5].p0.z = -50;
Lines[5].p1.x = 50;
Lines[5].p1.y = 50;
Lines[5].p1.z = -50;
Lines[6].p0.x = 50;
Lines[6].p0.y = 50;
Lines[6].p0.z = -50;
Lines[6].p1.x = -50;
Lines[6].p1.y = 50;
Lines[6].p1.z = -50;
Lines[7].p0.x = -50;
Lines[7].p0.y = 50;
Lines[7].p0.z = -50;
Lines[7].p1.x = -50;
Lines[7].p1.y = -50;
Lines[7].p1.z = -50;
// now the 4 edge lines.
Lines[8].p0.x = -50;
Lines[8].p0.y = -50;
Lines[8].p0.z = 50;
Lines[8].p1.x = -50;
Lines[8].p1.y = -50;
Lines[8].p1.z = -50;
Lines[9].p0.x = 50;
Lines[9].p0.y = -50;
Lines[9].p0.z = 50;
Lines[9].p1.x = 50;
Lines[9].p1.y = -50;
Lines[9].p1.z = -50;
Lines[10].p0.x = -50;
Lines[10].p0.y = 50;
Lines[10].p0.z = 50;
Lines[10].p1.x = -50;
Lines[10].p1.y = 50;
Lines[10].p1.z = -50;
Lines[11].p0.x = 50;
Lines[11].p0.y = 50;
Lines[11].p0.z = 50;
Lines[11].p1.x = 50;
Lines[11].p1.y = 50;
Lines[11].p1.z = -50;
LinestoRender = 12;
OldLinestoRender = LinestoRender;
// Initialize MPU
Wire.beginTransmission(MPU);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
}
void RenderImage( void)
{
for (int i = 0; i < OldLinestoRender; i++ )
{
display.drawLine(ORender[i].p0.x, ORender[i].p0.y, ORender[i].p1.x, ORender[i].p1.y, BLACK); // erase the old lines.
}
for (int i = 0; i < LinestoRender; i++ )
{
display.drawLine(Render[i].p0.x, Render[i].p0.y, Render[i].p1.x, Render[i].p1.y, WHITE);
}
OldLinestoRender = LinestoRender;
Wire.beginTransmission(MPU);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(true);
Wire.requestFrom(MPU, 14, true); // request a total of 14 registers
AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
// text display tests
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
//Display ACC
display.print("X: ");
display.println(AcX);
display.print("Y: ");
display.println(AcY);
display.print("Z: ");
display.println(AcZ);
delay(10);
}
void loop() {
display.display();
display.clearDisplay(); // clears the screen and buffer
//For cube rotation
int xOut = 0;
int yOut = 0;
xOut = map(AcX, -17000, 17000, -50, 50);
yOut = map(AcY, -17000, 17000, -50, 50);
Xan += xOut;
Yan += yOut;
Yan = Yan % 360;
Xan = Xan % 360; // prevents overflow.
SetVars(); //sets up the global vars to do the conversion.
for (int i = 0; i < LinestoRender ; i++)
{
ORender[i] = Render[i]; // stores the old line segment so we can delete it later.
ProcessLine(&Render[i], Lines[i]); // converts the 3d line segments to 2d.
}
RenderImage();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment