sprintf returning unexpected values

Background for the project: using an uno to run a small engine dyno. The dyno monitors a single cylinder briggs and stratton engine (max rpm =3800, idle rpm = 1800), a connected shaft, and eventually force by a lever arm on that shaft. The connected shaft has a 4 tooth sprocket monitored by a PNP proximity sensor.

The arduino then prints these values out on serial, which is picked up by PLX-DAQ. When printing the engine data, it must be proceeded by "DATA", separated by ",".

When I run the code using multiple serial.prints, the code runs okay at low speeds, but loses track at higher speeds ~2200 rpm on either interrupt. This is why I am trying to use one serial.print, thus speeding up the program. I copied my code from an on-board DAQ system that I had previously worked on, and that worked correctly for monitoring the engine and transmission RPMs. Both are attached below

If I try running my dyno code with the sprintf, I am able to see the number of shaft counts, but the rpms are 0. I don't have much programming experience, which is why I am here.

This is the project that I am working on:

// ----------LIBRARIES-------------------------------------------------------------------------------------------------------------
#include "Arduino.h"

// --------CONSTANTS (won't change)--------------------------------------------------------------------------------------------------------------
const int cvtPin = 2;       //input pin for proxy sensor
const int enginePin = 3;    //input pin for coil rpm
const int lbPin = A3;       //input for force sensor

//------------ VARIABLES (will change)--------------------------------------------------------------------------

//CVT
  volatile byte CVT_REV = 0;       //holds the cvt count while other tasks are happening
  unsigned long cvt_time;
  unsigned long last_cvt_time;
  byte cvt_counts;
  int cvt_rpm;                      //place holder for cvt rpm once calculated

//ENGINE
  volatile byte E_REV = 0;
  unsigned long engine_time;
  unsigned long last_engine_time;
  byte e_counts;
  int e_rpm;

//FORCE
  int lb = 0;                       //holds force measurement from force sensor
  int hp = 0;                       //holds horsepower based on rpm and lb with a 6.7in torque arm

//TIMING
  unsigned long lastMillis = 0;    //keeps track of the last time measurements were taken
  unsigned long currMillis;
  int Update = 1000;                //change to update faster or slower NOTE: TIME IS IN MILLISECONDS
  char serialBuffer[50] = "";

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

void setup(){ 
  Serial.begin(9600); // the bigger number the better, matches Baud setting in PLX-DAQ



//set up for excell 
  Serial.println("CLEARDATA"); //clears up any data left from previous projects in PLX-DAQ
  Serial.println("LABEL,t (s),Engine RPM,CVT RPM,Power (HP),Torque (in-lbf)"); //always write LABEL, so
  //PLX knows the next things will be the names of the columns, seperated by commas

//must be last 
   attachInterrupt(1, e_count, FALLING); //pin 2 interupt 1
  attachInterrupt(0, cvt_count, RISING); //pin 3 interupt 0
 } 

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

void loop(){ 
  currMillis = millis(); 
 if (currMillis - lastMillis >= Update){ //change update to change resolution, reading too fast might cause the engine to not count
 
   noInterrupts () ;//Disable interrupt when copying
    e_counts = E_REV;
    E_REV = 0;  // Reset the engine REVolution counter

    cvt_counts = CVT_REV;
    CVT_REV = 0;
    interrupts () ; //enable interrupt (interupt #, code to run, when to run it)
   
    e_rpm = (e_counts  / (currMillis - lastMillis)); // Convert counts to REV per ms
    e_rpm = e_rpm / .0000166;   //convert Engine REV per ms to min

    cvt_rpm = (cvt_counts / (currMillis - lastMillis)); // Convert counts to REV per ms
    cvt_rpm = cvt_rpm / .0000166 /4;   //convert Engine REV per ms to min
     

    sprintf(serialBuffer, "DATA, %ld, %d, %d, %d, %d", millis(), e_rpm, e_counts, cvt_rpm, cvt_counts);

    Serial.println(serialBuffer);



     
/*     Serial.print(millis());
     Serial.print(" , ");
     Serial.print(e_rpm);
     Serial.print(" , ");
     Serial.print(E_REV);
     Serial.print(" , ");
     Serial.print(cvt_rpm);
     Serial.print(" , ");
     Serial.println(CVT_REV);

*/


     
     cvt_rpm = 0;                 //reset the cvt RPM counter
     e_rpm = 0;              //reset the engine RPM
     lastMillis = millis();       //uptade when the last loop occured
     
   }
 delay(1);                        //needs to be here to let the program advance
 }
 
//=====================================================================

void e_count(){
  engine_time = micros();
  //debounce engine rpm reader
  if (engine_time - last_engine_time > 1400) //15873 microseconds @ 3800 rpm
  {
    E_REV++; 
    last_engine_time = engine_time;
  }
 }

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

 void cvt_count(){
  cvt_time = micros();
  //decounce rw reader
  if (cvt_time - last_cvt_time > 100){
    CVT_REV++;
    last_cvt_time = cvt_time;
  }
 }

Here is the project that I copied from:

// ----------LIBRARIES--------------
#include "Arduino.h"
#include <SPI.h>
#include <LiquidCrystal.h>
#include <SD.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

//setup LEDs
#define PIN 4
#define TRANSLED 0
#define CVTLED 1
#define ENGINELED 2
Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, PIN, NEO_RGB + NEO_KHZ800);

// --------CONSTANTS (won't change)---------------
const byte interval = 1000;

//------------ VARIABLES (will change)---------------------

unsigned long lastMillis = 0;
unsigned long currMillis = 0;


//engine vars
volatile byte E_REV = 0;
unsigned long engine_time;
unsigned long last_engine_time;
byte e_counts;
int e_rpm;

//rear wheel vars
volatile byte RW_REV = 0;
unsigned long rw_time;
unsigned long last_rw_time;
byte rw_counts;
int rw_rpm;

void setup() {
  Serial.begin(9600); // Open serial communications and wait for port to open

//other set up items

  attachInterrupt(0, e_count, FALLING); //pin 2
  attachInterrupt(1, rw_count, RISING); //pin 3
  
}//end setup


void loop() {
  currMillis = millis();
  if(currMillis - lastMillis >= interval){
//Get RPM
    noInterrupts () ;//Disable interrupt when copying
    e_counts = E_REV;
    E_REV = 0;  // Reset the engine REVolution counter

    rw_counts = RW_REV;
    RW_REV = 0;
    interrupts () ; //enable interrupt (interupt #, code to run, when to run it)
   
    e_rpm = (e_counts  / (currMillis - lastMillis)); // Convert counts to REV per ms
    e_rpm = e_rpm / .0000166;   //convert Engine REV per ms to min

    rw_rpm = (rw_counts / (currMillis - lastMillis)); // Convert counts to REV per ms
    rw_rpm = rw_rpm / .0000166;   //convert rear wheel REV per ms to min
    

//write temps to SD & lcd
    SDWrite();
    logfile.flush();

    char serialBuffer[26] = "";
    sprintf(serialBuffer, "%ld,%d,%d,%d,%d,%d,%d", currMillis/1000, rw_rpm, e_rpm, transtemp, cvttemp, etemp, ambtemp);

    Serial.println(serialBuffer);

    
//keep track of time intervals
    lastMillis = currMillis;
  }
}


void e_count(){
  engine_time = micros();
  //debounce engine rpm reader
  if (engine_time - last_engine_time > 1400) //15873 microseconds @ 3800 rpm
  {
    E_REV++; 
    last_engine_time = engine_time;
  }
 }


 void rw_count(){
  rw_time = micros();
  //decounce rw reader
  if (rw_time - last_rw_time > 1400){
    RW_REV++;
    last_rw_time = rw_time;
  }
 }


void SDWrite(){//time(s), Rw rpm, engine rpm, transtemp, cvttemp, etemp";
  char lineBuffer[24] = "";
  sprintf(lineBuffer, "%ld, %d, %d, %d, %d, %d", currMillis/1000, rw_rpm, e_rpm, transtemp, cvttemp, etemp);
  
  logfile.println(lineBuffer);
}

What happens if you reduce the Serial.print() calls to just 1. If I remember correctly, the Serial object uses interrupts itself, which means some may be missed.

econjack, that is what I am trying to do with the sprintf. It normally would concatenate all of the variables into a buffer. Then I serial.print that buffer. But the sprintf function is not behaving as expected

Serial.begin(9600); // the bigger number the better, matches Baud setting in PLX-DAQ

This is quite slow, and probably not fast enough to keep up with the data. Use the fastest serial baud rate that works.

jremington:

Serial.begin(9600); // the bigger number the better, matches Baud setting in PLX-DAQ

This is quite slow, and probably not fast enough to keep up with the data. Use the fastest serial baud rate that works.

Code only runs once a second; at 9600baud that should not pose a problem for a max of 49 characters.

  Serial.println("LABEL,t (s),Engine RPM,CVT RPM,Power (HP),Torque (in-lbf)"); //always write LABEL, so
  //PLX knows the next things will be the names of the columns, seperated by commas

So, why is that NOT the NEXT thing you send? Put the stupid comment where it belong!

 delay(1);                        //needs to be here to let the program advance

Nonsense. If you REALLY think that there needs to be a delay(), "to let the program advance" is nothing more than a random collection of words with no inherent meaning. Describe EXACTLY why you think the delay() is necessary. It isn't, of course.

Just to clarify, I am NOT a programmer, never have been, never really wanted to be one. I am mechanical by nature, its just that electronics are required for this project.

jremington, sterretje I will try bumping up the BAUD rate and see if that helps the lagging issue. I agree that sending only 49 characters it shouldn't be an issue as most of the calculations of the data are handled with excel on the computer side.

PaulS: I didn't know comments in the code would upset someone so much when they are a non-issue.

Serial.println("LABEL,t (s),Engine RPM,CVT RPM,Power (HP),Torque (in-lbf)"); //always write LABEL, so
 //PLX knows the next things will be the names of the columns, separated by commas

this comment is referring to after LABEL, is the labels for the top row of the excel sheet as read by PLX-DAQ.

This still does not explain why the sprintf function wont work in the first set of code when the code was copied from the second set of code

volatile byte E_REV = 0;E_REV can count up to 255 before it overflows. Is that enough?

the engine is goverened at 3800 rpm, 1 pulse per revolution via the coil, equates to 3800rev/min X 1min/60sec = 63.3 pulses per second

the cvt shaft is connected at 1:1 ratio via chain, and has 4 pulses per revolution, 63.3 X 4pulse/rev = 253.3 pulses per second. I will change this to an int type variable.

EDIT:

After changing cvt to int type, it still does not keep track of rpm reliably. After about 2200rpm, it drops down to less the the idle rpm even though the store bought mini tach says other wise.

this comment is referring to after LABEL, is the labels for the top row of the excel sheet as read by PLX-DAQ.

So, why doesn't the comment say that? And, why isn't the comment BEFORE the code that it refers to?

   attachInterrupt(1, e_count, FALLING); //pin 2 interupt 1
  attachInterrupt(0, cvt_count, RISING); //pin 3 interupt 0
 }

More incorrect comments. Interrupt 0 is on pin 2. Interrupt 1 is on pin 3.

  if (engine_time - last_engine_time > 1400) //15873 microseconds @ 3800 rpm
  {
    E_REV++;
    last_engine_time = engine_time;
  }

I need for you to explain the significance of this comment.

While it may appear that I'm picking on your comments, I look to see if the code actually matches the comments. When the code does not, that indicates a problem. It may be that the comment is wrong, as in the first three examples, or it could mean that that code is wrong. When the comments and code do not agree, only you can decide which is wrong, and how to fix things.

What I have a problem with with the last comment is that you only increment E_REV (or CVT_REV) if a significant amount of time has passed (and refer, in the comments, to debouncing). What I'm trying to determine is if those "significant amounts of time" are reasonable.

What could cause your sensor to bounce? What find of sensors are they?

Does the problem go away/get better if you pick up the serial communications speed? If you send less data?

Most of my comments happen in the same line as the code they are referencing, If the comment grows longer than the screen, instead of having to scroll the screen I simply return it to the next line underneath.

The 15873 came from an excel work book that calculated that value, see attached pictures, one is the calculated values, the other is the formulas of how I arrived at those numbers.

The engine speed pick up is a custom sensor created folowing this diagram:

found here: RPM Readings - SportDevices. Chassis and Engine Dynamometers

The other pickup is a PNP proximity sensor. I do not have a spec sheet for this, but just realized that it is of the 6-32V, but seems to work as expected at low speeds.

The de-bouncing of the interrupts was my project partner's idea, and it didn't seem to hurt. I have taken the time value down to 0 and it doesn't get any better or worse than before.

Capture.JPG