Go Down

Topic: sending a float over serial (Read 3540 times) previous topic - next topic

adamohern

Hi all:

I know there have been a few threads on this topic already, but I haven't found anything that's worked for me yet.

I'm making a simple speedometer with a reed switch, and I want to send a MPH value with 2 decimal places over USB to a Processing app. So far everything works perfectly, except that what's being sent over USB seems to be an integer approximation of my float value. I've tried multiplying by 100, but I just end up with round 100's, like "800". So I'm not sure what to do. Thanks in advance for your help!

Arduino:
Code: [Select]
#define LED 13                   //pin for the LED
#define SWITCH 0                 //input for REED SWITCH
int rim = 2170;                  //circumference in mm

int val = 0;                     // used to store input value
int previousVal = 0;             // lets not be too repetitious
unsigned long revTimer;          // the last time the output pin was toggled
unsigned long serialTimer;
long debounce = 10;              // the debounce time, increase if the output flickers
int cycles = 1;                  // total number of revolutions
float currentSpeed = 0;           // current speed in MPH

void setup() {
pinMode(LED, OUTPUT);           //tell arduino LED is an output
pinMode(SWITCH, INPUT);         //SWITCH is input
Serial.begin(9600);
revTimer = millis();
serialTimer = millis();
}

void loop(){
val=digitalRead(SWITCH);        //read input value and store it

//check whether input is HIGH (switch closed)
if (val==HIGH) {
  digitalWrite(LED, HIGH);     //turn LED on
  previousVal = HIGH;
  //if(millis()-revTimer > 2000) currentSpeed = 0;
} else{
  digitalWrite(LED, LOW);
  if (previousVal != LOW && millis() - revTimer > debounce) {
    currentSpeed = (float) (millis() - revTimer)*0.001;   // Convert time elapsed to milliseconds to seconds
    currentSpeed = rim/currentSpeed;               // S = d/t: Rim circumference divided by time elapsed
    currentSpeed = currentSpeed*0.002237;          // MPH Conversion: 1 mm/s = 0.001 m/s * 3600 s/hr * 1 mile / 1609 m = 0.002237 mi/hr
    revTimer = millis();                           // Keep a log of the timestamp for each reed switch pulse
    cycles++;                                      // The wheel has obviously turned another revolution
    sendStats();
    serialTimer = millis();
  }
  previousVal = LOW;
}

if (millis() - serialTimer > 1000) {
  sendStats();
  serialTimer = millis();
}

}

void sendStats() {
 Serial.print("cycles="); Serial.print(cycles);
 Serial.print("&currentSpeedx100="); Serial.print((int)currentSpeed*100);
 Serial.print(10,BYTE);
}

void decimate(char test[],int dec) {
int i=0;  
int length=strlen(test);
char msg[10]="";

strcpy(msg,test);

if (length <= dec) {
  for(i=dec;i>(dec-length);i--)  msg[i] = msg[i-(dec-length+1)];
  for(i=0;i<(dec+1-length);i++)  msg[i]='0';
  length = strlen(msg);
}
for (i=length;i>(length-dec);i--)  msg[i]=msg[i-1];
msg[length-dec]='.';

strcpy(test,msg);
}


Processing:
Code: [Select]
import processing.opengl.*;
import processing.serial.*;
Serial myPort;  // Create object from Serial class
PFont fontA;
String val;
String prev;
int lf = 10; //Linefeed in ASCII

void setup()
{
 size(400,400,OPENGL);
 hint(ENABLE_OPENGL_4X_SMOOTH);
 fontA = loadFont("HelveticaNeue-Bold-48.vlw");
 textFont(fontA, 48); // Set the font and its size (in units of pixels)
 
 String portName = Serial.list()[0];        
 // I know that the first port in the serial list on my mac
 // is always my  FTDI adaptor, so I open Serial.list()[0].
 // On Windows machines, this generally opens COM1.
 myPort = new Serial(this, portName, 9600);
}

void draw()
{
 background(255);
 if ( myPort.available() > 0) {  // If data is available,
       val = myPort.readStringUntil(lf);         // read it and store it in val
       if(val != null) val = trim(val);
 }
 fill(0); textAlign(CENTER); noStroke();
 if(val != null && val != prev)
 {
   println(val);
   prev = val;
 }
}


Result:

...
cycles=56&currentSpeedx100=1900
cycles=56&currentSpeedx100=1900
cycles=56&currentSpeedx100=1900
cycles=56&currentSpeedx100=1900
cycles=56&currentSpeedx100=1900
cycles=56&currentSpeedx100=1900

Groove

#1
Apr 19, 2010, 09:06 am Last Edit: Apr 19, 2010, 09:08 am by GrooveFlotilla Reason: 1
Quote
what's being sent over USB seems to be an integer approximation of my float value


Quote
Serial.print((int)currentSpeed*100);


Not surprising - you're truncating "currentSpeed" into an "int" and then multiplying by 100.

Quote
Serial.print((int)(currentSpeed*100.0));


[edit]BTW this isn't a hardware troubleshooting issue.[/edit]
Per Arduino ad Astra

adamohern

Sorry for the mis-categorization of the post. Is there a way to have it moved to the appropriate forum?

Sorry, leaving in the (int) when I posted this question was a mistake. Without the (int) there, I can't send the message. Your response did get me thinking however, and this solutions seems to have worked:

Code: [Select]
 currentSpeed = currentSpeed*100;
 Serial.print("&currentSpeedx100="); Serial.print((int) currentSpeed);


It's a shame I can't just send the dang float straight over!

Thanks!

Senso

Why you just don't use ftoa(float to ascii) to send the float trough the serial connection?
You cant send one float directly because it uses one specific order of bits, and one serial connection is made to send ascii chars so it would end up killing the float number and in the pc side you would only receive garbage.

adamohern

Quote
Why you just don't use ftoa(float to ascii) to send the float trough the serial connection?


I'm not quite sure what you mean by that. I couldn't find anything in the forums quite like what you describe, but this seems to work out well:

Code: [Select]
 char ascii[32];
 int currentSpeedDec = (currentSpeed - (int)currentSpeed) * 100;
 sprintf(ascii,"&currentSpeed=%0d.%d", (int)currentSpeed, currentSpeedDec);
 Serial.print(ascii);

donkahones

have you tried using

Serial.println(val, format) ;


adamohern

Quote
have you tried using

Serial.println(val, format) ;


Hmm, that hasn't worked for me so far (just sends an int), plus it inserts a linefeed :(

donkahones

Quote
Serial.print((int)currentSpeed*100);


why do you do this here?

try doing the multiplying by 100 prior to the serial.print.

adamohern

#8
Apr 20, 2010, 09:50 pm Last Edit: Apr 20, 2010, 09:50 pm by adamohern Reason: 1
As you can see above, my current method doesn't:

Code: [Select]
char ascii[32];
 int currentSpeedDec = (currentSpeed - (int)currentSpeed) * 100;
 sprintf(ascii,"&currentSpeed=%0d.%20d", (int)currentSpeed, currentSpeedDec);
 Serial.print(ascii);

donkahones

#9
Apr 20, 2010, 11:12 pm Last Edit: Apr 20, 2010, 11:13 pm by donkahones Reason: 1
Just to get this straight:

Code: [Select]

float currentSpeed;
...
...
currentSpeed = currentSpeed * 100;
...
...
Serial.print(currentSpeed, 2);


will only print integers?

adamohern

Not quite. It won't even print integers; it just prints combinations of one's and zeros.

Arduino:
Code: [Select]
float currentSpeed;
...
Serial.print(currentSpeed,2); Serial.print(10,BYTE);


Processing:
Code: [Select]
void draw()
{
 if ( myPort.available() > 0) {  // If data is available,
       val = myPort.readStringUntil(lf);         // read it and store it in val
       if(val != null) val = trim(val);
 }
 if(val != null && val != prev)
 {
   println(val);
   prev = val;
 }
}


Result:
Code: [Select]

...
100
0
110
101
1
11
100
100
...

PaulS

Code: [Select]
Serial.print(currentSpeed,2);

What version of the IDE are you using. As I recall, with 18, this should print a float with two decimal places. Older versions, I think, treated the second argument as a base. So, you might be getting this interpreted as "print this as in integer in base 2" instead of "print this as a float with 2 decimal places".

adamohern

Quote
What version of the IDE are you using. As I recall, with 18, this should print a float with two decimal places.


Ah, that indeed was the problem. I hadn't updated in quite some time as it turns out! Thanks a ton!!

Go Up