#include <Wire.h>
// Pin definitions
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
// State variables
int lastStateCLK;
int rotationAngle = 0;
void setup() {
// Initialize serial communication (for debugging)
Serial.begin(9600);
// Initialize I2C communication for OLED
Wire.begin();
// Set up the pins for the rotary encoder
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
// Read the initial state of the encoder
lastStateCLK = digitalRead(ENCODER_CLK);
// Initialize OLED
initializeOLED();
}
void loop() {
// Read the current state of the rotary encoder
int currentStateCLK = digitalRead(ENCODER_CLK);
// If the state has changed, determine rotation direction
if (currentStateCLK != lastStateCLK) {
if (digitalRead(ENCODER_DT) != currentStateCLK) {
rotationAngle += 10; // Rotate clockwise
} else {
rotationAngle -= 10; // Rotate counterclockwise
}
// Constrain the rotation angle to 0-359 degrees
if (rotationAngle >= 360) rotationAngle -= 360;
if (rotationAngle < 0) rotationAngle += 360;
// Display the arrow on the OLED
drawArrow(rotationAngle);
}
// Save the last state of the CLK pin
lastStateCLK = currentStateCLK;
}
// Function to initialize the OLED display
void initializeOLED() {
Wire.beginTransmission(0x3C); // The I2C address of the SSD1306
Wire.write(0x00); // Command mode
Wire.write(0xAE); // Display OFF
Wire.write(0x20); // Set memory addressing mode
Wire.write(0x00); // Horizontal addressing mode
Wire.write(0xB0); // Set page start address for page addressing mode
Wire.write(0xC8); // COM scan direction
Wire.write(0x00); // Set lower column address
Wire.write(0x10); // Set higher column address
Wire.write(0x40); // Set display start line
Wire.write(0x81); // Set contrast control
Wire.write(0x7F);
Wire.write(0xA1); // Segment remap
Wire.write(0xA6); // Normal display
Wire.write(0xA8); // Set multiplex ratio
Wire.write(0x3F); // 1/64 duty
Wire.write(0xA4); // Display follow RAM
Wire.write(0xD3); // Set display offset
Wire.write(0x00); // No offset
Wire.write(0xD5); // Set display clock divide ratio
Wire.write(0xF0); // Highest frequency
Wire.write(0xD9); // Set pre-charge period
Wire.write(0x22);
Wire.write(0xDA); // Set com pins hardware configuration
Wire.write(0x12);
Wire.write(0xDB); // Set vcomh
Wire.write(0x20); // 0.77x Vcc
Wire.write(0x8D); // Charge pump
Wire.write(0x14);
Wire.write(0xAF); // Display ON
Wire.endTransmission();
}
// Function to draw the arrow based on the angle
void drawArrow(int angle) {
clearDisplay();
// Calculate the end point of the arrow based on the angle
int x0 = OLED_WIDTH / 2;
int y0 = OLED_HEIGHT / 2;
int arrowLength = 20;
float radians = angle * 3.14159 / 180.0;
int x1 = x0 + arrowLength * cos(radians);
int y1 = y0 + arrowLength * sin(radians);
drawLine(x0, y0, x1, y1);
updateDisplay();
}
// Function to clear the OLED display
void clearDisplay() {
Wire.beginTransmission(0x3C);
Wire.write(0x00); // Command mode
for (int i = 0; i < 1024; i++) {
Wire.write(0x00);
}
Wire.endTransmission();
}
// Function to draw a line on the OLED display (Bresenham's algorithm)
void drawLine(int x0, int y0, int x1, int y1) {
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int err = dx - dy;
while (true) {
setPixel(x0, y0);
if (x0 == x1 && y0 == y1) break;
int e2 = err * 2;
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
// Function to set a pixel on the OLED display
void setPixel(int x, int y) {
if (x < 0 || x >= OLED_WIDTH || y < 0 || y >= OLED_HEIGHT) return;
Wire.beginTransmission(0x3C);
Wire.write(0x40); // Data mode
Wire.write(0xFF); // Pixel on
Wire.endTransmission();
}
// Function to update the OLED display
void updateDisplay() {
Wire.beginTransmission(0x3C);
Wire.write(0x00); // Command mode
Wire.write(0x21); // Set column address
Wire.write(0x00);
Wire.write(OLED_WIDTH - 1);
Wire.write(0x22); // Set page address
Wire.write(0x00);
Wire.write((OLED_HEIGHT / 8) - 1);
Wire.endTransmission();
}