Assign multiple outputs to individual inputs via shift registers

This is what I came up with. It uses an inputs and outputs struct that holds the metedata about each input or output. Each shift register shift register input (button / switch / IR sensor) will be associated with one or more outputs. The code compares each byte from the input shift registers and only processes a byte if it is different from the previous version of that input byte (reduce time in loop) and then look at witch bit changed. From here I can look up the outputs associated with that input bit and then set the new state of just the outputs that need changing.

Code is still pretty messy but this was just a proof of concept. I need to do more work on the data structures, putting code into functions and using pointers to the structs to reduce memory footprint.

In the code below I simulate the input shift register reads with 2 x hardcoded input bytes.

Any advice welcome on how to do this better - I'm sure there are better bitwise comparison techniques I could be using rather than trying to work out bit positions in various shift registers.

I simulate the button presses with two new input bytes:

inBytes[0] = B11110000;  // 4 bits have changed
inBytes[1] = B10000000;  // 1 bit has changed

Input byte0 will change 4 output shift register bits (1 each) and input byte1 has one bit changing but will trigger two output bits to change.

Serial output:

Start
bit 4 changed in shift register0
bitState: 1
outputNum: 4
outShiftRegister: 0
outShiftBit: 4

bit 5 changed in shift register0
bitState: 1
outputNum: 5
outShiftRegister: 0
outShiftBit: 5

bit 6 changed in shift register0
bitState: 1
outputNum: 6
outShiftRegister: 0
outShiftBit: 6

bit 7 changed in shift register0
bitState: 1
outputNum: 7
outShiftRegister: 0
outShiftBit: 7

bit 7 changed in shift register1
bitState: 1
outputNum: 15
outShiftRegister: 1
outShiftBit: 7
outputNum: 2
outShiftRegister: 0
outShiftBit: 2

Output shift register 0: 11110100
Output shift register 1: 10000000

As expected - bits 2,4,5,6,7 changed in shift register0 and bit 7 changed in register1

test_io.ino

#include "myTypes.h"

const int NUM_SHIFT_IN = 2;  // number on input shift registers
const int NUM_SHIFT_OUT = 2;  // number on output shift registers

// array to hold each previous byte read in from the shift registers for comparison
byte prevInBytes[NUM_SHIFT_IN];

// array to hold each byte read in from the shift registers
byte inBytes[NUM_SHIFT_IN] = {
  B00000000,  // input shift register0
  B00000000   // input shift register1
};

// array to hold each byte to be written to the shift registers
byte outBytes[NUM_SHIFT_OUT] = {
  B00000000,  // output shift register0
  B00000000   // output shift register1
};

// array of input_t structs that holds metatdata about each input
input_t inputs[NUM_SHIFT_IN * 8] = {  // 8 bits per shift register
  {LOW,1,{0}},  // input0
  {LOW,1,{1}},  // input1
  {LOW,1,{2}},  // input2
  {LOW,1,{3}},  // input3
  {LOW,1,{4}},  // input4
  {LOW,1,{5}},  // input5
  {LOW,1,{6}},  // input6
  {LOW,1,{7}},  // input7
  
  {LOW,1,{8}},  // input8
  {LOW,1,{9}},  // input9
  {LOW,1,{10}},  // input10
  {LOW,1,{11}},  // input11
  {LOW,1,{12}},  // input12
  {LOW,2,{13,14}},  // input13
  {LOW,2,{0,1}},    // input14
  {LOW,2,{15,2}},   // input15
};

void setup() {
  delay(2000);
  Serial.begin(115200);
  Serial.println("Start");

  // copy current input bytes into prevInBytes[] to allow comparison later
  memcpy( prevInBytes, inBytes, NUM_SHIFT_IN );
  // read in inputs
  // would be an SPI transaction to read each byte in a loop
  // place byte into inBytes[]
  inBytes[0] = B11110000;  // 4 bits have changed
  inBytes[1] = B10000000;  // 1 bit has changed

  // loop over input array compare bytes and work out what has to be processed
  for (int i = 0; i < NUM_SHIFT_IN; i++) {
    byte compareByte = inBytes[i] ^ prevInBytes[i];  // 1 if a bit is different
    if (compareByte) {
      // find which bit/s changed
      for(int bitCnt=0;bitCnt<8;bitCnt++) {
        // use bit mask  of 1 shifted by the bit position; 1 & 1 = 1
        if((compareByte & (1 << bitCnt)) == (1 << bitCnt)) {
          Serial.print("bit ");
          Serial.print(bitCnt);
          Serial.print(" changed in shift register");
          Serial.println(i);
          
          // read the new bit state
          bool bitState = bitRead(inBytes[i], bitCnt);
          Serial.print("bitState: ");
          Serial.println(bitState);
          
          // need an index lookup into inputs[] array
          int inputNum; 
          if (i > 0) {
            inputNum = bitCnt + (i * 8);  // shift along 8 bits for each shift register
          } else {
            inputNum = bitCnt;
          }

          // loop over the outputs array within inputs[] array
          for (int j = 0; j < inputs[inputNum].numOutputs; j++) {
            
            byte outputNum = inputs[inputNum].outputs[j];
            Serial.print("outputNum: ");
            Serial.println(outputNum);
            
            byte outShiftRegister = (outputNum/8);
            Serial.print("outShiftRegister: ");
            Serial.println(outShiftRegister);
            
            byte outShiftBit;
            if (outShiftRegister == 0) {
              outShiftBit = outputNum;
            }  else {
              outShiftBit = outputNum - (outShiftRegister * 8);
            }
            Serial.print("outShiftBit: ");
            Serial.println(outShiftBit);

            // now we can write the output bit
            bitWrite(outBytes[outShiftRegister], outShiftBit, bitState);
          }
          Serial.println();
        }
      }
    }
  }
  
  for (int i = 0; i < NUM_SHIFT_OUT; i++) {
    Serial.print("Output shift register ");
    Serial.print(i);
    Serial.print(": ");
    prntBits(outBytes[i]);
  }
}

void prntBits(byte b)  //function to print bytes in binary form
{
  for(int i = 7; i >= 0; i--)
    Serial.print(bitRead(b,i));
    Serial.println();  
}

void loop() {
  // nothing to do here, running once in setup for testing
}

myTypes.h

typedef struct {
  bool state; // not used yet, may be used to work out short press / long press
  byte numOutputs;
  byte outputs[5];  // max 5 outputs per input
} input_t;