Puzzling behaviour in Morse code detector

I have written a code to decode simple Morse messages for the nine digits. I am taking inputs from a piezo, and when the voltage is greater than a threshold, it registers a knock. I am distinguishing between a dot and a dash according to the timing between knocks on the piezo.

So, I have a counter and a while loop that is supposed to ensure that I get five valid knocks, which I then represent as an array of ones or zeros depending on whether the knock was a dot or a dash. The code I’m using is the following :

int knockSensor = A0;               
unsigned char val = 0;
int statePin = LOW;
int THRESHOLD = 60;
int counter = 0;
float time1 = 0;
float time2 = 0;
float eventtime=0;
int sum;
int i;
int number = 0;
int array[5]={0,0,0,0,0};
void setup() {
  Serial.begin(9600);
}

void loop() {  
  while (counter<5){
    val = analogRead(knockSensor);     
    time2 = time1;
    sum = 0;
    if (val >= THRESHOLD){
      counter = counter+1;
      number = number+1;
      statePin = !statePin;
      time1 = millis();
      eventtime = (time1-time2)/1000;
      if(eventtime>0.4) 
        array[counter] = 1;
      else 
        array[counter] = 0;
        
      Serial.println(array[counter]);
    }
  }
      
    for (i=0;i<5;i++)
      Serial.print(array[i]);
      Serial.println(" ");
 
  counter = 0;
  for (i=0;i<5;i++)  array[i] = 0;
  
  delay(20);  // we have to make a delay to avoid overloading the serial port
}

Expected output : Nothing happens for the first four knocks. At the fifth knock, an array to be printed, telling me the sequence of dots and dashes using ones and zeros.

Actual output : I get an array printed at every press of piezo, and the array reads 00000 for a dot (a dot would mean time between knocks is less, and a dash would mean a longer time between knocks.) and 01000 for a dash.

This behaviour is puzzling me quite a bit, and I would appreciate some help. I feel it’s something to do with timing? The fact that it’s a loop within the overall void loop of the code?

Thanks!

As soon as val crosses your threshold value, the if clause will be executed on every iteration of loop, until val drops below your threshold value. It's not timing the duration of, or interval between, dots and dashes.

Regards, Ray L.

But, the val will go below the threshold anytime I don't knock the piezo. Then, the val should (and does) go to zero again just after the knock. So that I'm able to get five valid knocks.

   if (val >= THRESHOLD){
      counter = counter+1;

As RayLivingston said, you are not waiting for the val to go below the threshold. Thus it will, almost instantly, fill up all the spots. You need to detect, once it has exceeded the threshold, that it has gone back down below it, before proceeding.

So would I have to detect the falling below the threshold just after detecting value greater than threshold? like so:

if (val >= THRESHOLD)
      if (val<THRESHOLD){
      counter = counter+1;
      number = number+1;
      statePin = !statePin;
      time1 = millis();
    
      eventtime = (time1-time2)/1000;
      if(eventtime>0.4) 
        array[counter] = 1;
      else 
        array[counter] = 0;
        
      Serial.println(array[counter]);
    }
if (val >= THRESHOLD)
      if (val<THRESHOLD){

Well, no. The value is hardly going to be above and below the threshold at the same moment, is it?

More like:

void loop() {  
  while (counter<5){
    val = analogRead(knockSensor);     
    time2 = time1;
    sum = 0;
    if (val >= THRESHOLD){
      counter = counter+1;
      number = number+1;
      statePin = !statePin;
      time1 = millis();

      while (analogRead(knockSensor) >= THRESHOLD)
         { }  // wait till it drops below the threshold

...

Thank you very much. This has solved my problem.