Hi Everyone,
The intend of this code is to calculate RPM of a an engine using frequency signal from a Halleffect sensor ,display it on 2.42oled and drive Neopixel shift light. At this stage signal is simulated with a square function generator with amplitude of -5V (triggering at LOW state).
When sending 100Hz square wave hardware reads 60-120RPM (jumping between these 2 values)
With 500Hz signal, RPM jumps between 540 and 600 etc... Seems like bunch of wrong calculations on my end...
In my understanding 50Hz (events a second) should equal to 3000RPM (events in a minute) etc. In theory engine signal would never exceed ~8000RPM (~150Hz). Therefore, Arduino Nano Every should easily handle these frequencies with decent accuracy, without necessity of using Low-Pass filters.
Hardware used:
-Arduino Nano Every (Atmega 4809 @ 20MHz)
-
2.42 oled connected on SPI (using u8g2 library)
-
8 Bit 2812 RGB (using Adafruit NeoPixel library)
-
[Not a single "delay" used in the code.]
Code bellow (skipped long pixel() function for clarity of reading):
#include <Arduino.h>
//~~~PIXEL DEF~~~//
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
#define PIN 4 // PIN SENDING DATA TO LED STRIP
#define NUMPIXELS 8 // LEDS IN THE ARRAY
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
//~~~SCREEN DEF~~~//
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
U8G2_SSD1309_128X64_NONAME0_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
//~~~RPM CALC~~~//
volatile int rpmcount = 0;
int rpm = 0;
unsigned long lastmillis = 0;
unsigned long timenow = millis();
boolean detectState = false;
boolean lastDetectState = false;
int hallPin = 2; //Halleffect sensor for RPM
int led = 0; // for neopixel function
void setup(void) {
u8g2.begin(); // INITIALIZE 2.42" oled screen
pixels.begin(); // INITIALIZE NeoPixel strip
Serial.begin(9600);
pinMode(hallPin, INPUT_PULLUP); //read pulses from hall sensor
attachInterrupt(digitalPinToInterrupt(2), rpm_interr, FALLING); //interruprt for rpm calculation
}
void loop(void)
{
rpm_calc();
rpm_display(rpm);
}
void rpm_calc(){
//~~~Never executes the statement when calling for millis() instead of previously defined timenow~~~//
if(timenow - lastmillis >=100UL){
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
detachInterrupt(digitalPinToInterrupt(2)); //time sensitive calculations
//~~~rpm offlay close to input Hz... constantly oscillates~~~//
rpm = rpmcount * 60 ; // convert to Rev per Minute
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
led = map(rpm, 0, 8000, 0, 8); // convert rpm to 1-8 values to drive LEDS
pixel(led); // calling function lighting up pixels of the strip (led=1, 1st led lit etc)
}
rpmcount = 0;
lastmillis = millis();
attachInterrupt(digitalPinToInterrupt(2), rpm_interr, FALLING);
}
//~~~Used with interrupt~~~//
void rpm_interr(){
rpmcount++;
}
//~~~Test function to display obtained RPM~~~//
void rpm_display(int revs){
u8g2.clearBuffer();
u8g2.setFontDirection(0);
u8g2.setDrawColor(1);
u8g2.setFont(u8g2_font_10x20_mf);
u8g2.drawStr(112, 20, "R");
u8g2.drawStr(112, 40, "P");
u8g2.drawStr(112, 60, "M");
u8g2.setCursor(5, 40);
u8g2.print(revs);
u8g2.sendBuffer();
}
Code inspired by other codes found on the forum.
I would highly appreciate any help to make this code work correctly and convert input frequency to RPM. I would like to stop it from jumping between values. Perhaps, show RPM in the increments of 100s? Any suggestions for more reliable calculations?
Thanks in advance!