Deceleration, light on

I have a light that I want to come on when I reach a certain deceleration level or the external trigger, measured using the pulse from my speedo. External trigger works its the speedo side thats the problem. My code is below but have not got it functioning, any help would be good. Thank you

const int pulsePin = 2; // Pulse input pin
const int relayPin = 3; // Relay control pin
const int externalTriggerPin = 4; // External trigger pin

volatile unsigned long lastPulseTime = 0;
volatile unsigned long pulseInterval = 0;
unsigned long lastCheckTime = 0;
unsigned long checkInterval = 100; // Check every 100ms

float currentFrequency = 0; 
float previousFrequency = 0;
float decelerationRate = 0;

bool relayActivated = false;
bool externalTriggerActive = false;
void setup() {
  pinMode(pulsePin, INPUT);
  pinMode(relayPin, OUTPUT);
  pinMode(externalTriggerPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(pulsePin), pulseISR, FALLING);
  Serial.begin(9600);
}

void loop() {
  unsigned long currentTime = millis();


 if (digitalRead(externalTriggerPin) == HIGH){
 externalTriggerActive = true;
 } else {
externalTriggerActive = false;
}

  if (currentTime - lastCheckTime >= checkInterval) {
    lastCheckTime = currentTime;

    if (pulseInterval > 0) {
      currentFrequency = (pulseInterval * 1000); // multiplied to give finer tuning
    } else {
      currentFrequency = 0;
    }

    decelerationRate = previousFrequency - currentFrequency;
    previousFrequency = currentFrequency;

    
    float threshold = 500; // Adjust as needed to change amount of deceleration

    bool decelerationCondition = (decelerationRate > threshold); 
    

    if (externalTriggerActive || decelerationCondition) {
      if (!relayActivated) {
         // Pulse the relay 4 times in 4 seconds
  for (int i = 0; i < 4; i++) {
    digitalWrite(relayPin, HIGH);
    delay(900); // 0.7 seconds on
    digitalWrite(relayPin, LOW);
    delay(100); // 0.3 seconds off
  }
  // Keep the relay on after pulsing
  digitalWrite(relayPin, HIGH);
        relayActivated = true;
      }
    } else {
      if (relayActivated) {
        relayActivated = false;
        digitalWrite(relayPin, LOW); // Deactivate relay
      }
      else
      relayActivated = false;
        digitalWrite(relayPin, LOW); // Deactivate relay
    }

    // Debugging output
    Serial.print("Current Frequency: ");
    Serial.print(currentFrequency);
    Serial.print(" Hz, Deceleration Rate: ");
    Serial.print(decelerationRate);
    Serial.print(" Hz, Threshold: ");
    Serial.println(threshold);
  }
}

void pulseISR() {
  unsigned long currentTime = millis();
  pulseInterval = currentTime - lastPulseTime;
  lastPulseTime = currentTime;
}

What is the unintentional behavior you're seeing?

I suspect that this gets you into trouble, since you only look at the previous speed. Comparing one speed to another theoretically would allow you to determine if there's acceleration or deceleration. But this only works if the speed readings are perfect. In practice, they will likely jitter, especially at constant speed, which will involve slightly fluctuating readings.

So I'd recommend you convert your sketch so that it only detects deceleration if there's a series of readings that show a pattern of deceleration. You'll have to determine empirically how long this series (i.e. how many readings) it will need to be, and how many 'false positives' are permissible in such a series.

This looks immediately suspicious. Frequency and interval have a reciprocal relationship! Maybe you meant

currentFrequency = 1000.0 / pulseInterval; 
1 Like

I've started testing your code on an Arduino Uno R3.
I implemented the modification suggested by PaulRB in post #3.

I fed the output of a function generator into pin 2.

Two things became immediately obvious:

  1. You take a reading every 100ms, but the serial monitor is taking more than 80ms to print the results at 9600Bd.

  2. When feeding a constant frequency into the Arduino, the frequency reported varies due to the millis count of the period varying by ±1ms.
    e.g. at 50Hz input frequency, the reported frequency is 52.63Hz, 50Hz, or 47.62Hz depending on whether the millis count is 19, 20 or 21.


The error in the frequency measurement gets worse as the frequency gets higher. At 500Hz input frequency , the reported frequency can be 333.33Hz, 500Hz, or 1000Hz.

I would suggest 2 changes straight away:

  • Increase the Baud rate so you spend less time printing.
  • Use micros() to do the timing, instead of millis.

You have correctly

volatile unsigned long lastPulseTime = 0;
volatile unsigned long pulseInterval = 0;

but that's only half the trouble with variables used in an ISR.

This may not be or solve your problem, it is just necessary, good practice:

Any access to those variables outside the ISR must be done with interrupts off. For getting the value, ppl will usually make a new variable and grab a copy

  int myPpulseInterval;

  noInterrupts();
  myPpulseInterval = pulseInterval;
  interrupts();

then use myPpulseInterval.

a7

Using micros to do the timing, for a 50Hz input the reported frequency now only varies between 49.92Hz and 49.94Hz.
The slightly low value is likely to be due to my 16MHz clock being off frequency

At 500Hz input frequency, it now reports 499Hz or 500Hz.

.

It's quite likely that the real world system that generates the pulses is even more variable even at "constant" speed. Which takes us back, I think, to my suggestions in #2.

I have found that my pulse is getting to arduino but it is not counting it.
Here is my pulse at a crawl.

and at 50kmh

The amplitude of your waveform seems to be 500mV (0.5V). You'll need to amplify it by 6-7 times for a 3.3V Arduino or 10 times for a 5V Arduino.

Or it could be 5V, if nzgrub is using a x10 probe, but forgot to change from x1 in the settings.
We need clarification here.

1 Like

How about using an accelerometer sensor ?

Yes you are right, just checked, using 10x probe. My bag, sorry. I can redo with 1x if it makes any difference.

How do you know it is not counting it?
Are you using special testing code?

Hi, @nzgrub

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

What is the source of your input pulses?

Can you please post some images of your project?
So we can see your component layout.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

1 Like

No need to. Now we know that the amplitude is really 5V.
It looks like a nice square wave.

I have changed the code to read
currentFrequency = (1000 / pulseInterval);
serial monitor constantly reading
Current Frequency: 500.00 Hz, Deceleration Rate: 0.00 Hz, Threshold: 500.00
so seems to be calculating a pulse rate of 2 all the time.

Heres my wiring circuit, powered via usb. Ground is common between arduino and 12v.

There should be a ground connection along with the speedo wire.

I have found that my pulse is getting to arduino but it is not counting it.

Well check it with this test code.


volatile unsigned long RPMisrMicros;
volatile bool RPMnewIsrMicros = false;
unsigned long oldtime = 0, currentTime;

void setup()
{
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, FALLING);
}

void loop()
{

  if (RPMnewIsrMicros)
  {
    RPMnewIsrMicros = false;
    currentTime = RPMisrMicros - oldtime;
    oldtime = RPMisrMicros;
    // Print the time of 10 pulses
    Serial.println (currentTime);
  }

}

// RPMisrCount can be local, the main program does not need it
// This ISR will give you the time every 10 pulses
void RPMSensorISR()
{
  RPMisrMicros = micros();
  static byte RPMisrCount = 0;
  if (RPMisrCount == 10)
  {
    RPMnewIsrMicros = true;
    RPMisrCount = 0;
  }
  RPMisrCount++;
}

Ground on arduino is connected to ground on bike.

Sample of serial monitor running your code...
99684

95464

97332

98968

101900

96996

95524

94528

Is that on the bike or on a signal generator?

Tom... :smiley: :+1: :coffee: :australia: