/*
  twoDisplaysExample.ino - Example file to demonstrate TM74HC595LedTube class use with two displays simultaneously
  Created by Gabriel D. Goldman, May, 2023.
  Updated by Gabriel D. Goldman, October, 2023.
  Released into the public domain in accordance with "GPL-3.0-or-later" license terms.
  
  WOKWI simulation available at: https://wokwi.com/projects/414802678289036289

*/
#include <Arduino.h>
#include <FourBitLedDigitalTube.h>

//Pin connection for Display #1
// Pin connected to DS of 74HC595 AKA DIO
const uint8_t dsp1Dio {2}; // Pin A3 of Arduino Nano
// Pin connected to ST_CP of 74HC595 AKA RCLK
const uint8_t dsp1Rclk {3}; // Pin A4 of Arduino Nano
// Pin connected to SH_CP of 74HC595 AKA SCLK
const uint8_t dsp1Sclk {4}; // Pin A5 of Arduino Nano

//Pin connection for Display #2
const uint8_t dsp2Dio {7}; // Pin A3 of Arduino Nano
const uint8_t dsp2Rclk {8}; // Pin A4 of Arduino Nano
const uint8_t dsp2Sclk {9}; // Pin A5 of Arduino Nano

//Set of variables and constants needed just for Demo purposes
int testNum{0};
const int firstTest{-5};
const int lastTest{0};
bool testResultOne{};
bool testResultTwo{};
const long testTime{1800};
unsigned long timer{millis() - (testTime + 1)};

// Display instances creation
// Creating two instances of the displays, the only parameters needed are
// the pins that will be connected to the display module usually marked as:
// SCLK
// RCLK
// DIO
TM74HC595LedTube myLedDispOne(dsp1Sclk, dsp1Rclk, dsp1Dio);
TM74HC595LedTube myLedDispTwo(dsp2Sclk, dsp2Rclk, dsp2Dio);

void setup()
{
  testNum = firstTest - 1;
}

void loop()
{
  // A mishmash of tests for showing the library methods provided
  // Some of this tests try to display non supported values, to show the response of the methods.
  // These failed method calls will display "Er" + testNum to help to identify why this message was set.
  
  if ((millis() - timer) >= testTime)
  {
    // time to change the test
    testNum++;
    if (testNum == firstTest){
      //Attaching the display refresh to an ISR trough the begin() method
      myLedDispOne.begin();
      myLedDispTwo.begin();
    }
    switch (testNum)
    {
    case -5:
      //print() with a string argument, composed partially as a result of calling the .getInstanceNbr()
      //that returns a value stating the sequencial order of the display instantiation
      testResultOne = myLedDispOne.print("On " + String(myLedDispOne.getInstanceNbr() + 1));
      testResultTwo = myLedDispTwo.print("On " + String(myLedDispTwo.getInstanceNbr() + 1));
      break;
    case -4:
      //print() with a string argument, all characters included in the representable characters list
      testResultOne = myLedDispOne.print("Strt");
      testResultTwo = myLedDispTwo.print("HeY");
      break;
    case -3:
      //print() with a string argument, fails to represent as it is 5 chars long (enough to fail), AND has a non-representable char included (!)
      testResultOne = myLedDispOne.print("Hello!");
      //while this print() is OK
      testResultTwo = myLedDispTwo.print("Here");
      break;
    case -2:
      testResultOne = myLedDispOne.print("thiS");
      testResultTwo = myLedDispTwo.print("You");
      break;
    case -1:
      testResultOne = myLedDispOne.print("teSt");
      testResultTwo = myLedDispTwo.print("See");
      break;
    case 0:
      //print() with a string argument, four characters long AND usable dots, all characters included in the representable characters list
      //Each valid character might be followed by a "." if needed, without being counted as a character, even spaces and special chars
      testResultOne = myLedDispOne.print("I.F.Y.I.");

      testResultTwo = myLedDispTwo.print("the");
      break;
    case 1:
      //print() with a floating point argument, one decimal digit argument, ONE decimal digit place to display, no alignment specified
      testResultOne = myLedDispOne.print(2.3, 1);
      //print() with a string argument, four characters long AND a dot, all characters included in the representable characters list
      testResultTwo = myLedDispTwo.print("dIFF.");
      break;
    case 2:
      //print() with a floating point argument, one decimal digit argument, TWO decimal digit places to display, no alignment specified
      testResultOne = myLedDispOne.print(2.3, 2);
      //print() with a string argument
      myLedDispTwo.print("OFF");
      break;
    case 3:
      //print() with a floating point argument, one decimal digit argument, TWO decimal digit places to display, right alignment specified
      testResultOne = myLedDispOne.print(2.3, 2, true);
      break;
    case 4:
      //print() with a floating point argument, one decimal digit argument, TWO decimal digit places to display, right alignment specified, zero padded specified
      testResultOne = myLedDispOne.print(2.3, 2, true, true);
      //.clear() one of the displays while keeping the other active
      myLedDispTwo.clear();
      break;
    case 5:
      //print() with a negative floating point argument, one decimal digit argument, TWO decimal digit places to display, no alignment specified
      testResultOne = myLedDispOne.print(-2.3, 2);
      break;
    case 6:
      //print() with a floating point argument, one decimal digit argument, ONE decimal digit place to display, right alignment specified
      testResultOne = myLedDispOne.print(-2.3, 1, true);
      break;
    case 7:
      //print() with a floating point argument, one decimal digit argument, ONE decimal digit place to display, right alignment specified, zero padded specified
      testResultOne = myLedDispOne.print(-2.3, 1, true, true);
      testResultTwo= myLedDispTwo.print("On");
      break;
    case 8:
      //gauge() with a floating point argument, 0 <= value <= 1.0, representing a percentage, the four ranges are:
      // 0 <= 1st range <0.25
      //0.25 <= 2nd range < 0.50
      //0.50 <= 3rd range < 0.75
      //0.75 <= 4rd range <= 1.0
      //A character to show ahead of the value is an option, 
      //if not specified will be a blank space, in this case 'b', or any other representable char, for example:
      //b for battery, F for fuel, t for temperature, r for remaining...
      testResultOne = myLedDispOne.gauge(1.0, 'b');

      //gauge() with an integer argument, 0 <= value <= 3, ranges are:
      // 0: 1st range
      // 1: 2nd range
      // 2: 3rd range
      // 3: 4rd range
      //A character to show ahead of the value is an option, 
      //if not specified will be a blank space, or any other representable char, for example:
      //b for battery, F for fuel, t for temperature, r for remaining...
      //gauge() with an integer argument, 1st range
      testResultTwo = myLedDispTwo.gauge(0);

      break;
    case 9:
      //gauge() with a floating point argument, 3rd range
      testResultOne = myLedDispOne.gauge(0.74, 'b');
      //gauge() with an integer argument, 2nd range
      testResultTwo = myLedDispTwo.gauge(1);
      break;
    case 10:
      testResultOne = myLedDispOne.gauge(1, 'b');
      testResultTwo = myLedDispTwo.gauge(2);
      break;
    case 11:
      //gauge() with an integer argument, 1st range
      testResultOne = myLedDispOne.gauge(0, 'b');
      testResultTwo = myLedDispTwo.gauge(3);
      break;
    case 12:
      //blink() activates the option to make the present data and any further data printed to de display to blink until noBlink() is invoked, the initial blink rate is set to 500 milliseconds
      //setBlinkRate() modifies the blink rate to the argument -in milliseconds- speed
      myLedDispOne.blink();
      testResultOne = myLedDispOne.setBlinkRate(600);
      testResultTwo = myLedDispTwo.gauge(3, 'F');
      break;
    case 13:
      // blinking speedup to 300 milliseconds on - same time off sequence
      testResultOne = myLedDispOne.setBlinkRate(300);
      testResultTwo = myLedDispTwo.gauge(2, 'F');
      break;
    case 14:
      //more blinking speedup to 150 milliseconds each
      testResultOne = myLedDispOne.setBlinkRate(150);
      testResultTwo = myLedDispTwo.gauge(1, 'F');
      break;
    case 15:
      //noBlink() stops the blinking option for the display
      //and use of the limited display capabilities to show a message
      myLedDispOne.noBlink();      
      testResultOne = myLedDispOne.print("You");
      testResultTwo = myLedDispTwo.gauge(0, 'F');
      break;
    case 16:
      testResultOne = myLedDispOne.print("ran");
      break;
    case 17:
      testResultOne = myLedDispOne.print("out");
      testResultTwo = myLedDispTwo.print("out");
      break;
    case 18:
        testResultOne = myLedDispOne.print("oF");
        testResultTwo = myLedDispTwo.print("oF");
      break;
    case 19:
        testResultOne = myLedDispOne.print("batt.");
        testResultTwo = myLedDispTwo.print("FUEL");
      break;
    case 20:
      //gauge() with an integer argument, and without the optional heading character parameter, uses de default SPACE character
      testResultOne = myLedDispOne.print("ouch");
      testResultTwo = myLedDispTwo.print("Good");
      break;
    case 21:
      //This counts show: negative integers 3 digits to positive numbers 3 digits
      //Alignment: left
      //Zero Padding: No

      for (int i{-150}; i < 151; i++)
      {
        testResultOne = myLedDispOne.print(i);
        testResultTwo = myLedDispTwo.print((-1)*i);
        delay(20);
      }

      delay(1000);

      //Alignment: right
      //Zero Padding: No
      for (int i{-150}; i < 151; i++)
      {
        testResultOne = myLedDispOne.print(i, true);
        testResultTwo = myLedDispTwo.print((-2)*i, true);
        delay(20);
      }

      delay(1000);

      //Alignment: left
      //Zero Padding: Yes (useless as the left alignment doesn't leave space for the extra 0's)
      for (int i{-150}; i < 151; i++)
      {
        testResultOne = myLedDispOne.print(i, false, true);
        testResultTwo = myLedDispTwo.print((-3)*i, false, true);
        delay(20);
      }

      delay(1000);

      //Alignment: right
      //Zero Padding: Yes
      for (int i{150}; i > -151; i--)
      {
        testResultOne = myLedDispOne.print(i, true, true);
        testResultTwo = myLedDispTwo.print((-4)*i, true, true);
        delay(20);
      }
      break;
    case 22:
      testResultTwo = myLedDispTwo.setWaitChar('.');
      testResultTwo = myLedDispTwo.wait();
      //This counts show: positive integers 3 digits to positive integers 4 digits
      //Alignment: left
      //Zero Padding: No
      for (int i{950}; i < 1051; i++)
      {
        testResultOne = myLedDispOne.print(i);
        delay(20);
      }      
      delay(1000);
      
      //Alignment: right
      //Zero Padding: No
      for (int i{950}; i < 1051; i++)
      {
        testResultOne = myLedDispOne.print(i, true);
        delay(20);
      }

      delay(1000);
      testResultTwo = myLedDispTwo.noWait();
      
      //Alignment: left
      //Zero Padding: Yes (useless as the left alignment doesn't leave space for the extra 0's)
      testResultTwo = myLedDispTwo.setWaitChar('o');
      testResultTwo = myLedDispTwo.wait();
      for (int i{950}; i < 1051; i++)
      {
        testResultOne = myLedDispOne.print(i, false, true);
        delay(20);
      }

      delay(1000);
      
      //Alignment: right
      //Zero Padding: Yes
      for (int i{950}; i < 1051; i++)
      {
        testResultOne = myLedDispOne.print(i, true, true);
        delay(20);
      }
      break;
    case 23:
      testResultTwo = myLedDispTwo.setWaitChar('0');

      //This counts show: positive integers 4 digits to positive integers 5 digits. When the count reaches 10,000 the print() returns false,
      //and an Error message stating the Error and number of test is displayed
      for (int i{9900}; i < 10001; i++)
      {
        testResultOne = myLedDispOne.print(i, true, true);
        delay(20);
      }
      break;
    case 24:

      //This counts show: negative integers 3 digits to negative integers 4 digits. When the count reaches -1,000 the print() returns false,
      //and an Error message stating the Error and number of test is displayed
      for (int i{-900}; i > -1001; i--)
      {
        testResultOne = myLedDispOne.print(i, true, true);
        delay(20);
      }
      break;
    case 25:
      //Use of the degrees symbol (°) by using the * ASCII character
      testResultOne = myLedDispOne.print("36.7*");
      testResultTwo = myLedDispTwo.noWait();
      testResultTwo = myLedDispTwo.print("98.6*");
      break;
    case 26:
      //Use of the c character to express a value in centigrade degrees
      testResultOne = myLedDispOne.print("36.7c");
      testResultTwo = myLedDispTwo.print("98.6F");
      break;
    case 27:
      //Use of the F character to express a value in Fahrenheit degrees
      testResultOne = myLedDispOne.print("25*c");
      testResultTwo = myLedDispTwo.print("77*F");
      break;
    case 28:
      //An easy way to use the display to show that a process is ongoing or a "Waiting State"
      testResultOne = myLedDispOne.wait();
      break;
    case 29:
      testResultOne = myLedDispOne.setWaitRate(200);
      break;
    case 30:
      testResultOne = myLedDispOne.setWaitRate(150);
      break;
    case 31:
      testResultOne = myLedDispOne.setWaitRate(100);
      testResultTwo = myLedDispTwo.print("I");
      break;
    case 32:
      //Also possible using the dots, here showing the 4 dots lit with independence of other characters
      testResultOne = myLedDispOne.noWait();
      testResultOne = myLedDispOne.print("Feel");
      testResultTwo = myLedDispTwo.print("bord");
      break;
    case 33:
      testResultOne = myLedDispOne.print("bYe");
      testResultTwo = myLedDispTwo.print("LetS");

      break;
    case 34:
      testResultOne = myLedDispOne.print("End");
      testResultTwo = myLedDispTwo.print("Go");
      break;
    case 35:
      testResultOne = myLedDispOne.print("StOP");
      testResultTwo = myLedDispTwo.print("Once");
      break;
    case 36:
      testResultOne = myLedDispOne.print("OFF");
      testResultTwo = myLedDispTwo.print("aGin");
      break;
    default:
      //Blanking the display
      myLedDispOne.clear();
      myLedDispTwo.clear();
      //Disataching the display refresh from the ISR
      myLedDispOne.stop();
      myLedDispTwo.stop();
      testNum = firstTest - 1;
      break;
    }

    if (testResultOne == false)
      //Use of a combination of characters and an integer converted to a string to display the test that produced an Error.
      myLedDispOne.print("Er" + String(testNum));

    if (testResultTwo == false)
      //Use of a combination of characters and an integer converted to a string to display the test that produced an Error.
      myLedDispTwo.print("Er" + String(testNum));

    if ((lastTest > 0) && (testNum > lastTest))
      testNum = firstTest - 1;
    timer = millis();
  }
}
nano:12
nano:11
nano:10
nano:9
nano:8
nano:7
nano:6
nano:5
nano:4
nano:3
nano:2
nano:GND.2
nano:RESET.2
nano:0
nano:1
nano:13
nano:3.3V
nano:AREF
nano:A0
nano:A1
nano:A2
nano:A3
nano:A4
nano:A5
nano:A6
nano:A7
nano:5V
nano:RESET
nano:GND.1
nano:VIN
nano:12.2
nano:5V.2
nano:13.2
nano:11.2
nano:RESET.3
nano:GND.3
sevseg1:A
sevseg1:B
sevseg1:C
sevseg1:D
sevseg1:E
sevseg1:F
sevseg1:G
sevseg1:DP
sevseg1:DIG1
sevseg1:DIG2
sevseg1:DIG3
sevseg1:DIG4
sevseg1:COM
sevseg1:CLN
74HC595
sr1:Q1
sr1:Q2
sr1:Q3
sr1:Q4
sr1:Q5
sr1:Q6
sr1:Q7
sr1:GND
sr1:Q7S
sr1:MR
sr1:SHCP
sr1:STCP
sr1:OE
sr1:DS
sr1:Q0
sr1:VCC
74HC595
sr2:Q1
sr2:Q2
sr2:Q3
sr2:Q4
sr2:Q5
sr2:Q6
sr2:Q7
sr2:GND
sr2:Q7S
sr2:MR
sr2:SHCP
sr2:STCP
sr2:OE
sr2:DS
sr2:Q0
sr2:VCC
vcc2:VCC
gnd3:GND
gnd4:GND
sevseg2:A
sevseg2:B
sevseg2:C
sevseg2:D
sevseg2:E
sevseg2:F
sevseg2:G
sevseg2:DP
sevseg2:DIG1
sevseg2:DIG2
sevseg2:DIG3
sevseg2:DIG4
sevseg2:COM
sevseg2:CLN
74HC595
sr3:Q1
sr3:Q2
sr3:Q3
sr3:Q4
sr3:Q5
sr3:Q6
sr3:Q7
sr3:GND
sr3:Q7S
sr3:MR
sr3:SHCP
sr3:STCP
sr3:OE
sr3:DS
sr3:Q0
sr3:VCC
74HC595
sr4:Q1
sr4:Q2
sr4:Q3
sr4:Q4
sr4:Q5
sr4:Q6
sr4:Q7
sr4:GND
sr4:Q7S
sr4:MR
sr4:SHCP
sr4:STCP
sr4:OE
sr4:DS
sr4:Q0
sr4:VCC
vcc1:VCC
gnd1:GND
gnd2:GND
r1:1
r1:2
r2:1
r2:2
r3:1
r3:2
r4:1
r4:2
r5:1
r5:2
r6:1
r6:2
r7:1
r7:2
r8:1
r8:2
r9:1
r9:2
r10:1
r10:2
r11:1
r11:2
r12:1
r12:2
r13:1
r13:2
r14:1
r14:2
r15:1
r15:2
r16:1
r16:2