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;
}
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.
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:
You take a reading every 100ms, but the serial monitor is taking more than 80ms to print the results at 9600Bd.
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.
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();
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
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.
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.
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.
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++;
}