Modify Serial.print() to print an int value

Hi everyone, first of all sorry for my english... I wrote a sketch that read all the analog pins and sends the values trough usb connection. I need to read/convert values and send them in a fixed time and at fixed frequency. My problem is that the serial.print() function takes too much time so i read the print.cpp file and i noticed that void Print::print(int n, int base) function does not send an int value (2 byte) but a long value (4 byte). How can i operate to force serial.print() to send an int value?
Thanks, Daniele.

Whether you print 4 byte or 2 byte values, most of the time taken by the "print" method will be in actually transmitting the ASCII characters.
Have you thought about increasing the baud rate?

Yes, i set the highest baud rate in arduino and in my software/listener on pc (without useful results) but i need to execute the operations in specific time and the "print()" function take too much time because it sends 4 byte (like a long variable) instead of 2 byte that returns analorRead() function. I would to do something like modify the print() function itself only to send my int value...

You're being vague, but the time taken to pass 2 bytes instead of 4 to a subroutine is minuscule compared to the time taken to convert and print those bytes.

Can you post some code, or explain in more detail what it is you want to do?

Sorry, maybe i have not explained well... I was not referring to time taken to pass value to a subroutine but to time taken to send it via serial.
If I set a rate of 115200 bauds, I should use 8,68us for each bit so, in a comunication at 115200 bauds, no parity,8 data bit and 1 bit stop, I should take 86,8us for each byte so 173,6us to send 2 byte. I would take this time (almost 175us) to send my int value but the serial.print() takes twice as long because it sends a long value (4 bytes). The code is simply like:
int var=analogRead(pin);
Serial.print(var);
Can i change something in somewhere to increase performance?

An analogue read will give a value zero to 1023, so will need to transmit a maximum of four bytes to print the value. that is a simple fact of ASCII.
Do you need to look at Serial.write?

the analogRead() function return an int value; int datatype stores a 2 byte value. if you read the print.cpp you can notice the print(int) prototype that receives an int value (stored in 2 bytes) but prints a long value (stored in 4 byte) so it takes double of time... if i use write() function i will only receive a sequence of bits...

No, you're overcomplicating this.
It does not matter if print is passed two or four bytes, because if there are four decimal digits to be printed, then four ASCII characters will be output.
if the value is zero, then it still outputs the ASCII zero character.

I understand what you say but according to Arduino Starter Kit kaufen [verschiedene Ausführungen] if i pass an int value the related method cast the value to long and send it, doesnt'it?

Yes, it casts it to a wider datatype, but if the variable contains only two significant decimals (i.e. 10 to 99) then it will only output two bytes.
if it contains 100 to 999 then it will output three bytes.
If it contains 1000 to 1023 then it will output four bytes.
It doesn't matter how wide the variable is, only the number of significant decimal digits.

If I give you a litre of water, it does not matter if I give it to you in a five litre bucket or a ten litre bucket, it is still a litre of water.

:slight_smile: i love your paragon and i finally understand... the problem is that give you a ten litre bucket is more comlicated than give you a litre bucket and i take more time... i estimated, using motecarlo method, that serial.print() take almost 500us in the worst case (sending 1023,the highest value returned by analogRead) at 115200 baud/s. I also estimated that analogRead() takes 112us. Total 612us in worst case but i need to do analogRead and Serial.print plus a couple of operations in 694us with Arduino Diecimila first (i have enough time) and with Arduino Mega later (i haven't enough time, i need to do everything in 297us with Arduino Mega :-? )... I don't know how make it possible, any suggestion? I read something about concurrency or parallelism....

you could send as binary using Serial.write, or you could split the analogRead so that the initiation of the read (setup) and the read itself. This eliminates a while loop that is just wasting time.

i use interrupts instead useless while loop.
i generate interrupts at 240 Hz so every 4,167ms i read an analog pin; on arduino diecimila there are 6 analog pins, so i have 694,4us (4,167/6) to read the value and send it to pc but i will work with arduino mega because i need to work with 14 analog pins, so at 240 Hz i have only 297,6us (4,167/14) to do everything...
I would use serial.write() function (so i can save a lot of time) but i don't know how correctly interpret the sequence of bits :-/

You could split the int into 2 bytes yourself. Then send the most significant byte first, then the least significant byte. On the receiving end, put the int back together in the order that they were sent.

You're going to have trouble deciding which byte belongs to which sample

have a close look at the source of analogRead in wiring_analog.cpp ; that while loop in the middle is wasting a lot of your time. Split the routine into two - there was a thread about this a little while ago in Development.

@PaulS:
great idea, i'm just implementing it in order to try it, thanks!

@AWOL:
as i wrote above, i don't have much problems. i'm doing something like:
val=analogRead(pin);
high=highByte(val);
low=lowByte(val);
when i receive a byte, just convert it.

i can't find the thread, can you link it in reply?

I found this, which has some useful links:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1208715493/11

I think the trick is to split "analogRead" into two parts:
(this is for 0018)

int analogRead(uint8_t pin)
{
      uint8_t low, high;

      // set the analog reference (high two bits of ADMUX) and select the
      // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
      // to 0 (the default).
      ADMUX = (analog_reference << 6) | (pin & 0x07);
  
#if defined(__AVR_ATmega1280__)
      // the MUX5 bit of ADCSRB selects whether we're reading from channels
      // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
      ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif

      // without a delay, we seem to read from the wrong channel
      //delay(1);

      // start the conversion
      sbi(ADCSRA, ADSC);

      // ADSC is cleared when the conversion finishes
      while (bit_is_set(ADCSRA, ADSC));

      // we have to read ADCL first; doing so locks both ADCL
      // and ADCH until ADCH is read.  reading ADCL second would
      // cause the results of each conversion to be discarded,
      // as ADCL and ADCH would be locked when it completed.
      low = ADCL;
      high = ADCH;

      // combine the two bytes
      return (high << 8) | low;
}

There in the middle is the "while" loop that polls the bit that tells us the conversion we asked for with "sbi(ADCSRA, ADSC)" is complete.
So, if we've got something like: (uncompiled, untested)

void analogReadStart(uint8_t pin)
{
      // set the analog reference (high two bits of ADMUX) and select the
      // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
      // to 0 (the default).
      ADMUX = (analog_reference << 6) | (pin & 0x07);
  
#if defined(__AVR_ATmega1280__)
      // the MUX5 bit of ADCSRB selects whether we're reading from channels
      // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
      ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif

      // without a delay, we seem to read from the wrong channel
      //delay(1);

      // start the conversion
      sbi(ADCSRA, ADSC);
}

int analogReadResult ()
{      
      uint8_t low, high;

      // ADSC is cleared when the conversion finishes
      while (bit_is_set(ADCSRA, ADSC));

      // we have to read ADCL first; doing so locks both ADCL
      // and ADCH until ADCH is read.  reading ADCL second would
      // cause the results of each conversion to be discarded,
      // as ADCL and ADCH would be locked when it completed.
      low = ADCL;
      high = ADCH;

      // combine the two bytes
      return (high << 8) | low;
}

Now, we call "analogReadStart (pinNumber)" which returns almost immediately having set up the multiplexer and started the conversion, so now we can go off and do something, like transmit the last value read from the analogue input, then we call "result = analogReadResult ()".
We still have to have the "while" loop to ensure the conversion is complete, but hopefully, it won't waste any more time.
Obviously, this approach needs a little tuning, but it may gain you a few microseconds.

You have 300 usec to do the printing, you say. That implies sending 3000 printed numbers to the PC per second. You aren't reading those with your eye in real time. Can you sample for a while, save to RAM on the Arduino, and then print?

@Groove:
I'm very interesting in your solution; for now I am comfortable with PaulS' solution:I gained a lot of time; I will try your suggestion anyway.

@jrraines:
I have 300us to do everything: read/convert from a pin, do a couple of operation then send the value. I am not interested to read in real time with my eyes, I need the datas in post-processing in order to do some sampling and reconstruction stuffs

Coming back to your original question (despite the fact that your problem is solved), I did a little experiment and wrote:

Serial.print( (long) analogRead(0));

with and without the cast (long). The difference is a remarkable 28 bytes! I didn't do any timing experiment, but I'm sure it is faster with the cast. Your original observation was quite perceptive.

Some of the functions in the print library look like candidates for inline -- they are one liners. I wonder what the optimizer would accomplish if that were done.