Tips/ideas on speeding up loop for my project

Hey everyone.
I am not sure if this is the correct category but I have written a program for Arduino to write the addressing pins on 2 multiplexers and a demultiplexes and collect data(read and send over serial). I basically have 3 sensors that are switched through by two multiplexers and amplified differentially. I then demultiplex the signal to read on 3 Arduino analog pins sent over serial.

However, I have found that it currently is not quick enough to collect the signal. It is currently sampling at around 2000 Hz but I need at least 3000Hz. I found the sample rate by running the program for a period of time and seeing how many samples I got. It is likely mostly due to the speed of my serial print especially sending the unsigned long that I use for the time data. I tried to increase the baud rate to 2,000,000 which speed things up but not enough. I am not really sure on how to speed this code up any more. Does anyone have any other ideas? I was thinking of storing the data in memory, however I don't think the Arduino has enough memory space for the number of samples I am collecting (probably at least 100,000 samples). I am using an Arduino mega.

//Used to toggle fast adc
#define FASTADC 1

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


//enable pins
#define en 8
#define en_2 7
#define en_demux 9

//variables hold data and time
unsigned long t1;
unsigned long t2;
unsigned long t3;
unsigned long start;

int data1;
int data2;
int data3;



void setup() {

  //Writes high to all enable pins on mux and demux
  pinMode(en, OUTPUT);
  pinMode(en_2, OUTPUT);
  pinMode(en_demux, OUTPUT);
  digitalWrite(en, HIGH);
  digitalWrite(en_2, HIGH);
  digitalWrite(en_demux, HIGH);
 

  DDRA = DDRA | B11111111; //22-30 as output
  DDRC = DDRC | B11110000; //sets pins 30-33 as output

  //sets prescale from 128 to 16 to speed up adc 
  #if FASTADC
    // set prescale to 16
    //http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf page 285
    sbi(ADCSRA,ADPS2) ;
    cbi(ADCSRA,ADPS1) ;
    cbi(ADCSRA,ADPS0) ;
  #endif

  //I think the first conversion takes more cycles so do it in setup
  analogRead(54);
  analogRead(55);
  analogRead(56);

  //Baudrate of 2,000,000 for serial
  Serial.begin(2000000);

  Serial.println("Start Time");
  Serial.println(micros());
  start = micros();
}

void loop() {


  read1and2();
  read2and3();
  read1and3();
  
  Serial.println(data1);
  Serial.println(t1);

  Serial.println(data2);
  Serial.println(t2);

  Serial.println(data3);
  Serial.println(t3);

  
}

void read1and2() {
  PORTA = B00000000; // all low select pin 1 and 2 on muxes
  PORTC = B00000000; // select pin 1 on demux
  t1 = micros();
  data1 = analogRead(54);
}

void read2and3() {
  PORTA = B00010001; //write pin 22 and 27 select pins 2 and 3 on muxes
  PORTC = B10000000; // select pin 2 on demux
  t2 = micros();
  data2 = analogRead(55);
}

void read1and3() {
  PORTA = B00010000; //write pin 27 select pins 1 and 3 on muxes
  PORTC = B00100000; // selects pin 3 on demux
  t3 = micros();
  data3 = analogRead(56);
}

I strongly suspect that the Serial.print() statements are the limiting factor - and that is easily tested.

One option may be to collect (say) 100 readings into an array, only print when the array is full and accept that sampling will be affected during the printing process.

...R

What are you sending the data to? Currently, you are sending 3 unsigned longs as strings, 3 ints as strings, 6 carriage returns, and 6 line feeds each iteration of loop(). You could send the data with commas between the strings, and save 4 bytes. You could send the data in binary mode and save even more.

I don't believe I could stop the data collection to send a whole array as it is important the signal not get interrupted.

I am currently just reading it off the serial monitor and copying from the serial monitor into matlab to split it and plot it. I don't need the data to be sent as a string, it could be sent as the data type it is whether it be int or unsinged long. Eventually I hope to capture the serial data with matlab, but I have not started that yet.

Are you saying rather than the println() just use print() with commas? I can try doing that and check for comma to split the data.

For sending in binary mode, would I just use serial write() instead of serial print(). I have never used the write() method before, but does it output the same thing as a print would? I know print() converts it to a string representation but otherwise will the output look the same?

I have never used the write() method before, but does it output the same thing as a print would?

It outputs the raw data as a byte, not an ASCII representation of the byte. So in the serial monitor, no it would not look the same.

However what is taking the time is not only the serial print but also the analogRead, these go at 10KHz, so using up three of them brings you down to nearly your required maximum.

I set the prescaler to 16 versus the default 128. This should speed up the analogWrite enough so its not as big a factor in the time since each call should only take around 13 microseconds or am I incorrect? I think the significant chunk is from the serial use.

So for serial write I have to break up the value into bytes and send that? For example integer is 2 bytes, so I would break the int into two bytes and send two bytes? Would I need another device/program to recombine the bytes into int and longs on the receiving end?

This should speed up the analogWrite enough so its not as big a factor in the time since each call should only take around 13 microseconds or am I incorrect

The prescaller has no effect on a digitalWrite, do you mean an analogRead?
If so I think the 16 is too much, I tend to use a prescaller value of 32, to give a 38K sample rate.

So for serial write I have to break up the value into bytes and send that?

Yes.

Would I need another device/program to recombine the bytes into int and longs on the receiving end?

Yes the code reading the serial program will have to combine the bytes into the appropriate combinations to rehydrated the variable.
This can be tricky because with more than a couple of variables to transfer you run the danger of getting out of sync. This is where you can get creative with the values you send. For example an A/D sample only goes up to 1023, or 10 bits so the upper bits are always zero. You could put a bit pattern into this upper bits to form a unique identifier for a sample int, assuming no other variable will be large enough to extend into those bits.

You could always use a faster Arduino device - eg a Due.

Allan