I am creating practice buzzers for my HiQ team. I am trying to have it so that when you hit the buzzer, it will serial print how early or late you were. My problem is since everything is in milliseconds, I am dividing by 1000 to get seconds, but when I do this, it only spits out the whole numbers. I know that my code isn't complete and I have removed a few parts to trouble shoot. I was wondering how to display decimals with serial.print.
here is the snip it of code that does the serial printing:
Serial.print((p1time-previousMillis)/1000, 4);
and here is all of my code:
#include <SoftwareSerial.h>
const int softwareTx = 7;
const int softwareRx = 6;
SoftwareSerial s7s(softwareRx, softwareTx);
const int p1=2;
const int p2=3;
int p1answ=1;
int p2answ=1;
int reading1;
int previous1=HIGH;
int reading2;
int previous2=HIGH;
long p1time=0;
long p2time=0;
long debounce=200;
int timeansw=16;
int led1=10;
int led2=11;
//button 2
unsigned long previousMillis = 0; // will store last time LED was updated
// constants won't change :
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
pinMode(p1, INPUT_PULLUP); //buttons configured as inputs but with a pullup
pinMode(p2, INPUT_PULLUP);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
s7s.begin(9600);
Serial.begin(9600);
s7s.write(0x76);
s7s.print("-HI-");
delay(1000);
s7s.write(0x76);
Serial.println("HiQ buzzer practice program");
Serial.println("Written by Alex Rodgers 2016");
}
void loop() {
reading1=digitalRead(p1);
if (reading1 == LOW && previous1 == HIGH && millis() - p1time >= debounce && p1answ == 1){
p1time=millis();
Serial.print((p1time-previousMillis)/1000);
}
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval && timeansw>0) {
if (timeansw>0){
timeansw = timeansw-1;
}
previousMillis = currentMillis;// save the last time you blinked the LED
s7s.write(0x76); //clear display
s7s.write(0x79); //set cursor
s7s.write(0x2); //cursor position
s7s.write(timeansw/10); //write the tens place of time
if (timeansw >= 10){ //check if time left is more than 10
s7s.write(0x79); //set cursor
s7s.write(0x3); //cursor position
s7s.write(timeansw - 10); //write the one's place of time
} else{ //if time left is less than 10
s7s.write(0x79); //set cursor
s7s.write(0x3); //cursor position
s7s.write(timeansw); //set time
}
}
}
Please do not link me to another question, because I have been googling for about 2 hours and I couldn't get it to display the decimals, and when I create a float variable and assign the value of what I am currently serial printing, and serial.print(myfloatvalue, 4); it spits out the whole number followed by .0000.
That's probably enough to satisfy your need, but you should be aware it's really inefficient. Your processor is doing two 16bit math operations to simply figure out where to stick the '.' character. When you call Serial.print(uint16_t), it's already doing divisions to convert your integer into a string. If you could just tell it where to insert the decimal point it'd be a lot more efficient. You could do that with something like this which also outputs "1.234":
#define MS_PER_SEC 1000
void print_decimal_point_string(uint16_t num, uint8_t dec_place)
{
char temp_buffer[7]; // uint16_t can be 5 digits + '.' + '\0'
char *temp_ptr = &temp_buffer[sizeof(temp_buffer)-1]; // start pointing to the end of the buffer
*temp_ptr-- = '\0'; // null terminate the string. todo - i might have lost a few cycles here if your optimizer sucks.
do
{
*temp_ptr-- = num % 10 + '0'; // convert least significant digit
if(--dec_place == 0) *temp_ptr-- = '.';
num /= 10;
} while (num != 0);
Serial.print(++temp_ptr); // print string, although we should have decremented one past it in the loop.
return;
}
void setup()
{
print_decimal_point_string(1234, 3);
}
That might look like a lot, but that's what Serial.print(uint16_t) is already doing for you when you call it. It's too bad that Serial.print(uint16_t) won't just insert a decimal point someplace that you asked it. It's also too bad that my function won't work right if num < MS_PER_SEC. It also doesn't deal with +/- signs, zero padding, long numbers, etc.
These are some of the reasons that I just write my own printf() functions. I include the "%+0.#d" formatter which then prints decimal point strings very efficiently with # precision which is incredibly useful in embedded programming. Heh...and it's not like the avr libraries don't have their own custom formatters for their printf() already, so there's nothing wrong with making your own.
BigBobby:
That's probably enough to satisfy your need, but you should be aware it's really inefficient. Your processor is doing two 16bit math operations to simply figure out where to stick the '.' character.
If this is just for a one time output routine, seeing the results 1/1000 of a second faster isn't going to be noticeable. This usage is simple, easy to read and maintain. It's when you run something thousands or millions of times, or when you have to react to something in microsecond times, that efficiency trumps coding simplicity.
On the other hand, it lacks the generality of a printf() - like routine, which allows different parts of the code to use the same function (thus saving memory), contains all the maintenance in one place, and allows efficiency to be implemented safely away from the main logic of the program.
For a tiny program, sometimes the simple, dumb approach is better. As your program grows, it becomes more and more advantageous to modularize functionality in the manner of the second example above.
As a beginner, you can't leap right into that. It's better to start with simple ideas. Later you will be thrilled to see how you can apply the same ideas to functions that you can use in all your different code ideas.
These are the results:
1.234
480
1.234
252
1.234
172
So, turning it into a float to print it is the worst at 480us. Using two 16bit integer operations takes 252us. Using a function that inserts the '.' during the ASCII conversion takes 172us. These measurements are +/- 4us since they don't ignore ISR time, but they are still pretty clear in terms of relative speed.
As my esteemed colleague who is very stingy with Karma pointed out above, execution time is only one measure of code quality. If you are only doing this once, then defining the print_decimal_point_string() function would score worse than the second option in terms of 1) FLASH memory used and 2) number of minutes typing. The first option...converting to a float...well, when I find engineers using floats out of laziness I want to chop their fingers off so that they can never harm another program again...but HR says the best I'm allowed to do is fire them
BigBobby:
As my esteemed colleague who is very stingy with Karma pointed out above
How am I supposed to know you want Karma? Assuming that I am the esteemed one of which you speak. I gave you one point, for nothing in return as far as I know. Let it never be said that I am stingy again...
aarg:
How am I supposed to know you want Karma? Assuming that I am the esteemed one of which you speak. I gave you one point, for nothing in return as far as I know. Let it never be said that I am stingy again...
AWOL:
To display decimals, you have to print floating-point values.
An integer divided by an integer is an integer.
You can always multiply the value by 10, 100, 1000, etc... (depending on how many decimal places you want), then divide by the same number, then divide it by 10 and keep going until the divider is 0.
Here's a function that I use in my stand-alone projects (i.e. compiled with AVR-GCC and no Arduino code):
void printFloat (double value, uint8_t digits, uint8_t dp)
{
uint8_t c, f, x;
uint32_t d, p, v;
if (value < 0.0) {
value = -value;
Serial.write ('-');
}
d = 1;
x = (digits - 2);
while (x--) {
d *= 10;
}
p = 1;
x = dp;
while (x--) {
p *= 10;
}
v = ((uint32_t) ((value * p) + 0.5)); // rounding
f = 0; // zero suppress flag
while (d) {
c = ((v / d) % 10);
if (c || f || (d == p)) {
Serial.write (c + '0');
f = 1;
} else {
Serial.write (' ');
}
if (d == p) {
Serial.write ('.');
}
d /= 10;
}
}
The "digits" and "dp" values are the same as printf %f values (for example, if you would use "%5.2f", then you would use "5" for digits and "2" for "dp".