Printing a series of sensor values and efficiency

So I've read that the Serial.print is inefficient CPU wise.

Take this section of code from my interrupt service routine:
sensorA = analogRead(sensorPinA);
sensorB = analogRead(sensorPinB);
sensorC = analogRead(sensorPinC);
sensorD = analogRead(sensorPinD);
Serial.print(sensorA);
Serial.print(", ");
Serial.print(sensorB);
Serial.print(", ");
Serial.print(sensorC);
Serial.print(", ");
Serial.println(sensorD);

Will give me a result like this: 513, 518, 507, 467

Now I'm planning on having these sensors read at a 1 or 10Khz rate and print out the values. So because of the inefficiency of the Serial.print function I was wondering how to streamline this.

Serial.println(sensorA","sensorB","sensorc","sensorD);
This doesn't work. So I was wondering how could I efficiently print out the four sensor values with a comma between them so I can process the data later in excel?

The problem with using Serial.print(ln) in an ISR isn't that the Serial.print(ln) functions are inefficient. It is that Serial.print(ln) is slow.

The additional time required to make multiple function calls is minuscule compared to the time required to Serial.print(ln) the data.

Attempts to combine the data so that a single call to Serial.println in the ISR is required will not significantly affect the time required to execute the ISR.

513, 518, 507, 467

Consists of 19 characters - to print 10 000 ("10kHz") of these a second, you'd have a serial line running at at least 1.9 Mbit per second.
Even at 1kHz, that would be 190 000 bps - are you clear about what it is you want to do?

This is a problem you'll find you just can't get around; the Arduino cannot move data off-chip fast enough. The solution is to move computation on to the Arduino and reduce the volume of data that you need to transfer.

This might give you some ideas of your own, or if you explain your objective a little more you might find some good one suggestions here.

Good points everyone. I’m going to remove analog 3 which is sensorD from the picture.

So I’m going to have a 555 astable circuit as the interrupt generator tied to digital pin 2. Pin3 is then tied to the reset of the 555 circuit and held low until I want to start the ISR.

The sensors are two of the 1123 - Precision Voltage Sensor and one 1118 - 50Amp Current
Sensor AC&DC:
http://www.phidgets.com/products.php?category=8&product_id=1123
http://www.phidgets.com/products.php?category=8&product_id=1118

I only need 3-4 seconds of the 1Khz data gathering. I’ll use a button to tell the interrupt reset pin to go high, then start counting the number of interrupts that happen and stop by pulling the 555 reset LOW after I reach my goal of 3-4K interrupts.

So I’ve removed 1/4 of the data to print, and being each value will only be 3 integers long (they will not go up to 4 digits), I have a known size of data so I do not need the space or comma. I can just split the values and do the math using cut and such in a shell script later on. That brings the data down to 11 bytes.

So this all should just barely fit through an 115200 baud rate connection.

Does this make more sense?

It might work at that rate, but there is further optimization you can do.

  1. Send the data as a byte rather than ASCII characters. I.e. send 489 as B111101001. With some fancy bit management you could reduce the four 10-bit values into only 5 bytes.

  2. You can write a buffer class that stores the info being requested for send, and handles all the serial communications itself. In your interrupt routines you fill up the buffer, and in your main loop you let the buffer class empty itself at it’s own rate. In this way it’ll take longer before you overflow if you’re flooding too much data. Where do you find such a class you ask? Lucky for you I wrote just the thing. Note there is a bug with the first transmitted item in the ring buffer always being zero (haven’t bothered to track it down). Also note the value used for MY_RINGBUFFER_SIZE should be adjusted for the amount of free memory you have on your Arduino.

.h

#define MY_RINGBUFFER_SIZE 1024
#define EVENT_FULL 200
#define EVENT_ENDTX 255

struct myRingBuffer {
  byte event[MY_RINGBUFFER_SIZE];
  unsigned int time[MY_RINGBUFFER_SIZE];
  int head;
  int tail;
};

class DataLogger
{
  public:
    DataLogger();  // constructor
    void Init();  // initialize
    void Clear();  // clear
    boolean Store(byte event, unsigned int eventtime);
    void Transmit(unsigned int count);  // send count qty of queued data
    void Enable();
    void Disable();
    boolean IsEnabled();
    
  private:
    myRingBuffer databuffer;
    boolean bEnabled;
};

.cpp

#include "DataLogger.h"

DataLogger::DataLogger()
{
  Init()
}

void DataLogger::Init()
{
  databuffer.head = 0;
  databuffer.tail = 0;
  databuffer.event[0] = 0;
  databuffer.time[0] = 0;
  bEnabled = false;
}

boolean DataLogger::IsEnabled()
{
  return bEnabled;
}

void DataLogger::Enable()
{
  Init();
  bEnabled = true;
}

void DataLogger::Disable()
{
  if (bEnabled) {
    bEnabled = false;
    Serial.print(EVENT_ENDTX, BYTE);  // indicate end of transmission to listening program
    Serial.print(EVENT_ENDTX, BYTE);
    Serial.print(EVENT_ENDTX, BYTE);
    Serial.println("");
    databuffer.head = 0;
    databuffer.tail = 0;
  }
}

boolean DataLogger::Store(byte event, unsigned int eventtime)
{
  if (bEnabled) {
    int i = (databuffer.head + 1) % MY_RINGBUFFER_SIZE;
    if (i != databuffer.tail) {  // if we're not about to over-run the tail, queue the data at the head
      databuffer.event[i] = event;
      databuffer.time[i] = eventtime;
      databuffer.head = i;
      SendsCount++;
      return true;
    }
    else { // indicate that we over-ran the tail by over-writing head info with FULL state
      databuffer.event[databuffer.head] = EVENT_FULL;
      databuffer.time[databuffer.head] = 0;
    }
    
  }
  return false;
}

// transmit count queued items
void DataLogger::Transmit(unsigned int count)
{
  for(unsigned int i = 0; i < count; i++)
  {
    if (databuffer.head != databuffer.tail) {  // transmit data while it exists in the queue
      Serial.print(highByte(databuffer.time[databuffer.tail]));
      Serial.print(lowByte(databuffer.time[databuffer.tail]));
      Serial.print(databuffer.event[databuffer.tail], BYTE);
      databuffer.tail = (databuffer.tail + 1) % MY_RINGBUFFER_SIZE;
    }
    else delay(5); // try waiting for data if it isn't in queue
  }
}

Useage: Call .Store from the ISR. Call .Transmit(quantity) from main loop.

  1. You can modify the existing Serial class to use a larger defined value for RX_BUFFER_SIZE (cores\arduino\HardwareSerial.cpp line 22). With this you can increase the buffer at the Serial port to avoid an over-flow there. Note that #2 above is very similar to this