I modified this project and i am having some issues.
Explanation of project:
I fully restored a 50s jukebox and i want to control the volume remotely using a rotary encoder and connecting a motor to the volume pot on the back of the jukebox.
Current Setup:
Currently i have a single arduino with a rotary encoder, oled display, dual channel pwm motor driver and a dc motor with a magnetic encoder.
Issues:
my main issue i would like to solve is when i remove power from the device and turn it back on i would like it to show the current position.
Ex. i turn volume to 7 the oled shows 7 and when power is off and turns back on id like it to show 7 again. currently it goes back to 0.
second issue is id like it to be wireless(this is my first project and I have no idea how to do this)
third is i have a momentary switch that id like to add and this will activate a relay momentarily(This is for the rejecting of a record)
Other issues i want to look into are:
If the motor encounters some resistance make it stop
currently i have a 12v motor, can i run it at 5v so i dont need 2 power supplies?
#include <Wire.h> //This is for i2C
#include <SSD1306Ascii.h> //i2C OLED
#include <SSD1306AsciiWire.h> //i2C OLED
#include <EEPROM.h>
#define I2C_ADDRESS 0x3C
#define RST_PIN -1
SSD1306AsciiWire oled;
float OLEDTimer = 0; //Timer for the display refresh interval
//I2C pins:
//STM32F103C8T6: SDA: PB7 SCL: PB6
//Arduino: SDA: A4 SCL: A5
//Input and output pins
//Motor encoder
const int encoderPin1 = 3; //this pin is also the interrupt pin!
const int encoderPin2 = 4; //this pin is a normal pin, read upon the interrupt
int encoderPin2Value = 0; //value of the encoder pin (0 or 1), this pin is read inside the interrupt - used for direction determination
//----------------------------------
//PWM motor driver
const int PWMPin = 11; //this is an analog pin (with the tilde (~) symbol), this pin can also do higher frequency + independent from millis()'s timer
int PWMValue = 0; //0-255 PWM value for speed, external PWM boards can go higher (e.g. PCA9685: 12-bit => 0-4095)
const int directionPin1 = 10; //digital pin, output, sets the direction
const int directionPin2 = 9; //digital pin, output, sets the direction
const int standByPin = 6; //STDBY pin, must be active high. Stops the H-bridge.
int motorDirection = 1; //direction value 0: CCW, 1: CW. - Stored value
//----------------------------------
//Rotary encoder
const int RotaryCLK = 2; //CLK pin on the rotary encoder, interrupt pin!
const int RotaryDT = 7; //DT pin on the rotary encoder, read inside the interrupt
const int RotarySW = 8; //SW pin on the rotary encoder (Button function)
int RotaryButtonValue = 0; //0 or 1 (pressed or not)
float RotaryTime; //timer for debouncing
volatile int rotaryValue = 0; //value manipulated by the encoder
int previousRotaryValue = -1; //a variable that stores the previous value - easy to follow changes
//----------------------
//Target values - Also called as setpoint!
float targetPosition = 0; //the PID will try to reach this value
//-----------------------
//Measured values
volatile float motorPosition = 0; //position based on the encoder
float previousMotorPosition = -1; //helps to keep track of changes (useful for the display update)
//-----------------------------------
//-----------------------------------
//PID parameters - tuned by the user
float proportional = 1.35; //k_p = 0.5
float integral = 0.00005; //k_i = 3
float derivative = 0.01; //k_d = 1
float controlSignal = 0; //u - Also called as process variable (PV)
//-----------------------------------
//-----------------------------------
//PID-related
float previousTime = 0; //for calculating delta t
float previousError = 0; //for calculating the derivative (edot)
float errorIntegral = 0; //integral error
float currentTime = 0; //time in the moment of calculation
float deltaTime = 0; //time difference
float errorValue = 0; //error
float edot = 0; //derivative (de/dt)
//Statuses of the DT and CLK pins on the encoder
int CLKNow;
int CLKPrevious;
int DTNow;
int DTPrevious;
// Define a memory address to store the volume position
#define VOLUME_POSITION_ADDRESS 0
void setup() {
Serial.begin(115200);
Wire.begin(); //start i2C
Wire.setClock(800000L); //faster clock
//Motor encoder-related
pinMode(encoderPin1, INPUT); //A
pinMode(encoderPin2, INPUT); //B
attachInterrupt(digitalPinToInterrupt(encoderPin1), checkEncoder, RISING);
pinMode(standByPin, OUTPUT); //
//Definition of the pins, remember where you need
pinMode(RotaryCLK, INPUT_PULLUP); //CLK
pinMode(RotaryDT, INPUT_PULLUP); //DT
pinMode(RotarySW, INPUT_PULLUP); //SW
attachInterrupt(digitalPinToInterrupt(RotaryCLK), RotaryEncoder, CHANGE);
//Store states
CLKPrevious = digitalRead(RotaryCLK);
DTPrevious = digitalRead(RotaryDT);
//------------------------------------------------------------------------------
//OLED part
#if RST_PIN >= 0
oled.begin(&Adafruit128x32, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
oled.begin(&Adafruit128x32, I2C_ADDRESS);
#endif // RST_PIN >= 0
oled.setFont(Adafruit5x7);
oled.clear(); //clear display
oled.set2X(); //
oled.println("Seeburg"); //print some welcome message
oled.println("Controller");
oled.set1X();
delay(1000);
OLEDTimer = millis(); //start the timer
oled.clear();
displayPermanentItems();
refreshDisplay();
// Load volume position from EEPROM
targetPosition = loadVolumePosition();
}
void loop() {
calculatePID();
driveMotor();
CheckRotaryButton();
refreshDisplay();
}
void checkEncoder() {
//We need to read the other pin of the encoder which will be either 1 or 0 depending on the direction
encoderPin2Value = digitalRead(encoderPin2);
if (encoderPin2Value == 1) //CW direction
{
motorPosition++;
}
else //else, it is zero... -> CCW direction
{
motorPosition--;
}
}
void driveMotor() {
//Determine speed and direction based on the value of the control signal
//direction
if (controlSignal < 0) //negative value: CCW
{
motorDirection = 1;
}
else if (controlSignal > 0) //positive: CW
{
motorDirection = -1;
}
else //0: STOP - this might be a bad practice when you overshoot the setpoint
{
motorDirection = 0;
}
//---------------------------------------------------------------------------
//Speed
PWMValue = (int)fabs(controlSignal); //PWM values cannot be negative and have to be integers
if (PWMValue > 255) //fabs() = floating point absolute value
{
PWMValue = 255; //capping the PWM signal - 8 bit
}
if (PWMValue < 30 && errorValue != 0)
{
PWMValue = 30;
}
//A little explanation for the
// "bottom capping":
//Under a certain PWM value, there won't be enough current flowing through the coils of the motor
//Therefore, despite the fact that the PWM value is set to the "correct" value, the motor will not move
//The above value is an empirical value, it depends on the motors perhaps, but 30 seems to work well in my case
//we set the direction - this is a user-defined value, adjusted for TB6612FNG driver
if (motorDirection == 1) //-1 == CCW
{
digitalWrite(directionPin1, LOW);
digitalWrite(directionPin2, HIGH);
}
else if (motorDirection == -1) // == 1, CW
{
digitalWrite(directionPin1, HIGH);
digitalWrite(directionPin2, LOW);
}
else // == 0, stop/break
{
digitalWrite(directionPin1, LOW);
digitalWrite(directionPin2, LOW);
digitalWrite(standByPin, LOW);
PWMValue = 0;
//In this block, we also shut down the motor and set the PWM to zero
}
//----------------------------------------------------
//Then we set the motor speed
analogWrite(PWMPin, PWMValue);
//Optional printing on the terminal to check what's up
/*
Serial.print(errorValue);
Serial.print(" ");
Serial.print(PWMValue);
Serial.print(" ");
Serial.print(targetPosition);
Serial.print(" ");
Serial.print(motorPosition);
Serial.println();
*/
}
void calculatePID() {
//Determining the elapsed time
currentTime = micros(); //current time
deltaTime = (currentTime - previousTime) / 1000000.0; //time difference in seconds
previousTime = currentTime; //save the current time for the next iteration to get the time difference
//---
errorValue = motorPosition - targetPosition; //Current position - target position (or setpoint)
edot = (errorValue - previousError) / deltaTime; //edot = de/dt - derivative term
errorIntegral = errorIntegral + (errorValue * deltaTime); //integral term - Newton-Leibniz, notice, this is a running sum!
controlSignal = (proportional * errorValue) + (derivative * edot) + (integral * errorIntegral); //final sum, proportional term also calculated here
previousError = errorValue; //save the error for the next iteration to get the difference (for edot)
}
void printValues() {
//Serial.print("Position: ");
Serial.println(motorPosition);
}
void displayPermanentItems() {
//print the permanent items on the display
oled.setCursor(0, 0); //(x [pixels], y[lines])
oled.print("Target Volume");
oled.setCursor(0, 2);
oled.print("Volume Position");
}
void refreshDisplay() {
if (millis() - OLEDTimer > 100) //check if we will update every 100 ms
{
if (previousRotaryValue != rotaryValue) {
oled.setCursor(0, 1);
oled.print(" ");
oled.setCursor(0, 1);
oled.print(rotaryValue / 515); //print the target value set by the rotary encoder
previousRotaryValue = rotaryValue;
OLEDTimer = millis(); //reset timer
}
if (motorPosition != previousMotorPosition) {
oled.setCursor(0, 3);
oled.print(" ");
oled.setCursor(0, 3);
oled.print(motorPosition / 515, 0); //print the new absolute position
previousMotorPosition = motorPosition;
OLEDTimer = millis(); //reset timer
}
} else {
//skip
}
}
void RotaryEncoder() {
CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
// If last and current state of CLK are different, then a pulse occurred
if (CLKNow != CLKPrevious && CLKNow == 1) {
if (digitalRead(RotaryDT) != CLKNow) //the increment/decrement can depend on the actual polarity of CLK and DT
{
rotaryValue = max(0, rotaryValue - 515); //1
} else {
rotaryValue = min(515 * 10, rotaryValue + 515); //1
}
}
CLKPrevious = CLKNow; // Store the last CLK state
}
void CheckRotaryButton() {
RotaryButtonValue = digitalRead(RotarySW); //read the button state
if (RotaryButtonValue == 0) //0 (activates when pressed)and 1 can differ based on the wiring
{
if (millis() - RotaryTime > 1000) {
targetPosition = rotaryValue; //pass the setpoint (target value) to the corresponding variable
digitalWrite(standByPin, HIGH); //enable motor driver
RotaryTime = millis(); //save time
// Save volume position to EEPROM
saveVolumePosition(targetPosition);
}
}
}
void saveVolumePosition(float position) {
EEPROM.put(VOLUME_POSITION_ADDRESS, position);
}
float loadVolumePosition() {
float savedPosition;
EEPROM.get(VOLUME_POSITION_ADDRESS, savedPosition);
return savedPosition;
}