Complex menu for controlling multiple I/O pins

In this video I show you how to set up a relatively complex menu for controlling several I/O pins using a single rotary encoder. I made a GUI for a 1.8" LCD panel which allows you to see and modify the status of the different I/O pins. This kind of menu is very useful when you want to have control over several switches, LEDs or other switchable devices.



Schematics

In this example an Arduino Nano is used as the microcontroller.  The I/O expanders are connected to the microcontroller via i2C. The display is connected via SPI through a 5 V → 3.3 V level shifter. The rotary encoder uses an interrupt pin.

In this example an Arduino Nano is used as the microcontroller. The I/O expanders are connected to the microcontroller via i2C. The display is connected via SPI through a 5 V → 3.3 V level shifter. The rotary encoder uses an interrupt pin.

The above figure shows the wiring scheme of the circuit shown in the video. The I/O expander boards are set up in the following way: SDA is connected to A4 and SCL is connected to A5. The first circuit on the left has its A0-A1-A2 jumpers untouched and it is on the 0x20 address. The middle unit is simply plugged in to the first unit and its A0 jumper is on the left side which gives it the 0x21 address. The last unit is plugged into the middle unit and its A1 (middle) jumper is on the left side which results in the 0x22 i2C address. As I mentioned in the video, the I/O pins are used to pull to ground.

The rotary encoder is simply connected to three I/O pins and the power. The CLK pin has to be an interrupt, which is the D2 in this case.

The display is a bit more complicated. It uses the standard SPI pins, however since the display is 3.3 V compatible, it needs a level shifter. A TXS0108E level shifter is used in this example. The B side takes the 5 V signal and the A side provides the 3.3 V signal. The VCCA on the 3.3 V side is connected to the OE pin, then this pin is connected to the ground by a 10 kOhm resistor. The pins on the two sides (A and B) of the board are connected directly so what goes to A1 appears on B1… and so on. The pin of the display are connected to the Arduino (level shifter) in the following way:

  • VCC → 3.3 V

  • GND → GND

  • CS → D10

  • Reset → D8

  • A0 → D9

  • SDA → D11

  • SCK → D13

  • LED → 3.3 V

Another notice for the video demonstration: When you connect some large external devices such as the relay panel I used, you must use an external power supply for it. Do not power those devices from the Arduino! Also, make sure that the ground is shared all across the circuit components.



Wiring schematics for controlling the LEDs. The LEDs are connected to the 5 V power rail and then through a 1k5 resistor they go to the I/O expander which is the ground. When a pin has the value of 1 in the Arduino code, the LED is OFF. When it is 0, they are pulled to ground via the I/O expander and they light up.

Wiring schematics for controlling the LEDs. The LEDs are connected to the 5 V power rail and then through a 1k5 resistor they go to the I/O expander which is the ground. When a pin has the value of 1 in the Arduino code, the LED is OFF. When it is 0, they are pulled to ground via the I/O expander and they light up.



Arduino source code

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_ST7735.h> //graphics libraries
#include <Adafruit_GFX.h>
#define TFT_CS     10
#define TFT_RST    8
#define TFT_DC     9

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

//Defining pins
const int RotaryCLK = 2; //CLK pin on the rotary encoder
const int RotaryDT = 4; //DT pin on the rotary encoder
const int RotarySW = 5; //SW pin on the rotary encoder (Button function)
//Statuses of the DT and CLK pins on the encoder
int CLKNow;
int CLKPrevious;
int DTNow;
int DTPrevious;
int RotaryButtonValue; //stores the button reading
float RotaryButtonTime = 0; //timing/debounce

volatile int MainMenuCounter = 0; //0,1,2,3,4 - 4 items
volatile int PreviousMainMenuCounter = 0;
//
volatile int Menu1Counter = 0;
volatile int PreviousMenu1Counter = 0;
//
volatile int Menu2Counter = 0;
volatile int PreviousMenu2Counter = 0;
//
volatile int Menu3Counter = 0;
volatile int PreviousMenu3Counter = 0;
//
volatile int Menu4Counter = 0;
volatile int PreviousMenu4Counter = 0;

//Booleans
bool MainMenuChanged = false;
bool Menu1Changed = false;
bool Menu2Changed = false;
bool Menu3Changed = false;
bool Menu4Changed = false;

bool mainMenuSelected = true;
bool Menu1Selected = false;
bool Menu2Selected = false;
bool Menu3Selected = false;
bool Menu4Selected = false;

//These are the names in the buttons, you can edit them, but do not use too long names
String MainMenuItems[5] = {"MENU 1", "MENU 2", "MENU 3", "MENU 4", "RESET"};
String Menu1Items[9] = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Back"};
String Menu2Items[9] = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Back"};
String Menu3Items[8] = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "OFF", "Back"};
String Menu4Items[8] = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "OFF", "Back"};

//They store whether a pin is active or not
bool Menu1Activation[8] = {false, false, false, false, false, false, false, false};
bool Menu2Activation[8] = {false, false, false, false, false, false, false, false};
bool Menu3Activation[7] = {false, false, false, false, false, false, false};
bool Menu4Activation[7] = {false, false, false, false, false, false, false};

uint8_t SwitchPanel1Value = 255; //8 channels (11111111)
uint8_t SwitchPanel2Value = 255;
uint8_t SwitchPanel3Value = 255;
uint8_t SwitchPanel4Value = 255; //6 channels (2x3 channels)

void setup()
{
  Wire.begin();
  Wire.setClock(400000L);

  //LED panel
  Wire.beginTransmission(0x20);
  Wire.write(B11111111); //Turn all the LEDs OFF
  Wire.endTransmission();

  //RELAY panel
  Wire.beginTransmission(0x21);
  Wire.write(B11111111); //Turn all the relays OFF
  Wire.endTransmission();

  //RF panel
  Wire.beginTransmission(0x22);
  Wire.write(B11111111); //Turn all the RF switches OFF
  Wire.endTransmission();

  tft.initR(INITR_BLACKTAB);
  tft.fillScreen(ST7735_BLACK);

  //Definition of the pins
  pinMode(2, INPUT); //CLK
  pinMode(4, INPUT); //DT
  pinMode(5, INPUT_PULLUP); //SW
  //Store states
  CLKPrevious = digitalRead(RotaryCLK);
  DTPrevious = digitalRead(RotaryDT);

  //Rotary encoder interrupts
  attachInterrupt(digitalPinToInterrupt(RotaryCLK), RotaryEncoder, CHANGE);

  drawMainMenu();

}

void loop()
{
  checkMainMenu();
  CheckRotaryButton();
  checkSubMenus();
}


void checkSubMenus()
{
  if (Menu1Changed == true)
  {
    tft.setTextSize(1); //set font before printing the menu

    //Based on the previous menu position, we reset the coloring of the previously highlighted item
    //(X, Y, WIDTH, HEIGHT) <--- All units are in pixels
    tft.fillRect (10, 20 + (PreviousMenu1Counter * 15), 96, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (PreviousMenu1Counter * 15)); //Text cursor (X,Y)
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu1Items[PreviousMenu1Counter]);

    //Highlight the current position in the menu
    tft.fillRect (10, 20 + (Menu1Counter * 15), 96, 9, ST7735_GREEN);
    tft.setCursor(20, 21 + (Menu1Counter * 15));
    tft.setTextColor(ST7735_RED);
    tft.print(Menu1Items[Menu1Counter]);

    if (Menu1Counter < 8) //We don't print anything on the back button (i = 8)
    {
      if (Menu1Activation[Menu1Counter] == true)
      {
        tft.fillRect (109, 21 + (Menu1Counter * 15), 6, 7, ST7735_GREEN);
      }
      else
      {
        tft.fillRect (109, 21 + (Menu1Counter * 15), 6, 7, ST7735_RED);
      }
    }
    Menu1Changed = false;
    PreviousMenu1Counter = Menu1Counter;
    return; //jump out
  }

  if (Menu2Changed == true)
  {
    tft.setTextSize(1);

    tft.fillRect (10, 20 + (PreviousMenu2Counter * 15), 96, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (PreviousMenu2Counter * 15));
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu2Items[PreviousMenu2Counter]);

    tft.fillRect (10, 20 + (Menu2Counter * 15), 96, 9, ST7735_GREEN);
    tft.setCursor(20, 21 + (Menu2Counter * 15));
    tft.setTextColor(ST7735_RED);
    tft.print(Menu2Items[Menu2Counter]);

    if (Menu2Counter < 8)
    {
      if (Menu2Activation[Menu2Counter] == true)
      {
        tft.fillRect (109, 21 + (Menu2Counter * 15), 6, 7, ST7735_GREEN);
      }
      else
      {
        tft.fillRect (109, 21 + (Menu2Counter * 15), 6, 7, ST7735_RED);
      }
    }
    Menu2Changed = false;
    PreviousMenu2Counter = Menu2Counter;
    return;
  }

  if (Menu3Changed == true)
  {
    tft.setTextSize(1);

    tft.fillRect (10, 20 + (PreviousMenu3Counter * 15), 96, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (PreviousMenu3Counter * 15));
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu3Items[PreviousMenu3Counter]);

    tft.fillRect (10, 20 + (Menu3Counter * 15), 96, 9, ST7735_GREEN);
    tft.setCursor(20, 21 + (Menu3Counter * 15));
    tft.setTextColor(ST7735_RED);
    tft.print(Menu3Items[Menu3Counter]);

    Menu3Changed = false;
    PreviousMenu3Counter = Menu3Counter;
    return;
  }

  if (Menu4Changed == true)
  {
    tft.setTextSize(1);

    tft.fillRect (10, 20 + (PreviousMenu4Counter * 15), 96, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (PreviousMenu4Counter * 15));
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu4Items[PreviousMenu4Counter]);

    tft.fillRect (10, 20 + (Menu4Counter * 15), 96, 9, ST7735_GREEN);
    tft.setCursor(20, 21 + (Menu4Counter * 15));
    tft.setTextColor(ST7735_RED);
    tft.print(Menu4Items[Menu4Counter]);

    Menu4Changed = false;
    PreviousMenu4Counter = Menu4Counter;
    return;
  }

}


void checkMainMenu()
{
  if (MainMenuChanged == true)
  {
    tft.setTextSize(2); //Select a larger fonts (because of the larger buttons)

    tft.fillRect (10, 18 + (PreviousMainMenuCounter * 25), 108, 20, ST7735_WHITE);
    tft.setCursor(20, 21 + (PreviousMainMenuCounter * 25));
    tft.setTextColor(ST7735_BLACK);
    tft.print(MainMenuItems[PreviousMainMenuCounter]);

    tft.fillRect (10, 18 + (MainMenuCounter * 25), 108, 20, ST7735_GREEN);
    tft.setCursor(20, 21 + (MainMenuCounter * 25));
    tft.setTextColor(ST7735_RED);
    tft.print(MainMenuItems[MainMenuCounter]);

    PreviousMainMenuCounter = MainMenuCounter;
  }
  MainMenuChanged = false;
}

void RotaryEncoder()
{
  if (Menu1Selected == true)
  {
    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 the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (Menu1Counter < 8)
        {
          Menu1Counter++;
        }
        else //if the next increment would change the value to 8, we "manually" set the value to 0
        {
          Menu1Counter = 0;
        }
      }
      else //otherwide we decrease the value of the variable
      {
        if (Menu1Counter < 1) //if the next increment would change the value to -1, we "manually" set the value to 8
        {
          Menu1Counter = 8;
        }
        else
        {
          Menu1Counter--;
        }
      }
      Menu1Changed = true; //Let the code know that the value is updated
    }
    CLKPrevious = CLKNow;
  }
  else if (Menu2Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK);
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (Menu2Counter < 8)
        {
          Menu2Counter++;
        }
        else
        {
          Menu2Counter = 0;
        }
      }
      else
      {
        if (Menu2Counter < 1)
        {
          Menu2Counter = 8;
        }
        else
        {
          Menu2Counter--;
        }
      }
      Menu2Changed = true;
    }
    CLKPrevious = CLKNow;
  }
  else if (Menu3Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK);
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (Menu3Counter < 7)
        {
          Menu3Counter++;
        }
        else
        {
          Menu3Counter = 0;
        }
      }
      else
      {
        if (Menu3Counter < 1)
        {
          Menu3Counter = 7;
        }
        else
        {
          Menu3Counter--;
        }
      }
      Menu3Changed = true;
    }
    CLKPrevious = CLKNow;
  }
  else if (Menu4Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (Menu4Counter < 7)
        {
          Menu4Counter++;
        }
        else
        {
          Menu4Counter = 0;
        }
      }
      else
      {
        if (Menu4Counter < 1)
        {
          Menu4Counter = 7;
        }
        else
        {
          Menu4Counter--;
        }
      }
      Menu4Changed = true;
    }
    CLKPrevious = CLKNow;
  }
  else
  {
    CLKNow = digitalRead(RotaryCLK);
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (MainMenuCounter < 4)
        {
          MainMenuCounter++;
        }
        else
        {
          MainMenuCounter = 0;
        }
      }
      else
      {
        if (MainMenuCounter < 1)
        {
          MainMenuCounter = 4;
        }
        else
        {
          MainMenuCounter--;
        }
      }
      MainMenuChanged = true;
    }
    CLKPrevious = CLKNow;
  }
}

void drawMainMenu()
{
  tft.fillScreen(ST7735_BLACK); //Erase the whole previous screen before redrawing the main menu
  tft.setTextSize(1); //Small font
  tft.setCursor(0, 0); //Text cursor (X,Y)
  tft.setTextColor(ST7735_WHITE); //Font color == WHITE
  tft.print("Menu random top text"); //Text (single line) ---> you can write anything here but keep it short

  tft.setTextSize(2); //set font large before printing the menu

  for (int i = 0; i < 5; i++) //iterate over the buttons and draw them, then fills them up with text
  {
    tft.fillRect (10, 18 + (i * 25), 108, 20, ST7735_WHITE); //white background box
    tft.setCursor(20, 21 + (i * 25)); //Text cursor (X,Y)
    tft.setTextColor(ST7735_BLACK); //Font color
    tft.print(MainMenuItems[i]); //ith item (text) in the MainMenuItems[] array
  }

  //Bottom text
  tft.setTextSize(1); //set font size to small
  tft.setCursor(10, 150); //Text cursor
  tft.setTextColor(ST7735_WHITE); //Font color
  tft.print("Bottom text v1.0"); //Bottom text, for example version number, or "signature"...etc

}

void drawMenu_1()
{
  for (int i = 0; i < 9; i++)
  {
    tft.fillRect (10, 20 + (i * 15), 108, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (i * 15));
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu1Items[i]);

    if (i < 8) //We don't print anything on the back button (i = 8)
    {
      if (Menu1Activation[i] == true)
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_GREEN);
      }
      else
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_RED);
      }
    }
  }
}

void drawMenu_2()
{
  for (int i = 0; i < 9; i++)
  {
    tft.fillRect (10, 20 + (i * 15), 108, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (i * 15)); //Text cursor x,y
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu2Items[i]);

    if (i < 8)
    {
      if (Menu2Activation[i] == true)
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_GREEN);
      }
      else
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_RED);
      }
    }
  }
}

void drawMenu_3()
{
  for (int i = 0; i < 8; i++)
  {
    tft.fillRect (10, 20 + (i * 15), 108, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (i * 15));
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu3Items[i]);

    if (i < 7)
    {
      if (Menu3Activation[i] == true)
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_GREEN);
      }
      else
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_RED);
      }
    }
  }
}

void drawMenu_4()
{
  for (int i = 0; i < 8; i++)
  {
    tft.fillRect (10, 20 + (i * 15), 108, 9, ST7735_WHITE);
    tft.setCursor(20, 21 + (i * 15));
    tft.setTextColor(ST7735_BLACK);
    tft.print(Menu4Items[i]);

    if (i < 7)
    {
      if (Menu4Activation[i] == true)
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_GREEN);
      }
      else
      {
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_RED);
      }
    }
  }
}

void CheckRotaryButton()
{
  RotaryButtonValue = digitalRead(RotarySW); //read the button state

  if (RotaryButtonValue == 0) //0 and 1 can differ based on the wiring
  {
    if (millis() - RotaryButtonTime > 1000) //1 sec debounce
    {
      if (mainMenuSelected == true)
      {
        switch (MainMenuCounter)
        {
          case 0:
            //Main Menu 1
            mainMenuSelected = false; //We are not in the main menu anymore
            Menu1Selected = true; //We jumped into the Menu 1

            tft.fillScreen(ST7735_BLACK); //erase the previous screen
            //Menu title
            tft.setTextSize(1);
            tft.setCursor(0, 0);
            tft.setTextColor(ST7735_WHITE);
            tft.print("Menu 1 items"); //Title of the 1st submenu
            drawMenu_1(); //Draw the submenu
            break;
          //
          case 1:
            //Main Menu 2
            mainMenuSelected = false;
            Menu2Selected = true;

            tft.fillScreen(ST7735_BLACK);
            tft.setTextSize(1);
            tft.setCursor(0, 0);
            tft.setTextColor(ST7735_WHITE);
            tft.print("Menu 2 items");
            drawMenu_2();
            break;
          //
          case 2:
            //Main Menu 3
            mainMenuSelected = false;
            Menu3Selected = true;

            tft.fillScreen(ST7735_BLACK);
            tft.setTextSize(1);
            tft.setCursor(0, 0);
            tft.setTextColor(ST7735_WHITE);
            tft.print("Menu 3 items");
            drawMenu_3();
            break;
          //
          case 3:
            //Open Menu 4
            mainMenuSelected = false;
            Menu4Selected = true;

            tft.fillScreen(ST7735_BLACK);
            //top text
            tft.setTextSize(1);
            tft.setCursor(0, 0);
            tft.setTextColor(ST7735_WHITE);
            tft.print("Menu 4 items");
            drawMenu_4();
            break;
          case 4:
            //RESET - default values
            SwitchPanel1Value = 255;
            SwitchPanel2Value = 255;
            SwitchPanel3Value = 255;
            SwitchPanel4Value = 255;

            Wire.beginTransmission(0x20);
            Wire.write(B11111111);
            Wire.endTransmission();
            delay(1000);

            Wire.beginTransmission(0x21);
            Wire.write(B11111111);
            Wire.endTransmission();
            delay(1000);

            Wire.beginTransmission(0x22);
            Wire.write(B11111111);
            Wire.endTransmission();
            delay(1000);

            //Reset the booleans too
            for (int i = 0; i < 8; i++)
            {
              Menu1Activation[i] = false;
              Menu2Activation[i] = false;

              if (i < 7)
              {
                Menu3Activation[i] = false;
                Menu4Activation[i] = false;
              }
            }
            break;
        }
        RotaryButtonTime = millis();
      }

      else if (Menu1Selected == true)
      {
        switch (Menu1Counter)
        {
          case 0:
            //Submenu 1 Item 1
            Menu1Activation[0] = !Menu1Activation[0];
            if (Menu1Activation[0] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B11111110;
              //                                      P7<-----P0
              //Binary is read backwards (last digit is P0, first digit is P7).
              //The circuit SINKS! and does not power.
              //1 does nothing, 0 switches the component on.
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B00000001;
            }
            break;
          //
          case 1:
            //Submenu 1 Item 2
            Menu1Activation[1] = !Menu1Activation[1];
            if (Menu1Activation[1] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B11111101;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B00000010;
            }
            break;
          //
          case 2:
            //Submenu 1 Item 3
            Menu1Activation[2] = !Menu1Activation[2];
            if (Menu1Activation[2] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B11111011;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B00000100;
            }
            break;
          //
          case 3:
            //Submenu 1 Item 4
            Menu1Activation[3] = !Menu1Activation[3];
            if (Menu1Activation[3] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B11110111;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B00001000;
            }
            break;
          //
          case 4:
            //Submenu 1 Item 5
            Menu1Activation[4] = !Menu1Activation[4];
            if (Menu1Activation[4] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B11101111;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B00010000;
            }
            break;
          //
          case 5:
            //Submenu 1 Item 6
            Menu1Activation[5] = !Menu1Activation[5];
            if (Menu1Activation[5] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B11011111;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B00100000;
            }
            break;
          //
          case 6:
            //Submenu 1 Item 7
            Menu1Activation[6] = !Menu1Activation[6];
            if (Menu1Activation[6] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B10111111;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B01000000;
            }
            break;
          //
          case 7:
            //Submenu 1 Item 8
            Menu1Activation[7] = !Menu1Activation[7];
            if (Menu1Activation[7] == true)
            {
              SwitchPanel1Value = SwitchPanel1Value & B01111111;
            }
            else
            {
              SwitchPanel1Value = SwitchPanel1Value | B10000000;
            }
            break;
          //
          case 8:
            //Submenu 1 back button
            mainMenuSelected = true;
            Menu1Selected = false;
            drawMainMenu();
            RotaryButtonTime = millis();
            return; //jump out from the whole stuff, otherwise the Menu1Changed will be set to true
            break;
        }
        //Send the new value to the corresponding I/O board
        Wire.beginTransmission(0x20);
        Wire.write(SwitchPanel1Value);
        Wire.endTransmission();
        Menu1Changed = true;
        RotaryButtonTime = millis();
      }

      else if (Menu2Selected == true)
      {
        switch (Menu2Counter)
        {
          case 0:
            //Submenu 2 Item 1
            Menu2Activation[0] = !Menu2Activation[0];
            if (Menu2Activation[0] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B11111110;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B00000001;
            }
            break;
          //
          case 1:
            //Submenu 2 Item 2
            Menu2Activation[1] = !Menu2Activation[1];
            if (Menu2Activation[1] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B11111101;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B00000010;
            }
            break;
          //
          case 2:
            //Submenu 2 Item 3
            Menu2Activation[2] = !Menu2Activation[2];
            if (Menu2Activation[2] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B11111011;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B00000100;
            }
            break;
          //
          case 3:
            //Submenu 2 Item 4
            Menu2Activation[3] = !Menu2Activation[3];
            if (Menu2Activation[3] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B11110111;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B00001000;
            }
            break;
          //
          case 4:
            //Submenu 2 Item 5
            Menu2Activation[4] = !Menu2Activation[4];
            if (Menu2Activation[4] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B11101111;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B00010000;
            }
            break;
          //
          case 5:
            //Submenu 2 Item 6
            Menu2Activation[5] = !Menu2Activation[5];
            if (Menu2Activation[5] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B11011111;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B00100000;
            }
            break;
          //
          case 6:
            //Submenu 2 Item 7
            Menu2Activation[6] = !Menu2Activation[6];
            if (Menu2Activation[6] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B10111111;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B01000000;
            }
            break;
          //
          case 7:
            //Submenu 2 Item 8
            Menu2Activation[7] = !Menu2Activation[7];
            if (Menu2Activation[7] == true)
            {
              SwitchPanel2Value = SwitchPanel2Value & B01111111;
            }
            else
            {
              SwitchPanel2Value = SwitchPanel2Value | B10000000;
            }
            break;
          //
          case 8:
            //Submenu 2 back button
            mainMenuSelected = true;
            Menu2Selected = false;
            drawMainMenu();
            RotaryButtonTime = millis();
            return;
            break;
        }
        Wire.beginTransmission(0x21);
        Wire.write(SwitchPanel2Value);
        Wire.endTransmission();
        Menu2Changed = true;
        RotaryButtonTime = millis();
      }

      else if (Menu3Selected == true)
      {
        //RF1-RF6A : P0, P1, P2
        ResetMenu3Activation(); //First, deactivate the previous switch (we cannot have 2 concurrent switch states)
        switch (Menu3Counter)
        {
          case 0:
            //Submenu 3 Item 1
            Menu3Activation[0] = true;
            SwitchPanel3Value = B11111000; //LLL
            break;
          //
          case 1:
            //Submenu 3 Item 2
            Menu3Activation[1] = true;
            SwitchPanel3Value = B11111001; //HLL
            break;
          //
          case 2:
            //Submenu 3 Item 3
            Menu3Activation[2] = true;
            SwitchPanel3Value = B11111010; //LHL
            break;
          //
          case 3:
            //Submenu 3 Item 4
            Menu3Activation[3] = true;
            SwitchPanel3Value =  B11111011; //HHL
            break;
          //
          case 4:
            //Submenu 3 Item 5
            Menu3Activation[4] = true;
            SwitchPanel3Value =  B11111100; //LLH
            break;
          //
          case 5:
            //Submenu 3 Item 6
            Menu3Activation[5] = true;
            SwitchPanel3Value = B11111101; //HLH
            break;
          //
          case 6:
            //Submenu 3 Item 7 -> OFF
            Menu3Activation[6] = true;
            SwitchPanel3Value = B11111111; //HHH = OFF
            break;
          //
          case 7:
            //Submenu 3 back button
            mainMenuSelected = true;
            Menu3Selected = false;
            RotaryButtonTime = millis();
            drawMainMenu();
            return;
            break;
        }
        if (Menu3Counter != 7)
        {
          //This part redraws the green rectangle. In this menu, there's only 1 active item at a time
          tft.fillRect (109, 21 + (Menu3Counter * 15), 6, 7, ST7735_GREEN);
        }
        Wire.beginTransmission(0x22);
        Wire.write(SwitchPanel3Value & SwitchPanel4Value);
        Wire.endTransmission();
        Menu3Changed = true;
        RotaryButtonTime = millis();
      }

      else if (Menu4Selected == true)
      {
        //RF1-6 B: P4, P5, P6
        ResetMenu4Activation();
        switch (Menu4Counter)
        {
          case 0:
            //Submenu 4 Item 1
            Menu4Activation[0] = true;
            SwitchPanel4Value = B11000111; //LLL
            break;
          //
          case 1:
            //Submenu 4 Item 2
            Menu4Activation[1] = true;
            SwitchPanel4Value = B11001111; //HLL
            break;
          //
          case 2:
            //Submenu 4 Item 3
            Menu4Activation[2] = true;
            SwitchPanel4Value = B11010111; //LHL
            break;
          //
          case 3:
            //Submenu 4 Item 4
            Menu4Activation[3] = true;
            SwitchPanel4Value = B11011111; //HHL
            break;
          //
          case 4:
            //Submenu 4 Item 5
            Menu4Activation[4] = true;
            SwitchPanel4Value = B11100111; //LLH
            break;
          //
          case 5:
            //Submenu 4 Item 6
            Menu4Activation[5] = true;
            SwitchPanel4Value = B11101111; //HLH
            break;
          //
          case 6:
            //Submenu 4 Item 7 = OFF
            Menu4Activation[6] = true;
            SwitchPanel4Value = B11111111; //HHH = OFF
            break;
          //
          case 7:
            //Submenu 4 back button
            mainMenuSelected = true;
            Menu4Selected = false;
            RotaryButtonTime = millis();
            drawMainMenu();
            return;
            break;
        }
        if (Menu4Counter != 7)
        {
          tft.fillRect (109, 21 + (Menu4Counter * 15), 6, 7, ST7735_GREEN);
        }
        Wire.beginTransmission(0x22);
        Wire.write(SwitchPanel3Value & SwitchPanel4Value);
        Wire.endTransmission();
        Menu4Changed = true;
        RotaryButtonTime = millis();
      }
    }
  }
}

void ResetMenu3Activation()
{
  if (Menu3Counter != 7)
  {
    for (int i = 0; i < 7; i++)
    {
      if (Menu3Activation[i] == true)
      {
        Menu3Activation[i] = false;
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_RED); //paint the ith rectangle red
      }
    }
  }
}

void ResetMenu4Activation()
{
  if (Menu4Counter != 7)
  {
    for (int i = 0; i < 7; i++)
    {
      if (Menu4Activation[i] == true)
      {
        Menu4Activation[i] = false;
        tft.fillRect (109, 21 + (i * 15), 6, 7, ST7735_RED); //paint the ith rectangle red
      }
    }
  }
}

Previous
Previous

Building a coil winder [Part 6] - A few improvements

Next
Next

Peltier cooler-based air cooler