How to hold MAX value from analog read.

This is a followup from this
http://forum.arduino.cc/index.php?topic=207345
where I get stream of values coming from piezo and my goal is to play MIDI note according to the highest pressure value of given hit. Since piezo(and audio) acts like a wave I need to isolate just the max value and ignore the rest.
I was able to build a patch with Isadora(troikatronix) in few steps to work very well. However I can not use computers for this installation/show.
So here is what I want to achieve with Arduino:
read analog pin - it will be a stream of values rising very rapidly to maximum according to the force of a hit and decaying in about 1/2sec to 0.
record the rising stream
hold the last highest value before the decay
reset when values start rising again
use the held highest value to calculate MIDI note number
What functions I need to use in the code to make this?

read analog pin - it will be a stream of values rising very rapidly to maximum according to the force of a hit and decaying in about 1/2sec to 0.

If you know how to read the analog pin, then saving the value in a variable should be pretty easy. Comparing that value to another value is trivial. If the value is higher, saving that in place of the previous highest value is trivial.

The only tricky parts are knowing when to reset the highest value variable to 0, and knowing when to actually do something with the highest value.

Perhaps you could note when the current reading is some number of steps lower than the highest reading. When that happens, use the highest value.

Knowing when to reset the highest value to 0 is a bit trickier. Resetting when the values start rising again is not enough, since this assumes a constantly rising set of values, followed by a constantly falling set of values. It is unlikely that the values are strictly increasing followed by strictly decreasing. Resetting when the values increase above some threshold after falling below some lower threshold might work.

OK, it starts making sense.
In Isadora I use tool ”inside range" that gives me an output as trigger when values are entering and exiting the range. I am using exit trigger.

So here is a code I was able to put together

const int piezo = 0;  // the piezo is connected to analog pin 0
int Old=0;
int New=0;

int threshold=55;      
void setup() {

  Serial.begin(9600); 
  Old = 0;  //set Old to 0
}

void loop()  {
  
  New = analogRead(piezo);   // read input pin0 into New

  if(New > Old)

    Old = New;  // put new analog value into Old
  if (New < Old) // if New starts to go down 

    Serial.println(Old);  //print Old ei. last highest value
  delay(10);

  if(New <threshold)  //if New falls below threshold
      Old = 0;  // zero the Old


   if(New > threshold) //when New will rise over threshold
        Old = New;  //start put new values to Old again

    }

Could someone explain what this code does(different I think it does)?
I get values with Serial Monitor:
499
252
331
261
192
144
101
77
58
So it seems to give me the highest reading, but after that not what I thought it will. Looks very similar to the readings without any functions - analog reading strait.

Funny, it's doable in few steps with computer and probably not at all with Arduino.

Arduino IS a computer. So whatever program you've written on "computer" you repeat in Arduino. But the differnece isn't Arduino vs "Computer". It is probably the language you used on "computer" (an Excel spreadsheet, Mathlab, .net ... ?) and you have difficulty with the C/C++ language used on the Arduino.

What was the soultion that worked on "computer"?

Thanks Msquare!
I understand that Arduino is a computer - a tiny one and it needs programs(codes) to run as any other computer. And I have difficulties to use C++ indeed. The way I learn easy is through visual and I have difficulties to write ”command line” always. So I was hoping to tackle it a bit different this time. I wrote the code at the best I was able to and asked help for explaining what I had written. But I get no replies - so either I have written something very stupid or what I want to do is too complicated.
The app I use for my works is Isadora where author have done all code writing and developed visual blocks that do stuff with inputs/outputs.
Look at the picture I attached to see what I am doing with Isadora>

What values do you get after 58, your readings are steadily decreasing and I wonder if it's something to do with the threshold part.

Also I would print out every value read so you can see what value does what, or would that be too much data?

rising very rapidly to maximum according to the force of a hit and decaying in about 1/2sec to 0.

Is the decay constant,or does it "ring", I wonder if you are detecting a series of peaks as the signal decays.


Rob

After 58 it will fall under threshold.
I tried to print out every step. Actually every step with separate code. But SerialMonitor will scroll the values too fast if I use meaningful delay. If I use delay(100); I shall miss most of the values coming in. If there is a way to save the SerialMonitor output as a file for further inspection?
Actually the sensor rings like a bell indeed. So there will be some flux upwards. But if I reset the max "a bit later"(not on the first valu down) it should be OK. Like I do with Isadora - when values exit the range of 0-22.

If there is a way to save the SerialMonitor output as a file for further inspection?

Get a real terminal program (like Putty, Realterm, Tera Term), they can all save to a file.


Rob

So with further investigation(with the help of screenshot)>>>
I see the highest value followed by a much lower, then it go up again and decrease without bumps.
I think it is the piezo ringing however I have conditioned the signal to eliminate this with filter.
http://leucos.lstilde.org/wp/2009/06/piezo-transducer-signal-conditioning/

So the goal would be to use just the highest value before the much lower one and ignore all the rest down to 55 when loop will be reset.

Are you able to get a scope trace of the actual ADC input you are using?

But yes it does seem to be just recording the decay.

I'll have a look at the code again, but in essence it should do this I think

if (new > old)
old = new
else
// offhand I can't think how you would tell the difference between the first pulse
// and any subsequent ring pulses, so a delay is the best I can come up with now
delay (as long as required)

But I'm pretty sure you can be smarter then that, for example continue detecting maximums but if they are < the first one ignore them. Do that until the threshold is reached then start again.


Rob

It looked like a job for a state machine to me so I thought I'd play with that

const int piezo = 0;  // the piezo is connected to analog pin 0
int Old=0;
int New=0;

const int RING_TIME = 5;	// number of mS the pulse has to be stable 
// and below threshold before we reset and 
// look for a new pulse
int timer;

enum states {
	IN_IDLE,
	IN_FIRST_PULSE,
	IN_RINGING
};

int state  = IN_IDLE;

const int threshold=55;    

void setup() {

	Serial.begin(9600); 
	Old = 0;  //set Old to 0
}

void loop()  {

	New = analogRead(piezo);   // read input pin0 into New

	switch (state) {

	case IN_IDLE:
		if(New > Old) {
			Old = New;
			timer = 0;
			state = IN_FIRST_PULSE;
		}
		break;

	case IN_FIRST_PULSE:
		if(New > Old) {
			Old = New;			// pulse still rising
		} 
		else {
			state = IN_RINGING; // that's the first pulse topped out
		}
		break;

	case IN_RINGING:
		// now we don't care about rising or falling, just that 
		// we are stable below the threshold for a while
		if (New < threshold) {
			timer++;
			if (timer >= RING_TIME) {
				state = IN_IDLE;
			}
		} 
		else {
			// looks like anther ring pulse, restart timer
			timer = 0; 
		}
		break;

	}
	delay(1);

}

I have no idea if it will work but at least it compiles :slight_smile: Also I think it's much clearer where you are in the process, and depending on the frequencies involved it might be better to use delayMicroseconds().

It's probably still better to be clever about detecting the ring pulses, but that's for another day.


Rob

OK. Thank you very much for this. I works the best so far I have tried. When I test with Serial Monitor it will print just the one high value most of the time
With MIDI included it is not so clean cut any more. But it is pretty close to the goal to intuitively get higher not notes with stronger hits. One could not play a tune with this, but it was not expected either.
I played with this many hours today. I shall study the code more tomorrow. Looks promising.