Distance counter

I want to make a counter because distance data goes to zero when it reach 30000(600 meters).

Meters are printed fine, when car move, meters are running, when car stops meters stop running.
But counter increase it's value endlessly, if meters are two, then counter is 4, 8, 16 etc. until i turn ignition off.
I tried other ways instead of counter += meters, but always same result.

So, i want counter simply store meters, not multiply it all the time.

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string


#define CAN0_INT 2                              // Set INT to pin 2
MCP_CAN CAN0(9);                               // Set CS to pin 9

uint32_t meters;
uint32_t counter;


void setup() {

  Serial.begin(115200);
  
  
   // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK)
   
  
  
  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.

  pinMode(CAN0_INT, INPUT);                            // Configuring pin for /INT input

}

void loop() {
   if(!digitalRead(CAN0_INT))                         // If CAN0_INT pin is low, read receive buffer
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);      // Read data: len = data length, buf = data byte(s)

   sprintf(msgString, "Standard ID: 0x%.3lX       DLC: %1d  Data:", rxId, len);
  

   for (byte i = 0; i < len; i++) {
      sprintf(msgString, " 0x%.2X", rxBuf[i]);
//      Serial.print(msgString);
      

      }

    }

    if (rxId == 0x5A0) {
      uint16_t rawDistance = (uint16_t)rxBuf[6] << 8;
      rawDistance |= rxBuf[5];
      
      meters = uint32_t (rawDistance / 50);
      
      counter += meters;
      
      Serial.println(meters);
      Serial.println(counter);
   
      }
  }

Consider what happens once rxId has a value of 0x5A0. When will it change ?

It will change when other can-frames are received.
I had there else if for other can-frames, but i deleted for this example.
In my understanding that should not play a role with this.
Maybe i did not understand what you mean.

How often to other CAN frames arrive ?

Start by setting rxId to something other than 0x5A0 once you have added the distance to counter

ECU's "user manual" says that most of them arrive in every 20ms even data does not change in frame. If i understood it right.

Should i also try to change something to for (byte i = 0; i < len; i++)?

That sounds like a problem if it is true because if the message with an rxId of 0x5A0 is received repeatedly then no wonder that the counter variable is updated frequently

What else is sent in the rxBuf array ?

In this 0x540, byte 5 is low byte and 6 is high for distance.
I wonder why meters are counted as it should still.
In my opinion, meters should be divided with 50 also every time. Fortunately no in this case.

Using a Uno?

No, i am using Mega

Most likely the Mega is doing a unit16_t instead of a real unit32_t. Just a guess.

Change 'counter' to 'distanceTraveled'. The 32-bit number should be good for 85899 kilometers.

  if (rxId == 0x5A0) {
    static uint16_t lastRawDistance = 0;
    uint16_t rawDistance = (uint16_t)rxBuf[6] << 8;
    rawDistance |= rxBuf[5];
    uint16_t newRawDistance = rawDistance - lastRawDistance;
    lastRawDistance = rawDistance;

    distanceTraveled += newRawDistance;
    meters = distanceTraveled / 50;

    Serial.println(newRawDistance);
    Serial.println(meters);
  }

Alright! Need to try that in next weekend.

I tried that example, but meters jump to 1351 after 81 meters.

distanceTraveled was uint32_t, now uint16_t.
It goes zero after about 4095 now. Values not jump anymore.

81 meters is a 'distanceTraveled' of 4050.
1351 meters is a 'distanceTraveled' of 67500;

the difference is 63450. That is close to 65535, the maximum value that fits in an uint16_t. I suspect a math underflow where rawDistance was less than lastRawDistance. That would result in a small negative number which looks like a very large unsigned number.

Try this version which will report any backward counting:

  if (rxId == 0x5A0)
  {
    static uint16_t lastRawDistance = 0;
    uint16_t rawDistance = (uint16_t)rxBuf[6] << 8;
    rawDistance |= rxBuf[5];
    uint16_t newRawDistance = rawDistance - lastRawDistance;

    // Report a problem if the distance traveled is more 
    // than a kilometer.
    if (newRawDistance > 50000u)
    {
      Serial.println("Math problem");
      Serial.print("    lastRawDistance: ");
      Serial.println(lastRawDistance);
      Serial.print("    rawDistance: ");
      Serial.println(rawDistance);
      while(1) {}  // Halt the sketch if this error occurs.
    }

    distanceTraveled += newRawDistance;

    lastRawDistance = rawDistance;

    meters = distanceTraveled / 50;

    // Only report the distance traveled when meters change
    // to avoid spamming the serial output
    static uint32_t lastMeters = 0;
    if (meters != lastMeters)
    {
      Serial.println(meters);
      lastMeters = meters;
    }
  }

Problem report come almost immediately when car moves. Cannot get more than 2 meters, that is the record. Once report came even a car did not move at all.

Yes when rawDistance is less than lastRawdistance.

What were the numbers reported with the errors?

I wrote down first three attemps:

1
2
Math problem
lastRawDistance: 136
rawDistance: 136

1
Math problem
lastRawDistance: 62
rawDistance: 62

1
2
3
4
Math problem
lastRawDistance: 224
rawDistance: 223

That is weird. 136 isn't less than 136 and 62 isn't less than 62. After the subtractions, the newRawDistance should be zero, not > 50000!

Of course, 223 IS less than 224 so subtracting 224 from 223 gives an underflow: 65535. That shows that the 'rawDistance' can go DOWN. Let's try ignoring backward motion:

if (rxId == 0x5A0)
  {
    static uint16_t lastRawDistance = 0;
    uint16_t rawDistance = (uint16_t)rxBuf[6] << 8;
    rawDistance |= rxBuf[5];
    uint16_t newRawDistance = rawDistance - lastRawDistance;

    // Report a problem if the distance traveled is more 
    // than a kilometer.
    if (newRawDistance > 50000u)
    {
      Serial.println("Moving backward? Ignoring.");
      Serial.print("    lastRawDistance: ");
      Serial.println(lastRawDistance);
      Serial.print("    rawDistance: ");
      Serial.println(rawDistance);
      
      newRawDistance = 0;
    }

    distanceTraveled += newRawDistance;

    lastRawDistance = rawDistance;

    meters = distanceTraveled / 50;

    // Only report the distance traveled when meters change
    // to avoid spamming the serial output
    static uint32_t lastMeters = 0;
    if (meters != lastMeters)
    {
      Serial.println(meters);
      lastMeters = meters;
    }
  }

I do not understand why it think newRawDistance is over 50000, but it meters come as should in this short test.
Because rawDistance - lastRawDistance as mentioned?

There's no equal numbers anymore...