Go Down

Topic: Sending Data Frames in a single block for speed (Read 78 times) previous topic - next topic

falcoso

Hello!

I am currently trying to implement some FFT hardware on an arduino, which then passes the spectrum over the serial port to a python script which can then display the spectrograms. I'm fairly new to Arduino programming but do have some C++ experience.

As far as I'm aware calling print functions repeatedly has a hefty overhead, so I am trying to send the data in frames of 128 spectrum samples. At the moment I have this and it works:

Code: [Select]
     
for(int i=0; i<(SAMPLES/2); i++)
      {
          Serial.println(vReal[i], 1);
      }


Where vReal is the real component of the frequency spectrum in a double array of length 128.

However, this repeatedly calls println for each sample, and if i decide to increase the size of a frame this will only make the overheads worse. I'm trying to get my head around the difference between the Serial.prinln and Serial.write. From what I understand I should be able to send the double array as:

Code: [Select]

Serial.write(vReal, 128);
Serial.flush();


but then I keep getting error: no matching function for call to 'HardwareSerial::write(double [128], int) when I try to compile.

Simply put, what is the best way of sending blocks of numerical data in frames over a serial connection?

Thanks

groundFungus

Quote
no matching function for call to 'HardwareSerial::write(double [128], int)
The write function takes an array of bytes.  You are trying to feed it an array of doubles.




falcoso

Ah I see, so I need to convert the double array into an array of bytes and then just re-construct on the other end?

groundFungus

Yes.  Using unions is one way to do that.

Code: [Select]
union buffer
{
    float num;
    byte bytes[4];
};

buffer sampleBuffer;


Sets up a union where the 4 bytes of num and the 4 bytes of bytes[4] occupy the same memory locations.  So if you do:
Code: [Select]
sampleBuffer.num = thisSample;
You can send the 4 bytes:
Code: [Select]

for(int n = 0; n < 4; n++)
{
  Serial.print(sampleBuffer.bytes[n]);
}


On the other end you have an identical union.   Receive the bytes and put them into the unions byte array (bytes[4]) and then the new union's num member (sampleBuffer.num) will have the float.

gfvalvo

Easier to just cast it:
Code: [Select]
Serial.write((uint8_t *) vReal, sizeof(vReal));
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

MorganS

You must be running a devastatingly efficient FFT for Serial to be the bottleneck. Maybe just send the data less often?

What baud rate? Can you increase it? Maybe 500000?

println() sends carriage return and newline after each print. You could save time by not sending those.

What resolution do you need? println sends two decimal places by default. You can reduce that or send 100 times the value and save sending the decimal point character.

Code: (options) [Select]
 float f=12;
 Serial.println(f); //7 chars
 Serial.print(f); Serial.print(";"); //6 chars
 Serial.print(f,1); //1 decimal place, 5 chars with separator
 Serial.print(f*10,0); //4 chars
 Serial.print(f,0); //3 chars
 
 //convert to byte, allows only -128 to +127
 Serial.write((byte)round(f)); //1 char

 //convert to 16-bit int, equivalent to 2 bytes
 //range -32768 to +32767
 int16_t i = round(f*10); //choose a good multiplier
 Serial.write((byte)i>>8);
 Serial.write((byte)i);
"The problem is in the code you didn't post."

Go Up