High Speed Reed Switch Sensing for Automotive Use

Background:
I'm in the process of designing a dashboard a early model vehicle. The Arduino Nano is used to interpret the signals my computer does not process (turn signals, CEL, etc). One of the things i'm attempting to get working is reading the speed sensor that's in the car from the factory. It's a reed switch, 9 teeth on the ring gear for each revolution. I using an ISR to pickup each time the reed switch is activated. The Arduino is sending all this information to an application via can frames.

Problem:
Right now my code does work, but the problem comes at higher speeds, my speed output is way too noisy - it'll jump several MPH while cruising at a fixed speed. Debouncing with a time based code would usually work here, but the high speed limits how long I can run a debounce wait. For instance, if i were to run a time based debounce of 10 ms, my highest speed i can read from the reed switch is 43MPH. Does anyone have any suggestions on how to "smooth" the speed value effectively?

/// Define global variables 
unsigned long Time;
byte LastBroadcast;

// Define global variables for Pins; r stands for read; bool is true or false (on/off in our case)
bool LBLr; // Left Blinker from Digital in on Arduino
bool RBLr; // Right Blinker from Digital in on Arduino
bool HBr; // High Beams from Digital in on Arduino
bool MALFr; // Check Engine Light from Digital in on Arduino
bool ParkBkr; // Park Brake From Digital in
bool LowFuelr; //Low Fuel from Digital in
bool BrkRearr; //Brake Lining Rear from Digital in
bool Fogr; //Fog Light from Digital in
bool BrkFrontr; // Brake Lining Front from Digtial in
bool RunLghtsr; //Running Lights from Digital in
bool AltFailr; //Alternator Fail from Digital in
bool BrakeFldr; //Brake Fluid Low from Digital in
bool OilPressr; //Oil Pressure Low from Analog

// Assign numbers/Pins to variables 
int ParkBkPin = 2; // Parking Brake on Pin D2
int LBLPin = 4; //Left Blinker on Pin D4
int HBPin = 5; // High Beams on Pin D5
int RBLPin = 6; // Right Blinker on Pin D6
int BrkRearPin = 7; // Brake Lining Rear on D7
int FogPin = 8; // Fog lights on D8
int BrkFrontPin = 9; // Brake Lining Front D9
int MALFPin = 10; // Check Engine (MALF) on Pin D10
int RunLghtsPin = 11; // Running Lights on pin D11
int AltFailPin = 12; // Alternator Fail on D12
int BrakeFldPin = 13; // Brake Fluid Low on D13
int OilPressPin = A0; // Low Oil Pressure on A0
int LowFuelPin = A2; // Low Fuel on Pin A2
int FuelLevelPin = A6; //Fuel level sensor on pin A6
int TempPin = A7; // Temp Sensor on pin A7
int FuelLevel; //define this as an integer
int TempVal;


//all of this is for speed calc 
int ReedSwitch = 3;  //Declaring pin for reed switch to be used for Speed Calculation. Digital Pin 2 or 3 must be used for the reed switch on the Nano
float start, finished;
float elapsed, time;
const int circumference = 1.9812; //circumference of tire in meters 
float circMetric=(circumference); 
float circImperial; // using 1 kilometer = 0.621371192 miles
volatile byte Speed; // Keeping this as byte so it's smaller and transmitts every interrupt (not sure if required)
volatile byte Speed1;
volatile unsigned long count = 0;
volatile unsigned long shortcount = 0;


//Fuel
const int numReadings = 250; //Size of array, larger the number the larger the array and the smoother the average value.
int readIndex = 0;
int readings[numReadings];
int total = 0;
int average = 0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  delay (3000); //3s startup delay 

//Speed Calc Stuff
  start= millis();
  circImperial=circMetric*.62137; // convert metric to imperial for MPH calculations
  
  Serial.begin(9600); //enable serial connection. Needs to be 9600 for bluetooth.
 // Serial.begin(115200); // 115200 for direct connection to comptuer 

  pinMode(LBLPin, INPUT_PULLUP);      // Left Blinker will activate with ground to pin
  pinMode(RBLPin, INPUT_PULLUP);      // Right Blinker will activate with ground to pin
  pinMode(HBPin, INPUT_PULLUP);       // High Beams will activate with ground to pin
  pinMode(MALFPin, INPUT_PULLUP);     // Check engine light will activate when pin is grounded
  pinMode(ParkBkPin, INPUT_PULLUP);   // Park Brake will activate when pin is grounded
  pinMode(LowFuelPin, INPUT_PULLUP);  // Low Fuel will activate when pin is grounded
  pinMode(BrkRearPin, INPUT_PULLUP);  // Activate when pin is grounded
  pinMode(FogPin, INPUT_PULLUP);      // Activate on ground to pin
  pinMode(BrkFrontPin, INPUT_PULLUP); // Activate when pin is grounded
  pinMode(RunLghtsPin, INPUT_PULLUP); // Activate on ground to pin
  pinMode(AltFailPin, INPUT_PULLUP);  // Activate when pin is grounded
  pinMode(BrakeFldPin, INPUT_PULLUP); // Activate when pin is grounded
  pinMode(OilPressPin, INPUT_PULLUP); // Activate when pin is grounded
   
  attachInterrupt(digitalPinToInterrupt(ReedSwitch), CalculateSpeed, FALLING); //Reedswitch(D3) is detecting a change in voltage(FALLING) and running CalculateSpeed() at each interrupt. Inputpullup not needed before here based on the way I wired it (i think)
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0; }
  
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CalculateSpeed() //ISR for interrupt. (Run this each time the interrupt is called.)
{
  if((millis()-start)>10) // for debounce.  20ms = 23MPH Max; 10ms = 43MPH Max
  
    //calculate elapsed
      elapsed=millis()-start;

    //reset start
      start=millis();
  
        //calculate speed in mph
      Speed1=(3600*circImperial/4)/elapsed; //This comes out somewhat close to actual MPH. Value needs to be changed in Realdash via gague math compared to a known speed.

      if (Speed1 >= 2){
        Speed = Speed1;    // No changes if Speed is higher than 2 MPH
      }

      if (Speed1 <= 2){
        Speed = 0;        // If speed is less than 2 MPH, Speed = 0. This is so speed doesn't show greater than 0 MPH when stopped
      }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() //Main functions that run continuously
{
Time = millis();

// Go read the ardiuno Pins
  ReadIOPins(); 
  
//Run the Calculate Fuel Level code
  CalculateFuelLevel();
  
//Check Temperature
  Temperature();
   
// Send Data to CanBus every 10 milliseconds: 23 ms orginally, not sure if 10ms is required for anything. Speed calc maybe? 
  if (Time - LastBroadcast > 20) 
    {
      LastBroadcast = millis();
      BuildCanFrames();
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ReadIOPins() // Reads current status of each pin; r stands for read
{
  LBLr = !digitalRead(LBLPin);
  RBLr = !digitalRead(RBLPin);
  HBr = !digitalRead(HBPin);
  MALFr = !digitalRead(MALFPin);
  ParkBkr = !digitalRead(ParkBkPin);
  LowFuelr = !digitalRead(LowFuelPin);
  BrkRearr = !digitalRead(BrkRearPin);
  Fogr = !digitalRead(FogPin);
  BrkFrontr = !digitalRead(BrkFrontPin);
  RunLghtsr = !digitalRead(RunLghtsPin);
  AltFailr = !digitalRead(AltFailPin);
  BrakeFldr = !digitalRead(BrakeFldPin);
  OilPressr = !analogRead(OilPressPin);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CalculateFuelLevel()  //Math to calculate fuel level. 
{
  //int tankvalue; //define integer to be used below
  total = total - readings[readIndex];
  readings[readIndex] = analogRead(FuelLevelPin);
  total = total + readings[readIndex];
  readIndex = readIndex + 1;
  if (readIndex >= numReadings) {
    readIndex = 0;
  }
  
  FuelLevel = total/numReadings;
 // tankvalue = analogRead(FuelLevelPin); //read the signal coming from the sender
 // FuelLevel = map(tankvalue, 80, 10, 0, 100); //convert that number to percentage. My values: 80 = empty(0); 10 = full(100) 
  
 if (FuelLevel < 0)
  {                     // Apply rule if tank is reading less than empty, Prevents negative values.
    FuelLevel = 0;
  }
 if (FuelLevel > 100)   // Apply rule if tank is reading more than full, Prevents values greater than 100.
  { 
    FuelLevel = 100; 
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Temperature()
{
  TempVal = analogRead(TempPin);            //Read the temperature pin (A7)
  float mv = (TempVal/1024.0)*5000;         //Scale value
  float celcius = mv/10;                    //Convert to C
  float Temperature = (celcius*9)/5 + 32;   //Convert to F
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BuildCanFrames()
{
  byte CanBuffer[8];

  // build & send CAN frames to RealDash.
  // a CAN frame payload is always 8 bytes (0-7) containing data in a manner. 
  // described by the RealDash custom channel description XML file
  // all multibyte values are handled as little endian by default.
  // endianess of the values can be specified in XML file if it is required to use big endian values

  // build 1st CAN frame:              //This is what is transmitted to Realdash 
  CanBuffer[0] = HBr;
  CanBuffer[1] = MALFr;
  CanBuffer[2] = LBLr;
  CanBuffer[3] = RBLr;
  CanBuffer[4] = ParkBkr;
  CanBuffer[5] = LowFuelr;
  CanBuffer[6] = BrkRearr;
  CanBuffer[7] = Fogr;

  // write first CAN frame to serial:
  SendCANFrameToSerial(3200, CanBuffer); //1st CAN frame id is 3200 

  // build 2nd CAN frame:
  CanBuffer[0] = BrkFrontr;
  CanBuffer[1] = RunLghtsr;
  CanBuffer[2] = AltFailr;
  CanBuffer[3] = BrakeFldr;
  CanBuffer[4] = OilPressr;
  CanBuffer[5] = FuelLevel;
  CanBuffer[6] = Speed;
  CanBuffer[7] = Temperature;

  // write second CAN frame to serial:
  SendCANFrameToSerial(3201, CanBuffer); //2nd CAN frame id is 3201, 3rd would be 3202, etc. 
}

// -----------------------------------

// I don't understand anything after this but it works 
void SendCANFrameToSerial(unsigned long canFrameId, const byte* frameData)
{
  // the 4 byte identifier at the beginning of each CAN frame
  // this is required for RealDash to 'catch-up' on ongoing stream of CAN frames
  const byte serialBlockTag[4] = { 0x44, 0x33, 0x22, 0x11 };
  Serial.write(serialBlockTag, 4);

  // the CAN frame id number (as 32bit little endian value)
  Serial.write((const byte*)&canFrameId, 4);

  // CAN frame payload
  Serial.write(frameData, 8);
}

read about direct port pin manipulation. this method make your sketch much faster.
in other RPM issue topics here I have seen that interrupt made only increment of a variable and main code take time after last calculation and counted difference of a variable and calculate rotation an then frequency.

Can you show us a picture of the noise form your scope? Is the noise random or repetitive? How is the reed switch wired? How long are the wires? Are they shielded?

const float circumference = 1.9812; right?

Unique naming is better, at least not similar to system variables.

The wires travel from the dashboard of the car to the rear end, i would guess approximately 10'. I do not believe they are shielded.

agree, i have yet to clean everything up.

This is an oversight, thank you.

const int numReadings = 250; //Size of array, larger the number the larger the array and the smoother the average value.
int readIndex = 0;
int readings[numReadings];
int total = 0;
  total = total - readings[readIndex];
  readings[readIndex] = analogRead(FuelLevelPin);
  total = total + readings[readIndex];

it is wrong.
first, INT variable can't contain number bigger as 32767. analogRead() gives maximum 1023, by 250 readings, 1023*250 =255750 . variable total in this case will contain -6394;
seconds, when you add the value to total why is array needed? it is unnecessary.

The goal here is to smooth the signal that I am reading from the fuel level sensor by taking the average from the array of values. I found a similar code online which is how I got to this. I am happy to make changes you suggest. Most all this code is multiple other codes combined with changes here and there.

instead of fragile, wearing out, bouncing reed switch is hall switch sensor recommended.
(there are analog output sensor and logic level output sensor - you need second)

Agree, I am attempting to use the vehicle’s original sensor so I don’t have to add my own, leaving everything plug and play. Hall sensor will be my final resort if I cannot get reed switch working.

Can you tell us the pulses per Mile? Basically you should know how fast the reed switch is changing at the max speed you wish to measure.

Next.... are you sure its a reed switch? I wouldn't expect any OEM would use a reed switch for MPH. And a gear tooth would not cause a reed switch to actuate. I would have expected it to be an inductive pickup.

pinMode(OilPressPin, INPUT_PULLUP); // Activate when pin is grounded
.....
OilPressr = !analogRead(OilPressPin);

either digital input with pull up or analog input

I am pretty sure it’s a reed switch. See factory wiring schematic:

1609 meters /(1.9/9)= ~7,313 pluses in a mile (I think)
1.9 is circumference of wheel
9 pulses per revolution

Good read: Moving average filter - Using Arduino / Sensors - Arduino Forum

The best operation from reed switches occurs when the magnetic field is oriented PARALLEL to reed. I wonder how your magnets are oriented?

Awesome info, thank you. i was thinking of this exact problem with my current method of averaging. I have changed the code to this new way of averaging. I think i did it correctly. I have also made other changes mentioned earlier in the thead:

/// Define global variables 
unsigned long Time;
byte LastBroadcast;

// Define global variables for Pins; r stands for read; bool is true or false (on/off in our case)
bool LBLr; // Left Blinker from Digital in on Arduino
bool RBLr; // Right Blinker from Digital in on Arduino
bool HBr; // High Beams from Digital in on Arduino
bool MALFr; // Check Engine Light from Digital in on Arduino
bool ParkBkr; // Park Brake From Digital in
bool LowFuelr; //Low Fuel from Digital in
bool BrkRearr; //Brake Lining Rear from Digital in
bool Fogr; //Fog Light from Digital in
bool BrkFrontr; // Brake Lining Front from Digtial in
bool RunLghtsr; //Running Lights from Digital in
bool AltFailr; //Alternator Fail from Digital in
bool BrakeFldr; //Brake Fluid Low from Digital in
bool OilPressr; //Oil Pressure Low from Analog

// Assign numbers/Pins to variables 
int ParkBkPin = 2; // Parking Brake on Pin D2
int LBLPin = 4; //Left Blinker on Pin D4
int HBPin = 5; // High Beams on Pin D5
int RBLPin = 6; // Right Blinker on Pin D6
int BrkRearPin = 7; // Brake Lining Rear on D7
int FogPin = 8; // Fog lights on D8
int BrkFrontPin = 9; // Brake Lining Front D9
int MALFPin = 10; // Check Engine (MALF) on Pin D10
int RunLghtsPin = 11; // Running Lights on pin D11
int AltFailPin = 12; // Alternator Fail on D12
int BrakeFldPin = 13; // Brake Fluid Low on D13
int OilPressPin = A0; // Low Oil Pressure on A0
int LowFuelPin = A2; // Low Fuel on Pin A2
int FuelLevelPin = A6; //Fuel level sensor on pin A6
int TempPin = A7; // Temp Sensor on pin A7
int FuelLevel; //define this as an integer
int TempVal;


//all of this is for speed calc 
int ReedSwitch = 3;  //Declaring pin for reed switch to be used for Speed Calculation. Digital Pin 2 or 3 must be used for the reed switch on the Nano
float start, finished;
float elapsed, time;
const float circumference = 1.9812; //circumference of tire in meters 
float circMetric=(circumference); 
float circImperial; // using 1 kilometer = 0.621371192 miles
volatile byte Speed; // Keeping this as byte so it's smaller and transmitts every interrupt (not sure if required)
volatile byte Speed1;
volatile unsigned long count = 0;
volatile unsigned long shortcount = 0;


//Fuel
const int numReadings = 250; //Size of array, larger the number the larger the array and the smoother the average value.
int readIndex = 0;
int readings[numReadings];
long total = 0;
int average = 0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  delay (3000); //3s startup delay 

//Speed Calc Stuff
  start= millis();
  circImperial=circMetric*.62137; // convert metric to imperial for MPH calculations
  
  Serial.begin(9600); //enable serial connection. Needs to be 9600 for bluetooth.
 // Serial.begin(115200); // 115200 for direct connection to comptuer 

  pinMode(LBLPin, INPUT_PULLUP);      // Left Blinker will activate with ground to pin
  pinMode(RBLPin, INPUT_PULLUP);      // Right Blinker will activate with ground to pin
  pinMode(HBPin, INPUT_PULLUP);       // High Beams will activate with ground to pin
  pinMode(MALFPin, INPUT_PULLUP);     // Check engine light will activate when pin is grounded
  pinMode(ParkBkPin, INPUT_PULLUP);   // Park Brake will activate when pin is grounded
  pinMode(LowFuelPin, INPUT_PULLUP);  // Low Fuel will activate when pin is grounded
  pinMode(BrkRearPin, INPUT_PULLUP);  // Activate when pin is grounded
  pinMode(FogPin, INPUT_PULLUP);      // Activate on ground to pin
  pinMode(BrkFrontPin, INPUT_PULLUP); // Activate when pin is grounded
  pinMode(RunLghtsPin, INPUT_PULLUP); // Activate on ground to pin
  pinMode(AltFailPin, INPUT_PULLUP);  // Activate when pin is grounded
  pinMode(BrakeFldPin, INPUT_PULLUP); // Activate when pin is grounded
  //pinMode(OilPressPin, INPUT_PULLUP); // Activate when pin is grounded
   
  attachInterrupt(digitalPinToInterrupt(ReedSwitch), CalculateSpeed, FALLING); //Reedswitch(D3) is detecting a change in voltage(FALLING) and running CalculateSpeed() at each interrupt. Inputpullup not needed before here based on the way I wired it (i think)
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0; }
  
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CalculateSpeed() //ISR for interrupt. (Run this each time the interrupt is called.)
{
  if((millis()-start)>10) // for debounce.  20ms = 23MPH Max; 10ms = 43MPH Max
  
    //calculate elapsed
      elapsed=millis()-start;

    //reset start
      start=millis();
  
        //calculate speed in mph
      Speed1=(3600*circImperial/4)/elapsed; //This comes out somewhat close to actual MPH. Value needs to be changed in Realdash via gague math compared to a known speed.

      if (Speed1 >= 2){
        Speed = Speed1;    // No changes if Speed is higher than 2 MPH
      }

      if (Speed1 <= 2){
        Speed = 0;        // If speed is less than 2 MPH, Speed = 0. This is so speed doesn't show greater than 0 MPH when stopped
      }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() //Main functions that run continuously
{
Time = millis();

// Go read the ardiuno Pins
  ReadIOPins(); 
  
//Run the Calculate Fuel Level code
  CalculateFuelLevel();
  
//Check Temperature
  Temperature();
   
// Send Data to CanBus every 10 milliseconds: 23 ms orginally, not sure if 10ms is required for anything. Speed calc maybe? 
  if (Time - LastBroadcast > 20) 
    {
      LastBroadcast = millis();
      BuildCanFrames();
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ReadIOPins() // Reads current status of each pin; r stands for read
{
  LBLr = !digitalRead(LBLPin);
  RBLr = !digitalRead(RBLPin);
  HBr = !digitalRead(HBPin);
  MALFr = !digitalRead(MALFPin);
  ParkBkr = !digitalRead(ParkBkPin);
  LowFuelr = !digitalRead(LowFuelPin);
  BrkRearr = !digitalRead(BrkRearPin);
  Fogr = !digitalRead(FogPin);
  BrkFrontr = !digitalRead(BrkFrontPin);
  RunLghtsr = !digitalRead(RunLghtsPin);
  AltFailr = !digitalRead(AltFailPin);
  BrakeFldr = !digitalRead(BrakeFldPin);
  OilPressr = !analogRead(OilPressPin);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CalculateFuelLevel()  //Math to calculate fuel level. 
{
  //int tankvalue; //define integer to be used below
  total -= readings[readIndex];
  total += analogRead(FuelLevelPin);
  readings[readIndex] = analogRead(FuelLevelPin);
  readIndex = (readIndex + 1) % numReadings;
  //return (float) total/numReadings; // no sure this applies here
  FuelLevel = 100 - (total/numReadings); //subtract from 100 because 100 = empty tank, 0 = full tank
  
  //total = total - readings[readIndex];
 // readings[readIndex] = analogRead(FuelLevelPin);
 // total = total + readings[readIndex];
 // readIndex = readIndex + 1;
 // if (readIndex >= numReadings) {
 //   readIndex = 0;
 // }
  
  FuelLevel = total/numReadings;
 // tankvalue = analogRead(FuelLevelPin); //read the signal coming from the sender
 // FuelLevel = map(tankvalue, 80, 10, 0, 100); //convert that number to percentage. My values: 80 = empty(0); 10 = full(100) 
  
 if (FuelLevel < 0)
  {                     // Apply rule if tank is reading less than empty, Prevents negative values.
    FuelLevel = 0;
  }
 if (FuelLevel > 100)   // Apply rule if tank is reading more than full, Prevents values greater than 100.
  { 
    FuelLevel = 100; 
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Temperature()
{
  TempVal = analogRead(TempPin);            //Read the temperature pin (A7)
  float mv = (TempVal/1024.0)*5000;         //Scale value
  float celcius = mv/10;                    //Convert to C
  float Temperature = (celcius*9)/5 + 32;   //Convert to F
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BuildCanFrames()
{
  byte CanBuffer[8];

  // build & send CAN frames to RealDash.
  // a CAN frame payload is always 8 bytes (0-7) containing data in a manner. 
  // described by the RealDash custom channel description XML file
  // all multibyte values are handled as little endian by default.
  // endianess of the values can be specified in XML file if it is required to use big endian values

  // build 1st CAN frame:              //This is what is transmitted to Realdash 
  CanBuffer[0] = HBr;
  CanBuffer[1] = MALFr;
  CanBuffer[2] = LBLr;
  CanBuffer[3] = RBLr;
  CanBuffer[4] = ParkBkr;
  CanBuffer[5] = LowFuelr;
  CanBuffer[6] = BrkRearr;
  CanBuffer[7] = Fogr;

  // write first CAN frame to serial:
  SendCANFrameToSerial(3200, CanBuffer); //1st CAN frame id is 3200 

  // build 2nd CAN frame:
  CanBuffer[0] = BrkFrontr;
  CanBuffer[1] = RunLghtsr;
  CanBuffer[2] = AltFailr;
  CanBuffer[3] = BrakeFldr;
  CanBuffer[4] = OilPressr;
  CanBuffer[5] = FuelLevel;
  CanBuffer[6] = Speed;
  CanBuffer[7] = Temperature;

  // write second CAN frame to serial:
  SendCANFrameToSerial(3201, CanBuffer); //2nd CAN frame id is 3201, 3rd would be 3202, etc. 
}

// -----------------------------------

// I don't understand anything after this but it works 
void SendCANFrameToSerial(unsigned long canFrameId, const byte* frameData)
{
  // the 4 byte identifier at the beginning of each CAN frame
  // this is required for RealDash to 'catch-up' on ongoing stream of CAN frames
  const byte serialBlockTag[4] = { 0x44, 0x33, 0x22, 0x11 };
  Serial.write(serialBlockTag, 4);

  // the CAN frame id number (as 32bit little endian value)
  Serial.write((const byte*)&canFrameId, 4);

  // CAN frame payload
  Serial.write(frameData, 8);
}

The best operation from reed switches occurs when the magnetic field is oriented PARALLEL to reed. I wonder how your magnets are oriented?

I would think they're oriented in the best possible way since this is the original sensor.

Resolution Problem:

Assuming a clean signal, there wouldn't be anything significant to smooth if you had better timing resolution. At low speeds, say 1 pulse per 100ms, each millisecond represents 1% of your speed calculation. At higher speeds, say 1 pulse per 10ms, each millisecond represents 10% of your speed calculation which could easily be ±4 mph. If 1 pulse per 5ms (86 mph), each millisecond represents 20% of 86 (17.2 mph), so now ±1 ms represents ±17.2 mph.

Solution:

micros()

/// Define global variables
unsigned long startTime;
unsigned long LastBroadcast;

// Define global variables ; r stands for read;
byte FlagByte1 = 0;
byte FlagByte2 = 0;

//FlagByte1 are: (less significant bit first)
#define LBLr (1<<0)// Left Blinker from Digital in on Arduino
#define RBLr (1<<1)// Right Blinker from Digital in on Arduino
#define HBr (1<<2)// High Beams from Digital in on Arduino
#define MALFr (1<<3)// Check Engine Light from Digital in on Arduino
#define ParkBkr (1<<4)// Park Brake From Digital in
#define LowFuelr (1<<5)//Low Fuel from Digital in
#define BrkRearr (1<<6)//Brake Lining Rear from Digital in
#define Fogr (1<<7)//Fog Light from Digital in

//FlagByte2 are:
#define BrkFrontr (1<<0)// Brake Lining Front from Digtial in
#define RunLghtsr (1<<1)//Running Lights from Digital in
#define AltFailr (1<<2)//Alternator Fail from Digital in
#define BrakeFldr (1<<3)//Brake Fluid Low from Digital in
#define OilPressr (1<<4)//Oil Pressure Low from Analog
* /

// Assign numbers/Pins to variables
#define ParkBkPin  2 // Parking Brake on Pin D2
#define LBLPin  4 //Left Blinker on Pin D4
#define HBPin  5 // High Beams on Pin D5
#define RBLPin  6 // Right Blinker on Pin D6
#define BrkRearPin  7 // Brake Lining Rear on D7
#define FogPin  8 // Fog lights on D8
#define BrkFrontPin  9 // Brake Lining Front D9
#define MALFPin  10 // Check Engine (MALF) on Pin D10
#define RunLghtsPin  11 // Running Lights on pin D11
#define AltFailPin  12 // Alternator Fail on D12
#define BrakeFldPin  13 // Brake Fluid Low on D13
#define OilPressPin  A0 // Low Oil Pressure on A0
#define LowFuelPin  A2 // Low Fuel on Pin A2
#define FuelLevelPin  A6 //Fuel level sensor on pin A6
#define TempPin  A7 // Temp Sensor on pin A7
byte FuelLevel = 0;
byte Speed = 0;
//all of this is for speed calc
const int ReedSwitch = 3;  //Declaring pin for reed switch to be used for Speed Calculation. Digital Pin 2 or 3 must be used for the reed switch on the Nano
float Temperature = 0;
float elapsed, fTime;
#define circumference  1.9812 //circumference of tire in meters 
float circMetric = (circumference);
float circImperial = 1.231058244; // using 1 kilometer = 0.621371192 miles
volatile unsigned long count = 0;

//Fuel
const byte numReadings = 50; // larger the number longer time to calculate and the smoother the average value.
unsigned int total = 0;
#define readInput(b) ( (b)<8 ? (PIND &(1<<(b))): (PINB &(1<<(b-8))) )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  delay (3000); //3s startup delay
  Serial.begin(9600); //enable serial connection. Needs to be 9600 for bluetooth.
  // Serial.begin(115200); // 115200 for direct connection to comptuer
  PORTB |= B11110100;
  PORTD |= B00011111;
  startTime = millis();
  attachInterrupt(digitalPinToInterrupt(ReedSwitch), CalculateSpeed, FALLING); //Reedswitch(D3) is detecting a change in voltage(FALLING) and running CalculateSpeed() at each interrupt. Inputpullup not needed before here based on the way I wired it (i think)
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CalculateSpeed() { //ISR for interrupt. (Run this each time the interrupt is called.)
  count++;
}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() {

  // Send Data to CanBus every 10 milliseconds: 23 ms orginally, not sure if 10ms is required for anything. Speed calc maybe?
  if (millis() - LastBroadcast >= 23)
  {
    startTime = millis() - startTime;
    Speed = (byte)((492.274032544783 * (float)count)) / (float)startTime);
    startTime = millis();
    count = 0;
    if (Speed < 5)Speed = 0;
    //FlagByte1=
    readInput(LBLPin) ? FlagByte1 &= ~LBLr : FlagByte1 |= LBLr;
    readInput(RBLPin) ? FlagByte1 &= ~RBLr : FlagByte1 |= RBLr;
    readInput(HBPin) ? FlagByte1 &= ~HBr : FlagByte1 |= HBr;
    readInput(MALFPin) ? FlagByte1 &= ~MALFr : FlagByte1 |= MALFr;
    readInput(ParkBkPin) ? FlagByte1 &= ~ ParkBkr : FlagByte1 |= ParkBkr;
    readInput(LowFuelPin) ? FlagByte1 &= ~ LowFuelr : FlagByte1 |= LowFuelr;
    readInput(BrkRearPin) ? FlagByte1 &= ~ BrkRearr : FlagByte1 |= BrkRearr;
    readInput(FogPin) ? FlagByte1 &= ~ Fogr : FlagByte1 |= Fogr;
    //FlagByte2
    readInput(BrkFrontPin) ? FlagByte2 &= ~ BrkFrontr : FlagByte2 |= BrkFrontr;
    readInput(RunLghtsPin) ? FlagByte2 &= ~ RunLghtsr : FlagByte2 |= RunLghtsr;
    readInput(AltFailPin) ? FlagByte2 &= ~ AltFailr : FlagByte2 |= AltFailr;
    readInput(BrakeFldPin) ? FlagByte2 &= ~ BrakeFldr : FlagByte2 |= BrakeFldr;
    readInput(OilPressPin) ? FlagByte2 &= ~ OilPressr : FlagByte2 |= OilPressr;

    for (byte i = 0; i < numReadings; i++)  total = analogRead(FuelLevelPin);
  FuelLevel = map(total / numReadings, 80, 10, 0, 100); //convert that number to percentage. My values: 80 = empty(0); 10 = full(100)

  Temperature = ((float)analogRead(TempPin) * 0, 87890625)  + 32.0; // °F
    noInterrupts();
    BuildCanFrames();
    LastBroadcast = millis();
    interrupts();
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BuildCanFrames() {
  byte CanBuffer[8];

  // build & send CAN frames to RealDash.
  // a CAN frame payload is always 8 bytes (0-7) containing data in a manner.
  // described by the RealDash custom channel description XML file
  // all multibyte values are handled as little endian by default.
  // endianess of the values can be specified in XML file if it is required to use big endian values

  // build 1st CAN frame:              //This is what is transmitted to Realdash
  CanBuffer[0] = FlagByte1 & HBr;
  CanBuffer[1] = FlagByte1 & MALFr;
  CanBuffer[2] = FlagByte1 & LBLr;
  CanBuffer[3] = FlagByte1 & RBLr;
  CanBuffer[4] = FlagByte1 & ParkBkr;
  CanBuffer[5] = FlagByte1 & LowFuelr;
  CanBuffer[6] = FlagByte1 & BrkRearr;
  CanBuffer[7] = FlagByte1 & Fogr;

  // write first CAN frame to serial:
  SendCANFrameToSerial(3200, CanBuffer); //1st CAN frame id is 3200

  // build 2nd CAN frame:
  CanBuffer[0] = FlagByte2 & BrkFrontr;
  CanBuffer[1] = FlagByte2 & RunLghtsr;
  CanBuffer[2] = FlagByte2 & AltFailr;
  CanBuffer[3] = FlagByte2 & BrakeFldr;
  CanBuffer[4] = FlagByte2 & OilPressr;
  CanBuffer[5] = FuelLevel;
  CanBuffer[6] = Speed;
  CanBuffer[7] = (byte)Temperature;

  // write second CAN frame to serial:
  SendCANFrameToSerial(3201, CanBuffer); //2nd CAN frame id is 3201, 3rd would be 3202, etc.
}

void SendCANFrameToSerial(unsigned long canFrameId, const byte* frameData) {
  // the 4 byte identifier at the beginning of each CAN frame
  // this is required for RealDash to 'catch-up' on ongoing stream of CAN frames
  const byte serialBlockTag[4] = { 0x44, 0x33, 0x22, 0x11 };
  Serial.write(serialBlockTag, 4);

  // the CAN frame id number (as 32bit little endian value)
  Serial.write((const byte*)&canFrameId, 4);

  // CAN frame payload
  Serial.write(frameData, 8);
}