Ain't Broken, Still Want to Fix. Incorporating Interrupt Function

Just got a keypad of 16 capacitive buttons and finally got it working over a 2-wire system. I don't think the way I did it is at all close to the best possible way to do it, the array bit-at-which-position referencing seems quite roundabout. It spits out a bit per button, active low, so a string (using the term colloquially) of 16 1's is nothing pressed, each button pressed changes its position to 0.
I've only written enough to make some button affect one LED just as a marker. As shown, keypad button 10 toggles LED on and off.

I see that it is possible to move all of this business to an interrupt so that it doesn't have to constantly poll the keypad for changes. I tried that one code by tomschlitz or something like that for interrupt but couldn't get it working since it as written for a Leonardo but switching around pins to the Uno's 2/3 got no response.

So, here's the working code I made, I've never used an interrupt other than for a single button that keeps an LED on as an exercise. I need help understanding what parts become a function, what parts move to setup, how I can still have the Serial.print since it shouldn't be in the interrupt, and what my trigger would be since there's 16 buttons instead of 1. Need help learning how to make this suck less, basically.

/*
  16-key Capacitive Keypad (type TTP229-B)

  Jumpered "3", "4", and "5" on keypad (TP2, TP3, and TP4). Jumpers ground the pins through on-board 1MOhm resistor. 
  "3" for 16-key function
  "4" and "5" for 'All multikeys: one group (16 keys)'

  VCC - 5V
  GND - GND
  SCL - D2
  SDO - D3
*/

#define clockPin 2
#define dataPin 3
#define ledPin 11

void setup(){
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  pinMode(clockPin, OUTPUT);
  digitalWrite(clockPin, HIGH);
  pinMode(dataPin, INPUT);
}

void loop() {
  byte inputArray[16];
  for (byte i = 0; i < 16; i++){
    digitalWrite(clockPin, LOW);    
    byte databit = digitalRead(dataPin);   
    digitalWrite(clockPin, HIGH);  
    inputArray[i] = databit;
    Serial.print(inputArray[i]);
  }
  Serial.println();

  /*
    //Begin Momentary
    if (inputArray[2] == 0)
    digitalWrite(ledPin, HIGH);
    else
    digitalWrite(ledPin, LOW);
    //End Momentary
  */

  //Begin Toggle
  byte static prevState = 1;
  byte static state = 1;
  if (inputArray[9] == 0 && inputArray[9] != prevState) {
    digitalWrite(ledPin, state);
    state = !state;
  }
  prevState = inputArray[9];
  //End Toggle
} //End loop

Have you checked This ?

Yeah, couldn't get it to work for whatever reason, will stare at it more

Check if you have an oscillatorscope that

SDO pin generating a pulse of width DV (typ. 93uS) to signal data available.

Have you soldered all the right connectors?

No oscope on hand, entirely possible that it was jumped incorrectly, hard to tell what positions are referenced as the numbers can mean diff things.
But for that particular code I had it according to the pic.

If no oscilloscope at hand - Given its supposed to be a "long" low of 93uS, I guess you could just try to catch that with a port read in the loop() just to ensure the iSR would really happen

Got that interrupt code working, could've just been a baud rate mismatch from all the diff codes I was trying.

I'm still very green with the bitshifting so I can't understand what's going on yet. I think it's doing something similar to the array I wrote out but with bitshift math and turning the 16-digit binary into decimal. Big numbers show. Also, "ATOMIC_BLOCK(ATOMIC_FORCEON)" what is?

The bit shifting thingy is indeed just to save space. 16 one or zero can fit into each bit of an int so no need of a big array of 16 bytes - saves memory if you are constrained

Two sample ways to do this

Let's say v is the value where you want to store the 16 states (and I'll do it with 8 because I'm sure you'll get the idea)

V = 0 ---> 0000 0000 binary representation

The way it works int a for loop is you start filling those bust from the right side

Shift things to left 0000 0000 then put a 0 or 1 to the right bit, so or (or add 0 or 1) to v, let's say it's 1 --> 0000 0001

Shift things left 0000 0010 then put a 0 or 1 to the right bit, so or (or add 0 or 1) to v, let's say it's 1 --> 0000 0011

Shift things left 0000 0110 then put a 0 or 1 to the right bit, so or (or add 0 or 1) to v, let's say it's 0 --> 0000 0110

Shift things left 0000 1100 then put a 0 or 1 to the right bit, so or (or add 0 or 1) to v, let's say it's 1 --> 0000 1101

Etc.. When you are done with your 16 iterations, each bit has been set to what you wanted.

Shift to left can be done either by the << operator or just by multiplying by 2 given the binary representation

Another way to do this is what he has in his code is more about bit masking, you build a mask with the value you want to set at the right bit position and zeros everywhere else and then you
* *OR* *
that (| operator) with your value (you can clear a bit if you were to use the binary
* *AND* *
( & operator) and a mask with 1s everywhere and a 0 (or 1) as the bit you want to clear (or not) in the right position)

He does have a for loop from o to 15, each representing a position in the bits and he does touchVal |= (digitalRead(sdo_Pin) << i);

The way that works is the digitalRead returns 0 or 1. --> so the last bit to the right of that memory is 0 or 1 and all other bits are 0. Because of the resulting storage variable, the compiler decides to use integer so 16 bits and let's say the digitalRead returned 1, so its representation is 0000 0000 0000 0001

Then the << i thingy shift left all the bits in the integer by i positions. So assuming i is 3 (so 4th iteration of the loop), we end up with

0000 0000 0000 1000

then he does touchVal |=

So this is the same as touchVal = touchVal | 0000 0000 0000 [color=red][b]1[/b][/color]000;

A logical OR only sets a 1 in the result if the value of a bit is 1 (and does nothing for the 0s), so what it ends up doing is setting the 4th bit of touchVal to whatever was read with the digital read and not mess wit the other bits already in touchVal

Repeating this 16 times sets all the bits to the 16 digital reads, each one in the right position. Because you use only one variable in the loop and bit operator, this is likely to be quite efficiently optimized by the compiler using a 16 bit register and 1 cycle instructions to do bit shifting and the or-ing


The ATOMIC_BLOCK(ATOMIC_FORCEON) stuff comes from what he included #include <util/atomic.h>

This ATOMIC_BLOCK macro is used to declare a block of code that is guaranteed to be executed atomically i.e. It won't be interrupted. (the macro manages this for you: when you enter the block, the Global Interrupt Status flag in SREG is disabled, and re-enabled upon exiting the block).

So he is asking that {touchValc = touchVal;} does not get interrupted. That might seems weird at first given its just one single line of code but When you deal with 16 bit values assignment in memory, it is two 8 bits data movement from one memory location to the other memory location. If you were to be interrupted at the wrong place you might have non matching most significant a least significant bytes and if you are really unlucky that will happen when a 1 was moving from the low byte to the high one

Say you have 0000 0000 1000 0000 to copy

So you read the high part 0000 0000 and copy it it the night part of the destination 0000 0000

Then bad luck you get interrupted and say the interrupt shifts content left by 1 bit, so now your memory holds

0000 0001 0000 0000

Then you resume from the interrupt, read the low part which is now 0000 0000 and copy that to the destination low part --> big issue, your destination is now 0000 0000 0000 0000

Makes sense?

Makes more sense than it did before reading that. Thanks.

How can I analyze/reference the entirety of each 16-bit result?

My cheaty way was just doing "if the bit at the third position is 0, do stuff"
How can I say "if the whole result from this time through the loop is 0110100100001111, do stuff"?

I have the same question for my RF transmitter/receiver that outputs a message by making a character array one character at a time.

good stuff to read on bitwise operation is here

How can I say "if the whole result from this time through the loop is 0110100100001111, do stuff"?

well that's the beauty of it

you just need to know the equivalent of your comparison value as an unsigned int in Hex for example

(0110100100001111)base 2 = (690F)base 16

so if you have an unsigned int [color=blue][b]val[/b][/color]; in which you built up the 16 bits, then it's just a compare

if (val == 0x690F) { // check if all the bits match 0110100100001111 in one go!
    // got the right match
}

and of course you can still do the

"if the bit at the third position is 0, do stuff"

by using one of the arduino's bitwise macros

if (bitRead(val, 2) == 0) {
  // the 3rd bit is null, do something 
}

if individual bits are important

I was hoping that was the case.

So the 16 bits of an int is a single variable getting its bits modified, whereas an array of 16 bytes like I was doing is 16 'variables' in an array that can't be read through as one consecutive code? Or can that still be done with an intermediary translation to reprint each array piece concatenated into a singular string?

This is more for the RF project I have:

/*
  RF Transmitter on Nano with Button

   Transmitter code. Can only send char string as array. Max 27 characters.
   Pressing button will send message once. Must release and press again to send again.

   Transmitter on 5V, GND, and Data into pin ??
   Works on Pin 13, simultaneously on ICSP's SCK.
   Pin 12 = ICSP's MISO
   Pin 11 doesn't work either way (board or MISO). 11 powers MOSI during LED HIGH/LOW test. TIMER CONFLICT

   Button between GND and pin 3

   LED ANODE to 3v3, CATHODE into A0 (Write LOW to turn on)

   Nano can be powered with 9v: + into VIN, - into GND

   No antenna ~ 20meters line of sight
   173mm magnet wire straight antenna ~ 200meters line of sight
*/

#include <VirtualWire.h>
const byte button = 3;
const byte ledPin = A0;
const byte transmit_pin = 13; //Doesn't need to be PWM
//const int transmit_en_pin = 3;

byte lastButtonState = 1;
void setup()
{
  pinMode(button, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  // Initialise the IO and ISR
  vw_set_tx_pin(transmit_pin);
  //  vw_set_ptt_pin(transmit_en_pin);
  vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000);   // Bits per sec
}

void loop()
{
  byte buttonState = digitalRead(button);
  if (buttonState == 0 && buttonState != lastButtonState) {
    char msg[12] = {'H', 'e', 'l', 'l', 'o', ',', 'M', 'i', 'c', 'k', 'e', 'y'};
    digitalWrite(ledPin, LOW); // LED to show transmitting
    vw_send((uint8_t *)msg, 12);
    vw_wait_tx(); // Wait until the whole message is gone
    delay(500);
    digitalWrite(ledPin, HIGH);
  }
  lastButtonState = buttonState;
}

So how would I do "if message received is <Hello,Mickey>, do stuff", because just like my first attempt with this 16-key thing, I'm just doing "if array[6]='M', do stuff"

So the 16 bits of an int is a single variable getting its bits modified, whereas an array of 16 bytes like I was doing is 16 'variables' in an array that can't be read through as one consecutive code?

that's exactly right - there is no basic way to compare 2 arrays in one simple operation. (thought you could compare with a function call (memcmp()) the full memory associated to your array with another array, but basically that does a for loop to compare each byte in memory).

So how would I do "if message received is <Hello,Mickey>, do stuff", because just like my first attempt with this 16-key thing, I'm just doing "if array[6]='M', do stuff"

well if you have a c-string (i.e. char array, null terminated) holding "Hello,Mickey" then you would use high level C-function such as strcmp()()

int strcmp ( const char * str1, const char * str2 );

which behind the scenes takes each char from str1 and compares with the matching byte of str2


in your code above

    char msg[12] = {'H', 'e', 'l', 'l', 'o', ',', 'M', 'i', 'c', 'k', 'e', 'y'};

is OK but still a pb because you can't use C functions on this msg buffer because it's not null terminated.

    char msg[] = {'H', 'e', 'l', 'l', 'o', ',', 'M', 'i', 'c', 'k', 'e', 'y', '\0'}; would do or easier

    char msg[] = "Hello,Mickey";

then if buff is holding the message you received as a c-string you would do

if (! strcmp ( buff, msg)) {
    // strings match (strcmp returns 0 on match) - do something
}

or if you only want to compare with something you know

if (! strcmp ( buff, "Hello,Mickey")) {
    // strings match (strcmp returns 0 on match) - do something
}