I am working on a motor control system, and am using an interrupt to count pulses from a meter on my Arduino Yun. It appears to count pulses accurately for a short time but will occasionally start counting extra pulses, then revert back to being accurate. The meter is currently putting out a pulse that's 50ms wide at a constant 50 ms interval. I tried rearranging my interrupt routine but it did not seem to make a difference. Any idea what could be causing these extra pulses?
More info: There's a 10k pull-down resistor hooked into the signal line. The meter has an open collector and is set to read 0V at zero flow, positive voltage in flow state. Maybe my resistor isn't properly sized? I'm not much of an electrical engineer.
Code:
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
byte statusLed = 13;
int sensorInterrupt = 4; // 4 = digital pin 7
// The hall-effect flow sensor outputs approximately .5 per gallon of flow
float calibrationFactor = .25;
volatile byte pulseCount;
float flowRate;
float flowGallons;
float totalGallons;
float lastTotal;
unsigned long oldTime;
YunServer server;
void setup() {
pinMode(statusLed,OUTPUT);
digitalWrite(13, LOW);
Bridge.begin();
digitalWrite(13, HIGH);
server.listenOnLocalhost();
server.begin();
Console.begin();
pulseCount = 0;
flowRate = 0.0;
flowGallons = 0;
totalGallons = 0;
oldTime = 0;
// The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
// Configured to trigger on a FALLING state change (transition from HIGH
// state to LOW state)
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
void loop() {
YunClient client = server.accept();
if (client) {
process(client);
client.stop();
}
delay(50);
}
void process(YunClient client) {
String command = client.readStringUntil('/');
if (command == "start") {
totalGallons = 0;
Bridge.put("STATE",0);
startCommand(client);
}
}
void startCommand(YunClient client){
int requestedGallons = client.parseInt();
char state[1];
startPump();
while(totalGallons <= requestedGallons){
if((millis() - oldTime) > 1000) // Only process counters once per second
{
detachInterrupt(sensorInterrupt);
Bridge.get("STATE", state, 1);
String stateString = String(state[0]);
if(stateString == "1"){
stopPump();
Console.println("Successfully Stopped");
lastTotal = totalGallons;
Bridge.put("GalsPumped", String(lastTotal));
Console.println(lastTotal);
goto endRun;
break;
}
flowRate = (((1000.0 / (millis() - oldTime)) * (pulseCount))*calibrationFactor)*60;
oldTime = millis();
flowGallons = (flowRate / 60);
// Add the millilitres passed in this second to the cumulative total
totalGallons += flowGallons;
unsigned int frac;
// Print the flow rate for this second in litres / minute
Console.print("Flow rate: ");
Console.print(int(flowRate)); // Print the integer part of the variable
Console.print("."); // Print the decimal point
// Determine the fractional part. The 10 multiplier gives us 1 decimal place.
frac = (flowRate - int(flowRate)) * 10;
Console.print(frac, DEC) ; // Print the fractional part of the variable
Console.print(" Gal/Min");
// Print the number of litres flowed in this second
Console.print(" Current Liquid Flowing: "); // Output separator
Console.print(flowGallons);
Console.print(" Gal/Sec");
// Print the cumulative total of litres flowed since starting
Console.print(" Output Liquid Quantity: "); // Output separator
Console.print(totalGallons);
Console.println(" Gal");
Console.println(pulseCount);
// Reset the pulse counter so we can start incrementing again
pulseCount = 0;
// Enable the interrupt again now that we've finished sending output
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
}
endRun:{
stopPump();
Bridge.put("GalsPumped", String(totalGallons));
}
}
/*
Insterrupt Service Routine
*/
void pulseCounter()
{
// Increment the pulse counter
pulseCount++;
}
void stopPump(){
//code to stop pump
}
void startPump(){
//code to stop pump
}
From serial monitor:
Flow rate: 149.2 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 323.10 Gal
10
Flow rate: 149.2 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 325.58 Gal
10
Flow rate: 149.2 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 328.07 Gal
10
Flow rate: 149.2 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 330.56 Gal
10
Flow rate: 149.2 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 333.05 Gal
10
Flow rate: 179.1 Gal/Min Current Liquid Flowing: 2.99 Gal/Sec Output Liquid Quantity: 336.03 Gal
12
Flow rate: 238.5 Gal/Min Current Liquid Flowing: 3.98 Gal/Sec Output Liquid Quantity: 340.01 Gal
16
Flow rate: 253.7 Gal/Min Current Liquid Flowing: 4.23 Gal/Sec Output Liquid Quantity: 344.24 Gal
17
Flow rate: 194.0 Gal/Min Current Liquid Flowing: 3.23 Gal/Sec Output Liquid Quantity: 347.47 Gal
13
Flow rate: 194.0 Gal/Min Current Liquid Flowing: 3.23 Gal/Sec Output Liquid Quantity: 350.70 Gal
13
Flow rate: 208.9 Gal/Min Current Liquid Flowing: 3.48 Gal/Sec Output Liquid Quantity: 354.19 Gal
14
Flow rate: 223.8 Gal/Min Current Liquid Flowing: 3.73 Gal/Sec Output Liquid Quantity: 357.92 Gal
15
Flow rate: 194.0 Gal/Min Current Liquid Flowing: 3.23 Gal/Sec Output Liquid Quantity: 361.15 Gal
13
Flow rate: 238.5 Gal/Min Current Liquid Flowing: 3.98 Gal/Sec Output Liquid Quantity: 365.13 Gal
16
Flow rate: 194.0 Gal/Min Current Liquid Flowing: 3.23 Gal/Sec Output Liquid Quantity: 368.36 Gal
13
Flow rate: 194.0 Gal/Min Current Liquid Flowing: 3.23 Gal/Sec Output Liquid Quantity: 371.60 Gal
13
Flow rate: 328.3 Gal/Min Current Liquid Flowing: 5.47 Gal/Sec Output Liquid Quantity: 377.07 Gal
22
Flow rate: 283.5 Gal/Min Current Liquid Flowing: 4.73 Gal/Sec Output Liquid Quantity: 381.79 Gal
19
Flow rate: 283.5 Gal/Min Current Liquid Flowing: 4.73 Gal/Sec Output Liquid Quantity: 386.52 Gal
19
Flow rate: 298.5 Gal/Min Current Liquid Flowing: 4.98 Gal/Sec Output Liquid Quantity: 391.50 Gal
20
Flow rate: 179.1 Gal/Min Current Liquid Flowing: 2.99 Gal/Sec Output Liquid Quantity: 394.48 Gal
12
Flow rate: 149.2 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 396.97 Gal
10
Flow rate: 149.1 Gal/Min Current Liquid Flowing: 2.49 Gal/Sec Output Liquid Quantity: 399.45 Gal
10
Flow rate:
Moderator edit: </mark> <mark>[code]</mark> <mark>
Sorry, this is my first post on the forum and did not see the code button.
Before doing anything with the value of pulseCount, I detach the interrupt, then reattach after the calculations. This should have the same effect, no?
if((millis() - oldTime) > 1000) // Only process counters once per second
{
detachInterrupt(sensorInterrupt);
Bridge.get("STATE", state, 1);
String stateString = String(state[0]);
if(stateString == "1"){
stopPump();
Console.println("Successfully Stopped");
lastTotal = totalGallons;
//Bridge.put("GalsPumped", String(lastTotal));
Console.println(lastTotal);
goto endRun;
break;
}
flowRate = (((1000.0 / (millis() - oldTime)) * (pulseCount))*calibrationFactor)*60;
oldTime = millis();
flowGallons = (flowRate / 60);
// Add the millilitres passed in this second to the cumulative total
totalGallons += flowGallons;
unsigned int frac;
// Print the flow rate for this second in litres / minute
Console.print("Flow rate: ");
Console.print(int(flowRate)); // Print the integer part of the variable
Console.print("."); // Print the decimal point
// Determine the fractional part. The 10 multiplier gives us 1 decimal place.
frac = (flowRate - int(flowRate)) * 10;
Console.print(frac, DEC) ; // Print the fractional part of the variable
Console.print(" Gal/Min");
// Print the number of litres flowed in this second
Console.print(" Current Liquid Flowing: "); // Output separator
Console.print(flowGallons);
Console.print(" Gal/Sec");
// Print the cumulative total of litres flowed since starting
Console.print(" Output Liquid Quantity: "); // Output separator
Console.print(totalGallons);
Console.println(" Gal");
Console.println(pulseCount);
// Reset the pulse counter so we can start incrementing again
pulseCount = 0;
// Enable the interrupt again now that we've finished sending output
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
I detach the interrupt, then reattach after the calculations. This should have the same effect, no?
You would thinks so, but there can be strange results which occur from timing issues (race conditions) when you use detach/attach with an external interrupt rather than the suggested noInterrupts() interrupts() syntax.
I'm not sure i totally understand, but some time ago, the interrupt gurus on this forum told me to do it the way Robin2 suggested and I have adopted it. There is no downside to the suggested syntax. Don't fight it. Use it.
jamesmkehoe:
Before doing anything with the value of pulseCount, I detach the interrupt, then reattach after the calculations. This should have the same effect, no?
No, it will lose pulses. Leave the interrupt running and use atomic sequences to read, write or
read-modify-write any volatile variables it uses (as explained above).
Are you sure your source of pulses produces clean logic signals?
You mention open-collector and a pull-down (normally you'd need a pull-up).
10k is a worrying large value of pull-down or pull-up resistor if the sensor is remote at the
end of a cable, go for something more like 2k2 or 1k to reject noise.
Thanks for the advice. I changed the code and I am now getting garbage in the console, and the pulse count is now completely useless. I am not sure what could be going wrong - cleaned up the code a bit too. Any more suggestions? Here's the updated code:
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
byte statusLed = 13;
int sensorInterrupt = 4; // 4 = digital pin 7
// The hall-effect flow sensor outputs approximately .5 per gallon of flow
float calibrationFactor = .25;
volatile byte pulseCount;
float flowRate;
float flowGallons;
float totalGallons;
float lastTotal;
unsigned int newPulseCount;
unsigned int oldPulseCount;
unsigned int currentPulseCount;
unsigned long oldTime;
YunServer server;
void setup() {
pinMode(statusLed,OUTPUT);
digitalWrite(13, LOW);
Bridge.begin();
digitalWrite(13, HIGH);
server.listenOnLocalhost();
server.begin();
Console.begin();
pulseCount = 0;
flowRate = 0.0;
flowGallons = 0;
totalGallons = 0;
oldTime = 0;
newPulseCount = 0;
oldPulseCount = 0;
currentPulseCount = 0;
// The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
// Configured to trigger on a FALLING state change (transition from HIGH
// state to LOW state)
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
void loop() {
YunClient client = server.accept();
if (client) {
process(client);
client.stop();
}
delay(50);
}
void process(YunClient client) {
String command = client.readStringUntil('/');
if (command == "start") {
totalGallons = 0;
Bridge.put("STATE",0);
startCommand(client);
}
}
void startCommand(YunClient client){
int requestedGallons = client.parseInt();
char state[1];
startPump();
while(totalGallons <= requestedGallons){
if((millis() - oldTime) > 1000) // Only process counters once per second
{
Bridge.get("STATE", state, 1);
String stateString = String(state[0]);
if(stateString == "1"){
goto endRun;
}
oldPulseCount = newPulseCount;
noInterrupts();
newPulseCount = pulseCount;
interrupts();
Console.println("new pulse count: " + newPulseCount);
currentPulseCount = newPulseCount - oldPulseCount;
flowRate = (((1000.0 / (millis() - oldTime)) * (currentPulseCount))*calibrationFactor)*60;
oldTime = millis();
flowGallons = (flowRate / 60);
// Add gallons passed this second to Total Gallons
totalGallons += flowGallons;
unsigned int frac;
// Print the flow rate for this second in gal / minute
Console.print("Flow rate: ");
Console.print(int(flowRate));
Console.print(".");
frac = (flowRate - int(flowRate)) * 10;
Console.print(frac, DEC) ;
Console.print(" Gal/Min");
// Print the number of gal flowed in this second
Console.print(" Current Liquid Flowing: "); // Output separator
Console.print(flowGallons);
Console.print(" Gal/Sec");
// Print the cumulative total of gal flowed since starting
Console.print(" Output Liquid Quantity: "); // Output separator
Console.print(totalGallons);
Console.println(" Gal");
// Print pulses over past second
Console.println(currentPulseCount);
}
}
endRun:{
stopPump();
Console.println("Successfully Stopped");
lastTotal = totalGallons;
Console.println(lastTotal);
Bridge.put("GalsPumped", String(totalGallons));
}
}
/*
Insterrupt Service Routine
*/
void pulseCounter()
{
// Increment the pulse counter
pulseCount++;
}
void stopPump(){
//code to stop pump
}
void startPump(){
//code to stop pump
}
What is the flow rate and pulse count you expect? In the first print out there were many readings of 10 counts per second? The latest readings appear to be more like 20.
My trouble shooting suggestion is to use software pulse generation to get your code working independent of the flow meter. Then you can address issues of pullups/pulldowns and signal quality.
Here is simple square wave generating code output on Pin 8 and the interrupt reading it on Pin 2. Substitute the pulse generating code for the actual flow meter. With that, you can separate the interrupt processing and other code from the hardware function of the flow meter.
//Variables for Pulse generating
const int pulsePin = 8;//pin sending pulses jumper to interrupt pin 2
const long pulseLength = 50000; // microseconds for 10 pps square wave
unsigned long microsLast;//last time the pulse pin changed state
//Variables for pulse reading
volatile unsigned int count = 0;
unsigned int copyCount = 0;
unsigned long lastRead = 0;
int readInterval = 1000; //read data every second
void isrCount()
{
count++;
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
pinMode(pulsePin, OUTPUT);
attachInterrupt(0, isrCount, RISING); //interrupt signal to pin2
}
void loop() {
if (micros() - microsLast > pulseLength)
{
digitalWrite(pulsePin, !digitalRead(pulsePin)); //toggle pulsePin
microsLast = micros();
}
if (millis() - lastRead >= readInterval)
{
noInterrupts();
copyCount = count;
count = 0;
interrupts();
lastRead = millis();
Serial.println(copyCount);
}
}