#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#include <LowPower.h>
// Include the FreeSans9 and FreeSans18 font libraries
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
// OLED display settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Custom splash screen bitmap (example, replace with your own bitmap data)
const unsigned char splashScreenBitmap[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xdf, 0xf8, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x07, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x80, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x80, 0x70, 0xf8, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x03, 0x10, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x0c, 0x02, 0x04, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0x00, 0x01, 0x02, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xd0, 0xc0, 0x01, 0x01, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xdb, 0xfe, 0x3d, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x7b, 0xdc, 0x47, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7b, 0x3c, 0x07, 0x80, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7e, 0xf8, 0x07, 0xc0, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xf0, 0x07, 0xe0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x67, 0xf7, 0xe0, 0x26, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x03, 0xfc, 0x30, 0x23, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1e, 0x0d, 0xc0, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xfb, 0xb0, 0x02, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x7f, 0xfb, 0x3c, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x7f, 0xf7, 0xff, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0x6f, 0x47, 0xf4, 0xff, 0xc0, 0xe0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x6f, 0xe8, 0xff, 0xf0, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x07, 0x5f, 0x7f, 0xec, 0xff, 0xfe, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x17, 0x6f, 0x7f, 0xff, 0xff, 0xff, 0x98, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x17, 0x46, 0xff, 0xdf, 0xff, 0xff, 0xe4, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x9f, 0x46, 0xff, 0xdf, 0xff, 0xff, 0xf2, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0xdf, 0x7e, 0xff, 0xbf, 0xff, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x7e, 0xff, 0xbf, 0xff, 0xff, 0xf4, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xcf, 0x7e, 0xff, 0x7f, 0xff, 0xff, 0xf4, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x4f, 0xfe, 0xff, 0x7f, 0xff, 0xff, 0xec, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xbd, 0xf8, 0xff, 0xf3, 0xff, 0xd8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xbd, 0xf9, 0xff, 0x83, 0xff, 0xd0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xbd, 0xf9, 0xfe, 0x03, 0xff, 0x90, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xbd, 0xfb, 0xf8, 0x03, 0xfe, 0x90, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xbd, 0xfb, 0xf0, 0x01, 0xf7, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xbb, 0xf7, 0xf0, 0x01, 0xf8, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xbb, 0xf7, 0xf0, 0x01, 0xee, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x9f, 0xbb, 0xef, 0xf0, 0x01, 0xf0, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xbb, 0xef, 0xf8, 0x00, 0xf0, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x9f, 0xbb, 0xdf, 0xff, 0x00, 0xfc, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x09, 0x1f, 0xbb, 0xdf, 0xfe, 0x41, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x1f, 0xb7, 0xbf, 0xfe, 0x63, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x0f, 0xb7, 0xbf, 0xff, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0xf7, 0x7f, 0xff, 0xff, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0xf7, 0x7f, 0xfc, 0x3f, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x31, 0x0f, 0xd6, 0xff, 0xb0, 0x1f, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x31, 0x0f, 0xde, 0xfe, 0x20, 0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0x07, 0xcf, 0x7c, 0x07, 0xc3, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3c, 0x84, 0x4b, 0xd8, 0x30, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x47, 0x81, 0x80, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x41, 0x02, 0x04, 0x07, 0xe1, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xc0, 0x30, 0x1f, 0xf0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x7f, 0xc0, 0x4f, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x03, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x84, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xff, 0x48, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x3f, 0xbf, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc7, 0xf0, 0x38, 0x0b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Rotary encoder settings
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define BUTTON_PIN 4
Encoder myEnc(ENCODER_PIN_A, ENCODER_PIN_B);
// Stepper driver settings
#define STEP_PIN 8
#define DIR_PIN 9
#define STEPS_PER_REV 200 // Adjust this according to your stepper motor
int currentPosition = -999;
int lastPosition = -999;
int selectedMenu = 0;
int menuSize = 7; // Increased to include Sleep option
int visibleItems = 4; // Number of items to display at once
const char* menuItems[] = {
"20 BB",
"40 BB",
"60 BB",
"80 BB",
"100 BB",
"240 BB",
"Sleep" // Added Sleep item
};
const int stepsForMenuItem[] = {
400, // Item 1: 2 full turns (200 steps per rev * 2)
800, // Item 2: 4 full turns
1200, // Item 3: 6 full turns
1600, // Item 4: 8 full turns
2000, // Item 5: 10 full turns
2400, // Item 6: 12 full turns
0 // Sleep does not require steps
};
unsigned long lastInteractionTime = 0;
const unsigned long sleepTimeout = 300000; // 5 minutes in milliseconds
void setup() {
Serial.begin(9600);
// Initialize OLED display
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
// Display splash screen
display.drawBitmap(0, 0, splashScreenBitmap, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
display.display();
delay(3000); // Show splash screen for 3 seconds
display.clearDisplay();
display.display();
// Initialize button pin
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initialize stepper driver pins
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
lastInteractionTime = millis(); // Set the initial interaction time
}
void loop() {
// Check if button is pressed to wake up
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50); // Debounce delay
if (digitalRead(BUTTON_PIN) == LOW) { // Check again to confirm
wakeUp();
}
}
long newPosition = myEnc.read() / 4;
if (newPosition != lastPosition) {
lastPosition = newPosition;
selectedMenu = newPosition % menuSize;
if (selectedMenu < 0) selectedMenu += menuSize;
updateDisplay();
lastInteractionTime = millis(); // Reset the interaction timer
}
if (digitalRead(BUTTON_PIN) == LOW) {
if (selectedMenu == menuSize - 1) { // If "Sleep" item is selected
enterSleepMode();
} else {
Serial.println(menuItems[selectedMenu]);
moveStepper(stepsForMenuItem[selectedMenu]);
delay(500); // debounce delay
lastInteractionTime = millis(); // Reset the interaction timer
}
}
// Check for inactivity and enter sleep mode if necessary
if (millis() - lastInteractionTime > sleepTimeout) {
enterSleepMode();
}
}
void wakeUp() {
// Optional: Add any wake-up logic here
display.clearDisplay();
display.display();
Serial.println("Woke up!");
}
void enterSleepMode() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Calculate the text width and height to center it
String sleepText = "Entering Sleep.";
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(sleepText, 0, 0, &x1, &y1, &w, &h);
int xPos = (SCREEN_WIDTH - w) / 2;
int yPos = (SCREEN_HEIGHT - h) / 2 + h; // Center vertically
display.setCursor(xPos, yPos);
display.println(sleepText);
display.display();
delay(3000); // Show message for 3 seconds
// Clear display to make the screen black
display.clearDisplay();
display.display();
// Enter sleep mode
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); // Sleep for 8 seconds
Serial.println("Device is sleeping.");
lastInteractionTime = millis(); // Reset the timer upon waking
}
void updateDisplay() {
display.clearDisplay();
display.setFont(&FreeSans9pt7b); // Set FreeSans9 font
display.setTextSize(1); // Text size to 1 when using custom font
// Calculate the starting index to show
int startIndex = selectedMenu - (visibleItems / 2);
if (startIndex < 0) startIndex = 0;
if (startIndex + visibleItems > menuSize) startIndex = menuSize - visibleItems;
for (int i = 0; i < visibleItems; i++) {
int itemIndex = startIndex + i;
if (itemIndex < menuSize) {
if (itemIndex == selectedMenu) {
display.fillRect(0, i * 16, SCREEN_WIDTH, 16, SSD1306_WHITE); // Highlight background
display.setTextColor(SSD1306_BLACK); // Black text on white background
} else {
display.setTextColor(SSD1306_WHITE);
}
display.setCursor(0, i * 16 + 12); // Adjust cursor position
display.println(menuItems[itemIndex]);
}
}
display.display();
}
void moveStepper(int steps) {
if (steps <= 0) return; // Skip if no steps to take
digitalWrite(DIR_PIN, steps > 0 ? HIGH : LOW);
steps = abs(steps);
int displayedSteps = 0;
unsigned long lastUpdateTime = 0;
const int updateInterval = 20; // Update the display every 100 milliseconds
for (int i = 0; i < steps; i++) {
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(500); // Adjust the speed if necessary
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(500); // Adjust the speed if necessary
// Only update the displayed steps every 20 actual steps
if (i % 20 == 0) {
displayedSteps++;
}
// Update the display at the specified interval
if (millis() - lastUpdateTime > updateInterval) {
display.clearDisplay();
display.setFont(&FreeSans18pt7b); // Set FreeSans18 font
display.setTextSize(1); // Text size to 1 when using custom font
display.setTextColor(SSD1306_WHITE);
// Prepare the text to display
String displayText = String(displayedSteps) + "/" + String(steps / 20);
// Calculate text width using getTextBounds
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(displayText, 0, 0, &x1, &y1, &w, &h);
int xPos = (SCREEN_WIDTH - w) / 2; // Center X position
display.setCursor(xPos, (SCREEN_HEIGHT + h) / 2); // Center vertically
display.print(displayText);
display.display();
lastUpdateTime = millis();
}
}
// Ensure the final step count is displayed after the loop
display.clearDisplay();
display.setFont(&FreeSans18pt7b); // Set FreeSans18 font
display.setTextSize(1); // Text size to 1 when using custom font
display.setTextColor(SSD1306_WHITE);
// Prepare final text for display
String finalText = String(displayedSteps) + "/" + String(steps / 20);
// Calculate text width using getTextBounds
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(finalText, 0, 0, &x1, &y1, &w, &h);
int finalXPos = (SCREEN_WIDTH - w) / 2; // Center X position
display.setCursor(finalXPos, (SCREEN_HEIGHT + h) / 2); // Center vertically
display.print(finalText);
display.display();
}