// File : Cascade165.ino
//
// Version 1, 5 August 2021, by Koepel
//     Initial version.
// Version 2, 5 August 2021, by Koepel
//     Layout of the wiring made better.
// Version 3, 13 August 2021, by Koepel
//     Changed 'SCK' to 'clockPin'.
//
// Cascade of four 74HC165 shift-in registers.
// Only three pins are used on the Arduino board, to read 32 switches.
//
// Using the 74HC165 is safe, because a pulse to the Latch pin 
// ('PL' on the 74HC165) will make a new start every time. 
// In case of an error or a wrong clock pulse by noise, 
// it synchronizes the data when inputs are read the next time.
//
// Based on:
//   (1)
//     Demo sketch to read from a 74HC165 input shift register
//     by Nick Gammon, https://www.gammon.com.au/forum/?id=11979
//   (2)
//     74HC165 Shift register input example
//     by Uri Shaked, https://wokwi.com/arduino/projects/306031380875182657
//
//

#include <shiftRegister4094SevenSeg.h>
// 7-segments LED uses HC4094, NOT HC595

// 16ch mux common pin
#define MUXCOM A4
//

// buttons
const byte latchPin = 11;        // to latch the inputs into the registers
const byte clockPin = 13;       // I choose the SCK pin
const byte dataPin = 12;        // I choose the MISO pin
uint32_t oldOptionSwitch = 0;   // previous state of all the inputs
const int pulseWidth = 10;      // pulse width in microseconds def 10

// 16ch mux
const uint8_t controlPins[] = { 14, 15, 16, 17  }; // A0, A1, A2, A3

uint32_t readMux(int channel)
{
  for(int i = 0; i < 4; i ++)
  {
    digitalWrite(controlPins[i], channel >> i & 1);
  }
  delay(10);
  return analogRead(MUXCOM);
}
//

// encoder
#define ENCODER_CLK 3
#define ENCODER_DT  2
//

// leds
//const byte clockPinLed = 7;    
//const byte latchPinLed = 6;    
const byte dataPinLed = 5;        // 7-segments

shiftRegister4094SevenSeg display(ANODE, 2);


void setup ()
{
  Serial.begin( 115200);
  Serial.println( "Turn on and off the switches");
  Serial.println( "Top row is switch 0 (right) to switch 7 (left)");
  Serial.println( "Second row is 8 to 15, and so on");

  pinMode( clockPin, OUTPUT);   // clock signal, idle LOW
  pinMode( latchPin, OUTPUT);   // latch (copy input into registers), idle HIGH

  digitalWrite( latchPin, HIGH);

// 16ch mux
  for (int i = 0; i < 4; ++i)
  {
    pinMode(controlPins[i], OUTPUT);
  }
//

// encoder
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
//

// leds
pinMode( dataPinLed, OUTPUT);

display.begin(dataPinLed, clockPin, latchPin);

display.print("HI");
//



}
//Setup END



// enc
int lastClk = HIGH;



void loop ()
{
  // Give a pulse to the parallel load latch of all 74HC165
  digitalWrite( latchPin, LOW);    
  delayMicroseconds( pulseWidth);
  digitalWrite( latchPin, HIGH);

  // Reading one 74HC165 at a time and combining them into a 32 bit variable
  // The last 74HC165 is at the bottom, but the switches start numbering
  // at the top. So the first byte has to be shifted into the highest place.
  uint32_t optionSwitch = 0;
  for( int i=24; i>=0; i-=8)
  {
    optionSwitch |= ((uint32_t) ReadOne165()) << i;
  }

  for( int i = 0; i<32; i++)
  {
    if( bitRead( optionSwitch, i) != bitRead( oldOptionSwitch,i))
    {
      Serial.print( "Switch ");
      if( i < 10)
        Serial.print( " ");
      Serial.print( i);
      Serial.print( " is now ");
      Serial.println( bitRead( optionSwitch, i) == 0 ? "down ↓" : "up   ↑");
    }
  }
  
  oldOptionSwitch = optionSwitch;
  delay( 25);      // slow down the sketch to avoid switch bounce

// 16ch mux only 12 used
  for(int i = 0; i < 12; ++i)
  {
   // if (readMux(i) > 0)
    {
   Serial.print(readMux(i));
   Serial.print( " ");
    }
  }
Serial.println();
//

// encoder

int newClk = digitalRead(ENCODER_CLK);
  if (newClk != lastClk) {
    // There was a change on the CLK pin
    lastClk = newClk;
    int dtValue = digitalRead(ENCODER_DT);
    if (newClk == LOW && dtValue == HIGH) {
      Serial.println("Rotated clockwise ⏩");
    }
    if (newClk == LOW && dtValue == LOW) {
      Serial.println("Rotated counterclockwise ⏪");
    }
  }


}
// Loop END




// The ReadOne165() function reads only 8 bits,
// because of the similar functions shiftIn() and SPI.transfer() 
// which both use 8 bits.
//
// The shiftIn() can not be used here, because the clock is set idle low
// and the shiftIn() makes the clock high to read a bit.
// The 74HC165 require to read the bit first and then give a clock pulse.
//

byte ReadOne165()
{
  byte ret = 0x00;

  // The first one that is read is the highest bit (input D7 of the 74HC165).
  for( int i=7; i>=0; i--)
  {
    if( digitalRead( dataPin) == HIGH)
      bitSet( ret, i);

    digitalWrite( clockPin, HIGH);
    delayMicroseconds( pulseWidth);
    digitalWrite( clockPin, LOW);
  }

  return( ret);
}
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
Loading
cd74hc4067
74HC595
74HC595