// simple project with rotating 3D cube using Arduino UNO and Transparent 128x64 OLED Display,
// created by upir, 2022
// youtube channel: https://www.youtube.com/upir_upir
// full tutoral is here: https://youtu.be/kBAcaA7NAlA
// Turbo pressure gauge tutorial: https://youtu.be/JXmw1xOlBdk
// Transparent OLED tutorial: https://youtu.be/hIFDcksXgBk
// Knob + OLED tutorial: https://youtu.be/NPfaLKKsf_Q
// useful links:
// u8g documentation: https://github.com/olikraus/u8glib/wiki/userreference
// Wokwi starting project: https://wokwi.com/arduino/projects/300867986768527882
// Arduino UNO: http://store.arduino.cc/products/arduino-uno-rev3
// Arduino UNO MINI: https://store.arduino.cc/products/uno-mini-le
// Multidimensional arrays: https://www.tutorialspoint.com/arduino/arduino_multi_dimensional_arrays.htm
// 2D Rotation: https://en.wikipedia.org/wiki/Rotation_(mathematics)
// Normal OLED Display: https://www.aliexpress.com/item/4001051535838.html
// Transparent OLED Display: https://a.aliexpress.com/_mKGmhKg
// Big OLED Display: https://www.aliexpress.com/item/1005003091769556.html
// Arduino breadboard prototyping shield: https://www.adafruit.com/product/2077
#include "U8glib.h" // u8g library, note there is a newer version u8g2, please use the older one
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
const uint8_t upir_logo[] U8G_PROGMEM = { // another simple way how to define pictures for u8g library
B00010101, B11010111, // ░░░█░█░███░█░███
B00010101, B01000101, // ░░░█░█░█░█░░░█░█
B00010101, B10010110, // ░░░█░█░██░░█░██░
B00011001, B00010101 // ░░░██░░█░░░█░█░█
};
// uncomment the correct connection - fast I2C, slow I2C, SPI
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); // Fast I2C / TWI
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK); // slow I2C / TWI -- I had to use "slow I2C" in my case
//U8GLIB_SSD1306_128X64 u8g(13, 11, 8, 9, 10); // SPI connection - SCL = 13, SDA = 11, RES = 10, DC = 9, CS = 8
typedef int cube2D_t[8][2]; ; // eight 2D points for the cube, values will be calculated in the code
typedef int cube3D_t[8][3]; ; // eight 3D points for the cube
cube2D_t points2D;
cube3D_t orig_points = { // eight 3D points - set values for 3D cube
{-1,-1, 1},
{1,-1,1},
{1,1,1},
{-1,1,1},
{-1,-1,-1},
{1,-1,-1},
{1,1,-1},
{-1,1,-1}
};
float angle_deg = 60.0; // rotation around the Y axis
float z_offset = -3; // offset on Z axis
float cube_size = 70.0; // cube size (multiplier)
float time_frame; // ever increasing time value
void setup() {
u8g.setColorIndex(1); // set color to white
pinMode(A0, INPUT);
}
void loop() {
time_frame++; // increase the time frame value by 1
//cube_size = 50 + sin(time_frame * 0.2)*20; // oscilate cube size between values 30 - 70
z_offset = (float)map(analogRead(A0),0,1023,-10.0,10.0);
//z_offset = -2.0; //
//cube_size = 18.0; // uncomment those two lines for a "wide angle camera" -- bigger perspective distort
// increase the angle by 5° increments
if (angle_deg < 90-1) {
angle_deg = angle_deg + 1;
} else {
angle_deg = 0;
}
// calculate cube
calculate_cube(cube_size, z_offset, angle_deg, orig_points, points2D);
u8g.firstPage();
do {
// draw cube
draw_cube(points2D);
// draw upir logo
u8g.drawBitmapP(112, 0, 2, 4, upir_logo);
} while ( u8g.nextPage() ); // u8g library specific, has to be there
}
// -----------------------------------------------------------------------------------
void calculate_cube(float csize, float offset, float angle, cube3D_t o, cube2D_t p) {
float rot3d_p [8][3]; // eight 3D points - rotated around Y axis
// calculate the points
for (int i=0; i<8; i++) {
// rotate 3d points around the Y axis (rotating X and Z positions)
rot3d_p [i][0] = o[i][0]*cos(radians(angle))-o[i][2]*sin(radians(angle));
rot3d_p [i][1] = o[i][1];
rot3d_p [i][2] = o[i][0]*sin(radians(angle))+o[i][2]*cos(radians(angle)) + offset;
// project 3d points into 2d space with perspective divide -- 2D x = x/z, 2D y = y/z
p[i][0] = round(SCREEN_WIDTH/2 + rot3d_p[i][0]/rot3d_p[i][2]*csize);
p[i][1] = round(SCREEN_HEIGHT/2 + rot3d_p[i][1]/rot3d_p[i][2]*csize);
}
}
//-------------------------------------------------------------------------------------
void draw_cube (cube2D_t p) {
// connect the lines between the individual points
u8g.drawLine(p[0][0],p[0][1],p[1][0],p[1][1]); //connect points 0-1
u8g.drawLine(p[1][0],p[1][1],p[2][0],p[2][1]); //connect points 1-2
u8g.drawLine(p[2][0],p[2][1],p[3][0],p[3][1]); //connect points 2-3
u8g.drawLine(p[3][0],p[3][1],p[0][0],p[0][1]); //connect points 3-0
u8g.drawLine(p[4][0],p[4][1],p[5][0],p[5][1]); //connect points 4-5
u8g.drawLine(p[5][0],p[5][1],p[6][0],p[6][1]); //connect points 5-6
u8g.drawLine(p[6][0],p[6][1],p[7][0],p[7][1]); //connect points 6-7
u8g.drawLine(p[7][0],p[7][1],p[4][0],p[4][1]); //connect points 7-4
u8g.drawLine(p[0][0],p[0][1],p[4][0],p[4][1]); //connect points 0-4
u8g.drawLine(p[1][0],p[1][1],p[5][0],p[5][1]); //connect points 1-5
u8g.drawLine(p[2][0],p[2][1],p[6][0],p[6][1]); //connect points 2-6
u8g.drawLine(p[3][0],p[3][1],p[7][0],p[7][1]); //connect points 3-7
}