Serial.println will miss a decimal point and spike my data 100 fold

Hi All,

I'm looking to use Serial.println() to send the data from a rotating mouse wheel to the computer where I can save it. It works 99% of the time, but every now and then it will miss a decimal point, causing the data to spike up 100 fold. The microcontroller seems to be reading everything fine, but just forgets to put the decimal point in the right spot. I don't want to filter through hundreds of thousands of data points looking for and fixing the spikes.

Is this a problem with my code, a variable type, or with my arduino nano?

The function that prints out the data is at the bottom and called screen()

Thanks in advance!
JJ


image

#include <Servo.h>
#include <Arduino.h>
#include <U8g2lib.h>
#include <U8x8lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
///////////////////////////////////////////////
//THIS IS ALL THAT NEEDS TO BE CHANGED/////////
///////////////////////////////////////////////
float lockedPeriod = .001; ///// time after startup for which the wheel will stay locked - enter in hours
float unlockPeriod = 24; ////time the wheel will stay unlocked for - enter in hours
//////////////////////////////////////////////



int lockedpos = 120;  //servo position to lock
int openpos = 100; //servo position to unlock
Servo myservo; // servo
int hallsensor = 2; //hall sensor pin

volatile long count; //rotation count

U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(A5, A4);

enum states
{
  lock,
  unlock,
  relocked
};

byte currentState;
float stateStartTime;
float statePeriod;
unsigned long currentTime;
unsigned long printTime;

unsigned long relockedPeriod;



void setup()
{
  Serial.begin(57600);

  while (!Serial);
  currentState = lock;
  stateStartTime = millis();

  count = 0;

  u8x8.begin();
  u8x8.setFlipMode(1);
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
  u8x8.setInverseFont(1);
  u8x8.setCursor(1, 0);
  u8x8.print("current status");

  u8x8.setCursor(1, 3);
  u8x8.print("distance (m)");
  u8x8.setCursor(1, 6);
  u8x8.print("time (min)");
  u8x8.setInverseFont(0);
  u8x8.setCursor(1, 1);
  u8x8.print("locked");

  attachInterrupt(digitalPinToInterrupt(hallsensor), sensor, FALLING); //attach interrupt
  pinMode(hallsensor, INPUT); //setup hall sensor
  myservo.attach(9); //setup pin for servo
  myservo.write(lockedpos); //default state is locked 
  statePeriod = (lockedPeriod * 1000 * 60 * 60);
}

void loop()
{
  currentTime = millis();
  switch (currentState)
  {
    case lock:
      if (currentTime - stateStartTime > statePeriod)
      {
        myservo.write(openpos); //open servo when condition reached
        stateStartTime = currentTime;
        statePeriod = (unlockPeriod * 1000 * 60 * 60); //converting to hours 
        currentState = unlock;
        u8x8.clearLine(1);
        u8x8.setCursor(1, 1);
        u8x8.print("unlocked"); //update screen

        break;
      }
    case unlock:
      if (currentTime - stateStartTime > statePeriod)
      {
        myservo.write(lockedpos); //relocking the wheel
        stateStartTime = currentTime;
        statePeriod = relockedPeriod;
        currentState = relocked;
        u8x8.clearLine(1);
        u8x8.setCursor(1, 1);
        u8x8.print("locked"); //update screen
        break;
      }

  }

  screen();

}




void sensor () { //interrupt to measure rotations
  count++; //every time hall effect sensor goes off, add 1 to count

}

void screen () { //function to update screen and serial monitor 

  Serial.println(float(count * .054 * 2 * 3.14));


  u8x8.setCursor(1, 4);
  u8x8.print(count * .054 * 2 * 3.14); //distance in meters (2 * pi * r)


printTime = millis() / 1000 / 60 ; //time in minutes since power on 

  u8x8.setCursor(1, 7);
  u8x8.print(printTime); //printing time 
  
}
1 Like

There are at least two potential sources of your problem: one is the PC connection, which we know nothing about.

The other is that "count" is a multibyte variable shared with an interrupt routine, and can be corrupted by the interrupt, while it is being accessed in the main program. You MUST protect the variable. One approach is to modify your code as follows:

void screen () { //function to update screen and serial monitor 

noInterrupts();
long count_copy = count;  //make a copy of count with interrupts turned off
interrupts();
Serial.println(count_copy * .054 * 2 * 3.14);
1 Like

Hell yeah that worked! Thank you!

PROTECT THE VARIABLE AT ALL COSTS

Funny, we give that advice all the time, but never see such a clear demonstration as this, that it's a real problem!

1 Like

You could use the following little example

void setup() {
  Serial.begin(115200);
  Serial.println(F("Wait for error...\r\n\r\nfrom - to - difference"));
  // set up Timer 1 for 2 kHz
  TCCR1A = 0;                        // normal operation
  TCCR1B = bit(WGM12) | bit(CS10);   // CTC, no pre-scaling
  OCR1A =  7999;                     // compare A register value for 2 kHz
  TIMSK1 = bit (OCIE1A);             // interrupt on Compare A Match
}

volatile unsigned long Counter;

ISR(TIMER1_COMPA_vect) {
  Counter++;
}

void loop() {
  static unsigned long before;
  //  noInterrupts(); // uncomment to make it work
  unsigned long current = Counter;
  //  interrupts();   // uncomment to make it work

  unsigned long difference = current - before;
  // loop is so fast, you should never see a difference  bigger than 1
  if (difference > 1) {
    Serial.print(before, HEX);
    Serial.write(' ');
    Serial.print(current, HEX);
    Serial.print(F(" - "));
    Serial.println(difference, HEX);
  }
  before = current;
}
Wait for error...

from - to - difference
FFF 10FF - 100
10FF 1001 - FFFFFF02
10FF 11FF - 100
11FF 1101 - FFFFFF02
14FF 15FF - 100
15FF 1501 - FFFFFF02
2FFF 30FF - 100
30FF 3001 - FFFFFF02
47FF 48FF - 100
48FF 4801 - FFFFFF02
55FF 56FF - 100
56FF 5601 - FFFFFF02