Time Controlled Relay (Arduino UNO, Joystick, UI/menu, OLED)

by n4th4n0 in Circuits > Arduino

1310 Views, 1 Favorites, 0 Comments

Time Controlled Relay (Arduino UNO, Joystick, UI/menu, OLED)

IMG_20220511_114203.jpg
IMG_20220511_114235.jpg

What this is about:

  • It is a setup for a time controlled (count down) relay.
  • Visualization with an OLED-display
  • A UI/menu, navigated with a joystick provide user interaction and control.
  • The code can easily be extended for different amounts of time or even further functionalities.

I put this together for a friend who wants to control UV-lights for a certain kind of photo development (cyanotype, photograms, etc.).

  • Next idea to be implemented:
  • configuration of individual timer/count down

Supplies

I got all parts from German suppliers. Links to their shops and further specs are given below.

  • Arduino UNO compatible board - shop
  • One-Chanel Relay (230VA) - shop
  • Joystick (2-axis, select button) - shop
  • 0.96" OLED-Display - shop
  • Cables (with male and female plug)

Wiring Components

TimerRelay_v0.1_bb.png

Wiring according to the schematic. Note, that the used version of the Arduino UNO compatible board has two 5V pins, which is not the case in the diagram. Here, the 5V pin is connected twice to provide for the joystick and the relay.

For this setup soldering is not required, but it is still in a raw state and needs to get into a proper casing.

Code

A few words to the code. The main reason for posting this here is to show the idea of a menu. I could not find a similar type where one has kind of sub menus that feel like different pages. As it is with such projects, I found everywhere some bits and pieces and recombined it to my purpose. This might be handy for other projects.

The menu is controlled via two variables menuCount and page_no (both are declared as type byte):

  • menuCount can be seen as an indicator of the selected menu position. As there is no scrolling, the number of menu entries is limited of course.
  • page_no controls the sub menus. If menuCount on position 2 is selected and "confirmed", it sets page_no to the value of 2. As can be seen in the code, page_no with the vale of 2 leads to the staticMenu_2

Another advantage of this is that each entry has coordinate-like address. Combined with the button stroke of the joystick we can have this as a condition to do something like:

if ((bValue==1) && (menuCount==1) && (page_no==1)){
 }

In this example, the count down is started.

Navigation is bound to movement of the joystick:

  • up/down - browse the menu
  • right - forward/enter sub menu
  • left - back to main menu
  • button - execute.


#include <SPI.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <ezButton.h> // For the Joystick
#include <CountDown.h>


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)

#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//For the Joystick
#define VRX_PIN  A0 // Arduino pin connected to VRX pin
#define VRY_PIN  A1 // Arduino pin connected to VRY pin
#define SW_PIN   2  // Arduino pin connected to SW  pin

ezButton button(SW_PIN);
CountDown CD;

int xValue = 0; // To store value of the X axis
int yValue = 0; // To store value of the Y axis
int bValue = 0; // To store value of the button

byte page_no = 0; //to navigate trough the menu

String msg = "prime string"; // To store value of the message

byte menuCount = 1; //Menu navigation

boolean TimerStatus = false; //Staus false=Finish/Standby, true=Running

const int RELAIS = 3; // Relais-Pin am Arduino


//Menu Header///////////////////////////////////
void MenuHead(String title){
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(1, 0);
  display.println(title);
  display.setCursor(0, 6);
  display.println("---------------------");
}

//Menu Footer///////////////////////////////////

void MenuFoot(){
  display.setCursor(0, 53);
  display.println("---------------------");
 
  display.setCursor(0,57);
  display.println("meCo:");
  display.setCursor(29, 57);
  display.println(menuCount);
  
  display.setCursor(80, 57);
  display.println("page:");
  display.setCursor(110, 57);
  display.println(page_no);
}

//Menu Navigation/////////////////////////////

void MenuNav(byte max, bool MenuForward){
    if (yValue > 600){            // menu up!
    menuCount--;
    Serial.println(F("menu navigation up"));
    Serial.println(menuCount);   
  }
  else if (yValue < 450){       // menu down!
    menuCount++;
    Serial.println(F("menu navigation down"));
    Serial.println(menuCount);        
  }
  if ((xValue < 500) && (MenuForward==true)){           // menu foreward/enter!
    page_no=menuCount;
    Serial.println(F("menu navigation foreward/enter"));
  } 
   
  if (xValue > 550){           // menu back! Always to Main Menu
    //if (page_no>0){
    //page_no--;}
    //else {
      page_no = 0;
      Serial.println(F("menu navigation left/back")); 
    }
      
  
    if (menuCount > max){
    menuCount=1;
  }
  else if (menuCount < 1){
    menuCount=max;
  }
  display.setCursor(2, (menuCount*10) + 5);
  display.println(">");
}


// Main Code starts here.../////////////////////////////


void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.display();
  delay(2000);
  display.clearDisplay();
  button.setDebounceTime(50); // set debounce time to 50 milliseconds //Joystick button

  CD.setResolution(CountDown::SECONDS);

  pinMode(RELAIS, OUTPUT); // Relais Pins als Ausgang deklarieren

}

void staticMenu_0() {
  MenuHead("MAIN MENU");
  //---------------------------------
  display.setTextSize(1);
  display.setCursor(10, 15);
  display.println("Timer");
  
  display.setCursor(10, 25);
  display.println("Option B");

  display.setCursor(10, 35);
  display.println("Diagnostics");

  MenuFoot();
  MenuNav(3, true); //give maximum number of options in menu
  display.display();

}

void staticMenu_1() {
  MenuHead("TIMER");
  display.setTextSize(1);
  display.setCursor(10, 15);
  display.println("Start 60 Sec.-Timer");
  
  display.setCursor(10, 25);
  display.println("Count Down:");
  display.setCursor(85, 25);
  display.println(CD.remaining());
  
  display.setCursor(10, 35);
  display.println("Status:");
  Serial.println();
  Serial.print(' ');
  Serial.print(CD.remaining());


  switch (TimerStatus){
    case 0:
      display.setCursor(55, 35);
      display.println("standby!");
      break;
    case 1:
      display.setCursor(55, 35);
      display.println("running!");
      break;
     }
  
  MenuFoot();
  MenuNav(1, false); //give maximum number of options in menu

  display.display();

}

void staticMenu_2() {
  MenuHead("OPTION B");
  
  display.setTextSize(1);
  display.setCursor(10, 15);
  display.println("something");

  MenuFoot();
  MenuNav(1, false); //give maximum number of options in menu

  display.display();
}

void staticMenu_3() {
  MenuHead("DIAGNOSTICS");
  
  display.setTextSize(1);
  display.setCursor(10, 15);
  display.println("X-Value:");
  display.setCursor(60, 15);
  display.println(xValue);

  display.setCursor(10, 25);
  display.println("Y-Value:");
  display.setCursor(60, 25);
  display.println(yValue);

  display.setCursor(10, 35);
  display.println("B-Value:");
  display.setCursor(60, 35);
  display.println(bValue);

  display.setCursor(10, 45);
  display.println("MSG:");
  display.setCursor(40, 45);
  display.println(msg);

  MenuFoot();
  MenuNav(1, false); //give maximum number of options in menu
  display.display();
}

void loop() {
  button.loop(); // MUST call the loop() function first
  delay(100);
  
  // read analog X and Y analog values
  xValue = analogRead(VRX_PIN);
  yValue = analogRead(VRY_PIN);

  // Read the button value
  bValue = button.getState();

  if (button.isPressed()) {
    Serial.println(F("The button is pressed:"));
    Serial.println(bValue);
    msg = "Button released";
    // TODO do something here
  }

  if (button.isReleased()) {
    Serial.println(F("The button is released"));
    Serial.println(bValue);
    msg = "Button pressed";
    // TODO do something here

  }
  
  if(page_no==0){
   staticMenu_0();
  }
  else if(page_no==1){
   staticMenu_1();
  } 
  else if(page_no==2){
   staticMenu_2();
  }
  else if (page_no==3){
   staticMenu_3();
  }
  
  if ((bValue==1) && (menuCount==1) && (page_no==1)){
    CD.start(60);
  }
  if (CD.remaining() > 0){
    TimerStatus=true;
    } 
  else {
     TimerStatus=false;
    }

    
   if (TimerStatus==true){
       digitalWrite(RELAIS, HIGH); //RELAIS an, current
       Serial.println(F("RELAIS ON"));
     }
   else if (TimerStatus==false){
        digitalWrite(RELAIS, LOW); //RELAIS aus, no current
        Serial.println(F("RELAIS OFF"));
    }

   display.clearDisplay();
}






Demo

timedrelaydemo(arduino-project)

See video for demo of the menu.