Frequency "buffer-modulator" - big project done in steps

Hi geveryone.
I was once coding with arduino some projects, but i'm already long time out of this. Now i'm planning kinda big project, which idea is as follows:

I'm getting as input a frequency, which is produced by a button, which is pressed regulary at different, very low, frequencies.
This frequency will vary from like 0,1Hz up to 10Hz. While the most important range for me is 0,5Hz to 4Hz.
The output have to be modulated then. Therefore i have 3 ranges:

  • the frequency below 6,5Hz have to be output "as is". Means 1:1; input = output.
  • the frequency from 6,5Hz up to 8,5Hz have to be "buffered". Means, every input above 6,5 and below 8,5 have to be output as "flat" 6,5Hz.
  • the frequency above 8,5Hz have to be output "as is" again. 1:1; input = output.

For initial coding and testing purposes i got an arduino uno R3.
At the end the whole thing have to run on a digispark, because i'm very low on space and available energy.

I don't know, whether there was already someone doing similar projects, but i found like nothing about this.
So i'm now on collecting code snipplets to manage the whole project.
First of all i noticed, that it's not that easy to manage the "clicks".
I started to read HIGH inputs on my "buttonPin". But when reading an input pin at full spead, i can read 2x (or even more) HIGH input on one (slow) click. Reading it too slow (by using delays) i may miss one (or more) click at higher frequencies.
So i guess i have to look out explicit for the change HIGH to LOW to get every button release, as this will return a relieable and countable value for clicks.
Further i think i can use the exact "timestamp" of the button release n and the next n+1 to measure the time preiod and calculate the frequency.
But this is a future-problem for now ^^

Do you have a question ?

Maybe someone saw a similar project and can help be with the code.

For this time i try to code a "high-to-low-counter".
But most of my problems result from not knowing the syntax.
Therefore at exactly this problem the answer would be "GIYF" ("google is your friend" :slight_smile: )

For this time i try to code a "high-to-low-counter".
But most of my problems result from not knowing the syntax.

Take a look at the StateChangeDetection examples in the IDE

Nice, didn't even notice there was pretty much what i need already in the examples.
Thanks!

I guess (but for now i didn't thought about this problem much) later it will be a big problem to get the frequency, as it is very (VERY) low, but i need to update the output at least once a second, even the frequency does not change.
So i will have to code something like a buffer, which will caclulate the mean frequency of the last mearurements or such :cold_sweat:

This is where i'm going to have a real big problem

later it will be a big problem to get the frequency, as it is very (VERY) low, but i need to update the output at least once a second, even the frequency does not change.

Save the value of micros() when the input goes LOW. Next time it goes LOW save the value again in a different variable. Now you have the interval and can calculate the frequency. You can update the output as often as you like but do not have any blocking code in the sketch so loop() keeps running at full speed

I've got now the first snipplet, which can count button press, but i noticed something strage.
First of all, the code, nice and easy:

int val = 0;
int oldVal = 0;
bool on = false;

void setup() {
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, INPUT);
  oldVal = digitalRead(4);
  Serial.begin(9600);
}

void loop() {
  val = digitalRead(4);
  if (val != oldVal) {
    if (val == HIGH) {
      digitalWrite(2, on);
      digitalWrite(3, !on);
      Serial.println("press");
    }
    else {
      Serial.println("release");
    }
  }
  oldVal = val;
  on = !on;
  delay(10);
}

You may notice, it's a pretty simple flip-flop-switch.
Pin 2 and Pin 3 are LEDs.
Pin 4 is the button, which i grounded through a pull-down resistor to avoid random flickering. (yep, i learned already ^^)

So now when i press the button, i see every time(!) the serial output for the button. This is ok.
But the LEDs does not flip every time.
Is there something i miss in the code?

      digitalWrite(3, on);
      digitalWrite(3, !on);

Turn on an output then turn it off again immediately

Why ?

It’s pin 2 and 3.
Not 3 and 3 :wink:

      digitalWrite(2, on);
      digitalWrite(3, !on);

First i flip the led output state, then i flip the “switch” itself, so the next time it does the whole thing vica versa.

edit …
got it. it is really my crappy code!
the “switch” flips all the time, independent on my button, which is wrong. So flipping the leds is just a random thing.
Or more exact: the state changes every time i press the button, But the switch-state is random at this point

A neater and more readable way to do it
Note the use of meaningful variable names and the built in pullup resistor

byte currentInputState;
byte previousInputState = HIGH;
bool on = false;
const byte ledPin1 = 3;
const byte ledPin2 = 5;
const byte buttonPin = A3;

void setup()
{
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  currentInputState = HIGH;
  Serial.begin(115200);
  digitalWrite(ledPin1, LOW);
  digitalWrite(ledPin2, HIGH);
}

void loop()
{
  currentInputState = digitalRead(buttonPin);
  if (currentInputState != previousInputState)
  {
    if (currentInputState == LOW)
    {
      Serial.println("press");
    }
    else
    {
      Serial.println("release");
    }
    digitalWrite(ledPin1, !digitalRead(ledPin1));
    digitalWrite(ledPin2, !digitalRead(ledPin2));
  }
  previousInputState = currentInputState;
}

You will, of course, need to change the pin numbers to suit your system

Thanks again ^^

So i did a few more coding meanwhile...

At the part, where the code gets the "button released" signal (or kinda signal) i start to set timestamps.

At the top i initialize some additional variables:

unsigned long newTime = 0;
unsigned long oldTime = 0;
long measure[5]; //this array i will need later, but for the first i use only index 0
byte i = 0;
//Serial.println("release");
      oldTime = newTime; //save last timestamp
      newTime = millis(); //set new timestamp
      measure[i] = newTime - oldTime; //time period between "button released"
      Serial.println(measure[i]); //debug output
      Serial.println(1/measure[i]);

My first output is fine. Depending on how fast i press my test button, i get nice values of like 1200, 400, 600 and such. Everything is finde.
But my second output is simply 0.
This is blowing my mind! :o

As soon as the arduino should devide 1/1200 it's output is simply 0

As soon as the arduino should devide 1/1200 it's output is simply 0

The calculation will be done using integers, hence the result of zero even if you put teh result in a float. You can force the calculation to be done as a float. Try this

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  float result;
  result = 1 / 1200;
  Serial.println(result, 6);
  result = 1 / 1200.0;
  Serial.println(result, 6);
}

void loop()
{
}

Please post your complete sketch as it is now

You were right about the data type.
Your solution worked, so i went back to my code and just tried out, where i did the mistake:
i had to switch my “long measure” to “float measure” and voila … it worked then.

I know the code looks like a mess, but i coded it in my lunch break and while sitting at the train, so don’t be that hard with me :wink:
I’ll refurbish it as soon i get all the breaking points to work.

byte val = 0; 
byte oldVal = 0; 
unsigned long newTime = 0; 
unsigned long oldTime = 0; 
float measure[2] = {0, 0}; 
float freq = 0; 
byte i = 0; 
 
void setup() { 
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT); 
  pinMode(4, INPUT); 
  oldVal = digitalRead(4); 
  Serial.begin(9600); 
} 
 
void loop() { 
  val = digitalRead(4); //check button input
  if (val != oldVal) {  //if button does something
    if (val == HIGH) {  //when button is pressed
      digitalWrite(2, HIGH); //show me the input click
      delay (1); 
      digitalWrite(2, LOW); //when button is released
      if (freq > 3 && freq < 4){ //check for the mid range frequency to delay
        delay(-=MY PROBLEM=-); //this part is my problem
        digitalWrite(3, HIGH);  //show me the output click
        delay(1); 
        digitalWrite(3, LOW); 
      } 
      else { 
        digitalWrite(3, HIGH); //synchronous input-output click on <3Hz and >4Hz
        delay(1); 
        digitalWrite(3, LOW); 
      } 
    } 
    else {
       //calculating the click frequency in Hz
      oldTime = newTime; 
      newTime = millis(); 
      measure[i] = newTime - oldTime; 
      //Serial.println(measure[i]); 
      //Serial.println(1/measure[i]); 
      i++; 
      freq = 1/(2/(1/measure[0]+1/measure[1])/1000); //homogeneous average to smooth single spikes
      if (i == 2){ 
        Serial.print("frequence: "); 
        Serial.print(freq); 
        Serial.println(" Hz"); 
      } 
      i = i%2; 
    } 
  } 
  oldVal = val; 
  delay(5); //dont touch this. timestamps are messed up and the code is trash, when removed
}

So far, it works as expected and my calculations are pretty much good, as far as i validated it …
I re-designed the leds output, so it’s now not flipping anymore, but one led shows me the input clicks, and the other shows me the output clicks. Just to check the code for function without serial monitor. Can be later re-written to control the hard wired output.
The hard thing, where i have no clue for a practical solution is now, how to delay the output clicks, without delaying the input …
When i do delay at the commented point, the output is delayed (as wanted) but meanwhile no input is recognized, so the first calculation after this delay is trash and only a nearby value (thanks to my smoothing).
When the clicks keep comming in the mid range frequency constantly my calculated frequency randomly “flickers” up and down before going into delay again.

When i do delay at the commented point, the output is delayed (as wanted) but meanwhile no input is recognized, so the first calculation after this delay is trash and only a nearby value (thanks to my smoothing).

Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

Ok, this is a nice idea i didn't even thought of.

  1. startTime as timestamp
  2. bufferOutput as bool
  3. currentTime as another timestamp
    and instead of "delay" i can do like "if (wantedDelay > currentTime - startTime && bufferOutput) then ..."
    This should really work :smiley:

renegade2k:
Nice, didn't even notice there was pretty much what i need already in the examples.
Thanks!

I guess (but for now i didn't thought about this problem much) later it will be a big problem to get the frequency, as it is very (VERY) low, but i need to update the output at least once a second, even the frequency does not change.
So i will have to code something like a buffer, which will caclulate the mean frequency of the last mearurements or such :cold_sweat:

This is where i'm going to have a real big problem

The PCNT (Pulse Counter) module is designed to count the number of rising and/or falling edges of an input signal. Each pulse counter unit has a 16-bit signed counter register and two channels that can be configured to either increment or decrement the counter. Each channel has a signal input that accepts signal edges to be detected, as well as a control input that can be used to enable or disable the signal input. The inputs have optional filters that can be used to discard unwanted glitches in the signal.

The ESP32 has a Pulse Counter, the PCNT that is a self contained module that does not require CPU time. The PCNT can count pulses when the CPU is doing other things and interrupt, the CPU upon reaching a count.
Pulse Counter API: Pulse Counter - ESP32 - — ESP-IDF Programming Guide latest documentation
The PCNT has 8 independent pulse counter units and each unit is, separately, programmable.

Sadly i could not find anything about PCNT on the Digispark attiny.
I guess it's only part of the arduino uno and "bigger" boards?

ESP32 is another MCU.

Oh, ok then.
As i'm now kinda finished with the project (well, let's say 80% with a lot of help from UKHeliBob) i'm not going to buy another piece of hardware to begin everything from the scratch :wink:

renegade2k:
Oh, ok then.
As i'm now kinda finished with the project (well, let's say 80% with a lot of help from UKHeliBob) i'm not going to buy another piece of hardware to begin everything from the scratch :wink:

Makes sense, good luck.