Pretty new to this.
I am building a timer for exposing photo resist to UV. Using an MCU and display seems to be the obvious solution. I have set about doing this initially using an Arduino Uno, rotary encoder and OLED display.
One issue remains that I cannot resolve.
The encoder works 100% when rotating it.
The pin to turn on/off an external FET is working 100%
The encoder switch, which I am using to start the exposure, almost works. The problem is that when the switch is pressed the ISR for the encoder is triggered for every second indent. So 50% of the indents work fine and 50% result in a reduction of the time (and state of the exposure desired) by 1s. My assumption is that pressing the momentary switch activates the rotary encoder for half the indents.
To work around this I have tried a number of alternatives but suspect that physically the encoder ISR is triggered before it can be disabled i.e. before the switch press ISR is triggered (which simply disables the encoder ISR).
Below is the code. Is there any way to resolve this?
/*
* Code for an exposure timer. It provides for:
* 1) A rotary encoder is used to set the time in steps of 1s
* 2) The time is displayed on an oled screen
* 3) A minimum of 0s and a maximum of 600s are permitted
* 4) The lamp is switched via a relay, which is driven using an external mosfet
* 5) Once the encoder switch is pressed, to start the exposure, an isr is used and prevents polling
* of the encoder until the exposure completes.
*
* Tutorial page: https://arduinogetstarted.com/tutorials/arduino-rotary-encoder - rotary encoder code used (and modified)
*/
//OLED libraries to include
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//Define pins and directions for the rotary encoder. Note that only pins 2 and 3 are external interrupt pins
#define CLK_PIN 3
#define DT_PIN 4
#define SW_PIN 2
#define DIRECTION_CW 0 // encoder clockwise direction
#define DIRECTION_CCW 1 // encoder counter-clockwise direction
// Define a pin to drive the external FET
#define FET_PIN 7
// Define the screen size
#define SCREEN_WIDTH 128 //OLED width in pixels
#define SCREEN_HEIGHT 32 //OLED display height in pixels
// Declare an OLED (SSD1306) object connected to I2C
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Set encoder variables
volatile int counter = 60; //Default exposure time is 60s
volatile int direction = DIRECTION_CW;
volatile unsigned long last_time; // for debouncing, however, we are using hardware debounce so may disable
int prev_counter;
int count_down; //Used for timing the exposure
void setup() {
Serial.begin(9600);
// configure encoder and switch pins as inputs
pinMode(CLK_PIN, INPUT);
pinMode(DT_PIN, INPUT);
pinMode(SW_PIN, INPUT_PULLUP);
pinMode(FET_PIN, OUTPUT);
digitalWrite(FET_PIN, LOW); //Immediately turn the pin off. This means that an N channel FET will be used
// Initialise the OLED display with address 0x3C
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)){
Serial.println(F("SSD1306 allocation failed"));
while(true);
}
delay(1000); // wait for the oled to initialise
oled.setTextColor(WHITE); // text colour
oled.setTextSize(3); // text size
// use an interrupt for CLK pin
// call ISR_encoderChange() when CLK pin changes from LOW to HIGH
attachInterrupt(digitalPinToInterrupt(CLK_PIN), ISR_encoderChange, RISING);
// use an interrupt to disable ISR_encoderChange. Hopefully a higher priority
// whcih is why the switch is attached to pin 2 and the CLK to pin 3
attachInterrupt(digitalPinToInterrupt(SW_PIN), ISR_disableISR, FALLING);
}
void loop() {
if(prev_counter != counter){
//Print top the serial terminal
Serial.print("DIRECTION: ");
if (direction == DIRECTION_CW)
Serial.print("Clockwise");
else
Serial.print("Counter-clockwise");
Serial.print(" | COUNTER: ");
Serial.println(counter);
prev_counter = counter;
//Display counter on the OLED
oled.clearDisplay(); // clear the display
oled.setCursor(20, 10); // display position
oled.print(counter); // text to print
oled.print("s");
oled.display(); // show on OLED
}
//Check if the button is pressed
if(digitalRead(SW_PIN) == 0){
//reset_count = counter;
count_down = prev_counter;
prev_counter = 0; //change the prev_counter to force the if loop above to run on completion of the exposure
digitalWrite(FET_PIN, HIGH); //pull the gate high for an N channel FET to turn on
while(count_down >> 0){
Serial.print("Time remaining ");
Serial.println(count_down);
//Display counter on the OLED
oled.clearDisplay(); // clear the display
oled.setCursor(20, 10); // display position
oled.print(count_down); // text to print
oled.print("s");
oled.display(); // show on OLED
count_down--;
delay(978); //Use a delay of slightly less than 1s due to execution time
}
digitalWrite(FET_PIN, LOW); //turn off an N channel FET
attachInterrupt(digitalPinToInterrupt(CLK_PIN), ISR_encoderChange, RISING);
//counter = reset_count;
}
//interrupts();
}
void ISR_encoderChange(){
/* Disable software bounce as we are using hardware debounce
if((millis() - last_time) < 100) //debounce time is set to 50ms
return; */
if(digitalRead(DT_PIN) == HIGH){
//encoder is rotating in a counter clockwise direction
if (counter > 0){
counter--;
direction = DIRECTION_CCW;
}
}
else {
//the encoder is rotating in a clockwise direction
if (counter < 600){
counter++;
direction = DIRECTION_CW;
}
}
//last_time = millis();
}
void ISR_disableISR(){
detachInterrupt(digitalPinToInterrupt(CLK_PIN)); // disable the encoder ISR
}