Enter character or decimal number in Serial Monitor to change variables

Hello,

I am using an Arduino UNO R3 connected to an MPU6050 sensor and i am trying to make the program accept both datatype char and float in the Serial Monitor but it only works for char. I can't make it work for float. When i enter 'a' or 'b', the onboard LED of the Arduino turns ON and OFF, respectively, as expected. But if i enter a decimal number, then the variable1 simply turns to 10 all the time, which from my understanding is the decimal ASCII code for new line. I tried to have an if statement which would check if the value of val is neither of those 2 characters and then accept the float value entered but it doesn't work. Any help is much appreciated.

Here is my full sketch:

#include <FlexiTimer2.h>
#include <Wire.h>
#include <MPU6050.h>

MPU6050 accelgyro; 

volatile int16_t ax, ay, az, gx, gy, gz;

float Gyro_x;
float accelAngle = 0;
float variable1 = 12.34;

char val;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 300;

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  Wire.begin();                            //join I2C bus sequence
  Serial.begin(9600);                      //open the serial monitor, set the baud rate to 9600
  accelgyro.initialize();                    //initialize MPU6050
  
  delay(4000);  //give enough time for MPU-6050 values to stabilise.
  
  FlexiTimer2::set(5, timerISR);
  FlexiTimer2::start();
  
  startMillis = millis();
}

void loop()
{
  currentMillis = millis(); 
  
  if(Serial.available() > 0)
  {
    val = Serial.read();
    //Serial.println(val);
    
    switch(val)  //switch case statement
    {
      case 'a': 
        digitalWrite(LED_BUILTIN, HIGH);
        //Serial.println("**** LED ON ****");
        break;       
      case 'b': 
        digitalWrite(LED_BUILTIN, LOW);
        //Serial.println("**** LED OFF ****");
        break;
    }


    if(val != 'a' && val != 'b')
    {
      variable1 = val;
    }
  }
    
  // data is printed every 300 ms.
  if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  {
    //By default, Serial. print() prints floats with two decimal digits.
     Serial.print(accelAngle);   
     Serial.print(";");
     Serial.print(Gyro_x); 
     Serial.print(";");
     Serial.print(variable1);
     Serial.println(";");
    
    startMillis = currentMillis;  // to save the new start time.
  }
}

void timerISR()
{
  interrupts();   //Re-enables interrupts
  accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);     //IIC to get MPU6050 six-axis data ax ay az gx gy gz
  angle_calculation(ax, ay, az, gx, gy, gz);      // get angle
}

void angle_calculation(int16_t ax, int16_t ay, int16_t az, int16_t gx, int16_t gy, int16_t gz)
{
  accelAngle = -atan2(ay, az) * (180/ PI); //tilt angle from accelerometer
  Gyro_x = -gx / 131.0; //angular speed of X-axis from gyro
}

Here is a sample output from the Arduino Serial Monitor:

76.14;-0.77;12.34;
75.48;-0.65;12.34;
75.27;-0.68;12.34;
74.66;-0.65;12.34;
75.79;-0.62;10.00;
75.27;-0.73;10.00;
75.23;-0.63;10.00;
75.76;-0.82;10.00;

The third variable value always changes to 10 when i type anything and press Enter. I would like it to accept any decimal number instead.

Serial.read() reads one byte. A float data type can't fit in one byte. A float from the serial monitor will be a series of characters. The serial input basics tutorial may help. Example #5 shows reading a float as characters and parsing and converting the characters (string) to a float number.

The code below does what you want.
There are two options here, the Arduino toFloat() method which is not very good
"kdf" => 0.0
"5.3.6" => 5.3
"5a" => 5.0
The atof() method in serial basics tutorial has the same problems, toFloat() just calls atof()

and the SafeString method which is much more reliable (from my SafeString library available from Arduino library manager)
"kdf" => "kdf is not a float"

If you want to try the SafeString method, uncomment the #define USE_SAFESTRING at the top of the code
I commented some of your code to simplify the testing.
Also if you start printing a lot of debug messages you may run into problems with the Serial.print's blocking the rest of the loop. See my tutorial Arduino Serial I/O for the Real World for solutions to that problem if it arises

//https://forum.arduino.cc/?topic=731754#msg4922779
//#include <FlexiTimer2.h>
//#include <Wire.h>
//#include <MPU6050.h>
//
//MPU6050 accelgyro;

// uncomment this define to use the more robust SafeString toFloat() method
//#define USE_SAFESTRING

#ifdef USE_SAFESTRING
#include "SafeString.h"
// install from Arduino library manager
#endif

volatile int16_t ax, ay, az, gx, gy, gz;

float Gyro_x;
float accelAngle = 0;
float variable1 = 12.34;

String input;
char val;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 1000;//300;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  //  Wire.begin();                            //join I2C bus sequence
  Serial.begin(9600);                      //open the serial monitor, set the baud rate to 9600
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  //  accelgyro.initialize();                    //initialize MPU6050

  delay(4000);  //give enough time for MPU-6050 values to stabilise.

  //  FlexiTimer2::set(5, timerISR);
  //  FlexiTimer2::start();

  startMillis = millis();
}

// read a line of input
bool readInput() {
  if (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '\n') {
      return true;
    }
    // else
    input += c;
  }
  return false;
}

void loop() {
  currentMillis = millis();

  if (readInput()) { // got a line of input
    input.trim();
    Serial.println(input);
    if (input == "a") {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.println("**** LED ON ****");
    } else if (input == "b") {
      digitalWrite(LED_BUILTIN, LOW);
      Serial.println("**** LED OFF ****");
    } else {
      // try to convert to float
#ifndef USE_SAFESTRING
      variable1 = input.toFloat();
      // NOTE: this is not a very good conversion fn
      // "kdf" => 0.0
      // "5.3.6" => 5.3
      // "5a" => 5.0
#else
      // SafeString library has a better conversion fn
      cSF(sfInput, input.length()); // create a temporary SafeString large enough to hold the input
      sfInput = input.c_str();
      if (!sfInput.toFloat(variable1)) {
        Serial.print(input); Serial.println(F(" is not a float."));
      }
#endif
    }
    input = ""; // clear for next line of input
  }

  // data is printed every 300 ms.
  if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  {
    //By default, Serial. print() prints floats with two decimal digits.
    //     Serial.print(accelAngle);
    //     Serial.print(";");
    //     Serial.print(Gyro_x);
    //     Serial.print(";");
    Serial.print(variable1);
    Serial.println(";");

    startMillis = currentMillis;  // to save the new start time.
  }
}

//void timerISR()
//{
//  interrupts();   //Re-enables interrupts
//  accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);     //IIC to get MPU6050 six-axis data ax ay az gx gy gz
//  angle_calculation(ax, ay, az, gx, gy, gz);      // get angle
//}
//
//void angle_calculation(int16_t ax, int16_t ay, int16_t az, int16_t gx, int16_t gy, int16_t gz)
//{
//  accelAngle = -atan2(ay, az) * (180/ PI); //tilt angle from accelerometer
//  Gyro_x = -gx / 131.0; //angular speed of X-axis from gyro
//}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.