#include <LiquidCrystal_I2C.h>
#include <TM1637Display.h>
#include <TimerOne.h>
// Define pins for the display
#define CLK 6
#define DIO 5
// Define pins for the rotary encoder
const int encoderPinA = 8; // Replace with actual pin
const int encoderPinB = 9; // Replace with actual pin
const int encoderButtonPin = 10; // Replace with actual pin
// Define Rotarty Encoder Debouncing
const unsigned long encoderDebounceDelay = 50; // Debounce delay for the encoder in milliseconds
unsigned long lastEncoderChange = 0; // Timestamp of the last encoder change
// Variables for rotary encoder
volatile int encoderPos = 0;
int lastEncoded = 0;
int encoderButtonState = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 200;
// Create a TM1637Display object
TM1637Display tmDisplay(CLK, DIO);
// Create an LCD object
LiquidCrystal_I2C lcd(0x27, 16, 2); // Adjust 0x27 to your LCD's I2C address
const int buttonPin = 4; // Push button pin
const int outputPin = 7; // TTL output pin
long startFrequency = 10; // Starting frequency in Hz
long targetFrequency = 50; // Target frequency in Hz
long rampTime = 5000; // Ramp time in milliseconds
bool rampUp = false; // Flag to control ramping up or down
unsigned long lastButtonPress = 0; // For debouncing
unsigned long rampStartTime = 0; // Time when ramping started
bool ramping = false; // Flag to indicate if currently ramping
long currentFrequency = startFrequency; // Variable to hold the current frequency
void setup() {
if (!i2CAddrTest(0x27)) {
lcd = LiquidCrystal_I2C(0x3F, 16, 2);
}
// Initialize LCD
lcd.init();
lcd.backlight();
// Show welcome message
lcd.setCursor(0, 0);
lcd.print(" ARRI Rental");
lcd.setCursor(0, 1);
lcd.print("Speed Ramp Cont.");
delay(3000);
lcd.clear();
// Initialize rotary encoder
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
pinMode(encoderButtonPin, INPUT_PULLUP);
// attachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, CHANGE);
// attachInterrupt(digitalPinToInterrupt(encoderPinB), updateEncoder, CHANGE);
// Setup startFrequency
startFrequency = setParameter("Start FPS:", startFrequency, 1, 60);
// Setup targetFrequency
targetFrequency = setParameter("Target FPS:", targetFrequency, 1, 60);
// Setup rampTime
rampTime = setParameter("Ramp Time:", rampTime / 1000, 1, 120) * 1000; // Convert to ms
pinMode(buttonPin, INPUT_PULLUP);
pinMode(outputPin, OUTPUT);
Serial.begin(9600);
// Initialize TM1637 display
tmDisplay.setBrightness(0x01);
tmDisplay.showNumberDec(startFrequency, false);
// Initialize TimerOne
Timer1.initialize(1000000 / startFrequency / 2);
Timer1.attachInterrupt(toggleOutput);
// Enable the output
pinMode(outputPin, OUTPUT);
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
if (millis() - lastButtonPress > 500) {
ramping = true;
rampStartTime = millis();
rampUp = !rampUp;
lastButtonPress = millis();
}
}
if (ramping) {
adjustFrequency();
} else {
setFrequency(startFrequency);
}
// Update the 16x2 LCD with the current frequency
lcd.setCursor(0, 0);
lcd.print("FPS: ");
lcd.setCursor(0, 1);
lcd.print(currentFrequency);
updateDisplay();
}
void readEncoder() {
unsigned long currentMillis = millis();
static int lastEncoded = 0; // Static variable to retain the value between calls
if (currentMillis - lastEncoderChange < encoderDebounceDelay) {
// If we're within the debounce time, ignore this call
return;
}
int MSB = digitalRead(encoderPinA); // MSB = most significant bit
int LSB = digitalRead(encoderPinB); // LSB = least significant bit
int encoded = (MSB << 1) | LSB; // Converting the 2 pin value to single number
int sum = (lastEncoded << 2) | encoded; // Adding it to the previous encoded value
if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) {
encoderPos++;
lastEncoderChange = currentMillis;
}
if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) {
encoderPos--;
lastEncoderChange = currentMillis;
}
lastEncoded = encoded; // Store this value for next time
}
void adjustFrequency() {
unsigned long elapsedTime = millis() - rampStartTime;
if (elapsedTime <= rampTime) {
if (rampUp) {
currentFrequency = map(elapsedTime, 0, rampTime, startFrequency, targetFrequency);
} else {
currentFrequency = map(elapsedTime, 0, rampTime, targetFrequency, startFrequency);
}
setFrequency(currentFrequency);
} else if (rampUp) {
setFrequency(targetFrequency);
} else {
ramping = false;
}
}
void setFrequency(long frequency) {
currentFrequency = frequency;
Timer1.setPeriod(1000000 / frequency / 2); // Period in microseconds
}
void toggleOutput() {
static bool outputState = LOW;
outputState = !outputState;
digitalWrite(outputPin, outputState);
}
long setParameter(const char* parameterName, long currentValue, long minValue, long maxValue) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(parameterName);
encoderPos = currentValue; // Initialize encoderPos with the current value
while (true) {
readEncoder();
lcd.setCursor(0, 1);
lcd.print(currentValue);
lcd.print(" ");
int newPos = encoderPos;
if (newPos != currentValue){
currentValue = constrain(newPos, minValue, maxValue);
lcd.setCursor(0, 1);
lcd.print(currentValue);
lcd.print(" ");
}
// Serial.println(encoderPos);
// Serial.print('\n');
// delay(200);
int buttonState = digitalRead(encoderButtonPin);
if (buttonState != encoderButtonState) {
if ((millis() - lastDebounceTime) > debounceDelay) {
encoderButtonState = buttonState;
lastDebounceTime = millis();
if (buttonState == LOW) {
return currentValue;
}
}
}
}
}
void updateDisplay() {
static unsigned long lastDisplayUpdate = 0;
if (millis() - lastDisplayUpdate > 100) {
tmDisplay.showNumberDec(currentFrequency, false);
lastDisplayUpdate = millis();
}
}
bool i2CAddrTest(uint8_t addr) {
Wire.begin();
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
return true;
}
return false;
}