Pulse detection on a PWM waveform

I'm trying to work out how to pulse detect on a PWM waveform, and need to detect when there are no pulses, when pulses are a certain length and when pulses get shorter than that.

The signal is active low, so it sits at 5V when there are no pulses.

When the pulses start, they drop to about 3V.

My main concern is this. The duty cycle starts at about 50% then moves in steps till it's 100%, or 0% as it's active low.

So I can check for no pulses initially, then measure them when they arrive, but what is pulseIn going to do when is it just a steady low level, with no edges to detect ?

I'm thinking along the lines of a flag that says there were pulses, and they were getting shorter, so the signal is now fully active, and then negate that when the pulses return and do a similar thing going the other way.

The wavelength is 80mS, when there are pulses present.

Plan B is to try and use the ADC to sample the pulses, as that will cope with the steady states at 0% and 100% duty cycle, but I'm not sure how well it will cope with the pulses.

Is there a tried and tested methodology for this sort of situation ?

That is not low enough for reliable detection by an input pin on a 5V Arduino, and 5V idle is unsafe for input to a 3.3V Arduino.

For the Arduino Uno R3 and other 5V AVR Arduinos, the voltage must drop below 1.5V to be reliably detected as LOW.

You will need a comparator or other method to discriminate 5V -> 3V pulses. The Arduino Uno has a built in comparator, and how to use it is described in this excellent tutorial (the Gammon web site seems to be offline).

1 Like

Did You mesure that using an oscilloscope?
You can't use a DMM for that. Likely the pulses are good enough referring to @jremington.

Assuming your numbers are correct I would consider using a comparator such as a LM311 or whatever you have. Connect it so when the pulse falls the compactor output rises (inverts the signal). Then when you get the rising edge start a timer and when it falls stop the timer. You can do this in reverse if you like. There are frequency limits to this approach but you did not tell us what the frequency is.

worth trying to use an ADC to read the two voltage levels

You havent said what your objective is in doing this, nor what is providing the signal.
IF exact timing is not a concern perhaps you can just read the average value which will be 5V for no pulses, and a lower voltage when there are pulses present?

many. For example you could combine the pulses with an external clock, or even just read the input at intervals and see when it goes low.

It will timeout and return 0

A good point. The overall project is to control the heating element in an engine exhaust smoke emulator for an RC tank.

The exhaust smoke emulator uses a 24V 48W soldering iron element wrapped in heat proof matting sitting in a bath of oil, with a fan blowing over the matting to achieve the exhaust smoke effect.

The engine sound effects come from a custom designed sound board. This board also produces a signal, which is linked to the engine speed, so that the fan starts to run when the engine sound is started, and fan speed is in sync with the engine speed.

The element is currently controlled by a switch on the transmitter, so that it can be turned on and off remotely.

This all works as designed, but I wanted to add to it, so that the element is switched off automatically when the tank starts moving, saving power and stopping the smoke output, as these engines only really smoke badly at start up.

I can do this by monitoring the stick position of the RC channel used to control the motors, so that if the stick travel exceeds a certain amount, power is shut off to the element.

That seems doable.

The problem started when I decided that the engine should smoke at idle after running, but less than when first started, so I wanted to have three states:

  1. Engine off - element at full power to generate start up smoke.
  2. Engine idling - element at 50% power (percentage to be determined by experimentation).
  3. Engine running at speed - element off (or very lower power for haze).

I had hoped this switched signal for the fan speed from the sound board could be used to detect when the engine was running as well as whether it was at idle or at speed.

If the wavelength is 80mS and repeatable, could I use a timeout of 90mS and then do an analog read of the signal ? No pulse detected means it's either not running or at full speed, high signal means not running, low signal means full speed.

I'm not that bothered about it being 100% reliable, as incorrect readings will just mean that the element will be in the wrong state for a few tens of milliseconds.

I should also state, due to size requirements, I'm trying to do this on a DigiSpark board with the ATTiny85.

You can set the timeout to 90ms, no problem.
There is no need to do an analog read you can just do a digital read of the signal to find the state.

You can VERY easily measure the duty cycle of the PWM with a simple RC analog filter feeding the ADC. You can then use a logic level MOSFET to feed a suitable pwm signal to the element.

I managed to get it 95% as I wanted last night with pulseIn and analogRead but had to stop when the Digispark stopped responding to uploads. Something I've noticed on some Chinese boards.

I think you're correct and an RC filter and moving average would probably be more efficient and less prone to glitches. Will try this method during tinkering time tonight.

As I said, there is no need to do an analog read.
Are you using ATTinycore?

Sorry, should have checked the program. I'm doing digitalReads for the signal level but now using analogWrites, so I can PWM the element drive. I'm using ATTinycore 2.6.

No reason it should not work. Can you explain exactly what the problem is?

This was where I got to last night. I started off using the LED on P1, to show pulses detected, and two externals on P2 and P3 to show the steady off and on states. That's now changed to just P1 showing the PWM state.

When the engine sound isn't playing, the signal is high (off). When it starts playing there is a period where the signal is low (on) with no pulses, so that it drives the fan to provide a burst of smoke at start up, then the pulses start. When you move the stick to full travel, the pulses stop and the signal is just low (on).

The LEDs responded as I wanted most of the time, but occasionally the "off" LED would light at the same time as the pulses and would only go off again when I centred the stick so the pulses were in the "idle" detection range. I've set 'level = 1' as a reset in each loop but for that to take preceidence it must mean that there were no pulses detect and the digitalRead happened during the peak of a pulse. I think it may just be timing between the programming looping and pulse command blocking whilst waiting for a pulse. That's why the timeout is so high, to give it a chance to detect a pulse before giving up.

unsigned long pulse = 0;
unsigned long level = 0;
unsigned long timeout = 150000;

// ==================================================================================================================

void setup() {

  pinMode(0, INPUT);   // Pulse IN From Receiver For Heating Enable Switch
  pinMode(1, OUTPUT);  // Pulse IN From Receiver for Engine Speed Sensing

  pinMode(2, OUTPUT);  // PWM Output To FET OptoIsolator +ve
  pinMode(3, OUTPUT);  // Switched Output To FET OptoIsolator -ve

  analogWrite(1, 255);  // Turn Off Smoker Heating Element Using PWM
  delay(500);
  analogWrite(2, 255);  // Turn Off Smoker Heating Element Using PWM
  delay(500);
  analogWrite(3, 255);  // Turn Off Smoker Heating Element Using PWM
  delay(500);
  analogWrite(1, 0);  // Turn Off Smoker Heating Element Using PWM
  delay(500);
  analogWrite(2, 0);  // Turn Off Smoker Heating Element
  delay(500);
  analogWrite(3, 0);  // Turn Off Smoker Heating Element
  delay(500);
}

// ==================================================================================================================

void loop() {

  pulse = (pulseIn(0, LOW, timeout));

  level = 1;

  if (pulse == 0) {
    level = digitalRead(0);
  }

  if (pulse >= 17000) {
    level = 0;
  }

  if ((pulse >= 15000) && (pulse <= 17000)) {
    analogWrite(1, 128);
  }

  if ((pulse == 0) && (level == 0)) {
    analogWrite(1, 0);
  }

  if ((pulse == 0) && (level == 1)) {
    analogWrite(1, 255);
  }
}

Please ignore the programming laziness, there was a lot of cut & paste going on from a previous program :slight_smile:

The signal is designed to drive a fan, so is off when there's no sound playing, PWM during idle and mid speed and fully on when at full speed. I want the inverse, so that the element is on when there is no sound playing, PWM when idling and off when at any speed.

Well it seems that you need to monitor both the signal from the sound module and the RC speed signal to do what you want.