TTP229BSF Capacitive Touch button 1 not reading

Hi there helpful Arduino forum folk. I'm hoping someone can point out the hopefully easy to solve error I've made with my project.

I have followed the instructions of using more than one ttp229 cap sensor by using a 4051 multiplexer, found here Instruction: One or more 16-key capacitive keypads with Arduino Uno & SPI - #3

I have the first 229 chip set to operate in 16 keys input mode, the second in 8 key mode. I have made a custom PCB after testing with the keypad modules you can get on ebay.

In making the PCB I had to route traces to suit the layout so key order is remapped thanks to some code a forum member suggested a couple of months back (uint16_t reorder(uint16_t inputWord))

The project is a simple 16 step beat sequencer for a music installation, and the electrode attached to TP15 of the cap IC will not read for me. I no longer have the keypad modules set up in my test rig and if the problem happened then I didn't detect it, but I don't think I did.

So this could be a hardware problem or a software problem, which is why I've attached the schematic I used and the code. Everything else works from a hardware perspective, though my code is still being worked on.

The relevant piece of code is the part below the "READ STEP INPUT BUTTONS AND COUNT PRESSES" comment. I considered editing my code to remove all the other stuff but didn't in case there's something interacting with the button code I'm missing.

My hope is I've made some silly mistake with a a for loop, or an array is too small or some other thing like that, but I've tried everything I can think of. Hence, coming here and baring my soul to the critical eyes of the forum experts!

/*


code for one "Unit" of eight 16 step sequencers. One arduino Nano (addressed as Pro Mini in Arduino IDE due to CH340 chip issues)
handles touch interface (2x TTP229), neopixels, network communication and power.
Another Arduino (actual Pro Mini, programmed via 6 pin header and red USB-FTDI) handles Mozzi based audio, triggered via hardware Serial port TX on sequencer Nano to RX on Pro Mini.

working:
Multiply: when pressed for the first time, multiply tempo by 2. Second press, multiple based tempo by 0.5 for half speed. Thrird press, base tempo

Direction: toggle between forward and reverse behaviour of the sequencer count

IO: toggle between sequencer running (lights and sound) and not. Sequence input should still work but not playback

Clear: toggle on and off to erase steps as it plays through the loop

Elect: Concept: the 8 players can vote for a leader who can control the other sequencers from one unit. They should be able to mute and reverse.
this needs a reliable network to function but the idea is to hold this button (which will require timing the button press?)which will turn off
the led display of the sequencer working, but not the sound, and display every second of the 16 ring leds to indicate the 7 other units. You can't vote for
yourself so the 12 o'clock position should be indicated with a different colour led. If one of the 8 units receives 5 or more votes then they become leader
for a set duration. The more I think about this the more I think it's for September as it's going to take ages to code this.


Trigger: acts as a realtime sample trigger. TRY step input whne playing

Leds are Green Red Blue GRB

bug: step button 5 doesn't work. LED is fine but not this? Something to do with re-ordering of buttons from cap touch sensor. Tried fiddling with the for()loop to no avail.

autoClear: prevents the installation being left on interminably and creating a nuisance. Resets to default settings after 100 bars

to do: elect...figure out how this will work in practice

From : kinda stuck in a rut with this. Initial state should mean that the sequencer is in defualt run mode but instead it goes straight into fromMode. Once in fromMode you can pick the start point
    but the leds are messed up. Desired behaviour:
    press From button. Enter fromMode, where the current from point is lit in blue, the other steps (by defualt, all the other 15 positions) are lit green.
                      Automatically enter editing mode which polls buttons states. Once a step is chosen, light the selected step blue and all other buttons green.
                      To exit, it would be nice if it just automatically went back to normal sequencer operation after one revolution, or press the From button again

                      bugs: step input still works in fromMode. 
                      - once from point is selected, no steps before it are accessible to change it again, only steps within the new loop bounds



*/

#include <SPI.h>

const uint8_t MUX_A = 5;  // Used to select mux data channel
const uint8_t MUX_B = 6;


uint16_t key_map_8229_0;  // The scanned keys
uint16_t key_map_8229_1;

//note that silkscreen on PCB does not match the functions: Mute =ONOFF , Direction = ELECT, To= FROM, From = TO, Speed = MULTIPLY, Trigger = DIRECTION, Elect = CLEAR, Clear = TRIGGER
#define FROM 0       //from
#define ONOFF 1      //mute
#define DIRECTION 2  //direction
#define TO 3         //to
#define TRIGGER 4    //trigger
#define CLEAR 5      //clear
#define ELECT 6      //elect
#define SPEED 7      //speed

#define halfSpeed 1
#define normalSpeed 0
#define doubleSpeed 2

// inputWord is your incoming 16-bit value
uint16_t inputWord;
// Remapped output value
uint16_t outputWord = 0;

int stepNum[16];
int stopCount;  //save variable count for when playhead is stopped
#include <Adafruit_NeoPixel.h>
#define LED_PIN 4
#define LED_COUNT 16

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);


unsigned long pixelPrevious = 0;    // Previous Pixel Millis
unsigned long patternPrevious = 0;  // Previous Pattern Millis
int patternCurrent = 0;             // Current Pattern Number
int patternInterval = 5000;         // Pattern Interval (ms)
int pixelInterval = 50;             // Pixel Interval (ms)
int pixelQueue = 0;                 // Pattern Pixel Queue
int pixelCycle = 0;                 // Pattern Pixel Cycle
uint16_t pixelCurrent = 0;          // Pattern Current Pixel Number
uint16_t pixelNumber = LED_COUNT;   // Total Number of Pixels
const long tempo = 100;             // interval at which to blink (milliseconds)
long interval = 0;                  // interval at which to blink (milliseconds)

int loopStart = 0;
int loopEnd = 16;
int count = 0;
int reverseCount = 16;

int autoClear = 0;     //if same loop plays a certain number of times it gets wiped to prevent leaving the installation running when no-one is there.
int resetCount = 100;  //the number of bar repetitions before global parameter reset occurs

int onOffCounter = 1;  //on-off function button
int multiplyCounter = 0;
int clear = 0;
int trigger = 0;
int direction = 0;
int loopFrom = 0;
int fromMode = 0;  // flag for when loop start position is chosen by pressing step position.
int toMode = 0;
int ledOrder[16] = { 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5 };  // fixing layout of leds. First led in the chain is
//in position 13 in relation the touchPCB, and are also running anti-clockwise so this fixes that issue
// Variables will change:
int buttonPushCounter[16];  // counter for the number of button presses
int buttonOrder[16];        // = { 12, 13, 14, 15, 0, 2, 3, 1, 7, 6, 5, 4, 8,  11, 10, 9 };
int buttonState[16];        // current state of the button
int lastButtonState[16];    // previous state of the button
int functionNames[8] = { FROM, ONOFF, DIRECTION, TO, TRIGGER, CLEAR, ELECT, SPEED };
int functionButtonState[8];
int functionLastButtonState[8];
int frombuttonState;               // current state of the button
int lastFromButtonState;           // previous state of the button
unsigned long previousMillis = 0;  // will store last time LED was updated

int frontRemainder = 0;
int backRemainder = 0;
// setup() function -- runs once at startup --------------------------------
void setup() {

  pinMode(MUX_A, OUTPUT);
  pinMode(MUX_B, OUTPUT);

  fromMode = 0;
  Serial.begin(9600);

  SPI.begin();
  SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE3));
  delay(500);  // Allow 500ms for the 8229BSF to get ready after turn-on

  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  strip.begin();             // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();              // Turn OFF all pixels ASAP
  strip.setBrightness(200);  // Set BRIGHTNESS to about 1/5 (max = 255)
}

// loop() function -- runs repeatedly as long as board is on ---------------
void loop() {


  //Serial.println(fromMode);
  // Get data frpm the Keypads
  spi_scan_8229_0();
  spi_scan_8229_1();

  // MULTIPLY TEMPO CODE (half / normal / double)
  if (multiplyCounter == halfSpeed) {
    interval = tempo * 2;
    //Serial.println("half");
  }
  if (multiplyCounter == normalSpeed) {
    interval = tempo * 1;
    //Serial.println("normal");
  }
  if (multiplyCounter == doubleSpeed) {
    interval = tempo * 0.5;
    //Serial.println("doulbe");
  }

  //SEQUENCER BUTTONS LOOP
  if (fromMode == 0 && toMode == 0) {  //if From and To modes aren't engaged then run the main show

    unsigned long currentMillis = millis();

    if (currentMillis - previousMillis >= interval) {

      // save the last time you blinked the LED
      previousMillis = currentMillis;
      //Serial.println(count);

      //FORWARD DIRECTION PLAYBACK
      if (onOffCounter == 1 && direction == 1) {  //main play control.
        count++;
        //REAL TIME TRIGGER MESSAGE
        //from Trig function key
        if (trigger == 1) {              //
          Serial.print(1);               // trigger the drum in realtime, but quantised by the division of the sequencer
          buttonPushCounter[count] = 1;  //TRY THIS FOR LIVE STEP INPUT
        }
        if (count >= loopEnd) {  //if count goes beyond its bounds reset to whatever value loopstart is
          count = loopStart;
          autoClear++;  //keep track of how many loops have played
                        //Serial.println(autoClear);
        }
        //Playhead indicated with WHITE led chaser in currently selected direction
        strip.setPixelColor(ledOrder[count], strip.Color(200, 200, 200));
        strip.show();
      }

      //RESET FUNCTION

      if (autoClear >= resetCount) {
        for (int i = 0; i < 16; i++) {  //resets the sequencer clearing every step
          buttonPushCounter[i] = 0;
        }
        direction = 1;         //reset direction to forward
        interval = tempo * 1;  //reset speed to normal
        autoClear = 0;         // reset counter
        loopStart = 0;
        loopEnd = 16;
      }


      ///////////////////////////MAIN TRIGGER TO PLAY SAMPLE///////////////////////////////////////////////////////

      Serial.print(buttonPushCounter[count]);  //this is the main line to trigger drum samples on Arduino Pro Mini via serial

      ///////////////////////////MAIN TRIGGER TO PLAY SAMPLE///////////////////////////////////////////////////////


      //REVERSE DIRECTION PLAYBACK
      if (onOffCounter == 1 && direction == 0) {
        count--;                         //decrement counter
                                         // Serial.println(count);
        if (trigger == 1) {              //
          Serial.print(1);               // trigger the drum in realtime, but quantised by the division of the sequencer
          buttonPushCounter[count] = 1;  //TRY THIS FOR LIVE STEP INPUT
        }
        if (count < loopStart) {
          count = loopEnd - 1;  //minus one stops decrementer going into non-existent minus numbers and confusing the sequencer.
          autoClear++;          //while this works, it auto resets every n number of bars regardless of whether anything is in the sequence.
          //would be better to have it only start counting once some user input has been made.
        }
        //Playhead indicated with WHITE led chaser in currently selected direction
        strip.setPixelColor(ledOrder[count], strip.Color(200, 200, 200));
        strip.show();
      }
    }



    //STOPPED BEHAVIOUR
    if (onOffCounter == 0 && direction == 1 || direction == 0) {  //if transport stopped in either forward or reverse playback direction
      stopCount = count;                                          //log the current position in the step count so this can be indicated with led when transport stopped.
      strip.setPixelColor(ledOrder[stopCount], strip.Color(200, 200, 200));
      strip.show();
    }
    if (onOffCounter == 0) {
      //REAL TIME TRIGGER MESSAGE
      //from Trig function key
      if (trigger == 1) {   //
        Serial.println(1);  // trigger the drum in realtime, but quantised by the division of the sequencer
      }
    }

    // CLEAR
    if (clear == 0) {  //erase step input data. Indicate erase mode by RED rotating led ring
      strip.setPixelColor(ledOrder[count], strip.Color(0, 200, 0));
      strip.show();
      buttonPushCounter[count] = 0;
    }


    // LED POSITION ANIMATION
    //turns off the last led so they don't all stay on after one iteration through loop
    if (buttonPushCounter[ledOrder[count]] == 0) {
      strip.setPixelColor(count - 1, strip.Color(0, 0, 0));
      strip.show();
    }

//   READ STEP INPUT BUTTONS AND COUNT PRESSES
    for (int i = loopStart; i < loopEnd; i++) {  //for (int i = ; i < 16; i++) {
      //read the values of the 16 (re-ordered) sequencer input buttons.
      buttonState[i] = bitRead(reorder(key_map_8229_0), i);

      if (buttonState[i] != lastButtonState[i]) {
        // if the state has changed, increment the counter
        if (buttonState[i] == 0) {

          // if the current state is HIGH then the button went from off to on:
          buttonPushCounter[i]++;
        }
      }
      // save the current state as the last state, for next time through the loop
      lastButtonState[i] = buttonState[i];


      //STEP VELOCITY LED COLOURS
      //These if statements determine colour of button presses in Sequencer step velocity layers. Yellow = LOW, red = HIGH

      if (buttonPushCounter[i] == 0) {
        strip.setPixelColor(ledOrder[i], strip.Color(0, 0, 0));
        strip.show();
      }
      if (buttonPushCounter[i] == 1) {  // yellow or low velocity
        strip.setPixelColor(ledOrder[i], strip.Color(200, 250, 0));
        strip.show();
      }
      if (buttonPushCounter[i] == 2) {  //orange or medium velocity
        strip.setPixelColor(ledOrder[i], strip.Color(40, 250, 0));
        strip.show();
      }

      if (buttonPushCounter[i] == 3) {
        buttonPushCounter[i] = 0;
      }
    }
  }

  if (fromMode == 2) {  //inital fromMode state
    Serial.println("from mode 1");
    strip.setPixelColor(ledOrder[loopStart], strip.Color(0, 0, 250));
    strip.show();
    for (int i = loopStart + 1; i < loopEnd; i++) {  // Loop keeps playing but all LEDs go GREEN to show loop length, and From position flashes BLUE
      strip.setPixelColor(ledOrder[i], strip.Color(250, 0, 0));
      strip.show();
    }
    fromMode = 3;
  }
  if (fromMode == 3) {
    Serial.println("from mode 2");
    for (int i = 0; i < 16; i++) {  //when button is selected, new loopStart is set, and leds should indicate this

      //read the values of the 16 (re-ordered) sequencer input buttons.
      buttonState[i] = bitRead(reorder(key_map_8229_0), i);

      if (buttonState[i] != lastButtonState[i]) {
        // if the state has changed, increment the counter
        if (buttonState[i] == 0) {

          // if the current state is HIGH then the button went from off to on:
          buttonPushCounter[i]++;
        }
      }
      // save the current state as the last state, for next time through the loop
      lastButtonState[i] = buttonState[i];


      if (buttonPushCounter[i] == 1) {  // yellow or low velocity
        loopStart = i;

        strip.setPixelColor(ledOrder[loopStart], strip.Color(0, 0, 250)); //doesn't shut off previously selected loopStart positions
        strip.show();
        for (int i = loopStart + 1; i < loopEnd; i++) {  // Loop keeps playing but all LEDs go GREEN to show loop length, and From position flashes BLUE
          strip.setPixelColor(ledOrder[i], strip.Color(250, 0, 0));
          strip.show();
        }

        /////////////////////////// turn off leds before and after loop start and end if new endpoints selected
        if (loopEnd != 15) {
          for (int i = loopEnd + 1; i <= 15; i++) {
            strip.setPixelColor(ledOrder[i], strip.Color(0, 0, 0));
            strip.show();
          }
        }
        if (loopStart != 0) {
          for (int i = 0; i <= loopStart - 1; i++) {
            strip.setPixelColor(ledOrder[i], strip.Color(0, 0, 0));
            strip.show();
          }
        }

        fromMode = 0;
        //need to turn off leds outside of the loop length
      }
    }
  }


  // for (int i = loopEnd; i > loopStart; i--) {
  //   strip.setPixelColor(ledOrder[i], strip.Color(0, 0, 0));
  //   strip.show();
  //   Serial.println("turn off others");
  // }
  // FUNCTION BUTTONS LOOP
  /*
0 = multiply DONE
1 = clear DONE
2 = trig DONE
3 = direction
4 = from
5 = elect LATERZ
6 = onoff DONE
7 = to



  */

  // This for loop reads values from the function buttons to control how the sequencer operates
  for (int i = 0; i <= 7; i++) {
    functionButtonState[i] = bitRead((key_map_8229_1), i);
    //Serial.println(functionButtonState[i]);
    if (functionButtonState[i] != functionLastButtonState[i]) {
      // if the state has changed, increment the counter


      if (functionButtonState[2] == 0) {  // trigger drum pad
        trigger = 1;
        // Serial.println("trig");
      } else {
        trigger = 0;
      }

      if (functionButtonState[0] == 0) {  // mutliply pad
        multiplyCounter++;
        Serial.println(multiplyCounter);
        if (multiplyCounter > 2) {  //default behaviour where sequencer runs at "x1" speed
          multiplyCounter = -1;
        }
      }

      if (functionButtonState[1] == 0) {  // clear pad
        //ideally this would work as a function button, so that when it's pressed and the step keys that are to be cleared as selected, they are turned off / buttonPushCounter set to 0
        clear++;
        //Serial.println("clear");
        if (clear > 1) {
          clear = 0;
        }
      }

      if (functionButtonState[3] == 0) {  // direction pad
        //
        direction++;
        //Serial.println("direction");
        //Serial.println(direction);
        if (direction > 1) {
          direction = 0;
        }
      }

      if (functionButtonState[7] == 0) {  // loopFrom pad

        fromMode++;
        Serial.print("from");
        Serial.println(fromMode);
        if (fromMode > 2) {
          fromMode = 0;
        }
      }

      if (functionButtonState[4] == 0) {  // loopT0 pad

        // loopTo++;
        // Serial.println("to");
        // //Serial.println(direction);
        // if (loopTo > 1) {
        //   loopTo = 0;
        // }
      }
      if (functionButtonState[6] == 0) {  //on off pad
                                          // if the current state is HIGH then the button went from off to on:
        onOffCounter++;
        //Serial.print("onOff counter = ");
        //Serial.println(onOffCounter);

        if (onOffCounter >= 2) {
          onOffCounter = 0;
        }

        //Serial.print(i);
        // Serial.print("number of button pushes: ");
        //Serial.println(buttonPushCounter[i]);
      }
    }
    functionLastButtonState[i] = functionButtonState[i];
  }
}  //END LOOP




// READING TTP229 capacitive ICs
//for Sequencer
void spi_scan_8229_0() {
  // Select MUX channel for 8229_0
  digitalWrite(MUX_A, 0);
  digitalWrite(MUX_B, 0);

  // Get state of all the keys as a 16 bit integer
  key_map_8229_0 = SPI.transfer16(0);
}

//for Function keys
void spi_scan_8229_1() {
  // Select MUX channel for 8229_1
  digitalWrite(MUX_A, 1);
  digitalWrite(MUX_B, 0);

  // Get state of all the keys as a 16 bit integer
  key_map_8229_1 = SPI.transfer(0);
}

//re-ordering physical layout of keys vs desired UI location
uint16_t reorder(uint16_t inputWord) {
  // Remapped output value
  uint16_t outputWord = 0;

  // Relates input bit positions to outputs
  // e.g., output bit 0 is input bit 3
  const byte mapbit[16] = { 11, 12, 13, 14, 16, 1, 2, 0, 6, 5, 4, 3, 7, 10, 9, 8 };  //{12, 13, 14, 15, 0, 2, 3, 1, 7, 6, 5, 4, 8, 11, 10, 9};

  for (int i = 0; i < 16; i++) {  //something iffy happening with TP15(interface=5). Either nt reporting or conflicting with 6
    if (inputWord & (1 << mapbit[i])) {
      outputWord |= (1 << i);
    }
  }
  return outputWord;
}
[TouchPCB v47.pdf|attachment](upload://iMJgl3LlZWLEjCjZxnqWf7RHmXf.pdf) (284.8 KB)
![20241006_121955|500x500](upload://nzu33CP4lPrZ3GjiraFNHemHD5Q.jpeg)

TouchPCB v47.pdf (284.8 KB)
apologies for the multiple posts, I uploaded both of these in the initial post but they didn't appear for some reason.

... Look at your reorder:508... if it truly is a bit mapping for 16 bits it should only include bits 0 thru 15... I don't see 15 but I do see a 16... this does not match patterns I would expect. If my quick glance is correct, it is hard to left shift 16 bits when your valid range is 0 to 15.